• Rafael J. Wysocki's avatar
    PM: sleep: Simplify suspend-to-idle control flow · 56b99184
    Rafael J. Wysocki authored
    After commit 33e4f80e ("ACPI / PM: Ignore spurious SCI wakeups
    from suspend-to-idle") the "noirq" phases of device suspend and
    resume may run for multiple times during suspend-to-idle, if there
    are spurious system wakeup events while suspended.  However, this
    is complicated and fragile and actually unnecessary.
    
    The main reason for doing this is that on some systems the EC may
    signal system wakeup events (power button events, for example) as
    well as events that should not cause the system to resume (spurious
    system wakeup events).  Thus, in order to determine whether or not
    a given event signaled by the EC while suspended is a proper system
    wakeup one, the EC GPE needs to be dispatched and to start with that
    was achieved by allowing the ACPI SCI action handler to run, which
    was only possible after calling resume_device_irqs().
    
    However, dispatching the EC GPE this way turned out to take too much
    time in some cases and some EC events might be missed due to that, so
    commit 68e22011 ("ACPI: EC: Dispatch the EC GPE directly on
    s2idle wake") started to dispatch the EC GPE right after a wakeup
    event has been detected, so in fact the full ACPI SCI action handler
    doesn't need to run any more to deal with the wakeups coming from the
    EC.
    
    Use this observation to simplify the suspend-to-idle control flow
    so that the "noirq" phases of device suspend and resume are each
    run only once in every suspend-to-idle cycle, which is reported to
    significantly reduce power drawn by some systems when suspended to
    idle (by allowing them to reach a deep platform-wide low-power state
    through the suspend-to-idle flow).  [What appears to happen is that
    the "noirq" resume of devices after a spurious EC wakeup brings some
    devices into a state in which they prevent the platform from reaching
    the deep low-power state going forward, even after a subsequent
    "noirq" suspend phase, and on some systems the EC triggers such
    wakeups already when the "noirq" suspend of devices is running for
    the first time in the given suspend/resume cycle, so the platform
    cannot reach the deep low-power state at all.]
    
    First, make acpi_s2idle_wake() use the acpi_ec_dispatch_gpe() return
    value to determine whether or not the wakeup may have been triggered
    by the EC (in which case the system wakeup is canceled and ACPI
    events are processed in order to determine whether or not the event
    is a proper system wakeup one) and use rearm_wake_irq() (introduced
    by a previous change) in it to rearm the ACPI SCI for system wakeup
    detection in case the system will remain suspended.
    
    Second, drop acpi_s2idle_sync(), which is not needed any more, and
    the corresponding global platform suspend-to-idle callback.
    
    Next, drop the pm_wakeup_pending() check (which is an optimization
    only) from __device_suspend_noirq() to prevent it from returning
    errors on system wakeups occurring before the "noirq" phase of
    device suspend is complete (as in the case of suspend-to-idle it is
    not known whether or not these wakeups are suprious at that point),
    in order to avoid having to carry out a "noirq" resume of devices
    on a spurious system wakeup.
    
    Finally, change the code flow in s2idle_loop() to (1) run the
    "noirq" suspend of devices once before starting the loop, (2) check
    for spurious EC wakeups (via the platform ->wake callback) for the
    first time before calling s2idle_enter(), and (3) run the "noirq"
    resume of devices once after leaving the loop.
    Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
    Acked-by: default avatarThomas Gleixner <tglx@linutronix.de>
    56b99184
suspend.c 15.3 KB