From 7b301750f7f8f6503e11f1af4a03832525f58c66 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 9 May 2020 10:44:41 +0200 Subject: ACPI: EC: PM: Avoid premature returns from acpi_s2idle_wake() If the EC GPE status is not set after checking all of the other GPEs, acpi_s2idle_wake() returns 'false', to indicate that the SCI event that has just triggered is not a system wakeup one, but it does that without canceling the pending wakeup and re-arming the SCI for system wakeup which is a mistake, because it may cause s2idle_loop() to busy spin until the next valid wakeup event. [If that happens, the first spurious wakeup is still pending after acpi_s2idle_wake() has returned, so s2idle_enter() does nothing, acpi_s2idle_wake() is called again and it sees that the SCI has triggered, but no GPEs are active, so 'false' is returned again, and so on.] Fix that by moving all of the GPE checking logic from acpi_s2idle_wake() to acpi_ec_dispatch_gpe() and making the latter return 'true' only if a non-EC GPE has triggered and 'false' otherwise, which will cause acpi_s2idle_wake() to cancel the pending SCI wakeup and re-arm the SCI for system wakeup regardless of the EC GPE status. This also addresses a lockup observed on an Elitegroup EF20EA laptop after attempting to wake it up from suspend-to-idle by a key press. Fixes: d5406284ff80 ("ACPI: PM: s2idle: Refine active GPEs check") Link: https://bugzilla.kernel.org/show_bug.cgi?id=207603 Reported-by: Todd Brandt Fixes: fdde0ff8590b ("ACPI: PM: s2idle: Prevent spurious SCIs from waking up the system") Link: https://lore.kernel.org/linux-acpi/CAB4CAwdqo7=MvyG_PE+PGVfeA17AHF5i5JucgaKqqMX6mjArbQ@mail.gmail.com/ Reported-by: Chris Chiu Tested-by: Chris Chiu Cc: 5.4+ # 5.4+ Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sleep.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'drivers/acpi/sleep.c') diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 4edc8a3ce40f..3850704570c0 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -1013,20 +1013,10 @@ static bool acpi_s2idle_wake(void) if (acpi_check_wakeup_handlers()) return true; - /* - * If the status bit is set for any enabled GPE other than the - * EC one, the wakeup is regarded as a genuine one. - */ - if (acpi_ec_other_gpes_active()) + /* Check non-EC GPE wakeups and dispatch the EC GPE. */ + if (acpi_ec_dispatch_gpe()) return true; - /* - * If the EC GPE status bit has not been set, the wakeup is - * regarded as a spurious one. - */ - if (!acpi_ec_dispatch_gpe()) - return false; - /* * Cancel the wakeup and process all pending events in case * there are any wakeup ones in there. -- cgit v1.2.3 From 607b9df63057a56f6172d560d5366cca6a030c76 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 15 May 2020 12:58:19 +0200 Subject: ACPI: EC: PM: Avoid flushing EC work when EC GPE is inactive Flushing the EC work while suspended to idle when the EC GPE status is not set causes some EC wakeup events (notably power button and lid ones) to be missed after a series of spurious wakeups on the Dell XPS13 9360 in my office. If that happens, the machine cannot be woken up from suspend-to-idle by the power button or lid status change and it needs to be woken up in some other way (eg. by a key press). Flushing the EC work only after successful dispatching the EC GPE, which means that its status has been set, avoids the issue, so change the code in question accordingly. Fixes: 7b301750f7f8 ("ACPI: EC: PM: Avoid premature returns from acpi_s2idle_wake()") Cc: 5.4+ # 5.4+ Signed-off-by: Rafael J. Wysocki Tested-by: Chris Chiu --- drivers/acpi/ec.c | 6 +++++- drivers/acpi/sleep.c | 15 ++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers/acpi/sleep.c') diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 145ec0b6f20b..1af2125e17d5 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -2016,9 +2016,13 @@ bool acpi_ec_dispatch_gpe(void) * to allow the caller to process events properly after that. */ ret = acpi_dispatch_gpe(NULL, first_ec->gpe); - if (ret == ACPI_INTERRUPT_HANDLED) + if (ret == ACPI_INTERRUPT_HANDLED) { pm_pr_dbg("EC GPE dispatched\n"); + /* Flush the event and query workqueues. */ + acpi_ec_flush_work(); + } + return false; } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 3850704570c0..fd9d4e8318e9 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -980,13 +980,6 @@ static int acpi_s2idle_prepare_late(void) return 0; } -static void acpi_s2idle_sync(void) -{ - /* The EC driver uses special workqueues that need to be flushed. */ - acpi_ec_flush_work(); - acpi_os_wait_events_complete(); /* synchronize Notify handling */ -} - static bool acpi_s2idle_wake(void) { if (!acpi_sci_irq_valid()) @@ -1018,7 +1011,7 @@ static bool acpi_s2idle_wake(void) return true; /* - * Cancel the wakeup and process all pending events in case + * Cancel the SCI wakeup and process all pending events in case * there are any wakeup ones in there. * * Note that if any non-EC GPEs are active at this point, the @@ -1026,8 +1019,7 @@ static bool acpi_s2idle_wake(void) * should be missed by canceling the wakeup here. */ pm_system_cancel_wakeup(); - - acpi_s2idle_sync(); + acpi_os_wait_events_complete(); /* * The SCI is in the "suspended" state now and it cannot produce @@ -1060,7 +1052,8 @@ static void acpi_s2idle_restore(void) * of GPEs. */ acpi_os_wait_events_complete(); /* synchronize GPE processing */ - acpi_s2idle_sync(); + acpi_ec_flush_work(); /* flush the EC driver's workqueues */ + acpi_os_wait_events_complete(); /* synchronize Notify handling */ s2idle_wakeup = false; -- cgit v1.2.3