diff options
| -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; |
