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