linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCHv3 0/5] Linux generic wakeirq handling
@ 2015-05-13 23:36 Tony Lindgren
  2015-05-13 23:36 ` [PATCH 1/5] PM / Runtime: Update last_busy in rpm_resume Tony Lindgren
                   ` (4 more replies)
  0 siblings, 5 replies; 43+ messages in thread
From: Tony Lindgren @ 2015-05-13 23:36 UTC (permalink / raw)
  To: Rafael J. Wysocki, Alan Stern
  Cc: Andreas Fenkart, Greg Kroah-Hartman, Felipe Balbi, Huiquan Zhong,
	Kevin Hilman, NeilBrown, Mika Westerberg, Nishanth Menon,
	Peter Hurley, Sebastian Andrzej Siewior, Ulf Hansson,
	Thomas Gleixner, linux-pm, linux-kernel, linux-serial,
	linux-omap

Hi all,

Here's the third version of the Linux generic wakeirq support.

I've added support for automatically handling wake-up capable
IO interrupts too as suggested by Rafael the last time around.

I think I've addressed all the comments, please review and let
me know if I've missed something.

For reference, the two earlier versions can be found at:

http://www.spinics.net/lists/arm-kernel/msg377300.html
https://lwn.net/Articles/635904/

Regards,

Tony


Tony Lindgren (5):
  PM / Runtime: Update last_busy in rpm_resume
  PM / Wakeirq: Add automated device wake IRQ handling
  serial: omap: Switch wake-up interrupt to generic wakeirq
  serial: 8250_omap: Move wake-up interrupt to generic wakeirq
  mmc: omap_hsmmc: Change wake-up interrupt to use generic wakeirq

 arch/arm/mach-omap2/Kconfig         |   1 +
 drivers/base/power/Makefile         |   1 +
 drivers/base/power/main.c           |   2 +
 drivers/base/power/power.h          |  38 +++++
 drivers/base/power/runtime.c        |   1 +
 drivers/base/power/wakeirq.c        | 316 ++++++++++++++++++++++++++++++++++++
 drivers/base/power/wakeup.c         |  96 +++++++++++
 drivers/mmc/host/omap_hsmmc.c       |  51 +-----
 drivers/tty/serial/8250/8250_omap.c |  61 +------
 drivers/tty/serial/omap-serial.c    |  38 +----
 include/linux/pm.h                  |   2 +
 include/linux/pm_wakeirq.h          |  72 ++++++++
 include/linux/pm_wakeup.h           |   7 +
 kernel/power/Kconfig                |   4 +
 14 files changed, 562 insertions(+), 128 deletions(-)
 create mode 100644 drivers/base/power/wakeirq.c
 create mode 100644 include/linux/pm_wakeirq.h

-- 
2.1.4


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH 1/5] PM / Runtime: Update last_busy in rpm_resume
  2015-05-13 23:36 [PATCHv3 0/5] Linux generic wakeirq handling Tony Lindgren
@ 2015-05-13 23:36 ` Tony Lindgren
  2015-05-20  7:36   ` Ulf Hansson
  2015-05-13 23:36 ` [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling Tony Lindgren
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-13 23:36 UTC (permalink / raw)
  To: Rafael J. Wysocki, Alan Stern
  Cc: Andreas Fenkart, Greg Kroah-Hartman, Felipe Balbi, Huiquan Zhong,
	Kevin Hilman, NeilBrown, Mika Westerberg, Nishanth Menon,
	Peter Hurley, Sebastian Andrzej Siewior, Ulf Hansson,
	Thomas Gleixner, linux-pm, linux-kernel, linux-serial,
	linux-omap

If we don't update last_busy in rpm_resume, devices can go back
to sleep immediately after resume. This happens at least in
cases where the device has been powered off and does not have
any interrupt pending until there's something in the FIFO.

Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 drivers/base/power/runtime.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 5070c4f..4ffe4a2 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -741,6 +741,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
 	} else {
  no_callback:
 		__update_runtime_status(dev, RPM_ACTIVE);
+		pm_runtime_mark_last_busy(dev);
 		if (parent)
 			atomic_inc(&parent->power.child_count);
 	}
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-13 23:36 [PATCHv3 0/5] Linux generic wakeirq handling Tony Lindgren
  2015-05-13 23:36 ` [PATCH 1/5] PM / Runtime: Update last_busy in rpm_resume Tony Lindgren
@ 2015-05-13 23:36 ` Tony Lindgren
  2015-05-14  2:06   ` Felipe Balbi
  2015-05-13 23:36 ` [PATCH 3/5] serial: omap: Switch wake-up interrupt to generic wakeirq Tony Lindgren
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-13 23:36 UTC (permalink / raw)
  To: Rafael J. Wysocki, Alan Stern
  Cc: Andreas Fenkart, Greg Kroah-Hartman, Felipe Balbi, Huiquan Zhong,
	Kevin Hilman, NeilBrown, Mika Westerberg, Nishanth Menon,
	Peter Hurley, Sebastian Andrzej Siewior, Ulf Hansson,
	Thomas Gleixner, linux-pm, linux-kernel, linux-serial,
	linux-omap

Turns out we can automate the handling for the device_may_wakeup()
quite a bit by using the kernel wakeup source list.

And as some hardware has separate dedicated wake-up interrupt
in addition to the IO interrupt, we can automate the handling by
adding a generic threaded interrupt handler that just calls the
device PM runtime to wake up the device.

This allows dropping code from device drivers as we currently
are doing it in multiple ways, and often wrong.

For most drivers, we should be able to drop the following
boilerplate code from runtime_suspend and runtime_resume
functions:

	...
	if (device_may_wakeup(dev)
		enable_irq_wake(irq);
	...
	if (device_may_wakeup(dev)
		enable_irq_wake(irq);
	...

We can replace it with just the following init and exit
time code:

	...
	device_init_wakeup(dev, true);
	dev_pm_set_wake_irq(dev, irq);
	...
	dev_pm_clear_wake_irq(dev);
	device_init_wakeup(dev, false);
	...

And for hardware with dedicated wake-up interrupts:

	...
	device_init_wakeup(dev, true);
	dev_pm_request_wake_irq(dev, wakeirq, NULL, 0, NULL);
	...
	dev_pm_enable_wake_irq(dev);
	...
	dev_pm_disable_wake_irq(dev);
	...
	dev_pm_free_wake_irq(dev);
	device_init_wakeup(dev, false);
	...

For now, let's only enable it for select PM_WAKEIRQ.

Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 arch/arm/mach-omap2/Kconfig  |   1 +
 drivers/base/power/Makefile  |   1 +
 drivers/base/power/main.c    |   2 +
 drivers/base/power/power.h   |  38 ++++++
 drivers/base/power/wakeirq.c | 316 +++++++++++++++++++++++++++++++++++++++++++
 drivers/base/power/wakeup.c  |  96 +++++++++++++
 include/linux/pm.h           |   2 +
 include/linux/pm_wakeirq.h   |  72 ++++++++++
 include/linux/pm_wakeup.h    |   7 +
 kernel/power/Kconfig         |   4 +
 10 files changed, 539 insertions(+)
 create mode 100644 drivers/base/power/wakeirq.c
 create mode 100644 include/linux/pm_wakeirq.h

diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 6468f15..ac7b570 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -85,6 +85,7 @@ config ARCH_OMAP2PLUS
 	select OMAP_DM_TIMER
 	select OMAP_GPMC
 	select PINCTRL
+	select PM_WAKEIRQ if PM_SLEEP
 	select SOC_BUS
 	select TI_PRIV_EDMA
 	select OMAP_IRQCHIP
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 1cb8544..527546e 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -4,5 +4,6 @@ obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 obj-$(CONFIG_PM_OPP)	+= opp.o
 obj-$(CONFIG_PM_GENERIC_DOMAINS)	+=  domain.o domain_governor.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
+obj-$(CONFIG_PM_WAKEIRQ)	+= wakeirq.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 3d874ec..24515e7 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -587,6 +587,7 @@ void dpm_resume_noirq(pm_message_t state)
 	async_synchronize_full();
 	dpm_show_time(starttime, state, "noirq");
 	resume_device_irqs();
+	device_wakeup_disarm_wakeirqs();
 	cpuidle_resume();
 	trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
 }
@@ -1104,6 +1105,7 @@ int dpm_suspend_noirq(pm_message_t state)
 
 	trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
 	cpuidle_pause();
+	device_wakeup_arm_wakeirqs();
 	suspend_device_irqs();
 	mutex_lock(&dpm_list_mtx);
 	pm_transition = state;
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index b6b8a27..6183c5d 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -20,6 +20,44 @@ static inline void pm_runtime_early_init(struct device *dev)
 extern void pm_runtime_init(struct device *dev);
 extern void pm_runtime_remove(struct device *dev);
 
+#ifdef CONFIG_PM_WAKEIRQ
+
+extern int device_wakeup_attach_irq(struct device *dev,
+				    struct wake_irq *wakeirq);
+extern void device_wakeup_detach_irq(struct device *dev);
+extern void device_wakeup_arm_wakeirqs(void);
+extern void device_wakeup_disarm_wakeirqs(void);
+
+extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
+extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
+
+#else
+
+static inline int
+device_wakeup_attach_irq(struct device *dev,
+			 struct wake_irq *wakeirq)
+{
+	return 0;
+}
+
+static inline void device_wakeup_detach_irq(struct device *dev)
+{
+}
+
+static inline void device_wakeup_arm_wakeirqs(void)
+{
+}
+
+static inline void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+}
+
+static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+}
+
+#endif /* CONFIG_PM_WAKEIRQ */
+
 /*
  * sysfs.c
  */
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
new file mode 100644
index 0000000..579157b
--- /dev/null
+++ b/drivers/base/power/wakeirq.c
@@ -0,0 +1,316 @@
+/*
+ * wakeirq.c - Device wakeirq helper functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
+
+#include "power.h"
+
+/**
+ * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ
+ * @dev: Device entry
+ * @irq: Device wake-up capable interrupt
+ * @wirq: Wake irq specific data
+ *
+ * Internal function to attach either a device IO interrupt or a
+ * dedicated wake-up interrupt as a wake IRQ.
+ */
+static int dev_pm_attach_wake_irq(struct device *dev, int irq,
+				  struct wake_irq *wirq)
+{
+	unsigned long flags;
+	int err;
+
+	if (!dev || !wirq)
+		return -EINVAL;
+
+	if (!dev->power.wakeup) {
+		dev_err(dev, "forgot to call call device_init_wakeup?\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+	if (WARN_ON(dev->power.wakeirq)) {
+		dev_err(dev, "wake irq already initialized\n");
+		err = -EEXIST;
+		goto err_unlock;
+	}
+
+	dev->power.wakeirq = wirq;
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	err = device_wakeup_attach_irq(dev, wirq);
+	if (err)
+		goto err_free_mem;
+
+	return 0;
+
+err_unlock:
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+err_free_mem:
+
+	return err;
+}
+
+/**
+ * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ
+ * @dev: Device entry
+ * @irq: Device IO interrupt
+ *
+ * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets
+ * automatically configured for wake-up from suspend  based
+ * on the device specific sysfs wakeup entry. Typically called
+ * during driver probe after calling device_init_wakeup().
+ */
+int dev_pm_set_wake_irq(struct device *dev, int irq)
+{
+	struct wake_irq *wirq;
+	int err;
+
+	wirq = devm_kzalloc(dev, sizeof(*wirq), GFP_KERNEL);
+	if (!wirq)
+		return -ENOMEM;
+
+	wirq->dev = dev;
+	wirq->irq = irq;
+
+	err = dev_pm_attach_wake_irq(dev, irq, wirq);
+	if (err)
+		devm_kfree(dev, wirq);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq);
+
+/**
+ * dev_pm_clear_wake_irq - Detach a device IO interrupt wake IRQ
+ * @dev: Device entry
+ *
+ * Detach a device IO interrupt wake IRQ and free resources.
+ */
+void dev_pm_clear_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+	unsigned long flags;
+
+	if (!wirq)
+		return;
+
+	device_wakeup_detach_irq(dev);
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+	dev->power.wakeirq = NULL;
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	wirq->irq = -EINVAL;
+	devm_kfree(dev, wirq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
+
+/**
+ * handle_threaded_wakeirq - Handler for dedicated wake-up interrupts
+ * @irq: Device dedicated wake-up interrupt
+ * @_wirq: Wake IRQ data
+ *
+ * Some devices have a separate wake-up interrupt in addition to the
+ * device IO interrupt. The wake-up interrupts signal that the device
+ * should be woken up from a idle state. This handler uses device
+ * specific pm_runtime functions to wake the device and then it's
+ * up to the device to do whatever it needs to. Note as the device
+ * may need to restore context and start up regulators, we use a
+ * threaded IRQ.
+ *
+ * Also note that we are not resending the lost device interrupts.
+ * We assume that the wake-up interrupt just needs to wake-up the
+ * device, and the device pm_runtime_resume() can deal with the
+ * situation.
+ */
+static irqreturn_t handle_threaded_wakeirq(int wakeirq, void *_wirq)
+{
+	struct wake_irq *wirq = _wirq;
+	irqreturn_t ret = IRQ_NONE;
+
+	if (!pm_runtime_suspended(wirq->dev))
+		goto out;
+
+	/* We don't want RPM_ASYNC or RPM_NOWAIT here */
+	pm_runtime_resume(wirq->dev);
+	ret = IRQ_HANDLED;
+
+	if (wirq->handler)
+		ret = wirq->handler(wakeirq, wirq->data);
+out:
+	return ret;
+}
+
+/**
+ * dev_pm_request_wake_irq - Request a dedicated wake-up interrupt
+ * @dev: Device entry
+ * @irq: Device wake-up interrupt
+ * @handler: Optional device specific handler
+ * @irqflags: Optional irqflags, IRQF_ONESHOT if not specified
+ * @data: Optional device specific data
+ *
+ * Unless your hardware has separate wake-up interrupts in addition
+ * to the device IO interrupts, you don't need this.
+ *
+ * Sets up a threaded interrupt handler for a device that has
+ * a dedicated wake-up interrupt in addition to the device IO
+ * interrupt.
+ *
+ * The interrupt starts disabled, and needs to be managed for
+ * the device by the bus code or the device driver using
+ * dev_pm_enable_wake_irq() and dev_pm_disable_wake_irq()
+ * functions.
+ */
+int dev_pm_request_wake_irq(struct device *dev,
+			    int irq,
+			    irq_handler_t handler,
+			    unsigned long irqflags,
+			    void *data)
+{
+	struct wake_irq *wirq;
+	int err;
+
+	wirq = devm_kzalloc(dev, sizeof(*wirq), GFP_KERNEL);
+	if (!wirq)
+		return -ENOMEM;
+
+	wirq->dev = dev;
+	wirq->irq = irq;
+	wirq->handler = handler;
+	wirq->data = data;
+	if (!irqflags) {
+		irqflags = IRQF_ONESHOT;
+		wirq->manage_irq = true;
+	}
+	irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+	/*
+	 * Consumer device may need to power up and restore state
+	 * so we use a threaded irq.
+	 */
+	err = devm_request_threaded_irq(dev, irq, handler,
+					handle_threaded_wakeirq,
+					irqflags, dev_name(dev),
+					wirq);
+	if (err)
+		goto err_free;
+
+	err = dev_pm_attach_wake_irq(dev, irq, wirq);
+	if (err)
+		goto err_free_irq;
+
+	return err;
+
+err_free_irq:
+	devm_free_irq(dev, irq, wirq);
+err_free:
+	devm_kfree(dev, wirq);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_request_wake_irq);
+
+/**
+ * dev_pm_free_wake_irq - Free a wake-up interrupt
+ * @wirq: Device wake-up interrupt
+ */
+void dev_pm_free_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+	unsigned long flags;
+
+	if (!wirq)
+		return;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+	wirq->manage_irq = false;
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+	devm_free_irq(wirq->dev, wirq->irq, wirq);
+	dev_pm_clear_wake_irq(dev);
+}
+EXPORT_SYMBOL_GPL(dev_pm_free_wake_irq);
+
+/**
+ * dev_pm_enable_wake_irq - Enable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_suspend() to enable the wake-up interrupt while
+ * the device is running.
+ *
+ * Note that for runtime_suspend()) the wake-up interrupts
+ * should be unconditionally enabled unlike for suspend()
+ * that is conditional.
+ */
+void dev_pm_enable_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+
+	if (wirq && wirq->manage_irq)
+		enable_irq(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
+
+/**
+ * dev_pm_disable_wake_irq - Disable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_resume() to disable the wake-up interrupt while
+ * the device is running.
+ */
+void dev_pm_disable_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+
+	if (wirq && wirq->manage_irq)
+		disable_irq_nosync(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
+
+/**
+ * dev_pm_arm_wake_irq - Arm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Sets up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+	if (!wirq)
+		return;
+
+	if (device_may_wakeup(wirq->dev))
+		enable_irq_wake(wirq->irq);
+}
+
+/**
+ * dev_pm_disarm_wake_irq - Disarm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Clears up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+	if (!wirq)
+		return;
+
+	if (device_may_wakeup(wirq->dev))
+		disable_irq_wake(wirq->irq);
+}
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 7726200..47ae173 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -11,9 +11,11 @@
 #include <linux/sched.h>
 #include <linux/capability.h>
 #include <linux/export.h>
+#include <linux/interrupt.h>
 #include <linux/suspend.h>
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
+#include <linux/pm_wakeirq.h>
 #include <trace/events/power.h>
 
 #include "power.h"
@@ -238,6 +240,100 @@ int device_wakeup_enable(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(device_wakeup_enable);
 
+#ifdef CONFIG_PM_WAKEIRQ
+
+/**
+ * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source
+ * @dev: Device to handle
+ * @irq: Device specific wakeirq entry
+ *
+ * Attach a device specific wakeirq to the device specific
+ * wakeup source so the device wakeirq can be configured
+ * automatically for suspend and resume.
+ */
+int device_wakeup_attach_irq(struct device *dev,
+			     struct wake_irq *wakeirq)
+{
+	struct wakeup_source *ws;
+	int ret = 0;
+
+	spin_lock_irq(&dev->power.lock);
+	ws = dev->power.wakeup;
+	if (!ws) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	if (ws->wakeirq) {
+		ret = -EEXIST;
+		goto unlock;
+	}
+
+	ws->wakeirq = wakeirq;
+
+unlock:
+	spin_unlock_irq(&dev->power.lock);
+
+	return ret;
+}
+
+/**
+ * device_wakeup_detach_irq - Detach a wakeirq from a wakeup source
+ * @dev: Device to handle
+ *
+ * Removes the wakeirq from a wakeup source.
+ */
+void device_wakeup_detach_irq(struct device *dev)
+{
+	struct wakeup_source *ws;
+
+	spin_lock_irq(&dev->power.lock);
+	ws = dev->power.wakeup;
+	if (!ws)
+		goto unlock;
+
+	ws->wakeirq = NULL;
+
+unlock:
+	spin_unlock_irq(&dev->power.lock);
+}
+
+/**
+ * device_wakeup_arm_wakeirqs(void)
+ *
+ * Itereates over the list of wakeirqs to arm them.
+ */
+void device_wakeup_arm_wakeirqs(void)
+{
+	struct wakeup_source *ws;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+		if (ws->wakeirq)
+			dev_pm_arm_wake_irq(ws->wakeirq);
+	}
+	rcu_read_unlock();
+}
+
+/**
+ * device_wakeup_disarm_wakeirqs(void)
+ *
+ * Itereates over the list of wakeirqs to disarm them.
+ */
+void device_wakeup_disarm_wakeirqs(void)
+{
+	struct wakeup_source *ws;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+		if (ws->wakeirq)
+			dev_pm_disarm_wake_irq(ws->wakeirq);
+	}
+	rcu_read_unlock();
+}
+
+#endif /* CONFIG_PM_WAKEIRQ */
+
 /**
  * device_wakeup_detach - Detach a device's wakeup source object from it.
  * @dev: Device to detach the wakeup source object from.
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 2d29c64..d1130d5 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -529,6 +529,7 @@ enum rpm_request {
 };
 
 struct wakeup_source;
+struct wake_irq;
 struct pm_domain_data;
 
 struct pm_subsys_data {
@@ -558,6 +559,7 @@ struct dev_pm_info {
 	struct list_head	entry;
 	struct completion	completion;
 	struct wakeup_source	*wakeup;
+	struct wake_irq		*wakeirq;
 	bool			wakeup_path:1;
 	bool			syscore:1;
 #else
diff --git a/include/linux/pm_wakeirq.h b/include/linux/pm_wakeirq.h
new file mode 100644
index 0000000..48bbc1c
--- /dev/null
+++ b/include/linux/pm_wakeirq.h
@@ -0,0 +1,72 @@
+/*
+ * pm_wakeirq.h - Device wakeirq helper functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_PM_WAKEIRQ_H
+#define _LINUX_PM_WAKEIRQ_H
+
+struct wake_irq {
+	struct device *dev;
+	int irq;
+	irq_handler_t handler;
+	void *data;
+	bool manage_irq:1;
+};
+
+#ifdef CONFIG_PM_WAKEIRQ
+
+extern int dev_pm_set_wake_irq(struct device *dev, int irq);
+extern void dev_pm_clear_wake_irq(struct device *dev);
+
+extern int dev_pm_request_wake_irq(struct device *dev,
+				   int wakeirq,
+				   irq_handler_t handler,
+				   unsigned long irqflags,
+				   void *data);
+extern void dev_pm_free_wake_irq(struct device *dev);
+extern void dev_pm_enable_wake_irq(struct device *dev);
+extern void dev_pm_disable_wake_irq(struct device *dev);
+
+#else	/* !CONFIG_PM_WAKEIRQ */
+
+static inline int dev_pm_set_wake_irq(struct device *dev, int irq)
+{
+	return 0;
+}
+
+static inline void dev_pm_clear_wake_irq(struct device *dev)
+{
+}
+
+static inline int dev_pm_request_wake_irq(struct device *dev,
+					  int wakeirq,
+					  irq_handler_t handler,
+					  unsigned long irqflags,
+					  void *data)
+{
+	return 0;
+}
+
+static inline void dev_pm_free_wake_irq(struct device *dev)
+{
+}
+
+static inline void dev_pm_enable_wake_irq(struct device *dev)
+{
+}
+
+static inline void dev_pm_disable_wake_irq(struct device *dev)
+{
+}
+
+#endif	/* CONFIG_PM_WAKEIRQ */
+#endif	/* _LINUX_PM_WAKEIRQ_H */
diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h
index a0f7080..86d1cbe 100644
--- a/include/linux/pm_wakeup.h
+++ b/include/linux/pm_wakeup.h
@@ -28,9 +28,15 @@
 
 #include <linux/types.h>
 
+struct wake_irq;
+
 /**
  * struct wakeup_source - Representation of wakeup sources
  *
+ * @name: Name of the wakeup source
+ * @entry: Wakeup source list entry
+ * @lock: Wakeup source lock
+ * @wakeirq: Optional device specific wakeirq
  * @total_time: Total time this wakeup source has been active.
  * @max_time: Maximum time this wakeup source has been continuously active.
  * @last_time: Monotonic clock when the wakeup source's was touched last time.
@@ -47,6 +53,7 @@ struct wakeup_source {
 	const char 		*name;
 	struct list_head	entry;
 	spinlock_t		lock;
+	struct wake_irq		*wakeirq;
 	struct timer_list	timer;
 	unsigned long		timer_expires;
 	ktime_t total_time;
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 7e01f78..d3735bd 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -267,6 +267,10 @@ config PM_CLK
 	def_bool y
 	depends on PM && HAVE_CLK
 
+config PM_WAKEIRQ
+	bool
+	depends on PM_SLEEP
+
 config PM_GENERIC_DOMAINS
 	bool
 	depends on PM
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 3/5] serial: omap: Switch wake-up interrupt to generic wakeirq
  2015-05-13 23:36 [PATCHv3 0/5] Linux generic wakeirq handling Tony Lindgren
  2015-05-13 23:36 ` [PATCH 1/5] PM / Runtime: Update last_busy in rpm_resume Tony Lindgren
  2015-05-13 23:36 ` [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling Tony Lindgren
@ 2015-05-13 23:36 ` Tony Lindgren
  2015-05-28 14:56   ` Tony Lindgren
  2015-05-13 23:36 ` [PATCH 4/5] serial: 8250_omap: Move " Tony Lindgren
  2015-05-13 23:36 ` [PATCH 5/5] mmc: omap_hsmmc: Change wake-up interrupt to use " Tony Lindgren
  4 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-13 23:36 UTC (permalink / raw)
  To: Rafael J. Wysocki, Alan Stern
  Cc: Andreas Fenkart, Greg Kroah-Hartman, Felipe Balbi, Huiquan Zhong,
	Kevin Hilman, NeilBrown, Mika Westerberg, Nishanth Menon,
	Peter Hurley, Sebastian Andrzej Siewior, Ulf Hansson,
	Thomas Gleixner, linux-pm, linux-kernel, linux-serial,
	linux-omap

We can now use generic wakeirq handling and remove the custom handling
for the wake-up interrupts.

Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 drivers/tty/serial/omap-serial.c | 38 +++++++-------------------------------
 1 file changed, 7 insertions(+), 31 deletions(-)

diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 211479a..b0cadaa 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -38,6 +38,7 @@
 #include <linux/serial_core.h>
 #include <linux/irq.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/gpio.h>
@@ -160,7 +161,6 @@ struct uart_omap_port {
 	unsigned long		port_activity;
 	int			context_loss_cnt;
 	u32			errata;
-	u8			wakeups_enabled;
 	u32			features;
 
 	int			rts_gpio;
@@ -209,28 +209,11 @@ static int serial_omap_get_context_loss_count(struct uart_omap_port *up)
 	return pdata->get_context_loss_count(up->dev);
 }
 
-static inline void serial_omap_enable_wakeirq(struct uart_omap_port *up,
-				       bool enable)
-{
-	if (!up->wakeirq)
-		return;
-
-	if (enable)
-		enable_irq(up->wakeirq);
-	else
-		disable_irq_nosync(up->wakeirq);
-}
-
+/* REVISIT: Remove this when omap3 boots in device tree only mode */
 static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable)
 {
 	struct omap_uart_port_info *pdata = dev_get_platdata(up->dev);
 
-	if (enable == up->wakeups_enabled)
-		return;
-
-	serial_omap_enable_wakeirq(up, enable);
-	up->wakeups_enabled = enable;
-
 	if (!pdata || !pdata->enable_wakeup)
 		return;
 
@@ -750,13 +733,12 @@ static int serial_omap_startup(struct uart_port *port)
 
 	/* Optional wake-up IRQ */
 	if (up->wakeirq) {
-		retval = request_irq(up->wakeirq, serial_omap_irq,
-				     up->port.irqflags, up->name, up);
+		retval = dev_pm_request_wake_irq(up->dev, up->wakeirq,
+						 NULL, 0, NULL);
 		if (retval) {
 			free_irq(up->port.irq, up);
 			return retval;
 		}
-		disable_irq(up->wakeirq);
 	}
 
 	dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->port.line);
@@ -845,8 +827,7 @@ static void serial_omap_shutdown(struct uart_port *port)
 	pm_runtime_mark_last_busy(up->dev);
 	pm_runtime_put_autosuspend(up->dev);
 	free_irq(up->port.irq, up);
-	if (up->wakeirq)
-		free_irq(up->wakeirq, up);
+	dev_pm_free_wake_irq(up->dev);
 }
 
 static void serial_omap_uart_qos_work(struct work_struct *work)
@@ -1139,13 +1120,6 @@ serial_omap_pm(struct uart_port *port, unsigned int state,
 	serial_out(up, UART_EFR, efr);
 	serial_out(up, UART_LCR, 0);
 
-	if (!device_may_wakeup(up->dev)) {
-		if (!state)
-			pm_runtime_forbid(up->dev);
-		else
-			pm_runtime_allow(up->dev);
-	}
-
 	pm_runtime_mark_last_busy(up->dev);
 	pm_runtime_put_autosuspend(up->dev);
 }
@@ -1838,6 +1812,7 @@ static int serial_omap_runtime_suspend(struct device *dev)
 	up->context_loss_cnt = serial_omap_get_context_loss_count(up);
 
 	serial_omap_enable_wakeup(up, true);
+	dev_pm_enable_wake_irq(dev);
 
 	up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
 	schedule_work(&up->qos_work);
@@ -1852,6 +1827,7 @@ static int serial_omap_runtime_resume(struct device *dev)
 	int loss_cnt = serial_omap_get_context_loss_count(up);
 
 	serial_omap_enable_wakeup(up, false);
+	dev_pm_disable_wake_irq(dev);
 
 	if (loss_cnt < 0) {
 		dev_dbg(dev, "serial_omap_get_context_loss_count failed : %d\n",
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 4/5] serial: 8250_omap: Move wake-up interrupt to generic wakeirq
  2015-05-13 23:36 [PATCHv3 0/5] Linux generic wakeirq handling Tony Lindgren
                   ` (2 preceding siblings ...)
  2015-05-13 23:36 ` [PATCH 3/5] serial: omap: Switch wake-up interrupt to generic wakeirq Tony Lindgren
@ 2015-05-13 23:36 ` Tony Lindgren
  2015-05-13 23:36 ` [PATCH 5/5] mmc: omap_hsmmc: Change wake-up interrupt to use " Tony Lindgren
  4 siblings, 0 replies; 43+ messages in thread
From: Tony Lindgren @ 2015-05-13 23:36 UTC (permalink / raw)
  To: Rafael J. Wysocki, Alan Stern
  Cc: Andreas Fenkart, Greg Kroah-Hartman, Felipe Balbi, Huiquan Zhong,
	Kevin Hilman, NeilBrown, Mika Westerberg, Nishanth Menon,
	Peter Hurley, Sebastian Andrzej Siewior, Ulf Hansson,
	Thomas Gleixner, linux-pm, linux-kernel, linux-serial,
	linux-omap

We can now use generic wakeirq handling and remove the custom handling
for the wake-up interrupts.

Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 drivers/tty/serial/8250/8250_omap.c | 61 +++++--------------------------------
 1 file changed, 7 insertions(+), 54 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 9289999..ae9950b 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -22,6 +22,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/console.h>
 #include <linux/pm_qos.h>
+#include <linux/pm_wakeirq.h>
 #include <linux/dma-mapping.h>
 
 #include "8250.h"
@@ -551,17 +552,6 @@ static void omap8250_uart_qos_work(struct work_struct *work)
 	pm_qos_update_request(&priv->pm_qos_request, priv->latency);
 }
 
-static irqreturn_t omap_wake_irq(int irq, void *dev_id)
-{
-	struct uart_port *port = dev_id;
-	int ret;
-
-	ret = port->handle_irq(port);
-	if (ret)
-		return IRQ_HANDLED;
-	return IRQ_NONE;
-}
-
 static int omap_8250_startup(struct uart_port *port)
 {
 	struct uart_8250_port *up =
@@ -571,11 +561,10 @@ static int omap_8250_startup(struct uart_port *port)
 	int ret;
 
 	if (priv->wakeirq) {
-		ret = request_irq(priv->wakeirq, omap_wake_irq,
-				  port->irqflags, "uart wakeup irq", port);
+		ret = dev_pm_request_wake_irq(port->dev, priv->wakeirq,
+					      NULL, 0, NULL);
 		if (ret)
 			return ret;
-		disable_irq(priv->wakeirq);
 	}
 
 	pm_runtime_get_sync(port->dev);
@@ -603,8 +592,7 @@ static int omap_8250_startup(struct uart_port *port)
 err:
 	pm_runtime_mark_last_busy(port->dev);
 	pm_runtime_put_autosuspend(port->dev);
-	if (priv->wakeirq)
-		free_irq(priv->wakeirq, port);
+	dev_pm_free_wake_irq(port->dev);
 	return ret;
 }
 
@@ -625,9 +613,7 @@ static void omap_8250_shutdown(struct uart_port *port)
 
 	pm_runtime_mark_last_busy(port->dev);
 	pm_runtime_put_autosuspend(port->dev);
-
-	if (priv->wakeirq)
-		free_irq(priv->wakeirq, port);
+	dev_pm_free_wake_irq(port->dev);
 }
 
 static void omap_8250_throttle(struct uart_port *port)
@@ -1129,31 +1115,6 @@ static int omap8250_remove(struct platform_device *pdev)
 	return 0;
 }
 
-#ifdef CONFIG_PM
-
-static inline void omap8250_enable_wakeirq(struct omap8250_priv *priv,
-					   bool enable)
-{
-	if (!priv->wakeirq)
-		return;
-
-	if (enable)
-		enable_irq(priv->wakeirq);
-	else
-		disable_irq_nosync(priv->wakeirq);
-}
-
-static void omap8250_enable_wakeup(struct omap8250_priv *priv,
-				   bool enable)
-{
-	if (enable == priv->wakeups_enabled)
-		return;
-
-	omap8250_enable_wakeirq(priv, enable);
-	priv->wakeups_enabled = enable;
-}
-#endif
-
 #ifdef CONFIG_PM_SLEEP
 static int omap8250_prepare(struct device *dev)
 {
@@ -1180,11 +1141,6 @@ static int omap8250_suspend(struct device *dev)
 
 	serial8250_suspend_port(priv->line);
 	flush_work(&priv->qos_work);
-
-	if (device_may_wakeup(dev))
-		omap8250_enable_wakeup(priv, true);
-	else
-		omap8250_enable_wakeup(priv, false);
 	return 0;
 }
 
@@ -1192,9 +1148,6 @@ static int omap8250_resume(struct device *dev)
 {
 	struct omap8250_priv *priv = dev_get_drvdata(dev);
 
-	if (device_may_wakeup(dev))
-		omap8250_enable_wakeup(priv, false);
-
 	serial8250_resume_port(priv->line);
 	return 0;
 }
@@ -1236,7 +1189,7 @@ static int omap8250_runtime_suspend(struct device *dev)
 			return -EBUSY;
 	}
 
-	omap8250_enable_wakeup(priv, true);
+	dev_pm_enable_wake_irq(dev);
 	if (up->dma)
 		omap_8250_rx_dma(up, UART_IIR_RX_TIMEOUT);
 
@@ -1257,7 +1210,7 @@ static int omap8250_runtime_resume(struct device *dev)
 		return 0;
 
 	up = serial8250_get_port(priv->line);
-	omap8250_enable_wakeup(priv, false);
+	dev_pm_disable_wake_irq(dev);
 	loss_cntx = omap8250_lost_context(up);
 
 	if (loss_cntx)
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 5/5] mmc: omap_hsmmc: Change wake-up interrupt to use generic wakeirq
  2015-05-13 23:36 [PATCHv3 0/5] Linux generic wakeirq handling Tony Lindgren
                   ` (3 preceding siblings ...)
  2015-05-13 23:36 ` [PATCH 4/5] serial: 8250_omap: Move " Tony Lindgren
@ 2015-05-13 23:36 ` Tony Lindgren
  2015-05-25  8:38   ` Ulf Hansson
  4 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-13 23:36 UTC (permalink / raw)
  To: Rafael J. Wysocki, Alan Stern
  Cc: Andreas Fenkart, Greg Kroah-Hartman, Felipe Balbi, Huiquan Zhong,
	Kevin Hilman, NeilBrown, Mika Westerberg, Nishanth Menon,
	Peter Hurley, Sebastian Andrzej Siewior, Ulf Hansson,
	Thomas Gleixner, linux-pm, linux-kernel, linux-serial,
	linux-omap

We can now use generic wakeirq handling and remove the custom handling
for the wake-up interrupts.

Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 drivers/mmc/host/omap_hsmmc.c | 51 +++++++------------------------------------
 1 file changed, 8 insertions(+), 43 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 9df2b68..5fbf4d8 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -43,6 +43,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
 #include <linux/platform_data/hsmmc-omap.h>
 
 /* OMAP HSMMC Host Controller Registers */
@@ -218,7 +219,6 @@ struct omap_hsmmc_host {
 	unsigned int		flags;
 #define AUTO_CMD23		(1 << 0)        /* Auto CMD23 support */
 #define HSMMC_SDIO_IRQ_ENABLED	(1 << 1)        /* SDIO irq enabled */
-#define HSMMC_WAKE_IRQ_ENABLED	(1 << 2)
 	struct omap_hsmmc_next	next_data;
 	struct	omap_hsmmc_platform_data	*pdata;
 
@@ -1117,22 +1117,6 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id)
-{
-	struct omap_hsmmc_host *host = dev_id;
-
-	/* cirq is level triggered, disable to avoid infinite loop */
-	spin_lock(&host->irq_lock);
-	if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
-		disable_irq_nosync(host->wake_irq);
-		host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
-	}
-	spin_unlock(&host->irq_lock);
-	pm_request_resume(host->dev); /* no use counter */
-
-	return IRQ_HANDLED;
-}
-
 static void set_sd_bus_power(struct omap_hsmmc_host *host)
 {
 	unsigned long i;
@@ -1665,7 +1649,6 @@ static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
 
 static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
 {
-	struct mmc_host *mmc = host->mmc;
 	int ret;
 
 	/*
@@ -1677,11 +1660,7 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
 	if (!host->dev->of_node || !host->wake_irq)
 		return -ENODEV;
 
-	/* Prevent auto-enabling of IRQ */
-	irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
-	ret = devm_request_irq(host->dev, host->wake_irq, omap_hsmmc_wake_irq,
-			       IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-			       mmc_hostname(mmc), host);
+	ret = dev_pm_request_wake_irq(host->dev, host->wake_irq, NULL, 0, NULL);
 	if (ret) {
 		dev_err(mmc_dev(host->mmc), "Unable to request wake IRQ\n");
 		goto err;
@@ -1718,7 +1697,7 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
 	return 0;
 
 err_free_irq:
-	devm_free_irq(host->dev, host->wake_irq, host);
+	dev_pm_free_wake_irq(host->dev);
 err:
 	dev_warn(host->dev, "no SDIO IRQ support, falling back to polling\n");
 	host->wake_irq = 0;
@@ -2007,6 +1986,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
 		omap_hsmmc_ops.multi_io_quirk = omap_hsmmc_multi_io_quirk;
 	}
 
+	device_init_wakeup(&pdev->dev, true);
 	pm_runtime_enable(host->dev);
 	pm_runtime_get_sync(host->dev);
 	pm_runtime_set_autosuspend_delay(host->dev, MMC_AUTOSUSPEND_DELAY);
@@ -2147,6 +2127,7 @@ err_slot_name:
 	if (host->use_reg)
 		omap_hsmmc_reg_put(host);
 err_irq:
+	device_init_wakeup(&pdev->dev, false);
 	if (host->tx_chan)
 		dma_release_channel(host->tx_chan);
 	if (host->rx_chan)
@@ -2178,6 +2159,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
 
 	pm_runtime_put_sync(host->dev);
 	pm_runtime_disable(host->dev);
+	device_init_wakeup(&pdev->dev, false);
 	if (host->dbclk)
 		clk_disable_unprepare(host->dbclk);
 
@@ -2204,11 +2186,6 @@ static int omap_hsmmc_suspend(struct device *dev)
 				OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
 	}
 
-	/* do not wake up due to sdio irq */
-	if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
-	    !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
-		disable_irq(host->wake_irq);
-
 	if (host->dbclk)
 		clk_disable_unprepare(host->dbclk);
 
@@ -2233,11 +2210,6 @@ static int omap_hsmmc_resume(struct device *dev)
 		omap_hsmmc_conf_bus_power(host);
 
 	omap_hsmmc_protect_card(host);
-
-	if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
-	    !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
-		enable_irq(host->wake_irq);
-
 	pm_runtime_mark_last_busy(host->dev);
 	pm_runtime_put_autosuspend(host->dev);
 	return 0;
@@ -2277,10 +2249,7 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
 		}
 
 		pinctrl_pm_select_idle_state(dev);
-
-		WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED);
-		enable_irq(host->wake_irq);
-		host->flags |= HSMMC_WAKE_IRQ_ENABLED;
+		dev_pm_enable_wake_irq(host->dev);
 	} else {
 		pinctrl_pm_select_idle_state(dev);
 	}
@@ -2302,11 +2271,7 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
 	spin_lock_irqsave(&host->irq_lock, flags);
 	if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
 	    (host->flags & HSMMC_SDIO_IRQ_ENABLED)) {
-		/* sdio irq flag can't change while in runtime suspend */
-		if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
-			disable_irq_nosync(host->wake_irq);
-			host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
-		}
+		dev_pm_disable_wake_irq(host->dev);
 
 		pinctrl_pm_select_default_state(host->dev);
 
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-13 23:36 ` [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling Tony Lindgren
@ 2015-05-14  2:06   ` Felipe Balbi
  2015-05-14 15:51     ` Alan Stern
  2015-05-14 15:59     ` Tony Lindgren
  0 siblings, 2 replies; 43+ messages in thread
From: Felipe Balbi @ 2015-05-14  2:06 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Felipe Balbi, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

[-- Attachment #1: Type: text/plain, Size: 15635 bytes --]

Hi,

On Wed, May 13, 2015 at 04:36:33PM -0700, Tony Lindgren wrote:
> Turns out we can automate the handling for the device_may_wakeup()
> quite a bit by using the kernel wakeup source list.
> 
> And as some hardware has separate dedicated wake-up interrupt
> in addition to the IO interrupt, we can automate the handling by
> adding a generic threaded interrupt handler that just calls the
> device PM runtime to wake up the device.
> 
> This allows dropping code from device drivers as we currently
> are doing it in multiple ways, and often wrong.
> 
> For most drivers, we should be able to drop the following
> boilerplate code from runtime_suspend and runtime_resume
> functions:
> 
> 	...
> 	if (device_may_wakeup(dev)
> 		enable_irq_wake(irq);
> 	...
> 	if (device_may_wakeup(dev)
> 		enable_irq_wake(irq);
> 	...
> 
> We can replace it with just the following init and exit
> time code:
> 
> 	...
> 	device_init_wakeup(dev, true);
> 	dev_pm_set_wake_irq(dev, irq);
> 	...
> 	dev_pm_clear_wake_irq(dev);
> 	device_init_wakeup(dev, false);
> 	...
> 
> And for hardware with dedicated wake-up interrupts:
> 
> 	...
> 	device_init_wakeup(dev, true);
> 	dev_pm_request_wake_irq(dev, wakeirq, NULL, 0, NULL);
> 	...
> 	dev_pm_enable_wake_irq(dev);
> 	...
> 	dev_pm_disable_wake_irq(dev);
> 	...
> 	dev_pm_free_wake_irq(dev);
> 	device_init_wakeup(dev, false);
> 	...
> 
> For now, let's only enable it for select PM_WAKEIRQ.
> 
> Signed-off-by: Tony Lindgren <tony@atomide.com>
> ---
>  arch/arm/mach-omap2/Kconfig  |   1 +
>  drivers/base/power/Makefile  |   1 +
>  drivers/base/power/main.c    |   2 +
>  drivers/base/power/power.h   |  38 ++++++
>  drivers/base/power/wakeirq.c | 316 +++++++++++++++++++++++++++++++++++++++++++
>  drivers/base/power/wakeup.c  |  96 +++++++++++++
>  include/linux/pm.h           |   2 +
>  include/linux/pm_wakeirq.h   |  72 ++++++++++
>  include/linux/pm_wakeup.h    |   7 +
>  kernel/power/Kconfig         |   4 +
>  10 files changed, 539 insertions(+)
>  create mode 100644 drivers/base/power/wakeirq.c
>  create mode 100644 include/linux/pm_wakeirq.h
> 
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> index 6468f15..ac7b570 100644
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -85,6 +85,7 @@ config ARCH_OMAP2PLUS
>  	select OMAP_DM_TIMER
>  	select OMAP_GPMC
>  	select PINCTRL
> +	select PM_WAKEIRQ if PM_SLEEP
>  	select SOC_BUS
>  	select TI_PRIV_EDMA
>  	select OMAP_IRQCHIP

seems like enabling this for OMAP should be a patch of its own.

> diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
> index 1cb8544..527546e 100644
> --- a/drivers/base/power/Makefile
> +++ b/drivers/base/power/Makefile
> @@ -4,5 +4,6 @@ obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
>  obj-$(CONFIG_PM_OPP)	+= opp.o
>  obj-$(CONFIG_PM_GENERIC_DOMAINS)	+=  domain.o domain_governor.o
>  obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
> +obj-$(CONFIG_PM_WAKEIRQ)	+= wakeirq.o
>  
>  ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
> diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
> index 3d874ec..24515e7 100644
> --- a/drivers/base/power/main.c
> +++ b/drivers/base/power/main.c
> @@ -587,6 +587,7 @@ void dpm_resume_noirq(pm_message_t state)
>  	async_synchronize_full();
>  	dpm_show_time(starttime, state, "noirq");
>  	resume_device_irqs();
> +	device_wakeup_disarm_wakeirqs();
>  	cpuidle_resume();
>  	trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
>  }
> @@ -1104,6 +1105,7 @@ int dpm_suspend_noirq(pm_message_t state)
>  
>  	trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
>  	cpuidle_pause();
> +	device_wakeup_arm_wakeirqs();
>  	suspend_device_irqs();
>  	mutex_lock(&dpm_list_mtx);
>  	pm_transition = state;
> diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
> index b6b8a27..6183c5d 100644
> --- a/drivers/base/power/power.h
> +++ b/drivers/base/power/power.h
> @@ -20,6 +20,44 @@ static inline void pm_runtime_early_init(struct device *dev)
>  extern void pm_runtime_init(struct device *dev);
>  extern void pm_runtime_remove(struct device *dev);
>  
> +#ifdef CONFIG_PM_WAKEIRQ
> +
> +extern int device_wakeup_attach_irq(struct device *dev,
> +				    struct wake_irq *wakeirq);
> +extern void device_wakeup_detach_irq(struct device *dev);
> +extern void device_wakeup_arm_wakeirqs(void);
> +extern void device_wakeup_disarm_wakeirqs(void);
> +
> +extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
> +extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
> +
> +#else
> +
> +static inline int
> +device_wakeup_attach_irq(struct device *dev,
> +			 struct wake_irq *wakeirq)
> +{
> +	return 0;
> +}
> +
> +static inline void device_wakeup_detach_irq(struct device *dev)
> +{
> +}
> +
> +static inline void device_wakeup_arm_wakeirqs(void)
> +{
> +}
> +
> +static inline void dev_pm_arm_wake_irq(struct wake_irq *wirq)
> +{
> +}
> +
> +static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
> +{
> +}
> +
> +#endif /* CONFIG_PM_WAKEIRQ */
> +
>  /*
>   * sysfs.c
>   */
> diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
> new file mode 100644
> index 0000000..579157b
> --- /dev/null
> +++ b/drivers/base/power/wakeirq.c
> @@ -0,0 +1,316 @@
> +/*
> + * wakeirq.c - Device wakeirq helper functions
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> + * kind, whether express or implied; without even the implied warranty
> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/pm_wakeirq.h>
> +
> +#include "power.h"
> +
> +/**
> + * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ
> + * @dev: Device entry
> + * @irq: Device wake-up capable interrupt
> + * @wirq: Wake irq specific data
> + *
> + * Internal function to attach either a device IO interrupt or a
> + * dedicated wake-up interrupt as a wake IRQ.
> + */
> +static int dev_pm_attach_wake_irq(struct device *dev, int irq,
> +				  struct wake_irq *wirq)
> +{
> +	unsigned long flags;
> +	int err;
> +
> +	if (!dev || !wirq)
> +		return -EINVAL;
> +
> +	if (!dev->power.wakeup) {
> +		dev_err(dev, "forgot to call call device_init_wakeup?\n");
> +		return -EINVAL;
> +	}
> +
> +	spin_lock_irqsave(&dev->power.lock, flags);
> +	if (WARN_ON(dev->power.wakeirq)) {
> +		dev_err(dev, "wake irq already initialized\n");
> +		err = -EEXIST;
> +		goto err_unlock;
> +	}
> +
> +	dev->power.wakeirq = wirq;
> +	spin_unlock_irqrestore(&dev->power.lock, flags);
> +
> +	err = device_wakeup_attach_irq(dev, wirq);
> +	if (err)
> +		goto err_free_mem;
> +
> +	return 0;
> +
> +err_unlock:
> +	spin_unlock_irqrestore(&dev->power.lock, flags);
> +err_free_mem:

minor nit, there's no memory being freed here :-)

> +	return err;
> +}
> +
> +/**
> + * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ
> + * @dev: Device entry
> + * @irq: Device IO interrupt
> + *
> + * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets
> + * automatically configured for wake-up from suspend  based
> + * on the device specific sysfs wakeup entry. Typically called
> + * during driver probe after calling device_init_wakeup().
> + */
> +int dev_pm_set_wake_irq(struct device *dev, int irq)
> +{
> +	struct wake_irq *wirq;
> +	int err;
> +
> +	wirq = devm_kzalloc(dev, sizeof(*wirq), GFP_KERNEL);
> +	if (!wirq)
> +		return -ENOMEM;
> +
> +	wirq->dev = dev;
> +	wirq->irq = irq;
> +
> +	err = dev_pm_attach_wake_irq(dev, irq, wirq);
> +	if (err)
> +		devm_kfree(dev, wirq);
> +
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq);
> +
> +/**
> + * dev_pm_clear_wake_irq - Detach a device IO interrupt wake IRQ
> + * @dev: Device entry
> + *
> + * Detach a device IO interrupt wake IRQ and free resources.
> + */
> +void dev_pm_clear_wake_irq(struct device *dev)
> +{
> +	struct wake_irq *wirq = dev->power.wakeirq;
> +	unsigned long flags;
> +
> +	if (!wirq)

WARN() to catch bad users ? Maybe with unlikely() around to give
compiler a hint that this is likely not going to happen ?

> +		return;
> +
> +	device_wakeup_detach_irq(dev);
> +
> +	spin_lock_irqsave(&dev->power.lock, flags);
> +	dev->power.wakeirq = NULL;
> +	spin_unlock_irqrestore(&dev->power.lock, flags);
> +
> +	wirq->irq = -EINVAL;
> +	devm_kfree(dev, wirq);
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
> +
> +/**
> + * handle_threaded_wakeirq - Handler for dedicated wake-up interrupts
> + * @irq: Device dedicated wake-up interrupt
> + * @_wirq: Wake IRQ data
> + *
> + * Some devices have a separate wake-up interrupt in addition to the
> + * device IO interrupt. The wake-up interrupts signal that the device
> + * should be woken up from a idle state. This handler uses device
> + * specific pm_runtime functions to wake the device and then it's
> + * up to the device to do whatever it needs to. Note as the device
> + * may need to restore context and start up regulators, we use a
> + * threaded IRQ.
> + *
> + * Also note that we are not resending the lost device interrupts.
> + * We assume that the wake-up interrupt just needs to wake-up the
> + * device, and the device pm_runtime_resume() can deal with the
> + * situation.
> + */
> +static irqreturn_t handle_threaded_wakeirq(int wakeirq, void *_wirq)
> +{
> +	struct wake_irq *wirq = _wirq;
> +	irqreturn_t ret = IRQ_NONE;
> +
> +	if (!pm_runtime_suspended(wirq->dev))

should you WARN() here ? Why would this IRQ fire if we're not
pm_runtime_suspended() ?

> +		goto out;
> +
> +	/* We don't want RPM_ASYNC or RPM_NOWAIT here */
> +	pm_runtime_resume(wirq->dev);
> +	ret = IRQ_HANDLED;
> +
> +	if (wirq->handler)
> +		ret = wirq->handler(wakeirq, wirq->data);
> +out:
> +	return ret;
> +}
> +
> +/**
> + * dev_pm_request_wake_irq - Request a dedicated wake-up interrupt
> + * @dev: Device entry
> + * @irq: Device wake-up interrupt
> + * @handler: Optional device specific handler
> + * @irqflags: Optional irqflags, IRQF_ONESHOT if not specified
> + * @data: Optional device specific data
> + *
> + * Unless your hardware has separate wake-up interrupts in addition
> + * to the device IO interrupts, you don't need this.
> + *
> + * Sets up a threaded interrupt handler for a device that has
> + * a dedicated wake-up interrupt in addition to the device IO
> + * interrupt.
> + *
> + * The interrupt starts disabled, and needs to be managed for
> + * the device by the bus code or the device driver using
> + * dev_pm_enable_wake_irq() and dev_pm_disable_wake_irq()
> + * functions.
> + */
> +int dev_pm_request_wake_irq(struct device *dev,
> +			    int irq,
> +			    irq_handler_t handler,
> +			    unsigned long irqflags,
> +			    void *data)
> +{
> +	struct wake_irq *wirq;
> +	int err;
> +
> +	wirq = devm_kzalloc(dev, sizeof(*wirq), GFP_KERNEL);
> +	if (!wirq)
> +		return -ENOMEM;
> +
> +	wirq->dev = dev;
> +	wirq->irq = irq;
> +	wirq->handler = handler;

you need to make sure that IRQF_ONESHOT is set in cases where handler is
NULL. Either set it by default:

	if (!handler)
		irqflags |= IRQF_ONESHOT;

or WARN() about it:

	WARN_ON((!handler && !(irqflags & IRQF_ONESHOT));

Actually, after reading more of the code, you have some weird circular
call chain going on here. If handler is a valid function pointer, you
use as primary handler, so IRQ core will call it from hardirq context.
But you also save that pointer as wirq->handler, and call that from
within handle_threaded_wakeirq(). Essentially, handler() is called
twice, once with IRQs disabled, one with IRQs (potentially) enabled.

What did you have in mind for handler() anyway, it seems completely
unnecessary.

> +	wirq->data = data;
> +	if (!irqflags) {
> +		irqflags = IRQF_ONESHOT;
> +		wirq->manage_irq = true;
> +	}
> +	irq_set_status_flags(irq, IRQ_NOAUTOEN);
> +
> +	/*
> +	 * Consumer device may need to power up and restore state
> +	 * so we use a threaded irq.
> +	 */
> +	err = devm_request_threaded_irq(dev, irq, handler,
> +					handle_threaded_wakeirq,
> +					irqflags, dev_name(dev),
> +					wirq);
> +	if (err)
> +		goto err_free;
> +
> +	err = dev_pm_attach_wake_irq(dev, irq, wirq);
> +	if (err)
> +		goto err_free_irq;
> +
> +	return err;
> +
> +err_free_irq:
> +	devm_free_irq(dev, irq, wirq);
> +err_free:
> +	devm_kfree(dev, wirq);
> +
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_request_wake_irq);
> +
> +/**
> + * dev_pm_free_wake_irq - Free a wake-up interrupt
> + * @wirq: Device wake-up interrupt
> + */
> +void dev_pm_free_wake_irq(struct device *dev)
> +{
> +	struct wake_irq *wirq = dev->power.wakeirq;
> +	unsigned long flags;
> +
> +	if (!wirq)
> +		return;
> +
> +	spin_lock_irqsave(&dev->power.lock, flags);
> +	wirq->manage_irq = false;
> +	spin_unlock_irqrestore(&dev->power.lock, flags);
> +	devm_free_irq(wirq->dev, wirq->irq, wirq);

this seems unnecessary, the IRQ will be freed anyway when the device
kobj is destroyed, dev_pm_clear_wake_irq() seems important, however.

> +	dev_pm_clear_wake_irq(dev);
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_free_wake_irq);
> +
> +/**
> + * dev_pm_enable_wake_irq - Enable device wake-up interrupt
> + * @dev: Device
> + *
> + * Called from the bus code or the device driver for
> + * runtime_suspend() to enable the wake-up interrupt while
> + * the device is running.
> + *
> + * Note that for runtime_suspend()) the wake-up interrupts
> + * should be unconditionally enabled unlike for suspend()
> + * that is conditional.
> + */
> +void dev_pm_enable_wake_irq(struct device *dev)
> +{
> +	struct wake_irq *wirq = dev->power.wakeirq;
> +
> +	if (wirq && wirq->manage_irq)
> +		enable_irq(wirq->irq);
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);

you probably want to enable dev_pm_enable_wake_irq() automatically for
from rpm_suspend(). According to runtime_pm documentation, wakeup should
always be enabled for runtime suspended devices. I didn't really look
through the whole thing yet to know if you did call it or not.

> +/**
> + * dev_pm_disable_wake_irq - Disable device wake-up interrupt
> + * @dev: Device
> + *
> + * Called from the bus code or the device driver for
> + * runtime_resume() to disable the wake-up interrupt while
> + * the device is running.
> + */
> +void dev_pm_disable_wake_irq(struct device *dev)
> +{
> +	struct wake_irq *wirq = dev->power.wakeirq;
> +
> +	if (wirq && wirq->manage_irq)
> +		disable_irq_nosync(wirq->irq);
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);

likewise, call this automatically from rpm_resume().

This brings up a question, actually. What to do with devices which were
already runtime suspended when user initiated suspend-to-ram ? Do we
leave wakeups enabled, or do we revisit device_may_wakeup() and
conditionally runtime_resume the device, disable wakeup, and let its
->suspend() callback be called ?

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-14  2:06   ` Felipe Balbi
@ 2015-05-14 15:51     ` Alan Stern
  2015-05-14 15:54       ` Felipe Balbi
  2015-05-14 15:59     ` Tony Lindgren
  1 sibling, 1 reply; 43+ messages in thread
From: Alan Stern @ 2015-05-14 15:51 UTC (permalink / raw)
  To: Felipe Balbi
  Cc: Tony Lindgren, Rafael J. Wysocki, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

On Wed, 13 May 2015, Felipe Balbi wrote:

> This brings up a question, actually. What to do with devices which were
> already runtime suspended when user initiated suspend-to-ram ? Do we
> leave wakeups enabled, or do we revisit device_may_wakeup() and
> conditionally runtime_resume the device, disable wakeup, and let its
> ->suspend() callback be called ?

In theory, the subsystem/device drivers already have code do this.  
They're supposed to, anyway.  The USB host stack does it in
drivers/usb/core/driver.c:choose_wakeup().

Alan Stern


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-14 15:51     ` Alan Stern
@ 2015-05-14 15:54       ` Felipe Balbi
  0 siblings, 0 replies; 43+ messages in thread
From: Felipe Balbi @ 2015-05-14 15:54 UTC (permalink / raw)
  To: Alan Stern
  Cc: Felipe Balbi, Tony Lindgren, Rafael J. Wysocki, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

[-- Attachment #1: Type: text/plain, Size: 1439 bytes --]

On Thu, May 14, 2015 at 11:51:10AM -0400, Alan Stern wrote:
> On Wed, 13 May 2015, Felipe Balbi wrote:
> 
> > This brings up a question, actually. What to do with devices which were
> > already runtime suspended when user initiated suspend-to-ram ? Do we
> > leave wakeups enabled, or do we revisit device_may_wakeup() and
> > conditionally runtime_resume the device, disable wakeup, and let its
> > ->suspend() callback be called ?
> 
> In theory, the subsystem/device drivers already have code do this.  
> They're supposed to, anyway.  The USB host stack does it in
> drivers/usb/core/driver.c:choose_wakeup().

that answers the question, specifically, this comment and piece of code
do:

1395 static void choose_wakeup(struct usb_device *udev, pm_message_t msg)
1396 {

[...]

1409         /* Enable remote wakeup if it is allowed, even if no interface drivers
1410          * actually want it.
1411          */
1412         w = device_may_wakeup(&udev->dev);
1413 
1414         /* If the device is autosuspended with the wrong wakeup setting,
1415          * autoresume now so the setting can be changed.
1416          */
1417         if (udev->state == USB_STATE_SUSPENDED && w != udev->do_remote_wakeup)
1418                 pm_runtime_resume(&udev->dev);
1419         udev->do_remote_wakeup = w;
1420 }

so that's something that also needs to be taken into consideration.

Thanks

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-14  2:06   ` Felipe Balbi
  2015-05-14 15:51     ` Alan Stern
@ 2015-05-14 15:59     ` Tony Lindgren
  2015-05-14 16:09       ` Felipe Balbi
  2015-05-14 21:15       ` Tony Lindgren
  1 sibling, 2 replies; 43+ messages in thread
From: Tony Lindgren @ 2015-05-14 15:59 UTC (permalink / raw)
  To: Felipe Balbi
  Cc: Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

* Felipe Balbi <balbi@ti.com> [150513 19:09]:
> On Wed, May 13, 2015 at 04:36:33PM -0700, Tony Lindgren wrote:
> > --- a/arch/arm/mach-omap2/Kconfig
> > +++ b/arch/arm/mach-omap2/Kconfig
> > @@ -85,6 +85,7 @@ config ARCH_OMAP2PLUS
> >  	select OMAP_DM_TIMER
> >  	select OMAP_GPMC
> >  	select PINCTRL
> > +	select PM_WAKEIRQ if PM_SLEEP
> >  	select SOC_BUS
> >  	select TI_PRIV_EDMA
> >  	select OMAP_IRQCHIP
> 
> seems like enabling this for OMAP should be a patch of its own.

Sure if people prefer that.
 
> > --- /dev/null
> > +++ b/drivers/base/power/wakeirq.c
> > +
> > +err_unlock:
> > +	spin_unlock_irqrestore(&dev->power.lock, flags);
> > +err_free_mem:
> 
> minor nit, there's no memory being freed here :-)

Will fix.
 
> > +void dev_pm_clear_wake_irq(struct device *dev)
> > +{
> > +	struct wake_irq *wirq = dev->power.wakeirq;
> > +	unsigned long flags;
> > +
> > +	if (!wirq)
> 
> WARN() to catch bad users ? Maybe with unlikely() around to give
> compiler a hint that this is likely not going to happen ?

Sure.
 
> > +static irqreturn_t handle_threaded_wakeirq(int wakeirq, void *_wirq)
> > +{
> > +	struct wake_irq *wirq = _wirq;
> > +	irqreturn_t ret = IRQ_NONE;
> > +
> > +	if (!pm_runtime_suspended(wirq->dev))
> 
> should you WARN() here ? Why would this IRQ fire if we're not
> pm_runtime_suspended() ?

Hmm or we could just leave it out. I had it initially to test
things with the wake-up interrupt enabled during runtime also.
 
> > +int dev_pm_request_wake_irq(struct device *dev,
> > +			    int irq,
> > +			    irq_handler_t handler,
> > +			    unsigned long irqflags,
> > +			    void *data)
> > +{
> > +	struct wake_irq *wirq;
> > +	int err;
> > +
> > +	wirq = devm_kzalloc(dev, sizeof(*wirq), GFP_KERNEL);
> > +	if (!wirq)
> > +		return -ENOMEM;
> > +
> > +	wirq->dev = dev;
> > +	wirq->irq = irq;
> > +	wirq->handler = handler;
> 
> you need to make sure that IRQF_ONESHOT is set in cases where handler is
> NULL. Either set it by default:
> 
> 	if (!handler)
> 		irqflags |= IRQF_ONESHOT;
> 
> or WARN() about it:
> 
> 	WARN_ON((!handler && !(irqflags & IRQF_ONESHOT));
> 
> Actually, after reading more of the code, you have some weird circular
> call chain going on here. If handler is a valid function pointer, you
> use as primary handler, so IRQ core will call it from hardirq context.
> But you also save that pointer as wirq->handler, and call that from
> within handle_threaded_wakeirq(). Essentially, handler() is called
> twice, once with IRQs disabled, one with IRQs (potentially) enabled.
> 
> What did you have in mind for handler() anyway, it seems completely
> unnecessary.

Yeah.. Let's just leave it out. You already mentioned it earlier and
there's no use for it.

The device driver can deal with the situation anyways in runtime resume.

And like you said, it must be IRQF_ONESHOT, now there's a chance of
consumer drivers passing other flags too.

The the IO wake-up interrupt vs dedicated wake-up interrupt functions
can be just something like:

int dev_pm_request_wake_irq(struct device *dev, int irq);
int dev_pm_request_wake_irq_managed(struct device *dev, int irq);
 
> > +void dev_pm_free_wake_irq(struct device *dev)
> > +{
> > +	struct wake_irq *wirq = dev->power.wakeirq;
> > +	unsigned long flags;
> > +
> > +	if (!wirq)
> > +		return;
> > +
> > +	spin_lock_irqsave(&dev->power.lock, flags);
> > +	wirq->manage_irq = false;
> > +	spin_unlock_irqrestore(&dev->power.lock, flags);
> > +	devm_free_irq(wirq->dev, wirq->irq, wirq);
> 
> this seems unnecessary, the IRQ will be freed anyway when the device
> kobj is destroyed, dev_pm_clear_wake_irq() seems important, however.
> 
> > +	dev_pm_clear_wake_irq(dev);
> > +}

The life cycle of the request and free of the wake irq is not the
same as the life cycle of the device driver. For example, serial
drivers can request interrupts on startup and free them on shutdown.

> > +void dev_pm_enable_wake_irq(struct device *dev)
> > +{
> > +	struct wake_irq *wirq = dev->power.wakeirq;
> > +
> > +	if (wirq && wirq->manage_irq)
> > +		enable_irq(wirq->irq);
> > +}
> > +EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
> 
> you probably want to enable dev_pm_enable_wake_irq() automatically for
> from rpm_suspend(). According to runtime_pm documentation, wakeup should
> always be enabled for runtime suspended devices. I didn't really look
> through the whole thing yet to know if you did call it or not.

Yes I think we can also automate that part, I've been playing with an
additional patch doing that for pm runtime. Been still thinking if
there's any need to manage that in the consomer driver, I guess not.
 
> > +void dev_pm_disable_wake_irq(struct device *dev)
> > +{
> > +	struct wake_irq *wirq = dev->power.wakeirq;
> > +
> > +	if (wirq && wirq->manage_irq)
> > +		disable_irq_nosync(wirq->irq);
> > +}
> > +EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
> 
> likewise, call this automatically from rpm_resume().

Right.
 
> This brings up a question, actually. What to do with devices which were
> already runtime suspended when user initiated suspend-to-ram ? Do we
> leave wakeups enabled, or do we revisit device_may_wakeup() and
> conditionally runtime_resume the device, disable wakeup, and let its
> ->suspend() callback be called ?

I believe that's already handled properly, but implemented in each
consumer driver with the if device_may_wakeup enabled_irq_wake.

Regards,

Tony

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-14 15:59     ` Tony Lindgren
@ 2015-05-14 16:09       ` Felipe Balbi
  2015-05-14 16:28         ` Tony Lindgren
  2015-05-14 21:15       ` Tony Lindgren
  1 sibling, 1 reply; 43+ messages in thread
From: Felipe Balbi @ 2015-05-14 16:09 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Felipe Balbi, Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

[-- Attachment #1: Type: text/plain, Size: 3873 bytes --]

Hi,

On Thu, May 14, 2015 at 08:59:46AM -0700, Tony Lindgren wrote:
> > > +int dev_pm_request_wake_irq(struct device *dev,
> > > +			    int irq,
> > > +			    irq_handler_t handler,
> > > +			    unsigned long irqflags,
> > > +			    void *data)
> > > +{
> > > +	struct wake_irq *wirq;
> > > +	int err;
> > > +
> > > +	wirq = devm_kzalloc(dev, sizeof(*wirq), GFP_KERNEL);
> > > +	if (!wirq)
> > > +		return -ENOMEM;
> > > +
> > > +	wirq->dev = dev;
> > > +	wirq->irq = irq;
> > > +	wirq->handler = handler;
> > 
> > you need to make sure that IRQF_ONESHOT is set in cases where handler is
> > NULL. Either set it by default:
> > 
> > 	if (!handler)
> > 		irqflags |= IRQF_ONESHOT;
> > 
> > or WARN() about it:
> > 
> > 	WARN_ON((!handler && !(irqflags & IRQF_ONESHOT));
> > 
> > Actually, after reading more of the code, you have some weird circular
> > call chain going on here. If handler is a valid function pointer, you
> > use as primary handler, so IRQ core will call it from hardirq context.
> > But you also save that pointer as wirq->handler, and call that from
> > within handle_threaded_wakeirq(). Essentially, handler() is called
> > twice, once with IRQs disabled, one with IRQs (potentially) enabled.
> > 
> > What did you have in mind for handler() anyway, it seems completely
> > unnecessary.
> 
> Yeah.. Let's just leave it out. You already mentioned it earlier and
> there's no use for it.
> 
> The device driver can deal with the situation anyways in runtime resume.
> 
> And like you said, it must be IRQF_ONESHOT, now there's a chance of
> consumer drivers passing other flags too.
> 
> The the IO wake-up interrupt vs dedicated wake-up interrupt functions
> can be just something like:
> 
> int dev_pm_request_wake_irq(struct device *dev, int irq);

right, then it's always IRQF_ONESHOT and always threaded.

> int dev_pm_request_wake_irq_managed(struct device *dev, int irq);

I don't get this. Would this request with devm_ while the former
wouldn't use devm_ ?

> > > +void dev_pm_free_wake_irq(struct device *dev)
> > > +{
> > > +	struct wake_irq *wirq = dev->power.wakeirq;
> > > +	unsigned long flags;
> > > +
> > > +	if (!wirq)
> > > +		return;
> > > +
> > > +	spin_lock_irqsave(&dev->power.lock, flags);
> > > +	wirq->manage_irq = false;
> > > +	spin_unlock_irqrestore(&dev->power.lock, flags);
> > > +	devm_free_irq(wirq->dev, wirq->irq, wirq);
> > 
> > this seems unnecessary, the IRQ will be freed anyway when the device
> > kobj is destroyed, dev_pm_clear_wake_irq() seems important, however.
> > 
> > > +	dev_pm_clear_wake_irq(dev);
> > > +}
> 
> The life cycle of the request and free of the wake irq is not the
> same as the life cycle of the device driver. For example, serial
> drivers can request interrupts on startup and free them on shutdown.

fair enough, but then we start to consider the benefits of using
devm_ IRQ :-)

> > > +void dev_pm_disable_wake_irq(struct device *dev)
> > > +{
> > > +	struct wake_irq *wirq = dev->power.wakeirq;
> > > +
> > > +	if (wirq && wirq->manage_irq)
> > > +		disable_irq_nosync(wirq->irq);
> > > +}
> > > +EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
> > 
> > likewise, call this automatically from rpm_resume().
> 
> Right.
>  
> > This brings up a question, actually. What to do with devices which were
> > already runtime suspended when user initiated suspend-to-ram ? Do we
> > leave wakeups enabled, or do we revisit device_may_wakeup() and
> > conditionally runtime_resume the device, disable wakeup, and let its
> > ->suspend() callback be called ?
> 
> I believe that's already handled properly, but implemented in each
> consumer driver with the if device_may_wakeup enabled_irq_wake.

I see, but perhaps even that can be partially automated at some point
:-)

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-14 16:09       ` Felipe Balbi
@ 2015-05-14 16:28         ` Tony Lindgren
  2015-05-14 17:51           ` Tony Lindgren
  0 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-14 16:28 UTC (permalink / raw)
  To: Felipe Balbi
  Cc: Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

* Felipe Balbi <balbi@ti.com> [150514 09:12]:
> On Thu, May 14, 2015 at 08:59:46AM -0700, Tony Lindgren wrote:
> > > > +int dev_pm_request_wake_irq(struct device *dev,
> > > > +			    int irq,
> > > > +			    irq_handler_t handler,
> > > > +			    unsigned long irqflags,
> > > > +			    void *data)
> > > > +{
> > > > +	struct wake_irq *wirq;
> > > > +	int err;
> > > > +
> > > > +	wirq = devm_kzalloc(dev, sizeof(*wirq), GFP_KERNEL);
> > > > +	if (!wirq)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	wirq->dev = dev;
> > > > +	wirq->irq = irq;
> > > > +	wirq->handler = handler;
> > > 
> > > you need to make sure that IRQF_ONESHOT is set in cases where handler is
> > > NULL. Either set it by default:
> > > 
> > > 	if (!handler)
> > > 		irqflags |= IRQF_ONESHOT;
> > > 
> > > or WARN() about it:
> > > 
> > > 	WARN_ON((!handler && !(irqflags & IRQF_ONESHOT));
> > > 
> > > Actually, after reading more of the code, you have some weird circular
> > > call chain going on here. If handler is a valid function pointer, you
> > > use as primary handler, so IRQ core will call it from hardirq context.
> > > But you also save that pointer as wirq->handler, and call that from
> > > within handle_threaded_wakeirq(). Essentially, handler() is called
> > > twice, once with IRQs disabled, one with IRQs (potentially) enabled.
> > > 
> > > What did you have in mind for handler() anyway, it seems completely
> > > unnecessary.
> > 
> > Yeah.. Let's just leave it out. You already mentioned it earlier and
> > there's no use for it.
> > 
> > The device driver can deal with the situation anyways in runtime resume.
> > 
> > And like you said, it must be IRQF_ONESHOT, now there's a chance of
> > consumer drivers passing other flags too.
> > 
> > The the IO wake-up interrupt vs dedicated wake-up interrupt functions
> > can be just something like:
> > 
> > int dev_pm_request_wake_irq(struct device *dev, int irq);
> 
> right, then it's always IRQF_ONESHOT and always threaded.

Yes threaded is just fine here at least for the cases I've seen.

PM runtime resume may need to bring up regulators, clocks and restore
the driver context etc.
 
> > int dev_pm_request_wake_irq_managed(struct device *dev, int irq);
> 
> I don't get this. Would this request with devm_ while the former
> wouldn't use devm_ ?

Typo :) Both can be devm no problem.
 
> > > > +void dev_pm_free_wake_irq(struct device *dev)
> > > > +{
> > > > +	struct wake_irq *wirq = dev->power.wakeirq;
> > > > +	unsigned long flags;
> > > > +
> > > > +	if (!wirq)
> > > > +		return;
> > > > +
> > > > +	spin_lock_irqsave(&dev->power.lock, flags);
> > > > +	wirq->manage_irq = false;
> > > > +	spin_unlock_irqrestore(&dev->power.lock, flags);
> > > > +	devm_free_irq(wirq->dev, wirq->irq, wirq);
> > > 
> > > this seems unnecessary, the IRQ will be freed anyway when the device
> > > kobj is destroyed, dev_pm_clear_wake_irq() seems important, however.
> > > 
> > > > +	dev_pm_clear_wake_irq(dev);
> > > > +}
> > 
> > The life cycle of the request and free of the wake irq is not the
> > same as the life cycle of the device driver. For example, serial
> > drivers can request interrupts on startup and free them on shutdown.
> 
> fair enough, but then we start to consider the benefits of using
> devm_ IRQ :-)

Hmm probably the extra checks do not hurt there either.
 
> > > > +void dev_pm_disable_wake_irq(struct device *dev)
> > > > +{
> > > > +	struct wake_irq *wirq = dev->power.wakeirq;
> > > > +
> > > > +	if (wirq && wirq->manage_irq)
> > > > +		disable_irq_nosync(wirq->irq);
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
> > > 
> > > likewise, call this automatically from rpm_resume().
> > 
> > Right.
> >  
> > > This brings up a question, actually. What to do with devices which were
> > > already runtime suspended when user initiated suspend-to-ram ? Do we
> > > leave wakeups enabled, or do we revisit device_may_wakeup() and
> > > conditionally runtime_resume the device, disable wakeup, and let its
> > > ->suspend() callback be called ?
> > 
> > I believe that's already handled properly, but implemented in each
> > consumer driver with the if device_may_wakeup enabled_irq_wake.
> 
> I see, but perhaps even that can be partially automated at some point
> :-)

Yeah it seems we might be able to eventually.

Regards,

Tony

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-14 16:28         ` Tony Lindgren
@ 2015-05-14 17:51           ` Tony Lindgren
  0 siblings, 0 replies; 43+ messages in thread
From: Tony Lindgren @ 2015-05-14 17:51 UTC (permalink / raw)
  To: Felipe Balbi
  Cc: Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

* Tony Lindgren <tony@atomide.com> [150514 09:30]:
> * Felipe Balbi <balbi@ti.com> [150514 09:12]:
>  
> > > int dev_pm_request_wake_irq_managed(struct device *dev, int irq);
> > 
> > I don't get this. Would this request with devm_ while the former
> > wouldn't use devm_ ?
> 
> Typo :) Both can be devm no problem.
...

> > > The life cycle of the request and free of the wake irq is not the
> > > same as the life cycle of the device driver. For example, serial
> > > drivers can request interrupts on startup and free them on shutdown.
> > 
> > fair enough, but then we start to consider the benefits of using
> > devm_ IRQ :-)
> 
> Hmm probably the extra checks do not hurt there either.

We should keep the PM related functions called dev_pm_*, using
devm_pm_* just gets hard to pronounce.. So yeah I too am thinking
just not using devm here at all as the consumer drivers are not
allocating anything.

Regards,

Tony

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-14 15:59     ` Tony Lindgren
  2015-05-14 16:09       ` Felipe Balbi
@ 2015-05-14 21:15       ` Tony Lindgren
  2015-05-14 21:25         ` Felipe Balbi
  2015-05-14 22:00         ` Rafael J. Wysocki
  1 sibling, 2 replies; 43+ messages in thread
From: Tony Lindgren @ 2015-05-14 21:15 UTC (permalink / raw)
  To: Felipe Balbi
  Cc: Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

* Tony Lindgren <tony@atomide.com> [150514 09:01]:
> * Felipe Balbi <balbi@ti.com> [150513 19:09]:
> > > +void dev_pm_enable_wake_irq(struct device *dev)
> > > +{
> > > +	struct wake_irq *wirq = dev->power.wakeirq;
> > > +
> > > +	if (wirq && wirq->manage_irq)
> > > +		enable_irq(wirq->irq);
> > > +}
> > > +EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
> > 
> > you probably want to enable dev_pm_enable_wake_irq() automatically for
> > from rpm_suspend(). According to runtime_pm documentation, wakeup should
> > always be enabled for runtime suspended devices. I didn't really look
> > through the whole thing yet to know if you did call it or not.
> 
> Yes I think we can also automate that part, I've been playing with an
> additional patch doing that for pm runtime. Been still thinking if
> there's any need to manage that in the consomer driver, I guess not.

Here's what that would roughly look and now I also remember why
I did not include it. It adds currently extra checks also for
devices not using dedicated wakeirqs. But basically this would
allow leaving out the enable/disable PM runtime calls from drivers.

Regards,

Tony

8< -------------------
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -10,6 +10,7 @@
 #include <linux/sched.h>
 #include <linux/export.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
 #include <trace/events/rpm.h>
 #include "power.h"
 
@@ -514,6 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 
 	callback = RPM_GET_CALLBACK(dev, runtime_suspend);
 
+	dev_pm_enable_wake_irq(dev);
 	retval = rpm_callback(callback, dev);
 	if (retval)
 		goto fail;
@@ -552,6 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 	return retval;
 
  fail:
+	dev_pm_disable_wake_irq(dev);
 	__update_runtime_status(dev, RPM_ACTIVE);
 	dev->power.deferred_resume = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -734,10 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags)
 
 	callback = RPM_GET_CALLBACK(dev, runtime_resume);
 
+	dev_pm_disable_wake_irq(dev);
 	retval = rpm_callback(callback, dev);
 	if (retval) {
 		__update_runtime_status(dev, RPM_SUSPENDED);
 		pm_runtime_cancel_pending(dev);
+		dev_pm_enable_wake_irq(dev);
 	} else {
  no_callback:
 		__update_runtime_status(dev, RPM_ACTIVE);
@@ -1427,6 +1432,7 @@ int pm_runtime_force_suspend(struct device *dev)
 		goto err;
 	}
 
+	dev_pm_enable_wake_irq(dev);
 	ret = callback(dev);
 	if (ret)
 		goto err;
@@ -1434,6 +1440,7 @@ int pm_runtime_force_suspend(struct device *dev)
 	pm_runtime_set_suspended(dev);
 	return 0;
 err:
+	dev_pm_disable_wake_irq(dev);
 	pm_runtime_enable(dev);
 	return ret;
 }
@@ -1463,6 +1470,7 @@ int pm_runtime_force_resume(struct device *dev)
 		goto out;
 	}
 
+	dev_pm_disable_wake_irq(dev);
 	ret = callback(dev);
 	if (ret)
 		goto out;
@@ -1470,6 +1478,7 @@ int pm_runtime_force_resume(struct device *dev)
 	pm_runtime_set_active(dev);
 	pm_runtime_mark_last_busy(dev);
 out:
+	dev_pm_enable_wake_irq(dev);
 	pm_runtime_enable(dev);
 	return ret;
 }

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-14 21:15       ` Tony Lindgren
@ 2015-05-14 21:25         ` Felipe Balbi
  2015-05-14 22:00         ` Rafael J. Wysocki
  1 sibling, 0 replies; 43+ messages in thread
From: Felipe Balbi @ 2015-05-14 21:25 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Felipe Balbi, Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

[-- Attachment #1: Type: text/plain, Size: 1365 bytes --]

Hi,

On Thu, May 14, 2015 at 02:15:01PM -0700, Tony Lindgren wrote:
> * Tony Lindgren <tony@atomide.com> [150514 09:01]:
> > * Felipe Balbi <balbi@ti.com> [150513 19:09]:
> > > > +void dev_pm_enable_wake_irq(struct device *dev)
> > > > +{
> > > > +	struct wake_irq *wirq = dev->power.wakeirq;
> > > > +
> > > > +	if (wirq && wirq->manage_irq)
> > > > +		enable_irq(wirq->irq);
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
> > > 
> > > you probably want to enable dev_pm_enable_wake_irq() automatically for
> > > from rpm_suspend(). According to runtime_pm documentation, wakeup should
> > > always be enabled for runtime suspended devices. I didn't really look
> > > through the whole thing yet to know if you did call it or not.
> > 
> > Yes I think we can also automate that part, I've been playing with an
> > additional patch doing that for pm runtime. Been still thinking if
> > there's any need to manage that in the consomer driver, I guess not.
> 
> Here's what that would roughly look and now I also remember why
> I did not include it. It adds currently extra checks also for
> devices not using dedicated wakeirqs. But basically this would
> allow leaving out the enable/disable PM runtime calls from drivers.

hey cool, the only requirement now is that drivers attach and detach
wakeirq :-)

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-14 22:00         ` Rafael J. Wysocki
@ 2015-05-14 21:59           ` Tony Lindgren
  2015-05-15 22:25             ` Tony Lindgren
  0 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-14 21:59 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Felipe Balbi, Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

* Rafael J. Wysocki <rjw@rjwysocki.net> [150514 14:36]:
> On Thursday, May 14, 2015 02:15:01 PM Tony Lindgren wrote:
> > * Tony Lindgren <tony@atomide.com> [150514 09:01]:
> > > * Felipe Balbi <balbi@ti.com> [150513 19:09]:
> > > > > +void dev_pm_enable_wake_irq(struct device *dev)
> > > > > +{
> > > > > +	struct wake_irq *wirq = dev->power.wakeirq;
> > > > > +
> > > > > +	if (wirq && wirq->manage_irq)
> > > > > +		enable_irq(wirq->irq);
> > > > > +}
> > > > > +EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
> > > > 
> > > > you probably want to enable dev_pm_enable_wake_irq() automatically for
> > > > from rpm_suspend(). According to runtime_pm documentation, wakeup should
> > > > always be enabled for runtime suspended devices. I didn't really look
> > > > through the whole thing yet to know if you did call it or not.
> > > 
> > > Yes I think we can also automate that part, I've been playing with an
> > > additional patch doing that for pm runtime. Been still thinking if
> > > there's any need to manage that in the consomer driver, I guess not.
> > 
> > Here's what that would roughly look and now I also remember why
> > I did not include it. It adds currently extra checks also for
> > devices not using dedicated wakeirqs. But basically this would
> > allow leaving out the enable/disable PM runtime calls from drivers.
...

> > @@ -1427,6 +1432,7 @@ int pm_runtime_force_suspend(struct device *dev)
> >  		goto err;
> >  	}
> >  
> > +	dev_pm_enable_wake_irq(dev);
> 
> Why here?  This is for system suspend.
> 
> >  	ret = callback(dev);
> >  	if (ret)
> >  		goto err;
> > @@ -1434,6 +1440,7 @@ int pm_runtime_force_suspend(struct device *dev)
> >  	pm_runtime_set_suspended(dev);
> >  	return 0;
> >  err:
> > +	dev_pm_disable_wake_irq(dev);
> >  	pm_runtime_enable(dev);
> >  	return ret;
> >  }
> > @@ -1463,6 +1470,7 @@ int pm_runtime_force_resume(struct device *dev)
> >  		goto out;
> >  	}
> >  
> > +	dev_pm_disable_wake_irq(dev);
> 
> This one too.

Oh you're right, seems to work fine for me without those. So it would
be the following additional patch then (only quickly tested).

Regards,

Tony

8< ----------------
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -10,6 +10,7 @@
 #include <linux/sched.h>
 #include <linux/export.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
 #include <trace/events/rpm.h>
 #include "power.h"
 
@@ -514,6 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 
 	callback = RPM_GET_CALLBACK(dev, runtime_suspend);
 
+	dev_pm_enable_wake_irq(dev);
 	retval = rpm_callback(callback, dev);
 	if (retval)
 		goto fail;
@@ -552,6 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 	return retval;
 
  fail:
+	dev_pm_disable_wake_irq(dev);
 	__update_runtime_status(dev, RPM_ACTIVE);
 	dev->power.deferred_resume = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -734,10 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags)
 
 	callback = RPM_GET_CALLBACK(dev, runtime_resume);
 
+	dev_pm_disable_wake_irq(dev);
 	retval = rpm_callback(callback, dev);
 	if (retval) {
 		__update_runtime_status(dev, RPM_SUSPENDED);
 		pm_runtime_cancel_pending(dev);
+		dev_pm_enable_wake_irq(dev);
 	} else {
  no_callback:
 		__update_runtime_status(dev, RPM_ACTIVE);

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-14 21:15       ` Tony Lindgren
  2015-05-14 21:25         ` Felipe Balbi
@ 2015-05-14 22:00         ` Rafael J. Wysocki
  2015-05-14 21:59           ` Tony Lindgren
  1 sibling, 1 reply; 43+ messages in thread
From: Rafael J. Wysocki @ 2015-05-14 22:00 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Felipe Balbi, Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

On Thursday, May 14, 2015 02:15:01 PM Tony Lindgren wrote:
> * Tony Lindgren <tony@atomide.com> [150514 09:01]:
> > * Felipe Balbi <balbi@ti.com> [150513 19:09]:
> > > > +void dev_pm_enable_wake_irq(struct device *dev)
> > > > +{
> > > > +	struct wake_irq *wirq = dev->power.wakeirq;
> > > > +
> > > > +	if (wirq && wirq->manage_irq)
> > > > +		enable_irq(wirq->irq);
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
> > > 
> > > you probably want to enable dev_pm_enable_wake_irq() automatically for
> > > from rpm_suspend(). According to runtime_pm documentation, wakeup should
> > > always be enabled for runtime suspended devices. I didn't really look
> > > through the whole thing yet to know if you did call it or not.
> > 
> > Yes I think we can also automate that part, I've been playing with an
> > additional patch doing that for pm runtime. Been still thinking if
> > there's any need to manage that in the consomer driver, I guess not.
> 
> Here's what that would roughly look and now I also remember why
> I did not include it. It adds currently extra checks also for
> devices not using dedicated wakeirqs. But basically this would
> allow leaving out the enable/disable PM runtime calls from drivers.
> 
> Regards,
> 
> Tony
> 
> 8< -------------------
> --- a/drivers/base/power/runtime.c
> +++ b/drivers/base/power/runtime.c
> @@ -10,6 +10,7 @@
>  #include <linux/sched.h>
>  #include <linux/export.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/pm_wakeirq.h>
>  #include <trace/events/rpm.h>
>  #include "power.h"
>  
> @@ -514,6 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
>  
>  	callback = RPM_GET_CALLBACK(dev, runtime_suspend);
>  
> +	dev_pm_enable_wake_irq(dev);
>  	retval = rpm_callback(callback, dev);
>  	if (retval)
>  		goto fail;
> @@ -552,6 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
>  	return retval;
>  
>   fail:
> +	dev_pm_disable_wake_irq(dev);
>  	__update_runtime_status(dev, RPM_ACTIVE);
>  	dev->power.deferred_resume = false;
>  	wake_up_all(&dev->power.wait_queue);
> @@ -734,10 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags)
>  
>  	callback = RPM_GET_CALLBACK(dev, runtime_resume);
>  
> +	dev_pm_disable_wake_irq(dev);
>  	retval = rpm_callback(callback, dev);
>  	if (retval) {
>  		__update_runtime_status(dev, RPM_SUSPENDED);
>  		pm_runtime_cancel_pending(dev);
> +		dev_pm_enable_wake_irq(dev);
>  	} else {
>   no_callback:
>  		__update_runtime_status(dev, RPM_ACTIVE);
> @@ -1427,6 +1432,7 @@ int pm_runtime_force_suspend(struct device *dev)
>  		goto err;
>  	}
>  
> +	dev_pm_enable_wake_irq(dev);

Why here?  This is for system suspend.

>  	ret = callback(dev);
>  	if (ret)
>  		goto err;
> @@ -1434,6 +1440,7 @@ int pm_runtime_force_suspend(struct device *dev)
>  	pm_runtime_set_suspended(dev);
>  	return 0;
>  err:
> +	dev_pm_disable_wake_irq(dev);
>  	pm_runtime_enable(dev);
>  	return ret;
>  }
> @@ -1463,6 +1470,7 @@ int pm_runtime_force_resume(struct device *dev)
>  		goto out;
>  	}
>  
> +	dev_pm_disable_wake_irq(dev);

This one too.

>  	ret = callback(dev);
>  	if (ret)
>  		goto out;
> @@ -1470,6 +1478,7 @@ int pm_runtime_force_resume(struct device *dev)
>  	pm_runtime_set_active(dev);
>  	pm_runtime_mark_last_busy(dev);
>  out:
> +	dev_pm_enable_wake_irq(dev);
>  	pm_runtime_enable(dev);
>  	return ret;
>  }
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-14 21:59           ` Tony Lindgren
@ 2015-05-15 22:25             ` Tony Lindgren
  2015-05-16  1:56               ` Felipe Balbi
  0 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-15 22:25 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Felipe Balbi, Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

* Tony Lindgren <tony@atomide.com> [150514 15:00]:
> * Rafael J. Wysocki <rjw@rjwysocki.net> [150514 14:36]:
> > On Thursday, May 14, 2015 02:15:01 PM Tony Lindgren wrote:
> > > * Tony Lindgren <tony@atomide.com> [150514 09:01]:
> > > > * Felipe Balbi <balbi@ti.com> [150513 19:09]:
> > > > > > +void dev_pm_enable_wake_irq(struct device *dev)
> > > > > > +{
> > > > > > +	struct wake_irq *wirq = dev->power.wakeirq;
> > > > > > +
> > > > > > +	if (wirq && wirq->manage_irq)
> > > > > > +		enable_irq(wirq->irq);
> > > > > > +}
> > > > > > +EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
> > > > > 
> > > > > you probably want to enable dev_pm_enable_wake_irq() automatically for
> > > > > from rpm_suspend(). According to runtime_pm documentation, wakeup should
> > > > > always be enabled for runtime suspended devices. I didn't really look
> > > > > through the whole thing yet to know if you did call it or not.
> > > > 
> > > > Yes I think we can also automate that part, I've been playing with an
> > > > additional patch doing that for pm runtime. Been still thinking if
> > > > there's any need to manage that in the consomer driver, I guess not.
> > > 
> > > Here's what that would roughly look and now I also remember why
> > > I did not include it. It adds currently extra checks also for
> > > devices not using dedicated wakeirqs. But basically this would
> > > allow leaving out the enable/disable PM runtime calls from drivers.
> ...
> 
> > > @@ -1427,6 +1432,7 @@ int pm_runtime_force_suspend(struct device *dev)
> > >  		goto err;
> > >  	}
> > >  
> > > +	dev_pm_enable_wake_irq(dev);
> > 
> > Why here?  This is for system suspend.
> > 
> > >  	ret = callback(dev);
> > >  	if (ret)
> > >  		goto err;
> > > @@ -1434,6 +1440,7 @@ int pm_runtime_force_suspend(struct device *dev)
> > >  	pm_runtime_set_suspended(dev);
> > >  	return 0;
> > >  err:
> > > +	dev_pm_disable_wake_irq(dev);
> > >  	pm_runtime_enable(dev);
> > >  	return ret;
> > >  }
> > > @@ -1463,6 +1470,7 @@ int pm_runtime_force_resume(struct device *dev)
> > >  		goto out;
> > >  	}
> > >  
> > > +	dev_pm_disable_wake_irq(dev);
> > 
> > This one too.
> 
> Oh you're right, seems to work fine for me without those. So it would
> be the following additional patch then (only quickly tested).

And here's the whole $subject patch with the above folded in and with
Felipe's comments addressed. I'll repost the whole series within few days
after waiting for some more comments.

Regards,

Tony

8< ------------------------
From: Tony Lindgren <tony@atomide.com>
Date: Wed, 13 May 2015 11:43:02 -0700
Subject: [PATCH] PM / Wakeirq: Add automated device wake IRQ handling

Turns out we can automate the handling for the device_may_wakeup()
quite a bit by using the kernel wakeup source list.

And as some hardware has separate dedicated wake-up interrupt
in addition to the IO interrupt, we can automate the handling by
adding a generic threaded interrupt handler that just calls the
device PM runtime to wake up the device.

This allows dropping code from device drivers as we currently
are doing it in multiple ways, and often wrong.

For most drivers, we should be able to drop the following
boilerplate code from runtime_suspend and runtime_resume
functions:

	...
	if (device_may_wakeup(dev)
		enable_irq_wake(irq);
	...
	if (device_may_wakeup(dev)
		enable_irq_wake(irq);
	...

We can replace it with just the following init and exit
time code:

	...
	device_init_wakeup(dev, true);
	dev_pm_set_wake_irq(dev, irq);
	...
	dev_pm_clear_wake_irq(dev);
	device_init_wakeup(dev, false);
	...

And for hardware with dedicated wake-up interrupts:

	...
	device_init_wakeup(dev, true);
	dev_pm_set_dedicated_wake_irq(dev, wakeirq);
	...
	dev_pm_clear_wake_irq(dev);
	device_init_wakeup(dev, false);
	...

For now, let's only enable it for select PM_WAKEIRQ.

Signed-off-by: Tony Lindgren <tony@atomide.com>

diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 1cb8544..527546e 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -4,5 +4,6 @@ obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 obj-$(CONFIG_PM_OPP)	+= opp.o
 obj-$(CONFIG_PM_GENERIC_DOMAINS)	+=  domain.o domain_governor.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
+obj-$(CONFIG_PM_WAKEIRQ)	+= wakeirq.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 3d874ec..2748268 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -24,6 +24,7 @@
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm-trace.h>
+#include <linux/pm_wakeirq.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
 #include <linux/async.h>
@@ -587,6 +588,7 @@ void dpm_resume_noirq(pm_message_t state)
 	async_synchronize_full();
 	dpm_show_time(starttime, state, "noirq");
 	resume_device_irqs();
+	device_wakeup_disarm_wakeirqs();
 	cpuidle_resume();
 	trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
 }
@@ -1104,6 +1106,7 @@ int dpm_suspend_noirq(pm_message_t state)
 
 	trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
 	cpuidle_pause();
+	device_wakeup_arm_wakeirqs();
 	suspend_device_irqs();
 	mutex_lock(&dpm_list_mtx);
 	pm_transition = state;
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index b6b8a27..7ee5402 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -20,6 +20,54 @@ static inline void pm_runtime_early_init(struct device *dev)
 extern void pm_runtime_init(struct device *dev);
 extern void pm_runtime_remove(struct device *dev);
 
+#ifdef CONFIG_PM_WAKEIRQ
+
+struct wake_irq {
+	struct device *dev;
+	int irq;
+	bool manage_irq:1;
+};
+
+extern int device_wakeup_attach_irq(struct device *dev,
+				    struct wake_irq *wakeirq);
+extern void device_wakeup_detach_irq(struct device *dev);
+extern void device_wakeup_arm_wakeirqs(void);
+extern void device_wakeup_disarm_wakeirqs(void);
+
+extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
+extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
+
+#else
+
+static inline int
+device_wakeup_attach_irq(struct device *dev,
+			 struct wake_irq *wakeirq)
+{
+	return 0;
+}
+
+static inline void device_wakeup_detach_irq(struct device *dev)
+{
+}
+
+static inline void device_wakeup_arm_wakeirqs(void)
+{
+}
+
+static inline void device_wakeup_disarm_wakeirqs(void)
+{
+}
+
+static inline void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+}
+
+static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+}
+
+#endif /* CONFIG_PM_WAKEIRQ */
+
 /*
  * sysfs.c
  */
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 4ffe4a2..e1a10a0 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -10,6 +10,7 @@
 #include <linux/sched.h>
 #include <linux/export.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
 #include <trace/events/rpm.h>
 #include "power.h"
 
@@ -514,6 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 
 	callback = RPM_GET_CALLBACK(dev, runtime_suspend);
 
+	dev_pm_enable_wake_irq(dev);
 	retval = rpm_callback(callback, dev);
 	if (retval)
 		goto fail;
@@ -552,6 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 	return retval;
 
  fail:
+	dev_pm_disable_wake_irq(dev);
 	__update_runtime_status(dev, RPM_ACTIVE);
 	dev->power.deferred_resume = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -734,10 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags)
 
 	callback = RPM_GET_CALLBACK(dev, runtime_resume);
 
+	dev_pm_disable_wake_irq(dev);
 	retval = rpm_callback(callback, dev);
 	if (retval) {
 		__update_runtime_status(dev, RPM_SUSPENDED);
 		pm_runtime_cancel_pending(dev);
+		dev_pm_enable_wake_irq(dev);
 	} else {
  no_callback:
 		__update_runtime_status(dev, RPM_ACTIVE);
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
new file mode 100644
index 0000000..1125481
--- /dev/null
+++ b/drivers/base/power/wakeirq.c
@@ -0,0 +1,276 @@
+/*
+ * wakeirq.c - Device wakeirq helper functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
+
+#include "power.h"
+
+/**
+ * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ
+ * @dev: Device entry
+ * @irq: Device wake-up capable interrupt
+ * @wirq: Wake irq specific data
+ *
+ * Internal function to attach either a device IO interrupt or a
+ * dedicated wake-up interrupt as a wake IRQ.
+ */
+static int dev_pm_attach_wake_irq(struct device *dev, int irq,
+				  struct wake_irq *wirq)
+{
+	unsigned long flags;
+	int err;
+
+	if (!dev || !wirq)
+		return -EINVAL;
+
+	if (!dev->power.wakeup) {
+		dev_err(dev, "forgot to call call device_init_wakeup?\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+	if (WARN_ON(dev->power.wakeirq)) {
+		dev_err(dev, "wake irq already initialized\n");
+		spin_unlock_irqrestore(&dev->power.lock, flags);
+		return -EEXIST;
+	}
+
+	dev->power.wakeirq = wirq;
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	err = device_wakeup_attach_irq(dev, wirq);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+/**
+ * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ
+ * @dev: Device entry
+ * @irq: Device IO interrupt
+ *
+ * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets
+ * automatically configured for wake-up from suspend  based
+ * on the device specific sysfs wakeup entry. Typically called
+ * during driver probe after calling device_init_wakeup().
+ */
+int dev_pm_set_wake_irq(struct device *dev, int irq)
+{
+	struct wake_irq *wirq;
+	int err;
+
+	wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
+	if (!wirq)
+		return -ENOMEM;
+
+	wirq->dev = dev;
+	wirq->irq = irq;
+
+	err = dev_pm_attach_wake_irq(dev, irq, wirq);
+	if (err)
+		kfree(wirq);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq);
+
+/**
+ * dev_pm_clear_wake_irq - Detach a device IO interrupt wake IRQ
+ * @dev: Device entry
+ *
+ * Detach a device IO interrupt wake IRQ and free resources.
+ *
+ * Note that it's OK for drivers to call this without calling
+ * dev_pm_set_wake_irq() first as all the driver instances may
+ * not have the wakeirq configured. This avoid adding wake IRQ
+ * checks into the drivers.
+ */
+void dev_pm_clear_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+	unsigned long flags;
+
+	if (!wirq)
+		return;
+
+	device_wakeup_detach_irq(dev);
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+	if (wirq->manage_irq) {
+		free_irq(wirq->irq, wirq);
+		wirq->manage_irq = false;
+	}
+	dev->power.wakeirq = NULL;
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	wirq->irq = -EINVAL;
+	kfree(wirq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
+
+/**
+ * handle_threaded_wakeirq - Handler for dedicated wake-up interrupts
+ * @irq: Device dedicated wake-up interrupt
+ * @_wirq: Wake IRQ data
+ *
+ * Some devices have a separate wake-up interrupt in addition to the
+ * device IO interrupt. The wake-up interrupts signal that the device
+ * should be woken up from a idle state. This handler uses device
+ * specific pm_runtime functions to wake the device and then it's
+ * up to the device to do whatever it needs to. Note as the device
+ * may need to restore context and start up regulators, we use a
+ * threaded IRQ.
+ *
+ * Also note that we are not resending the lost device interrupts.
+ * We assume that the wake-up interrupt just needs to wake-up the
+ * device, and the device pm_runtime_resume() can deal with the
+ * situation.
+ */
+static irqreturn_t handle_threaded_wakeirq(int wakeirq, void *_wirq)
+{
+	struct wake_irq *wirq = _wirq;
+
+	/* We don't want RPM_ASYNC or RPM_NOWAIT here */
+	return pm_runtime_resume(wirq->dev) ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/**
+ * dev_pm_set_dedicated_wake_irq - Request a dedicated wake-up interrupt
+ * @dev: Device entry
+ * @irq: Device wake-up interrupt
+ *
+ * Unless your hardware has separate wake-up interrupts in addition
+ * to the device IO interrupts, you don't need this.
+ *
+ * Sets up a threaded interrupt handler for a device that has
+ * a dedicated wake-up interrupt in addition to the device IO
+ * interrupt.
+ *
+ * The interrupt starts disabled, and needs to be managed for
+ * the device by the bus code or the device driver using
+ * dev_pm_enable_wake_irq() and dev_pm_disable_wake_irq()
+ * functions.
+ */
+int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
+{
+	struct wake_irq *wirq;
+	int err;
+
+	wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
+	if (!wirq)
+		return -ENOMEM;
+
+	wirq->dev = dev;
+	wirq->irq = irq;
+	wirq->manage_irq = true;
+	irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+	/*
+	 * Consumer device may need to power up and restore state
+	 * so we use a threaded irq.
+	 */
+	err = request_threaded_irq(irq, NULL, handle_threaded_wakeirq,
+				   IRQF_ONESHOT, dev_name(dev), wirq);
+	if (err)
+		goto err_free;
+
+	err = dev_pm_attach_wake_irq(dev, irq, wirq);
+	if (err)
+		goto err_free_irq;
+
+	return err;
+
+err_free_irq:
+	free_irq(irq, wirq);
+err_free:
+	kfree(wirq);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq);
+
+/**
+ * dev_pm_enable_wake_irq - Enable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_suspend() to enable the wake-up interrupt while
+ * the device is running.
+ *
+ * Note that for runtime_suspend()) the wake-up interrupts
+ * should be unconditionally enabled unlike for suspend()
+ * that is conditional.
+ */
+void dev_pm_enable_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+
+	if (wirq && wirq->manage_irq)
+		enable_irq(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
+
+/**
+ * dev_pm_disable_wake_irq - Disable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_resume() to disable the wake-up interrupt while
+ * the device is running.
+ */
+void dev_pm_disable_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+
+	if (wirq && wirq->manage_irq)
+		disable_irq_nosync(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
+
+/**
+ * dev_pm_arm_wake_irq - Arm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Sets up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+	if (!wirq)
+		return;
+
+	if (device_may_wakeup(wirq->dev))
+		enable_irq_wake(wirq->irq);
+}
+
+/**
+ * dev_pm_disarm_wake_irq - Disarm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Clears up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+	if (!wirq)
+		return;
+
+	if (device_may_wakeup(wirq->dev))
+		disable_irq_wake(wirq->irq);
+}
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 7726200..7191519 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -14,6 +14,7 @@
 #include <linux/suspend.h>
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
+#include <linux/pm_wakeirq.h>
 #include <trace/events/power.h>
 
 #include "power.h"
@@ -238,6 +239,100 @@ int device_wakeup_enable(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(device_wakeup_enable);
 
+#ifdef CONFIG_PM_WAKEIRQ
+
+/**
+ * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source
+ * @dev: Device to handle
+ * @irq: Device specific wakeirq entry
+ *
+ * Attach a device specific wakeirq to the device specific
+ * wakeup source so the device wakeirq can be configured
+ * automatically for suspend and resume.
+ */
+int device_wakeup_attach_irq(struct device *dev,
+			     struct wake_irq *wakeirq)
+{
+	struct wakeup_source *ws;
+	int ret = 0;
+
+	spin_lock_irq(&dev->power.lock);
+	ws = dev->power.wakeup;
+	if (!ws) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	if (ws->wakeirq) {
+		ret = -EEXIST;
+		goto unlock;
+	}
+
+	ws->wakeirq = wakeirq;
+
+unlock:
+	spin_unlock_irq(&dev->power.lock);
+
+	return ret;
+}
+
+/**
+ * device_wakeup_detach_irq - Detach a wakeirq from a wakeup source
+ * @dev: Device to handle
+ *
+ * Removes the wakeirq from a wakeup source.
+ */
+void device_wakeup_detach_irq(struct device *dev)
+{
+	struct wakeup_source *ws;
+
+	spin_lock_irq(&dev->power.lock);
+	ws = dev->power.wakeup;
+	if (!ws)
+		goto unlock;
+
+	ws->wakeirq = NULL;
+
+unlock:
+	spin_unlock_irq(&dev->power.lock);
+}
+
+/**
+ * device_wakeup_arm_wakeirqs(void)
+ *
+ * Itereates over the list of wakeirqs to arm them.
+ */
+void device_wakeup_arm_wakeirqs(void)
+{
+	struct wakeup_source *ws;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+		if (ws->wakeirq)
+			dev_pm_arm_wake_irq(ws->wakeirq);
+	}
+	rcu_read_unlock();
+}
+
+/**
+ * device_wakeup_disarm_wakeirqs(void)
+ *
+ * Itereates over the list of wakeirqs to disarm them.
+ */
+void device_wakeup_disarm_wakeirqs(void)
+{
+	struct wakeup_source *ws;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+		if (ws->wakeirq)
+			dev_pm_disarm_wake_irq(ws->wakeirq);
+	}
+	rcu_read_unlock();
+}
+
+#endif /* CONFIG_PM_WAKEIRQ */
+
 /**
  * device_wakeup_detach - Detach a device's wakeup source object from it.
  * @dev: Device to detach the wakeup source object from.
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 2d29c64..d1130d5 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -529,6 +529,7 @@ enum rpm_request {
 };
 
 struct wakeup_source;
+struct wake_irq;
 struct pm_domain_data;
 
 struct pm_subsys_data {
@@ -558,6 +559,7 @@ struct dev_pm_info {
 	struct list_head	entry;
 	struct completion	completion;
 	struct wakeup_source	*wakeup;
+	struct wake_irq		*wakeirq;
 	bool			wakeup_path:1;
 	bool			syscore:1;
 #else
diff --git a/include/linux/pm_wakeirq.h b/include/linux/pm_wakeirq.h
new file mode 100644
index 0000000..b92df630b
--- /dev/null
+++ b/include/linux/pm_wakeirq.h
@@ -0,0 +1,52 @@
+/*
+ * pm_wakeirq.h - Device wakeirq helper functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_PM_WAKEIRQ_H
+#define _LINUX_PM_WAKEIRQ_H
+
+#ifdef CONFIG_PM_WAKEIRQ
+
+extern int dev_pm_set_wake_irq(struct device *dev, int irq);
+extern int dev_pm_set_dedicated_wake_irq(struct device *dev,
+					 int wakeirq);
+extern void dev_pm_clear_wake_irq(struct device *dev);
+extern void dev_pm_enable_wake_irq(struct device *dev);
+extern void dev_pm_disable_wake_irq(struct device *dev);
+
+#else	/* !CONFIG_PM_WAKEIRQ */
+
+static inline int dev_pm_set_wake_irq(struct device *dev, int irq)
+{
+	return 0;
+}
+
+static inline int dev_pm_set_managed__wake_irq(struct device *dev,
+					       int wakeirq)
+{
+	return 0;
+}
+
+static inline void dev_pm_clear_wake_irq(struct device *dev)
+{
+}
+
+static inline void dev_pm_enable_wake_irq(struct device *dev)
+{
+}
+
+static inline void dev_pm_disable_wake_irq(struct device *dev)
+{
+}
+
+#endif	/* CONFIG_PM_WAKEIRQ */
+#endif	/* _LINUX_PM_WAKEIRQ_H */
diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h
index a0f7080..86d1cbe 100644
--- a/include/linux/pm_wakeup.h
+++ b/include/linux/pm_wakeup.h
@@ -28,9 +28,15 @@
 
 #include <linux/types.h>
 
+struct wake_irq;
+
 /**
  * struct wakeup_source - Representation of wakeup sources
  *
+ * @name: Name of the wakeup source
+ * @entry: Wakeup source list entry
+ * @lock: Wakeup source lock
+ * @wakeirq: Optional device specific wakeirq
  * @total_time: Total time this wakeup source has been active.
  * @max_time: Maximum time this wakeup source has been continuously active.
  * @last_time: Monotonic clock when the wakeup source's was touched last time.
@@ -47,6 +53,7 @@ struct wakeup_source {
 	const char 		*name;
 	struct list_head	entry;
 	spinlock_t		lock;
+	struct wake_irq		*wakeirq;
 	struct timer_list	timer;
 	unsigned long		timer_expires;
 	ktime_t total_time;
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 7e01f78..d3735bd 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -267,6 +267,10 @@ config PM_CLK
 	def_bool y
 	depends on PM && HAVE_CLK
 
+config PM_WAKEIRQ
+	bool
+	depends on PM_SLEEP
+
 config PM_GENERIC_DOMAINS
 	bool
 	depends on PM

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-15 22:25             ` Tony Lindgren
@ 2015-05-16  1:56               ` Felipe Balbi
  2015-05-18 22:05                 ` Tony Lindgren
  0 siblings, 1 reply; 43+ messages in thread
From: Felipe Balbi @ 2015-05-16  1:56 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Rafael J. Wysocki, Felipe Balbi, Rafael J. Wysocki, Alan Stern,
	Andreas Fenkart, Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

[-- Attachment #1: Type: text/plain, Size: 3777 bytes --]

Hi,

On Fri, May 15, 2015 at 03:25:13PM -0700, Tony Lindgren wrote:

<snip>

> diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
> new file mode 100644
> index 0000000..1125481
> --- /dev/null
> +++ b/drivers/base/power/wakeirq.c
> @@ -0,0 +1,276 @@
> +/*
> + * wakeirq.c - Device wakeirq helper functions
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> + * kind, whether express or implied; without even the implied warranty
> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/slab.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/pm_wakeirq.h>
> +
> +#include "power.h"
> +
> +/**
> + * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ
> + * @dev: Device entry
> + * @irq: Device wake-up capable interrupt
> + * @wirq: Wake irq specific data
> + *
> + * Internal function to attach either a device IO interrupt or a
> + * dedicated wake-up interrupt as a wake IRQ.
> + */
> +static int dev_pm_attach_wake_irq(struct device *dev, int irq,
> +				  struct wake_irq *wirq)
> +{
> +	unsigned long flags;
> +	int err;
> +
> +	if (!dev || !wirq)
> +		return -EINVAL;
> +
> +	if (!dev->power.wakeup) {
> +		dev_err(dev, "forgot to call call device_init_wakeup?\n");
> +		return -EINVAL;
> +	}
> +
> +	spin_lock_irqsave(&dev->power.lock, flags);
> +	if (WARN_ON(dev->power.wakeirq)) {
> +		dev_err(dev, "wake irq already initialized\n");

these two can be combined if you can live with a WARN_ONCE() instead:

	if (dev_WARN_ONCE(dev, dev->power.wakeirq,
		"wake irq already initialized\n")) {
		spin_unlock_irqrestore(&dev->power.lock, flags);
		return -EEXIST;
	}

dev_WARN() needs to be fixed at some point to accept a "condition"
argument :s

But really, no strong feelings.

> +static irqreturn_t handle_threaded_wakeirq(int wakeirq, void *_wirq)
> +{
> +	struct wake_irq *wirq = _wirq;
> +
> +	/* We don't want RPM_ASYNC or RPM_NOWAIT here */
> +	return pm_runtime_resume(wirq->dev) ? IRQ_NONE : IRQ_HANDLED;

I wonder if you should add a pm_runtime_mark_last_busy() here. I guess
not after your previous patch ?

> diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
> index 7726200..7191519 100644
> --- a/drivers/base/power/wakeup.c
> +++ b/drivers/base/power/wakeup.c
> @@ -238,6 +239,100 @@ int device_wakeup_enable(struct device *dev)
>  }
>  EXPORT_SYMBOL_GPL(device_wakeup_enable);
>  
> +#ifdef CONFIG_PM_WAKEIRQ
> +
> +/**
> + * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source
> + * @dev: Device to handle
> + * @irq: Device specific wakeirq entry

s/irq/wakeirq to match argument name below ?

> + * Attach a device specific wakeirq to the device specific
> + * wakeup source so the device wakeirq can be configured
> + * automatically for suspend and resume.
> + */
> +int device_wakeup_attach_irq(struct device *dev,
> +			     struct wake_irq *wakeirq)
> +{
> +	struct wakeup_source *ws;
> +	int ret = 0;
> +
> +	spin_lock_irq(&dev->power.lock);
> +	ws = dev->power.wakeup;
> +	if (!ws) {
> +		ret = -EINVAL;
> +		goto unlock;
> +	}
> +
> +	if (ws->wakeirq) {
> +		ret = -EEXIST;
> +		goto unlock;
> +	}
> +
> +	ws->wakeirq = wakeirq;
> +
> +unlock:
> +	spin_unlock_irq(&dev->power.lock);
> +
> +	return ret;
> +}

<snip>

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-16  1:56               ` Felipe Balbi
@ 2015-05-18 22:05                 ` Tony Lindgren
  2015-05-18 23:44                   ` Tony Lindgren
  0 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-18 22:05 UTC (permalink / raw)
  To: Felipe Balbi
  Cc: Rafael J. Wysocki, Rafael J. Wysocki, Alan Stern,
	Andreas Fenkart, Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

* Felipe Balbi <balbi@ti.com> [150515 19:00]:
> On Fri, May 15, 2015 at 03:25:13PM -0700, Tony Lindgren wrote:
> > --- /dev/null
> > +++ b/drivers/base/power/wakeirq.c
> > +
> > +	spin_lock_irqsave(&dev->power.lock, flags);
> > +	if (WARN_ON(dev->power.wakeirq)) {
> > +		dev_err(dev, "wake irq already initialized\n");
> 
> these two can be combined if you can live with a WARN_ONCE() instead:
> 
> 	if (dev_WARN_ONCE(dev, dev->power.wakeirq,
> 		"wake irq already initialized\n")) {
> 		spin_unlock_irqrestore(&dev->power.lock, flags);
> 		return -EEXIST;
> 	}
> 
> dev_WARN() needs to be fixed at some point to accept a "condition"
> argument :s
> 
> But really, no strong feelings.

Sure, updated to use dev_WARN_ONCE.
 
> > +static irqreturn_t handle_threaded_wakeirq(int wakeirq, void *_wirq)
> > +{
> > +	struct wake_irq *wirq = _wirq;
> > +
> > +	/* We don't want RPM_ASYNC or RPM_NOWAIT here */
> > +	return pm_runtime_resume(wirq->dev) ? IRQ_NONE : IRQ_HANDLED;
> 
> I wonder if you should add a pm_runtime_mark_last_busy() here. I guess
> not after your previous patch ?

Correct, that's no longer needed with patch 1 in this series.
 
> > diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
> > index 7726200..7191519 100644
> > --- a/drivers/base/power/wakeup.c
> > +++ b/drivers/base/power/wakeup.c
> > @@ -238,6 +239,100 @@ int device_wakeup_enable(struct device *dev)
> >  }
> >  EXPORT_SYMBOL_GPL(device_wakeup_enable);
> >  
> > +#ifdef CONFIG_PM_WAKEIRQ
> > +
> > +/**
> > + * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source
> > + * @dev: Device to handle
> > + * @irq: Device specific wakeirq entry
> 
> s/irq/wakeirq to match argument name below ?

I've updated the patch below to standardize on wake_irq naming for
the functions, then use just int irq in the functions.  I'll repost
the whole series after waiting a bit in case there are more comments.

Regards,

Tony

8< ----------------------------
From: Tony Lindgren <tony@atomide.com>
Date: Wed, 13 May 2015 11:43:02 -0700
Subject: [PATCH] PM / Wakeirq: Add automated device wake IRQ handling

Turns out we can automate the handling for the device_may_wakeup()
quite a bit by using the kernel wakeup source list.

And as some hardware has separate dedicated wake-up interrupt
in addition to the IO interrupt, we can automate the handling by
adding a generic threaded interrupt handler that just calls the
device PM runtime to wake up the device.

This allows dropping code from device drivers as we currently
are doing it in multiple ways, and often wrong.

For most drivers, we should be able to drop the following
boilerplate code from runtime_suspend and runtime_resume
functions:

	...
	device_init_wakeup(dev, true);
	...
	if (device_may_wakeup(dev)
		enable_irq_wake(irq);
	...
	if (device_may_wakeup(dev)
		enable_irq_wake(irq);
	...
	device_init_wakeup(dev, false);
	...

We can replace it with just the following init and exit
time code:

	...
	device_init_wakeup(dev, true);
	dev_pm_set_wake_irq(dev, irq);
	...
	dev_pm_clear_wake_irq(dev);
	device_init_wakeup(dev, false);
	...

And for hardware with dedicated wake-up interrupts:

	...
	device_init_wakeup(dev, true);
	dev_pm_set_dedicated_wake_irq(dev, irq);
	...
	dev_pm_clear_wake_irq(dev);
	device_init_wakeup(dev, false);
	...

For now, let's only enable it for select PM_WAKEIRQ.

Signed-off-by: Tony Lindgren <tony@atomide.com>

diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 1cb8544..527546e 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -4,5 +4,6 @@ obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 obj-$(CONFIG_PM_OPP)	+= opp.o
 obj-$(CONFIG_PM_GENERIC_DOMAINS)	+=  domain.o domain_governor.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
+obj-$(CONFIG_PM_WAKEIRQ)	+= wakeirq.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 3d874ec..6f2515c 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -24,6 +24,7 @@
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm-trace.h>
+#include <linux/pm_wakeirq.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
 #include <linux/async.h>
@@ -587,6 +588,7 @@ void dpm_resume_noirq(pm_message_t state)
 	async_synchronize_full();
 	dpm_show_time(starttime, state, "noirq");
 	resume_device_irqs();
+	device_wakeup_disarm_wake_irqs();
 	cpuidle_resume();
 	trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
 }
@@ -1104,6 +1106,7 @@ int dpm_suspend_noirq(pm_message_t state)
 
 	trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
 	cpuidle_pause();
+	device_wakeup_arm_wake_irqs();
 	suspend_device_irqs();
 	mutex_lock(&dpm_list_mtx);
 	pm_transition = state;
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index b6b8a27..708de3b 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -20,6 +20,54 @@ static inline void pm_runtime_early_init(struct device *dev)
 extern void pm_runtime_init(struct device *dev);
 extern void pm_runtime_remove(struct device *dev);
 
+#ifdef CONFIG_PM_WAKEIRQ
+
+struct wake_irq {
+	struct device *dev;
+	int irq;
+	bool manage_irq:1;
+};
+
+extern int device_wakeup_attach_irq(struct device *dev,
+				    struct wake_irq *wakeirq);
+extern void device_wakeup_detach_irq(struct device *dev);
+extern void device_wakeup_arm_wake_irqs(void);
+extern void device_wakeup_disarm_wake_irqs(void);
+
+extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
+extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
+
+#else
+
+static inline int
+device_wakeup_attach_irq(struct device *dev,
+			 struct wake_irq *wakeirq)
+{
+	return 0;
+}
+
+static inline void device_wakeup_detach_irq(struct device *dev)
+{
+}
+
+static inline void device_wakeup_arm_wake_irqs(void)
+{
+}
+
+static inline void device_wakeup_disarm_wake_irqs(void)
+{
+}
+
+static inline void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+}
+
+static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+}
+
+#endif /* CONFIG_PM_WAKEIRQ */
+
 /*
  * sysfs.c
  */
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 4ffe4a2..e1a10a0 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -10,6 +10,7 @@
 #include <linux/sched.h>
 #include <linux/export.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
 #include <trace/events/rpm.h>
 #include "power.h"
 
@@ -514,6 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 
 	callback = RPM_GET_CALLBACK(dev, runtime_suspend);
 
+	dev_pm_enable_wake_irq(dev);
 	retval = rpm_callback(callback, dev);
 	if (retval)
 		goto fail;
@@ -552,6 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 	return retval;
 
  fail:
+	dev_pm_disable_wake_irq(dev);
 	__update_runtime_status(dev, RPM_ACTIVE);
 	dev->power.deferred_resume = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -734,10 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags)
 
 	callback = RPM_GET_CALLBACK(dev, runtime_resume);
 
+	dev_pm_disable_wake_irq(dev);
 	retval = rpm_callback(callback, dev);
 	if (retval) {
 		__update_runtime_status(dev, RPM_SUSPENDED);
 		pm_runtime_cancel_pending(dev);
+		dev_pm_enable_wake_irq(dev);
 	} else {
  no_callback:
 		__update_runtime_status(dev, RPM_ACTIVE);
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
new file mode 100644
index 0000000..6edb1f6
--- /dev/null
+++ b/drivers/base/power/wakeirq.c
@@ -0,0 +1,276 @@
+/*
+ * wakeirq.c - Device wakeirq helper functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
+
+#include "power.h"
+
+/**
+ * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ
+ * @dev: Device entry
+ * @irq: Device wake-up capable interrupt
+ * @wirq: Wake irq specific data
+ *
+ * Internal function to attach either a device IO interrupt or a
+ * dedicated wake-up interrupt as a wake IRQ.
+ */
+static int dev_pm_attach_wake_irq(struct device *dev, int irq,
+				  struct wake_irq *wirq)
+{
+	unsigned long flags;
+	int err;
+
+	if (!dev || !wirq)
+		return -EINVAL;
+
+	if (!dev->power.wakeup) {
+		dev_err(dev, "forgot to call call device_init_wakeup?\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+	if (dev_WARN_ONCE(dev, dev->power.wakeirq,
+			  "wake irq already initialized\n")) {
+		spin_unlock_irqrestore(&dev->power.lock, flags);
+		return -EEXIST;
+	}
+
+	dev->power.wakeirq = wirq;
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	err = device_wakeup_attach_irq(dev, wirq);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+/**
+ * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ
+ * @dev: Device entry
+ * @irq: Device IO interrupt
+ *
+ * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets
+ * automatically configured for wake-up from suspend  based
+ * on the device specific sysfs wakeup entry. Typically called
+ * during driver probe after calling device_init_wakeup().
+ */
+int dev_pm_set_wake_irq(struct device *dev, int irq)
+{
+	struct wake_irq *wirq;
+	int err;
+
+	wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
+	if (!wirq)
+		return -ENOMEM;
+
+	wirq->dev = dev;
+	wirq->irq = irq;
+
+	err = dev_pm_attach_wake_irq(dev, irq, wirq);
+	if (err)
+		kfree(wirq);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq);
+
+/**
+ * dev_pm_clear_wake_irq - Detach a device IO interrupt wake IRQ
+ * @dev: Device entry
+ *
+ * Detach a device IO interrupt wake IRQ and free resources.
+ *
+ * Note that it's OK for drivers to call this without calling
+ * dev_pm_set_wake_irq() first as all the driver instances may
+ * not have the wake IRQ configured. This avoid adding wake IRQ
+ * checks into the drivers.
+ */
+void dev_pm_clear_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+	unsigned long flags;
+
+	if (!wirq)
+		return;
+
+	device_wakeup_detach_irq(dev);
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+	if (wirq->manage_irq) {
+		free_irq(wirq->irq, wirq);
+		wirq->manage_irq = false;
+	}
+	dev->power.wakeirq = NULL;
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	wirq->irq = -EINVAL;
+	kfree(wirq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
+
+/**
+ * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts
+ * @irq: Device dedicated wake-up interrupt
+ * @_wirq: Wake IRQ data
+ *
+ * Some devices have a separate wake-up interrupt in addition to the
+ * device IO interrupt. The wake-up interrupts signal that the device
+ * should be woken up from a idle state. This handler uses device
+ * specific pm_runtime functions to wake the device and then it's
+ * up to the device to do whatever it needs to. Note as the device
+ * may need to restore context and start up regulators, we use a
+ * threaded IRQ.
+ *
+ * Also note that we are not resending the lost device interrupts.
+ * We assume that the wake-up interrupt just needs to wake-up the
+ * device, and the device pm_runtime_resume() can deal with the
+ * situation.
+ */
+static irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq)
+{
+	struct wake_irq *wirq = _wirq;
+
+	/* We don't want RPM_ASYNC or RPM_NOWAIT here */
+	return pm_runtime_resume(wirq->dev) ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/**
+ * dev_pm_set_dedicated_wake_irq - Request a dedicated wake-up interrupt
+ * @dev: Device entry
+ * @irq: Device wake-up interrupt
+ *
+ * Unless your hardware has separate wake-up interrupts in addition
+ * to the device IO interrupts, you don't need this.
+ *
+ * Sets up a threaded interrupt handler for a device that has
+ * a dedicated wake-up interrupt in addition to the device IO
+ * interrupt.
+ *
+ * The interrupt starts disabled, and needs to be managed for
+ * the device by the bus code or the device driver using
+ * dev_pm_enable_wake_irq() and dev_pm_disable_wake_irq()
+ * functions.
+ */
+int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
+{
+	struct wake_irq *wirq;
+	int err;
+
+	wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
+	if (!wirq)
+		return -ENOMEM;
+
+	wirq->dev = dev;
+	wirq->irq = irq;
+	wirq->manage_irq = true;
+	irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+	/*
+	 * Consumer device may need to power up and restore state
+	 * so we use a threaded irq.
+	 */
+	err = request_threaded_irq(irq, NULL, handle_threaded_wake_irq,
+				   IRQF_ONESHOT, dev_name(dev), wirq);
+	if (err)
+		goto err_free;
+
+	err = dev_pm_attach_wake_irq(dev, irq, wirq);
+	if (err)
+		goto err_free_irq;
+
+	return err;
+
+err_free_irq:
+	free_irq(irq, wirq);
+err_free:
+	kfree(wirq);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq);
+
+/**
+ * dev_pm_enable_wake_irq - Enable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_suspend() to enable the wake-up interrupt while
+ * the device is running.
+ *
+ * Note that for runtime_suspend()) the wake-up interrupts
+ * should be unconditionally enabled unlike for suspend()
+ * that is conditional.
+ */
+void dev_pm_enable_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+
+	if (wirq && wirq->manage_irq)
+		enable_irq(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
+
+/**
+ * dev_pm_disable_wake_irq - Disable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_resume() to disable the wake-up interrupt while
+ * the device is running.
+ */
+void dev_pm_disable_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+
+	if (wirq && wirq->manage_irq)
+		disable_irq_nosync(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
+
+/**
+ * dev_pm_arm_wake_irq - Arm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Sets up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+	if (!wirq)
+		return;
+
+	if (device_may_wakeup(wirq->dev))
+		enable_irq_wake(wirq->irq);
+}
+
+/**
+ * dev_pm_disarm_wake_irq - Disarm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Clears up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+	if (!wirq)
+		return;
+
+	if (device_may_wakeup(wirq->dev))
+		disable_irq_wake(wirq->irq);
+}
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 7726200..bed2fbb 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -14,6 +14,7 @@
 #include <linux/suspend.h>
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
+#include <linux/pm_wakeirq.h>
 #include <trace/events/power.h>
 
 #include "power.h"
@@ -238,6 +239,100 @@ int device_wakeup_enable(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(device_wakeup_enable);
 
+#ifdef CONFIG_PM_WAKEIRQ
+
+/**
+ * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source
+ * @dev: Device to handle
+ * @irq: Device specific wakeirq entry
+ *
+ * Attach a device specific wakeirq to the device specific
+ * wakeup source so the device wakeirq can be configured
+ * automatically for suspend and resume.
+ */
+int device_wakeup_attach_irq(struct device *dev,
+			     struct wake_irq *wakeirq)
+{
+	struct wakeup_source *ws;
+	int ret = 0;
+
+	spin_lock_irq(&dev->power.lock);
+	ws = dev->power.wakeup;
+	if (!ws) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	if (ws->wakeirq) {
+		ret = -EEXIST;
+		goto unlock;
+	}
+
+	ws->wakeirq = wakeirq;
+
+unlock:
+	spin_unlock_irq(&dev->power.lock);
+
+	return ret;
+}
+
+/**
+ * device_wakeup_detach_irq - Detach a wakeirq from a wakeup source
+ * @dev: Device to handle
+ *
+ * Removes the wakeirq from a wakeup source.
+ */
+void device_wakeup_detach_irq(struct device *dev)
+{
+	struct wakeup_source *ws;
+
+	spin_lock_irq(&dev->power.lock);
+	ws = dev->power.wakeup;
+	if (!ws)
+		goto unlock;
+
+	ws->wakeirq = NULL;
+
+unlock:
+	spin_unlock_irq(&dev->power.lock);
+}
+
+/**
+ * device_wakeup_arm_wake_irqs(void)
+ *
+ * Itereates over the list of wakeirqs to arm them.
+ */
+void device_wakeup_arm_wake_irqs(void)
+{
+	struct wakeup_source *ws;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+		if (ws->wakeirq)
+			dev_pm_arm_wake_irq(ws->wakeirq);
+	}
+	rcu_read_unlock();
+}
+
+/**
+ * device_wakeup_disarm_wake_irqs(void)
+ *
+ * Itereates over the list of wakeirqs to disarm them.
+ */
+void device_wakeup_disarm_wake_irqs(void)
+{
+	struct wakeup_source *ws;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+		if (ws->wakeirq)
+			dev_pm_disarm_wake_irq(ws->wakeirq);
+	}
+	rcu_read_unlock();
+}
+
+#endif /* CONFIG_PM_WAKEIRQ */
+
 /**
  * device_wakeup_detach - Detach a device's wakeup source object from it.
  * @dev: Device to detach the wakeup source object from.
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 2d29c64..d1130d5 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -529,6 +529,7 @@ enum rpm_request {
 };
 
 struct wakeup_source;
+struct wake_irq;
 struct pm_domain_data;
 
 struct pm_subsys_data {
@@ -558,6 +559,7 @@ struct dev_pm_info {
 	struct list_head	entry;
 	struct completion	completion;
 	struct wakeup_source	*wakeup;
+	struct wake_irq		*wakeirq;
 	bool			wakeup_path:1;
 	bool			syscore:1;
 #else
diff --git a/include/linux/pm_wakeirq.h b/include/linux/pm_wakeirq.h
new file mode 100644
index 0000000..d6478c7
--- /dev/null
+++ b/include/linux/pm_wakeirq.h
@@ -0,0 +1,52 @@
+/*
+ * pm_wakeirq.h - Device wakeirq helper functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_PM_WAKEIRQ_H
+#define _LINUX_PM_WAKEIRQ_H
+
+#ifdef CONFIG_PM_WAKEIRQ
+
+extern int dev_pm_set_wake_irq(struct device *dev, int irq);
+extern int dev_pm_set_dedicated_wake_irq(struct device *dev,
+					 int irq);
+extern void dev_pm_clear_wake_irq(struct device *dev);
+extern void dev_pm_enable_wake_irq(struct device *dev);
+extern void dev_pm_disable_wake_irq(struct device *dev);
+
+#else	/* !CONFIG_PM_WAKEIRQ */
+
+static inline int dev_pm_set_wake_irq(struct device *dev, int irq)
+{
+	return 0;
+}
+
+static inline int dev_pm_set_dedicated__wake_irq(struct device *dev,
+						 int irq)
+{
+	return 0;
+}
+
+static inline void dev_pm_clear_wake_irq(struct device *dev)
+{
+}
+
+static inline void dev_pm_enable_wake_irq(struct device *dev)
+{
+}
+
+static inline void dev_pm_disable_wake_irq(struct device *dev)
+{
+}
+
+#endif	/* CONFIG_PM_WAKEIRQ */
+#endif	/* _LINUX_PM_WAKEIRQ_H */
diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h
index a0f7080..86d1cbe 100644
--- a/include/linux/pm_wakeup.h
+++ b/include/linux/pm_wakeup.h
@@ -28,9 +28,15 @@
 
 #include <linux/types.h>
 
+struct wake_irq;
+
 /**
  * struct wakeup_source - Representation of wakeup sources
  *
+ * @name: Name of the wakeup source
+ * @entry: Wakeup source list entry
+ * @lock: Wakeup source lock
+ * @wakeirq: Optional device specific wakeirq
  * @total_time: Total time this wakeup source has been active.
  * @max_time: Maximum time this wakeup source has been continuously active.
  * @last_time: Monotonic clock when the wakeup source's was touched last time.
@@ -47,6 +53,7 @@ struct wakeup_source {
 	const char 		*name;
 	struct list_head	entry;
 	spinlock_t		lock;
+	struct wake_irq		*wakeirq;
 	struct timer_list	timer;
 	unsigned long		timer_expires;
 	ktime_t total_time;
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 7e01f78..d3735bd 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -267,6 +267,10 @@ config PM_CLK
 	def_bool y
 	depends on PM && HAVE_CLK
 
+config PM_WAKEIRQ
+	bool
+	depends on PM_SLEEP
+
 config PM_GENERIC_DOMAINS
 	bool
 	depends on PM

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-18 22:05                 ` Tony Lindgren
@ 2015-05-18 23:44                   ` Tony Lindgren
  2015-05-19 14:04                     ` Rafael J. Wysocki
  0 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-18 23:44 UTC (permalink / raw)
  To: Felipe Balbi
  Cc: Rafael J. Wysocki, Rafael J. Wysocki, Alan Stern,
	Andreas Fenkart, Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

* Tony Lindgren <tony@atomide.com> [150518 15:06]:
> +/**
> + * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ
> + * @dev: Device entry
> + * @irq: Device IO interrupt
> + *
> + * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets
> + * automatically configured for wake-up from suspend  based
> +void dev_pm_clear_wake_irq(struct device *dev)
> +{
> +	struct wake_irq *wirq = dev->power.wakeirq;
> +	unsigned long flags;
> +
> +	if (!wirq)
> +		return;
> +
> +	device_wakeup_detach_irq(dev);
> +
> +	spin_lock_irqsave(&dev->power.lock, flags);
> +	if (wirq->manage_irq) {
> +		free_irq(wirq->irq, wirq);
> +		wirq->manage_irq = false;
> +	}
> +	dev->power.wakeirq = NULL;
> +	spin_unlock_irqrestore(&dev->power.lock, flags);
> +
> +	wirq->irq = -EINVAL;
> +	kfree(wirq);
> +}

I just noticed most of the dev_pm_clear_wake_irq is no longer needed.
We're now freeing it anyways. so it can be just:

void dev_pm_clear_wake_irq(struct device *dev)
{
	struct wake_irq *wirq = dev->power.wakeirq;
	unsigned long flags;

	if (!wirq)
		return;

	spin_lock_irqsave(&dev->power.lock, flags);
	dev->power.wakeirq = NULL;
	spin_unlock_irqrestore(&dev->power.lock, flags);

	device_wakeup_detach_irq(dev);
	if (wirq->manage_irq)
		free_irq(wirq->irq, wirq);
	kfree(wirq);
}


Regards,

Tony

8< ---------------------
From: Tony Lindgren <tony@atomide.com>
Date: Mon, 18 May 2015 15:40:29 -0700
Subject: [PATCH] PM / Wakeirq: Add automated device wake IRQ handling

Turns out we can automate the handling for the device_may_wakeup()
quite a bit by using the kernel wakeup source list.

And as some hardware has separate dedicated wake-up interrupt
in addition to the IO interrupt, we can automate the handling by
adding a generic threaded interrupt handler that just calls the
device PM runtime to wake up the device.

This allows dropping code from device drivers as we currently
are doing it in multiple ways, and often wrong.

For most drivers, we should be able to drop the following
boilerplate code from runtime_suspend and runtime_resume
functions:

	...
	device_init_wakeup(dev, true);
	...
	if (device_may_wakeup(dev)
		enable_irq_wake(irq);
	...
	if (device_may_wakeup(dev)
		enable_irq_wake(irq);
	...
	device_init_wakeup(dev, false);
	...

We can replace it with just the following init and exit
time code:

	...
	device_init_wakeup(dev, true);
	dev_pm_set_wake_irq(dev, irq);
	...
	dev_pm_clear_wake_irq(dev);
	device_init_wakeup(dev, false);
	...

And for hardware with dedicated wake-up interrupts:

	...
	device_init_wakeup(dev, true);
	dev_pm_set_dedicated_wake_irq(dev, irq);
	...
	dev_pm_clear_wake_irq(dev);
	device_init_wakeup(dev, false);
	...

For now, let's only enable it for select PM_WAKEIRQ.

Signed-off-by: Tony Lindgren <tony@atomide.com>

diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 1cb8544..527546e 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -4,5 +4,6 @@ obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 obj-$(CONFIG_PM_OPP)	+= opp.o
 obj-$(CONFIG_PM_GENERIC_DOMAINS)	+=  domain.o domain_governor.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
+obj-$(CONFIG_PM_WAKEIRQ)	+= wakeirq.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 3d874ec..6f2515c 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -24,6 +24,7 @@
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm-trace.h>
+#include <linux/pm_wakeirq.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
 #include <linux/async.h>
@@ -587,6 +588,7 @@ void dpm_resume_noirq(pm_message_t state)
 	async_synchronize_full();
 	dpm_show_time(starttime, state, "noirq");
 	resume_device_irqs();
+	device_wakeup_disarm_wake_irqs();
 	cpuidle_resume();
 	trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
 }
@@ -1104,6 +1106,7 @@ int dpm_suspend_noirq(pm_message_t state)
 
 	trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
 	cpuidle_pause();
+	device_wakeup_arm_wake_irqs();
 	suspend_device_irqs();
 	mutex_lock(&dpm_list_mtx);
 	pm_transition = state;
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index b6b8a27..708de3b 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -20,6 +20,54 @@ static inline void pm_runtime_early_init(struct device *dev)
 extern void pm_runtime_init(struct device *dev);
 extern void pm_runtime_remove(struct device *dev);
 
+#ifdef CONFIG_PM_WAKEIRQ
+
+struct wake_irq {
+	struct device *dev;
+	int irq;
+	bool manage_irq:1;
+};
+
+extern int device_wakeup_attach_irq(struct device *dev,
+				    struct wake_irq *wakeirq);
+extern void device_wakeup_detach_irq(struct device *dev);
+extern void device_wakeup_arm_wake_irqs(void);
+extern void device_wakeup_disarm_wake_irqs(void);
+
+extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
+extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
+
+#else
+
+static inline int
+device_wakeup_attach_irq(struct device *dev,
+			 struct wake_irq *wakeirq)
+{
+	return 0;
+}
+
+static inline void device_wakeup_detach_irq(struct device *dev)
+{
+}
+
+static inline void device_wakeup_arm_wake_irqs(void)
+{
+}
+
+static inline void device_wakeup_disarm_wake_irqs(void)
+{
+}
+
+static inline void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+}
+
+static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+}
+
+#endif /* CONFIG_PM_WAKEIRQ */
+
 /*
  * sysfs.c
  */
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 4ffe4a2..e1a10a0 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -10,6 +10,7 @@
 #include <linux/sched.h>
 #include <linux/export.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
 #include <trace/events/rpm.h>
 #include "power.h"
 
@@ -514,6 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 
 	callback = RPM_GET_CALLBACK(dev, runtime_suspend);
 
+	dev_pm_enable_wake_irq(dev);
 	retval = rpm_callback(callback, dev);
 	if (retval)
 		goto fail;
@@ -552,6 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 	return retval;
 
  fail:
+	dev_pm_disable_wake_irq(dev);
 	__update_runtime_status(dev, RPM_ACTIVE);
 	dev->power.deferred_resume = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -734,10 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags)
 
 	callback = RPM_GET_CALLBACK(dev, runtime_resume);
 
+	dev_pm_disable_wake_irq(dev);
 	retval = rpm_callback(callback, dev);
 	if (retval) {
 		__update_runtime_status(dev, RPM_SUSPENDED);
 		pm_runtime_cancel_pending(dev);
+		dev_pm_enable_wake_irq(dev);
 	} else {
  no_callback:
 		__update_runtime_status(dev, RPM_ACTIVE);
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
new file mode 100644
index 0000000..aa0e636
--- /dev/null
+++ b/drivers/base/power/wakeirq.c
@@ -0,0 +1,272 @@
+/*
+ * wakeirq.c - Device wakeirq helper functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
+
+#include "power.h"
+
+/**
+ * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ
+ * @dev: Device entry
+ * @irq: Device wake-up capable interrupt
+ * @wirq: Wake irq specific data
+ *
+ * Internal function to attach either a device IO interrupt or a
+ * dedicated wake-up interrupt as a wake IRQ.
+ */
+static int dev_pm_attach_wake_irq(struct device *dev, int irq,
+				  struct wake_irq *wirq)
+{
+	unsigned long flags;
+	int err;
+
+	if (!dev || !wirq)
+		return -EINVAL;
+
+	if (!dev->power.wakeup) {
+		dev_err(dev, "forgot to call call device_init_wakeup?\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+	if (dev_WARN_ONCE(dev, dev->power.wakeirq,
+			  "wake irq already initialized\n")) {
+		spin_unlock_irqrestore(&dev->power.lock, flags);
+		return -EEXIST;
+	}
+
+	dev->power.wakeirq = wirq;
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	err = device_wakeup_attach_irq(dev, wirq);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+/**
+ * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ
+ * @dev: Device entry
+ * @irq: Device IO interrupt
+ *
+ * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets
+ * automatically configured for wake-up from suspend  based
+ * on the device specific sysfs wakeup entry. Typically called
+ * during driver probe after calling device_init_wakeup().
+ */
+int dev_pm_set_wake_irq(struct device *dev, int irq)
+{
+	struct wake_irq *wirq;
+	int err;
+
+	wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
+	if (!wirq)
+		return -ENOMEM;
+
+	wirq->dev = dev;
+	wirq->irq = irq;
+
+	err = dev_pm_attach_wake_irq(dev, irq, wirq);
+	if (err)
+		kfree(wirq);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq);
+
+/**
+ * dev_pm_clear_wake_irq - Detach a device IO interrupt wake IRQ
+ * @dev: Device entry
+ *
+ * Detach a device IO interrupt wake IRQ and free resources.
+ *
+ * Note that it's OK for drivers to call this without calling
+ * dev_pm_set_wake_irq() first as all the driver instances may
+ * not have the wake IRQ configured. This avoid adding wake IRQ
+ * checks into the drivers.
+ */
+void dev_pm_clear_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+	unsigned long flags;
+
+	if (!wirq)
+		return;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+	dev->power.wakeirq = NULL;
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	device_wakeup_detach_irq(dev);
+	if (wirq->manage_irq)
+		free_irq(wirq->irq, wirq);
+	kfree(wirq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
+
+/**
+ * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts
+ * @irq: Device dedicated wake-up interrupt
+ * @_wirq: Wake IRQ data
+ *
+ * Some devices have a separate wake-up interrupt in addition to the
+ * device IO interrupt. The wake-up interrupts signal that the device
+ * should be woken up from a idle state. This handler uses device
+ * specific pm_runtime functions to wake the device and then it's
+ * up to the device to do whatever it needs to. Note as the device
+ * may need to restore context and start up regulators, we use a
+ * threaded IRQ.
+ *
+ * Also note that we are not resending the lost device interrupts.
+ * We assume that the wake-up interrupt just needs to wake-up the
+ * device, and the device pm_runtime_resume() can deal with the
+ * situation.
+ */
+static irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq)
+{
+	struct wake_irq *wirq = _wirq;
+
+	/* We don't want RPM_ASYNC or RPM_NOWAIT here */
+	return pm_runtime_resume(wirq->dev) ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/**
+ * dev_pm_set_dedicated_wake_irq - Request a dedicated wake-up interrupt
+ * @dev: Device entry
+ * @irq: Device wake-up interrupt
+ *
+ * Unless your hardware has separate wake-up interrupts in addition
+ * to the device IO interrupts, you don't need this.
+ *
+ * Sets up a threaded interrupt handler for a device that has
+ * a dedicated wake-up interrupt in addition to the device IO
+ * interrupt.
+ *
+ * The interrupt starts disabled, and needs to be managed for
+ * the device by the bus code or the device driver using
+ * dev_pm_enable_wake_irq() and dev_pm_disable_wake_irq()
+ * functions.
+ */
+int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
+{
+	struct wake_irq *wirq;
+	int err;
+
+	wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
+	if (!wirq)
+		return -ENOMEM;
+
+	wirq->dev = dev;
+	wirq->irq = irq;
+	wirq->manage_irq = true;
+	irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+	/*
+	 * Consumer device may need to power up and restore state
+	 * so we use a threaded irq.
+	 */
+	err = request_threaded_irq(irq, NULL, handle_threaded_wake_irq,
+				   IRQF_ONESHOT, dev_name(dev), wirq);
+	if (err)
+		goto err_free;
+
+	err = dev_pm_attach_wake_irq(dev, irq, wirq);
+	if (err)
+		goto err_free_irq;
+
+	return err;
+
+err_free_irq:
+	free_irq(irq, wirq);
+err_free:
+	kfree(wirq);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq);
+
+/**
+ * dev_pm_enable_wake_irq - Enable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_suspend() to enable the wake-up interrupt while
+ * the device is running.
+ *
+ * Note that for runtime_suspend()) the wake-up interrupts
+ * should be unconditionally enabled unlike for suspend()
+ * that is conditional.
+ */
+void dev_pm_enable_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+
+	if (wirq && wirq->manage_irq)
+		enable_irq(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
+
+/**
+ * dev_pm_disable_wake_irq - Disable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_resume() to disable the wake-up interrupt while
+ * the device is running.
+ */
+void dev_pm_disable_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+
+	if (wirq && wirq->manage_irq)
+		disable_irq_nosync(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
+
+/**
+ * dev_pm_arm_wake_irq - Arm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Sets up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+	if (!wirq)
+		return;
+
+	if (device_may_wakeup(wirq->dev))
+		enable_irq_wake(wirq->irq);
+}
+
+/**
+ * dev_pm_disarm_wake_irq - Disarm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Clears up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+	if (!wirq)
+		return;
+
+	if (device_may_wakeup(wirq->dev))
+		disable_irq_wake(wirq->irq);
+}
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 7726200..bed2fbb 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -14,6 +14,7 @@
 #include <linux/suspend.h>
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
+#include <linux/pm_wakeirq.h>
 #include <trace/events/power.h>
 
 #include "power.h"
@@ -238,6 +239,100 @@ int device_wakeup_enable(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(device_wakeup_enable);
 
+#ifdef CONFIG_PM_WAKEIRQ
+
+/**
+ * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source
+ * @dev: Device to handle
+ * @irq: Device specific wakeirq entry
+ *
+ * Attach a device specific wakeirq to the device specific
+ * wakeup source so the device wakeirq can be configured
+ * automatically for suspend and resume.
+ */
+int device_wakeup_attach_irq(struct device *dev,
+			     struct wake_irq *wakeirq)
+{
+	struct wakeup_source *ws;
+	int ret = 0;
+
+	spin_lock_irq(&dev->power.lock);
+	ws = dev->power.wakeup;
+	if (!ws) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	if (ws->wakeirq) {
+		ret = -EEXIST;
+		goto unlock;
+	}
+
+	ws->wakeirq = wakeirq;
+
+unlock:
+	spin_unlock_irq(&dev->power.lock);
+
+	return ret;
+}
+
+/**
+ * device_wakeup_detach_irq - Detach a wakeirq from a wakeup source
+ * @dev: Device to handle
+ *
+ * Removes the wakeirq from a wakeup source.
+ */
+void device_wakeup_detach_irq(struct device *dev)
+{
+	struct wakeup_source *ws;
+
+	spin_lock_irq(&dev->power.lock);
+	ws = dev->power.wakeup;
+	if (!ws)
+		goto unlock;
+
+	ws->wakeirq = NULL;
+
+unlock:
+	spin_unlock_irq(&dev->power.lock);
+}
+
+/**
+ * device_wakeup_arm_wake_irqs(void)
+ *
+ * Itereates over the list of wakeirqs to arm them.
+ */
+void device_wakeup_arm_wake_irqs(void)
+{
+	struct wakeup_source *ws;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+		if (ws->wakeirq)
+			dev_pm_arm_wake_irq(ws->wakeirq);
+	}
+	rcu_read_unlock();
+}
+
+/**
+ * device_wakeup_disarm_wake_irqs(void)
+ *
+ * Itereates over the list of wakeirqs to disarm them.
+ */
+void device_wakeup_disarm_wake_irqs(void)
+{
+	struct wakeup_source *ws;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+		if (ws->wakeirq)
+			dev_pm_disarm_wake_irq(ws->wakeirq);
+	}
+	rcu_read_unlock();
+}
+
+#endif /* CONFIG_PM_WAKEIRQ */
+
 /**
  * device_wakeup_detach - Detach a device's wakeup source object from it.
  * @dev: Device to detach the wakeup source object from.
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 2d29c64..d1130d5 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -529,6 +529,7 @@ enum rpm_request {
 };
 
 struct wakeup_source;
+struct wake_irq;
 struct pm_domain_data;
 
 struct pm_subsys_data {
@@ -558,6 +559,7 @@ struct dev_pm_info {
 	struct list_head	entry;
 	struct completion	completion;
 	struct wakeup_source	*wakeup;
+	struct wake_irq		*wakeirq;
 	bool			wakeup_path:1;
 	bool			syscore:1;
 #else
diff --git a/include/linux/pm_wakeirq.h b/include/linux/pm_wakeirq.h
new file mode 100644
index 0000000..d6478c7
--- /dev/null
+++ b/include/linux/pm_wakeirq.h
@@ -0,0 +1,52 @@
+/*
+ * pm_wakeirq.h - Device wakeirq helper functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_PM_WAKEIRQ_H
+#define _LINUX_PM_WAKEIRQ_H
+
+#ifdef CONFIG_PM_WAKEIRQ
+
+extern int dev_pm_set_wake_irq(struct device *dev, int irq);
+extern int dev_pm_set_dedicated_wake_irq(struct device *dev,
+					 int irq);
+extern void dev_pm_clear_wake_irq(struct device *dev);
+extern void dev_pm_enable_wake_irq(struct device *dev);
+extern void dev_pm_disable_wake_irq(struct device *dev);
+
+#else	/* !CONFIG_PM_WAKEIRQ */
+
+static inline int dev_pm_set_wake_irq(struct device *dev, int irq)
+{
+	return 0;
+}
+
+static inline int dev_pm_set_dedicated__wake_irq(struct device *dev,
+						 int irq)
+{
+	return 0;
+}
+
+static inline void dev_pm_clear_wake_irq(struct device *dev)
+{
+}
+
+static inline void dev_pm_enable_wake_irq(struct device *dev)
+{
+}
+
+static inline void dev_pm_disable_wake_irq(struct device *dev)
+{
+}
+
+#endif	/* CONFIG_PM_WAKEIRQ */
+#endif	/* _LINUX_PM_WAKEIRQ_H */
diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h
index a0f7080..86d1cbe 100644
--- a/include/linux/pm_wakeup.h
+++ b/include/linux/pm_wakeup.h
@@ -28,9 +28,15 @@
 
 #include <linux/types.h>
 
+struct wake_irq;
+
 /**
  * struct wakeup_source - Representation of wakeup sources
  *
+ * @name: Name of the wakeup source
+ * @entry: Wakeup source list entry
+ * @lock: Wakeup source lock
+ * @wakeirq: Optional device specific wakeirq
  * @total_time: Total time this wakeup source has been active.
  * @max_time: Maximum time this wakeup source has been continuously active.
  * @last_time: Monotonic clock when the wakeup source's was touched last time.
@@ -47,6 +53,7 @@ struct wakeup_source {
 	const char 		*name;
 	struct list_head	entry;
 	spinlock_t		lock;
+	struct wake_irq		*wakeirq;
 	struct timer_list	timer;
 	unsigned long		timer_expires;
 	ktime_t total_time;
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 7e01f78..d3735bd 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -267,6 +267,10 @@ config PM_CLK
 	def_bool y
 	depends on PM && HAVE_CLK
 
+config PM_WAKEIRQ
+	bool
+	depends on PM_SLEEP
+
 config PM_GENERIC_DOMAINS
 	bool
 	depends on PM

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-18 23:44                   ` Tony Lindgren
@ 2015-05-19 14:04                     ` Rafael J. Wysocki
  2015-05-19 14:26                       ` Rafael J. Wysocki
  2015-05-19 15:15                       ` Tony Lindgren
  0 siblings, 2 replies; 43+ messages in thread
From: Rafael J. Wysocki @ 2015-05-19 14:04 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Felipe Balbi, Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

On Monday, May 18, 2015 04:44:01 PM Tony Lindgren wrote:
> * Tony Lindgren <tony@atomide.com> [150518 15:06]:
> > +/**
> > + * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ
> > + * @dev: Device entry
> > + * @irq: Device IO interrupt
> > + *
> > + * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets
> > + * automatically configured for wake-up from suspend  based
> > +void dev_pm_clear_wake_irq(struct device *dev)
> > +{
> > +	struct wake_irq *wirq = dev->power.wakeirq;
> > +	unsigned long flags;
> > +
> > +	if (!wirq)
> > +		return;
> > +
> > +	device_wakeup_detach_irq(dev);
> > +
> > +	spin_lock_irqsave(&dev->power.lock, flags);
> > +	if (wirq->manage_irq) {
> > +		free_irq(wirq->irq, wirq);
> > +		wirq->manage_irq = false;
> > +	}
> > +	dev->power.wakeirq = NULL;
> > +	spin_unlock_irqrestore(&dev->power.lock, flags);
> > +
> > +	wirq->irq = -EINVAL;
> > +	kfree(wirq);
> > +}
> 
> I just noticed most of the dev_pm_clear_wake_irq is no longer needed.
> We're now freeing it anyways. so it can be just:
> 
> void dev_pm_clear_wake_irq(struct device *dev)
> {
> 	struct wake_irq *wirq = dev->power.wakeirq;
> 	unsigned long flags;
> 
> 	if (!wirq)
> 		return;
> 
> 	spin_lock_irqsave(&dev->power.lock, flags);
> 	dev->power.wakeirq = NULL;
> 	spin_unlock_irqrestore(&dev->power.lock, flags);
> 
> 	device_wakeup_detach_irq(dev);
> 	if (wirq->manage_irq)
> 		free_irq(wirq->irq, wirq);
> 	kfree(wirq);
> }
> 
> 
> Regards,
> 
> Tony
> 
> 8< ---------------------
> From: Tony Lindgren <tony@atomide.com>
> Date: Mon, 18 May 2015 15:40:29 -0700
> Subject: [PATCH] PM / Wakeirq: Add automated device wake IRQ handling
> 
> Turns out we can automate the handling for the device_may_wakeup()
> quite a bit by using the kernel wakeup source list.
> 
> And as some hardware has separate dedicated wake-up interrupt
> in addition to the IO interrupt, we can automate the handling by
> adding a generic threaded interrupt handler that just calls the
> device PM runtime to wake up the device.
> 
> This allows dropping code from device drivers as we currently
> are doing it in multiple ways, and often wrong.
> 
> For most drivers, we should be able to drop the following
> boilerplate code from runtime_suspend and runtime_resume
> functions:
> 
> 	...
> 	device_init_wakeup(dev, true);
> 	...
> 	if (device_may_wakeup(dev)
> 		enable_irq_wake(irq);
> 	...
> 	if (device_may_wakeup(dev)
> 		enable_irq_wake(irq);

Closing parens are missin in the above two if () statements.

Also, should the second one be disable_irq_wake(irq)?

> 	...
> 	device_init_wakeup(dev, false);
> 	...
> 
> We can replace it with just the following init and exit
> time code:
> 
> 	...
> 	device_init_wakeup(dev, true);
> 	dev_pm_set_wake_irq(dev, irq);
> 	...
> 	dev_pm_clear_wake_irq(dev);
> 	device_init_wakeup(dev, false);
> 	...
> 
> And for hardware with dedicated wake-up interrupts:
> 
> 	...
> 	device_init_wakeup(dev, true);
> 	dev_pm_set_dedicated_wake_irq(dev, irq);
> 	...
> 	dev_pm_clear_wake_irq(dev);
> 	device_init_wakeup(dev, false);
> 	...
> 
> For now, let's only enable it for select PM_WAKEIRQ.

Why?  What would be wrong with doing that unconditionally?


> Signed-off-by: Tony Lindgren <tony@atomide.com>

Looks good overall, a couple of nits below.

[cut]

> +/**
> + * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts
> + * @irq: Device dedicated wake-up interrupt
> + * @_wirq: Wake IRQ data
> + *
> + * Some devices have a separate wake-up interrupt in addition to the
> + * device IO interrupt. The wake-up interrupts signal that the device
> + * should be woken up from a idle state. This handler uses device
> + * specific pm_runtime functions to wake the device and then it's
> + * up to the device to do whatever it needs to. Note as the device
> + * may need to restore context and start up regulators, we use a
> + * threaded IRQ.
> + *
> + * Also note that we are not resending the lost device interrupts.
> + * We assume that the wake-up interrupt just needs to wake-up the
> + * device, and the device pm_runtime_resume() can deal with the
> + * situation.
> + */
> +static irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq)
> +{
> +	struct wake_irq *wirq = _wirq;
> +
> +	/* We don't want RPM_ASYNC or RPM_NOWAIT here */
> +	return pm_runtime_resume(wirq->dev) ? IRQ_NONE : IRQ_HANDLED;

There are various reasons why pm_runtime_resume() may return error codes and
some of them don't mean that the interrupt was not legitimate.

Moreover, it returns 1 if the device is already active, in which case the above
check won't do any good to us.

Why not to return IRQ_HANDLED unconditionally from here?


[cut]

> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 7e01f78..d3735bd 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -267,6 +267,10 @@ config PM_CLK
>  	def_bool y
>  	depends on PM && HAVE_CLK
>  
> +config PM_WAKEIRQ
> +	bool
> +	depends on PM_SLEEP
> +

If you really really want this (I'm still not sure why exactly, though), it
should depend on PM_SLEEP || PM_RUNTIME, because the latter uses it too.

>  config PM_GENERIC_DOMAINS
>  	bool
>  	depends on PM

Thanks,
Rafael


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-19 14:04                     ` Rafael J. Wysocki
@ 2015-05-19 14:26                       ` Rafael J. Wysocki
  2015-05-19 15:09                         ` Tony Lindgren
  2015-05-19 15:15                       ` Tony Lindgren
  1 sibling, 1 reply; 43+ messages in thread
From: Rafael J. Wysocki @ 2015-05-19 14:26 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Felipe Balbi, Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

On Tuesday, May 19, 2015 04:04:43 PM Rafael J. Wysocki wrote:
> On Monday, May 18, 2015 04:44:01 PM Tony Lindgren wrote:
> > * Tony Lindgren <tony@atomide.com> [150518 15:06]:
> > > +/**
> > > + * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ
> > > + * @dev: Device entry
> > > + * @irq: Device IO interrupt
> > > + *
> > > + * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets
> > > + * automatically configured for wake-up from suspend  based
> > > +void dev_pm_clear_wake_irq(struct device *dev)
> > > +{
> > > +	struct wake_irq *wirq = dev->power.wakeirq;
> > > +	unsigned long flags;
> > > +
> > > +	if (!wirq)
> > > +		return;
> > > +
> > > +	device_wakeup_detach_irq(dev);
> > > +
> > > +	spin_lock_irqsave(&dev->power.lock, flags);
> > > +	if (wirq->manage_irq) {
> > > +		free_irq(wirq->irq, wirq);
> > > +		wirq->manage_irq = false;
> > > +	}
> > > +	dev->power.wakeirq = NULL;
> > > +	spin_unlock_irqrestore(&dev->power.lock, flags);
> > > +
> > > +	wirq->irq = -EINVAL;
> > > +	kfree(wirq);
> > > +}
> > 
> > I just noticed most of the dev_pm_clear_wake_irq is no longer needed.
> > We're now freeing it anyways. so it can be just:
> > 
> > void dev_pm_clear_wake_irq(struct device *dev)
> > {
> > 	struct wake_irq *wirq = dev->power.wakeirq;
> > 	unsigned long flags;
> > 
> > 	if (!wirq)
> > 		return;
> > 
> > 	spin_lock_irqsave(&dev->power.lock, flags);
> > 	dev->power.wakeirq = NULL;
> > 	spin_unlock_irqrestore(&dev->power.lock, flags);
> > 
> > 	device_wakeup_detach_irq(dev);
> > 	if (wirq->manage_irq)
> > 		free_irq(wirq->irq, wirq);
> > 	kfree(wirq);
> > }
> > 
> > 
> > Regards,
> > 
> > Tony
> > 
> > 8< ---------------------
> > From: Tony Lindgren <tony@atomide.com>
> > Date: Mon, 18 May 2015 15:40:29 -0700
> > Subject: [PATCH] PM / Wakeirq: Add automated device wake IRQ handling
> > 
> > Turns out we can automate the handling for the device_may_wakeup()
> > quite a bit by using the kernel wakeup source list.
> > 
> > And as some hardware has separate dedicated wake-up interrupt
> > in addition to the IO interrupt, we can automate the handling by
> > adding a generic threaded interrupt handler that just calls the
> > device PM runtime to wake up the device.
> > 
> > This allows dropping code from device drivers as we currently
> > are doing it in multiple ways, and often wrong.
> > 
> > For most drivers, we should be able to drop the following
> > boilerplate code from runtime_suspend and runtime_resume
> > functions:
> > 
> > 	...
> > 	device_init_wakeup(dev, true);
> > 	...
> > 	if (device_may_wakeup(dev)
> > 		enable_irq_wake(irq);
> > 	...
> > 	if (device_may_wakeup(dev)
> > 		enable_irq_wake(irq);
> 
> Closing parens are missin in the above two if () statements.
> 
> Also, should the second one be disable_irq_wake(irq)?
> 
> > 	...
> > 	device_init_wakeup(dev, false);
> > 	...
> > 
> > We can replace it with just the following init and exit
> > time code:
> > 
> > 	...
> > 	device_init_wakeup(dev, true);
> > 	dev_pm_set_wake_irq(dev, irq);
> > 	...
> > 	dev_pm_clear_wake_irq(dev);
> > 	device_init_wakeup(dev, false);
> > 	...
> > 
> > And for hardware with dedicated wake-up interrupts:
> > 
> > 	...
> > 	device_init_wakeup(dev, true);
> > 	dev_pm_set_dedicated_wake_irq(dev, irq);
> > 	...
> > 	dev_pm_clear_wake_irq(dev);
> > 	device_init_wakeup(dev, false);
> > 	...
> > 
> > For now, let's only enable it for select PM_WAKEIRQ.
> 
> Why?  What would be wrong with doing that unconditionally?

I mean, what about making it depend on CONFIG_PM directly?


-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-19 14:26                       ` Rafael J. Wysocki
@ 2015-05-19 15:09                         ` Tony Lindgren
  2015-05-19 18:18                           ` Tony Lindgren
  0 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-19 15:09 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Felipe Balbi, Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

* Rafael J. Wysocki <rjw@rjwysocki.net> [150519 07:02]:
> On Tuesday, May 19, 2015 04:04:43 PM Rafael J. Wysocki wrote:
> > On Monday, May 18, 2015 04:44:01 PM Tony Lindgren wrote:
> > > 
> > > For most drivers, we should be able to drop the following
> > > boilerplate code from runtime_suspend and runtime_resume
> > > functions:
> > > 
> > > 	...
> > > 	device_init_wakeup(dev, true);
> > > 	...
> > > 	if (device_may_wakeup(dev)
> > > 		enable_irq_wake(irq);
> > > 	...
> > > 	if (device_may_wakeup(dev)
> > > 		enable_irq_wake(irq);
> > 
> > Closing parens are missin in the above two if () statements.
> > 
> > Also, should the second one be disable_irq_wake(irq)?

Thanks yeah it should disable_irq_wake :) Will fix.

> > > 	...
> > > 	device_init_wakeup(dev, false);
> > > 	...
> > > 
> > > We can replace it with just the following init and exit
> > > time code:
> > > 
> > > 	...
> > > 	device_init_wakeup(dev, true);
> > > 	dev_pm_set_wake_irq(dev, irq);
> > > 	...
> > > 	dev_pm_clear_wake_irq(dev);
> > > 	device_init_wakeup(dev, false);
> > > 	...
> > > 
> > > And for hardware with dedicated wake-up interrupts:
> > > 
> > > 	...
> > > 	device_init_wakeup(dev, true);
> > > 	dev_pm_set_dedicated_wake_irq(dev, irq);
> > > 	...
> > > 	dev_pm_clear_wake_irq(dev);
> > > 	device_init_wakeup(dev, false);
> > > 	...
> > > 
> > > For now, let's only enable it for select PM_WAKEIRQ.
> > 
> > Why?  What would be wrong with doing that unconditionally?

No reason to make it conditional any longer. it's there from
the earlier version that only handled the dedicated wake IRQS.
 
> I mean, what about making it depend on CONFIG_PM directly?

OK let's do that.

Regards,

Tony

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-19 14:04                     ` Rafael J. Wysocki
  2015-05-19 14:26                       ` Rafael J. Wysocki
@ 2015-05-19 15:15                       ` Tony Lindgren
  1 sibling, 0 replies; 43+ messages in thread
From: Tony Lindgren @ 2015-05-19 15:15 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Felipe Balbi, Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

* Rafael J. Wysocki <rjw@rjwysocki.net> [150519 06:40]:
> On Monday, May 18, 2015 04:44:01 PM Tony Lindgren wrote:
> > +/**
> > + * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts
> > + * @irq: Device dedicated wake-up interrupt
> > + * @_wirq: Wake IRQ data
> > + *
> > + * Some devices have a separate wake-up interrupt in addition to the
> > + * device IO interrupt. The wake-up interrupts signal that the device
> > + * should be woken up from a idle state. This handler uses device
> > + * specific pm_runtime functions to wake the device and then it's
> > + * up to the device to do whatever it needs to. Note as the device
> > + * may need to restore context and start up regulators, we use a
> > + * threaded IRQ.
> > + *
> > + * Also note that we are not resending the lost device interrupts.
> > + * We assume that the wake-up interrupt just needs to wake-up the
> > + * device, and the device pm_runtime_resume() can deal with the
> > + * situation.
> > + */
> > +static irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq)
> > +{
> > +	struct wake_irq *wirq = _wirq;
> > +
> > +	/* We don't want RPM_ASYNC or RPM_NOWAIT here */
> > +	return pm_runtime_resume(wirq->dev) ? IRQ_NONE : IRQ_HANDLED;
> 
> There are various reasons why pm_runtime_resume() may return error codes and
> some of them don't mean that the interrupt was not legitimate.
> 
> Moreover, it returns 1 if the device is already active, in which case the above
> check won't do any good to us.

OK yeah that check won't work then.
 
> Why not to return IRQ_HANDLED unconditionally from here?

OK

Regards,

Tony

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-19 15:09                         ` Tony Lindgren
@ 2015-05-19 18:18                           ` Tony Lindgren
  2015-05-19 23:01                             ` Rafael J. Wysocki
  0 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-19 18:18 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Felipe Balbi, Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

* Tony Lindgren <tony@atomide.com> [150519 08:21]:
> * Rafael J. Wysocki <rjw@rjwysocki.net> [150519 07:02]:
> > On Tuesday, May 19, 2015 04:04:43 PM Rafael J. Wysocki wrote:
> > > On Monday, May 18, 2015 04:44:01 PM Tony Lindgren wrote:
> > > > 
> > > > We can replace it with just the following init and exit
> > > > time code:
> > > > 
> > > > 	...
> > > > 	device_init_wakeup(dev, true);
> > > > 	dev_pm_set_wake_irq(dev, irq);
> > > > 	...
> > > > 	dev_pm_clear_wake_irq(dev);
> > > > 	device_init_wakeup(dev, false);
> > > > 	...
> > > > 
> > > > And for hardware with dedicated wake-up interrupts:
> > > > 
> > > > 	...
> > > > 	device_init_wakeup(dev, true);
> > > > 	dev_pm_set_dedicated_wake_irq(dev, irq);
> > > > 	...
> > > > 	dev_pm_clear_wake_irq(dev);
> > > > 	device_init_wakeup(dev, false);
> > > > 	...
> > > > 
> > > > For now, let's only enable it for select PM_WAKEIRQ.
> > > 
> > > Why?  What would be wrong with doing that unconditionally?
> 
> No reason to make it conditional any longer. it's there from
> the earlier version that only handled the dedicated wake IRQS.
>  
> > I mean, what about making it depend on CONFIG_PM directly?
> 
> OK let's do that.

Here's the patch updated and now using just CONFIG_PM.

Regards,

Tony

8< ---------------------------
From: Tony Lindgren <tony@atomide.com>
Date: Mon, 18 May 2015 15:40:29 -0700
Subject: [PATCH] PM / Wakeirq: Add automated device wake IRQ handling

Turns out we can automate the handling for the device_may_wakeup()
quite a bit by using the kernel wakeup source list as suggested
by Rafael J. Wysocki <rjw@rjwysocki.net>.

And as some hardware has separate dedicated wake-up interrupt
in addition to the IO interrupt, we can automate the handling by
adding a generic threaded interrupt handler that just calls the
device PM runtime to wake up the device.

This allows dropping code from device drivers as we currently
are doing it in multiple ways, and often wrong.

For most drivers, we should be able to drop the following
boilerplate code from runtime_suspend and runtime_resume
functions:

	...
	device_init_wakeup(dev, true);
	...
	if (device_may_wakeup(dev))
		enable_irq_wake(irq);
	...
	if (device_may_wakeup(dev))
		disable_irq_wake(irq);
	...
	device_init_wakeup(dev, false);
	...

We can replace it with just the following init and exit
time code:

	...
	device_init_wakeup(dev, true);
	dev_pm_set_wake_irq(dev, irq);
	...
	dev_pm_clear_wake_irq(dev);
	device_init_wakeup(dev, false);
	...

And for hardware with dedicated wake-up interrupts:

	...
	device_init_wakeup(dev, true);
	dev_pm_set_dedicated_wake_irq(dev, irq);
	...
	dev_pm_clear_wake_irq(dev);
	device_init_wakeup(dev, false);
	...

Signed-off-by: Tony Lindgren <tony@atomide.com>

diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 1cb8544..f94a6cc 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_PM)	+= sysfs.o generic_ops.o common.o qos.o runtime.o
+obj-$(CONFIG_PM)	+= sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o
 obj-$(CONFIG_PM_SLEEP)	+= main.o wakeup.o
 obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 obj-$(CONFIG_PM_OPP)	+= opp.o
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 3d874ec..6f2515c 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -24,6 +24,7 @@
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm-trace.h>
+#include <linux/pm_wakeirq.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
 #include <linux/async.h>
@@ -587,6 +588,7 @@ void dpm_resume_noirq(pm_message_t state)
 	async_synchronize_full();
 	dpm_show_time(starttime, state, "noirq");
 	resume_device_irqs();
+	device_wakeup_disarm_wake_irqs();
 	cpuidle_resume();
 	trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
 }
@@ -1104,6 +1106,7 @@ int dpm_suspend_noirq(pm_message_t state)
 
 	trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
 	cpuidle_pause();
+	device_wakeup_arm_wake_irqs();
 	suspend_device_irqs();
 	mutex_lock(&dpm_list_mtx);
 	pm_transition = state;
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index b6b8a27..f1a5d95 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -20,6 +20,46 @@ static inline void pm_runtime_early_init(struct device *dev)
 extern void pm_runtime_init(struct device *dev);
 extern void pm_runtime_remove(struct device *dev);
 
+struct wake_irq {
+	struct device *dev;
+	int irq;
+	bool dedicated_irq:1;
+};
+
+extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
+extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
+
+#ifdef CONFIG_PM_SLEEP
+
+extern int device_wakeup_attach_irq(struct device *dev,
+				    struct wake_irq *wakeirq);
+extern void device_wakeup_detach_irq(struct device *dev);
+extern void device_wakeup_arm_wake_irqs(void);
+extern void device_wakeup_disarm_wake_irqs(void);
+
+#else
+
+static inline int
+device_wakeup_attach_irq(struct device *dev,
+			 struct wake_irq *wakeirq)
+{
+	return 0;
+}
+
+static inline void device_wakeup_detach_irq(struct device *dev)
+{
+}
+
+static inline void device_wakeup_arm_wake_irqs(void)
+{
+}
+
+static inline void device_wakeup_disarm_wake_irqs(void)
+{
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
 /*
  * sysfs.c
  */
@@ -52,6 +92,14 @@ static inline void wakeup_sysfs_remove(struct device *dev) {}
 static inline int pm_qos_sysfs_add(struct device *dev) { return 0; }
 static inline void pm_qos_sysfs_remove(struct device *dev) {}
 
+static inline void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+}
+
+static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+}
+
 #endif
 
 #ifdef CONFIG_PM_SLEEP
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 4ffe4a2..e1a10a0 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -10,6 +10,7 @@
 #include <linux/sched.h>
 #include <linux/export.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
 #include <trace/events/rpm.h>
 #include "power.h"
 
@@ -514,6 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 
 	callback = RPM_GET_CALLBACK(dev, runtime_suspend);
 
+	dev_pm_enable_wake_irq(dev);
 	retval = rpm_callback(callback, dev);
 	if (retval)
 		goto fail;
@@ -552,6 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 	return retval;
 
  fail:
+	dev_pm_disable_wake_irq(dev);
 	__update_runtime_status(dev, RPM_ACTIVE);
 	dev->power.deferred_resume = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -734,10 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags)
 
 	callback = RPM_GET_CALLBACK(dev, runtime_resume);
 
+	dev_pm_disable_wake_irq(dev);
 	retval = rpm_callback(callback, dev);
 	if (retval) {
 		__update_runtime_status(dev, RPM_SUSPENDED);
 		pm_runtime_cancel_pending(dev);
+		dev_pm_enable_wake_irq(dev);
 	} else {
  no_callback:
 		__update_runtime_status(dev, RPM_ACTIVE);
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
new file mode 100644
index 0000000..7470004
--- /dev/null
+++ b/drivers/base/power/wakeirq.c
@@ -0,0 +1,273 @@
+/*
+ * wakeirq.c - Device wakeirq helper functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
+
+#include "power.h"
+
+/**
+ * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ
+ * @dev: Device entry
+ * @irq: Device wake-up capable interrupt
+ * @wirq: Wake irq specific data
+ *
+ * Internal function to attach either a device IO interrupt or a
+ * dedicated wake-up interrupt as a wake IRQ.
+ */
+static int dev_pm_attach_wake_irq(struct device *dev, int irq,
+				  struct wake_irq *wirq)
+{
+	unsigned long flags;
+	int err;
+
+	if (!dev || !wirq)
+		return -EINVAL;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+	if (dev_WARN_ONCE(dev, dev->power.wakeirq,
+			  "wake irq already initialized\n")) {
+		spin_unlock_irqrestore(&dev->power.lock, flags);
+		return -EEXIST;
+	}
+
+	dev->power.wakeirq = wirq;
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	err = device_wakeup_attach_irq(dev, wirq);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+/**
+ * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ
+ * @dev: Device entry
+ * @irq: Device IO interrupt
+ *
+ * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets
+ * automatically configured for wake-up from suspend  based
+ * on the device specific sysfs wakeup entry. Typically called
+ * during driver probe after calling device_init_wakeup().
+ */
+int dev_pm_set_wake_irq(struct device *dev, int irq)
+{
+	struct wake_irq *wirq;
+	int err;
+
+	wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
+	if (!wirq)
+		return -ENOMEM;
+
+	wirq->dev = dev;
+	wirq->irq = irq;
+
+	err = dev_pm_attach_wake_irq(dev, irq, wirq);
+	if (err)
+		kfree(wirq);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq);
+
+/**
+ * dev_pm_clear_wake_irq - Detach a device IO interrupt wake IRQ
+ * @dev: Device entry
+ *
+ * Detach a device wake IRQ and free resources.
+ *
+ * Note that it's OK for drivers to call this without calling
+ * dev_pm_set_wake_irq() as all the driver instances may not have
+ * a wake IRQ configured. This avoid adding wake IRQ specific
+ * checks into the drivers.
+ */
+void dev_pm_clear_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+	unsigned long flags;
+
+	if (!wirq)
+		return;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+	dev->power.wakeirq = NULL;
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	device_wakeup_detach_irq(dev);
+	if (wirq->dedicated_irq)
+		free_irq(wirq->irq, wirq);
+	kfree(wirq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
+
+/**
+ * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts
+ * @irq: Device specific dedicated wake-up interrupt
+ * @_wirq: Wake IRQ data
+ *
+ * Some devices have a separate wake-up interrupt in addition to the
+ * device IO interrupt. The wake-up interrupt signals that a device
+ * should be woken up from it's idle state. This handler uses device
+ * specific pm_runtime functions to wake the device, and then it's
+ * up to the device to do whatever it needs to. Note that as the
+ * device may need to restore context and start up regulators, we
+ * use a threaded IRQ.
+ *
+ * Also note that we are not resending the lost device interrupts.
+ * We assume that the wake-up interrupt just needs to wake-up the
+ * device, and then device's pm_runtime_resume() can deal with the
+ * situation.
+ */
+static irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq)
+{
+	struct wake_irq *wirq = _wirq;
+	int res;
+
+	/* We don't want RPM_ASYNC or RPM_NOWAIT here */
+	res = pm_runtime_resume(wirq->dev);
+	if (res < 0)
+		dev_warn(wirq->dev,
+			 "wake IRQ with no resume: %i\n", res);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * dev_pm_set_dedicated_wake_irq - Request a dedicated wake-up interrupt
+ * @dev: Device entry
+ * @irq: Device wake-up interrupt
+ *
+ * Unless your hardware has separate wake-up interrupts in addition
+ * to the device IO interrupts, you don't need this.
+ *
+ * Sets up a threaded interrupt handler for a device that has
+ * a dedicated wake-up interrupt in addition to the device IO
+ * interrupt.
+ *
+ * The interrupt starts disabled, and needs to be managed for
+ * the device by the bus code or the device driver using
+ * dev_pm_enable_wake_irq() and dev_pm_disable_wake_irq()
+ * functions.
+ */
+int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
+{
+	struct wake_irq *wirq;
+	int err;
+
+	wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
+	if (!wirq)
+		return -ENOMEM;
+
+	wirq->dev = dev;
+	wirq->irq = irq;
+	wirq->dedicated_irq = true;
+	irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+	/*
+	 * Consumer device may need to power up and restore state
+	 * so we use a threaded irq.
+	 */
+	err = request_threaded_irq(irq, NULL, handle_threaded_wake_irq,
+				   IRQF_ONESHOT, dev_name(dev), wirq);
+	if (err)
+		goto err_free;
+
+	err = dev_pm_attach_wake_irq(dev, irq, wirq);
+	if (err)
+		goto err_free_irq;
+
+	return err;
+
+err_free_irq:
+	free_irq(irq, wirq);
+err_free:
+	kfree(wirq);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq);
+
+/**
+ * dev_pm_enable_wake_irq - Enable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_suspend() to enable the wake-up interrupt while
+ * the device is running.
+ *
+ * Note that for runtime_suspend()) the wake-up interrupts
+ * should be unconditionally enabled unlike for suspend()
+ * that is conditional.
+ */
+void dev_pm_enable_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+
+	if (wirq && wirq->dedicated_irq)
+		enable_irq(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
+
+/**
+ * dev_pm_disable_wake_irq - Disable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_resume() to disable the wake-up interrupt while
+ * the device is running.
+ */
+void dev_pm_disable_wake_irq(struct device *dev)
+{
+	struct wake_irq *wirq = dev->power.wakeirq;
+
+	if (wirq && wirq->dedicated_irq)
+		disable_irq_nosync(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
+
+/**
+ * dev_pm_arm_wake_irq - Arm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Sets up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+	if (!wirq)
+		return;
+
+	if (device_may_wakeup(wirq->dev))
+		enable_irq_wake(wirq->irq);
+}
+
+/**
+ * dev_pm_disarm_wake_irq - Disarm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Clears up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+	if (!wirq)
+		return;
+
+	if (device_may_wakeup(wirq->dev))
+		disable_irq_wake(wirq->irq);
+}
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 7726200..7332ebc 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -14,6 +14,7 @@
 #include <linux/suspend.h>
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
+#include <linux/pm_wakeirq.h>
 #include <trace/events/power.h>
 
 #include "power.h"
@@ -239,6 +240,97 @@ int device_wakeup_enable(struct device *dev)
 EXPORT_SYMBOL_GPL(device_wakeup_enable);
 
 /**
+ * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source
+ * @dev: Device to handle
+ * @wakeirq: Device specific wakeirq entry
+ *
+ * Attach a device wakeirq to the wakeup source so the device
+ * wake IRQ can be configured automatically for suspend and
+ * resume.
+ */
+int device_wakeup_attach_irq(struct device *dev,
+			     struct wake_irq *wakeirq)
+{
+	struct wakeup_source *ws;
+	int ret = 0;
+
+	spin_lock_irq(&dev->power.lock);
+	ws = dev->power.wakeup;
+	if (!ws) {
+		dev_err(dev, "forgot to call call device_init_wakeup?\n");
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	if (ws->wakeirq) {
+		ret = -EEXIST;
+		goto unlock;
+	}
+
+	ws->wakeirq = wakeirq;
+
+unlock:
+	spin_unlock_irq(&dev->power.lock);
+
+	return ret;
+}
+
+/**
+ * device_wakeup_detach_irq - Detach a wakeirq from a wakeup source
+ * @dev: Device to handle
+ *
+ * Removes a device wakeirq from the wakeup source.
+ */
+void device_wakeup_detach_irq(struct device *dev)
+{
+	struct wakeup_source *ws;
+
+	spin_lock_irq(&dev->power.lock);
+	ws = dev->power.wakeup;
+	if (!ws)
+		goto unlock;
+
+	ws->wakeirq = NULL;
+
+unlock:
+	spin_unlock_irq(&dev->power.lock);
+}
+
+/**
+ * device_wakeup_arm_wake_irqs(void)
+ *
+ * Itereates over the list of device wakeirqs to arm them.
+ */
+void device_wakeup_arm_wake_irqs(void)
+{
+	struct wakeup_source *ws;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+		if (ws->wakeirq)
+			dev_pm_arm_wake_irq(ws->wakeirq);
+	}
+	rcu_read_unlock();
+}
+
+/**
+ * device_wakeup_disarm_wake_irqs(void)
+ *
+ * Itereates over the list of device wakeirqs to disarm them.
+ */
+void device_wakeup_disarm_wake_irqs(void)
+{
+	struct wakeup_source *ws;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+		if (ws->wakeirq)
+			dev_pm_disarm_wake_irq(ws->wakeirq);
+	}
+	rcu_read_unlock();
+}
+
+/**
  * device_wakeup_detach - Detach a device's wakeup source object from it.
  * @dev: Device to detach the wakeup source object from.
  *
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 2d29c64..1c4ed0c 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -529,6 +529,7 @@ enum rpm_request {
 };
 
 struct wakeup_source;
+struct wake_irq;
 struct pm_domain_data;
 
 struct pm_subsys_data {
@@ -568,6 +569,7 @@ struct dev_pm_info {
 	unsigned long		timer_expires;
 	struct work_struct	work;
 	wait_queue_head_t	wait_queue;
+	struct wake_irq		*wakeirq;
 	atomic_t		usage_count;
 	atomic_t		child_count;
 	unsigned int		disable_depth:3;
diff --git a/include/linux/pm_wakeirq.h b/include/linux/pm_wakeirq.h
new file mode 100644
index 0000000..4046fa1
--- /dev/null
+++ b/include/linux/pm_wakeirq.h
@@ -0,0 +1,52 @@
+/*
+ * pm_wakeirq.h - Device wakeirq helper functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_PM_WAKEIRQ_H
+#define _LINUX_PM_WAKEIRQ_H
+
+#ifdef CONFIG_PM
+
+extern int dev_pm_set_wake_irq(struct device *dev, int irq);
+extern int dev_pm_set_dedicated_wake_irq(struct device *dev,
+					 int irq);
+extern void dev_pm_clear_wake_irq(struct device *dev);
+extern void dev_pm_enable_wake_irq(struct device *dev);
+extern void dev_pm_disable_wake_irq(struct device *dev);
+
+#else	/* !CONFIG_PM */
+
+static inline int dev_pm_set_wake_irq(struct device *dev, int irq)
+{
+	return 0;
+}
+
+static inline int dev_pm_set_dedicated__wake_irq(struct device *dev,
+						 int irq)
+{
+	return 0;
+}
+
+static inline void dev_pm_clear_wake_irq(struct device *dev)
+{
+}
+
+static inline void dev_pm_enable_wake_irq(struct device *dev)
+{
+}
+
+static inline void dev_pm_disable_wake_irq(struct device *dev)
+{
+}
+
+#endif	/* CONFIG_PM */
+#endif	/* _LINUX_PM_WAKEIRQ_H */
diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h
index a0f7080..a344793 100644
--- a/include/linux/pm_wakeup.h
+++ b/include/linux/pm_wakeup.h
@@ -28,9 +28,17 @@
 
 #include <linux/types.h>
 
+struct wake_irq;
+
 /**
  * struct wakeup_source - Representation of wakeup sources
  *
+ * @name: Name of the wakeup source
+ * @entry: Wakeup source list entry
+ * @lock: Wakeup source lock
+ * @wakeirq: Optional device specific wakeirq
+ * @timer: Wakeup timer list
+ * @timer_expires: Wakeup timer expiration
  * @total_time: Total time this wakeup source has been active.
  * @max_time: Maximum time this wakeup source has been continuously active.
  * @last_time: Monotonic clock when the wakeup source's was touched last time.
@@ -47,6 +55,7 @@ struct wakeup_source {
 	const char 		*name;
 	struct list_head	entry;
 	spinlock_t		lock;
+	struct wake_irq		*wakeirq;
 	struct timer_list	timer;
 	unsigned long		timer_expires;
 	ktime_t total_time;

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-19 23:01                             ` Rafael J. Wysocki
@ 2015-05-19 22:41                               ` Thomas Gleixner
  2015-05-19 23:31                                 ` Rafael J. Wysocki
  0 siblings, 1 reply; 43+ messages in thread
From: Thomas Gleixner @ 2015-05-19 22:41 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Tony Lindgren, Felipe Balbi, Rafael J. Wysocki, Alan Stern,
	Andreas Fenkart, Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, linux-pm, linux-kernel,
	linux-serial, linux-omap

On Wed, 20 May 2015, Rafael J. Wysocki wrote:
> This one looks really good. :-)
> 
> If it doesn't depend on anything, I can apply it right away, so please let me
> know.

Can you pretty please trim your replies? It's a PITA to scroll through
several pages of quoted patch just to find your signature as the extra
content.

Thanks,

	tglx

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-19 18:18                           ` Tony Lindgren
@ 2015-05-19 23:01                             ` Rafael J. Wysocki
  2015-05-19 22:41                               ` Thomas Gleixner
  0 siblings, 1 reply; 43+ messages in thread
From: Rafael J. Wysocki @ 2015-05-19 23:01 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Felipe Balbi, Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap

On Tuesday, May 19, 2015 11:18:42 AM Tony Lindgren wrote:
> * Tony Lindgren <tony@atomide.com> [150519 08:21]:
> > * Rafael J. Wysocki <rjw@rjwysocki.net> [150519 07:02]:
> > > On Tuesday, May 19, 2015 04:04:43 PM Rafael J. Wysocki wrote:
> > > > On Monday, May 18, 2015 04:44:01 PM Tony Lindgren wrote:
> > > > > 
> > > > > We can replace it with just the following init and exit
> > > > > time code:
> > > > > 
> > > > > 	...
> > > > > 	device_init_wakeup(dev, true);
> > > > > 	dev_pm_set_wake_irq(dev, irq);
> > > > > 	...
> > > > > 	dev_pm_clear_wake_irq(dev);
> > > > > 	device_init_wakeup(dev, false);
> > > > > 	...
> > > > > 
> > > > > And for hardware with dedicated wake-up interrupts:
> > > > > 
> > > > > 	...
> > > > > 	device_init_wakeup(dev, true);
> > > > > 	dev_pm_set_dedicated_wake_irq(dev, irq);
> > > > > 	...
> > > > > 	dev_pm_clear_wake_irq(dev);
> > > > > 	device_init_wakeup(dev, false);
> > > > > 	...
> > > > > 
> > > > > For now, let's only enable it for select PM_WAKEIRQ.
> > > > 
> > > > Why?  What would be wrong with doing that unconditionally?
> > 
> > No reason to make it conditional any longer. it's there from
> > the earlier version that only handled the dedicated wake IRQS.
> >  
> > > I mean, what about making it depend on CONFIG_PM directly?
> > 
> > OK let's do that.
> 
> Here's the patch updated and now using just CONFIG_PM.

This one looks really good. :-)

If it doesn't depend on anything, I can apply it right away, so please let me
know.


> 8< ---------------------------
> From: Tony Lindgren <tony@atomide.com>
> Date: Mon, 18 May 2015 15:40:29 -0700
> Subject: [PATCH] PM / Wakeirq: Add automated device wake IRQ handling
> 
> Turns out we can automate the handling for the device_may_wakeup()
> quite a bit by using the kernel wakeup source list as suggested
> by Rafael J. Wysocki <rjw@rjwysocki.net>.
> 
> And as some hardware has separate dedicated wake-up interrupt
> in addition to the IO interrupt, we can automate the handling by
> adding a generic threaded interrupt handler that just calls the
> device PM runtime to wake up the device.
> 
> This allows dropping code from device drivers as we currently
> are doing it in multiple ways, and often wrong.
> 
> For most drivers, we should be able to drop the following
> boilerplate code from runtime_suspend and runtime_resume
> functions:
> 
> 	...
> 	device_init_wakeup(dev, true);
> 	...
> 	if (device_may_wakeup(dev))
> 		enable_irq_wake(irq);
> 	...
> 	if (device_may_wakeup(dev))
> 		disable_irq_wake(irq);
> 	...
> 	device_init_wakeup(dev, false);
> 	...
> 
> We can replace it with just the following init and exit
> time code:
> 
> 	...
> 	device_init_wakeup(dev, true);
> 	dev_pm_set_wake_irq(dev, irq);
> 	...
> 	dev_pm_clear_wake_irq(dev);
> 	device_init_wakeup(dev, false);
> 	...
> 
> And for hardware with dedicated wake-up interrupts:
> 
> 	...
> 	device_init_wakeup(dev, true);
> 	dev_pm_set_dedicated_wake_irq(dev, irq);
> 	...
> 	dev_pm_clear_wake_irq(dev);
> 	device_init_wakeup(dev, false);
> 	...
> 
> Signed-off-by: Tony Lindgren <tony@atomide.com>
> 
> diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
> index 1cb8544..f94a6cc 100644
> --- a/drivers/base/power/Makefile
> +++ b/drivers/base/power/Makefile
> @@ -1,4 +1,4 @@
> -obj-$(CONFIG_PM)	+= sysfs.o generic_ops.o common.o qos.o runtime.o
> +obj-$(CONFIG_PM)	+= sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o
>  obj-$(CONFIG_PM_SLEEP)	+= main.o wakeup.o
>  obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
>  obj-$(CONFIG_PM_OPP)	+= opp.o
> diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
> index 3d874ec..6f2515c 100644
> --- a/drivers/base/power/main.c
> +++ b/drivers/base/power/main.c
> @@ -24,6 +24,7 @@
>  #include <linux/pm.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/pm-trace.h>
> +#include <linux/pm_wakeirq.h>
>  #include <linux/interrupt.h>
>  #include <linux/sched.h>
>  #include <linux/async.h>
> @@ -587,6 +588,7 @@ void dpm_resume_noirq(pm_message_t state)
>  	async_synchronize_full();
>  	dpm_show_time(starttime, state, "noirq");
>  	resume_device_irqs();
> +	device_wakeup_disarm_wake_irqs();
>  	cpuidle_resume();
>  	trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
>  }
> @@ -1104,6 +1106,7 @@ int dpm_suspend_noirq(pm_message_t state)
>  
>  	trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
>  	cpuidle_pause();
> +	device_wakeup_arm_wake_irqs();
>  	suspend_device_irqs();
>  	mutex_lock(&dpm_list_mtx);
>  	pm_transition = state;
> diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
> index b6b8a27..f1a5d95 100644
> --- a/drivers/base/power/power.h
> +++ b/drivers/base/power/power.h
> @@ -20,6 +20,46 @@ static inline void pm_runtime_early_init(struct device *dev)
>  extern void pm_runtime_init(struct device *dev);
>  extern void pm_runtime_remove(struct device *dev);
>  
> +struct wake_irq {
> +	struct device *dev;
> +	int irq;
> +	bool dedicated_irq:1;
> +};
> +
> +extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
> +extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
> +
> +#ifdef CONFIG_PM_SLEEP
> +
> +extern int device_wakeup_attach_irq(struct device *dev,
> +				    struct wake_irq *wakeirq);
> +extern void device_wakeup_detach_irq(struct device *dev);
> +extern void device_wakeup_arm_wake_irqs(void);
> +extern void device_wakeup_disarm_wake_irqs(void);
> +
> +#else
> +
> +static inline int
> +device_wakeup_attach_irq(struct device *dev,
> +			 struct wake_irq *wakeirq)
> +{
> +	return 0;
> +}
> +
> +static inline void device_wakeup_detach_irq(struct device *dev)
> +{
> +}
> +
> +static inline void device_wakeup_arm_wake_irqs(void)
> +{
> +}
> +
> +static inline void device_wakeup_disarm_wake_irqs(void)
> +{
> +}
> +
> +#endif /* CONFIG_PM_SLEEP */
> +
>  /*
>   * sysfs.c
>   */
> @@ -52,6 +92,14 @@ static inline void wakeup_sysfs_remove(struct device *dev) {}
>  static inline int pm_qos_sysfs_add(struct device *dev) { return 0; }
>  static inline void pm_qos_sysfs_remove(struct device *dev) {}
>  
> +static inline void dev_pm_arm_wake_irq(struct wake_irq *wirq)
> +{
> +}
> +
> +static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
> +{
> +}
> +
>  #endif
>  
>  #ifdef CONFIG_PM_SLEEP
> diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
> index 4ffe4a2..e1a10a0 100644
> --- a/drivers/base/power/runtime.c
> +++ b/drivers/base/power/runtime.c
> @@ -10,6 +10,7 @@
>  #include <linux/sched.h>
>  #include <linux/export.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/pm_wakeirq.h>
>  #include <trace/events/rpm.h>
>  #include "power.h"
>  
> @@ -514,6 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
>  
>  	callback = RPM_GET_CALLBACK(dev, runtime_suspend);
>  
> +	dev_pm_enable_wake_irq(dev);
>  	retval = rpm_callback(callback, dev);
>  	if (retval)
>  		goto fail;
> @@ -552,6 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
>  	return retval;
>  
>   fail:
> +	dev_pm_disable_wake_irq(dev);
>  	__update_runtime_status(dev, RPM_ACTIVE);
>  	dev->power.deferred_resume = false;
>  	wake_up_all(&dev->power.wait_queue);
> @@ -734,10 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags)
>  
>  	callback = RPM_GET_CALLBACK(dev, runtime_resume);
>  
> +	dev_pm_disable_wake_irq(dev);
>  	retval = rpm_callback(callback, dev);
>  	if (retval) {
>  		__update_runtime_status(dev, RPM_SUSPENDED);
>  		pm_runtime_cancel_pending(dev);
> +		dev_pm_enable_wake_irq(dev);
>  	} else {
>   no_callback:
>  		__update_runtime_status(dev, RPM_ACTIVE);
> diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
> new file mode 100644
> index 0000000..7470004
> --- /dev/null
> +++ b/drivers/base/power/wakeirq.c
> @@ -0,0 +1,273 @@
> +/*
> + * wakeirq.c - Device wakeirq helper functions
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> + * kind, whether express or implied; without even the implied warranty
> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/slab.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/pm_wakeirq.h>
> +
> +#include "power.h"
> +
> +/**
> + * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ
> + * @dev: Device entry
> + * @irq: Device wake-up capable interrupt
> + * @wirq: Wake irq specific data
> + *
> + * Internal function to attach either a device IO interrupt or a
> + * dedicated wake-up interrupt as a wake IRQ.
> + */
> +static int dev_pm_attach_wake_irq(struct device *dev, int irq,
> +				  struct wake_irq *wirq)
> +{
> +	unsigned long flags;
> +	int err;
> +
> +	if (!dev || !wirq)
> +		return -EINVAL;
> +
> +	spin_lock_irqsave(&dev->power.lock, flags);
> +	if (dev_WARN_ONCE(dev, dev->power.wakeirq,
> +			  "wake irq already initialized\n")) {
> +		spin_unlock_irqrestore(&dev->power.lock, flags);
> +		return -EEXIST;
> +	}
> +
> +	dev->power.wakeirq = wirq;
> +	spin_unlock_irqrestore(&dev->power.lock, flags);
> +
> +	err = device_wakeup_attach_irq(dev, wirq);
> +	if (err)
> +		return err;
> +
> +	return 0;
> +}
> +
> +/**
> + * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ
> + * @dev: Device entry
> + * @irq: Device IO interrupt
> + *
> + * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets
> + * automatically configured for wake-up from suspend  based
> + * on the device specific sysfs wakeup entry. Typically called
> + * during driver probe after calling device_init_wakeup().
> + */
> +int dev_pm_set_wake_irq(struct device *dev, int irq)
> +{
> +	struct wake_irq *wirq;
> +	int err;
> +
> +	wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
> +	if (!wirq)
> +		return -ENOMEM;
> +
> +	wirq->dev = dev;
> +	wirq->irq = irq;
> +
> +	err = dev_pm_attach_wake_irq(dev, irq, wirq);
> +	if (err)
> +		kfree(wirq);
> +
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq);
> +
> +/**
> + * dev_pm_clear_wake_irq - Detach a device IO interrupt wake IRQ
> + * @dev: Device entry
> + *
> + * Detach a device wake IRQ and free resources.
> + *
> + * Note that it's OK for drivers to call this without calling
> + * dev_pm_set_wake_irq() as all the driver instances may not have
> + * a wake IRQ configured. This avoid adding wake IRQ specific
> + * checks into the drivers.
> + */
> +void dev_pm_clear_wake_irq(struct device *dev)
> +{
> +	struct wake_irq *wirq = dev->power.wakeirq;
> +	unsigned long flags;
> +
> +	if (!wirq)
> +		return;
> +
> +	spin_lock_irqsave(&dev->power.lock, flags);
> +	dev->power.wakeirq = NULL;
> +	spin_unlock_irqrestore(&dev->power.lock, flags);
> +
> +	device_wakeup_detach_irq(dev);
> +	if (wirq->dedicated_irq)
> +		free_irq(wirq->irq, wirq);
> +	kfree(wirq);
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
> +
> +/**
> + * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts
> + * @irq: Device specific dedicated wake-up interrupt
> + * @_wirq: Wake IRQ data
> + *
> + * Some devices have a separate wake-up interrupt in addition to the
> + * device IO interrupt. The wake-up interrupt signals that a device
> + * should be woken up from it's idle state. This handler uses device
> + * specific pm_runtime functions to wake the device, and then it's
> + * up to the device to do whatever it needs to. Note that as the
> + * device may need to restore context and start up regulators, we
> + * use a threaded IRQ.
> + *
> + * Also note that we are not resending the lost device interrupts.
> + * We assume that the wake-up interrupt just needs to wake-up the
> + * device, and then device's pm_runtime_resume() can deal with the
> + * situation.
> + */
> +static irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq)
> +{
> +	struct wake_irq *wirq = _wirq;
> +	int res;
> +
> +	/* We don't want RPM_ASYNC or RPM_NOWAIT here */
> +	res = pm_runtime_resume(wirq->dev);
> +	if (res < 0)
> +		dev_warn(wirq->dev,
> +			 "wake IRQ with no resume: %i\n", res);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * dev_pm_set_dedicated_wake_irq - Request a dedicated wake-up interrupt
> + * @dev: Device entry
> + * @irq: Device wake-up interrupt
> + *
> + * Unless your hardware has separate wake-up interrupts in addition
> + * to the device IO interrupts, you don't need this.
> + *
> + * Sets up a threaded interrupt handler for a device that has
> + * a dedicated wake-up interrupt in addition to the device IO
> + * interrupt.
> + *
> + * The interrupt starts disabled, and needs to be managed for
> + * the device by the bus code or the device driver using
> + * dev_pm_enable_wake_irq() and dev_pm_disable_wake_irq()
> + * functions.
> + */
> +int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
> +{
> +	struct wake_irq *wirq;
> +	int err;
> +
> +	wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
> +	if (!wirq)
> +		return -ENOMEM;
> +
> +	wirq->dev = dev;
> +	wirq->irq = irq;
> +	wirq->dedicated_irq = true;
> +	irq_set_status_flags(irq, IRQ_NOAUTOEN);
> +
> +	/*
> +	 * Consumer device may need to power up and restore state
> +	 * so we use a threaded irq.
> +	 */
> +	err = request_threaded_irq(irq, NULL, handle_threaded_wake_irq,
> +				   IRQF_ONESHOT, dev_name(dev), wirq);
> +	if (err)
> +		goto err_free;
> +
> +	err = dev_pm_attach_wake_irq(dev, irq, wirq);
> +	if (err)
> +		goto err_free_irq;
> +
> +	return err;
> +
> +err_free_irq:
> +	free_irq(irq, wirq);
> +err_free:
> +	kfree(wirq);
> +
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq);
> +
> +/**
> + * dev_pm_enable_wake_irq - Enable device wake-up interrupt
> + * @dev: Device
> + *
> + * Called from the bus code or the device driver for
> + * runtime_suspend() to enable the wake-up interrupt while
> + * the device is running.
> + *
> + * Note that for runtime_suspend()) the wake-up interrupts
> + * should be unconditionally enabled unlike for suspend()
> + * that is conditional.
> + */
> +void dev_pm_enable_wake_irq(struct device *dev)
> +{
> +	struct wake_irq *wirq = dev->power.wakeirq;
> +
> +	if (wirq && wirq->dedicated_irq)
> +		enable_irq(wirq->irq);
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
> +
> +/**
> + * dev_pm_disable_wake_irq - Disable device wake-up interrupt
> + * @dev: Device
> + *
> + * Called from the bus code or the device driver for
> + * runtime_resume() to disable the wake-up interrupt while
> + * the device is running.
> + */
> +void dev_pm_disable_wake_irq(struct device *dev)
> +{
> +	struct wake_irq *wirq = dev->power.wakeirq;
> +
> +	if (wirq && wirq->dedicated_irq)
> +		disable_irq_nosync(wirq->irq);
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
> +
> +/**
> + * dev_pm_arm_wake_irq - Arm device wake-up
> + * @wirq: Device wake-up interrupt
> + *
> + * Sets up the wake-up event conditionally based on the
> + * device_may_wake().
> + */
> +void dev_pm_arm_wake_irq(struct wake_irq *wirq)
> +{
> +	if (!wirq)
> +		return;
> +
> +	if (device_may_wakeup(wirq->dev))
> +		enable_irq_wake(wirq->irq);
> +}
> +
> +/**
> + * dev_pm_disarm_wake_irq - Disarm device wake-up
> + * @wirq: Device wake-up interrupt
> + *
> + * Clears up the wake-up event conditionally based on the
> + * device_may_wake().
> + */
> +void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
> +{
> +	if (!wirq)
> +		return;
> +
> +	if (device_may_wakeup(wirq->dev))
> +		disable_irq_wake(wirq->irq);
> +}
> diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
> index 7726200..7332ebc 100644
> --- a/drivers/base/power/wakeup.c
> +++ b/drivers/base/power/wakeup.c
> @@ -14,6 +14,7 @@
>  #include <linux/suspend.h>
>  #include <linux/seq_file.h>
>  #include <linux/debugfs.h>
> +#include <linux/pm_wakeirq.h>
>  #include <trace/events/power.h>
>  
>  #include "power.h"
> @@ -239,6 +240,97 @@ int device_wakeup_enable(struct device *dev)
>  EXPORT_SYMBOL_GPL(device_wakeup_enable);
>  
>  /**
> + * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source
> + * @dev: Device to handle
> + * @wakeirq: Device specific wakeirq entry
> + *
> + * Attach a device wakeirq to the wakeup source so the device
> + * wake IRQ can be configured automatically for suspend and
> + * resume.
> + */
> +int device_wakeup_attach_irq(struct device *dev,
> +			     struct wake_irq *wakeirq)
> +{
> +	struct wakeup_source *ws;
> +	int ret = 0;
> +
> +	spin_lock_irq(&dev->power.lock);
> +	ws = dev->power.wakeup;
> +	if (!ws) {
> +		dev_err(dev, "forgot to call call device_init_wakeup?\n");
> +		ret = -EINVAL;
> +		goto unlock;
> +	}
> +
> +	if (ws->wakeirq) {
> +		ret = -EEXIST;
> +		goto unlock;
> +	}
> +
> +	ws->wakeirq = wakeirq;
> +
> +unlock:
> +	spin_unlock_irq(&dev->power.lock);
> +
> +	return ret;
> +}
> +
> +/**
> + * device_wakeup_detach_irq - Detach a wakeirq from a wakeup source
> + * @dev: Device to handle
> + *
> + * Removes a device wakeirq from the wakeup source.
> + */
> +void device_wakeup_detach_irq(struct device *dev)
> +{
> +	struct wakeup_source *ws;
> +
> +	spin_lock_irq(&dev->power.lock);
> +	ws = dev->power.wakeup;
> +	if (!ws)
> +		goto unlock;
> +
> +	ws->wakeirq = NULL;
> +
> +unlock:
> +	spin_unlock_irq(&dev->power.lock);
> +}
> +
> +/**
> + * device_wakeup_arm_wake_irqs(void)
> + *
> + * Itereates over the list of device wakeirqs to arm them.
> + */
> +void device_wakeup_arm_wake_irqs(void)
> +{
> +	struct wakeup_source *ws;
> +
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
> +		if (ws->wakeirq)
> +			dev_pm_arm_wake_irq(ws->wakeirq);
> +	}
> +	rcu_read_unlock();
> +}
> +
> +/**
> + * device_wakeup_disarm_wake_irqs(void)
> + *
> + * Itereates over the list of device wakeirqs to disarm them.
> + */
> +void device_wakeup_disarm_wake_irqs(void)
> +{
> +	struct wakeup_source *ws;
> +
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
> +		if (ws->wakeirq)
> +			dev_pm_disarm_wake_irq(ws->wakeirq);
> +	}
> +	rcu_read_unlock();
> +}
> +
> +/**
>   * device_wakeup_detach - Detach a device's wakeup source object from it.
>   * @dev: Device to detach the wakeup source object from.
>   *
> diff --git a/include/linux/pm.h b/include/linux/pm.h
> index 2d29c64..1c4ed0c 100644
> --- a/include/linux/pm.h
> +++ b/include/linux/pm.h
> @@ -529,6 +529,7 @@ enum rpm_request {
>  };
>  
>  struct wakeup_source;
> +struct wake_irq;
>  struct pm_domain_data;
>  
>  struct pm_subsys_data {
> @@ -568,6 +569,7 @@ struct dev_pm_info {
>  	unsigned long		timer_expires;
>  	struct work_struct	work;
>  	wait_queue_head_t	wait_queue;
> +	struct wake_irq		*wakeirq;
>  	atomic_t		usage_count;
>  	atomic_t		child_count;
>  	unsigned int		disable_depth:3;
> diff --git a/include/linux/pm_wakeirq.h b/include/linux/pm_wakeirq.h
> new file mode 100644
> index 0000000..4046fa1
> --- /dev/null
> +++ b/include/linux/pm_wakeirq.h
> @@ -0,0 +1,52 @@
> +/*
> + * pm_wakeirq.h - Device wakeirq helper functions
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> + * kind, whether express or implied; without even the implied warranty
> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _LINUX_PM_WAKEIRQ_H
> +#define _LINUX_PM_WAKEIRQ_H
> +
> +#ifdef CONFIG_PM
> +
> +extern int dev_pm_set_wake_irq(struct device *dev, int irq);
> +extern int dev_pm_set_dedicated_wake_irq(struct device *dev,
> +					 int irq);
> +extern void dev_pm_clear_wake_irq(struct device *dev);
> +extern void dev_pm_enable_wake_irq(struct device *dev);
> +extern void dev_pm_disable_wake_irq(struct device *dev);
> +
> +#else	/* !CONFIG_PM */
> +
> +static inline int dev_pm_set_wake_irq(struct device *dev, int irq)
> +{
> +	return 0;
> +}
> +
> +static inline int dev_pm_set_dedicated__wake_irq(struct device *dev,
> +						 int irq)
> +{
> +	return 0;
> +}
> +
> +static inline void dev_pm_clear_wake_irq(struct device *dev)
> +{
> +}
> +
> +static inline void dev_pm_enable_wake_irq(struct device *dev)
> +{
> +}
> +
> +static inline void dev_pm_disable_wake_irq(struct device *dev)
> +{
> +}
> +
> +#endif	/* CONFIG_PM */
> +#endif	/* _LINUX_PM_WAKEIRQ_H */
> diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h
> index a0f7080..a344793 100644
> --- a/include/linux/pm_wakeup.h
> +++ b/include/linux/pm_wakeup.h
> @@ -28,9 +28,17 @@
>  
>  #include <linux/types.h>
>  
> +struct wake_irq;
> +
>  /**
>   * struct wakeup_source - Representation of wakeup sources
>   *
> + * @name: Name of the wakeup source
> + * @entry: Wakeup source list entry
> + * @lock: Wakeup source lock
> + * @wakeirq: Optional device specific wakeirq
> + * @timer: Wakeup timer list
> + * @timer_expires: Wakeup timer expiration
>   * @total_time: Total time this wakeup source has been active.
>   * @max_time: Maximum time this wakeup source has been continuously active.
>   * @last_time: Monotonic clock when the wakeup source's was touched last time.
> @@ -47,6 +55,7 @@ struct wakeup_source {
>  	const char 		*name;
>  	struct list_head	entry;
>  	spinlock_t		lock;
> +	struct wake_irq		*wakeirq;
>  	struct timer_list	timer;
>  	unsigned long		timer_expires;
>  	ktime_t total_time;

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-19 23:31                                 ` Rafael J. Wysocki
@ 2015-05-19 23:27                                   ` Tony Lindgren
  2015-05-20  0:25                                     ` Rafael J. Wysocki
  0 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-19 23:27 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Thomas Gleixner, Felipe Balbi, Rafael J. Wysocki, Alan Stern,
	Andreas Fenkart, Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, linux-pm, linux-kernel,
	linux-serial, linux-omap

* Rafael J. Wysocki <rjw@rjwysocki.net> [150519 16:07]:
> On Wednesday, May 20, 2015 12:41:06 AM Thomas Gleixner wrote:
> > On Wed, 20 May 2015, Rafael J. Wysocki wrote:
> > > This one looks really good. :-)
> > > 
> > > If it doesn't depend on anything, I can apply it right away, so please let me
> > > know.

Sure works for me, it just has a dependency to patch #1 in this
series ("[PATCH 1/5] PM / Runtime: Update last_busy in rpm_resume").

> > Can you pretty please trim your replies? It's a PITA to scroll through
> > several pages of quoted patch just to find your signature as the extra
> > content.
> 
> Sorry about that.

Sounds like Thomas might be OK with the patch too then :)

Regards,

Tony 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-19 22:41                               ` Thomas Gleixner
@ 2015-05-19 23:31                                 ` Rafael J. Wysocki
  2015-05-19 23:27                                   ` Tony Lindgren
  0 siblings, 1 reply; 43+ messages in thread
From: Rafael J. Wysocki @ 2015-05-19 23:31 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Tony Lindgren, Felipe Balbi, Rafael J. Wysocki, Alan Stern,
	Andreas Fenkart, Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, linux-pm, linux-kernel,
	linux-serial, linux-omap

On Wednesday, May 20, 2015 12:41:06 AM Thomas Gleixner wrote:
> On Wed, 20 May 2015, Rafael J. Wysocki wrote:
> > This one looks really good. :-)
> > 
> > If it doesn't depend on anything, I can apply it right away, so please let me
> > know.
> 
> Can you pretty please trim your replies? It's a PITA to scroll through
> several pages of quoted patch just to find your signature as the extra
> content.

Sorry about that.


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-19 23:27                                   ` Tony Lindgren
@ 2015-05-20  0:25                                     ` Rafael J. Wysocki
  2015-05-20  2:10                                       ` Tony Lindgren
  0 siblings, 1 reply; 43+ messages in thread
From: Rafael J. Wysocki @ 2015-05-20  0:25 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Thomas Gleixner, Felipe Balbi, Rafael J. Wysocki, Alan Stern,
	Andreas Fenkart, Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, linux-pm, linux-kernel,
	linux-serial, linux-omap

On Tuesday, May 19, 2015 04:27:56 PM Tony Lindgren wrote:
> * Rafael J. Wysocki <rjw@rjwysocki.net> [150519 16:07]:
> > On Wednesday, May 20, 2015 12:41:06 AM Thomas Gleixner wrote:
> > > On Wed, 20 May 2015, Rafael J. Wysocki wrote:
> > > > This one looks really good. :-)
> > > > 
> > > > If it doesn't depend on anything, I can apply it right away, so please let me
> > > > know.
> 
> Sure works for me, it just has a dependency to patch #1 in this
> series ("[PATCH 1/5] PM / Runtime: Update last_busy in rpm_resume").

OK, I'll queue them both up for 4.2, then.

Please let me know if you need a special branch with them.

> > > Can you pretty please trim your replies? It's a PITA to scroll through
> > > several pages of quoted patch just to find your signature as the extra
> > > content.
> > 
> > Sorry about that.
> 
> Sounds like Thomas might be OK with the patch too then :)

Well, yeah.


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-20  0:25                                     ` Rafael J. Wysocki
@ 2015-05-20  2:10                                       ` Tony Lindgren
  2015-05-21  0:54                                         ` Rafael J. Wysocki
  0 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-20  2:10 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Thomas Gleixner, Felipe Balbi, Rafael J. Wysocki, Alan Stern,
	Andreas Fenkart, Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, linux-pm, linux-kernel,
	linux-serial, linux-omap

* Rafael J. Wysocki <rjw@rjwysocki.net> [150519 17:01]:
> On Tuesday, May 19, 2015 04:27:56 PM Tony Lindgren wrote:
> > * Rafael J. Wysocki <rjw@rjwysocki.net> [150519 16:07]:
> > > On Wednesday, May 20, 2015 12:41:06 AM Thomas Gleixner wrote:
> > > > On Wed, 20 May 2015, Rafael J. Wysocki wrote:
> > > > > This one looks really good. :-)
> > > > > 
> > > > > If it doesn't depend on anything, I can apply it right away, so please let me
> > > > > know.
> > 
> > Sure works for me, it just has a dependency to patch #1 in this
> > series ("[PATCH 1/5] PM / Runtime: Update last_busy in rpm_resume").
> 
> OK, I'll queue them both up for 4.2, then.
> 
> Please let me know if you need a special branch with them.

A separate branch would be nice so I can merge that in too.

Thanks,

Tony 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/5] PM / Runtime: Update last_busy in rpm_resume
  2015-05-13 23:36 ` [PATCH 1/5] PM / Runtime: Update last_busy in rpm_resume Tony Lindgren
@ 2015-05-20  7:36   ` Ulf Hansson
  0 siblings, 0 replies; 43+ messages in thread
From: Ulf Hansson @ 2015-05-20  7:36 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Rafael J. Wysocki, Alan Stern, Andreas Fenkart,
	Greg Kroah-Hartman, Felipe Balbi, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Thomas Gleixner, linux-pm,
	linux-kernel, linux-serial, linux-omap

On 14 May 2015 at 01:36, Tony Lindgren <tony@atomide.com> wrote:
> If we don't update last_busy in rpm_resume, devices can go back
> to sleep immediately after resume. This happens at least in
> cases where the device has been powered off and does not have
> any interrupt pending until there's something in the FIFO.
>
> Signed-off-by: Tony Lindgren <tony@atomide.com>

If it's not too late,

Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-21  0:54                                         ` Rafael J. Wysocki
@ 2015-05-21  0:35                                           ` Tony Lindgren
  2015-05-21  1:40                                           ` Felipe Balbi
  1 sibling, 0 replies; 43+ messages in thread
From: Tony Lindgren @ 2015-05-21  0:35 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Thomas Gleixner, Felipe Balbi, Rafael J. Wysocki, Alan Stern,
	Andreas Fenkart, Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, linux-pm, linux-kernel,
	linux-serial, linux-omap

* Rafael J. Wysocki <rjw@rjwysocki.net> [150520 17:30]:
> On Tuesday, May 19, 2015 07:10:57 PM Tony Lindgren wrote:
> > * Rafael J. Wysocki <rjw@rjwysocki.net> [150519 17:01]:
> > > On Tuesday, May 19, 2015 04:27:56 PM Tony Lindgren wrote:
> > > > * Rafael J. Wysocki <rjw@rjwysocki.net> [150519 16:07]:
> > > > > On Wednesday, May 20, 2015 12:41:06 AM Thomas Gleixner wrote:
> > > > > > On Wed, 20 May 2015, Rafael J. Wysocki wrote:
> > > > > > > This one looks really good. :-)
> > > > > > > 
> > > > > > > If it doesn't depend on anything, I can apply it right away, so please let me
> > > > > > > know.
> > > > 
> > > > Sure works for me, it just has a dependency to patch #1 in this
> > > > series ("[PATCH 1/5] PM / Runtime: Update last_busy in rpm_resume").
> > > 
> > > OK, I'll queue them both up for 4.2, then.
> > > 
> > > Please let me know if you need a special branch with them.
> > 
> > A separate branch would be nice so I can merge that in too.
> 
> OK
> 
> I added a new pm-wakeirq branch to linux-pm.git with these two patches and
> one other commit related to runtime PM (all based on 4.1-rc4).  This branch
> will not be rebased going forward.

Great thanks!

Tony

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-20  2:10                                       ` Tony Lindgren
@ 2015-05-21  0:54                                         ` Rafael J. Wysocki
  2015-05-21  0:35                                           ` Tony Lindgren
  2015-05-21  1:40                                           ` Felipe Balbi
  0 siblings, 2 replies; 43+ messages in thread
From: Rafael J. Wysocki @ 2015-05-21  0:54 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Thomas Gleixner, Felipe Balbi, Rafael J. Wysocki, Alan Stern,
	Andreas Fenkart, Greg Kroah-Hartman, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, linux-pm, linux-kernel,
	linux-serial, linux-omap

On Tuesday, May 19, 2015 07:10:57 PM Tony Lindgren wrote:
> * Rafael J. Wysocki <rjw@rjwysocki.net> [150519 17:01]:
> > On Tuesday, May 19, 2015 04:27:56 PM Tony Lindgren wrote:
> > > * Rafael J. Wysocki <rjw@rjwysocki.net> [150519 16:07]:
> > > > On Wednesday, May 20, 2015 12:41:06 AM Thomas Gleixner wrote:
> > > > > On Wed, 20 May 2015, Rafael J. Wysocki wrote:
> > > > > > This one looks really good. :-)
> > > > > > 
> > > > > > If it doesn't depend on anything, I can apply it right away, so please let me
> > > > > > know.
> > > 
> > > Sure works for me, it just has a dependency to patch #1 in this
> > > series ("[PATCH 1/5] PM / Runtime: Update last_busy in rpm_resume").
> > 
> > OK, I'll queue them both up for 4.2, then.
> > 
> > Please let me know if you need a special branch with them.
> 
> A separate branch would be nice so I can merge that in too.

OK

I added a new pm-wakeirq branch to linux-pm.git with these two patches and
one other commit related to runtime PM (all based on 4.1-rc4).  This branch
will not be rebased going forward.


-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling
  2015-05-21  0:54                                         ` Rafael J. Wysocki
  2015-05-21  0:35                                           ` Tony Lindgren
@ 2015-05-21  1:40                                           ` Felipe Balbi
  1 sibling, 0 replies; 43+ messages in thread
From: Felipe Balbi @ 2015-05-21  1:40 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Tony Lindgren, Thomas Gleixner, Felipe Balbi, Rafael J. Wysocki,
	Alan Stern, Andreas Fenkart, Greg Kroah-Hartman, Huiquan Zhong,
	Kevin Hilman, NeilBrown, Mika Westerberg, Nishanth Menon,
	Peter Hurley, Sebastian Andrzej Siewior, Ulf Hansson, linux-pm,
	linux-kernel, linux-serial, linux-omap

[-- Attachment #1: Type: text/plain, Size: 1353 bytes --]

On Thu, May 21, 2015 at 02:54:14AM +0200, Rafael J. Wysocki wrote:
> On Tuesday, May 19, 2015 07:10:57 PM Tony Lindgren wrote:
> > * Rafael J. Wysocki <rjw@rjwysocki.net> [150519 17:01]:
> > > On Tuesday, May 19, 2015 04:27:56 PM Tony Lindgren wrote:
> > > > * Rafael J. Wysocki <rjw@rjwysocki.net> [150519 16:07]:
> > > > > On Wednesday, May 20, 2015 12:41:06 AM Thomas Gleixner wrote:
> > > > > > On Wed, 20 May 2015, Rafael J. Wysocki wrote:
> > > > > > > This one looks really good. :-)
> > > > > > > 
> > > > > > > If it doesn't depend on anything, I can apply it right away, so please let me
> > > > > > > know.
> > > > 
> > > > Sure works for me, it just has a dependency to patch #1 in this
> > > > series ("[PATCH 1/5] PM / Runtime: Update last_busy in rpm_resume").
> > > 
> > > OK, I'll queue them both up for 4.2, then.
> > > 
> > > Please let me know if you need a special branch with them.
> > 
> > A separate branch would be nice so I can merge that in too.
> 
> OK
> 
> I added a new pm-wakeirq branch to linux-pm.git with these two patches and
> one other commit related to runtime PM (all based on 4.1-rc4).  This branch
> will not be rebased going forward.

Hopefully it's not too late to get my

Reviewed-by: Felipe Balbi <balbi@ti.com>

in the patch. If it is, no problem :-)

cheers

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 5/5] mmc: omap_hsmmc: Change wake-up interrupt to use generic wakeirq
  2015-05-13 23:36 ` [PATCH 5/5] mmc: omap_hsmmc: Change wake-up interrupt to use " Tony Lindgren
@ 2015-05-25  8:38   ` Ulf Hansson
  2015-05-27 22:42     ` Rafael J. Wysocki
  0 siblings, 1 reply; 43+ messages in thread
From: Ulf Hansson @ 2015-05-25  8:38 UTC (permalink / raw)
  To: Tony Lindgren, Rafael J. Wysocki
  Cc: Alan Stern, Andreas Fenkart, Greg Kroah-Hartman, Felipe Balbi,
	Huiquan Zhong, Kevin Hilman, NeilBrown, Mika Westerberg,
	Nishanth Menon, Peter Hurley, Sebastian Andrzej Siewior,
	Thomas Gleixner, linux-pm, linux-kernel, linux-serial,
	linux-omap

On 14 May 2015 at 01:36, Tony Lindgren <tony@atomide.com> wrote:
> We can now use generic wakeirq handling and remove the custom handling
> for the wake-up interrupts.
>
> Signed-off-by: Tony Lindgren <tony@atomide.com>

Rafael, if you are fine with it, please take this one through your
linux-pm tree.

Acked-by: Ulf Hansson <ulf.hansson@linaro.org>

Kind regards
Uffe

> ---
>  drivers/mmc/host/omap_hsmmc.c | 51 +++++++------------------------------------
>  1 file changed, 8 insertions(+), 43 deletions(-)
>
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index 9df2b68..5fbf4d8 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -43,6 +43,7 @@
>  #include <linux/regulator/consumer.h>
>  #include <linux/pinctrl/consumer.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/pm_wakeirq.h>
>  #include <linux/platform_data/hsmmc-omap.h>
>
>  /* OMAP HSMMC Host Controller Registers */
> @@ -218,7 +219,6 @@ struct omap_hsmmc_host {
>         unsigned int            flags;
>  #define AUTO_CMD23             (1 << 0)        /* Auto CMD23 support */
>  #define HSMMC_SDIO_IRQ_ENABLED (1 << 1)        /* SDIO irq enabled */
> -#define HSMMC_WAKE_IRQ_ENABLED (1 << 2)
>         struct omap_hsmmc_next  next_data;
>         struct  omap_hsmmc_platform_data        *pdata;
>
> @@ -1117,22 +1117,6 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
>         return IRQ_HANDLED;
>  }
>
> -static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id)
> -{
> -       struct omap_hsmmc_host *host = dev_id;
> -
> -       /* cirq is level triggered, disable to avoid infinite loop */
> -       spin_lock(&host->irq_lock);
> -       if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
> -               disable_irq_nosync(host->wake_irq);
> -               host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
> -       }
> -       spin_unlock(&host->irq_lock);
> -       pm_request_resume(host->dev); /* no use counter */
> -
> -       return IRQ_HANDLED;
> -}
> -
>  static void set_sd_bus_power(struct omap_hsmmc_host *host)
>  {
>         unsigned long i;
> @@ -1665,7 +1649,6 @@ static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
>
>  static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
>  {
> -       struct mmc_host *mmc = host->mmc;
>         int ret;
>
>         /*
> @@ -1677,11 +1660,7 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
>         if (!host->dev->of_node || !host->wake_irq)
>                 return -ENODEV;
>
> -       /* Prevent auto-enabling of IRQ */
> -       irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
> -       ret = devm_request_irq(host->dev, host->wake_irq, omap_hsmmc_wake_irq,
> -                              IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> -                              mmc_hostname(mmc), host);
> +       ret = dev_pm_request_wake_irq(host->dev, host->wake_irq, NULL, 0, NULL);
>         if (ret) {
>                 dev_err(mmc_dev(host->mmc), "Unable to request wake IRQ\n");
>                 goto err;
> @@ -1718,7 +1697,7 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
>         return 0;
>
>  err_free_irq:
> -       devm_free_irq(host->dev, host->wake_irq, host);
> +       dev_pm_free_wake_irq(host->dev);
>  err:
>         dev_warn(host->dev, "no SDIO IRQ support, falling back to polling\n");
>         host->wake_irq = 0;
> @@ -2007,6 +1986,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>                 omap_hsmmc_ops.multi_io_quirk = omap_hsmmc_multi_io_quirk;
>         }
>
> +       device_init_wakeup(&pdev->dev, true);
>         pm_runtime_enable(host->dev);
>         pm_runtime_get_sync(host->dev);
>         pm_runtime_set_autosuspend_delay(host->dev, MMC_AUTOSUSPEND_DELAY);
> @@ -2147,6 +2127,7 @@ err_slot_name:
>         if (host->use_reg)
>                 omap_hsmmc_reg_put(host);
>  err_irq:
> +       device_init_wakeup(&pdev->dev, false);
>         if (host->tx_chan)
>                 dma_release_channel(host->tx_chan);
>         if (host->rx_chan)
> @@ -2178,6 +2159,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
>
>         pm_runtime_put_sync(host->dev);
>         pm_runtime_disable(host->dev);
> +       device_init_wakeup(&pdev->dev, false);
>         if (host->dbclk)
>                 clk_disable_unprepare(host->dbclk);
>
> @@ -2204,11 +2186,6 @@ static int omap_hsmmc_suspend(struct device *dev)
>                                 OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
>         }
>
> -       /* do not wake up due to sdio irq */
> -       if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
> -           !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
> -               disable_irq(host->wake_irq);
> -
>         if (host->dbclk)
>                 clk_disable_unprepare(host->dbclk);
>
> @@ -2233,11 +2210,6 @@ static int omap_hsmmc_resume(struct device *dev)
>                 omap_hsmmc_conf_bus_power(host);
>
>         omap_hsmmc_protect_card(host);
> -
> -       if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
> -           !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
> -               enable_irq(host->wake_irq);
> -
>         pm_runtime_mark_last_busy(host->dev);
>         pm_runtime_put_autosuspend(host->dev);
>         return 0;
> @@ -2277,10 +2249,7 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
>                 }
>
>                 pinctrl_pm_select_idle_state(dev);
> -
> -               WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED);
> -               enable_irq(host->wake_irq);
> -               host->flags |= HSMMC_WAKE_IRQ_ENABLED;
> +               dev_pm_enable_wake_irq(host->dev);
>         } else {
>                 pinctrl_pm_select_idle_state(dev);
>         }
> @@ -2302,11 +2271,7 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
>         spin_lock_irqsave(&host->irq_lock, flags);
>         if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
>             (host->flags & HSMMC_SDIO_IRQ_ENABLED)) {
> -               /* sdio irq flag can't change while in runtime suspend */
> -               if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
> -                       disable_irq_nosync(host->wake_irq);
> -                       host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
> -               }
> +               dev_pm_disable_wake_irq(host->dev);
>
>                 pinctrl_pm_select_default_state(host->dev);
>
> --
> 2.1.4
>

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 5/5] mmc: omap_hsmmc: Change wake-up interrupt to use generic wakeirq
  2015-05-25  8:38   ` Ulf Hansson
@ 2015-05-27 22:42     ` Rafael J. Wysocki
  2015-05-27 22:45       ` Tony Lindgren
  0 siblings, 1 reply; 43+ messages in thread
From: Rafael J. Wysocki @ 2015-05-27 22:42 UTC (permalink / raw)
  To: Ulf Hansson, Tony Lindgren
  Cc: Alan Stern, Andreas Fenkart, Greg Kroah-Hartman, Felipe Balbi,
	Huiquan Zhong, Kevin Hilman, NeilBrown, Mika Westerberg,
	Nishanth Menon, Peter Hurley, Sebastian Andrzej Siewior,
	Thomas Gleixner, linux-pm, linux-kernel, linux-serial,
	linux-omap

On 5/25/2015 10:38 AM, Ulf Hansson wrote:
> On 14 May 2015 at 01:36, Tony Lindgren <tony@atomide.com> wrote:
>> We can now use generic wakeirq handling and remove the custom handling
>> for the wake-up interrupts.
>>
>> Signed-off-by: Tony Lindgren <tony@atomide.com>
> Rafael, if you are fine with it, please take this one through your
> linux-pm tree.
>
> Acked-by: Ulf Hansson <ulf.hansson@linaro.org>

I thought that Tony would resubmit this one.  Tony?


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 5/5] mmc: omap_hsmmc: Change wake-up interrupt to use generic wakeirq
  2015-05-27 22:42     ` Rafael J. Wysocki
@ 2015-05-27 22:45       ` Tony Lindgren
  2015-05-28 14:36         ` Tony Lindgren
  0 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-27 22:45 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Ulf Hansson, Alan Stern, Andreas Fenkart, Greg Kroah-Hartman,
	Felipe Balbi, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Thomas Gleixner, linux-pm,
	linux-kernel, linux-serial, linux-omap

* Rafael J. Wysocki <rafael.j.wysocki@intel.com> [150527 15:44]:
> On 5/25/2015 10:38 AM, Ulf Hansson wrote:
> >On 14 May 2015 at 01:36, Tony Lindgren <tony@atomide.com> wrote:
> >>We can now use generic wakeirq handling and remove the custom handling
> >>for the wake-up interrupts.
> >>
> >>Signed-off-by: Tony Lindgren <tony@atomide.com>
> >Rafael, if you are fine with it, please take this one through your
> >linux-pm tree.
> >
> >Acked-by: Ulf Hansson <ulf.hansson@linaro.org>
> 
> I thought that Tony would resubmit this one.  Tony?

OK I can queue it with Ulf's ack via arm-soc no problem.

Regards,

Tony 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 5/5] mmc: omap_hsmmc: Change wake-up interrupt to use generic wakeirq
  2015-05-27 22:45       ` Tony Lindgren
@ 2015-05-28 14:36         ` Tony Lindgren
  0 siblings, 0 replies; 43+ messages in thread
From: Tony Lindgren @ 2015-05-28 14:36 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Ulf Hansson, Alan Stern, Andreas Fenkart, Greg Kroah-Hartman,
	Felipe Balbi, Huiquan Zhong, Kevin Hilman, NeilBrown,
	Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Thomas Gleixner, linux-pm,
	linux-kernel, linux-serial, linux-omap

* Tony Lindgren <tony@atomide.com> [150527 15:46]:
> * Rafael J. Wysocki <rafael.j.wysocki@intel.com> [150527 15:44]:
> > On 5/25/2015 10:38 AM, Ulf Hansson wrote:
> > >On 14 May 2015 at 01:36, Tony Lindgren <tony@atomide.com> wrote:
> > >>We can now use generic wakeirq handling and remove the custom handling
> > >>for the wake-up interrupts.
> > >>
> > >>Signed-off-by: Tony Lindgren <tony@atomide.com>
> > >Rafael, if you are fine with it, please take this one through your
> > >linux-pm tree.
> > >
> > >Acked-by: Ulf Hansson <ulf.hansson@linaro.org>
> > 
> > I thought that Tony would resubmit this one.  Tony?
> 
> OK I can queue it with Ulf's ack via arm-soc no problem.

I've applied this one now into omap-for-v4.2/wakeirq branch that's
based on Rafael's pm-wakeirq branch.

Regards,

Tony

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 3/5] serial: omap: Switch wake-up interrupt to generic wakeirq
  2015-05-13 23:36 ` [PATCH 3/5] serial: omap: Switch wake-up interrupt to generic wakeirq Tony Lindgren
@ 2015-05-28 14:56   ` Tony Lindgren
  2015-05-31  7:16     ` Greg Kroah-Hartman
  0 siblings, 1 reply; 43+ messages in thread
From: Tony Lindgren @ 2015-05-28 14:56 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Andreas Fenkart, Felipe Balbi, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap,
	Rafael J. Wysocki, Alan Stern

Hi Greg,

* Tony Lindgren <tony@atomide.com> [150513 16:38]:
> We can now use generic wakeirq handling and remove the custom handling
> for the wake-up interrupts.

How do you prefer to handle the two omap specific serial driver
changes in this series?

There's a dependency to Rafael's pm-wakeirq branch that contains:

4990d4fe327b ("PM / Wakeirq: Add automated device wake IRQ handling")
56f487c78015 ("PM / Runtime: Update last_busy in rpm_resume")
f6a2fbb903d5 ("PM / runtime: add note about re-calling in during
device probe()")

The 8250_omap.c driver change also needs to be merged with:

9e91597f2423 ("serial: 8250_omap: provide complete custom startup &
shutdown callbacks")

Not sure if 9e91597f2423 is immutable, but if so then I could:

a) Send you a pull request for the two serial driver patches
   based on Rafael's pm-wakeirq branch merged with 9e91597f2423 to
   avoid the merge conflict.

b) Apply both serial driver patches into omap-for-v4.2/wakeirq
   based on Rafael's pm-wakeirq branch merged with 9e91597f2423.

Or do you have some better ideas?

Regards,

Tony

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 3/5] serial: omap: Switch wake-up interrupt to generic wakeirq
  2015-05-28 14:56   ` Tony Lindgren
@ 2015-05-31  7:16     ` Greg Kroah-Hartman
  2015-06-01 22:05       ` Tony Lindgren
  0 siblings, 1 reply; 43+ messages in thread
From: Greg Kroah-Hartman @ 2015-05-31  7:16 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Andreas Fenkart, Felipe Balbi, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap,
	Rafael J. Wysocki, Alan Stern

On Thu, May 28, 2015 at 07:56:16AM -0700, Tony Lindgren wrote:
> Hi Greg,
> 
> * Tony Lindgren <tony@atomide.com> [150513 16:38]:
> > We can now use generic wakeirq handling and remove the custom handling
> > for the wake-up interrupts.
> 
> How do you prefer to handle the two omap specific serial driver
> changes in this series?
> 
> There's a dependency to Rafael's pm-wakeirq branch that contains:
> 
> 4990d4fe327b ("PM / Wakeirq: Add automated device wake IRQ handling")
> 56f487c78015 ("PM / Runtime: Update last_busy in rpm_resume")
> f6a2fbb903d5 ("PM / runtime: add note about re-calling in during
> device probe()")
> 
> The 8250_omap.c driver change also needs to be merged with:
> 
> 9e91597f2423 ("serial: 8250_omap: provide complete custom startup &
> shutdown callbacks")
> 
> Not sure if 9e91597f2423 is immutable, but if so then I could:
> 
> a) Send you a pull request for the two serial driver patches
>    based on Rafael's pm-wakeirq branch merged with 9e91597f2423 to
>    avoid the merge conflict.
> 
> b) Apply both serial driver patches into omap-for-v4.2/wakeirq
>    based on Rafael's pm-wakeirq branch merged with 9e91597f2423.

That one sounds good to me, feel free to do so and add:

Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

It's the easiest for everyone involved, right?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 3/5] serial: omap: Switch wake-up interrupt to generic wakeirq
  2015-05-31  7:16     ` Greg Kroah-Hartman
@ 2015-06-01 22:05       ` Tony Lindgren
  0 siblings, 0 replies; 43+ messages in thread
From: Tony Lindgren @ 2015-06-01 22:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Andreas Fenkart, Felipe Balbi, Huiquan Zhong, Kevin Hilman,
	NeilBrown, Mika Westerberg, Nishanth Menon, Peter Hurley,
	Sebastian Andrzej Siewior, Ulf Hansson, Thomas Gleixner,
	linux-pm, linux-kernel, linux-serial, linux-omap,
	Rafael J. Wysocki, Alan Stern

* Greg Kroah-Hartman <gregkh@linuxfoundation.org> [150531 00:24]:
> On Thu, May 28, 2015 at 07:56:16AM -0700, Tony Lindgren wrote:
> > Hi Greg,
> > 
> > * Tony Lindgren <tony@atomide.com> [150513 16:38]:
> > > We can now use generic wakeirq handling and remove the custom handling
> > > for the wake-up interrupts.
> > 
> > How do you prefer to handle the two omap specific serial driver
> > changes in this series?
> > 
> > There's a dependency to Rafael's pm-wakeirq branch that contains:
> > 
> > 4990d4fe327b ("PM / Wakeirq: Add automated device wake IRQ handling")
> > 56f487c78015 ("PM / Runtime: Update last_busy in rpm_resume")
> > f6a2fbb903d5 ("PM / runtime: add note about re-calling in during
> > device probe()")
> > 
> > The 8250_omap.c driver change also needs to be merged with:
> > 
> > 9e91597f2423 ("serial: 8250_omap: provide complete custom startup &
> > shutdown callbacks")
> > 
> > Not sure if 9e91597f2423 is immutable, but if so then I could:
> > 
> > a) Send you a pull request for the two serial driver patches
> >    based on Rafael's pm-wakeirq branch merged with 9e91597f2423 to
> >    avoid the merge conflict.
> > 
> > b) Apply both serial driver patches into omap-for-v4.2/wakeirq
> >    based on Rafael's pm-wakeirq branch merged with 9e91597f2423.
> 
> That one sounds good to me, feel free to do so and add:
> 
> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> 
> It's the easiest for everyone involved, right?

OK thanks, looks like there's a new commit for the fix as
9809889c708e, I'll base on that and wait that the commit gets
merged before sending out any pull request.

Regards,

Tony

^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2015-06-01 22:06 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-05-13 23:36 [PATCHv3 0/5] Linux generic wakeirq handling Tony Lindgren
2015-05-13 23:36 ` [PATCH 1/5] PM / Runtime: Update last_busy in rpm_resume Tony Lindgren
2015-05-20  7:36   ` Ulf Hansson
2015-05-13 23:36 ` [PATCH 2/5] PM / Wakeirq: Add automated device wake IRQ handling Tony Lindgren
2015-05-14  2:06   ` Felipe Balbi
2015-05-14 15:51     ` Alan Stern
2015-05-14 15:54       ` Felipe Balbi
2015-05-14 15:59     ` Tony Lindgren
2015-05-14 16:09       ` Felipe Balbi
2015-05-14 16:28         ` Tony Lindgren
2015-05-14 17:51           ` Tony Lindgren
2015-05-14 21:15       ` Tony Lindgren
2015-05-14 21:25         ` Felipe Balbi
2015-05-14 22:00         ` Rafael J. Wysocki
2015-05-14 21:59           ` Tony Lindgren
2015-05-15 22:25             ` Tony Lindgren
2015-05-16  1:56               ` Felipe Balbi
2015-05-18 22:05                 ` Tony Lindgren
2015-05-18 23:44                   ` Tony Lindgren
2015-05-19 14:04                     ` Rafael J. Wysocki
2015-05-19 14:26                       ` Rafael J. Wysocki
2015-05-19 15:09                         ` Tony Lindgren
2015-05-19 18:18                           ` Tony Lindgren
2015-05-19 23:01                             ` Rafael J. Wysocki
2015-05-19 22:41                               ` Thomas Gleixner
2015-05-19 23:31                                 ` Rafael J. Wysocki
2015-05-19 23:27                                   ` Tony Lindgren
2015-05-20  0:25                                     ` Rafael J. Wysocki
2015-05-20  2:10                                       ` Tony Lindgren
2015-05-21  0:54                                         ` Rafael J. Wysocki
2015-05-21  0:35                                           ` Tony Lindgren
2015-05-21  1:40                                           ` Felipe Balbi
2015-05-19 15:15                       ` Tony Lindgren
2015-05-13 23:36 ` [PATCH 3/5] serial: omap: Switch wake-up interrupt to generic wakeirq Tony Lindgren
2015-05-28 14:56   ` Tony Lindgren
2015-05-31  7:16     ` Greg Kroah-Hartman
2015-06-01 22:05       ` Tony Lindgren
2015-05-13 23:36 ` [PATCH 4/5] serial: 8250_omap: Move " Tony Lindgren
2015-05-13 23:36 ` [PATCH 5/5] mmc: omap_hsmmc: Change wake-up interrupt to use " Tony Lindgren
2015-05-25  8:38   ` Ulf Hansson
2015-05-27 22:42     ` Rafael J. Wysocki
2015-05-27 22:45       ` Tony Lindgren
2015-05-28 14:36         ` Tony Lindgren

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).