diff options
Diffstat (limited to 'drivers/acpi/ec.c')
-rw-r--r-- | drivers/acpi/ec.c | 93 |
1 files changed, 40 insertions, 53 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 3f7935ab0cf5..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 | /* -------------------------------------------------------------------------- |
@@ -425,7 +426,7 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, | |||
425 | handler->func = func; | 426 | handler->func = func; |
426 | handler->data = data; | 427 | handler->data = data; |
427 | mutex_lock(&ec->lock); | 428 | mutex_lock(&ec->lock); |
428 | list_add_tail(&handler->node, &ec->list); | 429 | list_add(&handler->node, &ec->list); |
429 | mutex_unlock(&ec->lock); | 430 | mutex_unlock(&ec->lock); |
430 | return 0; | 431 | return 0; |
431 | } | 432 | } |
@@ -440,7 +441,6 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) | |||
440 | if (query_bit == handler->query_bit) { | 441 | if (query_bit == handler->query_bit) { |
441 | list_del(&handler->node); | 442 | list_del(&handler->node); |
442 | kfree(handler); | 443 | kfree(handler); |
443 | break; | ||
444 | } | 444 | } |
445 | } | 445 | } |
446 | mutex_unlock(&ec->lock); | 446 | mutex_unlock(&ec->lock); |
@@ -680,32 +680,50 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) | |||
680 | status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe); | 680 | status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe); |
681 | if (ACPI_FAILURE(status)) | 681 | if (ACPI_FAILURE(status)) |
682 | return status; | 682 | return status; |
683 | |||
684 | /* Find and register all query methods */ | 683 | /* Find and register all query methods */ |
685 | acpi_walk_namespace(ACPI_TYPE_METHOD, handle, 1, | 684 | acpi_walk_namespace(ACPI_TYPE_METHOD, handle, 1, |
686 | acpi_ec_register_query_methods, ec, NULL); | 685 | acpi_ec_register_query_methods, ec, NULL); |
687 | |||
688 | /* Use the global lock for all EC transactions? */ | 686 | /* Use the global lock for all EC transactions? */ |
689 | acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); | 687 | acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); |
690 | |||
691 | ec->handle = handle; | 688 | 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; | 689 | return AE_CTRL_TERMINATE; |
697 | } | 690 | } |
698 | 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 | |||
699 | static int acpi_ec_add(struct acpi_device *device) | 703 | static int acpi_ec_add(struct acpi_device *device) |
700 | { | 704 | { |
701 | struct acpi_ec *ec = NULL; | 705 | struct acpi_ec *ec = NULL; |
702 | 706 | ||
703 | if (!device) | 707 | if (!device) |
704 | return -EINVAL; | 708 | return -EINVAL; |
705 | |||
706 | strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); | 709 | strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); |
707 | strcpy(acpi_device_class(device), ACPI_EC_CLASS); | 710 | strcpy(acpi_device_class(device), ACPI_EC_CLASS); |
708 | 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 | |||
709 | ec = make_acpi_ec(); | 727 | ec = make_acpi_ec(); |
710 | if (!ec) | 728 | if (!ec) |
711 | return -ENOMEM; | 729 | return -ENOMEM; |
@@ -715,25 +733,14 @@ static int acpi_ec_add(struct acpi_device *device) | |||
715 | kfree(ec); | 733 | kfree(ec); |
716 | return -EINVAL; | 734 | return -EINVAL; |
717 | } | 735 | } |
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; | 736 | ec->handle = device->handle; |
737 | end: | ||
738 | if (!first_ec) | ||
739 | first_ec = ec; | ||
734 | acpi_driver_data(device) = ec; | 740 | acpi_driver_data(device) = ec; |
735 | |||
736 | 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); | ||
737 | return 0; | 744 | return 0; |
738 | } | 745 | } |
739 | 746 | ||
@@ -756,10 +763,7 @@ static int acpi_ec_remove(struct acpi_device *device, int type) | |||
756 | acpi_driver_data(device) = NULL; | 763 | acpi_driver_data(device) = NULL; |
757 | if (ec == first_ec) | 764 | if (ec == first_ec) |
758 | first_ec = NULL; | 765 | first_ec = NULL; |
759 | 766 | kfree(ec); | |
760 | /* Don't touch boot EC */ | ||
761 | if (boot_ec != ec) | ||
762 | kfree(ec); | ||
763 | return 0; | 767 | return 0; |
764 | } | 768 | } |
765 | 769 | ||
@@ -789,6 +793,8 @@ ec_parse_io_ports(struct acpi_resource *resource, void *context) | |||
789 | static int ec_install_handlers(struct acpi_ec *ec) | 793 | static int ec_install_handlers(struct acpi_ec *ec) |
790 | { | 794 | { |
791 | acpi_status status; | 795 | acpi_status status; |
796 | if (ec->handlers_installed) | ||
797 | return 0; | ||
792 | status = acpi_install_gpe_handler(NULL, ec->gpe, | 798 | status = acpi_install_gpe_handler(NULL, ec->gpe, |
793 | ACPI_GPE_EDGE_TRIGGERED, | 799 | ACPI_GPE_EDGE_TRIGGERED, |
794 | &acpi_ec_gpe_handler, ec); | 800 | &acpi_ec_gpe_handler, ec); |
@@ -807,6 +813,7 @@ static int ec_install_handlers(struct acpi_ec *ec) | |||
807 | return -ENODEV; | 813 | return -ENODEV; |
808 | } | 814 | } |
809 | 815 | ||
816 | ec->handlers_installed = 1; | ||
810 | return 0; | 817 | return 0; |
811 | } | 818 | } |
812 | 819 | ||
@@ -823,41 +830,22 @@ static int acpi_ec_start(struct acpi_device *device) | |||
823 | if (!ec) | 830 | if (!ec) |
824 | return -EINVAL; | 831 | return -EINVAL; |
825 | 832 | ||
826 | /* Boot EC is already working */ | 833 | ret = ec_install_handlers(ec); |
827 | if (ec != boot_ec) | ||
828 | ret = ec_install_handlers(ec); | ||
829 | 834 | ||
830 | /* EC is fully operational, allow queries */ | 835 | /* EC is fully operational, allow queries */ |
831 | atomic_set(&ec->query_pending, 0); | 836 | atomic_set(&ec->query_pending, 0); |
832 | |||
833 | return ret; | 837 | return ret; |
834 | } | 838 | } |
835 | 839 | ||
836 | static int acpi_ec_stop(struct acpi_device *device, int type) | 840 | static int acpi_ec_stop(struct acpi_device *device, int type) |
837 | { | 841 | { |
838 | acpi_status status; | ||
839 | struct acpi_ec *ec; | 842 | struct acpi_ec *ec; |
840 | |||
841 | if (!device) | 843 | if (!device) |
842 | return -EINVAL; | 844 | return -EINVAL; |
843 | |||
844 | ec = acpi_driver_data(device); | 845 | ec = acpi_driver_data(device); |
845 | if (!ec) | 846 | if (!ec) |
846 | return -EINVAL; | 847 | return -EINVAL; |
847 | 848 | 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 | 849 | ||
862 | return 0; | 850 | return 0; |
863 | } | 851 | } |
@@ -877,7 +865,7 @@ int __init acpi_ec_ecdt_probe(void) | |||
877 | status = acpi_get_table(ACPI_SIG_ECDT, 1, | 865 | status = acpi_get_table(ACPI_SIG_ECDT, 1, |
878 | (struct acpi_table_header **)&ecdt_ptr); | 866 | (struct acpi_table_header **)&ecdt_ptr); |
879 | if (ACPI_SUCCESS(status)) { | 867 | if (ACPI_SUCCESS(status)) { |
880 | 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"); |
881 | boot_ec->command_addr = ecdt_ptr->control.address; | 869 | boot_ec->command_addr = ecdt_ptr->control.address; |
882 | boot_ec->data_addr = ecdt_ptr->data.address; | 870 | boot_ec->data_addr = ecdt_ptr->data.address; |
883 | boot_ec->gpe = ecdt_ptr->gpe; | 871 | boot_ec->gpe = ecdt_ptr->gpe; |
@@ -899,7 +887,6 @@ int __init acpi_ec_ecdt_probe(void) | |||
899 | error: | 887 | error: |
900 | kfree(boot_ec); | 888 | kfree(boot_ec); |
901 | boot_ec = NULL; | 889 | boot_ec = NULL; |
902 | |||
903 | return -ENODEV; | 890 | return -ENODEV; |
904 | } | 891 | } |
905 | 892 | ||