diff options
author | Len Brown <len.brown@intel.com> | 2007-04-28 23:12:56 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2007-04-28 23:12:56 -0400 |
commit | 03feb0524660bcd890674d11d29a1873ca14d13c (patch) | |
tree | 6e1f75a2205568c27a8bd9534b8186d529cd0146 /drivers/acpi | |
parent | fb16596997ded3e70d308bec58e32c1e2c5cf9b0 (diff) | |
parent | 9fd9f8e8bdcfc9aa309dae5bccc55d02804337d0 (diff) |
Pull ec into release branch
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/Makefile | 7 | ||||
-rw-r--r-- | drivers/acpi/ec.c | 495 |
2 files changed, 181 insertions, 321 deletions
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 92642ab15451..b6266e79af00 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
@@ -1,6 +1,6 @@ | |||
1 | # | 1 | # |
2 | # Makefile for the Linux ACPI interpreter | 2 | # Makefile for the Linux ACPI interpreter |
3 | # | 3 | # |
4 | 4 | ||
5 | export ACPI_CFLAGS | 5 | export ACPI_CFLAGS |
6 | 6 | ||
@@ -32,16 +32,17 @@ obj-y += osl.o utils.o \ | |||
32 | processor-objs += processor_core.o processor_throttling.o \ | 32 | processor-objs += processor_core.o processor_throttling.o \ |
33 | processor_idle.o processor_thermal.o | 33 | processor_idle.o processor_thermal.o |
34 | ifdef CONFIG_CPU_FREQ | 34 | ifdef CONFIG_CPU_FREQ |
35 | processor-objs += processor_perflib.o | 35 | processor-objs += processor_perflib.o |
36 | endif | 36 | endif |
37 | 37 | ||
38 | obj-y += sleep/ | 38 | obj-y += sleep/ |
39 | obj-y += bus.o glue.o | 39 | obj-y += bus.o glue.o |
40 | obj-y += scan.o | 40 | obj-y += scan.o |
41 | # Keep EC driver first. Initialization of others depend on it. | ||
42 | obj-$(CONFIG_ACPI_EC) += ec.o | ||
41 | obj-$(CONFIG_ACPI_AC) += ac.o | 43 | obj-$(CONFIG_ACPI_AC) += ac.o |
42 | obj-$(CONFIG_ACPI_BATTERY) += battery.o | 44 | obj-$(CONFIG_ACPI_BATTERY) += battery.o |
43 | obj-$(CONFIG_ACPI_BUTTON) += button.o | 45 | obj-$(CONFIG_ACPI_BUTTON) += button.o |
44 | obj-$(CONFIG_ACPI_EC) += ec.o | ||
45 | obj-$(CONFIG_ACPI_FAN) += fan.o | 46 | obj-$(CONFIG_ACPI_FAN) += fan.o |
46 | obj-$(CONFIG_ACPI_DOCK) += dock.o | 47 | obj-$(CONFIG_ACPI_DOCK) += dock.o |
47 | obj-$(CONFIG_ACPI_BAY) += bay.o | 48 | obj-$(CONFIG_ACPI_BAY) += bay.o |
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index a802962ff2b4..e08cf98f504f 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -1,6 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * acpi_ec.c - ACPI Embedded Controller Driver ($Revision: 38 $) | 2 | * ec.c - ACPI Embedded Controller Driver (v2.0) |
3 | * | 3 | * |
4 | * Copyright (C) 2006, 2007 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com> | ||
5 | * Copyright (C) 2006 Denis Sadykov <denis.m.sadykov@intel.com> | ||
4 | * Copyright (C) 2004 Luming Yu <luming.yu@intel.com> | 6 | * Copyright (C) 2004 Luming Yu <luming.yu@intel.com> |
5 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> | 7 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> |
6 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> | 8 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> |
@@ -91,9 +93,9 @@ static struct acpi_driver acpi_ec_driver = { | |||
91 | }; | 93 | }; |
92 | 94 | ||
93 | /* If we find an EC via the ECDT, we need to keep a ptr to its context */ | 95 | /* If we find an EC via the ECDT, we need to keep a ptr to its context */ |
96 | /* External interfaces use first EC only, so remember */ | ||
94 | static struct acpi_ec { | 97 | static struct acpi_ec { |
95 | acpi_handle handle; | 98 | acpi_handle handle; |
96 | unsigned long uid; | ||
97 | unsigned long gpe; | 99 | unsigned long gpe; |
98 | unsigned long command_addr; | 100 | unsigned long command_addr; |
99 | unsigned long data_addr; | 101 | unsigned long data_addr; |
@@ -101,12 +103,8 @@ static struct acpi_ec { | |||
101 | struct mutex lock; | 103 | struct mutex lock; |
102 | atomic_t query_pending; | 104 | atomic_t query_pending; |
103 | atomic_t event_count; | 105 | atomic_t event_count; |
104 | atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */ | ||
105 | wait_queue_head_t wait; | 106 | wait_queue_head_t wait; |
106 | } *ec_ecdt; | 107 | } *boot_ec, *first_ec; |
107 | |||
108 | /* External interfaces use first EC only, so remember */ | ||
109 | static struct acpi_device *first_ec; | ||
110 | 108 | ||
111 | /* -------------------------------------------------------------------------- | 109 | /* -------------------------------------------------------------------------- |
112 | Transaction Management | 110 | Transaction Management |
@@ -173,56 +171,6 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, unsigned count) | |||
173 | return -ETIME; | 171 | return -ETIME; |
174 | } | 172 | } |
175 | 173 | ||
176 | #ifdef ACPI_FUTURE_USAGE | ||
177 | /* | ||
178 | * Note: samsung nv5000 doesn't work with ec burst mode. | ||
179 | * http://bugzilla.kernel.org/show_bug.cgi?id=4980 | ||
180 | */ | ||
181 | int acpi_ec_enter_burst_mode(struct acpi_ec *ec) | ||
182 | { | ||
183 | u8 tmp = 0; | ||
184 | u8 status = 0; | ||
185 | |||
186 | status = acpi_ec_read_status(ec); | ||
187 | if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)) { | ||
188 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); | ||
189 | if (status) | ||
190 | goto end; | ||
191 | acpi_ec_write_cmd(ec, ACPI_EC_BURST_ENABLE); | ||
192 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1); | ||
193 | tmp = acpi_ec_read_data(ec); | ||
194 | if (tmp != 0x90) { /* Burst ACK byte */ | ||
195 | return -EINVAL; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | atomic_set(&ec->leaving_burst, 0); | ||
200 | return 0; | ||
201 | end: | ||
202 | ACPI_EXCEPTION((AE_INFO, status, "EC wait, burst mode")); | ||
203 | return -1; | ||
204 | } | ||
205 | |||
206 | int acpi_ec_leave_burst_mode(struct acpi_ec *ec) | ||
207 | { | ||
208 | u8 status = 0; | ||
209 | |||
210 | status = acpi_ec_read_status(ec); | ||
211 | if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)) { | ||
212 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); | ||
213 | if (status) | ||
214 | goto end; | ||
215 | acpi_ec_write_cmd(ec, ACPI_EC_BURST_DISABLE); | ||
216 | acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); | ||
217 | } | ||
218 | atomic_set(&ec->leaving_burst, 1); | ||
219 | return 0; | ||
220 | end: | ||
221 | ACPI_EXCEPTION((AE_INFO, status, "EC leave burst mode")); | ||
222 | return -1; | ||
223 | } | ||
224 | #endif /* ACPI_FUTURE_USAGE */ | ||
225 | |||
226 | static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, | 174 | static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, |
227 | const u8 * wdata, unsigned wdata_len, | 175 | const u8 * wdata, unsigned wdata_len, |
228 | u8 * rdata, unsigned rdata_len) | 176 | u8 * rdata, unsigned rdata_len) |
@@ -312,6 +260,21 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, | |||
312 | return status; | 260 | return status; |
313 | } | 261 | } |
314 | 262 | ||
263 | /* | ||
264 | * Note: samsung nv5000 doesn't work with ec burst mode. | ||
265 | * http://bugzilla.kernel.org/show_bug.cgi?id=4980 | ||
266 | */ | ||
267 | int acpi_ec_burst_enable(struct acpi_ec *ec) | ||
268 | { | ||
269 | u8 d; | ||
270 | return acpi_ec_transaction(ec, ACPI_EC_BURST_ENABLE, NULL, 0, &d, 1); | ||
271 | } | ||
272 | |||
273 | int acpi_ec_burst_disable(struct acpi_ec *ec) | ||
274 | { | ||
275 | return acpi_ec_transaction(ec, ACPI_EC_BURST_DISABLE, NULL, 0, NULL, 0); | ||
276 | } | ||
277 | |||
315 | static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) | 278 | static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) |
316 | { | 279 | { |
317 | int result; | 280 | int result; |
@@ -333,18 +296,33 @@ static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) | |||
333 | /* | 296 | /* |
334 | * Externally callable EC access functions. For now, assume 1 EC only | 297 | * Externally callable EC access functions. For now, assume 1 EC only |
335 | */ | 298 | */ |
299 | int ec_burst_enable(void) | ||
300 | { | ||
301 | if (!first_ec) | ||
302 | return -ENODEV; | ||
303 | return acpi_ec_burst_enable(first_ec); | ||
304 | } | ||
305 | |||
306 | EXPORT_SYMBOL(ec_burst_enable); | ||
307 | |||
308 | int ec_burst_disable(void) | ||
309 | { | ||
310 | if (!first_ec) | ||
311 | return -ENODEV; | ||
312 | return acpi_ec_burst_disable(first_ec); | ||
313 | } | ||
314 | |||
315 | EXPORT_SYMBOL(ec_burst_disable); | ||
316 | |||
336 | int ec_read(u8 addr, u8 * val) | 317 | int ec_read(u8 addr, u8 * val) |
337 | { | 318 | { |
338 | struct acpi_ec *ec; | ||
339 | int err; | 319 | int err; |
340 | u8 temp_data; | 320 | u8 temp_data; |
341 | 321 | ||
342 | if (!first_ec) | 322 | if (!first_ec) |
343 | return -ENODEV; | 323 | return -ENODEV; |
344 | 324 | ||
345 | ec = acpi_driver_data(first_ec); | 325 | err = acpi_ec_read(first_ec, addr, &temp_data); |
346 | |||
347 | err = acpi_ec_read(ec, addr, &temp_data); | ||
348 | 326 | ||
349 | if (!err) { | 327 | if (!err) { |
350 | *val = temp_data; | 328 | *val = temp_data; |
@@ -357,15 +335,12 @@ EXPORT_SYMBOL(ec_read); | |||
357 | 335 | ||
358 | int ec_write(u8 addr, u8 val) | 336 | int ec_write(u8 addr, u8 val) |
359 | { | 337 | { |
360 | struct acpi_ec *ec; | ||
361 | int err; | 338 | int err; |
362 | 339 | ||
363 | if (!first_ec) | 340 | if (!first_ec) |
364 | return -ENODEV; | 341 | return -ENODEV; |
365 | 342 | ||
366 | ec = acpi_driver_data(first_ec); | 343 | err = acpi_ec_write(first_ec, addr, val); |
367 | |||
368 | err = acpi_ec_write(ec, addr, val); | ||
369 | 344 | ||
370 | return err; | 345 | return err; |
371 | } | 346 | } |
@@ -376,14 +351,10 @@ int ec_transaction(u8 command, | |||
376 | const u8 * wdata, unsigned wdata_len, | 351 | const u8 * wdata, unsigned wdata_len, |
377 | u8 * rdata, unsigned rdata_len) | 352 | u8 * rdata, unsigned rdata_len) |
378 | { | 353 | { |
379 | struct acpi_ec *ec; | ||
380 | |||
381 | if (!first_ec) | 354 | if (!first_ec) |
382 | return -ENODEV; | 355 | return -ENODEV; |
383 | 356 | ||
384 | ec = acpi_driver_data(first_ec); | 357 | return acpi_ec_transaction(first_ec, command, wdata, |
385 | |||
386 | return acpi_ec_transaction(ec, command, wdata, | ||
387 | wdata_len, rdata, rdata_len); | 358 | wdata_len, rdata, rdata_len); |
388 | } | 359 | } |
389 | 360 | ||
@@ -420,7 +391,7 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data) | |||
420 | 391 | ||
421 | static void acpi_ec_gpe_query(void *ec_cxt) | 392 | static void acpi_ec_gpe_query(void *ec_cxt) |
422 | { | 393 | { |
423 | struct acpi_ec *ec = (struct acpi_ec *)ec_cxt; | 394 | struct acpi_ec *ec = ec_cxt; |
424 | u8 value = 0; | 395 | u8 value = 0; |
425 | char object_name[8]; | 396 | char object_name[8]; |
426 | 397 | ||
@@ -438,8 +409,9 @@ static u32 acpi_ec_gpe_handler(void *data) | |||
438 | { | 409 | { |
439 | acpi_status status = AE_OK; | 410 | acpi_status status = AE_OK; |
440 | u8 value; | 411 | u8 value; |
441 | struct acpi_ec *ec = (struct acpi_ec *)data; | 412 | struct acpi_ec *ec = data; |
442 | atomic_inc(&ec->event_count); | 413 | atomic_inc(&ec->event_count); |
414 | |||
443 | if (acpi_ec_mode == EC_INTR) { | 415 | if (acpi_ec_mode == EC_INTR) { |
444 | wake_up(&ec->wait); | 416 | wake_up(&ec->wait); |
445 | } | 417 | } |
@@ -482,7 +454,7 @@ acpi_ec_space_handler(u32 function, | |||
482 | void *handler_context, void *region_context) | 454 | void *handler_context, void *region_context) |
483 | { | 455 | { |
484 | int result = 0; | 456 | int result = 0; |
485 | struct acpi_ec *ec = NULL; | 457 | struct acpi_ec *ec = handler_context; |
486 | u64 temp = *value; | 458 | u64 temp = *value; |
487 | acpi_integer f_v = 0; | 459 | acpi_integer f_v = 0; |
488 | int i = 0; | 460 | int i = 0; |
@@ -494,8 +466,6 @@ acpi_ec_space_handler(u32 function, | |||
494 | return AE_BAD_PARAMETER; | 466 | return AE_BAD_PARAMETER; |
495 | } | 467 | } |
496 | 468 | ||
497 | ec = (struct acpi_ec *)handler_context; | ||
498 | |||
499 | next_byte: | 469 | next_byte: |
500 | switch (function) { | 470 | switch (function) { |
501 | case ACPI_READ: | 471 | case ACPI_READ: |
@@ -551,18 +521,16 @@ static struct proc_dir_entry *acpi_ec_dir; | |||
551 | 521 | ||
552 | static int acpi_ec_read_info(struct seq_file *seq, void *offset) | 522 | static int acpi_ec_read_info(struct seq_file *seq, void *offset) |
553 | { | 523 | { |
554 | struct acpi_ec *ec = (struct acpi_ec *)seq->private; | 524 | struct acpi_ec *ec = seq->private; |
555 | 525 | ||
556 | if (!ec) | 526 | if (!ec) |
557 | goto end; | 527 | goto end; |
558 | 528 | ||
559 | seq_printf(seq, "gpe: 0x%02x\n", (u32) ec->gpe); | 529 | seq_printf(seq, "gpe:\t\t\t0x%02x\n", (u32) ec->gpe); |
560 | seq_printf(seq, "ports: 0x%02x, 0x%02x\n", | 530 | seq_printf(seq, "ports:\t\t\t0x%02x, 0x%02x\n", |
561 | (u32) ec->command_addr, (u32) ec->data_addr); | 531 | (unsigned)ec->command_addr, (unsigned)ec->data_addr); |
562 | seq_printf(seq, "use global lock: %s\n", | 532 | seq_printf(seq, "use global lock:\t%s\n", |
563 | ec->global_lock ? "yes" : "no"); | 533 | ec->global_lock ? "yes" : "no"); |
564 | acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); | ||
565 | |||
566 | end: | 534 | end: |
567 | return 0; | 535 | return 0; |
568 | } | 536 | } |
@@ -619,154 +587,122 @@ static int acpi_ec_remove_fs(struct acpi_device *device) | |||
619 | /* -------------------------------------------------------------------------- | 587 | /* -------------------------------------------------------------------------- |
620 | Driver Interface | 588 | Driver Interface |
621 | -------------------------------------------------------------------------- */ | 589 | -------------------------------------------------------------------------- */ |
590 | static acpi_status | ||
591 | ec_parse_io_ports(struct acpi_resource *resource, void *context); | ||
592 | |||
593 | static acpi_status | ||
594 | ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval); | ||
595 | |||
596 | static struct acpi_ec *make_acpi_ec(void) | ||
597 | { | ||
598 | struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); | ||
599 | if (!ec) | ||
600 | return NULL; | ||
601 | |||
602 | atomic_set(&ec->query_pending, 1); | ||
603 | atomic_set(&ec->event_count, 1); | ||
604 | mutex_init(&ec->lock); | ||
605 | init_waitqueue_head(&ec->wait); | ||
606 | |||
607 | return ec; | ||
608 | } | ||
622 | 609 | ||
623 | static int acpi_ec_add(struct acpi_device *device) | 610 | static int acpi_ec_add(struct acpi_device *device) |
624 | { | 611 | { |
625 | int result = 0; | ||
626 | acpi_status status = AE_OK; | 612 | acpi_status status = AE_OK; |
627 | struct acpi_ec *ec = NULL; | 613 | struct acpi_ec *ec = NULL; |
628 | 614 | ||
629 | if (!device) | 615 | if (!device) |
630 | return -EINVAL; | 616 | return -EINVAL; |
631 | 617 | ||
632 | ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); | ||
633 | if (!ec) | ||
634 | return -ENOMEM; | ||
635 | |||
636 | ec->handle = device->handle; | ||
637 | ec->uid = -1; | ||
638 | mutex_init(&ec->lock); | ||
639 | atomic_set(&ec->query_pending, 0); | ||
640 | atomic_set(&ec->event_count, 1); | ||
641 | if (acpi_ec_mode == EC_INTR) { | ||
642 | atomic_set(&ec->leaving_burst, 1); | ||
643 | init_waitqueue_head(&ec->wait); | ||
644 | } | ||
645 | strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); | 618 | strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); |
646 | strcpy(acpi_device_class(device), ACPI_EC_CLASS); | 619 | strcpy(acpi_device_class(device), ACPI_EC_CLASS); |
647 | acpi_driver_data(device) = ec; | ||
648 | |||
649 | /* Use the global lock for all EC transactions? */ | ||
650 | acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock); | ||
651 | |||
652 | /* XXX we don't test uids, because on some boxes ecdt uid = 0, see: | ||
653 | http://bugzilla.kernel.org/show_bug.cgi?id=6111 */ | ||
654 | if (ec_ecdt) { | ||
655 | acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, | ||
656 | ACPI_ADR_SPACE_EC, | ||
657 | &acpi_ec_space_handler); | ||
658 | 620 | ||
659 | acpi_remove_gpe_handler(NULL, ec_ecdt->gpe, | 621 | ec = make_acpi_ec(); |
660 | &acpi_ec_gpe_handler); | 622 | if (!ec) |
623 | return -ENOMEM; | ||
661 | 624 | ||
662 | kfree(ec_ecdt); | 625 | status = ec_parse_device(device->handle, 0, ec, NULL); |
626 | if (status != AE_CTRL_TERMINATE) { | ||
627 | kfree(ec); | ||
628 | return -EINVAL; | ||
663 | } | 629 | } |
664 | 630 | ||
665 | /* Get GPE bit assignment (EC events). */ | 631 | /* Check if we found the boot EC */ |
666 | /* TODO: Add support for _GPE returning a package */ | 632 | if (boot_ec) { |
667 | status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe); | 633 | if (boot_ec->gpe == ec->gpe) { |
668 | if (ACPI_FAILURE(status)) { | 634 | /* We might have incorrect info for GL at boot time */ |
669 | ACPI_EXCEPTION((AE_INFO, status, | 635 | mutex_lock(&boot_ec->lock); |
670 | "Obtaining GPE bit assignment")); | 636 | boot_ec->global_lock = ec->global_lock; |
671 | result = -ENODEV; | 637 | mutex_unlock(&boot_ec->lock); |
672 | goto end; | 638 | kfree(ec); |
673 | } | 639 | ec = boot_ec; |
640 | } | ||
641 | } else | ||
642 | first_ec = ec; | ||
643 | ec->handle = device->handle; | ||
644 | acpi_driver_data(device) = ec; | ||
674 | 645 | ||
675 | result = acpi_ec_add_fs(device); | 646 | acpi_ec_add_fs(device); |
676 | if (result) | ||
677 | goto end; | ||
678 | 647 | ||
679 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.", | 648 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.", |
680 | acpi_device_name(device), acpi_device_bid(device), | 649 | acpi_device_name(device), acpi_device_bid(device), |
681 | (u32) ec->gpe)); | 650 | (u32) ec->gpe)); |
682 | 651 | ||
683 | if (!first_ec) | 652 | return 0; |
684 | first_ec = device; | ||
685 | |||
686 | end: | ||
687 | if (result) | ||
688 | kfree(ec); | ||
689 | |||
690 | return result; | ||
691 | } | 653 | } |
692 | 654 | ||
693 | static int acpi_ec_remove(struct acpi_device *device, int type) | 655 | static int acpi_ec_remove(struct acpi_device *device, int type) |
694 | { | 656 | { |
695 | struct acpi_ec *ec = NULL; | 657 | struct acpi_ec *ec; |
696 | 658 | ||
697 | if (!device) | 659 | if (!device) |
698 | return -EINVAL; | 660 | return -EINVAL; |
699 | 661 | ||
700 | ec = acpi_driver_data(device); | 662 | ec = acpi_driver_data(device); |
701 | |||
702 | acpi_ec_remove_fs(device); | 663 | acpi_ec_remove_fs(device); |
664 | acpi_driver_data(device) = NULL; | ||
665 | if (ec == first_ec) | ||
666 | first_ec = NULL; | ||
703 | 667 | ||
704 | kfree(ec); | 668 | /* Don't touch boot EC */ |
705 | 669 | if (boot_ec != ec) | |
670 | kfree(ec); | ||
706 | return 0; | 671 | return 0; |
707 | } | 672 | } |
708 | 673 | ||
709 | static acpi_status | 674 | static acpi_status |
710 | acpi_ec_io_ports(struct acpi_resource *resource, void *context) | 675 | ec_parse_io_ports(struct acpi_resource *resource, void *context) |
711 | { | 676 | { |
712 | struct acpi_ec *ec = (struct acpi_ec *)context; | 677 | struct acpi_ec *ec = context; |
713 | 678 | ||
714 | if (resource->type != ACPI_RESOURCE_TYPE_IO) { | 679 | if (resource->type != ACPI_RESOURCE_TYPE_IO) |
715 | return AE_OK; | 680 | return AE_OK; |
716 | } | ||
717 | 681 | ||
718 | /* | 682 | /* |
719 | * The first address region returned is the data port, and | 683 | * The first address region returned is the data port, and |
720 | * the second address region returned is the status/command | 684 | * the second address region returned is the status/command |
721 | * port. | 685 | * port. |
722 | */ | 686 | */ |
723 | if (ec->data_addr == 0) { | 687 | if (ec->data_addr == 0) |
724 | ec->data_addr = resource->data.io.minimum; | 688 | ec->data_addr = resource->data.io.minimum; |
725 | } else if (ec->command_addr == 0) { | 689 | else if (ec->command_addr == 0) |
726 | ec->command_addr = resource->data.io.minimum; | 690 | ec->command_addr = resource->data.io.minimum; |
727 | } else { | 691 | else |
728 | return AE_CTRL_TERMINATE; | 692 | return AE_CTRL_TERMINATE; |
729 | } | ||
730 | 693 | ||
731 | return AE_OK; | 694 | return AE_OK; |
732 | } | 695 | } |
733 | 696 | ||
734 | static int acpi_ec_start(struct acpi_device *device) | 697 | static int ec_install_handlers(struct acpi_ec *ec) |
735 | { | 698 | { |
736 | acpi_status status = AE_OK; | 699 | acpi_status status; |
737 | struct acpi_ec *ec = NULL; | ||
738 | |||
739 | if (!device) | ||
740 | return -EINVAL; | ||
741 | |||
742 | ec = acpi_driver_data(device); | ||
743 | |||
744 | if (!ec) | ||
745 | return -EINVAL; | ||
746 | |||
747 | /* | ||
748 | * Get I/O port addresses. Convert to GAS format. | ||
749 | */ | ||
750 | status = acpi_walk_resources(ec->handle, METHOD_NAME__CRS, | ||
751 | acpi_ec_io_ports, ec); | ||
752 | if (ACPI_FAILURE(status) || ec->command_addr == 0) { | ||
753 | ACPI_EXCEPTION((AE_INFO, status, | ||
754 | "Error getting I/O port addresses")); | ||
755 | return -ENODEV; | ||
756 | } | ||
757 | |||
758 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx", | ||
759 | ec->gpe, ec->command_addr, ec->data_addr)); | ||
760 | |||
761 | /* | ||
762 | * Install GPE handler | ||
763 | */ | ||
764 | status = acpi_install_gpe_handler(NULL, ec->gpe, | 700 | status = acpi_install_gpe_handler(NULL, ec->gpe, |
765 | ACPI_GPE_EDGE_TRIGGERED, | 701 | ACPI_GPE_EDGE_TRIGGERED, |
766 | &acpi_ec_gpe_handler, ec); | 702 | &acpi_ec_gpe_handler, ec); |
767 | if (ACPI_FAILURE(status)) { | 703 | if (ACPI_FAILURE(status)) |
768 | return -ENODEV; | 704 | return -ENODEV; |
769 | } | 705 | |
770 | acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); | 706 | acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); |
771 | acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); | 707 | acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); |
772 | 708 | ||
@@ -779,18 +715,49 @@ static int acpi_ec_start(struct acpi_device *device) | |||
779 | return -ENODEV; | 715 | return -ENODEV; |
780 | } | 716 | } |
781 | 717 | ||
782 | return AE_OK; | 718 | /* EC is fully operational, allow queries */ |
719 | atomic_set(&ec->query_pending, 0); | ||
720 | |||
721 | return 0; | ||
722 | } | ||
723 | |||
724 | static int acpi_ec_start(struct acpi_device *device) | ||
725 | { | ||
726 | struct acpi_ec *ec; | ||
727 | |||
728 | if (!device) | ||
729 | return -EINVAL; | ||
730 | |||
731 | ec = acpi_driver_data(device); | ||
732 | |||
733 | if (!ec) | ||
734 | return -EINVAL; | ||
735 | |||
736 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx", | ||
737 | ec->gpe, ec->command_addr, ec->data_addr)); | ||
738 | |||
739 | /* Boot EC is already working */ | ||
740 | if (ec == boot_ec) | ||
741 | return 0; | ||
742 | |||
743 | return ec_install_handlers(ec); | ||
783 | } | 744 | } |
784 | 745 | ||
785 | static int acpi_ec_stop(struct acpi_device *device, int type) | 746 | static int acpi_ec_stop(struct acpi_device *device, int type) |
786 | { | 747 | { |
787 | acpi_status status = AE_OK; | 748 | acpi_status status; |
788 | struct acpi_ec *ec = NULL; | 749 | struct acpi_ec *ec; |
789 | 750 | ||
790 | if (!device) | 751 | if (!device) |
791 | return -EINVAL; | 752 | return -EINVAL; |
792 | 753 | ||
793 | ec = acpi_driver_data(device); | 754 | ec = acpi_driver_data(device); |
755 | if (!ec) | ||
756 | return -EINVAL; | ||
757 | |||
758 | /* Don't touch boot EC */ | ||
759 | if (ec == boot_ec) | ||
760 | return 0; | ||
794 | 761 | ||
795 | status = acpi_remove_address_space_handler(ec->handle, | 762 | status = acpi_remove_address_space_handler(ec->handle, |
796 | ACPI_ADR_SPACE_EC, | 763 | ACPI_ADR_SPACE_EC, |
@@ -805,164 +772,67 @@ static int acpi_ec_stop(struct acpi_device *device, int type) | |||
805 | return 0; | 772 | return 0; |
806 | } | 773 | } |
807 | 774 | ||
808 | static acpi_status __init | 775 | static acpi_status |
809 | acpi_fake_ecdt_callback(acpi_handle handle, | 776 | ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) |
810 | u32 Level, void *context, void **retval) | ||
811 | { | 777 | { |
812 | acpi_status status; | 778 | acpi_status status; |
813 | 779 | ||
814 | mutex_init(&ec_ecdt->lock); | 780 | struct acpi_ec *ec = context; |
815 | atomic_set(&ec_ecdt->event_count, 1); | ||
816 | if (acpi_ec_mode == EC_INTR) { | ||
817 | init_waitqueue_head(&ec_ecdt->wait); | ||
818 | } | ||
819 | status = acpi_walk_resources(handle, METHOD_NAME__CRS, | 781 | status = acpi_walk_resources(handle, METHOD_NAME__CRS, |
820 | acpi_ec_io_ports, ec_ecdt); | 782 | ec_parse_io_ports, ec); |
821 | if (ACPI_FAILURE(status)) | 783 | if (ACPI_FAILURE(status)) |
822 | return status; | 784 | return status; |
823 | 785 | ||
824 | ec_ecdt->uid = -1; | 786 | /* Get GPE bit assignment (EC events). */ |
825 | acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid); | 787 | /* TODO: Add support for _GPE returning a package */ |
826 | 788 | status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe); | |
827 | status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe); | ||
828 | if (ACPI_FAILURE(status)) | 789 | if (ACPI_FAILURE(status)) |
829 | return status; | 790 | return status; |
830 | ec_ecdt->global_lock = TRUE; | ||
831 | ec_ecdt->handle = handle; | ||
832 | |||
833 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx", | ||
834 | ec_ecdt->gpe, ec_ecdt->command_addr, | ||
835 | ec_ecdt->data_addr)); | ||
836 | 791 | ||
837 | return AE_CTRL_TERMINATE; | 792 | /* Use the global lock for all EC transactions? */ |
838 | } | 793 | acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); |
839 | |||
840 | /* | ||
841 | * Some BIOS (such as some from Gateway laptops) access EC region very early | ||
842 | * such as in BAT0._INI or EC._INI before an EC device is found and | ||
843 | * do not provide an ECDT. According to ACPI spec, ECDT isn't mandatorily | ||
844 | * required, but if EC regison is accessed early, it is required. | ||
845 | * The routine tries to workaround the BIOS bug by pre-scan EC device | ||
846 | * It assumes that _CRS, _HID, _GPE, _UID methods of EC don't touch any | ||
847 | * op region (since _REG isn't invoked yet). The assumption is true for | ||
848 | * all systems found. | ||
849 | */ | ||
850 | static int __init acpi_ec_fake_ecdt(void) | ||
851 | { | ||
852 | acpi_status status; | ||
853 | int ret = 0; | ||
854 | 794 | ||
855 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Try to make an fake ECDT")); | 795 | ec->handle = handle; |
856 | 796 | ||
857 | ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); | 797 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx", |
858 | if (!ec_ecdt) { | 798 | ec->gpe, ec->command_addr, ec->data_addr)); |
859 | ret = -ENOMEM; | ||
860 | goto error; | ||
861 | } | ||
862 | 799 | ||
863 | status = acpi_get_devices(ACPI_EC_HID, | 800 | return AE_CTRL_TERMINATE; |
864 | acpi_fake_ecdt_callback, NULL, NULL); | ||
865 | if (ACPI_FAILURE(status)) { | ||
866 | kfree(ec_ecdt); | ||
867 | ec_ecdt = NULL; | ||
868 | ret = -ENODEV; | ||
869 | ACPI_EXCEPTION((AE_INFO, status, "Can't make an fake ECDT")); | ||
870 | goto error; | ||
871 | } | ||
872 | return 0; | ||
873 | error: | ||
874 | return ret; | ||
875 | } | 801 | } |
876 | 802 | ||
877 | static int __init acpi_ec_get_real_ecdt(void) | 803 | int __init acpi_ec_ecdt_probe(void) |
878 | { | 804 | { |
805 | int ret; | ||
879 | acpi_status status; | 806 | acpi_status status; |
880 | struct acpi_table_ecdt *ecdt_ptr; | 807 | struct acpi_table_ecdt *ecdt_ptr; |
881 | 808 | ||
882 | status = acpi_get_table(ACPI_SIG_ECDT, 1, | 809 | boot_ec = make_acpi_ec(); |
883 | (struct acpi_table_header **)&ecdt_ptr); | 810 | if (!boot_ec) |
884 | if (ACPI_FAILURE(status)) | 811 | return -ENOMEM; |
885 | return -ENODEV; | ||
886 | |||
887 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT")); | ||
888 | |||
889 | /* | 812 | /* |
890 | * Generate a temporary ec context to use until the namespace is scanned | 813 | * Generate a boot ec context |
891 | */ | 814 | */ |
892 | ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); | ||
893 | if (!ec_ecdt) | ||
894 | return -ENOMEM; | ||
895 | 815 | ||
896 | mutex_init(&ec_ecdt->lock); | 816 | status = acpi_get_table(ACPI_SIG_ECDT, 1, |
897 | atomic_set(&ec_ecdt->event_count, 1); | 817 | (struct acpi_table_header **)&ecdt_ptr); |
898 | if (acpi_ec_mode == EC_INTR) { | 818 | if (ACPI_FAILURE(status)) |
899 | init_waitqueue_head(&ec_ecdt->wait); | ||
900 | } | ||
901 | ec_ecdt->command_addr = ecdt_ptr->control.address; | ||
902 | ec_ecdt->data_addr = ecdt_ptr->data.address; | ||
903 | ec_ecdt->gpe = ecdt_ptr->gpe; | ||
904 | /* use the GL just to be safe */ | ||
905 | ec_ecdt->global_lock = TRUE; | ||
906 | ec_ecdt->uid = ecdt_ptr->uid; | ||
907 | |||
908 | status = acpi_get_handle(NULL, ecdt_ptr->id, &ec_ecdt->handle); | ||
909 | if (ACPI_FAILURE(status)) { | ||
910 | goto error; | 819 | goto error; |
911 | } | ||
912 | |||
913 | return 0; | ||
914 | error: | ||
915 | ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT")); | ||
916 | kfree(ec_ecdt); | ||
917 | ec_ecdt = NULL; | ||
918 | 820 | ||
919 | return -ENODEV; | 821 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT")); |
920 | } | ||
921 | |||
922 | static int __initdata acpi_fake_ecdt_enabled; | ||
923 | int __init acpi_ec_ecdt_probe(void) | ||
924 | { | ||
925 | acpi_status status; | ||
926 | int ret; | ||
927 | 822 | ||
928 | ret = acpi_ec_get_real_ecdt(); | 823 | boot_ec->command_addr = ecdt_ptr->control.address; |
929 | /* Try to make a fake ECDT */ | 824 | boot_ec->data_addr = ecdt_ptr->data.address; |
930 | if (ret && acpi_fake_ecdt_enabled) { | 825 | boot_ec->gpe = ecdt_ptr->gpe; |
931 | ret = acpi_ec_fake_ecdt(); | 826 | boot_ec->handle = ACPI_ROOT_OBJECT; |
932 | } | ||
933 | 827 | ||
934 | if (ret) | 828 | ret = ec_install_handlers(boot_ec); |
829 | if (!ret) { | ||
830 | first_ec = boot_ec; | ||
935 | return 0; | 831 | return 0; |
936 | |||
937 | /* | ||
938 | * Install GPE handler | ||
939 | */ | ||
940 | status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe, | ||
941 | ACPI_GPE_EDGE_TRIGGERED, | ||
942 | &acpi_ec_gpe_handler, ec_ecdt); | ||
943 | if (ACPI_FAILURE(status)) { | ||
944 | goto error; | ||
945 | } | 832 | } |
946 | acpi_set_gpe_type(NULL, ec_ecdt->gpe, ACPI_GPE_TYPE_RUNTIME); | ||
947 | acpi_enable_gpe(NULL, ec_ecdt->gpe, ACPI_NOT_ISR); | ||
948 | |||
949 | status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, | ||
950 | ACPI_ADR_SPACE_EC, | ||
951 | &acpi_ec_space_handler, | ||
952 | &acpi_ec_space_setup, | ||
953 | ec_ecdt); | ||
954 | if (ACPI_FAILURE(status)) { | ||
955 | acpi_remove_gpe_handler(NULL, ec_ecdt->gpe, | ||
956 | &acpi_ec_gpe_handler); | ||
957 | goto error; | ||
958 | } | ||
959 | |||
960 | return 0; | ||
961 | |||
962 | error: | 833 | error: |
963 | ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT")); | 834 | kfree(boot_ec); |
964 | kfree(ec_ecdt); | 835 | boot_ec = NULL; |
965 | ec_ecdt = NULL; | ||
966 | 836 | ||
967 | return -ENODEV; | 837 | return -ENODEV; |
968 | } | 838 | } |
@@ -1003,13 +873,6 @@ static void __exit acpi_ec_exit(void) | |||
1003 | } | 873 | } |
1004 | #endif /* 0 */ | 874 | #endif /* 0 */ |
1005 | 875 | ||
1006 | static int __init acpi_fake_ecdt_setup(char *str) | ||
1007 | { | ||
1008 | acpi_fake_ecdt_enabled = 1; | ||
1009 | return 1; | ||
1010 | } | ||
1011 | |||
1012 | __setup("acpi_fake_ecdt", acpi_fake_ecdt_setup); | ||
1013 | static int __init acpi_ec_set_intr_mode(char *str) | 876 | static int __init acpi_ec_set_intr_mode(char *str) |
1014 | { | 877 | { |
1015 | int intr; | 878 | int intr; |
@@ -1017,12 +880,8 @@ static int __init acpi_ec_set_intr_mode(char *str) | |||
1017 | if (!get_option(&str, &intr)) | 880 | if (!get_option(&str, &intr)) |
1018 | return 0; | 881 | return 0; |
1019 | 882 | ||
1020 | if (intr) { | 883 | acpi_ec_mode = (intr) ? EC_INTR : EC_POLL; |
1021 | acpi_ec_mode = EC_INTR; | 884 | |
1022 | } else { | ||
1023 | acpi_ec_mode = EC_POLL; | ||
1024 | } | ||
1025 | acpi_ec_driver.ops.add = acpi_ec_add; | ||
1026 | printk(KERN_NOTICE PREFIX "%s mode.\n", intr ? "interrupt" : "polling"); | 885 | printk(KERN_NOTICE PREFIX "%s mode.\n", intr ? "interrupt" : "polling"); |
1027 | 886 | ||
1028 | return 1; | 887 | return 1; |