From: Saranya Gopal <saranya.gopal@intel.com>
To: linux-acpi@vger.kernel.org
Cc: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org,
rafael.j.wysocki@intel.com
Subject: [PATCH] ACPI: s2idle: Handle shared _Ixx GPEs without an EC
Date: Fri, 30 Oct 2026 08:35:15 +0530 [thread overview]
Message-ID: <20261030030515.3127310-1-saranya.gopal@intel.com> (raw)
When first_ec is NULL, acpi_ec_dispatch_gpe() previously treated any
active GPE as a wake source. On platforms where an _Ixx GPE is shared
by user-wakeup and background events, that can cause unnecessary
wakeups.
Return -ENODEV when no EC instance is available so the s2idle wake
path can handle that case. On x86, check active GPEs against those
registered from _Ixx methods. Preserve immediate wakeup for active
non-_Ixx GPEs; otherwise cancel the SCI wake request and dispatch
active _Ixx GPEs. The common s2idle path then synchronizes event
processing and checks whether a driver requested wakeup. Lookup or
dispatch failures conservatively request wakeup.
The non-x86 fallback retains the previous any-GPE status check.
Fixes: 46767b17fda3 ("ACPI: PM: s2idle: Add _Ixx GPE wake indicator support")
Signed-off-by: Saranya Gopal <saranya.gopal@intel.com>
---
drivers/acpi/ec.c | 10 ++++-----
drivers/acpi/internal.h | 6 +++---
drivers/acpi/sleep.c | 18 +++++++++-------
drivers/acpi/sleep.h | 8 ++++++++
drivers/acpi/x86/s2idle.c | 43 +++++++++++++++++++++++++++++++++++++++
5 files changed, 70 insertions(+), 15 deletions(-)
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 64ad4cfa6208..8be572af4115 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -2155,19 +2155,19 @@ static bool acpi_ec_work_in_progress(struct acpi_ec *ec)
return ec->events_in_progress + ec->queries_in_progress > 0;
}
-bool acpi_ec_dispatch_gpe(void)
+int acpi_ec_dispatch_gpe(void)
{
bool work_in_progress = false;
if (!first_ec)
- return acpi_any_gpe_status_set(U32_MAX);
+ return -ENODEV;
/*
* Report wakeup if the status bit is set for any enabled GPE other
* than the EC one.
*/
if (acpi_any_gpe_status_set(first_ec->gpe))
- return true;
+ return 1;
/*
* Cancel the SCI wakeup and process all pending events in case there
@@ -2195,7 +2195,7 @@ bool acpi_ec_dispatch_gpe(void)
spin_unlock_irq(&first_ec->lock);
if (!work_in_progress)
- return false;
+ return 0;
pm_pr_dbg("ACPI EC GPE dispatched\n");
@@ -2212,7 +2212,7 @@ bool acpi_ec_dispatch_gpe(void)
spin_unlock_irq(&first_ec->lock);
} while (work_in_progress && !pm_wakeup_pending());
- return false;
+ return 0;
}
#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 40f875b265a9..8a602dea402e 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -236,7 +236,7 @@ void acpi_ec_register_opregions(struct acpi_device *adev);
#ifdef CONFIG_PM_SLEEP
void acpi_ec_flush_work(void);
-bool acpi_ec_dispatch_gpe(void);
+int acpi_ec_dispatch_gpe(void);
#endif
#else
@@ -256,9 +256,9 @@ static inline void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit
static inline void acpi_ec_register_opregions(struct acpi_device *adev) {}
static inline void acpi_ec_flush_work(void) {}
-static inline bool acpi_ec_dispatch_gpe(void)
+static inline int acpi_ec_dispatch_gpe(void)
{
- return false;
+ return -ENOTSUP;
}
#endif
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 132a9df98471..9366c3d927c2 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -787,13 +787,17 @@ bool acpi_s2idle_wake(void)
return true;
}
- /*
- * Check non-EC GPE wakeups and if there are none, cancel the
- * SCI-related wakeup and dispatch the EC GPE.
- */
- if (acpi_ec_dispatch_gpe()) {
- pm_pr_dbg("ACPI non-EC GPE wakeup\n");
- return true;
+ /* Check EC handling and other GPE wake sources. */
+ {
+ int ret = acpi_ec_dispatch_gpe();
+
+ if (ret == -ENODEV) {
+ if (acpi_x86_s2idle_check_ixx_gpes())
+ return true;
+ } else if (ret > 0) {
+ pm_pr_dbg("ACPI non-EC GPE wakeup\n");
+ return true;
+ }
}
acpi_os_wait_events_complete();
diff --git a/drivers/acpi/sleep.h b/drivers/acpi/sleep.h
index 9c3cb109c5d2..955e9c22bf0d 100644
--- a/drivers/acpi/sleep.h
+++ b/drivers/acpi/sleep.h
@@ -20,6 +20,14 @@ extern int acpi_s2idle_prepare(void);
extern bool acpi_s2idle_wake(void);
extern void acpi_s2idle_restore(void);
extern void acpi_s2idle_end(void);
+#if defined(CONFIG_X86) && defined(CONFIG_SUSPEND)
+extern bool acpi_x86_s2idle_check_ixx_gpes(void);
+#else
+static inline bool acpi_x86_s2idle_check_ixx_gpes(void)
+{
+ return acpi_any_gpe_status_set(U32_MAX);
+}
+#endif
extern void acpi_s2idle_setup(void);
diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c
index c278ea14c0b3..979684950f24 100644
--- a/drivers/acpi/x86/s2idle.c
+++ b/drivers/acpi/x86/s2idle.c
@@ -16,6 +16,7 @@
*/
#include <linux/acpi.h>
+#include <linux/bitmap.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/suspend.h>
@@ -24,6 +25,8 @@
#ifdef CONFIG_SUSPEND
+static DECLARE_BITMAP(acpi_s2idle_ixx_gpes, 256);
+
static void acpi_setup_ixx_gpes(void)
{
acpi_handle gpe_root;
@@ -47,11 +50,51 @@ static void acpi_setup_ixx_gpes(void)
acpi_mark_gpe_for_wake(NULL, i);
acpi_set_gpe_wake_mask(NULL, i, ACPI_GPE_ENABLE);
+ set_bit(i, acpi_s2idle_ixx_gpes);
pm_pr_dbg("ACPI: GPE0x%02x armed for wake via %s\n", i, gpe_nr_str);
}
}
+bool acpi_x86_s2idle_check_ixx_gpes(void)
+{
+ acpi_handle gpe_device;
+ acpi_event_status status;
+ u32 gpe;
+
+ for (gpe = 0; gpe < acpi_current_gpe_count; gpe++) {
+ if (ACPI_FAILURE(acpi_get_gpe_device(gpe, &gpe_device)))
+ return true;
+
+ if (!gpe_device && gpe < 256 &&
+ test_bit(gpe, acpi_s2idle_ixx_gpes))
+ continue;
+
+ if (ACPI_FAILURE(acpi_get_gpe_status(gpe_device, gpe, &status)))
+ return true;
+
+ if ((status & ACPI_EVENT_FLAG_STATUS_SET) &&
+ (status & ACPI_EVENT_FLAG_ENABLE_SET))
+ return true;
+ }
+
+ pm_system_cancel_wakeup();
+
+ for_each_set_bit(gpe, acpi_s2idle_ixx_gpes, 256) {
+ if (ACPI_FAILURE(acpi_get_gpe_status(NULL, gpe, &status)))
+ return true;
+ if (!(status & ACPI_EVENT_FLAG_STATUS_SET) ||
+ !(status & ACPI_EVENT_FLAG_ENABLE_SET))
+ continue;
+
+ pm_pr_dbg("ACPI _Ixx GPE 0x%02x status set\n", gpe);
+ if (acpi_dispatch_gpe(NULL, gpe) != ACPI_INTERRUPT_HANDLED)
+ return true;
+ }
+
+ return false;
+}
+
static bool sleep_no_lps0 __read_mostly;
module_param(sleep_no_lps0, bool, 0644);
MODULE_PARM_DESC(sleep_no_lps0, "Do not use the special LPS0 device interface");
--
2.34.1
next reply other threads:[~2026-10-30 3:05 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 3:05 Saranya Gopal [this message]
2026-10-30 22:00 ` kernel test robot
2026-10-31 3:01 ` kernel test robot
2026-10-31 4:49 ` kernel test robot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261030030515.3127310-1-saranya.gopal@intel.com \
--to=saranya.gopal@intel.com \
--cc=linux-acpi@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=rafael.j.wysocki@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®