]>
Commit | Line | Data |
---|---|---|
0a0c5168 RW |
1 | /* |
2 | * linux/kernel/irq/pm.c | |
3 | * | |
4 | * Copyright (C) 2009 Rafael J. Wysocki <[email protected]>, 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> | |
9bab0b7f | 12 | #include <linux/syscore_ops.h> |
0a0c5168 RW |
13 | |
14 | #include "internals.h" | |
15 | ||
16 | /** | |
17 | * suspend_device_irqs - disable all currently enabled interrupt lines | |
18 | * | |
c71320d0 RW |
19 | * During system-wide suspend or hibernation device drivers need to be prevented |
20 | * from receiving interrupts and this function is provided for this purpose. | |
21 | * It marks all interrupt lines in use, except for the timer ones, as disabled | |
c531e836 | 22 | * and sets the IRQS_SUSPENDED flag for each of them. |
0a0c5168 RW |
23 | */ |
24 | void suspend_device_irqs(void) | |
25 | { | |
26 | struct irq_desc *desc; | |
27 | int irq; | |
28 | ||
29 | for_each_irq_desc(irq, desc) { | |
30 | unsigned long flags; | |
31 | ||
239007b8 | 32 | raw_spin_lock_irqsave(&desc->lock, flags); |
0a0c5168 | 33 | __disable_irq(desc, irq, true); |
239007b8 | 34 | raw_spin_unlock_irqrestore(&desc->lock, flags); |
0a0c5168 RW |
35 | } |
36 | ||
37 | for_each_irq_desc(irq, desc) | |
c531e836 | 38 | if (desc->istate & IRQS_SUSPENDED) |
0a0c5168 RW |
39 | synchronize_irq(irq); |
40 | } | |
41 | EXPORT_SYMBOL_GPL(suspend_device_irqs); | |
42 | ||
9bab0b7f | 43 | static void resume_irqs(bool want_early) |
0a0c5168 RW |
44 | { |
45 | struct irq_desc *desc; | |
46 | int irq; | |
47 | ||
48 | for_each_irq_desc(irq, desc) { | |
49 | unsigned long flags; | |
9bab0b7f IC |
50 | bool is_early = desc->action && |
51 | desc->action->flags & IRQF_EARLY_RESUME; | |
52 | ||
53 | if (is_early != want_early) | |
54 | continue; | |
0a0c5168 | 55 | |
239007b8 | 56 | raw_spin_lock_irqsave(&desc->lock, flags); |
0a0c5168 | 57 | __enable_irq(desc, irq, true); |
239007b8 | 58 | raw_spin_unlock_irqrestore(&desc->lock, flags); |
0a0c5168 RW |
59 | } |
60 | } | |
9bab0b7f IC |
61 | |
62 | /** | |
63 | * irq_pm_syscore_ops - enable interrupt lines early | |
64 | * | |
65 | * Enable all interrupt lines with %IRQF_EARLY_RESUME set. | |
66 | */ | |
67 | static void irq_pm_syscore_resume(void) | |
68 | { | |
69 | resume_irqs(true); | |
70 | } | |
71 | ||
72 | static struct syscore_ops irq_pm_syscore_ops = { | |
73 | .resume = irq_pm_syscore_resume, | |
74 | }; | |
75 | ||
76 | static int __init irq_pm_init_ops(void) | |
77 | { | |
78 | register_syscore_ops(&irq_pm_syscore_ops); | |
79 | return 0; | |
80 | } | |
81 | ||
82 | device_initcall(irq_pm_init_ops); | |
83 | ||
84 | /** | |
85 | * resume_device_irqs - enable interrupt lines disabled by suspend_device_irqs() | |
86 | * | |
87 | * Enable all non-%IRQF_EARLY_RESUME interrupt lines previously | |
88 | * disabled by suspend_device_irqs() that have the IRQS_SUSPENDED flag | |
89 | * set as well as those with %IRQF_FORCE_RESUME. | |
90 | */ | |
91 | void resume_device_irqs(void) | |
92 | { | |
93 | resume_irqs(false); | |
94 | } | |
0a0c5168 RW |
95 | EXPORT_SYMBOL_GPL(resume_device_irqs); |
96 | ||
97 | /** | |
98 | * check_wakeup_irqs - check if any wake-up interrupts are pending | |
99 | */ | |
100 | int check_wakeup_irqs(void) | |
101 | { | |
102 | struct irq_desc *desc; | |
103 | int irq; | |
104 | ||
d209a699 | 105 | for_each_irq_desc(irq, desc) { |
9c6079aa TG |
106 | /* |
107 | * Only interrupts which are marked as wakeup source | |
108 | * and have not been disabled before the suspend check | |
109 | * can abort suspend. | |
110 | */ | |
d209a699 | 111 | if (irqd_is_wakeup_set(&desc->irq_data)) { |
9c6079aa | 112 | if (desc->depth == 1 && desc->istate & IRQS_PENDING) |
d209a699 TG |
113 | return -EBUSY; |
114 | continue; | |
115 | } | |
116 | /* | |
117 | * Check the non wakeup interrupts whether they need | |
118 | * to be masked before finally going into suspend | |
119 | * state. That's for hardware which has no wakeup | |
120 | * source configuration facility. The chip | |
121 | * implementation indicates that with | |
122 | * IRQCHIP_MASK_ON_SUSPEND. | |
123 | */ | |
124 | if (desc->istate & IRQS_SUSPENDED && | |
125 | irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND) | |
126 | mask_irq(desc); | |
127 | } | |
0a0c5168 RW |
128 | |
129 | return 0; | |
130 | } |