Skip to content

Commit 4990d4f

Browse files
tmlindrafaeljw
authored andcommitted
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 <[email protected]>. 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 <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 56f487c commit 4990d4f

File tree

9 files changed

+485
-1
lines changed

9 files changed

+485
-1
lines changed

drivers/base/power/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o
1+
obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o
22
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
33
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
44
obj-$(CONFIG_PM_OPP) += opp.o

drivers/base/power/main.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/pm.h>
2525
#include <linux/pm_runtime.h>
2626
#include <linux/pm-trace.h>
27+
#include <linux/pm_wakeirq.h>
2728
#include <linux/interrupt.h>
2829
#include <linux/sched.h>
2930
#include <linux/async.h>
@@ -587,6 +588,7 @@ void dpm_resume_noirq(pm_message_t state)
587588
async_synchronize_full();
588589
dpm_show_time(starttime, state, "noirq");
589590
resume_device_irqs();
591+
device_wakeup_disarm_wake_irqs();
590592
cpuidle_resume();
591593
trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
592594
}
@@ -1104,6 +1106,7 @@ int dpm_suspend_noirq(pm_message_t state)
11041106

11051107
trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
11061108
cpuidle_pause();
1109+
device_wakeup_arm_wake_irqs();
11071110
suspend_device_irqs();
11081111
mutex_lock(&dpm_list_mtx);
11091112
pm_transition = state;

drivers/base/power/power.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,46 @@ static inline void pm_runtime_early_init(struct device *dev)
2020
extern void pm_runtime_init(struct device *dev);
2121
extern void pm_runtime_remove(struct device *dev);
2222

23+
struct wake_irq {
24+
struct device *dev;
25+
int irq;
26+
bool dedicated_irq:1;
27+
};
28+
29+
extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
30+
extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
31+
32+
#ifdef CONFIG_PM_SLEEP
33+
34+
extern int device_wakeup_attach_irq(struct device *dev,
35+
struct wake_irq *wakeirq);
36+
extern void device_wakeup_detach_irq(struct device *dev);
37+
extern void device_wakeup_arm_wake_irqs(void);
38+
extern void device_wakeup_disarm_wake_irqs(void);
39+
40+
#else
41+
42+
static inline int
43+
device_wakeup_attach_irq(struct device *dev,
44+
struct wake_irq *wakeirq)
45+
{
46+
return 0;
47+
}
48+
49+
static inline void device_wakeup_detach_irq(struct device *dev)
50+
{
51+
}
52+
53+
static inline void device_wakeup_arm_wake_irqs(void)
54+
{
55+
}
56+
57+
static inline void device_wakeup_disarm_wake_irqs(void)
58+
{
59+
}
60+
61+
#endif /* CONFIG_PM_SLEEP */
62+
2363
/*
2464
* sysfs.c
2565
*/
@@ -52,6 +92,14 @@ static inline void wakeup_sysfs_remove(struct device *dev) {}
5292
static inline int pm_qos_sysfs_add(struct device *dev) { return 0; }
5393
static inline void pm_qos_sysfs_remove(struct device *dev) {}
5494

95+
static inline void dev_pm_arm_wake_irq(struct wake_irq *wirq)
96+
{
97+
}
98+
99+
static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
100+
{
101+
}
102+
55103
#endif
56104

57105
#ifdef CONFIG_PM_SLEEP

drivers/base/power/runtime.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/sched.h>
1111
#include <linux/export.h>
1212
#include <linux/pm_runtime.h>
13+
#include <linux/pm_wakeirq.h>
1314
#include <trace/events/rpm.h>
1415
#include "power.h"
1516

@@ -514,6 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
514515

515516
callback = RPM_GET_CALLBACK(dev, runtime_suspend);
516517

518+
dev_pm_enable_wake_irq(dev);
517519
retval = rpm_callback(callback, dev);
518520
if (retval)
519521
goto fail;
@@ -552,6 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
552554
return retval;
553555

554556
fail:
557+
dev_pm_disable_wake_irq(dev);
555558
__update_runtime_status(dev, RPM_ACTIVE);
556559
dev->power.deferred_resume = false;
557560
wake_up_all(&dev->power.wait_queue);
@@ -734,10 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags)
734737

735738
callback = RPM_GET_CALLBACK(dev, runtime_resume);
736739

740+
dev_pm_disable_wake_irq(dev);
737741
retval = rpm_callback(callback, dev);
738742
if (retval) {
739743
__update_runtime_status(dev, RPM_SUSPENDED);
740744
pm_runtime_cancel_pending(dev);
745+
dev_pm_enable_wake_irq(dev);
741746
} else {
742747
no_callback:
743748
__update_runtime_status(dev, RPM_ACTIVE);

0 commit comments

Comments
 (0)