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