Skip to content

Commit

Permalink
PM / Wakeirq: Add automated device wake IRQ handling
Browse files Browse the repository at this point in the history
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>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  • Loading branch information
Tony Lindgren authored and Rafael J. Wysocki committed May 19, 2015
1 parent 56f487c commit 4990d4f
Show file tree
Hide file tree
Showing 9 changed files with 485 additions and 1 deletion.
2 changes: 1 addition & 1 deletion drivers/base/power/Makefile
Original file line number Diff line number Diff line change
@@ -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
Expand Down
3 changes: 3 additions & 0 deletions drivers/base/power/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down
48 changes: 48 additions & 0 deletions drivers/base/power/power.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions drivers/base/power/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 4990d4f

Please sign in to comment.