diff options
Diffstat (limited to 'kernel/irq/autoprobe.c')
-rw-r--r-- | kernel/irq/autoprobe.c | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/kernel/irq/autoprobe.c b/kernel/irq/autoprobe.c new file mode 100644 index 000000000000..98d62d8efeaf --- /dev/null +++ b/kernel/irq/autoprobe.c | |||
@@ -0,0 +1,189 @@ | |||
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> | ||
12 | |||
13 | /* | ||
14 | * Autodetection depends on the fact that any interrupt that | ||
15 | * comes in on to an unassigned handler will get stuck with | ||
16 | * "IRQ_WAITING" cleared and the interrupt disabled. | ||
17 | */ | ||
18 | static DECLARE_MUTEX(probe_sem); | ||
19 | |||
20 | /** | ||
21 | * probe_irq_on - begin an interrupt autodetect | ||
22 | * | ||
23 | * Commence probing for an interrupt. The interrupts are scanned | ||
24 | * and a mask of potential interrupt lines is returned. | ||
25 | * | ||
26 | */ | ||
27 | unsigned long probe_irq_on(void) | ||
28 | { | ||
29 | unsigned long val, delay; | ||
30 | irq_desc_t *desc; | ||
31 | unsigned int i; | ||
32 | |||
33 | down(&probe_sem); | ||
34 | /* | ||
35 | * something may have generated an irq long ago and we want to | ||
36 | * flush such a longstanding irq before considering it as spurious. | ||
37 | */ | ||
38 | for (i = NR_IRQS-1; i > 0; i--) { | ||
39 | desc = irq_desc + i; | ||
40 | |||
41 | spin_lock_irq(&desc->lock); | ||
42 | if (!irq_desc[i].action) | ||
43 | irq_desc[i].handler->startup(i); | ||
44 | spin_unlock_irq(&desc->lock); | ||
45 | } | ||
46 | |||
47 | /* Wait for longstanding interrupts to trigger. */ | ||
48 | for (delay = jiffies + HZ/50; time_after(delay, jiffies); ) | ||
49 | /* about 20ms delay */ barrier(); | ||
50 | |||
51 | /* | ||
52 | * enable any unassigned irqs | ||
53 | * (we must startup again here because if a longstanding irq | ||
54 | * happened in the previous stage, it may have masked itself) | ||
55 | */ | ||
56 | for (i = NR_IRQS-1; i > 0; i--) { | ||
57 | desc = irq_desc + i; | ||
58 | |||
59 | spin_lock_irq(&desc->lock); | ||
60 | if (!desc->action) { | ||
61 | desc->status |= IRQ_AUTODETECT | IRQ_WAITING; | ||
62 | if (desc->handler->startup(i)) | ||
63 | desc->status |= IRQ_PENDING; | ||
64 | } | ||
65 | spin_unlock_irq(&desc->lock); | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * Wait for spurious interrupts to trigger | ||
70 | */ | ||
71 | for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) | ||
72 | /* about 100ms delay */ barrier(); | ||
73 | |||
74 | /* | ||
75 | * Now filter out any obviously spurious interrupts | ||
76 | */ | ||
77 | val = 0; | ||
78 | for (i = 0; i < NR_IRQS; i++) { | ||
79 | irq_desc_t *desc = irq_desc + i; | ||
80 | unsigned int status; | ||
81 | |||
82 | spin_lock_irq(&desc->lock); | ||
83 | status = desc->status; | ||
84 | |||
85 | if (status & IRQ_AUTODETECT) { | ||
86 | /* It triggered already - consider it spurious. */ | ||
87 | if (!(status & IRQ_WAITING)) { | ||
88 | desc->status = status & ~IRQ_AUTODETECT; | ||
89 | desc->handler->shutdown(i); | ||
90 | } else | ||
91 | if (i < 32) | ||
92 | val |= 1 << i; | ||
93 | } | ||
94 | spin_unlock_irq(&desc->lock); | ||
95 | } | ||
96 | |||
97 | return val; | ||
98 | } | ||
99 | |||
100 | EXPORT_SYMBOL(probe_irq_on); | ||
101 | |||
102 | /** | ||
103 | * probe_irq_mask - scan a bitmap of interrupt lines | ||
104 | * @val: mask of interrupts to consider | ||
105 | * | ||
106 | * Scan the interrupt lines and return a bitmap of active | ||
107 | * autodetect interrupts. The interrupt probe logic state | ||
108 | * is then returned to its previous value. | ||
109 | * | ||
110 | * Note: we need to scan all the irq's even though we will | ||
111 | * only return autodetect irq numbers - just so that we reset | ||
112 | * them all to a known state. | ||
113 | */ | ||
114 | unsigned int probe_irq_mask(unsigned long val) | ||
115 | { | ||
116 | unsigned int mask; | ||
117 | int i; | ||
118 | |||
119 | mask = 0; | ||
120 | for (i = 0; i < NR_IRQS; i++) { | ||
121 | irq_desc_t *desc = irq_desc + i; | ||
122 | unsigned int status; | ||
123 | |||
124 | spin_lock_irq(&desc->lock); | ||
125 | status = desc->status; | ||
126 | |||
127 | if (status & IRQ_AUTODETECT) { | ||
128 | if (i < 16 && !(status & IRQ_WAITING)) | ||
129 | mask |= 1 << i; | ||
130 | |||
131 | desc->status = status & ~IRQ_AUTODETECT; | ||
132 | desc->handler->shutdown(i); | ||
133 | } | ||
134 | spin_unlock_irq(&desc->lock); | ||
135 | } | ||
136 | up(&probe_sem); | ||
137 | |||
138 | return mask & val; | ||
139 | } | ||
140 | EXPORT_SYMBOL(probe_irq_mask); | ||
141 | |||
142 | /** | ||
143 | * probe_irq_off - end an interrupt autodetect | ||
144 | * @val: mask of potential interrupts (unused) | ||
145 | * | ||
146 | * Scans the unused interrupt lines and returns the line which | ||
147 | * appears to have triggered the interrupt. If no interrupt was | ||
148 | * found then zero is returned. If more than one interrupt is | ||
149 | * found then minus the first candidate is returned to indicate | ||
150 | * their is doubt. | ||
151 | * | ||
152 | * The interrupt probe logic state is returned to its previous | ||
153 | * value. | ||
154 | * | ||
155 | * BUGS: When used in a module (which arguably shouldn't happen) | ||
156 | * nothing prevents two IRQ probe callers from overlapping. The | ||
157 | * results of this are non-optimal. | ||
158 | */ | ||
159 | int probe_irq_off(unsigned long val) | ||
160 | { | ||
161 | int i, irq_found = 0, nr_irqs = 0; | ||
162 | |||
163 | for (i = 0; i < NR_IRQS; i++) { | ||
164 | irq_desc_t *desc = irq_desc + i; | ||
165 | unsigned int status; | ||
166 | |||
167 | spin_lock_irq(&desc->lock); | ||
168 | status = desc->status; | ||
169 | |||
170 | if (status & IRQ_AUTODETECT) { | ||
171 | if (!(status & IRQ_WAITING)) { | ||
172 | if (!nr_irqs) | ||
173 | irq_found = i; | ||
174 | nr_irqs++; | ||
175 | } | ||
176 | desc->status = status & ~IRQ_AUTODETECT; | ||
177 | desc->handler->shutdown(i); | ||
178 | } | ||
179 | spin_unlock_irq(&desc->lock); | ||
180 | } | ||
181 | up(&probe_sem); | ||
182 | |||
183 | if (nr_irqs > 1) | ||
184 | irq_found = -irq_found; | ||
185 | return irq_found; | ||
186 | } | ||
187 | |||
188 | EXPORT_SYMBOL(probe_irq_off); | ||
189 | |||