Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 33e4f80e authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

ACPI / PM: Ignore spurious SCI wakeups from suspend-to-idle



The ACPI SCI (System Control Interrupt) is set up as a wakeup IRQ
during suspend-to-idle transitions and, consequently, any events
signaled through it wake up the system from that state.  However,
on some systems some of the events signaled via the ACPI SCI while
suspended to idle should not cause the system to wake up.  In fact,
quite often they should just be discarded.

Arguably, systems should not resume entirely on such events, but in
order to decide which events really should cause the system to resume
and which are spurious, it is necessary to resume up to the point
when ACPI SCIs are actually handled and processed, which is after
executing dpm_resume_noirq() in the system resume path.

For this reasons, add a loop around freeze_enter() in which the
platforms can process events signaled via multiplexed IRQ lines
like the ACPI SCI and add suspend-to-idle hooks that can be
used for this purpose to struct platform_freeze_ops.

In the ACPI case, the ->wake hook is used for checking if the SCI
has triggered while suspended and deferring the interrupt-induced
system wakeup until the events signaled through it are actually
processed sufficiently to decide whether or not the system should
resume.  In turn, the ->sync hook allows all of the relevant event
queues to be flushed so as to prevent events from being missed due
to race conditions.

In addition to that, some ACPI code processing wakeup events needs
to be modified to use the "hard" version of wakeup triggers, so that
it will cause a system resume to happen on device-induced wakeup
events even if the "soft" mechanism to prevent the system from
suspending is not enabled.  However, to preserve the existing
behavior with respect to suspend-to-RAM, this only is done in
the suspend-to-idle case and only if an SCI has occurred while
suspended.

Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 63dada87
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -782,7 +782,7 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume)
	if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) ||
	    (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
            (battery->capacity_now <= battery->alarm)))
		pm_wakeup_event(&battery->device->dev, 0);
		acpi_pm_wakeup_event(&battery->device->dev);

	return result;
}
+3 −2
Original line number Diff line number Diff line
@@ -217,7 +217,7 @@ static int acpi_lid_notify_state(struct acpi_device *device, int state)
	}

	if (state)
		pm_wakeup_event(&device->dev, 0);
		acpi_pm_wakeup_event(&device->dev);

	ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device);
	if (ret == NOTIFY_DONE)
@@ -402,7 +402,7 @@ static void acpi_button_notify(struct acpi_device *device, u32 event)
		} else {
			int keycode;

			pm_wakeup_event(&device->dev, 0);
			acpi_pm_wakeup_event(&device->dev);
			if (button->suspended)
				break;

@@ -534,6 +534,7 @@ static int acpi_button_add(struct acpi_device *device)
		lid_device = device;
	}

	device_init_wakeup(&device->dev, true);
	printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
	return 0;

+8 −1
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <linux/pm_qos.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/suspend.h>

#include "internal.h"

@@ -385,6 +386,12 @@ EXPORT_SYMBOL(acpi_bus_power_manageable);
#ifdef CONFIG_PM
static DEFINE_MUTEX(acpi_pm_notifier_lock);

void acpi_pm_wakeup_event(struct device *dev)
{
	pm_wakeup_dev_event(dev, 0, acpi_s2idle_wakeup());
}
EXPORT_SYMBOL_GPL(acpi_pm_wakeup_event);

static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
{
	struct acpi_device *adev;
@@ -399,7 +406,7 @@ static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
	mutex_lock(&acpi_pm_notifier_lock);

	if (adev->wakeup.flags.notifier_present) {
		__pm_wakeup_event(adev->wakeup.ws, 0);
		pm_wakeup_ws_event(adev->wakeup.ws, 0, acpi_s2idle_wakeup());
		if (adev->wakeup.context.func)
			adev->wakeup.context.func(&adev->wakeup.context);
	}
+2 −0
Original line number Diff line number Diff line
@@ -198,8 +198,10 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit);
                                  Suspend/Resume
  -------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT
extern bool acpi_s2idle_wakeup(void);
extern int acpi_sleep_init(void);
#else
static inline bool acpi_s2idle_wakeup(void) { return false; }
static inline int acpi_sleep_init(void) { return -ENXIO; }
#endif

+37 −0
Original line number Diff line number Diff line
@@ -650,6 +650,8 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = {
	.recover = acpi_pm_finish,
};

static bool s2idle_wakeup;

static int acpi_freeze_begin(void)
{
	acpi_scan_lock_acquire();
@@ -666,6 +668,33 @@ static int acpi_freeze_prepare(void)
	return 0;
}

static void acpi_freeze_wake(void)
{
	/*
	 * If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means
	 * that the SCI has triggered while suspended, so cancel the wakeup in
	 * case it has not been a wakeup event (the GPEs will be checked later).
	 */
	if (acpi_sci_irq_valid() &&
	    !irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) {
		pm_system_cancel_wakeup();
		s2idle_wakeup = true;
	}
}

static void acpi_freeze_sync(void)
{
	/*
	 * Process all pending events in case there are any wakeup ones.
	 *
	 * The EC driver uses the system workqueue, so that one needs to be
	 * flushed too.
	 */
	acpi_os_wait_events_complete();
	flush_scheduled_work();
	s2idle_wakeup = false;
}

static void acpi_freeze_restore(void)
{
	if (acpi_sci_irq_valid())
@@ -682,6 +711,8 @@ static void acpi_freeze_end(void)
static const struct platform_freeze_ops acpi_freeze_ops = {
	.begin = acpi_freeze_begin,
	.prepare = acpi_freeze_prepare,
	.wake = acpi_freeze_wake,
	.sync = acpi_freeze_sync,
	.restore = acpi_freeze_restore,
	.end = acpi_freeze_end,
};
@@ -700,9 +731,15 @@ static void acpi_sleep_suspend_setup(void)
}

#else /* !CONFIG_SUSPEND */
#define s2idle_wakeup	(false)
static inline void acpi_sleep_suspend_setup(void) {}
#endif /* !CONFIG_SUSPEND */

bool acpi_s2idle_wakeup(void)
{
	return s2idle_wakeup;
}

#ifdef CONFIG_PM_SLEEP
static u32 saved_bm_rld;

Loading