ACPI: EC: Fix acpi_ec_dispatch_gpe()
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 27 Jun 2023 14:35:52 +0000 (16:35 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 27 Jun 2023 15:06:51 +0000 (17:06 +0200)
Commit 896e97bf99ec ("ACPI: EC: Clear GPE on interrupt handling only")
broke suspend-to-idle at least on Dell XPS13 9360 and 9380.

The problem is that acpi_ec_dispatch_gpe() must clear the EC GPE,
because the EC GPE handler never runs when the system is in the
suspend-to-idle state and if the EC GPE is not cleared by the suspend-
to-idle loop, it is never cleared at all which leads to a GPE storm.
This causes suspend-to-idle to burn energy instead of saving it which
is potentially dangerous (the affected machines heat up rather badly
when that happens).

Addess this by making acpi_ec_dispatch_gpe() clear the EC GPE as it did
before.

Fixes: 896e97bf99ec ("ACPI: EC: Clear GPE on interrupt handling only")
Tested-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/ec.c

index 8569f55e55b6c9d269c5a42156be1f334caff5d6..660834a49c1f9ead66252f74685c2bbf8b90b2f4 100644 (file)
@@ -1267,12 +1267,8 @@ static void acpi_ec_event_handler(struct work_struct *work)
        spin_unlock_irq(&ec->lock);
 }
 
-static void acpi_ec_handle_interrupt(struct acpi_ec *ec)
+static void clear_gpe_and_advance_transaction(struct acpi_ec *ec, bool interrupt)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&ec->lock, flags);
-
        /*
         * Clear GPE_STS upfront to allow subsequent hardware GPE_STS 0->1
         * changes to always trigger a GPE interrupt.
@@ -1289,6 +1285,16 @@ static void acpi_ec_handle_interrupt(struct acpi_ec *ec)
                acpi_clear_gpe(NULL, ec->gpe);
 
        advance_transaction(ec, true);
+}
+
+static void acpi_ec_handle_interrupt(struct acpi_ec *ec)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ec->lock, flags);
+
+       clear_gpe_and_advance_transaction(ec, true);
+
        spin_unlock_irqrestore(&ec->lock, flags);
 }
 
@@ -2083,7 +2089,7 @@ bool acpi_ec_dispatch_gpe(void)
        if (acpi_ec_gpe_status_set(first_ec)) {
                pm_pr_dbg("ACPI EC GPE status set\n");
 
-               advance_transaction(first_ec, false);
+               clear_gpe_and_advance_transaction(first_ec, false);
                work_in_progress = acpi_ec_work_in_progress(first_ec);
        }