diff options
| author | Alexey Starikovskiy <astarikovskiy@suse.de> | 2007-09-05 19:56:38 -0400 |
|---|---|---|
| committer | Len Brown <len.brown@intel.com> | 2007-09-05 19:56:38 -0400 |
| commit | 4c611060660f0de3e9b8f02df207312bc6f5c331 (patch) | |
| tree | ed49169b7bdbf00d02a467649e7436a4854ca3ae | |
| parent | 40ffbfad6bb79a99cc7627bdaca0ee22dec526f6 (diff) | |
ACPI: EC: Drop ECDT-based boot_ec as soon as we find DSDT-based one.
ASUS notebooks have numerous problems with EC initialization
This patch tries to work around three known issues reported
in bugzilla 8598, 8709 and 8909/8919.
Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
| -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 | ||
