ec.c 22.2 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2
 *  ec.c - ACPI Embedded Controller Driver (v2.0)
Linus Torvalds's avatar
Linus Torvalds committed
3
 *
4
5
 *  Copyright (C) 2006, 2007 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
 *  Copyright (C) 2006 Denis Sadykov <denis.m.sadykov@intel.com>
Linus Torvalds's avatar
Linus Torvalds committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 *  Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or (at
 *  your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
Dmitry Torokhov's avatar
Dmitry Torokhov committed
36
#include <linux/interrupt.h>
37
#include <linux/list.h>
Linus Torvalds's avatar
Linus Torvalds committed
38
39
40
41
42
43
44
45
#include <asm/io.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
#include <acpi/actypes.h>

#define ACPI_EC_CLASS			"embedded_controller"
#define ACPI_EC_DEVICE_NAME		"Embedded Controller"
#define ACPI_EC_FILE_INFO		"info"
46

47
48
#undef PREFIX
#define PREFIX				"ACPI: EC: "
49

50
/* EC status register */
Linus Torvalds's avatar
Linus Torvalds committed
51
52
#define ACPI_EC_FLAG_OBF	0x01	/* Output buffer full */
#define ACPI_EC_FLAG_IBF	0x02	/* Input buffer full */
Dmitry Torokhov's avatar
Dmitry Torokhov committed
53
#define ACPI_EC_FLAG_BURST	0x10	/* burst mode */
Linus Torvalds's avatar
Linus Torvalds committed
54
#define ACPI_EC_FLAG_SCI	0x20	/* EC-SCI occurred */
55

56
/* EC commands */
57
enum ec_command {
58
59
60
61
62
	ACPI_EC_COMMAND_READ = 0x80,
	ACPI_EC_COMMAND_WRITE = 0x81,
	ACPI_EC_BURST_ENABLE = 0x82,
	ACPI_EC_BURST_DISABLE = 0x83,
	ACPI_EC_COMMAND_QUERY = 0x84,
63
};
64

65
/* EC events */
66
enum ec_event {
67
	ACPI_EC_EVENT_OBF_1 = 1,	/* Output buffer full */
68
	ACPI_EC_EVENT_IBF_0,	/* Input buffer empty */
69
70
};

71
#define ACPI_EC_DELAY		500	/* Wait 500ms max. during EC ops */
72
73
#define ACPI_EC_UDELAY_GLK	1000	/* Wait 1ms max. to get global lock */

74
static enum ec_mode {
75
76
	EC_INTR = 1,		/* Output buffer full */
	EC_POLL,		/* Input buffer empty */
77
} acpi_ec_mode = EC_INTR;
78

Len Brown's avatar
Len Brown committed
79
80
81
static int acpi_ec_remove(struct acpi_device *device, int type);
static int acpi_ec_start(struct acpi_device *device);
static int acpi_ec_stop(struct acpi_device *device, int type);
82
static int acpi_ec_add(struct acpi_device *device);
Linus Torvalds's avatar
Linus Torvalds committed
83

84
85
86
87
88
static const struct acpi_device_id ec_device_ids[] = {
	{"PNP0C09", 0},
	{"", 0},
};

Linus Torvalds's avatar
Linus Torvalds committed
89
static struct acpi_driver acpi_ec_driver = {
Len Brown's avatar
Len Brown committed
90
	.name = "ec",
Len Brown's avatar
Len Brown committed
91
	.class = ACPI_EC_CLASS,
92
	.ids = ec_device_ids,
Len Brown's avatar
Len Brown committed
93
	.ops = {
94
		.add = acpi_ec_add,
Len Brown's avatar
Len Brown committed
95
96
97
98
		.remove = acpi_ec_remove,
		.start = acpi_ec_start,
		.stop = acpi_ec_stop,
		},
Linus Torvalds's avatar
Linus Torvalds committed
99
};
100
101

/* If we find an EC via the ECDT, we need to keep a ptr to its context */
102
/* External interfaces use first EC only, so remember */
103
104
105
106
107
108
109
110
111
112
typedef int (*acpi_ec_query_func) (void *data);

struct acpi_ec_query_handler {
	struct list_head node;
	acpi_ec_query_func func;
	acpi_handle handle;
	void *data;
	u8 query_bit;
};

113
static struct acpi_ec {
114
	acpi_handle handle;
115
	unsigned long gpe;
116
117
	unsigned long command_addr;
	unsigned long data_addr;
118
	unsigned long global_lock;
119
	struct mutex lock;
120
	atomic_t query_pending;
121
	atomic_t event_count;
122
	wait_queue_head_t wait;
123
	struct list_head list;
124
} *boot_ec, *first_ec;
125

Linus Torvalds's avatar
Linus Torvalds committed
126
127
128
129
/* --------------------------------------------------------------------------
                             Transaction Management
   -------------------------------------------------------------------------- */

130
static inline u8 acpi_ec_read_status(struct acpi_ec *ec)
Linus Torvalds's avatar
Linus Torvalds committed
131
{
132
	return inb(ec->command_addr);
Dmitry Torokhov's avatar
Dmitry Torokhov committed
133
134
}

135
static inline u8 acpi_ec_read_data(struct acpi_ec *ec)
136
{
137
	return inb(ec->data_addr);
138
139
}

140
static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command)
141
{
142
	outb(command, ec->command_addr);
143
144
}

145
static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data)
146
{
147
	outb(data, ec->data_addr);
148
}
149

150
151
static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event,
				       unsigned old_count)
152
{
153
	u8 status = acpi_ec_read_status(ec);
154
155
	if (old_count == atomic_read(&ec->event_count))
		return 0;
Alexey Starikovskiy's avatar
Alexey Starikovskiy committed
156
	if (event == ACPI_EC_EVENT_OBF_1) {
157
158
		if (status & ACPI_EC_FLAG_OBF)
			return 1;
Alexey Starikovskiy's avatar
Alexey Starikovskiy committed
159
	} else if (event == ACPI_EC_EVENT_IBF_0) {
160
161
		if (!(status & ACPI_EC_FLAG_IBF))
			return 1;
162
163
	}

164
	return 0;
165
}
Dmitry Torokhov's avatar
Dmitry Torokhov committed
166

167
168
static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event,
                        unsigned count, int force_poll)
169
{
170
	if (unlikely(force_poll) || acpi_ec_mode == EC_POLL) {
171
172
		unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY);
		while (time_before(jiffies, delay)) {
173
			if (acpi_ec_check_status(ec, event, 0))
174
175
				return 0;
		}
176
177
	} else {
		if (wait_event_timeout(ec->wait,
178
				       acpi_ec_check_status(ec, event, count),
179
				       msecs_to_jiffies(ACPI_EC_DELAY)) ||
180
		    acpi_ec_check_status(ec, event, 0)) {
181
			return 0;
182
183
184
		} else {
			printk(KERN_ERR PREFIX "acpi_ec_wait timeout,"
			       " status = %d, expect_event = %d\n",
185
			       acpi_ec_read_status(ec), event);
186
		}
187
	}
Linus Torvalds's avatar
Linus Torvalds committed
188

189
	return -ETIME;
Linus Torvalds's avatar
Linus Torvalds committed
190
191
}

192
static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command,
193
					const u8 * wdata, unsigned wdata_len,
194
195
					u8 * rdata, unsigned rdata_len,
					int force_poll)
196
{
197
	int result = 0;
198
	unsigned count = atomic_read(&ec->event_count);
199
	acpi_ec_write_cmd(ec, command);
200

Alexey Starikovskiy's avatar
Alexey Starikovskiy committed
201
	for (; wdata_len > 0; --wdata_len) {
202
		result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, count, force_poll);
203
		if (result) {
204
205
			printk(KERN_ERR PREFIX
			       "write_cmd timeout, command = %d\n", command);
206
207
			goto end;
		}
208
		count = atomic_read(&ec->event_count);
209
		acpi_ec_write_data(ec, *(wdata++));
210
	}
211

212
	if (!rdata_len) {
213
		result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, count, force_poll);
214
		if (result) {
215
216
			printk(KERN_ERR PREFIX
			       "finish-write timeout, command = %d\n", command);
217
218
			goto end;
		}
219
220
	} else if (command == ACPI_EC_COMMAND_QUERY) {
		atomic_set(&ec->query_pending, 0);
221
	}
222

Alexey Starikovskiy's avatar
Alexey Starikovskiy committed
223
	for (; rdata_len > 0; --rdata_len) {
224
		result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1, count, force_poll);
225
226
		if (result) {
			printk(KERN_ERR PREFIX "read timeout, command = %d\n",
227
			       command);
228
229
			goto end;
		}
230
		count = atomic_read(&ec->event_count);
231
		*(rdata++) = acpi_ec_read_data(ec);
232
	}
233
234
      end:
	return result;
235
236
}

237
static int acpi_ec_transaction(struct acpi_ec *ec, u8 command,
238
			       const u8 * wdata, unsigned wdata_len,
239
240
			       u8 * rdata, unsigned rdata_len,
			       int force_poll)
Linus Torvalds's avatar
Linus Torvalds committed
241
{
242
	int status;
Len Brown's avatar
Len Brown committed
243
	u32 glk;
Linus Torvalds's avatar
Linus Torvalds committed
244

245
	if (!ec || (wdata_len && !wdata) || (rdata_len && !rdata))
246
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
247

248
249
	if (rdata)
		memset(rdata, 0, rdata_len);
Linus Torvalds's avatar
Linus Torvalds committed
250

251
	mutex_lock(&ec->lock);
252
	if (ec->global_lock) {
Linus Torvalds's avatar
Linus Torvalds committed
253
		status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
254
255
		if (ACPI_FAILURE(status)) {
			mutex_unlock(&ec->lock);
256
			return -ENODEV;
257
		}
Linus Torvalds's avatar
Linus Torvalds committed
258
	}
Dmitry Torokhov's avatar
Dmitry Torokhov committed
259

260
	/* Make sure GPE is enabled before doing transaction */
261
	acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
262

263
	status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, 0, 0);
264
	if (status) {
265
		printk(KERN_ERR PREFIX
266
		       "input buffer is not empty, aborting transaction\n");
Dmitry Torokhov's avatar
Dmitry Torokhov committed
267
		goto end;
268
	}
Linus Torvalds's avatar
Linus Torvalds committed
269

270
271
	status = acpi_ec_transaction_unlocked(ec, command,
					      wdata, wdata_len,
272
273
					      rdata, rdata_len,
					      force_poll);
Linus Torvalds's avatar
Linus Torvalds committed
274

275
      end:
Linus Torvalds's avatar
Linus Torvalds committed
276

277
	if (ec->global_lock)
Linus Torvalds's avatar
Linus Torvalds committed
278
		acpi_release_global_lock(glk);
279
	mutex_unlock(&ec->lock);
Linus Torvalds's avatar
Linus Torvalds committed
280

281
	return status;
Linus Torvalds's avatar
Linus Torvalds committed
282
283
}

284
285
286
287
288
289
290
/*
 * Note: samsung nv5000 doesn't work with ec burst mode.
 * http://bugzilla.kernel.org/show_bug.cgi?id=4980
 */
int acpi_ec_burst_enable(struct acpi_ec *ec)
{
	u8 d;
291
	return acpi_ec_transaction(ec, ACPI_EC_BURST_ENABLE, NULL, 0, &d, 1, 0);
292
293
294
295
}

int acpi_ec_burst_disable(struct acpi_ec *ec)
{
296
	return acpi_ec_transaction(ec, ACPI_EC_BURST_DISABLE, NULL, 0, NULL, 0, 0);
297
298
}

299
static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data)
300
301
302
303
304
{
	int result;
	u8 d;

	result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_READ,
305
				     &address, 1, &d, 1, 0);
306
307
308
	*data = d;
	return result;
}
309

310
311
static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data)
{
312
313
	u8 wdata[2] = { address, data };
	return acpi_ec_transaction(ec, ACPI_EC_COMMAND_WRITE,
314
				   wdata, 2, NULL, 0, 0);
315
316
}

Linus Torvalds's avatar
Linus Torvalds committed
317
318
319
/*
 * Externally callable EC access functions. For now, assume 1 EC only
 */
320
321
322
323
int ec_burst_enable(void)
{
	if (!first_ec)
		return -ENODEV;
324
	return acpi_ec_burst_enable(first_ec);
325
326
327
328
329
330
331
332
}

EXPORT_SYMBOL(ec_burst_enable);

int ec_burst_disable(void)
{
	if (!first_ec)
		return -ENODEV;
333
	return acpi_ec_burst_disable(first_ec);
334
335
336
337
}

EXPORT_SYMBOL(ec_burst_disable);

338
int ec_read(u8 addr, u8 * val)
Linus Torvalds's avatar
Linus Torvalds committed
339
340
{
	int err;
341
	u8 temp_data;
Linus Torvalds's avatar
Linus Torvalds committed
342
343
344
345

	if (!first_ec)
		return -ENODEV;

346
	err = acpi_ec_read(first_ec, addr, &temp_data);
Linus Torvalds's avatar
Linus Torvalds committed
347
348
349
350

	if (!err) {
		*val = temp_data;
		return 0;
Len Brown's avatar
Len Brown committed
351
	} else
Linus Torvalds's avatar
Linus Torvalds committed
352
353
		return err;
}
Len Brown's avatar
Len Brown committed
354

Linus Torvalds's avatar
Linus Torvalds committed
355
356
EXPORT_SYMBOL(ec_read);

Len Brown's avatar
Len Brown committed
357
int ec_write(u8 addr, u8 val)
Linus Torvalds's avatar
Linus Torvalds committed
358
359
360
361
362
363
{
	int err;

	if (!first_ec)
		return -ENODEV;

364
	err = acpi_ec_write(first_ec, addr, val);
Linus Torvalds's avatar
Linus Torvalds committed
365
366
367

	return err;
}
Len Brown's avatar
Len Brown committed
368

Linus Torvalds's avatar
Linus Torvalds committed
369
370
EXPORT_SYMBOL(ec_write);

371
int ec_transaction(u8 command,
372
		   const u8 * wdata, unsigned wdata_len,
373
374
		   u8 * rdata, unsigned rdata_len,
		   int force_poll)
375
{
376
377
	if (!first_ec)
		return -ENODEV;
378

379
	return acpi_ec_transaction(first_ec, command, wdata,
380
381
				   wdata_len, rdata, rdata_len,
				   force_poll);
382
}
Linus Torvalds's avatar
Linus Torvalds committed
383

384
385
EXPORT_SYMBOL(ec_transaction);

386
static int acpi_ec_query(struct acpi_ec *ec, u8 * data)
387
388
{
	int result;
389
	u8 d;
Linus Torvalds's avatar
Linus Torvalds committed
390

391
392
	if (!ec || !data)
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
393

394
395
396
397
398
	/*
	 * Query the EC to find out which _Qxx method we need to evaluate.
	 * Note that successful completion of the query causes the ACPI_EC_SCI
	 * bit to be cleared (and thus clearing the interrupt source).
	 */
399

400
	result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_QUERY, NULL, 0, &d, 1, 0);
401
402
	if (result)
		return result;
Linus Torvalds's avatar
Linus Torvalds committed
403

404
405
	if (!d)
		return -ENODATA;
Linus Torvalds's avatar
Linus Torvalds committed
406

407
408
	*data = d;
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
409
410
411
412
413
}

/* --------------------------------------------------------------------------
                                Event Management
   -------------------------------------------------------------------------- */
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
			      acpi_handle handle, acpi_ec_query_func func,
			      void *data)
{
	struct acpi_ec_query_handler *handler =
	    kzalloc(sizeof(struct acpi_ec_query_handler), GFP_KERNEL);
	if (!handler)
		return -ENOMEM;

	handler->query_bit = query_bit;
	handler->handle = handle;
	handler->func = func;
	handler->data = data;
	mutex_lock(&ec->lock);
	list_add_tail(&handler->node, &ec->list);
	mutex_unlock(&ec->lock);
	return 0;
}

EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler);

void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit)
{
	struct acpi_ec_query_handler *handler;
	mutex_lock(&ec->lock);
	list_for_each_entry(handler, &ec->list, node) {
		if (query_bit == handler->query_bit) {
			list_del(&handler->node);
			kfree(handler);
			break;
		}
	}
	mutex_unlock(&ec->lock);
}

EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler);
Linus Torvalds's avatar
Linus Torvalds committed
450

Len Brown's avatar
Len Brown committed
451
static void acpi_ec_gpe_query(void *ec_cxt)
452
{
453
	struct acpi_ec *ec = ec_cxt;
454
	u8 value = 0;
455
	struct acpi_ec_query_handler *handler, copy;
456

457
	if (!ec || acpi_ec_query(ec, &value))
458
		return;
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
	mutex_lock(&ec->lock);
	list_for_each_entry(handler, &ec->list, node) {
		if (value == handler->query_bit) {
			/* have custom handler for this bit */
			memcpy(&copy, handler, sizeof(copy));
			mutex_unlock(&ec->lock);
			if (copy.func) {
				copy.func(copy.data);
			} else if (copy.handle) {
				acpi_evaluate_object(copy.handle, NULL, NULL, NULL);
			}
			return;
		}
	}
	mutex_unlock(&ec->lock);
474
}
Linus Torvalds's avatar
Linus Torvalds committed
475

Len Brown's avatar
Len Brown committed
476
static u32 acpi_ec_gpe_handler(void *data)
Linus Torvalds's avatar
Linus Torvalds committed
477
{
Len Brown's avatar
Len Brown committed
478
	acpi_status status = AE_OK;
479
	u8 value;
480
	struct acpi_ec *ec = data;
481

482
	atomic_inc(&ec->event_count);
483

484
	if (acpi_ec_mode == EC_INTR) {
485
		wake_up(&ec->wait);
Dmitry Torokhov's avatar
Dmitry Torokhov committed
486
487
	}

488
	value = acpi_ec_read_status(ec);
489
490
	if ((value & ACPI_EC_FLAG_SCI) && !atomic_read(&ec->query_pending)) {
		atomic_set(&ec->query_pending, 1);
491
		status =
492
		    acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, ec);
Len Brown's avatar
Len Brown committed
493
	}
494

Dmitry Torokhov's avatar
Dmitry Torokhov committed
495
	return status == AE_OK ?
Len Brown's avatar
Len Brown committed
496
	    ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
Linus Torvalds's avatar
Linus Torvalds committed
497
498
499
500
501
502
503
}

/* --------------------------------------------------------------------------
                             Address Space Management
   -------------------------------------------------------------------------- */

static acpi_status
Len Brown's avatar
Len Brown committed
504
505
acpi_ec_space_setup(acpi_handle region_handle,
		    u32 function, void *handler_context, void **return_context)
Linus Torvalds's avatar
Linus Torvalds committed
506
507
508
509
510
{
	/*
	 * The EC object is in the handler context and is needed
	 * when calling the acpi_ec_space_handler.
	 */
Len Brown's avatar
Len Brown committed
511
512
	*return_context = (function != ACPI_REGION_DEACTIVATE) ?
	    handler_context : NULL;
Linus Torvalds's avatar
Linus Torvalds committed
513
514
515
516
517

	return AE_OK;
}

static acpi_status
518
519
acpi_ec_space_handler(u32 function, acpi_physical_address address,
		      u32 bits, acpi_integer *value,
Len Brown's avatar
Len Brown committed
520
		      void *handler_context, void *region_context)
Linus Torvalds's avatar
Linus Torvalds committed
521
{
522
	struct acpi_ec *ec = handler_context;
523
524
	int result = 0, i = 0;
	u8 temp = 0;
Linus Torvalds's avatar
Linus Torvalds committed
525
526

	if ((address > 0xFF) || !value || !handler_context)
527
		return AE_BAD_PARAMETER;
Linus Torvalds's avatar
Linus Torvalds committed
528

529
	if (function != ACPI_READ && function != ACPI_WRITE)
530
		return AE_BAD_PARAMETER;
Linus Torvalds's avatar
Linus Torvalds committed
531

532
533
	if (bits != 8 && acpi_strict)
		return AE_BAD_PARAMETER;
Linus Torvalds's avatar
Linus Torvalds committed
534

535
536
537
538
539
540
541
542
543
544
	while (bits - i > 0) {
		if (function == ACPI_READ) {
			result = acpi_ec_read(ec, address, &temp);
			(*value) |= ((acpi_integer)temp) << i;
		} else {
			temp = 0xff & ((*value) >> i);
			result = acpi_ec_write(ec, address, temp);
		}
		i += 8;
		++address;
Linus Torvalds's avatar
Linus Torvalds committed
545
546
547
548
	}

	switch (result) {
	case -EINVAL:
549
		return AE_BAD_PARAMETER;
Linus Torvalds's avatar
Linus Torvalds committed
550
551
		break;
	case -ENODEV:
552
		return AE_NOT_FOUND;
Linus Torvalds's avatar
Linus Torvalds committed
553
554
		break;
	case -ETIME:
555
		return AE_TIME;
Linus Torvalds's avatar
Linus Torvalds committed
556
557
		break;
	default:
558
		return AE_OK;
Linus Torvalds's avatar
Linus Torvalds committed
559
560
561
562
563
564
565
	}
}

/* --------------------------------------------------------------------------
                              FS Interface (/proc)
   -------------------------------------------------------------------------- */

Len Brown's avatar
Len Brown committed
566
static struct proc_dir_entry *acpi_ec_dir;
Linus Torvalds's avatar
Linus Torvalds committed
567

Len Brown's avatar
Len Brown committed
568
static int acpi_ec_read_info(struct seq_file *seq, void *offset)
Linus Torvalds's avatar
Linus Torvalds committed
569
{
570
	struct acpi_ec *ec = seq->private;
Linus Torvalds's avatar
Linus Torvalds committed
571
572
573
574

	if (!ec)
		goto end;

575
576
577
578
	seq_printf(seq, "gpe:\t\t\t0x%02x\n", (u32) ec->gpe);
	seq_printf(seq, "ports:\t\t\t0x%02x, 0x%02x\n",
		   (unsigned)ec->command_addr, (unsigned)ec->data_addr);
	seq_printf(seq, "use global lock:\t%s\n",
579
		   ec->global_lock ? "yes" : "no");
Len Brown's avatar
Len Brown committed
580
      end:
581
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
582
583
584
585
586
587
588
}

static int acpi_ec_info_open_fs(struct inode *inode, struct file *file)
{
	return single_open(file, acpi_ec_read_info, PDE(inode)->data);
}

589
static struct file_operations acpi_ec_info_ops = {
Len Brown's avatar
Len Brown committed
590
591
592
593
	.open = acpi_ec_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
Linus Torvalds's avatar
Linus Torvalds committed
594
595
596
	.owner = THIS_MODULE,
};

Len Brown's avatar
Len Brown committed
597
static int acpi_ec_add_fs(struct acpi_device *device)
Linus Torvalds's avatar
Linus Torvalds committed
598
{
Len Brown's avatar
Len Brown committed
599
	struct proc_dir_entry *entry = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
600
601
602

	if (!acpi_device_dir(device)) {
		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown's avatar
Len Brown committed
603
						     acpi_ec_dir);
Linus Torvalds's avatar
Linus Torvalds committed
604
		if (!acpi_device_dir(device))
605
			return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
606
607
608
	}

	entry = create_proc_entry(ACPI_EC_FILE_INFO, S_IRUGO,
Len Brown's avatar
Len Brown committed
609
				  acpi_device_dir(device));
Linus Torvalds's avatar
Linus Torvalds committed
610
	if (!entry)
611
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
612
613
614
615
616
617
	else {
		entry->proc_fops = &acpi_ec_info_ops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}

618
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
619
620
}

Len Brown's avatar
Len Brown committed
621
static int acpi_ec_remove_fs(struct acpi_device *device)
Linus Torvalds's avatar
Linus Torvalds committed
622
623
624
625
626
627
628
629
{

	if (acpi_device_dir(device)) {
		remove_proc_entry(ACPI_EC_FILE_INFO, acpi_device_dir(device));
		remove_proc_entry(acpi_device_bid(device), acpi_ec_dir);
		acpi_device_dir(device) = NULL;
	}

630
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
631
632
633
634
635
}

/* --------------------------------------------------------------------------
                               Driver Interface
   -------------------------------------------------------------------------- */
636
637
638
639
640
641
642
643
644
static acpi_status
ec_parse_io_ports(struct acpi_resource *resource, void *context);

static struct acpi_ec *make_acpi_ec(void)
{
	struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
	if (!ec)
		return NULL;

645
	atomic_set(&ec->query_pending, 1);
646
647
648
	atomic_set(&ec->event_count, 1);
	mutex_init(&ec->lock);
	init_waitqueue_head(&ec->wait);
649
	INIT_LIST_HEAD(&ec->list);
650
651
652

	return ec;
}
Linus Torvalds's avatar
Linus Torvalds committed
653

654
655
656
657
658
659
660
661
662
663
664
665
666
static acpi_status
acpi_ec_register_query_methods(acpi_handle handle, u32 level,
			       void *context, void **return_value)
{
	struct acpi_namespace_node *node = handle;
	struct acpi_ec *ec = context;
	int value = 0;
	if (sscanf(node->name.ascii, "_Q%x", &value) == 1) {
		acpi_ec_add_query_handler(ec, value, handle, NULL, NULL);
	}
	return AE_OK;
}

667
668
static acpi_status
ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
669
{
670
671
672
673
674
675
676
	acpi_status status;

	struct acpi_ec *ec = context;
	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
				     ec_parse_io_ports, ec);
	if (ACPI_FAILURE(status))
		return status;
677
678
679

	/* Get GPE bit assignment (EC events). */
	/* TODO: Add support for _GPE returning a package */
680
681
682
	status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe);
	if (ACPI_FAILURE(status))
		return status;
683
684
685
686
687
688
689
690
691

	/* Use the global lock for all EC transactions? */
	acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock);

	ec->handle = handle;

	printk(KERN_INFO PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx",
			  ec->gpe, ec->command_addr, ec->data_addr);

692
	return AE_CTRL_TERMINATE;
693
694
}

695
static int acpi_ec_add(struct acpi_device *device)
Linus Torvalds's avatar
Linus Torvalds committed
696
{
697
	struct acpi_ec *ec = NULL;
698
699

	if (!device)
700
		return -EINVAL;
701

702
703
704
705
	strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
	strcpy(acpi_device_class(device), ACPI_EC_CLASS);

	ec = make_acpi_ec();
706
	if (!ec)
707
		return -ENOMEM;
708

709
710
	if (ec_parse_device(device->handle, 0, ec, NULL) !=
	    AE_CTRL_TERMINATE) {
711
712
		kfree(ec);
		return -EINVAL;
713
	}
Linus Torvalds's avatar
Linus Torvalds committed
714

715
	/* Check if we found the boot EC */
716
717
	if (boot_ec) {
		if (boot_ec->gpe == ec->gpe) {
718
			/* We might have incorrect info for GL at boot time */
719
720
			mutex_lock(&boot_ec->lock);
			boot_ec->global_lock = ec->global_lock;
721
722
			/* Copy handlers from new ec into boot ec */
			list_splice(&ec->list, &boot_ec->list);
723
			mutex_unlock(&boot_ec->lock);
724
			kfree(ec);
725
			ec = boot_ec;
726
		}
727
728
	} else
		first_ec = ec;
729
730
	ec->handle = device->handle;
	acpi_driver_data(device) = ec;
Linus Torvalds's avatar
Linus Torvalds committed
731

732
733
	acpi_ec_add_fs(device);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
734
735
}

Len Brown's avatar
Len Brown committed
736
static int acpi_ec_remove(struct acpi_device *device, int type)
Linus Torvalds's avatar
Linus Torvalds committed
737
{
738
	struct acpi_ec *ec;
739
	struct acpi_ec_query_handler *handler;
Linus Torvalds's avatar
Linus Torvalds committed
740
741

	if (!device)
742
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
743
744

	ec = acpi_driver_data(device);
745
746
747
748
749
750
	mutex_lock(&ec->lock);
	list_for_each_entry(handler, &ec->list, node) {
		list_del(&handler->node);
		kfree(handler);
	}
	mutex_unlock(&ec->lock);
Linus Torvalds's avatar
Linus Torvalds committed
751
	acpi_ec_remove_fs(device);
752
	acpi_driver_data(device) = NULL;
753
	if (ec == first_ec)
754
755
756
		first_ec = NULL;

	/* Don't touch boot EC */
757
	if (boot_ec != ec)
758
		kfree(ec);
759
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
760
761
762
}

static acpi_status
763
ec_parse_io_ports(struct acpi_resource *resource, void *context)
Linus Torvalds's avatar
Linus Torvalds committed
764
{
765
	struct acpi_ec *ec = context;
Linus Torvalds's avatar
Linus Torvalds committed
766

767
	if (resource->type != ACPI_RESOURCE_TYPE_IO)
Linus Torvalds's avatar
Linus Torvalds committed
768
769
770
771
772
773
774
		return AE_OK;

	/*
	 * The first address region returned is the data port, and
	 * the second address region returned is the status/command
	 * port.
	 */
775
	if (ec->data_addr == 0)
776
		ec->data_addr = resource->data.io.minimum;
777
	else if (ec->command_addr == 0)
778
		ec->command_addr = resource->data.io.minimum;
779
	else
Linus Torvalds's avatar
Linus Torvalds committed
780
781
782
783
784
		return AE_CTRL_TERMINATE;

	return AE_OK;
}

785
786
static int ec_install_handlers(struct acpi_ec *ec)
{
787
788
789
790
	acpi_status status;
	status = acpi_install_gpe_handler(NULL, ec->gpe,
					  ACPI_GPE_EDGE_TRIGGERED,
					  &acpi_ec_gpe_handler, ec);
791
792
	if (ACPI_FAILURE(status))
		return -ENODEV;
793

794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
	acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME);
	acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);

	status = acpi_install_address_space_handler(ec->handle,
						    ACPI_ADR_SPACE_EC,
						    &acpi_ec_space_handler,
						    &acpi_ec_space_setup, ec);
	if (ACPI_FAILURE(status)) {
		acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler);
		return -ENODEV;
	}

	return 0;
}

Len Brown's avatar
Len Brown committed
809
static int acpi_ec_start(struct acpi_device *device)
Linus Torvalds's avatar
Linus Torvalds committed
810
{
811
	struct acpi_ec *ec;
812
	int ret = 0;
Linus Torvalds's avatar
Linus Torvalds committed
813
814

	if (!device)
815
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
816
817
818
819

	ec = acpi_driver_data(device);

	if (!ec)
820
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
821

822
	/* Boot EC is already working */
823
824
825
826
827
	if (ec != boot_ec)
		ret = ec_install_handlers(ec);

	/* EC is fully operational, allow queries */
	atomic_set(&ec->query_pending, 0);
828

829
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
830
831
}

Len Brown's avatar
Len Brown committed
832
static int acpi_ec_stop(struct acpi_device *device, int type)
Linus Torvalds's avatar
Linus Torvalds committed
833
{
834
835
	acpi_status status;
	struct acpi_ec *ec;
Linus Torvalds's avatar
Linus Torvalds committed
836
837

	if (!device)
838
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
839
840

	ec = acpi_driver_data(device);
841
842
843
844
	if (!ec)
		return -EINVAL;

	/* Don't touch boot EC */
845
	if (ec == boot_ec)
846
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
847

848
	status = acpi_remove_address_space_handler(ec->handle,
Len Brown's avatar
Len Brown committed
849
850
						   ACPI_ADR_SPACE_EC,
						   &acpi_ec_space_handler);
Linus Torvalds's avatar
Linus Torvalds committed
851
	if (ACPI_FAILURE(status))
852
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
853

854
	status = acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler);
Linus Torvalds's avatar
Linus Torvalds committed
855
	if (ACPI_FAILURE(status))
856
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
857

858
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
859
860
}

861
862
863
864
int __init acpi_ec_ecdt_probe(void)
{
	int ret;
	acpi_status status;
Len Brown's avatar
Len Brown committed
865
	struct acpi_table_ecdt *ecdt_ptr;
866

867
868
	boot_ec = make_acpi_ec();
	if (!boot_ec)
869
870
871
872
		return -ENOMEM;
	/*
	 * Generate a boot ec context
	 */
873
874
	status = acpi_get_table(ACPI_SIG_ECDT, 1,
				(struct acpi_table_header **)&ecdt_ptr);
875
876
877
878
879
880
881
882
883
884
885
886
887
	if (ACPI_SUCCESS(status)) {
		printk(KERN_INFO PREFIX "EC description table is found, configuring boot EC\n\n");
		boot_ec->command_addr = ecdt_ptr->control.address;
		boot_ec->data_addr = ecdt_ptr->data.address;
		boot_ec->gpe = ecdt_ptr->gpe;
		boot_ec->handle = ACPI_ROOT_OBJECT;
	} else {
		printk(KERN_DEBUG PREFIX "Look up EC in DSDT\n");
		status = acpi_get_devices(ec_device_ids[0].id, ec_parse_device,
						boot_ec, NULL);
		if (ACPI_FAILURE(status))
			goto error;
	}
Linus Torvalds's avatar
Linus Torvalds committed
888

889
	ret = ec_install_handlers(boot_ec);
890
891
	if (!ret) {
		first_ec = boot_ec;
892
		return 0;
893
	}
894
      error:
895
896
	kfree(boot_ec);
	boot_ec = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
897
898
899
900

	return -ENODEV;
}

Len Brown's avatar
Len Brown committed
901
static int __init acpi_ec_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
902
{
Len Brown's avatar
Len Brown committed
903
	int result = 0;
Linus Torvalds's avatar
Linus Torvalds committed
904
905

	if (acpi_disabled)
906
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
907
908
909

	acpi_ec_dir = proc_mkdir(ACPI_EC_CLASS, acpi_root_dir);
	if (!acpi_ec_dir)
910
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
911
912
913
914
915

	/* Now register the driver for the EC */
	result = acpi_bus_register_driver(&acpi_ec_driver);
	if (result < 0) {
		remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);
916
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
917
918
	}

919
	return result;
Linus Torvalds's avatar
Linus Torvalds committed
920
921
922
923
924
925
}

subsys_initcall(acpi_ec_init);

/* EC driver currently not unloadable */
#if 0
Len Brown's avatar
Len Brown committed
926
static void __exit acpi_ec_exit(void)
Linus Torvalds's avatar
Linus Torvalds committed
927
928
929
930
931
932
{

	acpi_bus_unregister_driver(&acpi_ec_driver);

	remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);

933
	return;
Linus Torvalds's avatar
Linus Torvalds committed
934
}
Len Brown's avatar
Len Brown committed
935
#endif				/* 0 */
Linus Torvalds's avatar
Linus Torvalds committed
936

937
static int __init acpi_ec_set_intr_mode(char *str)
938
{
939
	int intr;
940

941
	if (!get_option(&str, &intr))
942
943
		return 0;