From 0ab5bb64937d76c660c29813d8de0f4b47bf7550 Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Wed, 8 May 2013 07:28:46 +0000 Subject: [PATCH] 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 Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ac.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) 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 #include #include +#include +#include #ifdef CONFIG_ACPI_PROCFS_POWER #include #include @@ -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; } -- 2.30.2