Some firmware delivers the power or sleep button press that woke the
system as an ordinary button press (Notify 0x80) shortly after resume,
rather than as the wakeup notification (Notify 0x02) the ACPI
specification requires for a button that is also a wake source.
On affected machines (e.g. the Framework Laptop 12, Intel Alder Lake-P)
the power button is a control-method device behind the embedded
controller. The EC latches the key press that woke the system across the
sleep transition and flushes it through its normal _Qxx query path as
soon as it is reinitialized on resume. The replayed press is
indistinguishable from a genuine one, so the kernel honors it as a fresh
suspend request and the machine immediately sleeps again -- an endless
resume->suspend loop that otherwise can only be broken by an s2idle
cycle.
The event cannot be filtered at its source: it arrives over the same EC
query path that also carries legitimate events (lid, AC, thermal,
battery), so suppressing the drain would lose real notifications.
Instead, record the time of resume and ignore a button-initiated suspend
that arrives within a short, sub-second window of it. The replay is
delivered synchronously with resume -- measured at a consistent ~162 ms
across many cycles on a Framework Laptop 12 -- whereas a deliberate press
cannot occur that quickly, as it happens well after the display is back.
This separates the replayed wake event from genuine input without
ignoring real presses for any perceptible time.
Spec-compliant firmware reports the wake as Notify 0x02, which is handled
on a different path and never reaches this check, so there is no change
in behavior on such systems.
The replay window is a fixed compile-time constant rather than a tunable
on purpose: it tracks a hardware characteristic -- the EC's post-resume
replay latency -- not a user policy, so there is no value a user would
meaningfully choose. A deterministic in-kernel check is also preferable
to the time-based userspace holdoff that other systems rely on (for
example systemd-logind's 30 s HoldoffTimeoutSec). That said, if
reviewers would rather it be adjustable, exposing it as a hw.acpi.* OID
is a trivial follow-up.
