diff options
Diffstat (limited to 'drivers/acpi/ec.c')
-rw-r--r-- | drivers/acpi/ec.c | 90 |
1 files changed, 39 insertions, 51 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 3f7935ab0cf5..9cd7997312e0 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -121,6 +121,7 @@ static struct acpi_ec { | |||
121 | atomic_t event_count; | 121 | atomic_t event_count; |
122 | wait_queue_head_t wait; | 122 | wait_queue_head_t wait; |
123 | struct list_head list; | 123 | struct list_head list; |
124 | u8 handlers_installed; | ||
124 | } *boot_ec, *first_ec; | 125 | } *boot_ec, *first_ec; |
125 | 126 | ||
126 | /* -------------------------------------------------------------------------- | 127 | /* -------------------------------------------------------------------------- |
@@ -680,32 +681,50 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) | |||
680 | status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe); | 681 | status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe); |
681 | if (ACPI_FAILURE(status)) | 682 | if (ACPI_FAILURE(status)) |
682 | return status; | 683 | return status; |
683 | |||
684 | /* Find and register all query methods */ | 684 | /* Find and register all query methods */ |
685 | acpi_walk_namespace(ACPI_TYPE_METHOD, handle, 1, | 685 | acpi_walk_namespace(ACPI_TYPE_METHOD, handle, 1, |
686 | acpi_ec_register_query_methods, ec, NULL); | 686 | acpi_ec_register_query_methods, ec, NULL); |
687 | |||
688 | /* Use the global lock for all EC transactions? */ | 687 | /* Use the global lock for all EC transactions? */ |
689 | acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); | 688 | acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); |
690 | |||
691 | ec->handle = handle; | 689 | ec->handle = handle; |
692 | |||
693 | printk(KERN_INFO PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", | ||
694 | ec->gpe, ec->command_addr, ec->data_addr); | ||
695 | |||
696 | return AE_CTRL_TERMINATE; | 690 | return AE_CTRL_TERMINATE; |
697 | } | 691 | } |
698 | 692 | ||
693 | static void ec_remove_handlers(struct acpi_ec *ec) | ||
694 | { | ||
695 | if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, | ||
696 | ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) | ||
697 | printk(KERN_ERR PREFIX "failed to remove space handler\n"); | ||
698 | if (ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe, | ||
699 | &acpi_ec_gpe_handler))) | ||
700 | printk(KERN_ERR PREFIX "failed to remove gpe handler\n"); | ||
701 | ec->handlers_installed = 0; | ||
702 | } | ||
703 | |||
699 | static int acpi_ec_add(struct acpi_device *device) | 704 | static int acpi_ec_add(struct acpi_device *device) |
700 | { | 705 | { |
701 | struct acpi_ec *ec = NULL; | 706 | struct acpi_ec *ec = NULL; |
702 | 707 | ||
703 | if (!device) | 708 | if (!device) |
704 | return -EINVAL; | 709 | return -EINVAL; |
705 | |||
706 | strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); | 710 | strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); |
707 | strcpy(acpi_device_class(device), ACPI_EC_CLASS); | 711 | strcpy(acpi_device_class(device), ACPI_EC_CLASS); |
708 | 712 | ||
713 | /* Check for boot EC */ | ||
714 | if (boot_ec) { | ||
715 | if (boot_ec->handle == device->handle) { | ||
716 | /* Pre-loaded EC from DSDT, just move pointer */ | ||
717 | ec = boot_ec; | ||
718 | boot_ec = NULL; | ||
719 | goto end; | ||
720 | } else if (boot_ec->handle == ACPI_ROOT_OBJECT) { | ||
721 | /* ECDT-based EC, time to shut it down */ | ||
722 | ec_remove_handlers(boot_ec); | ||
723 | kfree(boot_ec); | ||
724 | first_ec = boot_ec = NULL; | ||
725 | } | ||
726 | } | ||
727 | |||
709 | ec = make_acpi_ec(); | 728 | ec = make_acpi_ec(); |
710 | if (!ec) | 729 | if (!ec) |
711 | return -ENOMEM; | 730 | return -ENOMEM; |
@@ -715,25 +734,14 @@ static int acpi_ec_add(struct acpi_device *device) | |||
715 | kfree(ec); | 734 | kfree(ec); |
716 | return -EINVAL; | 735 | return -EINVAL; |
717 | } | 736 | } |
718 | |||
719 | /* Check if we found the boot EC */ | ||
720 | if (boot_ec) { | ||
721 | if (boot_ec->gpe == ec->gpe) { | ||
722 | /* We might have incorrect info for GL at boot time */ | ||
723 | mutex_lock(&boot_ec->lock); | ||
724 | boot_ec->global_lock = ec->global_lock; | ||
725 | /* Copy handlers from new ec into boot ec */ | ||
726 | list_splice(&ec->list, &boot_ec->list); | ||
727 | mutex_unlock(&boot_ec->lock); | ||
728 | kfree(ec); | ||
729 | ec = boot_ec; | ||
730 | } | ||
731 | } else | ||
732 | first_ec = ec; | ||
733 | ec->handle = device->handle; | 737 | ec->handle = device->handle; |
738 | end: | ||
739 | if (!first_ec) | ||
740 | first_ec = ec; | ||
734 | acpi_driver_data(device) = ec; | 741 | acpi_driver_data(device) = ec; |
735 | |||
736 | acpi_ec_add_fs(device); | 742 | acpi_ec_add_fs(device); |
743 | printk(KERN_INFO PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", | ||
744 | ec->gpe, ec->command_addr, ec->data_addr); | ||
737 | return 0; | 745 | return 0; |
738 | } | 746 | } |
739 | 747 | ||
@@ -756,10 +764,7 @@ static int acpi_ec_remove(struct acpi_device *device, int type) | |||
756 | acpi_driver_data(device) = NULL; | 764 | acpi_driver_data(device) = NULL; |
757 | if (ec == first_ec) | 765 | if (ec == first_ec) |
758 | first_ec = NULL; | 766 | first_ec = NULL; |
759 | 767 | kfree(ec); | |
760 | /* Don't touch boot EC */ | ||
761 | if (boot_ec != ec) | ||
762 | kfree(ec); | ||
763 | return 0; | 768 | return 0; |
764 | } | 769 | } |
765 | 770 | ||
@@ -789,6 +794,8 @@ ec_parse_io_ports(struct acpi_resource *resource, void *context) | |||
789 | static int ec_install_handlers(struct acpi_ec *ec) | 794 | static int ec_install_handlers(struct acpi_ec *ec) |
790 | { | 795 | { |
791 | acpi_status status; | 796 | acpi_status status; |
797 | if (ec->handlers_installed) | ||
798 | return 0; | ||
792 | status = acpi_install_gpe_handler(NULL, ec->gpe, | 799 | status = acpi_install_gpe_handler(NULL, ec->gpe, |
793 | ACPI_GPE_EDGE_TRIGGERED, | 800 | ACPI_GPE_EDGE_TRIGGERED, |
794 | &acpi_ec_gpe_handler, ec); | 801 | &acpi_ec_gpe_handler, ec); |
@@ -807,6 +814,7 @@ static int ec_install_handlers(struct acpi_ec *ec) | |||
807 | return -ENODEV; | 814 | return -ENODEV; |
808 | } | 815 | } |
809 | 816 | ||
817 | ec->handlers_installed = 1; | ||
810 | return 0; | 818 | return 0; |
811 | } | 819 | } |
812 | 820 | ||
@@ -823,41 +831,22 @@ static int acpi_ec_start(struct acpi_device *device) | |||
823 | if (!ec) | 831 | if (!ec) |
824 | return -EINVAL; | 832 | return -EINVAL; |
825 | 833 | ||
826 | /* Boot EC is already working */ | 834 | ret = ec_install_handlers(ec); |
827 | if (ec != boot_ec) | ||
828 | ret = ec_install_handlers(ec); | ||
829 | 835 | ||
830 | /* EC is fully operational, allow queries */ | 836 | /* EC is fully operational, allow queries */ |
831 | atomic_set(&ec->query_pending, 0); | 837 | atomic_set(&ec->query_pending, 0); |
832 | |||
833 | return ret; | 838 | return ret; |
834 | } | 839 | } |
835 | 840 | ||
836 | static int acpi_ec_stop(struct acpi_device *device, int type) | 841 | static int acpi_ec_stop(struct acpi_device *device, int type) |
837 | { | 842 | { |
838 | acpi_status status; | ||
839 | struct acpi_ec *ec; | 843 | struct acpi_ec *ec; |
840 | |||
841 | if (!device) | 844 | if (!device) |
842 | return -EINVAL; | 845 | return -EINVAL; |
843 | |||
844 | ec = acpi_driver_data(device); | 846 | ec = acpi_driver_data(device); |
845 | if (!ec) | 847 | if (!ec) |
846 | return -EINVAL; | 848 | return -EINVAL; |
847 | 849 | ec_remove_handlers(ec); | |
848 | /* Don't touch boot EC */ | ||
849 | if (ec == boot_ec) | ||
850 | return 0; | ||
851 | |||
852 | status = acpi_remove_address_space_handler(ec->handle, | ||
853 | ACPI_ADR_SPACE_EC, | ||
854 | &acpi_ec_space_handler); | ||
855 | if (ACPI_FAILURE(status)) | ||
856 | return -ENODEV; | ||
857 | |||
858 | status = acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler); | ||
859 | if (ACPI_FAILURE(status)) | ||
860 | return -ENODEV; | ||
861 | 850 | ||
862 | return 0; | 851 | return 0; |
863 | } | 852 | } |
@@ -877,7 +866,7 @@ int __init acpi_ec_ecdt_probe(void) | |||
877 | status = acpi_get_table(ACPI_SIG_ECDT, 1, | 866 | status = acpi_get_table(ACPI_SIG_ECDT, 1, |
878 | (struct acpi_table_header **)&ecdt_ptr); | 867 | (struct acpi_table_header **)&ecdt_ptr); |
879 | if (ACPI_SUCCESS(status)) { | 868 | if (ACPI_SUCCESS(status)) { |
880 | printk(KERN_INFO PREFIX "EC description table is found, configuring boot EC\n\n"); | 869 | printk(KERN_INFO PREFIX "EC description table is found, configuring boot EC\n"); |
881 | boot_ec->command_addr = ecdt_ptr->control.address; | 870 | boot_ec->command_addr = ecdt_ptr->control.address; |
882 | boot_ec->data_addr = ecdt_ptr->data.address; | 871 | boot_ec->data_addr = ecdt_ptr->data.address; |
883 | boot_ec->gpe = ecdt_ptr->gpe; | 872 | boot_ec->gpe = ecdt_ptr->gpe; |
@@ -899,7 +888,6 @@ int __init acpi_ec_ecdt_probe(void) | |||
899 | error: | 888 | error: |
900 | kfree(boot_ec); | 889 | kfree(boot_ec); |
901 | boot_ec = NULL; | 890 | boot_ec = NULL; |
902 | |||
903 | return -ENODEV; | 891 | return -ENODEV; |
904 | } | 892 | } |
905 | 893 | ||