diff options
author | Lan Tianyu <tianyu.lan@intel.com> | 2013-05-08 09:28:46 +0200 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-05-12 14:03:15 +0200 |
commit | 0ab5bb64937d76c660c29813d8de0f4b47bf7550 (patch) | |
tree | df3706cde82feec3b4e3e47fc438406795c7058c /drivers/acpi/ac.c | |
parent | ACPI / EC: Restart transaction even when the IBF flag set (diff) | |
download | linux-0ab5bb64937d76c660c29813d8de0f4b47bf7550.tar.xz linux-0ab5bb64937d76c660c29813d8de0f4b47bf7550.zip |
ACPI / AC: Add sleep quirk for Thinkpad e530
The Thinkpad e530's BIOS notifies the AC device first and then
sleeps for certain amount of time before doing real work in the
EC event handler (_Qxx):
Method (_Q27, 0, NotSerialized)
{
Notify (AC, 0x80)
Sleep (0x03E8)
Store (Zero, PWRS)
PNOT ()
}
This causes the AC driver to report an outdated AC state to user
space, because it reads the state information from the device while
the EC handler is sleeping.
Introduce a quirk to cause the AC driver to wait in acpi_ac_notify()
before calling acpi_ac_get_state() on systems known to have this
problem and add Thinkpad e530 to the list of quirky machines (with
a 1s delay which has been verified to be sufficient for that
machine).
[rjw: Changelog]
References: https://bugzilla.kernel.org/show_bug.cgi?id=45221
Signed-off-by: Lan Tianyu <tianyu.lan@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/ac.c')
-rw-r--r-- | drivers/acpi/ac.c | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 00d2efd674df..4f4e741d34b2 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -28,6 +28,8 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/types.h> +#include <linux/dmi.h> +#include <linux/delay.h> #ifdef CONFIG_ACPI_PROCFS_POWER #include <linux/proc_fs.h> #include <linux/seq_file.h> @@ -74,6 +76,8 @@ static int acpi_ac_resume(struct device *dev); #endif static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume); +static int ac_sleep_before_get_state_ms; + static struct acpi_driver acpi_ac_driver = { .name = "ac", .class = ACPI_AC_CLASS, @@ -252,6 +256,16 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event) case ACPI_AC_NOTIFY_STATUS: case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_DEVICE_CHECK: + /* + * A buggy BIOS may notify AC first and then sleep for + * a specific time before doing actual operations in the + * EC event handler (_Qxx). This will cause the AC state + * reported by the ACPI event to be incorrect, so wait for a + * specific time for the EC event handler to make progress. + */ + if (ac_sleep_before_get_state_ms > 0) + msleep(ac_sleep_before_get_state_ms); + acpi_ac_get_state(ac); acpi_bus_generate_proc_event(device, event, (u32) ac->state); acpi_bus_generate_netlink_event(device->pnp.device_class, @@ -264,6 +278,24 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event) return; } +static int thinkpad_e530_quirk(const struct dmi_system_id *d) +{ + ac_sleep_before_get_state_ms = 1000; + return 0; +} + +static struct dmi_system_id ac_dmi_table[] = { + { + .callback = thinkpad_e530_quirk, + .ident = "thinkpad e530", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"), + }, + }, + {}, +}; + static int acpi_ac_add(struct acpi_device *device) { int result = 0; @@ -312,6 +344,7 @@ static int acpi_ac_add(struct acpi_device *device) kfree(ac); } + dmi_check_system(ac_dmi_table); return result; } |