diff options
Diffstat (limited to 'drivers/acpi/ec.c')
-rw-r--r-- | drivers/acpi/ec.c | 175 |
1 files changed, 125 insertions, 50 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index ffb8361bc1bf..10e851021eca 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/proc_fs.h> | 34 | #include <linux/proc_fs.h> |
35 | #include <linux/seq_file.h> | 35 | #include <linux/seq_file.h> |
36 | #include <linux/interrupt.h> | 36 | #include <linux/interrupt.h> |
37 | #include <linux/list.h> | ||
37 | #include <asm/io.h> | 38 | #include <asm/io.h> |
38 | #include <acpi/acpi_bus.h> | 39 | #include <acpi/acpi_bus.h> |
39 | #include <acpi/acpi_drivers.h> | 40 | #include <acpi/acpi_drivers.h> |
@@ -43,6 +44,7 @@ | |||
43 | #define ACPI_EC_HID "PNP0C09" | 44 | #define ACPI_EC_HID "PNP0C09" |
44 | #define ACPI_EC_DEVICE_NAME "Embedded Controller" | 45 | #define ACPI_EC_DEVICE_NAME "Embedded Controller" |
45 | #define ACPI_EC_FILE_INFO "info" | 46 | #define ACPI_EC_FILE_INFO "info" |
47 | |||
46 | #undef PREFIX | 48 | #undef PREFIX |
47 | #define PREFIX "ACPI: EC: " | 49 | #define PREFIX "ACPI: EC: " |
48 | 50 | ||
@@ -60,6 +62,7 @@ enum ec_command { | |||
60 | ACPI_EC_BURST_DISABLE = 0x83, | 62 | ACPI_EC_BURST_DISABLE = 0x83, |
61 | ACPI_EC_COMMAND_QUERY = 0x84, | 63 | ACPI_EC_COMMAND_QUERY = 0x84, |
62 | }; | 64 | }; |
65 | |||
63 | /* EC events */ | 66 | /* EC events */ |
64 | enum ec_event { | 67 | enum ec_event { |
65 | ACPI_EC_EVENT_OBF_1 = 1, /* Output buffer full */ | 68 | ACPI_EC_EVENT_OBF_1 = 1, /* Output buffer full */ |
@@ -93,6 +96,16 @@ static struct acpi_driver acpi_ec_driver = { | |||
93 | 96 | ||
94 | /* If we find an EC via the ECDT, we need to keep a ptr to its context */ | 97 | /* If we find an EC via the ECDT, we need to keep a ptr to its context */ |
95 | /* External interfaces use first EC only, so remember */ | 98 | /* External interfaces use first EC only, so remember */ |
99 | typedef int (*acpi_ec_query_func) (void *data); | ||
100 | |||
101 | struct acpi_ec_query_handler { | ||
102 | struct list_head node; | ||
103 | acpi_ec_query_func func; | ||
104 | acpi_handle handle; | ||
105 | void *data; | ||
106 | u8 query_bit; | ||
107 | }; | ||
108 | |||
96 | static struct acpi_ec { | 109 | static struct acpi_ec { |
97 | acpi_handle handle; | 110 | acpi_handle handle; |
98 | unsigned long gpe; | 111 | unsigned long gpe; |
@@ -103,6 +116,7 @@ static struct acpi_ec { | |||
103 | atomic_t query_pending; | 116 | atomic_t query_pending; |
104 | atomic_t event_count; | 117 | atomic_t event_count; |
105 | wait_queue_head_t wait; | 118 | wait_queue_head_t wait; |
119 | struct list_head list; | ||
106 | } *boot_ec, *first_ec; | 120 | } *boot_ec, *first_ec; |
107 | 121 | ||
108 | /* -------------------------------------------------------------------------- | 122 | /* -------------------------------------------------------------------------- |
@@ -393,21 +407,67 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data) | |||
393 | /* -------------------------------------------------------------------------- | 407 | /* -------------------------------------------------------------------------- |
394 | Event Management | 408 | Event Management |
395 | -------------------------------------------------------------------------- */ | 409 | -------------------------------------------------------------------------- */ |
410 | int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, | ||
411 | acpi_handle handle, acpi_ec_query_func func, | ||
412 | void *data) | ||
413 | { | ||
414 | struct acpi_ec_query_handler *handler = | ||
415 | kzalloc(sizeof(struct acpi_ec_query_handler), GFP_KERNEL); | ||
416 | if (!handler) | ||
417 | return -ENOMEM; | ||
418 | |||
419 | handler->query_bit = query_bit; | ||
420 | handler->handle = handle; | ||
421 | handler->func = func; | ||
422 | handler->data = data; | ||
423 | mutex_lock(&ec->lock); | ||
424 | list_add_tail(&handler->node, &ec->list); | ||
425 | mutex_unlock(&ec->lock); | ||
426 | return 0; | ||
427 | } | ||
428 | |||
429 | EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler); | ||
430 | |||
431 | void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) | ||
432 | { | ||
433 | struct acpi_ec_query_handler *handler; | ||
434 | mutex_lock(&ec->lock); | ||
435 | list_for_each_entry(handler, &ec->list, node) { | ||
436 | if (query_bit == handler->query_bit) { | ||
437 | list_del(&handler->node); | ||
438 | kfree(handler); | ||
439 | break; | ||
440 | } | ||
441 | } | ||
442 | mutex_unlock(&ec->lock); | ||
443 | } | ||
444 | |||
445 | EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); | ||
396 | 446 | ||
397 | static void acpi_ec_gpe_query(void *ec_cxt) | 447 | static void acpi_ec_gpe_query(void *ec_cxt) |
398 | { | 448 | { |
399 | struct acpi_ec *ec = ec_cxt; | 449 | struct acpi_ec *ec = ec_cxt; |
400 | u8 value = 0; | 450 | u8 value = 0; |
401 | char object_name[8]; | 451 | struct acpi_ec_query_handler *handler, copy; |
402 | 452 | ||
403 | if (!ec || acpi_ec_query(ec, &value)) | 453 | if (!ec || acpi_ec_query(ec, &value)) |
404 | return; | 454 | return; |
405 | 455 | mutex_lock(&ec->lock); | |
406 | snprintf(object_name, 8, "_Q%2.2X", value); | 456 | list_for_each_entry(handler, &ec->list, node) { |
407 | 457 | if (value == handler->query_bit) { | |
408 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s", object_name)); | 458 | /* have custom handler for this bit */ |
409 | 459 | memcpy(©, handler, sizeof(copy)); | |
410 | acpi_evaluate_object(ec->handle, object_name, NULL, NULL); | 460 | mutex_unlock(&ec->lock); |
461 | if (copy.func) { | ||
462 | copy.func(copy.data); | ||
463 | } else if (copy.handle) { | ||
464 | acpi_evaluate_object(copy.handle, NULL, NULL, NULL); | ||
465 | } | ||
466 | return; | ||
467 | } | ||
468 | } | ||
469 | mutex_unlock(&ec->lock); | ||
470 | printk(KERN_ERR PREFIX "Handler for query 0x%x is not found!\n", value); | ||
411 | } | 471 | } |
412 | 472 | ||
413 | static u32 acpi_ec_gpe_handler(void *data) | 473 | static u32 acpi_ec_gpe_handler(void *data) |
@@ -426,8 +486,7 @@ static u32 acpi_ec_gpe_handler(void *data) | |||
426 | if ((value & ACPI_EC_FLAG_SCI) && !atomic_read(&ec->query_pending)) { | 486 | if ((value & ACPI_EC_FLAG_SCI) && !atomic_read(&ec->query_pending)) { |
427 | atomic_set(&ec->query_pending, 1); | 487 | atomic_set(&ec->query_pending, 1); |
428 | status = | 488 | status = |
429 | acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, | 489 | acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, ec); |
430 | ec); | ||
431 | } | 490 | } |
432 | 491 | ||
433 | return status == AE_OK ? | 492 | return status == AE_OK ? |
@@ -574,9 +633,6 @@ static int acpi_ec_remove_fs(struct acpi_device *device) | |||
574 | static acpi_status | 633 | static acpi_status |
575 | ec_parse_io_ports(struct acpi_resource *resource, void *context); | 634 | ec_parse_io_ports(struct acpi_resource *resource, void *context); |
576 | 635 | ||
577 | static acpi_status | ||
578 | ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval); | ||
579 | |||
580 | static struct acpi_ec *make_acpi_ec(void) | 636 | static struct acpi_ec *make_acpi_ec(void) |
581 | { | 637 | { |
582 | struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); | 638 | struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); |
@@ -587,13 +643,52 @@ static struct acpi_ec *make_acpi_ec(void) | |||
587 | atomic_set(&ec->event_count, 1); | 643 | atomic_set(&ec->event_count, 1); |
588 | mutex_init(&ec->lock); | 644 | mutex_init(&ec->lock); |
589 | init_waitqueue_head(&ec->wait); | 645 | init_waitqueue_head(&ec->wait); |
646 | INIT_LIST_HEAD(&ec->list); | ||
590 | 647 | ||
591 | return ec; | 648 | return ec; |
592 | } | 649 | } |
593 | 650 | ||
651 | static acpi_status | ||
652 | acpi_ec_register_query_methods(acpi_handle handle, u32 level, | ||
653 | void *context, void **return_value) | ||
654 | { | ||
655 | struct acpi_namespace_node *node = handle; | ||
656 | struct acpi_ec *ec = context; | ||
657 | int value = 0; | ||
658 | if (sscanf(node->name.ascii, "_Q%x", &value) == 1) { | ||
659 | acpi_ec_add_query_handler(ec, value, handle, NULL, NULL); | ||
660 | } | ||
661 | return AE_OK; | ||
662 | } | ||
663 | |||
664 | static int ec_parse_device(struct acpi_ec *ec, acpi_handle handle) | ||
665 | { | ||
666 | if (ACPI_FAILURE(acpi_walk_resources(handle, METHOD_NAME__CRS, | ||
667 | ec_parse_io_ports, ec))) | ||
668 | return -EINVAL; | ||
669 | |||
670 | /* Get GPE bit assignment (EC events). */ | ||
671 | /* TODO: Add support for _GPE returning a package */ | ||
672 | if (ACPI_FAILURE(acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe))) | ||
673 | return -EINVAL; | ||
674 | |||
675 | /* Use the global lock for all EC transactions? */ | ||
676 | acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); | ||
677 | |||
678 | /* Find and register all query methods */ | ||
679 | acpi_walk_namespace(ACPI_TYPE_METHOD, handle, 1, | ||
680 | acpi_ec_register_query_methods, ec, NULL); | ||
681 | |||
682 | ec->handle = handle; | ||
683 | |||
684 | printk(KERN_INFO PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx", | ||
685 | ec->gpe, ec->command_addr, ec->data_addr); | ||
686 | |||
687 | return 0; | ||
688 | } | ||
689 | |||
594 | static int acpi_ec_add(struct acpi_device *device) | 690 | static int acpi_ec_add(struct acpi_device *device) |
595 | { | 691 | { |
596 | acpi_status status = AE_OK; | ||
597 | struct acpi_ec *ec = NULL; | 692 | struct acpi_ec *ec = NULL; |
598 | 693 | ||
599 | if (!device) | 694 | if (!device) |
@@ -606,8 +701,7 @@ static int acpi_ec_add(struct acpi_device *device) | |||
606 | if (!ec) | 701 | if (!ec) |
607 | return -ENOMEM; | 702 | return -ENOMEM; |
608 | 703 | ||
609 | status = ec_parse_device(device->handle, 0, ec, NULL); | 704 | if (ec_parse_device(ec, device->handle)) { |
610 | if (status != AE_CTRL_TERMINATE) { | ||
611 | kfree(ec); | 705 | kfree(ec); |
612 | return -EINVAL; | 706 | return -EINVAL; |
613 | } | 707 | } |
@@ -618,6 +712,8 @@ static int acpi_ec_add(struct acpi_device *device) | |||
618 | /* We might have incorrect info for GL at boot time */ | 712 | /* We might have incorrect info for GL at boot time */ |
619 | mutex_lock(&boot_ec->lock); | 713 | mutex_lock(&boot_ec->lock); |
620 | boot_ec->global_lock = ec->global_lock; | 714 | boot_ec->global_lock = ec->global_lock; |
715 | /* Copy handlers from new ec into boot ec */ | ||
716 | list_splice(&ec->list, &boot_ec->list); | ||
621 | mutex_unlock(&boot_ec->lock); | 717 | mutex_unlock(&boot_ec->lock); |
622 | kfree(ec); | 718 | kfree(ec); |
623 | ec = boot_ec; | 719 | ec = boot_ec; |
@@ -628,18 +724,24 @@ static int acpi_ec_add(struct acpi_device *device) | |||
628 | acpi_driver_data(device) = ec; | 724 | acpi_driver_data(device) = ec; |
629 | 725 | ||
630 | acpi_ec_add_fs(device); | 726 | acpi_ec_add_fs(device); |
631 | |||
632 | return 0; | 727 | return 0; |
633 | } | 728 | } |
634 | 729 | ||
635 | static int acpi_ec_remove(struct acpi_device *device, int type) | 730 | static int acpi_ec_remove(struct acpi_device *device, int type) |
636 | { | 731 | { |
637 | struct acpi_ec *ec; | 732 | struct acpi_ec *ec; |
733 | struct acpi_ec_query_handler *handler; | ||
638 | 734 | ||
639 | if (!device) | 735 | if (!device) |
640 | return -EINVAL; | 736 | return -EINVAL; |
641 | 737 | ||
642 | ec = acpi_driver_data(device); | 738 | ec = acpi_driver_data(device); |
739 | mutex_lock(&ec->lock); | ||
740 | list_for_each_entry(handler, &ec->list, node) { | ||
741 | list_del(&handler->node); | ||
742 | kfree(handler); | ||
743 | } | ||
744 | mutex_unlock(&ec->lock); | ||
643 | acpi_ec_remove_fs(device); | 745 | acpi_ec_remove_fs(device); |
644 | acpi_driver_data(device) = NULL; | 746 | acpi_driver_data(device) = NULL; |
645 | if (ec == first_ec) | 747 | if (ec == first_ec) |
@@ -695,15 +797,13 @@ static int ec_install_handlers(struct acpi_ec *ec) | |||
695 | return -ENODEV; | 797 | return -ENODEV; |
696 | } | 798 | } |
697 | 799 | ||
698 | /* EC is fully operational, allow queries */ | ||
699 | atomic_set(&ec->query_pending, 0); | ||
700 | |||
701 | return 0; | 800 | return 0; |
702 | } | 801 | } |
703 | 802 | ||
704 | static int acpi_ec_start(struct acpi_device *device) | 803 | static int acpi_ec_start(struct acpi_device *device) |
705 | { | 804 | { |
706 | struct acpi_ec *ec; | 805 | struct acpi_ec *ec; |
806 | int ret = 0; | ||
707 | 807 | ||
708 | if (!device) | 808 | if (!device) |
709 | return -EINVAL; | 809 | return -EINVAL; |
@@ -714,10 +814,13 @@ static int acpi_ec_start(struct acpi_device *device) | |||
714 | return -EINVAL; | 814 | return -EINVAL; |
715 | 815 | ||
716 | /* Boot EC is already working */ | 816 | /* Boot EC is already working */ |
717 | if (ec == boot_ec) | 817 | if (ec != boot_ec) |
718 | return 0; | 818 | ret = ec_install_handlers(ec); |
819 | |||
820 | /* EC is fully operational, allow queries */ | ||
821 | atomic_set(&ec->query_pending, 0); | ||
719 | 822 | ||
720 | return ec_install_handlers(ec); | 823 | return ret; |
721 | } | 824 | } |
722 | 825 | ||
723 | static int acpi_ec_stop(struct acpi_device *device, int type) | 826 | static int acpi_ec_stop(struct acpi_device *device, int type) |
@@ -749,34 +852,6 @@ static int acpi_ec_stop(struct acpi_device *device, int type) | |||
749 | return 0; | 852 | return 0; |
750 | } | 853 | } |
751 | 854 | ||
752 | static acpi_status | ||
753 | ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) | ||
754 | { | ||
755 | acpi_status status; | ||
756 | |||
757 | struct acpi_ec *ec = context; | ||
758 | status = acpi_walk_resources(handle, METHOD_NAME__CRS, | ||
759 | ec_parse_io_ports, ec); | ||
760 | if (ACPI_FAILURE(status)) | ||
761 | return status; | ||
762 | |||
763 | /* Get GPE bit assignment (EC events). */ | ||
764 | /* TODO: Add support for _GPE returning a package */ | ||
765 | status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe); | ||
766 | if (ACPI_FAILURE(status)) | ||
767 | return status; | ||
768 | |||
769 | /* Use the global lock for all EC transactions? */ | ||
770 | acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); | ||
771 | |||
772 | ec->handle = handle; | ||
773 | |||
774 | printk(KERN_INFO PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx", | ||
775 | ec->gpe, ec->command_addr, ec->data_addr); | ||
776 | |||
777 | return AE_CTRL_TERMINATE; | ||
778 | } | ||
779 | |||
780 | int __init acpi_ec_ecdt_probe(void) | 855 | int __init acpi_ec_ecdt_probe(void) |
781 | { | 856 | { |
782 | int ret; | 857 | int ret; |