diff options
-rw-r--r-- | include/linux/interrupt.h | 9 | ||||
-rw-r--r-- | include/linux/irq.h | 1 | ||||
-rw-r--r-- | kernel/irq/Makefile | 1 | ||||
-rw-r--r-- | kernel/irq/internals.h | 2 | ||||
-rw-r--r-- | kernel/irq/manage.c | 31 | ||||
-rw-r--r-- | kernel/irq/pm.c | 79 |
6 files changed, 116 insertions, 7 deletions
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 0c9cb63e6895..c68bffd182bb 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h | |||
@@ -117,6 +117,15 @@ extern void disable_irq_nosync(unsigned int irq); | |||
117 | extern void disable_irq(unsigned int irq); | 117 | extern void disable_irq(unsigned int irq); |
118 | extern void enable_irq(unsigned int irq); | 118 | extern void enable_irq(unsigned int irq); |
119 | 119 | ||
120 | /* The following three functions are for the core kernel use only. */ | ||
121 | extern void suspend_device_irqs(void); | ||
122 | extern void resume_device_irqs(void); | ||
123 | #ifdef CONFIG_PM_SLEEP | ||
124 | extern int check_wakeup_irqs(void); | ||
125 | #else | ||
126 | static inline int check_wakeup_irqs(void) { return 0; } | ||
127 | #endif | ||
128 | |||
120 | #if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS) | 129 | #if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS) |
121 | 130 | ||
122 | extern cpumask_var_t irq_default_affinity; | 131 | extern cpumask_var_t irq_default_affinity; |
diff --git a/include/linux/irq.h b/include/linux/irq.h index 9c62fbe2ef30..974890b3c52f 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h | |||
@@ -67,6 +67,7 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, | |||
67 | #define IRQ_SPURIOUS_DISABLED 0x00800000 /* IRQ was disabled by the spurious trap */ | 67 | #define IRQ_SPURIOUS_DISABLED 0x00800000 /* IRQ was disabled by the spurious trap */ |
68 | #define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ | 68 | #define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ |
69 | #define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/ | 69 | #define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/ |
70 | #define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */ | ||
70 | 71 | ||
71 | #ifdef CONFIG_IRQ_PER_CPU | 72 | #ifdef CONFIG_IRQ_PER_CPU |
72 | # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) | 73 | # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) |
diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile index 4dd5b1edac98..3394f8f52964 100644 --- a/kernel/irq/Makefile +++ b/kernel/irq/Makefile | |||
@@ -4,3 +4,4 @@ obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o | |||
4 | obj-$(CONFIG_PROC_FS) += proc.o | 4 | obj-$(CONFIG_PROC_FS) += proc.o |
5 | obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o | 5 | obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o |
6 | obj-$(CONFIG_NUMA_MIGRATE_IRQ_DESC) += numa_migrate.o | 6 | obj-$(CONFIG_NUMA_MIGRATE_IRQ_DESC) += numa_migrate.o |
7 | obj-$(CONFIG_PM_SLEEP) += pm.o | ||
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index ee1aa9f8e8b9..01ce20eab38f 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h | |||
@@ -12,6 +12,8 @@ extern void compat_irq_chip_set_default_handler(struct irq_desc *desc); | |||
12 | 12 | ||
13 | extern int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, | 13 | extern int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, |
14 | unsigned long flags); | 14 | unsigned long flags); |
15 | extern void __disable_irq(struct irq_desc *desc, unsigned int irq, bool susp); | ||
16 | extern void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume); | ||
15 | 17 | ||
16 | extern struct lock_class_key irq_desc_lock_class; | 18 | extern struct lock_class_key irq_desc_lock_class; |
17 | extern void init_kstat_irqs(struct irq_desc *desc, int cpu, int nr); | 19 | extern void init_kstat_irqs(struct irq_desc *desc, int cpu, int nr); |
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 6458e99984c0..1516ab77355c 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
@@ -162,6 +162,20 @@ static inline int setup_affinity(unsigned int irq, struct irq_desc *desc) | |||
162 | } | 162 | } |
163 | #endif | 163 | #endif |
164 | 164 | ||
165 | void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend) | ||
166 | { | ||
167 | if (suspend) { | ||
168 | if (!desc->action || (desc->action->flags & IRQF_TIMER)) | ||
169 | return; | ||
170 | desc->status |= IRQ_SUSPENDED; | ||
171 | } | ||
172 | |||
173 | if (!desc->depth++) { | ||
174 | desc->status |= IRQ_DISABLED; | ||
175 | desc->chip->disable(irq); | ||
176 | } | ||
177 | } | ||
178 | |||
165 | /** | 179 | /** |
166 | * disable_irq_nosync - disable an irq without waiting | 180 | * disable_irq_nosync - disable an irq without waiting |
167 | * @irq: Interrupt to disable | 181 | * @irq: Interrupt to disable |
@@ -182,10 +196,7 @@ void disable_irq_nosync(unsigned int irq) | |||
182 | return; | 196 | return; |
183 | 197 | ||
184 | spin_lock_irqsave(&desc->lock, flags); | 198 | spin_lock_irqsave(&desc->lock, flags); |
185 | if (!desc->depth++) { | 199 | __disable_irq(desc, irq, false); |
186 | desc->status |= IRQ_DISABLED; | ||
187 | desc->chip->disable(irq); | ||
188 | } | ||
189 | spin_unlock_irqrestore(&desc->lock, flags); | 200 | spin_unlock_irqrestore(&desc->lock, flags); |
190 | } | 201 | } |
191 | EXPORT_SYMBOL(disable_irq_nosync); | 202 | EXPORT_SYMBOL(disable_irq_nosync); |
@@ -215,15 +226,21 @@ void disable_irq(unsigned int irq) | |||
215 | } | 226 | } |
216 | EXPORT_SYMBOL(disable_irq); | 227 | EXPORT_SYMBOL(disable_irq); |
217 | 228 | ||
218 | static void __enable_irq(struct irq_desc *desc, unsigned int irq) | 229 | void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume) |
219 | { | 230 | { |
231 | if (resume) | ||
232 | desc->status &= ~IRQ_SUSPENDED; | ||
233 | |||
220 | switch (desc->depth) { | 234 | switch (desc->depth) { |
221 | case 0: | 235 | case 0: |
236 | err_out: | ||
222 | WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n", irq); | 237 | WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n", irq); |
223 | break; | 238 | break; |
224 | case 1: { | 239 | case 1: { |
225 | unsigned int status = desc->status & ~IRQ_DISABLED; | 240 | unsigned int status = desc->status & ~IRQ_DISABLED; |
226 | 241 | ||
242 | if (desc->status & IRQ_SUSPENDED) | ||
243 | goto err_out; | ||
227 | /* Prevent probing on this irq: */ | 244 | /* Prevent probing on this irq: */ |
228 | desc->status = status | IRQ_NOPROBE; | 245 | desc->status = status | IRQ_NOPROBE; |
229 | check_irq_resend(desc, irq); | 246 | check_irq_resend(desc, irq); |
@@ -253,7 +270,7 @@ void enable_irq(unsigned int irq) | |||
253 | return; | 270 | return; |
254 | 271 | ||
255 | spin_lock_irqsave(&desc->lock, flags); | 272 | spin_lock_irqsave(&desc->lock, flags); |
256 | __enable_irq(desc, irq); | 273 | __enable_irq(desc, irq, false); |
257 | spin_unlock_irqrestore(&desc->lock, flags); | 274 | spin_unlock_irqrestore(&desc->lock, flags); |
258 | } | 275 | } |
259 | EXPORT_SYMBOL(enable_irq); | 276 | EXPORT_SYMBOL(enable_irq); |
@@ -511,7 +528,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) | |||
511 | */ | 528 | */ |
512 | if (shared && (desc->status & IRQ_SPURIOUS_DISABLED)) { | 529 | if (shared && (desc->status & IRQ_SPURIOUS_DISABLED)) { |
513 | desc->status &= ~IRQ_SPURIOUS_DISABLED; | 530 | desc->status &= ~IRQ_SPURIOUS_DISABLED; |
514 | __enable_irq(desc, irq); | 531 | __enable_irq(desc, irq, false); |
515 | } | 532 | } |
516 | 533 | ||
517 | spin_unlock_irqrestore(&desc->lock, flags); | 534 | spin_unlock_irqrestore(&desc->lock, flags); |
diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c new file mode 100644 index 000000000000..638d8bedec14 --- /dev/null +++ b/kernel/irq/pm.c | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * linux/kernel/irq/pm.c | ||
3 | * | ||
4 | * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. | ||
5 | * | ||
6 | * This file contains power management functions related to interrupts. | ||
7 | */ | ||
8 | |||
9 | #include <linux/irq.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | |||
13 | #include "internals.h" | ||
14 | |||
15 | /** | ||
16 | * suspend_device_irqs - disable all currently enabled interrupt lines | ||
17 | * | ||
18 | * During system-wide suspend or hibernation device interrupts need to be | ||
19 | * disabled at the chip level and this function is provided for this purpose. | ||
20 | * It disables all interrupt lines that are enabled at the moment and sets the | ||
21 | * IRQ_SUSPENDED flag for them. | ||
22 | */ | ||
23 | void suspend_device_irqs(void) | ||
24 | { | ||
25 | struct irq_desc *desc; | ||
26 | int irq; | ||
27 | |||
28 | for_each_irq_desc(irq, desc) { | ||
29 | unsigned long flags; | ||
30 | |||
31 | spin_lock_irqsave(&desc->lock, flags); | ||
32 | __disable_irq(desc, irq, true); | ||
33 | spin_unlock_irqrestore(&desc->lock, flags); | ||
34 | } | ||
35 | |||
36 | for_each_irq_desc(irq, desc) | ||
37 | if (desc->status & IRQ_SUSPENDED) | ||
38 | synchronize_irq(irq); | ||
39 | } | ||
40 | EXPORT_SYMBOL_GPL(suspend_device_irqs); | ||
41 | |||
42 | /** | ||
43 | * resume_device_irqs - enable interrupt lines disabled by suspend_device_irqs() | ||
44 | * | ||
45 | * Enable all interrupt lines previously disabled by suspend_device_irqs() that | ||
46 | * have the IRQ_SUSPENDED flag set. | ||
47 | */ | ||
48 | void resume_device_irqs(void) | ||
49 | { | ||
50 | struct irq_desc *desc; | ||
51 | int irq; | ||
52 | |||
53 | for_each_irq_desc(irq, desc) { | ||
54 | unsigned long flags; | ||
55 | |||
56 | if (!(desc->status & IRQ_SUSPENDED)) | ||
57 | continue; | ||
58 | |||
59 | spin_lock_irqsave(&desc->lock, flags); | ||
60 | __enable_irq(desc, irq, true); | ||
61 | spin_unlock_irqrestore(&desc->lock, flags); | ||
62 | } | ||
63 | } | ||
64 | EXPORT_SYMBOL_GPL(resume_device_irqs); | ||
65 | |||
66 | /** | ||
67 | * check_wakeup_irqs - check if any wake-up interrupts are pending | ||
68 | */ | ||
69 | int check_wakeup_irqs(void) | ||
70 | { | ||
71 | struct irq_desc *desc; | ||
72 | int irq; | ||
73 | |||
74 | for_each_irq_desc(irq, desc) | ||
75 | if ((desc->status & IRQ_WAKEUP) && (desc->status & IRQ_PENDING)) | ||
76 | return -EBUSY; | ||
77 | |||
78 | return 0; | ||
79 | } | ||