summaryrefslogtreecommitdiff
path: root/drivers/acpi/ec.c
diff options
context:
space:
mode:
authorLv Zheng <lv.zheng@intel.com>2017-08-17 05:21:12 +0300
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-08-18 16:12:10 +0300
commit69b957c26b32c3407d1b8cc0d2390b271728db8a (patch)
tree7099d3ece760d58cb19d65782fb0b6edccf3bfe9 /drivers/acpi/ec.c
parenteb7f43c4adb4a789f99f53916182c3401b4e33c7 (diff)
downloadlinux-69b957c26b32c3407d1b8cc0d2390b271728db8a.tar.xz
ACPI: EC: Fix possible issues related to EC initialization order
Use the observation that the EC command/data register addresses are sufficient to determine if two EC devices are equivelent to modify acpi_is_boot_ec(). Then, for the removed comparison factors, EC ID and EC GPE, they need to be synchronized for the boot_ec: 1. Before registering the BIOS-provided EC event handlers in acpi_ec_register_query_methods(), the namespace node holding _Qxx methods should be located. The real namespace PNP0C09 device location then is apparently more trustworthy than the ECDT EC ID. 2. Because of the ASUS quirks, the ECDT EC GPE is more trustworthy than the namespace PNP0C09 device's _GPE setting. Use the above observations to synchronize the boot_ec settings in acpi_ec_add(). Finally, change the order of acpi_ec_ecdt_start() and acpi_ec_add(), called from acpi_bus_register_driver(), so as to follow the fast path of determining the location of _Qxx. Signed-off-by: Lv Zheng <lv.zheng@intel.com> [ rjw : Changelog & comments ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/ec.c')
-rw-r--r--drivers/acpi/ec.c25
1 files changed, 16 insertions, 9 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index ae3d6d152633..7ea7aa9701e3 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -1586,9 +1586,7 @@ static bool acpi_is_boot_ec(struct acpi_ec *ec)
{
if (!boot_ec)
return false;
- if (ec->handle == boot_ec->handle &&
- ec->gpe == boot_ec->gpe &&
- ec->command_addr == boot_ec->command_addr &&
+ if (ec->command_addr == boot_ec->command_addr &&
ec->data_addr == boot_ec->data_addr)
return true;
return false;
@@ -1613,6 +1611,13 @@ static int acpi_ec_add(struct acpi_device *device)
if (acpi_is_boot_ec(ec)) {
boot_ec_is_ecdt = false;
+ /*
+ * Trust PNP0C09 namespace location rather than ECDT ID.
+ *
+ * But trust ECDT GPE rather than _GPE because of ASUS quirks,
+ * so do not change boot_ec->gpe to ec->gpe.
+ */
+ boot_ec->handle = ec->handle;
acpi_handle_debug(ec->handle, "duplicated.\n");
acpi_ec_free(ec);
ec = boot_ec;
@@ -1747,18 +1752,20 @@ static int __init acpi_ec_ecdt_start(void)
if (!boot_ec)
return -ENODEV;
- /*
- * The DSDT EC should have already been started in
- * acpi_ec_add().
- */
+ /* In case acpi_ec_ecdt_start() is called after acpi_ec_add() */
if (!boot_ec_is_ecdt)
return -ENODEV;
/*
* At this point, the namespace and the GPE is initialized, so
* start to find the namespace objects and handle the events.
+ *
+ * Note: ec->handle can be valid if this function is called after
+ * acpi_ec_add(), hence the fast path.
*/
- if (!acpi_ec_ecdt_get_handle(&handle))
+ if (boot_ec->handle != ACPI_ROOT_OBJECT)
+ handle = boot_ec->handle;
+ else if (!acpi_ec_ecdt_get_handle(&handle))
return -ENODEV;
return acpi_config_boot_ec(boot_ec, handle, true, true);
}
@@ -2011,8 +2018,8 @@ int __init acpi_ec_init(void)
return result;
/* Drivers must be started after acpi_ec_query_init() */
- ecdt_fail = acpi_ec_ecdt_start();
dsdt_fail = acpi_bus_register_driver(&acpi_ec_driver);
+ ecdt_fail = acpi_ec_ecdt_start();
return ecdt_fail && dsdt_fail ? -ENODEV : 0;
}