diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/ec.c | 205 |
1 files changed, 109 insertions, 96 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 399cedf2cfa6..c9dcf9a2a469 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -604,74 +604,73 @@ static int acpi_ec_remove_fs(struct acpi_device *device) | |||
604 | /* -------------------------------------------------------------------------- | 604 | /* -------------------------------------------------------------------------- |
605 | Driver Interface | 605 | Driver Interface |
606 | -------------------------------------------------------------------------- */ | 606 | -------------------------------------------------------------------------- */ |
607 | static acpi_status | ||
608 | ec_parse_io_ports(struct acpi_resource *resource, void *context); | ||
609 | |||
610 | static acpi_status | ||
611 | ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval); | ||
612 | |||
613 | static struct acpi_ec *make_acpi_ec(void) | ||
614 | { | ||
615 | struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); | ||
616 | if (!ec) | ||
617 | return NULL; | ||
618 | |||
619 | atomic_set(&ec->query_pending, 0); | ||
620 | atomic_set(&ec->event_count, 1); | ||
621 | mutex_init(&ec->lock); | ||
622 | init_waitqueue_head(&ec->wait); | ||
623 | |||
624 | return ec; | ||
625 | } | ||
607 | 626 | ||
608 | static int acpi_ec_add(struct acpi_device *device) | 627 | static int acpi_ec_add(struct acpi_device *device) |
609 | { | 628 | { |
610 | int result = 0; | ||
611 | acpi_status status = AE_OK; | 629 | acpi_status status = AE_OK; |
612 | struct acpi_ec *ec = NULL; | 630 | struct acpi_ec *ec = NULL; |
613 | 631 | ||
614 | if (!device) | 632 | if (!device) |
615 | return -EINVAL; | 633 | return -EINVAL; |
616 | 634 | ||
617 | ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); | 635 | strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); |
636 | strcpy(acpi_device_class(device), ACPI_EC_CLASS); | ||
637 | |||
638 | ec = make_acpi_ec(); | ||
618 | if (!ec) | 639 | if (!ec) |
619 | return -ENOMEM; | 640 | return -ENOMEM; |
620 | 641 | ||
621 | ec->handle = device->handle; | 642 | status = ec_parse_device(device->handle, 0, ec, NULL); |
622 | ec->uid = -1; | 643 | if (status != AE_CTRL_TERMINATE) { |
623 | mutex_init(&ec->lock); | 644 | kfree(ec); |
624 | atomic_set(&ec->query_pending, 0); | 645 | return -EINVAL; |
625 | atomic_set(&ec->event_count, 1); | ||
626 | if (acpi_ec_mode == EC_INTR) { | ||
627 | init_waitqueue_head(&ec->wait); | ||
628 | } | 646 | } |
629 | strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); | ||
630 | strcpy(acpi_device_class(device), ACPI_EC_CLASS); | ||
631 | acpi_driver_data(device) = ec; | ||
632 | 647 | ||
633 | /* Use the global lock for all EC transactions? */ | 648 | /* Check if we found the boot EC */ |
634 | acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock); | ||
635 | |||
636 | /* XXX we don't test uids, because on some boxes ecdt uid = 0, see: | ||
637 | http://bugzilla.kernel.org/show_bug.cgi?id=6111 */ | ||
638 | if (ec_ecdt) { | 649 | if (ec_ecdt) { |
639 | acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, | 650 | if (ec_ecdt->gpe == ec->gpe) { |
640 | ACPI_ADR_SPACE_EC, | 651 | /* We might have incorrect info for GL at boot time */ |
641 | &acpi_ec_space_handler); | 652 | mutex_lock(&ec_ecdt->lock); |
653 | ec_ecdt->global_lock = ec->global_lock; | ||
654 | mutex_unlock(&ec_ecdt->lock); | ||
655 | kfree(ec); | ||
656 | ec = ec_ecdt; | ||
657 | } | ||
658 | } | ||
642 | 659 | ||
643 | acpi_remove_gpe_handler(NULL, ec_ecdt->gpe, | 660 | ec->handle = device->handle; |
644 | &acpi_ec_gpe_handler); | ||
645 | 661 | ||
646 | kfree(ec_ecdt); | 662 | acpi_driver_data(device) = ec; |
647 | } | ||
648 | 663 | ||
649 | /* Get GPE bit assignment (EC events). */ | 664 | if (!first_ec) |
650 | /* TODO: Add support for _GPE returning a package */ | 665 | first_ec = device; |
651 | status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe); | ||
652 | if (ACPI_FAILURE(status)) { | ||
653 | ACPI_EXCEPTION((AE_INFO, status, | ||
654 | "Obtaining GPE bit assignment")); | ||
655 | result = -ENODEV; | ||
656 | goto end; | ||
657 | } | ||
658 | 666 | ||
659 | result = acpi_ec_add_fs(device); | 667 | acpi_ec_add_fs(device); |
660 | if (result) | ||
661 | goto end; | ||
662 | 668 | ||
663 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.", | 669 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.", |
664 | acpi_device_name(device), acpi_device_bid(device), | 670 | acpi_device_name(device), acpi_device_bid(device), |
665 | (u32) ec->gpe)); | 671 | (u32) ec->gpe)); |
666 | 672 | ||
667 | if (!first_ec) | 673 | return 0; |
668 | first_ec = device; | ||
669 | |||
670 | end: | ||
671 | if (result) | ||
672 | kfree(ec); | ||
673 | |||
674 | return result; | ||
675 | } | 674 | } |
676 | 675 | ||
677 | static int acpi_ec_remove(struct acpi_device *device, int type) | 676 | static int acpi_ec_remove(struct acpi_device *device, int type) |
@@ -685,13 +684,19 @@ static int acpi_ec_remove(struct acpi_device *device, int type) | |||
685 | 684 | ||
686 | acpi_ec_remove_fs(device); | 685 | acpi_ec_remove_fs(device); |
687 | 686 | ||
688 | kfree(ec); | 687 | acpi_driver_data(device) = NULL; |
688 | if (device == first_ec) | ||
689 | first_ec = NULL; | ||
690 | |||
691 | /* Don't touch boot EC */ | ||
692 | if (ec_ecdt != ec) | ||
693 | kfree(ec); | ||
689 | 694 | ||
690 | return 0; | 695 | return 0; |
691 | } | 696 | } |
692 | 697 | ||
693 | static acpi_status | 698 | static acpi_status |
694 | acpi_ec_io_ports(struct acpi_resource *resource, void *context) | 699 | ec_parse_io_ports(struct acpi_resource *resource, void *context) |
695 | { | 700 | { |
696 | struct acpi_ec *ec = context; | 701 | struct acpi_ec *ec = context; |
697 | 702 | ||
@@ -717,9 +722,10 @@ acpi_ec_io_ports(struct acpi_resource *resource, void *context) | |||
717 | 722 | ||
718 | static int ec_install_handlers(struct acpi_ec *ec) | 723 | static int ec_install_handlers(struct acpi_ec *ec) |
719 | { | 724 | { |
720 | acpi_status status = acpi_install_gpe_handler(NULL, ec->gpe, | 725 | acpi_status status; |
721 | ACPI_GPE_EDGE_TRIGGERED, | 726 | status = acpi_install_gpe_handler(NULL, ec->gpe, |
722 | &acpi_ec_gpe_handler, ec); | 727 | ACPI_GPE_EDGE_TRIGGERED, |
728 | &acpi_ec_gpe_handler, ec); | ||
723 | if (ACPI_FAILURE(status)) | 729 | if (ACPI_FAILURE(status)) |
724 | return -ENODEV; | 730 | return -ENODEV; |
725 | acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); | 731 | acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); |
@@ -739,8 +745,7 @@ static int ec_install_handlers(struct acpi_ec *ec) | |||
739 | 745 | ||
740 | static int acpi_ec_start(struct acpi_device *device) | 746 | static int acpi_ec_start(struct acpi_device *device) |
741 | { | 747 | { |
742 | acpi_status status = AE_OK; | 748 | struct acpi_ec *ec; |
743 | struct acpi_ec *ec = NULL; | ||
744 | 749 | ||
745 | if (!device) | 750 | if (!device) |
746 | return -EINVAL; | 751 | return -EINVAL; |
@@ -750,32 +755,31 @@ static int acpi_ec_start(struct acpi_device *device) | |||
750 | if (!ec) | 755 | if (!ec) |
751 | return -EINVAL; | 756 | return -EINVAL; |
752 | 757 | ||
753 | /* | ||
754 | * Get I/O port addresses. Convert to GAS format. | ||
755 | */ | ||
756 | status = acpi_walk_resources(ec->handle, METHOD_NAME__CRS, | ||
757 | acpi_ec_io_ports, ec); | ||
758 | if (ACPI_FAILURE(status) || ec->command_addr == 0) { | ||
759 | ACPI_EXCEPTION((AE_INFO, status, | ||
760 | "Error getting I/O port addresses")); | ||
761 | return -ENODEV; | ||
762 | } | ||
763 | |||
764 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx", | 758 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx", |
765 | ec->gpe, ec->command_addr, ec->data_addr)); | 759 | ec->gpe, ec->command_addr, ec->data_addr)); |
766 | 760 | ||
761 | /* Boot EC is already working */ | ||
762 | if (ec == ec_ecdt) | ||
763 | return 0; | ||
764 | |||
767 | return ec_install_handlers(ec); | 765 | return ec_install_handlers(ec); |
768 | } | 766 | } |
769 | 767 | ||
770 | static int acpi_ec_stop(struct acpi_device *device, int type) | 768 | static int acpi_ec_stop(struct acpi_device *device, int type) |
771 | { | 769 | { |
772 | acpi_status status = AE_OK; | 770 | acpi_status status; |
773 | struct acpi_ec *ec = NULL; | 771 | struct acpi_ec *ec; |
774 | 772 | ||
775 | if (!device) | 773 | if (!device) |
776 | return -EINVAL; | 774 | return -EINVAL; |
777 | 775 | ||
778 | ec = acpi_driver_data(device); | 776 | ec = acpi_driver_data(device); |
777 | if (!ec) | ||
778 | return -EINVAL; | ||
779 | |||
780 | /* Don't touch boot EC */ | ||
781 | if (ec == ec_ecdt) | ||
782 | return 0; | ||
779 | 783 | ||
780 | status = acpi_remove_address_space_handler(ec->handle, | 784 | status = acpi_remove_address_space_handler(ec->handle, |
781 | ACPI_ADR_SPACE_EC, | 785 | ACPI_ADR_SPACE_EC, |
@@ -790,51 +794,64 @@ static int acpi_ec_stop(struct acpi_device *device, int type) | |||
790 | return 0; | 794 | return 0; |
791 | } | 795 | } |
792 | 796 | ||
793 | static int __init acpi_ec_get_real_ecdt(void) | 797 | static acpi_status |
798 | ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) | ||
794 | { | 799 | { |
795 | acpi_status status; | 800 | acpi_status status; |
801 | |||
802 | struct acpi_ec *ec = context; | ||
803 | status = acpi_walk_resources(handle, METHOD_NAME__CRS, | ||
804 | ec_parse_io_ports, ec); | ||
805 | if (ACPI_FAILURE(status)) | ||
806 | return status; | ||
807 | |||
808 | /* Get GPE bit assignment (EC events). */ | ||
809 | /* TODO: Add support for _GPE returning a package */ | ||
810 | status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe); | ||
811 | if (ACPI_FAILURE(status)) | ||
812 | return status; | ||
813 | |||
814 | /* Use the global lock for all EC transactions? */ | ||
815 | acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); | ||
816 | |||
817 | ec->handle = handle; | ||
818 | |||
819 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx", | ||
820 | ec->gpe, ec->command_addr, ec->data_addr)); | ||
821 | |||
822 | return AE_CTRL_TERMINATE; | ||
823 | } | ||
824 | |||
825 | int __init acpi_ec_ecdt_probe(void) | ||
826 | { | ||
827 | int ret; | ||
828 | acpi_status status; | ||
796 | struct acpi_table_ecdt *ecdt_ptr; | 829 | struct acpi_table_ecdt *ecdt_ptr; |
797 | 830 | ||
831 | ec_ecdt = make_acpi_ec(); | ||
832 | if (!ec_ecdt) | ||
833 | return -ENOMEM; | ||
834 | /* | ||
835 | * Generate a boot ec context | ||
836 | */ | ||
837 | |||
798 | status = acpi_get_table(ACPI_SIG_ECDT, 1, | 838 | status = acpi_get_table(ACPI_SIG_ECDT, 1, |
799 | (struct acpi_table_header **)&ecdt_ptr); | 839 | (struct acpi_table_header **)&ecdt_ptr); |
800 | if (ACPI_FAILURE(status)) | 840 | if (ACPI_FAILURE(status)) |
801 | return -ENODEV; | 841 | goto error; |
802 | 842 | ||
803 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT")); | 843 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT")); |
804 | 844 | ||
805 | /* | ||
806 | * Generate a temporary ec context to use until the namespace is scanned | ||
807 | */ | ||
808 | ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); | ||
809 | if (!ec_ecdt) | ||
810 | return -ENOMEM; | ||
811 | |||
812 | mutex_init(&ec_ecdt->lock); | ||
813 | atomic_set(&ec_ecdt->event_count, 1); | ||
814 | if (acpi_ec_mode == EC_INTR) { | ||
815 | init_waitqueue_head(&ec_ecdt->wait); | ||
816 | } | ||
817 | ec_ecdt->command_addr = ecdt_ptr->control.address; | 845 | ec_ecdt->command_addr = ecdt_ptr->control.address; |
818 | ec_ecdt->data_addr = ecdt_ptr->data.address; | 846 | ec_ecdt->data_addr = ecdt_ptr->data.address; |
819 | ec_ecdt->gpe = ecdt_ptr->gpe; | 847 | ec_ecdt->gpe = ecdt_ptr->gpe; |
820 | ec_ecdt->uid = ecdt_ptr->uid; | 848 | ec_ecdt->uid = ecdt_ptr->uid; |
821 | |||
822 | ec_ecdt->handle = ACPI_ROOT_OBJECT; | 849 | ec_ecdt->handle = ACPI_ROOT_OBJECT; |
823 | return 0; | ||
824 | } | ||
825 | |||
826 | int __init acpi_ec_ecdt_probe(void) | ||
827 | { | ||
828 | int ret; | ||
829 | |||
830 | ret = acpi_ec_get_real_ecdt(); | ||
831 | if (ret) | ||
832 | return 0; | ||
833 | 850 | ||
834 | ret = ec_install_handlers(ec_ecdt); | 851 | ret = ec_install_handlers(ec_ecdt); |
835 | if (!ret) | 852 | if (!ret) |
836 | return 0; | 853 | return 0; |
837 | 854 | error: | |
838 | kfree(ec_ecdt); | 855 | kfree(ec_ecdt); |
839 | ec_ecdt = NULL; | 856 | ec_ecdt = NULL; |
840 | 857 | ||
@@ -884,12 +901,8 @@ static int __init acpi_ec_set_intr_mode(char *str) | |||
884 | if (!get_option(&str, &intr)) | 901 | if (!get_option(&str, &intr)) |
885 | return 0; | 902 | return 0; |
886 | 903 | ||
887 | if (intr) { | 904 | acpi_ec_mode = (intr) ? EC_INTR : EC_POLL; |
888 | acpi_ec_mode = EC_INTR; | 905 | |
889 | } else { | ||
890 | acpi_ec_mode = EC_POLL; | ||
891 | } | ||
892 | acpi_ec_driver.ops.add = acpi_ec_add; | ||
893 | printk(KERN_NOTICE PREFIX "%s mode.\n", intr ? "interrupt" : "polling"); | 906 | printk(KERN_NOTICE PREFIX "%s mode.\n", intr ? "interrupt" : "polling"); |
894 | 907 | ||
895 | return 1; | 908 | return 1; |