]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/arch/m68k/kernel/ints.c -- Linux/m68k general interrupt handling code | |
3 | * | |
4 | * This file is subject to the terms and conditions of the GNU General Public | |
5 | * License. See the file COPYING in the main directory of this archive | |
6 | * for more details. | |
7 | * | |
8 | * 07/03/96: Timer initialization, and thus mach_sched_init(), | |
9 | * removed from request_irq() and moved to init_time(). | |
10 | * We should therefore consider renaming our add_isr() and | |
11 | * remove_isr() to request_irq() and free_irq() | |
12 | * respectively, so they are compliant with the other | |
13 | * architectures. /Jes | |
14 | * 11/07/96: Changed all add_/remove_isr() to request_/free_irq() calls. | |
15 | * Removed irq list support, if any machine needs an irq server | |
16 | * it must implement this itself (as it's already done), instead | |
17 | * only default handler are used with mach_default_handler. | |
18 | * request_irq got some flags different from other architectures: | |
19 | * - IRQ_FLG_REPLACE : Replace an existing handler (the default one | |
20 | * can be replaced without this flag) | |
21 | * - IRQ_FLG_LOCK : handler can't be replaced | |
22 | * There are other machine depending flags, see there | |
23 | * If you want to replace a default handler you should know what | |
24 | * you're doing, since it might handle different other irq sources | |
25 | * which must be served /Roman Zippel | |
26 | */ | |
27 | ||
1da177e4 LT |
28 | #include <linux/module.h> |
29 | #include <linux/types.h> | |
30 | #include <linux/sched.h> | |
31 | #include <linux/kernel_stat.h> | |
32 | #include <linux/errno.h> | |
33 | #include <linux/init.h> | |
34 | ||
35 | #include <asm/setup.h> | |
36 | #include <asm/system.h> | |
37 | #include <asm/irq.h> | |
38 | #include <asm/traps.h> | |
39 | #include <asm/page.h> | |
40 | #include <asm/machdep.h> | |
68387c44 | 41 | #include <asm/cacheflush.h> |
1da177e4 LT |
42 | |
43 | #ifdef CONFIG_Q40 | |
44 | #include <asm/q40ints.h> | |
45 | #endif | |
46 | ||
68387c44 RZ |
47 | extern u32 auto_irqhandler_fixup[]; |
48 | extern u32 user_irqhandler_fixup[]; | |
49 | extern u16 user_irqvec_fixup[]; | |
50 | ||
1da177e4 | 51 | /* table for system interrupt handlers */ |
68387c44 RZ |
52 | static struct irq_node *irq_list[NR_IRQS]; |
53 | static struct irq_controller *irq_controller[NR_IRQS]; | |
54 | static int irq_depth[NR_IRQS]; | |
55 | ||
56 | static int m68k_first_user_vec; | |
b5dc7840 RZ |
57 | |
58 | static struct irq_controller auto_irq_controller = { | |
59 | .name = "auto", | |
60 | .lock = SPIN_LOCK_UNLOCKED, | |
61 | .startup = m68k_irq_startup, | |
62 | .shutdown = m68k_irq_shutdown, | |
63 | }; | |
1da177e4 | 64 | |
68387c44 RZ |
65 | static struct irq_controller user_irq_controller = { |
66 | .name = "user", | |
67 | .lock = SPIN_LOCK_UNLOCKED, | |
68 | .startup = m68k_irq_startup, | |
69 | .shutdown = m68k_irq_shutdown, | |
1da177e4 LT |
70 | }; |
71 | ||
1da177e4 LT |
72 | #define NUM_IRQ_NODES 100 |
73 | static irq_node_t nodes[NUM_IRQ_NODES]; | |
74 | ||
1da177e4 LT |
75 | /* |
76 | * void init_IRQ(void) | |
77 | * | |
78 | * Parameters: None | |
79 | * | |
80 | * Returns: Nothing | |
81 | * | |
82 | * This function should be called during kernel startup to initialize | |
83 | * the IRQ handling routines. | |
84 | */ | |
85 | ||
86 | void __init init_IRQ(void) | |
87 | { | |
88 | int i; | |
89 | ||
6d2f16a8 RZ |
90 | /* assembly irq entry code relies on this... */ |
91 | if (HARDIRQ_MASK != 0x00ff0000) { | |
92 | extern void hardirq_mask_is_broken(void); | |
93 | hardirq_mask_is_broken(); | |
94 | } | |
95 | ||
68387c44 | 96 | for (i = IRQ_AUTO_1; i <= IRQ_AUTO_7; i++) |
b5dc7840 | 97 | irq_controller[i] = &auto_irq_controller; |
1da177e4 | 98 | |
68387c44 RZ |
99 | mach_init_IRQ(); |
100 | } | |
101 | ||
102 | /** | |
103 | * m68k_setup_auto_interrupt | |
104 | * @handler: called from auto vector interrupts | |
105 | * | |
106 | * setup the handler to be called from auto vector interrupts instead of the | |
107 | * standard m68k_handle_int(), it will be called with irq numbers in the range | |
108 | * from IRQ_AUTO_1 - IRQ_AUTO_7. | |
109 | */ | |
110 | void __init m68k_setup_auto_interrupt(void (*handler)(unsigned int, struct pt_regs *)) | |
111 | { | |
112 | if (handler) | |
113 | *auto_irqhandler_fixup = (u32)handler; | |
114 | flush_icache(); | |
115 | } | |
116 | ||
117 | /** | |
118 | * m68k_setup_user_interrupt | |
119 | * @vec: first user vector interrupt to handle | |
120 | * @cnt: number of active user vector interrupts | |
121 | * @handler: called from user vector interrupts | |
122 | * | |
123 | * setup user vector interrupts, this includes activating the specified range | |
124 | * of interrupts, only then these interrupts can be requested (note: this is | |
125 | * different from auto vector interrupts). An optional handler can be installed | |
126 | * to be called instead of the default m68k_handle_int(), it will be called | |
127 | * with irq numbers starting from IRQ_USER. | |
128 | */ | |
129 | void __init m68k_setup_user_interrupt(unsigned int vec, unsigned int cnt, | |
130 | void (*handler)(unsigned int, struct pt_regs *)) | |
131 | { | |
132 | int i; | |
133 | ||
134 | m68k_first_user_vec = vec; | |
135 | for (i = 0; i < cnt; i++) | |
136 | irq_controller[IRQ_USER + i] = &user_irq_controller; | |
137 | *user_irqvec_fixup = vec - IRQ_USER; | |
138 | if (handler) | |
139 | *user_irqhandler_fixup = (u32)handler; | |
140 | flush_icache(); | |
141 | } | |
142 | ||
143 | /** | |
144 | * m68k_setup_irq_controller | |
145 | * @contr: irq controller which controls specified irq | |
146 | * @irq: first irq to be managed by the controller | |
147 | * | |
148 | * Change the controller for the specified range of irq, which will be used to | |
149 | * manage these irq. auto/user irq already have a default controller, which can | |
150 | * be changed as well, but the controller probably should use m68k_irq_startup/ | |
151 | * m68k_irq_shutdown. | |
152 | */ | |
153 | void m68k_setup_irq_controller(struct irq_controller *contr, unsigned int irq, | |
154 | unsigned int cnt) | |
155 | { | |
156 | int i; | |
157 | ||
158 | for (i = 0; i < cnt; i++) | |
159 | irq_controller[irq + i] = contr; | |
1da177e4 LT |
160 | } |
161 | ||
162 | irq_node_t *new_irq_node(void) | |
163 | { | |
164 | irq_node_t *node; | |
165 | short i; | |
166 | ||
b5dc7840 RZ |
167 | for (node = nodes, i = NUM_IRQ_NODES-1; i >= 0; node++, i--) { |
168 | if (!node->handler) { | |
169 | memset(node, 0, sizeof(*node)); | |
1da177e4 | 170 | return node; |
b5dc7840 RZ |
171 | } |
172 | } | |
1da177e4 LT |
173 | |
174 | printk ("new_irq_node: out of nodes\n"); | |
175 | return NULL; | |
176 | } | |
177 | ||
b5dc7840 | 178 | int setup_irq(unsigned int irq, struct irq_node *node) |
1da177e4 | 179 | { |
b5dc7840 RZ |
180 | struct irq_controller *contr; |
181 | struct irq_node **prev; | |
182 | unsigned long flags; | |
183 | ||
68387c44 | 184 | if (irq >= NR_IRQS || !(contr = irq_controller[irq])) { |
1da177e4 | 185 | printk("%s: Incorrect IRQ %d from %s\n", |
b5dc7840 | 186 | __FUNCTION__, irq, node->devname); |
1da177e4 LT |
187 | return -ENXIO; |
188 | } | |
189 | ||
b5dc7840 RZ |
190 | spin_lock_irqsave(&contr->lock, flags); |
191 | ||
192 | prev = irq_list + irq; | |
193 | if (*prev) { | |
194 | /* Can't share interrupts unless both agree to */ | |
b0b9fdc1 | 195 | if (!((*prev)->flags & node->flags & IRQF_SHARED)) { |
b5dc7840 | 196 | spin_unlock_irqrestore(&contr->lock, flags); |
1da177e4 LT |
197 | return -EBUSY; |
198 | } | |
b5dc7840 RZ |
199 | while (*prev) |
200 | prev = &(*prev)->next; | |
1da177e4 | 201 | } |
1da177e4 | 202 | |
b5dc7840 RZ |
203 | if (!irq_list[irq]) { |
204 | if (contr->startup) | |
205 | contr->startup(irq); | |
206 | else | |
207 | contr->enable(irq); | |
208 | } | |
209 | node->next = NULL; | |
210 | *prev = node; | |
211 | ||
212 | spin_unlock_irqrestore(&contr->lock, flags); | |
213 | ||
1da177e4 LT |
214 | return 0; |
215 | } | |
216 | ||
68387c44 RZ |
217 | int request_irq(unsigned int irq, |
218 | irqreturn_t (*handler) (int, void *, struct pt_regs *), | |
219 | unsigned long flags, const char *devname, void *dev_id) | |
b5dc7840 RZ |
220 | { |
221 | struct irq_node *node; | |
222 | int res; | |
223 | ||
224 | node = new_irq_node(); | |
225 | if (!node) | |
226 | return -ENOMEM; | |
227 | ||
228 | node->handler = handler; | |
229 | node->flags = flags; | |
230 | node->dev_id = dev_id; | |
231 | node->devname = devname; | |
232 | ||
233 | res = setup_irq(irq, node); | |
234 | if (res) | |
235 | node->handler = NULL; | |
236 | ||
237 | return res; | |
238 | } | |
239 | ||
68387c44 RZ |
240 | EXPORT_SYMBOL(request_irq); |
241 | ||
242 | void free_irq(unsigned int irq, void *dev_id) | |
1da177e4 | 243 | { |
b5dc7840 RZ |
244 | struct irq_controller *contr; |
245 | struct irq_node **p, *node; | |
246 | unsigned long flags; | |
247 | ||
68387c44 | 248 | if (irq >= NR_IRQS || !(contr = irq_controller[irq])) { |
1da177e4 LT |
249 | printk("%s: Incorrect IRQ %d\n", __FUNCTION__, irq); |
250 | return; | |
251 | } | |
252 | ||
b5dc7840 RZ |
253 | spin_lock_irqsave(&contr->lock, flags); |
254 | ||
255 | p = irq_list + irq; | |
256 | while ((node = *p)) { | |
257 | if (node->dev_id == dev_id) | |
258 | break; | |
259 | p = &node->next; | |
260 | } | |
261 | ||
262 | if (node) { | |
263 | *p = node->next; | |
264 | node->handler = NULL; | |
265 | } else | |
266 | printk("%s: Removing probably wrong IRQ %d\n", | |
267 | __FUNCTION__, irq); | |
268 | ||
68387c44 RZ |
269 | if (!irq_list[irq]) { |
270 | if (contr->shutdown) | |
271 | contr->shutdown(irq); | |
272 | else | |
273 | contr->disable(irq); | |
274 | } | |
275 | ||
276 | spin_unlock_irqrestore(&contr->lock, flags); | |
277 | } | |
278 | ||
279 | EXPORT_SYMBOL(free_irq); | |
280 | ||
281 | void enable_irq(unsigned int irq) | |
282 | { | |
283 | struct irq_controller *contr; | |
284 | unsigned long flags; | |
285 | ||
286 | if (irq >= NR_IRQS || !(contr = irq_controller[irq])) { | |
287 | printk("%s: Incorrect IRQ %d\n", | |
288 | __FUNCTION__, irq); | |
289 | return; | |
290 | } | |
291 | ||
292 | spin_lock_irqsave(&contr->lock, flags); | |
293 | if (irq_depth[irq]) { | |
294 | if (!--irq_depth[irq]) { | |
295 | if (contr->enable) | |
296 | contr->enable(irq); | |
297 | } | |
298 | } else | |
299 | WARN_ON(1); | |
300 | spin_unlock_irqrestore(&contr->lock, flags); | |
301 | } | |
302 | ||
303 | EXPORT_SYMBOL(enable_irq); | |
1da177e4 | 304 | |
68387c44 RZ |
305 | void disable_irq(unsigned int irq) |
306 | { | |
307 | struct irq_controller *contr; | |
308 | unsigned long flags; | |
309 | ||
310 | if (irq >= NR_IRQS || !(contr = irq_controller[irq])) { | |
311 | printk("%s: Incorrect IRQ %d\n", | |
312 | __FUNCTION__, irq); | |
313 | return; | |
314 | } | |
315 | ||
316 | spin_lock_irqsave(&contr->lock, flags); | |
317 | if (!irq_depth[irq]++) { | |
318 | if (contr->disable) | |
319 | contr->disable(irq); | |
320 | } | |
b5dc7840 | 321 | spin_unlock_irqrestore(&contr->lock, flags); |
1da177e4 LT |
322 | } |
323 | ||
68387c44 RZ |
324 | EXPORT_SYMBOL(disable_irq); |
325 | ||
b5dc7840 RZ |
326 | int m68k_irq_startup(unsigned int irq) |
327 | { | |
328 | if (irq <= IRQ_AUTO_7) | |
329 | vectors[VEC_SPUR + irq] = auto_inthandler; | |
68387c44 RZ |
330 | else |
331 | vectors[m68k_first_user_vec + irq - IRQ_USER] = user_inthandler; | |
b5dc7840 RZ |
332 | return 0; |
333 | } | |
334 | ||
335 | void m68k_irq_shutdown(unsigned int irq) | |
336 | { | |
337 | if (irq <= IRQ_AUTO_7) | |
338 | vectors[VEC_SPUR + irq] = bad_inthandler; | |
68387c44 RZ |
339 | else |
340 | vectors[m68k_first_user_vec + irq - IRQ_USER] = bad_inthandler; | |
b5dc7840 RZ |
341 | } |
342 | ||
343 | ||
1da177e4 LT |
344 | /* |
345 | * Do we need these probe functions on the m68k? | |
346 | * | |
347 | * ... may be useful with ISA devices | |
348 | */ | |
349 | unsigned long probe_irq_on (void) | |
350 | { | |
351 | #ifdef CONFIG_Q40 | |
352 | if (MACH_IS_Q40) | |
353 | return q40_probe_irq_on(); | |
354 | #endif | |
355 | return 0; | |
356 | } | |
357 | ||
358 | EXPORT_SYMBOL(probe_irq_on); | |
359 | ||
360 | int probe_irq_off (unsigned long irqs) | |
361 | { | |
362 | #ifdef CONFIG_Q40 | |
363 | if (MACH_IS_Q40) | |
364 | return q40_probe_irq_off(irqs); | |
365 | #endif | |
366 | return 0; | |
367 | } | |
368 | ||
369 | EXPORT_SYMBOL(probe_irq_off); | |
370 | ||
68387c44 | 371 | unsigned int irq_canonicalize(unsigned int irq) |
1da177e4 | 372 | { |
68387c44 RZ |
373 | #ifdef CONFIG_Q40 |
374 | if (MACH_IS_Q40 && irq == 11) | |
375 | irq = 10; | |
376 | #endif | |
377 | return irq; | |
1da177e4 LT |
378 | } |
379 | ||
68387c44 | 380 | EXPORT_SYMBOL(irq_canonicalize); |
1da177e4 | 381 | |
92445eaa | 382 | asmlinkage void m68k_handle_int(unsigned int irq, struct pt_regs *regs) |
1da177e4 | 383 | { |
b5dc7840 RZ |
384 | struct irq_node *node; |
385 | ||
92445eaa | 386 | kstat_cpu(0).irqs[irq]++; |
b5dc7840 RZ |
387 | node = irq_list[irq]; |
388 | do { | |
389 | node->handler(irq, node->dev_id, regs); | |
390 | node = node->next; | |
391 | } while (node); | |
92445eaa RZ |
392 | } |
393 | ||
394 | asmlinkage void handle_badint(struct pt_regs *regs) | |
395 | { | |
396 | kstat_cpu(0).irqs[0]++; | |
397 | printk("unexpected interrupt from %u\n", regs->vector); | |
1da177e4 LT |
398 | } |
399 | ||
400 | int show_interrupts(struct seq_file *p, void *v) | |
401 | { | |
b5dc7840 RZ |
402 | struct irq_controller *contr; |
403 | struct irq_node *node; | |
1da177e4 LT |
404 | int i = *(loff_t *) v; |
405 | ||
406 | /* autovector interrupts */ | |
68387c44 | 407 | if (irq_list[i]) { |
b5dc7840 RZ |
408 | contr = irq_controller[i]; |
409 | node = irq_list[i]; | |
68387c44 | 410 | seq_printf(p, "%-8s %3u: %10u %s", contr->name, i, kstat_cpu(0).irqs[i], node->devname); |
b5dc7840 RZ |
411 | while ((node = node->next)) |
412 | seq_printf(p, ", %s", node->devname); | |
413 | seq_puts(p, "\n"); | |
68387c44 | 414 | } |
1da177e4 LT |
415 | return 0; |
416 | } | |
417 | ||
418 | void init_irq_proc(void) | |
419 | { | |
420 | /* Insert /proc/irq driver here */ | |
421 | } | |
422 |