Commit 0ab5bb64 authored by Lan Tianyu's avatar Lan Tianyu Committed by Rafael J. Wysocki

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=45221Signed-off-by: default avatarLan Tianyu <tianyu.lan@intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 28fe5c82
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/dmi.h>
#include <linux/delay.h>
#ifdef CONFIG_ACPI_PROCFS_POWER #ifdef CONFIG_ACPI_PROCFS_POWER
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
...@@ -74,6 +76,8 @@ static int acpi_ac_resume(struct device *dev); ...@@ -74,6 +76,8 @@ static int acpi_ac_resume(struct device *dev);
#endif #endif
static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume); 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 = { static struct acpi_driver acpi_ac_driver = {
.name = "ac", .name = "ac",
.class = ACPI_AC_CLASS, .class = ACPI_AC_CLASS,
...@@ -252,6 +256,16 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event) ...@@ -252,6 +256,16 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event)
case ACPI_AC_NOTIFY_STATUS: case ACPI_AC_NOTIFY_STATUS:
case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_BUS_CHECK:
case ACPI_NOTIFY_DEVICE_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_ac_get_state(ac);
acpi_bus_generate_proc_event(device, event, (u32) ac->state); acpi_bus_generate_proc_event(device, event, (u32) ac->state);
acpi_bus_generate_netlink_event(device->pnp.device_class, acpi_bus_generate_netlink_event(device->pnp.device_class,
...@@ -264,6 +278,24 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event) ...@@ -264,6 +278,24 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event)
return; 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) static int acpi_ac_add(struct acpi_device *device)
{ {
int result = 0; int result = 0;
...@@ -312,6 +344,7 @@ static int acpi_ac_add(struct acpi_device *device) ...@@ -312,6 +344,7 @@ static int acpi_ac_add(struct acpi_device *device)
kfree(ac); kfree(ac);
} }
dmi_check_system(ac_dmi_table);
return result; return result;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment