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>
This commit is contained in:
Lan Tianyu 2013-05-08 07:28:46 +00:00 committed by Rafael J. Wysocki
parent 28fe5c825f
commit 0ab5bb6493
1 changed files with 33 additions and 0 deletions

View File

@ -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;
}