diff options
| -rw-r--r-- | drivers/acpi/ec.c | 64 | ||||
| -rw-r--r-- | drivers/acpi/resource.c | 10 | ||||
| -rw-r--r-- | drivers/acpi/sleep.c | 7 |
3 files changed, 80 insertions, 1 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 959d41acc108..d7d32c28829b 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
| @@ -67,6 +67,8 @@ enum ec_command { | |||
| 67 | #define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ | 67 | #define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ |
| 68 | #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ | 68 | #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ |
| 69 | #define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */ | 69 | #define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */ |
| 70 | #define ACPI_EC_CLEAR_MAX 100 /* Maximum number of events to query | ||
| 71 | * when trying to clear the EC */ | ||
| 70 | 72 | ||
| 71 | enum { | 73 | enum { |
| 72 | EC_FLAGS_QUERY_PENDING, /* Query is pending */ | 74 | EC_FLAGS_QUERY_PENDING, /* Query is pending */ |
| @@ -116,6 +118,7 @@ EXPORT_SYMBOL(first_ec); | |||
| 116 | static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ | 118 | static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ |
| 117 | static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ | 119 | static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ |
| 118 | static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */ | 120 | static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */ |
| 121 | static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ | ||
| 119 | 122 | ||
| 120 | /* -------------------------------------------------------------------------- | 123 | /* -------------------------------------------------------------------------- |
| 121 | Transaction Management | 124 | Transaction Management |
| @@ -440,6 +443,29 @@ acpi_handle ec_get_handle(void) | |||
| 440 | 443 | ||
| 441 | EXPORT_SYMBOL(ec_get_handle); | 444 | EXPORT_SYMBOL(ec_get_handle); |
| 442 | 445 | ||
| 446 | static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 *data); | ||
| 447 | |||
| 448 | /* | ||
| 449 | * Clears stale _Q events that might have accumulated in the EC. | ||
| 450 | * Run with locked ec mutex. | ||
| 451 | */ | ||
| 452 | static void acpi_ec_clear(struct acpi_ec *ec) | ||
| 453 | { | ||
| 454 | int i, status; | ||
| 455 | u8 value = 0; | ||
| 456 | |||
| 457 | for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) { | ||
| 458 | status = acpi_ec_query_unlocked(ec, &value); | ||
| 459 | if (status || !value) | ||
| 460 | break; | ||
| 461 | } | ||
| 462 | |||
| 463 | if (unlikely(i == ACPI_EC_CLEAR_MAX)) | ||
| 464 | pr_warn("Warning: Maximum of %d stale EC events cleared\n", i); | ||
| 465 | else | ||
| 466 | pr_info("%d stale EC events cleared\n", i); | ||
| 467 | } | ||
| 468 | |||
| 443 | void acpi_ec_block_transactions(void) | 469 | void acpi_ec_block_transactions(void) |
| 444 | { | 470 | { |
| 445 | struct acpi_ec *ec = first_ec; | 471 | struct acpi_ec *ec = first_ec; |
| @@ -463,6 +489,10 @@ void acpi_ec_unblock_transactions(void) | |||
| 463 | mutex_lock(&ec->mutex); | 489 | mutex_lock(&ec->mutex); |
| 464 | /* Allow transactions to be carried out again */ | 490 | /* Allow transactions to be carried out again */ |
| 465 | clear_bit(EC_FLAGS_BLOCKED, &ec->flags); | 491 | clear_bit(EC_FLAGS_BLOCKED, &ec->flags); |
| 492 | |||
| 493 | if (EC_FLAGS_CLEAR_ON_RESUME) | ||
| 494 | acpi_ec_clear(ec); | ||
| 495 | |||
| 466 | mutex_unlock(&ec->mutex); | 496 | mutex_unlock(&ec->mutex); |
| 467 | } | 497 | } |
| 468 | 498 | ||
| @@ -821,6 +851,13 @@ static int acpi_ec_add(struct acpi_device *device) | |||
| 821 | 851 | ||
| 822 | /* EC is fully operational, allow queries */ | 852 | /* EC is fully operational, allow queries */ |
| 823 | clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); | 853 | clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); |
| 854 | |||
| 855 | /* Clear stale _Q events if hardware might require that */ | ||
| 856 | if (EC_FLAGS_CLEAR_ON_RESUME) { | ||
| 857 | mutex_lock(&ec->mutex); | ||
| 858 | acpi_ec_clear(ec); | ||
| 859 | mutex_unlock(&ec->mutex); | ||
| 860 | } | ||
| 824 | return ret; | 861 | return ret; |
| 825 | } | 862 | } |
| 826 | 863 | ||
| @@ -922,6 +959,30 @@ static int ec_enlarge_storm_threshold(const struct dmi_system_id *id) | |||
| 922 | return 0; | 959 | return 0; |
| 923 | } | 960 | } |
| 924 | 961 | ||
| 962 | /* | ||
| 963 | * On some hardware it is necessary to clear events accumulated by the EC during | ||
| 964 | * sleep. These ECs stop reporting GPEs until they are manually polled, if too | ||
| 965 | * many events are accumulated. (e.g. Samsung Series 5/9 notebooks) | ||
| 966 | * | ||
| 967 | * https://bugzilla.kernel.org/show_bug.cgi?id=44161 | ||
| 968 | * | ||
| 969 | * Ideally, the EC should also be instructed NOT to accumulate events during | ||
| 970 | * sleep (which Windows seems to do somehow), but the interface to control this | ||
| 971 | * behaviour is not known at this time. | ||
| 972 | * | ||
| 973 | * Models known to be affected are Samsung 530Uxx/535Uxx/540Uxx/550Pxx/900Xxx, | ||
| 974 | * however it is very likely that other Samsung models are affected. | ||
| 975 | * | ||
| 976 | * On systems which don't accumulate _Q events during sleep, this extra check | ||
| 977 | * should be harmless. | ||
| 978 | */ | ||
| 979 | static int ec_clear_on_resume(const struct dmi_system_id *id) | ||
| 980 | { | ||
| 981 | pr_debug("Detected system needing EC poll on resume.\n"); | ||
| 982 | EC_FLAGS_CLEAR_ON_RESUME = 1; | ||
| 983 | return 0; | ||
| 984 | } | ||
| 985 | |||
| 925 | static struct dmi_system_id ec_dmi_table[] __initdata = { | 986 | static struct dmi_system_id ec_dmi_table[] __initdata = { |
| 926 | { | 987 | { |
| 927 | ec_skip_dsdt_scan, "Compal JFL92", { | 988 | ec_skip_dsdt_scan, "Compal JFL92", { |
| @@ -965,6 +1026,9 @@ static struct dmi_system_id ec_dmi_table[] __initdata = { | |||
| 965 | ec_validate_ecdt, "ASUS hardware", { | 1026 | ec_validate_ecdt, "ASUS hardware", { |
| 966 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTek Computer Inc."), | 1027 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTek Computer Inc."), |
| 967 | DMI_MATCH(DMI_PRODUCT_NAME, "L4R"),}, NULL}, | 1028 | DMI_MATCH(DMI_PRODUCT_NAME, "L4R"),}, NULL}, |
| 1029 | { | ||
| 1030 | ec_clear_on_resume, "Samsung hardware", { | ||
| 1031 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD.")}, NULL}, | ||
| 968 | {}, | 1032 | {}, |
| 969 | }; | 1033 | }; |
| 970 | 1034 | ||
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index b7201fc6f1e1..0bdacc5e26a3 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c | |||
| @@ -77,18 +77,24 @@ bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res) | |||
| 77 | switch (ares->type) { | 77 | switch (ares->type) { |
| 78 | case ACPI_RESOURCE_TYPE_MEMORY24: | 78 | case ACPI_RESOURCE_TYPE_MEMORY24: |
| 79 | memory24 = &ares->data.memory24; | 79 | memory24 = &ares->data.memory24; |
| 80 | if (!memory24->address_length) | ||
| 81 | return false; | ||
| 80 | acpi_dev_get_memresource(res, memory24->minimum, | 82 | acpi_dev_get_memresource(res, memory24->minimum, |
| 81 | memory24->address_length, | 83 | memory24->address_length, |
| 82 | memory24->write_protect); | 84 | memory24->write_protect); |
| 83 | break; | 85 | break; |
| 84 | case ACPI_RESOURCE_TYPE_MEMORY32: | 86 | case ACPI_RESOURCE_TYPE_MEMORY32: |
| 85 | memory32 = &ares->data.memory32; | 87 | memory32 = &ares->data.memory32; |
| 88 | if (!memory32->address_length) | ||
| 89 | return false; | ||
| 86 | acpi_dev_get_memresource(res, memory32->minimum, | 90 | acpi_dev_get_memresource(res, memory32->minimum, |
| 87 | memory32->address_length, | 91 | memory32->address_length, |
| 88 | memory32->write_protect); | 92 | memory32->write_protect); |
| 89 | break; | 93 | break; |
| 90 | case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: | 94 | case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: |
| 91 | fixed_memory32 = &ares->data.fixed_memory32; | 95 | fixed_memory32 = &ares->data.fixed_memory32; |
| 96 | if (!fixed_memory32->address_length) | ||
| 97 | return false; | ||
| 92 | acpi_dev_get_memresource(res, fixed_memory32->address, | 98 | acpi_dev_get_memresource(res, fixed_memory32->address, |
| 93 | fixed_memory32->address_length, | 99 | fixed_memory32->address_length, |
| 94 | fixed_memory32->write_protect); | 100 | fixed_memory32->write_protect); |
| @@ -144,12 +150,16 @@ bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res) | |||
| 144 | switch (ares->type) { | 150 | switch (ares->type) { |
| 145 | case ACPI_RESOURCE_TYPE_IO: | 151 | case ACPI_RESOURCE_TYPE_IO: |
| 146 | io = &ares->data.io; | 152 | io = &ares->data.io; |
| 153 | if (!io->address_length) | ||
| 154 | return false; | ||
| 147 | acpi_dev_get_ioresource(res, io->minimum, | 155 | acpi_dev_get_ioresource(res, io->minimum, |
| 148 | io->address_length, | 156 | io->address_length, |
| 149 | io->io_decode); | 157 | io->io_decode); |
| 150 | break; | 158 | break; |
| 151 | case ACPI_RESOURCE_TYPE_FIXED_IO: | 159 | case ACPI_RESOURCE_TYPE_FIXED_IO: |
| 152 | fixed_io = &ares->data.fixed_io; | 160 | fixed_io = &ares->data.fixed_io; |
| 161 | if (!fixed_io->address_length) | ||
| 162 | return false; | ||
| 153 | acpi_dev_get_ioresource(res, fixed_io->address, | 163 | acpi_dev_get_ioresource(res, fixed_io->address, |
| 154 | fixed_io->address_length, | 164 | fixed_io->address_length, |
| 155 | ACPI_DECODE_10); | 165 | ACPI_DECODE_10); |
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index b718806657cd..b0f6c4a2a119 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c | |||
| @@ -807,7 +807,12 @@ int __init acpi_sleep_init(void) | |||
| 807 | acpi_sleep_hibernate_setup(); | 807 | acpi_sleep_hibernate_setup(); |
| 808 | 808 | ||
| 809 | status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b); | 809 | status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b); |
| 810 | if (ACPI_SUCCESS(status)) { | 810 | /* |
| 811 | * Check both ACPI S5 object and ACPI sleep registers to | ||
| 812 | * install pm_power_off_prepare/pm_power_off hook | ||
| 813 | */ | ||
| 814 | if (ACPI_SUCCESS(status) && acpi_gbl_FADT.sleep_control.address | ||
| 815 | && acpi_gbl_FADT.sleep_status.address) { | ||
| 811 | sleep_states[ACPI_STATE_S5] = 1; | 816 | sleep_states[ACPI_STATE_S5] = 1; |
| 812 | pm_power_off_prepare = acpi_power_off_prepare; | 817 | pm_power_off_prepare = acpi_power_off_prepare; |
| 813 | pm_power_off = acpi_power_off; | 818 | pm_power_off = acpi_power_off; |
