Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/kernel/irq/autoprobe.c | |
3 | * | |
4 | * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar | |
5 | * | |
6 | * This file contains the interrupt probing code and driver APIs. | |
7 | */ | |
8 | ||
9 | #include <linux/irq.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/interrupt.h> | |
47f176fd | 12 | #include <linux/delay.h> |
1da177e4 | 13 | |
7a55713a IM |
14 | #include "internals.h" |
15 | ||
1da177e4 LT |
16 | /* |
17 | * Autodetection depends on the fact that any interrupt that | |
18 | * comes in on to an unassigned handler will get stuck with | |
19 | * "IRQ_WAITING" cleared and the interrupt disabled. | |
20 | */ | |
74ffd553 | 21 | static DEFINE_MUTEX(probing_active); |
1da177e4 LT |
22 | |
23 | /** | |
24 | * probe_irq_on - begin an interrupt autodetect | |
25 | * | |
26 | * Commence probing for an interrupt. The interrupts are scanned | |
27 | * and a mask of potential interrupt lines is returned. | |
28 | * | |
29 | */ | |
30 | unsigned long probe_irq_on(void) | |
31 | { | |
34ffdb72 | 32 | struct irq_desc *desc; |
06fcb0c6 | 33 | unsigned long mask; |
1da177e4 LT |
34 | unsigned int i; |
35 | ||
74ffd553 | 36 | mutex_lock(&probing_active); |
1da177e4 LT |
37 | /* |
38 | * something may have generated an irq long ago and we want to | |
39 | * flush such a longstanding irq before considering it as spurious. | |
40 | */ | |
41 | for (i = NR_IRQS-1; i > 0; i--) { | |
42 | desc = irq_desc + i; | |
43 | ||
44 | spin_lock_irq(&desc->lock); | |
6a6de9ef | 45 | if (!desc->action && !(desc->status & IRQ_NOPROBE)) { |
7a55713a IM |
46 | /* |
47 | * An old-style architecture might still have | |
48 | * the handle_bad_irq handler there: | |
49 | */ | |
50 | compat_irq_chip_set_default_handler(desc); | |
51 | ||
6a6de9ef TG |
52 | /* |
53 | * Some chips need to know about probing in | |
54 | * progress: | |
55 | */ | |
56 | if (desc->chip->set_type) | |
57 | desc->chip->set_type(i, IRQ_TYPE_PROBE); | |
06fcb0c6 | 58 | desc->chip->startup(i); |
6a6de9ef | 59 | } |
1da177e4 LT |
60 | spin_unlock_irq(&desc->lock); |
61 | } | |
62 | ||
63 | /* Wait for longstanding interrupts to trigger. */ | |
47f176fd | 64 | msleep(20); |
1da177e4 LT |
65 | |
66 | /* | |
67 | * enable any unassigned irqs | |
68 | * (we must startup again here because if a longstanding irq | |
69 | * happened in the previous stage, it may have masked itself) | |
70 | */ | |
71 | for (i = NR_IRQS-1; i > 0; i--) { | |
72 | desc = irq_desc + i; | |
73 | ||
74 | spin_lock_irq(&desc->lock); | |
3418d724 | 75 | if (!desc->action && !(desc->status & IRQ_NOPROBE)) { |
1da177e4 | 76 | desc->status |= IRQ_AUTODETECT | IRQ_WAITING; |
d1bef4ed | 77 | if (desc->chip->startup(i)) |
1da177e4 LT |
78 | desc->status |= IRQ_PENDING; |
79 | } | |
80 | spin_unlock_irq(&desc->lock); | |
81 | } | |
82 | ||
83 | /* | |
84 | * Wait for spurious interrupts to trigger | |
85 | */ | |
47f176fd | 86 | msleep(100); |
1da177e4 LT |
87 | |
88 | /* | |
89 | * Now filter out any obviously spurious interrupts | |
90 | */ | |
06fcb0c6 | 91 | mask = 0; |
1da177e4 | 92 | for (i = 0; i < NR_IRQS; i++) { |
1da177e4 LT |
93 | unsigned int status; |
94 | ||
06fcb0c6 | 95 | desc = irq_desc + i; |
1da177e4 LT |
96 | spin_lock_irq(&desc->lock); |
97 | status = desc->status; | |
98 | ||
99 | if (status & IRQ_AUTODETECT) { | |
100 | /* It triggered already - consider it spurious. */ | |
101 | if (!(status & IRQ_WAITING)) { | |
102 | desc->status = status & ~IRQ_AUTODETECT; | |
d1bef4ed | 103 | desc->chip->shutdown(i); |
1da177e4 LT |
104 | } else |
105 | if (i < 32) | |
06fcb0c6 | 106 | mask |= 1 << i; |
1da177e4 LT |
107 | } |
108 | spin_unlock_irq(&desc->lock); | |
109 | } | |
110 | ||
06fcb0c6 | 111 | return mask; |
1da177e4 | 112 | } |
1da177e4 LT |
113 | EXPORT_SYMBOL(probe_irq_on); |
114 | ||
115 | /** | |
116 | * probe_irq_mask - scan a bitmap of interrupt lines | |
117 | * @val: mask of interrupts to consider | |
118 | * | |
119 | * Scan the interrupt lines and return a bitmap of active | |
120 | * autodetect interrupts. The interrupt probe logic state | |
121 | * is then returned to its previous value. | |
122 | * | |
123 | * Note: we need to scan all the irq's even though we will | |
124 | * only return autodetect irq numbers - just so that we reset | |
125 | * them all to a known state. | |
126 | */ | |
127 | unsigned int probe_irq_mask(unsigned long val) | |
128 | { | |
129 | unsigned int mask; | |
130 | int i; | |
131 | ||
132 | mask = 0; | |
133 | for (i = 0; i < NR_IRQS; i++) { | |
34ffdb72 | 134 | struct irq_desc *desc = irq_desc + i; |
1da177e4 LT |
135 | unsigned int status; |
136 | ||
137 | spin_lock_irq(&desc->lock); | |
138 | status = desc->status; | |
139 | ||
140 | if (status & IRQ_AUTODETECT) { | |
141 | if (i < 16 && !(status & IRQ_WAITING)) | |
142 | mask |= 1 << i; | |
143 | ||
144 | desc->status = status & ~IRQ_AUTODETECT; | |
d1bef4ed | 145 | desc->chip->shutdown(i); |
1da177e4 LT |
146 | } |
147 | spin_unlock_irq(&desc->lock); | |
148 | } | |
74ffd553 | 149 | mutex_unlock(&probing_active); |
1da177e4 LT |
150 | |
151 | return mask & val; | |
152 | } | |
153 | EXPORT_SYMBOL(probe_irq_mask); | |
154 | ||
155 | /** | |
156 | * probe_irq_off - end an interrupt autodetect | |
157 | * @val: mask of potential interrupts (unused) | |
158 | * | |
159 | * Scans the unused interrupt lines and returns the line which | |
160 | * appears to have triggered the interrupt. If no interrupt was | |
161 | * found then zero is returned. If more than one interrupt is | |
162 | * found then minus the first candidate is returned to indicate | |
163 | * their is doubt. | |
164 | * | |
165 | * The interrupt probe logic state is returned to its previous | |
166 | * value. | |
167 | * | |
168 | * BUGS: When used in a module (which arguably shouldn't happen) | |
169 | * nothing prevents two IRQ probe callers from overlapping. The | |
170 | * results of this are non-optimal. | |
171 | */ | |
172 | int probe_irq_off(unsigned long val) | |
173 | { | |
174 | int i, irq_found = 0, nr_irqs = 0; | |
175 | ||
176 | for (i = 0; i < NR_IRQS; i++) { | |
34ffdb72 | 177 | struct irq_desc *desc = irq_desc + i; |
1da177e4 LT |
178 | unsigned int status; |
179 | ||
180 | spin_lock_irq(&desc->lock); | |
181 | status = desc->status; | |
182 | ||
183 | if (status & IRQ_AUTODETECT) { | |
184 | if (!(status & IRQ_WAITING)) { | |
185 | if (!nr_irqs) | |
186 | irq_found = i; | |
187 | nr_irqs++; | |
188 | } | |
189 | desc->status = status & ~IRQ_AUTODETECT; | |
d1bef4ed | 190 | desc->chip->shutdown(i); |
1da177e4 LT |
191 | } |
192 | spin_unlock_irq(&desc->lock); | |
193 | } | |
74ffd553 | 194 | mutex_unlock(&probing_active); |
1da177e4 LT |
195 | |
196 | if (nr_irqs > 1) | |
197 | irq_found = -irq_found; | |
74ffd553 | 198 | |
1da177e4 LT |
199 | return irq_found; |
200 | } | |
1da177e4 LT |
201 | EXPORT_SYMBOL(probe_irq_off); |
202 |