diff mbox series

PM / runtime: Add support for wake-up reason for wakeirqs

Message ID 20191009182803.63742-1-tony@atomide.com (mailing list archive)
State Not Applicable, archived
Headers show
Series PM / runtime: Add support for wake-up reason for wakeirqs | expand

Commit Message

Tony Lindgren Oct. 9, 2019, 6:28 p.m. UTC
With generic wakeirqs we can wake a device, but do not know if the
device woke to a wakeirq. Let's add pm_runtime_wakeup_is_wakeirq() so
a device can check the wake-up reason.

This can used for cases where a device wakes up on its own to a
wake-up interrupt, and the device PM runtime resume function can
then skip some or all of the device wake-up sequence based on
pm_runtime_wakeup_is_wakeirq().

Let's only add RPM_WAKEUP_NONE and RPM_WAKEUP_WAKEIRQ for now, other
events can be added later on if really needed.

Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 drivers/base/power/wakeirq.c | 14 ++++++++++++--
 include/linux/pm.h           | 14 ++++++++++++++
 include/linux/pm_runtime.h   |  6 ++++++
 3 files changed, 32 insertions(+), 2 deletions(-)

Comments

Alan Stern Oct. 9, 2019, 6:51 p.m. UTC | #1
On Wed, 9 Oct 2019, Tony Lindgren wrote:

> With generic wakeirqs we can wake a device, but do not know if the
> device woke to a wakeirq. Let's add pm_runtime_wakeup_is_wakeirq() so
> a device can check the wake-up reason.

People have tried many times over the years to do something like this.  
It's never right.

The problem is simple: It's impossible to know for certain why the
system woke up from suspend.  In fact, there may be many wakeup sources
all active at the same time, and any of them could be the one
responsible for actually waking the system.

All you can do is check to see whether a particular wakeup source is
active at the present moment.  You can't tell whether it was active in
the past (while the system was suspended) or whether it caused the
system to resume.

Alan Stern
Tony Lindgren Oct. 9, 2019, 7:55 p.m. UTC | #2
* Alan Stern <stern@rowland.harvard.edu> [191009 18:51]:
> On Wed, 9 Oct 2019, Tony Lindgren wrote:
> 
> > With generic wakeirqs we can wake a device, but do not know if the
> > device woke to a wakeirq. Let's add pm_runtime_wakeup_is_wakeirq() so
> > a device can check the wake-up reason.
> 
> People have tried many times over the years to do something like this.  
> It's never right.
> 
> The problem is simple: It's impossible to know for certain why the
> system woke up from suspend.  In fact, there may be many wakeup sources
> all active at the same time, and any of them could be the one
> responsible for actually waking the system.

Hmm yeah good point. Even with dedicated wakeirq it could race
against a timer for the wake-up event.

> All you can do is check to see whether a particular wakeup source is
> active at the present moment.  You can't tell whether it was active in
> the past (while the system was suspended) or whether it caused the
> system to resume.

We can actually do more than that now though :)

With handle_threaded_wake_irq() we could optionally call a handler
before we call pm_runtime_resume() and let the consumer device
driver figure out what the state is.

Regards,

Tony
diff mbox series

Patch

diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
--- a/drivers/base/power/wakeirq.c
+++ b/drivers/base/power/wakeirq.c
@@ -127,13 +127,18 @@  EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
 static irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq)
 {
 	struct wake_irq *wirq = _wirq;
+	struct device *dev = wirq->dev;
+	unsigned long flags;
 	int res;
 
+	spin_lock_irqsave(&dev->power.lock, flags);
+	dev->power.wakeup_reason = RPM_WAKEUP_WAKEIRQ;
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
 	/* Maybe abort suspend? */
 	if (irqd_is_wakeup_set(irq_get_irq_data(irq))) {
 		pm_wakeup_event(wirq->dev, 0);
-
-		return IRQ_HANDLED;
+		goto out_handled;
 	}
 
 	/* We don't want RPM_ASYNC or RPM_NOWAIT here */
@@ -142,6 +147,11 @@  static irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq)
 		dev_warn(wirq->dev,
 			 "wake IRQ with no resume: %i\n", res);
 
+out_handled:
+	spin_lock_irqsave(&dev->power.lock, flags);
+	dev->power.wakeup_reason = RPM_WAKEUP_NONE;
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
 	return IRQ_HANDLED;
 }
 
diff --git a/include/linux/pm.h b/include/linux/pm.h
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -523,6 +523,19 @@  enum rpm_request {
 	RPM_REQ_RESUME,
 };
 
+/*
+ * Device run-time wake-up reason types.
+ *
+ * RPM_WAKEUP_NONE	No wake-up reason
+ *
+ * RPM_WAKEUP_WAKEIRQ	Wake-up to a dedicated wakeirq
+ */
+
+enum rpm_wakeup_reason {
+	RPM_WAKEUP_NONE = 0,
+	RPM_WAKEUP_WAKEIRQ,
+};
+
 struct wakeup_source;
 struct wake_irq;
 struct pm_domain_data;
@@ -615,6 +628,7 @@  struct dev_pm_info {
 	unsigned int		use_autosuspend:1;
 	unsigned int		timer_autosuspends:1;
 	unsigned int		memalloc_noio:1;
+	unsigned int		wakeup_reason:1;
 	unsigned int		links_count;
 	enum rpm_request	request;
 	enum rpm_status		runtime_status;
diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h
--- a/include/linux/pm_runtime.h
+++ b/include/linux/pm_runtime.h
@@ -117,6 +117,11 @@  static inline bool pm_runtime_is_irq_safe(struct device *dev)
 	return dev->power.irq_safe;
 }
 
+static inline bool pm_runtime_wakeup_is_wakeirq(struct device *dev)
+{
+	return dev->power.wakeup_reason == RPM_WAKEUP_WAKEIRQ;
+}
+
 extern u64 pm_runtime_suspended_time(struct device *dev);
 
 #else /* !CONFIG_PM */
@@ -183,6 +188,7 @@  static inline void pm_runtime_get_suppliers(struct device *dev) {}
 static inline void pm_runtime_put_suppliers(struct device *dev) {}
 static inline void pm_runtime_new_link(struct device *dev) {}
 static inline void pm_runtime_drop_link(struct device *dev) {}
+static inline bool pm_runtime_wakeup_is_wakeirq(struct device *dev) { return false; }
 
 #endif /* !CONFIG_PM */