2017-09-26 08:54:05

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH 0/2] ACPI / EC: EC regression fixes related to EC event stuck

There are 2 regressions reported on bugzilla.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=196847 [#1]
Link: https://bugzilla.kernel.org/show_bug.cgi?id=196833 [#2]
It looks they are related to the modification against the timing of
acpi_ec_enable_event() invocation, but they are all due to different root
causes.

For link 1, its root cause is: we rely on acpi_ec_suspend/resume() to
control the timing of EC event handling enablement, but ECDT is not an
ACPI device, thus the PM hooks are not invoked for it.
For link 2, reporter seems to favor a fix that can fix both of the
problems:
1. EC event can stuck if there is no triggering source.
2. Post-resume enabling of EC event handling may be too late.
But actually the report is only against problem 1 which is known to have a
very low and accepted reproduce ratio.

For problem 2, it should has a sympton related to the driver resume order
rather than EC event stuck. And its fix should be acpi.ec_freeze_events=Y
related. Note acpi.ec_freeze_events=Y is a feature we need to keep for
now as:
1. It's reasonable for EC driver to act as an agent for EC event
handling while EC transaction is handled by ACPI core handler, thus
1.1. unbind/suspend means suspending EC event handling;
1.2. bind/resume means resuming EC event handling.
2. According to the known EC FW facts, it takes time for EC FW to
initialize after boot/resume.

This patchset fixes the root causes of the 2 regressions and keeps the
possibility of tuning acpi.ec_freeze_events=Y. Note that PATCH 02
depends on PATCH 01 to be safe for driver unbind.

Lv Zheng (2):
ACPI / EC: Fix a regression related to the triggering source of the EC
event handling
ACPI / EC: Fix a regression related to the PM ops support of ECDT
device

drivers/acpi/ec.c | 81 +++++++++++++++++++++++++++++----------------
drivers/acpi/internal.h | 1 +
drivers/acpi/scan.c | 21 ++++++++++++
include/acpi/acpi_bus.h | 1 +
include/acpi/acpi_drivers.h | 1 +
5 files changed, 76 insertions(+), 29 deletions(-)

--
2.7.4


2017-09-26 08:54:13

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH 1/2] ACPI / EC: Fix a regression related to the triggering source of the EC event handling

Originally the Samsung quirks removed by commit 4c237371 can be covered by
commit e923e8e7 and ec_freeze_events=Y mode. But commit 9c40f956 changed
ec_freeze_events=Y back to N, making this problem re-surfaced.
Actually, if commit e923e8e7 is robust enough, we can freely change
ec_freeze_events mode, so this patch fixes the issue by improving
commit e923e8e7.

Related commits listed in the merged order:
Commit: e923e8e79e18fd6be9162f1be6b99a002e9df2cb
Subject: ACPI / EC: Fix an issue that SCI_EVT cannot be detected after
event is enabled
Commit: 4c237371f290d1ed3b2071dd43554362137b1cce
Subject: ACPI / EC: Remove old CLEAR_ON_RESUME quirk
Commit: 9c40f956ce9b331493347d1b3cb7e384f7dc0581
Subject: Revert "ACPI / EC: Enable event freeze mode..." to fix a
regression

This patch not only fixes the reported post-resume EC event triggering
source issue, but also fixes an unreported similar issue related to the
driver bind by adding EC event triggering source in ec_install_handlers().

Fixes: e923e8e79e18 (ACPI / EC: Fix an issue that SCI_EVT cannot be detected after event is enabled)
Fixes: 4c237371f290 (ACPI / EC: Remove old CLEAR_ON_RESUME quirk)
Fixes: 9c40f956ce9b (Revert "ACPI / EC: Enable event freeze mode..." to fix a regression)
Link: https://bugzilla.kernel.org/show_bug.cgi?id=196833
Signed-off-by: Lv Zheng <[email protected]>
Reported-by: Alistair Hamilton <[email protected]>
Tested-by: Alistair Hamilton <[email protected]>
Cc: 4.11+ <[email protected]> # 4.11+
---
drivers/acpi/ec.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 236b143..82b3ce5 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -486,8 +486,11 @@ static inline void __acpi_ec_enable_event(struct acpi_ec *ec)
{
if (!test_and_set_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
ec_log_drv("event unblocked");
- if (!test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags))
- advance_transaction(ec);
+ /*
+ * Unconditionally invoke this once after enabling the event
+ * handling mechanism to detect the pending events.
+ */
+ advance_transaction(ec);
}

static inline void __acpi_ec_disable_event(struct acpi_ec *ec)
@@ -1456,11 +1459,10 @@ static int ec_install_handlers(struct acpi_ec *ec, bool handle_events)
if (test_bit(EC_FLAGS_STARTED, &ec->flags) &&
ec->reference_count >= 1)
acpi_ec_enable_gpe(ec, true);
-
- /* EC is fully operational, allow queries */
- acpi_ec_enable_event(ec);
}
}
+ /* EC is fully operational, allow queries */
+ acpi_ec_enable_event(ec);

return 0;
}
--
2.7.4

2017-09-26 08:54:44

by Zheng, Lv

[permalink] [raw]
Subject: [PATCH 2/2] ACPI / EC: Fix a regression related to the PM ops support of ECDT device

On platforms (ASUS X550ZE and possibly all ASUS X series) with valid ECDT
EC but invalid DSDT EC, EC PM ops won't be invoked as ECDT EC is not an
ACPI device. Thus the following commit actually removed post-resume
acpi_ec_enable_event() invocation for such platforms, and triggered a
regression on them that after being resumed, EC (actually should be ECDT)
driver stops handling EC events:
Commit: c2b46d679b30c5c0d7eb47a21085943242bdd8dc
Subject: ACPI / EC: Add PM operations to improve event handling for resume process
Notice that the root cause actually is "ECDT is not an ACPI device" rather
than "the timing of acpi_ec_enable_event() invocation", this patch fixes
this issue by enumerating ECDT EC as an ACPI device. Due to the existence
of the noirq stage, the ability of tuning the timing of
acpi_ec_enable_event() invocation is still meaningful.

This patch is a little bit different from the posted fix by moving
acpi_config_boot_ec() from acpi_ec_ecdt_start() to acpi_ec_add() to make
sure that EC event handling won't be stopped as long as the ACPI EC driver
is bound. Thus the following sequence shouldn't disable EC event handling:
unbind,suspend,resume,bind.

Fixes: c2b46d679b30 (ACPI / EC: Add PM operations to improve event handling for resume process)
Link: https://bugzilla.kernel.org/show_bug.cgi?id=196847
Reported-by: Luya Tshimbalanga <[email protected]>
Tested-by: Luya Tshimbalanga <[email protected]>
Cc: 4.9+ <[email protected]> # 4.9+
Signed-off-by: Lv Zheng <[email protected]>
---
drivers/acpi/ec.c | 69 +++++++++++++++++++++++++++++----------------
drivers/acpi/internal.h | 1 +
drivers/acpi/scan.c | 21 ++++++++++++++
include/acpi/acpi_bus.h | 1 +
include/acpi/acpi_drivers.h | 1 +
5 files changed, 69 insertions(+), 24 deletions(-)

diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 82b3ce5..df84246 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -1597,32 +1597,41 @@ static int acpi_ec_add(struct acpi_device *device)
{
struct acpi_ec *ec = NULL;
int ret;
+ bool is_ecdt = false;
+ acpi_status status;

strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_EC_CLASS);

- ec = acpi_ec_alloc();
- if (!ec)
- return -ENOMEM;
- if (ec_parse_device(device->handle, 0, ec, NULL) !=
- AE_CTRL_TERMINATE) {
+ if (!strcmp(acpi_device_hid(device), ACPI_ECDT_HID)) {
+ is_ecdt = true;
+ ec = boot_ec;
+ } else {
+ ec = acpi_ec_alloc();
+ if (!ec)
+ return -ENOMEM;
+ status = ec_parse_device(device->handle, 0, ec, NULL);
+ if (status != AE_CTRL_TERMINATE) {
ret = -EINVAL;
goto err_alloc;
+ }
}

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;
- ret = acpi_config_boot_ec(ec, ec->handle, true, false);
+ boot_ec_is_ecdt = is_ecdt;
+ if (!is_ecdt) {
+ /*
+ * 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;
+ }
+ ret = acpi_config_boot_ec(ec, ec->handle, true, is_ecdt);
} else
ret = acpi_ec_setup(ec, true);
if (ret)
@@ -1635,8 +1644,10 @@ static int acpi_ec_add(struct acpi_device *device)
ret = !!request_region(ec->command_addr, 1, "EC cmd");
WARN(!ret, "Could not request EC cmd io port 0x%lx", ec->command_addr);

- /* Reprobe devices depending on the EC */
- acpi_walk_dep_device_list(ec->handle);
+ if (!is_ecdt) {
+ /* Reprobe devices depending on the EC */
+ acpi_walk_dep_device_list(ec->handle);
+ }
acpi_handle_debug(ec->handle, "enumerated.\n");
return 0;

@@ -1692,6 +1703,7 @@ ec_parse_io_ports(struct acpi_resource *resource, void *context)

static const struct acpi_device_id ec_device_ids[] = {
{"PNP0C09", 0},
+ {ACPI_ECDT_HID, 0},
{"", 0},
};

@@ -1764,11 +1776,14 @@ static int __init acpi_ec_ecdt_start(void)
* Note: ec->handle can be valid if this function is called after
* acpi_ec_add(), hence the fast path.
*/
- 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);
+ if (boot_ec->handle == ACPI_ROOT_OBJECT) {
+ if (!acpi_ec_ecdt_get_handle(&handle))
+ return -ENODEV;
+ boot_ec->handle = handle;
+ }
+
+ /* Register to ACPI bus with PM ops attached */
+ return acpi_bus_register_early_device(ACPI_BUS_TYPE_ECDT_EC);
}

#if 0
@@ -2020,6 +2035,12 @@ int __init acpi_ec_init(void)

/* Drivers must be started after acpi_ec_query_init() */
dsdt_fail = acpi_bus_register_driver(&acpi_ec_driver);
+ /*
+ * Register ECDT to ACPI bus only when PNP0C09 probe fails. This is
+ * useful for platforms (confirmed on ASUS X550ZE) with valid ECDT
+ * settings but invalid DSDT settings.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=196847
+ */
ecdt_fail = acpi_ec_ecdt_start();
return ecdt_fail && dsdt_fail ? -ENODEV : 0;
}
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 4361c44..ede83d3 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -115,6 +115,7 @@ bool acpi_device_is_present(const struct acpi_device *adev);
bool acpi_device_is_battery(struct acpi_device *adev);
bool acpi_device_is_first_physical_node(struct acpi_device *adev,
const struct device *dev);
+int acpi_bus_register_early_device(int type);

/* --------------------------------------------------------------------------
Device Matching and Notification
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 602f8ff..2f2f503 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1024,6 +1024,9 @@ static void acpi_device_get_busid(struct acpi_device *device)
case ACPI_BUS_TYPE_SLEEP_BUTTON:
strcpy(device->pnp.bus_id, "SLPF");
break;
+ case ACPI_BUS_TYPE_ECDT_EC:
+ strcpy(device->pnp.bus_id, "ECDT");
+ break;
default:
acpi_get_name(device->handle, ACPI_SINGLE_NAME, &buffer);
/* Clean up trailing underscores (if any) */
@@ -1304,6 +1307,9 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,
case ACPI_BUS_TYPE_SLEEP_BUTTON:
acpi_add_id(pnp, ACPI_BUTTON_HID_SLEEPF);
break;
+ case ACPI_BUS_TYPE_ECDT_EC:
+ acpi_add_id(pnp, ACPI_ECDT_HID);
+ break;
}
}

@@ -2049,6 +2055,21 @@ void acpi_bus_trim(struct acpi_device *adev)
}
EXPORT_SYMBOL_GPL(acpi_bus_trim);

+int acpi_bus_register_early_device(int type)
+{
+ struct acpi_device *device = NULL;
+ int result;
+
+ result = acpi_add_single_object(&device, NULL,
+ type, ACPI_STA_DEFAULT);
+ if (result)
+ return result;
+
+ device->flags.match_driver = true;
+ return device_attach(&device->dev);
+}
+EXPORT_SYMBOL_GPL(acpi_bus_register_early_device);
+
static int acpi_bus_scan_fixed(void)
{
int result = 0;
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index fa15052..324a04d 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -105,6 +105,7 @@ enum acpi_bus_device_type {
ACPI_BUS_TYPE_THERMAL,
ACPI_BUS_TYPE_POWER_BUTTON,
ACPI_BUS_TYPE_SLEEP_BUTTON,
+ ACPI_BUS_TYPE_ECDT_EC,
ACPI_BUS_DEVICE_TYPE_COUNT
};

diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h
index 29c6912..fd0aba7 100644
--- a/include/acpi/acpi_drivers.h
+++ b/include/acpi/acpi_drivers.h
@@ -58,6 +58,7 @@
#define ACPI_VIDEO_HID "LNXVIDEO"
#define ACPI_BAY_HID "LNXIOBAY"
#define ACPI_DOCK_HID "LNXDOCK"
+#define ACPI_ECDT_HID "LNXECDT"
/* Quirk for broken IBM BIOSes */
#define ACPI_SMBUS_IBM_HID "SMBUSIBM"

--
2.7.4