aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2014-08-29 08:00:16 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-09-01 07:48:59 -0400
commit9ce7a25849e80cfb264f4995f832b932c1987e1a (patch)
treec7f7301049b93dda90c16b1e8b09e29cdb0bc189
parentb76f16748fa61801b1a1fd3ffb6f25ee228a35e0 (diff)
genirq: Simplify wakeup mechanism
Currently we suspend wakeup interrupts by lazy disabling them and check later whether the interrupt has fired, but that's not sufficient for suspend to idle as there is no way to check that once we transitioned into the CPU idle state. So we change the mechanism in the following way: 1) Leave the wakeup interrupts enabled across suspend 2) Add a check to irq_may_run() which is called at the beginning of each flow handler whether the interrupt is an armed wakeup source. This check is basically free as it just extends the existing check for IRQD_IRQ_INPROGRESS. So no new conditional in the hot path. If the IRQD_WAKEUP_ARMED flag is set, then the interrupt is disabled, marked as pending/suspended and the pm core is notified about the wakeup event. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> [ rjw: syscore.c and put irq_pm_check_wakeup() into pm.c ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/base/syscore.c7
-rw-r--r--include/linux/interrupt.h5
-rw-r--r--kernel/irq/chip.c20
-rw-r--r--kernel/irq/internals.h2
-rw-r--r--kernel/irq/pm.c55
5 files changed, 53 insertions, 36 deletions
diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c
index dbb8350ea8dc..8d98a329f6ea 100644
--- a/drivers/base/syscore.c
+++ b/drivers/base/syscore.c
@@ -9,7 +9,7 @@
9#include <linux/syscore_ops.h> 9#include <linux/syscore_ops.h>
10#include <linux/mutex.h> 10#include <linux/mutex.h>
11#include <linux/module.h> 11#include <linux/module.h>
12#include <linux/interrupt.h> 12#include <linux/suspend.h>
13#include <trace/events/power.h> 13#include <trace/events/power.h>
14 14
15static LIST_HEAD(syscore_ops_list); 15static LIST_HEAD(syscore_ops_list);
@@ -54,9 +54,8 @@ int syscore_suspend(void)
54 pr_debug("Checking wakeup interrupts\n"); 54 pr_debug("Checking wakeup interrupts\n");
55 55
56 /* Return error code if there are any wakeup interrupts pending. */ 56 /* Return error code if there are any wakeup interrupts pending. */
57 ret = check_wakeup_irqs(); 57 if (pm_wakeup_pending())
58 if (ret) 58 return -EBUSY;
59 return ret;
60 59
61 WARN_ONCE(!irqs_disabled(), 60 WARN_ONCE(!irqs_disabled(),
62 "Interrupts enabled before system core suspend.\n"); 61 "Interrupts enabled before system core suspend.\n");
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 698ad053d064..69517a24bc50 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -193,11 +193,6 @@ extern void irq_wake_thread(unsigned int irq, void *dev_id);
193/* The following three functions are for the core kernel use only. */ 193/* The following three functions are for the core kernel use only. */
194extern void suspend_device_irqs(void); 194extern void suspend_device_irqs(void);
195extern void resume_device_irqs(void); 195extern void resume_device_irqs(void);
196#ifdef CONFIG_PM_SLEEP
197extern int check_wakeup_irqs(void);
198#else
199static inline int check_wakeup_irqs(void) { return 0; }
200#endif
201 196
202/** 197/**
203 * struct irq_affinity_notify - context for notification of IRQ affinity changes 198 * struct irq_affinity_notify - context for notification of IRQ affinity changes
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 6baf86085571..e7917ff8a486 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -344,8 +344,26 @@ static bool irq_check_poll(struct irq_desc *desc)
344 344
345static bool irq_may_run(struct irq_desc *desc) 345static bool irq_may_run(struct irq_desc *desc)
346{ 346{
347 if (!irqd_irq_inprogress(&desc->irq_data)) 347 unsigned int mask = IRQD_IRQ_INPROGRESS | IRQD_WAKEUP_ARMED;
348
349 /*
350 * If the interrupt is not in progress and is not an armed
351 * wakeup interrupt, proceed.
352 */
353 if (!irqd_has_set(&desc->irq_data, mask))
348 return true; 354 return true;
355
356 /*
357 * If the interrupt is an armed wakeup source, mark it pending
358 * and suspended, disable it and notify the pm core about the
359 * event.
360 */
361 if (irq_pm_check_wakeup(desc))
362 return false;
363
364 /*
365 * Handle a potential concurrent poll on a different core.
366 */
349 return irq_check_poll(desc); 367 return irq_check_poll(desc);
350} 368}
351 369
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index c402502a5111..4332d766619d 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -196,9 +196,11 @@ static inline void kstat_incr_irqs_this_cpu(unsigned int irq, struct irq_desc *d
196} 196}
197 197
198#ifdef CONFIG_PM_SLEEP 198#ifdef CONFIG_PM_SLEEP
199bool irq_pm_check_wakeup(struct irq_desc *desc);
199void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action); 200void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action);
200void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action); 201void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action);
201#else 202#else
203static inline bool irq_pm_check_wakeup(struct irq_desc *desc) { return false; }
202static inline void 204static inline void
203irq_pm_install_action(struct irq_desc *desc, struct irqaction *action) { } 205irq_pm_install_action(struct irq_desc *desc, struct irqaction *action) { }
204static inline void 206static inline void
diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c
index 766930eaeed9..3ca532592704 100644
--- a/kernel/irq/pm.c
+++ b/kernel/irq/pm.c
@@ -9,10 +9,24 @@
9#include <linux/irq.h> 9#include <linux/irq.h>
10#include <linux/module.h> 10#include <linux/module.h>
11#include <linux/interrupt.h> 11#include <linux/interrupt.h>
12#include <linux/suspend.h>
12#include <linux/syscore_ops.h> 13#include <linux/syscore_ops.h>
13 14
14#include "internals.h" 15#include "internals.h"
15 16
17bool irq_pm_check_wakeup(struct irq_desc *desc)
18{
19 if (irqd_is_wakeup_armed(&desc->irq_data)) {
20 irqd_clear(&desc->irq_data, IRQD_WAKEUP_ARMED);
21 desc->istate |= IRQS_SUSPENDED | IRQS_PENDING;
22 desc->depth++;
23 irq_disable(desc);
24 pm_system_wakeup();
25 return true;
26 }
27 return false;
28}
29
16/* 30/*
17 * Called from __setup_irq() with desc->lock held after @action has 31 * Called from __setup_irq() with desc->lock held after @action has
18 * been installed in the action chain. 32 * been installed in the action chain.
@@ -54,8 +68,16 @@ static bool suspend_device_irq(struct irq_desc *desc, int irq)
54 if (!desc->action || desc->no_suspend_depth) 68 if (!desc->action || desc->no_suspend_depth)
55 return false; 69 return false;
56 70
57 if (irqd_is_wakeup_set(&desc->irq_data)) 71 if (irqd_is_wakeup_set(&desc->irq_data)) {
58 irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED); 72 irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
73 /*
74 * We return true here to force the caller to issue
75 * synchronize_irq(). We need to make sure that the
76 * IRQD_WAKEUP_ARMED is visible before we return from
77 * suspend_device_irqs().
78 */
79 return true;
80 }
59 81
60 desc->istate |= IRQS_SUSPENDED; 82 desc->istate |= IRQS_SUSPENDED;
61 __disable_irq(desc, irq); 83 __disable_irq(desc, irq);
@@ -79,9 +101,13 @@ static bool suspend_device_irq(struct irq_desc *desc, int irq)
79 * for this purpose. 101 * for this purpose.
80 * 102 *
81 * So we disable all interrupts and mark them IRQS_SUSPENDED except 103 * So we disable all interrupts and mark them IRQS_SUSPENDED except
82 * for those which are unused and those which are marked as not 104 * for those which are unused, those which are marked as not
83 * suspendable via an interrupt request with the flag IRQF_NO_SUSPEND 105 * suspendable via an interrupt request with the flag IRQF_NO_SUSPEND
84 * set. 106 * set and those which are marked as active wakeup sources.
107 *
108 * The active wakeup sources are handled by the flow handler entry
109 * code which checks for the IRQD_WAKEUP_ARMED flag, suspends the
110 * interrupt and notifies the pm core about the wakeup.
85 */ 111 */
86void suspend_device_irqs(void) 112void suspend_device_irqs(void)
87{ 113{
@@ -173,26 +199,3 @@ void resume_device_irqs(void)
173 resume_irqs(false); 199 resume_irqs(false);
174} 200}
175EXPORT_SYMBOL_GPL(resume_device_irqs); 201EXPORT_SYMBOL_GPL(resume_device_irqs);
176
177/**
178 * check_wakeup_irqs - check if any wake-up interrupts are pending
179 */
180int check_wakeup_irqs(void)
181{
182 struct irq_desc *desc;
183 int irq;
184
185 for_each_irq_desc(irq, desc) {
186 /*
187 * Only interrupts which are marked as wakeup source
188 * and have not been disabled before the suspend check
189 * can abort suspend.
190 */
191 if (irqd_is_wakeup_set(&desc->irq_data)) {
192 if (desc->depth == 1 && desc->istate & IRQS_PENDING)
193 return -EBUSY;
194 }
195 }
196
197 return 0;
198}