diff options
Diffstat (limited to 'arch/m68k/mac/macints.c')
-rw-r--r-- | arch/m68k/mac/macints.c | 760 |
1 files changed, 760 insertions, 0 deletions
diff --git a/arch/m68k/mac/macints.c b/arch/m68k/mac/macints.c new file mode 100644 index 000000000000..1809601ad903 --- /dev/null +++ b/arch/m68k/mac/macints.c | |||
@@ -0,0 +1,760 @@ | |||
1 | /* | ||
2 | * Macintosh interrupts | ||
3 | * | ||
4 | * General design: | ||
5 | * In contrary to the Amiga and Atari platforms, the Mac hardware seems to | ||
6 | * exclusively use the autovector interrupts (the 'generic level0-level7' | ||
7 | * interrupts with exception vectors 0x19-0x1f). The following interrupt levels | ||
8 | * are used: | ||
9 | * 1 - VIA1 | ||
10 | * - slot 0: one second interrupt (CA2) | ||
11 | * - slot 1: VBlank (CA1) | ||
12 | * - slot 2: ADB data ready (SR full) | ||
13 | * - slot 3: ADB data (CB2) | ||
14 | * - slot 4: ADB clock (CB1) | ||
15 | * - slot 5: timer 2 | ||
16 | * - slot 6: timer 1 | ||
17 | * - slot 7: status of IRQ; signals 'any enabled int.' | ||
18 | * | ||
19 | * 2 - VIA2 or RBV | ||
20 | * - slot 0: SCSI DRQ (CA2) | ||
21 | * - slot 1: NUBUS IRQ (CA1) need to read port A to find which | ||
22 | * - slot 2: /EXP IRQ (only on IIci) | ||
23 | * - slot 3: SCSI IRQ (CB2) | ||
24 | * - slot 4: ASC IRQ (CB1) | ||
25 | * - slot 5: timer 2 (not on IIci) | ||
26 | * - slot 6: timer 1 (not on IIci) | ||
27 | * - slot 7: status of IRQ; signals 'any enabled int.' | ||
28 | * | ||
29 | * 2 - OSS (IIfx only?) | ||
30 | * - slot 0: SCSI interrupt | ||
31 | * - slot 1: Sound interrupt | ||
32 | * | ||
33 | * Levels 3-6 vary by machine type. For VIA or RBV Macintoshes: | ||
34 | * | ||
35 | * 3 - unused (?) | ||
36 | * | ||
37 | * 4 - SCC (slot number determined by reading RR3 on the SSC itself) | ||
38 | * - slot 1: SCC channel A | ||
39 | * - slot 2: SCC channel B | ||
40 | * | ||
41 | * 5 - unused (?) | ||
42 | * [serial errors or special conditions seem to raise level 6 | ||
43 | * interrupts on some models (LC4xx?)] | ||
44 | * | ||
45 | * 6 - off switch (?) | ||
46 | * | ||
47 | * For OSS Macintoshes (IIfx only at this point): | ||
48 | * | ||
49 | * 3 - Nubus interrupt | ||
50 | * - slot 0: Slot $9 | ||
51 | * - slot 1: Slot $A | ||
52 | * - slot 2: Slot $B | ||
53 | * - slot 3: Slot $C | ||
54 | * - slot 4: Slot $D | ||
55 | * - slot 5: Slot $E | ||
56 | * | ||
57 | * 4 - SCC IOP | ||
58 | * - slot 1: SCC channel A | ||
59 | * - slot 2: SCC channel B | ||
60 | * | ||
61 | * 5 - ISM IOP (ADB?) | ||
62 | * | ||
63 | * 6 - unused | ||
64 | * | ||
65 | * For PSC Macintoshes (660AV, 840AV): | ||
66 | * | ||
67 | * 3 - PSC level 3 | ||
68 | * - slot 0: MACE | ||
69 | * | ||
70 | * 4 - PSC level 4 | ||
71 | * - slot 1: SCC channel A interrupt | ||
72 | * - slot 2: SCC channel B interrupt | ||
73 | * - slot 3: MACE DMA | ||
74 | * | ||
75 | * 5 - PSC level 5 | ||
76 | * | ||
77 | * 6 - PSC level 6 | ||
78 | * | ||
79 | * Finally we have good 'ole level 7, the non-maskable interrupt: | ||
80 | * | ||
81 | * 7 - NMI (programmer's switch on the back of some Macs) | ||
82 | * Also RAM parity error on models which support it (IIc, IIfx?) | ||
83 | * | ||
84 | * The current interrupt logic looks something like this: | ||
85 | * | ||
86 | * - We install dispatchers for the autovector interrupts (1-7). These | ||
87 | * dispatchers are responsible for querying the hardware (the | ||
88 | * VIA/RBV/OSS/PSC chips) to determine the actual interrupt source. Using | ||
89 | * this information a machspec interrupt number is generated by placing the | ||
90 | * index of the interrupt hardware into the low three bits and the original | ||
91 | * autovector interrupt number in the upper 5 bits. The handlers for the | ||
92 | * resulting machspec interrupt are then called. | ||
93 | * | ||
94 | * - Nubus is a special case because its interrupts are hidden behind two | ||
95 | * layers of hardware. Nubus interrupts come in as index 1 on VIA #2, | ||
96 | * which translates to IRQ number 17. In this spot we install _another_ | ||
97 | * dispatcher. This dispatcher finds the interrupting slot number (9-F) and | ||
98 | * then forms a new machspec interrupt number as above with the slot number | ||
99 | * minus 9 in the low three bits and the pseudo-level 7 in the upper five | ||
100 | * bits. The handlers for this new machspec interrupt number are then | ||
101 | * called. This puts Nubus interrupts into the range 56-62. | ||
102 | * | ||
103 | * - The Baboon interrupts (used on some PowerBooks) are an even more special | ||
104 | * case. They're hidden behind the Nubus slot $C interrupt thus adding a | ||
105 | * third layer of indirection. Why oh why did the Apple engineers do that? | ||
106 | * | ||
107 | * - We support "fast" and "slow" handlers, just like the Amiga port. The | ||
108 | * fast handlers are called first and with all interrupts disabled. They | ||
109 | * are expected to execute quickly (hence the name). The slow handlers are | ||
110 | * called last with interrupts enabled and the interrupt level restored. | ||
111 | * They must therefore be reentrant. | ||
112 | * | ||
113 | * TODO: | ||
114 | * | ||
115 | */ | ||
116 | |||
117 | #include <linux/types.h> | ||
118 | #include <linux/kernel.h> | ||
119 | #include <linux/sched.h> | ||
120 | #include <linux/kernel_stat.h> | ||
121 | #include <linux/interrupt.h> /* for intr_count */ | ||
122 | #include <linux/delay.h> | ||
123 | #include <linux/seq_file.h> | ||
124 | |||
125 | #include <asm/system.h> | ||
126 | #include <asm/irq.h> | ||
127 | #include <asm/traps.h> | ||
128 | #include <asm/bootinfo.h> | ||
129 | #include <asm/machw.h> | ||
130 | #include <asm/macintosh.h> | ||
131 | #include <asm/mac_via.h> | ||
132 | #include <asm/mac_psc.h> | ||
133 | #include <asm/hwtest.h> | ||
134 | #include <asm/errno.h> | ||
135 | #include <asm/macints.h> | ||
136 | |||
137 | #define DEBUG_SPURIOUS | ||
138 | #define SHUTUP_SONIC | ||
139 | |||
140 | /* | ||
141 | * The mac_irq_list array is an array of linked lists of irq_node_t nodes. | ||
142 | * Each node contains one handler to be called whenever the interrupt | ||
143 | * occurs, with fast handlers listed before slow handlers. | ||
144 | */ | ||
145 | |||
146 | irq_node_t *mac_irq_list[NUM_MAC_SOURCES]; | ||
147 | |||
148 | /* SCC interrupt mask */ | ||
149 | |||
150 | static int scc_mask; | ||
151 | |||
152 | /* | ||
153 | * VIA/RBV hooks | ||
154 | */ | ||
155 | |||
156 | extern void via_init(void); | ||
157 | extern void via_register_interrupts(void); | ||
158 | extern void via_irq_enable(int); | ||
159 | extern void via_irq_disable(int); | ||
160 | extern void via_irq_clear(int); | ||
161 | extern int via_irq_pending(int); | ||
162 | |||
163 | /* | ||
164 | * OSS hooks | ||
165 | */ | ||
166 | |||
167 | extern int oss_present; | ||
168 | |||
169 | extern void oss_init(void); | ||
170 | extern void oss_register_interrupts(void); | ||
171 | extern void oss_irq_enable(int); | ||
172 | extern void oss_irq_disable(int); | ||
173 | extern void oss_irq_clear(int); | ||
174 | extern int oss_irq_pending(int); | ||
175 | |||
176 | /* | ||
177 | * PSC hooks | ||
178 | */ | ||
179 | |||
180 | extern int psc_present; | ||
181 | |||
182 | extern void psc_init(void); | ||
183 | extern void psc_register_interrupts(void); | ||
184 | extern void psc_irq_enable(int); | ||
185 | extern void psc_irq_disable(int); | ||
186 | extern void psc_irq_clear(int); | ||
187 | extern int psc_irq_pending(int); | ||
188 | |||
189 | /* | ||
190 | * IOP hooks | ||
191 | */ | ||
192 | |||
193 | extern void iop_register_interrupts(void); | ||
194 | |||
195 | /* | ||
196 | * Baboon hooks | ||
197 | */ | ||
198 | |||
199 | extern int baboon_present; | ||
200 | |||
201 | extern void baboon_init(void); | ||
202 | extern void baboon_register_interrupts(void); | ||
203 | extern void baboon_irq_enable(int); | ||
204 | extern void baboon_irq_disable(int); | ||
205 | extern void baboon_irq_clear(int); | ||
206 | extern int baboon_irq_pending(int); | ||
207 | |||
208 | /* | ||
209 | * SCC interrupt routines | ||
210 | */ | ||
211 | |||
212 | static void scc_irq_enable(int); | ||
213 | static void scc_irq_disable(int); | ||
214 | |||
215 | /* | ||
216 | * console_loglevel determines NMI handler function | ||
217 | */ | ||
218 | |||
219 | extern irqreturn_t mac_bang(int, void *, struct pt_regs *); | ||
220 | irqreturn_t mac_nmi_handler(int, void *, struct pt_regs *); | ||
221 | irqreturn_t mac_debug_handler(int, void *, struct pt_regs *); | ||
222 | |||
223 | /* #define DEBUG_MACINTS */ | ||
224 | |||
225 | void mac_init_IRQ(void) | ||
226 | { | ||
227 | int i; | ||
228 | |||
229 | #ifdef DEBUG_MACINTS | ||
230 | printk("mac_init_IRQ(): Setting things up...\n"); | ||
231 | #endif | ||
232 | /* Initialize the IRQ handler lists. Initially each list is empty, */ | ||
233 | |||
234 | for (i = 0; i < NUM_MAC_SOURCES; i++) { | ||
235 | mac_irq_list[i] = NULL; | ||
236 | } | ||
237 | |||
238 | scc_mask = 0; | ||
239 | |||
240 | /* Make sure the SONIC interrupt is cleared or things get ugly */ | ||
241 | #ifdef SHUTUP_SONIC | ||
242 | printk("Killing onboard sonic... "); | ||
243 | /* This address should hopefully be mapped already */ | ||
244 | if (hwreg_present((void*)(0x50f0a000))) { | ||
245 | *(long *)(0x50f0a014) = 0x7fffL; | ||
246 | *(long *)(0x50f0a010) = 0L; | ||
247 | } | ||
248 | printk("Done.\n"); | ||
249 | #endif /* SHUTUP_SONIC */ | ||
250 | |||
251 | /* | ||
252 | * Now register the handlers for the master IRQ handlers | ||
253 | * at levels 1-7. Most of the work is done elsewhere. | ||
254 | */ | ||
255 | |||
256 | if (oss_present) { | ||
257 | oss_register_interrupts(); | ||
258 | } else { | ||
259 | via_register_interrupts(); | ||
260 | } | ||
261 | if (psc_present) psc_register_interrupts(); | ||
262 | if (baboon_present) baboon_register_interrupts(); | ||
263 | iop_register_interrupts(); | ||
264 | cpu_request_irq(7, mac_nmi_handler, IRQ_FLG_LOCK, "NMI", | ||
265 | mac_nmi_handler); | ||
266 | #ifdef DEBUG_MACINTS | ||
267 | printk("mac_init_IRQ(): Done!\n"); | ||
268 | #endif | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * Routines to work with irq_node_t's on linked lists lifted from | ||
273 | * the Amiga code written by Roman Zippel. | ||
274 | */ | ||
275 | |||
276 | static inline void mac_insert_irq(irq_node_t **list, irq_node_t *node) | ||
277 | { | ||
278 | unsigned long flags; | ||
279 | irq_node_t *cur; | ||
280 | |||
281 | if (!node->dev_id) | ||
282 | printk("%s: Warning: dev_id of %s is zero\n", | ||
283 | __FUNCTION__, node->devname); | ||
284 | |||
285 | local_irq_save(flags); | ||
286 | |||
287 | cur = *list; | ||
288 | |||
289 | if (node->flags & IRQ_FLG_FAST) { | ||
290 | node->flags &= ~IRQ_FLG_SLOW; | ||
291 | while (cur && cur->flags & IRQ_FLG_FAST) { | ||
292 | list = &cur->next; | ||
293 | cur = cur->next; | ||
294 | } | ||
295 | } else if (node->flags & IRQ_FLG_SLOW) { | ||
296 | while (cur) { | ||
297 | list = &cur->next; | ||
298 | cur = cur->next; | ||
299 | } | ||
300 | } else { | ||
301 | while (cur && !(cur->flags & IRQ_FLG_SLOW)) { | ||
302 | list = &cur->next; | ||
303 | cur = cur->next; | ||
304 | } | ||
305 | } | ||
306 | |||
307 | node->next = cur; | ||
308 | *list = node; | ||
309 | |||
310 | local_irq_restore(flags); | ||
311 | } | ||
312 | |||
313 | static inline void mac_delete_irq(irq_node_t **list, void *dev_id) | ||
314 | { | ||
315 | unsigned long flags; | ||
316 | irq_node_t *node; | ||
317 | |||
318 | local_irq_save(flags); | ||
319 | |||
320 | for (node = *list; node; list = &node->next, node = *list) { | ||
321 | if (node->dev_id == dev_id) { | ||
322 | *list = node->next; | ||
323 | /* Mark it as free. */ | ||
324 | node->handler = NULL; | ||
325 | local_irq_restore(flags); | ||
326 | return; | ||
327 | } | ||
328 | } | ||
329 | local_irq_restore(flags); | ||
330 | printk ("%s: tried to remove invalid irq\n", __FUNCTION__); | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * Call all the handlers for a given interrupt. Fast handlers are called | ||
335 | * first followed by slow handlers. | ||
336 | * | ||
337 | * This code taken from the original Amiga code written by Roman Zippel. | ||
338 | */ | ||
339 | |||
340 | void mac_do_irq_list(int irq, struct pt_regs *fp) | ||
341 | { | ||
342 | irq_node_t *node, *slow_nodes; | ||
343 | unsigned long flags; | ||
344 | |||
345 | kstat_cpu(0).irqs[irq]++; | ||
346 | |||
347 | #ifdef DEBUG_SPURIOUS | ||
348 | if (!mac_irq_list[irq] && (console_loglevel > 7)) { | ||
349 | printk("mac_do_irq_list: spurious interrupt %d!\n", irq); | ||
350 | return; | ||
351 | } | ||
352 | #endif | ||
353 | |||
354 | /* serve first fast and normal handlers */ | ||
355 | for (node = mac_irq_list[irq]; | ||
356 | node && (!(node->flags & IRQ_FLG_SLOW)); | ||
357 | node = node->next) | ||
358 | node->handler(irq, node->dev_id, fp); | ||
359 | if (!node) return; | ||
360 | local_save_flags(flags); | ||
361 | local_irq_restore((flags & ~0x0700) | (fp->sr & 0x0700)); | ||
362 | /* if slow handlers exists, serve them now */ | ||
363 | slow_nodes = node; | ||
364 | for (; node; node = node->next) { | ||
365 | node->handler(irq, node->dev_id, fp); | ||
366 | } | ||
367 | } | ||
368 | |||
369 | /* | ||
370 | * mac_enable_irq - enable an interrupt source | ||
371 | * mac_disable_irq - disable an interrupt source | ||
372 | * mac_clear_irq - clears a pending interrupt | ||
373 | * mac_pending_irq - Returns the pending status of an IRQ (nonzero = pending) | ||
374 | * | ||
375 | * These routines are just dispatchers to the VIA/OSS/PSC routines. | ||
376 | */ | ||
377 | |||
378 | void mac_enable_irq (unsigned int irq) | ||
379 | { | ||
380 | int irq_src = IRQ_SRC(irq); | ||
381 | |||
382 | switch(irq_src) { | ||
383 | case 1: via_irq_enable(irq); | ||
384 | break; | ||
385 | case 2: | ||
386 | case 7: if (oss_present) { | ||
387 | oss_irq_enable(irq); | ||
388 | } else { | ||
389 | via_irq_enable(irq); | ||
390 | } | ||
391 | break; | ||
392 | case 3: | ||
393 | case 4: | ||
394 | case 5: | ||
395 | case 6: if (psc_present) { | ||
396 | psc_irq_enable(irq); | ||
397 | } else if (oss_present) { | ||
398 | oss_irq_enable(irq); | ||
399 | } else if (irq_src == 4) { | ||
400 | scc_irq_enable(irq); | ||
401 | } | ||
402 | break; | ||
403 | case 8: if (baboon_present) { | ||
404 | baboon_irq_enable(irq); | ||
405 | } | ||
406 | break; | ||
407 | } | ||
408 | } | ||
409 | |||
410 | void mac_disable_irq (unsigned int irq) | ||
411 | { | ||
412 | int irq_src = IRQ_SRC(irq); | ||
413 | |||
414 | switch(irq_src) { | ||
415 | case 1: via_irq_disable(irq); | ||
416 | break; | ||
417 | case 2: | ||
418 | case 7: if (oss_present) { | ||
419 | oss_irq_disable(irq); | ||
420 | } else { | ||
421 | via_irq_disable(irq); | ||
422 | } | ||
423 | break; | ||
424 | case 3: | ||
425 | case 4: | ||
426 | case 5: | ||
427 | case 6: if (psc_present) { | ||
428 | psc_irq_disable(irq); | ||
429 | } else if (oss_present) { | ||
430 | oss_irq_disable(irq); | ||
431 | } else if (irq_src == 4) { | ||
432 | scc_irq_disable(irq); | ||
433 | } | ||
434 | break; | ||
435 | case 8: if (baboon_present) { | ||
436 | baboon_irq_disable(irq); | ||
437 | } | ||
438 | break; | ||
439 | } | ||
440 | } | ||
441 | |||
442 | void mac_clear_irq( unsigned int irq ) | ||
443 | { | ||
444 | switch(IRQ_SRC(irq)) { | ||
445 | case 1: via_irq_clear(irq); | ||
446 | break; | ||
447 | case 2: | ||
448 | case 7: if (oss_present) { | ||
449 | oss_irq_clear(irq); | ||
450 | } else { | ||
451 | via_irq_clear(irq); | ||
452 | } | ||
453 | break; | ||
454 | case 3: | ||
455 | case 4: | ||
456 | case 5: | ||
457 | case 6: if (psc_present) { | ||
458 | psc_irq_clear(irq); | ||
459 | } else if (oss_present) { | ||
460 | oss_irq_clear(irq); | ||
461 | } | ||
462 | break; | ||
463 | case 8: if (baboon_present) { | ||
464 | baboon_irq_clear(irq); | ||
465 | } | ||
466 | break; | ||
467 | } | ||
468 | } | ||
469 | |||
470 | int mac_irq_pending( unsigned int irq ) | ||
471 | { | ||
472 | switch(IRQ_SRC(irq)) { | ||
473 | case 1: return via_irq_pending(irq); | ||
474 | case 2: | ||
475 | case 7: if (oss_present) { | ||
476 | return oss_irq_pending(irq); | ||
477 | } else { | ||
478 | return via_irq_pending(irq); | ||
479 | } | ||
480 | case 3: | ||
481 | case 4: | ||
482 | case 5: | ||
483 | case 6: if (psc_present) { | ||
484 | return psc_irq_pending(irq); | ||
485 | } else if (oss_present) { | ||
486 | return oss_irq_pending(irq); | ||
487 | } | ||
488 | } | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | /* | ||
493 | * Add an interrupt service routine to an interrupt source. | ||
494 | * Returns 0 on success. | ||
495 | * | ||
496 | * FIXME: You can register interrupts on nonexistent source (ie PSC4 on a | ||
497 | * non-PSC machine). We should return -EINVAL in those cases. | ||
498 | */ | ||
499 | |||
500 | int mac_request_irq(unsigned int irq, | ||
501 | irqreturn_t (*handler)(int, void *, struct pt_regs *), | ||
502 | unsigned long flags, const char *devname, void *dev_id) | ||
503 | { | ||
504 | irq_node_t *node; | ||
505 | |||
506 | #ifdef DEBUG_MACINTS | ||
507 | printk ("%s: irq %d requested for %s\n", __FUNCTION__, irq, devname); | ||
508 | #endif | ||
509 | |||
510 | if (irq < VIA1_SOURCE_BASE) { | ||
511 | return cpu_request_irq(irq, handler, flags, devname, dev_id); | ||
512 | } | ||
513 | |||
514 | if (irq >= NUM_MAC_SOURCES) { | ||
515 | printk ("%s: unknown irq %d requested by %s\n", | ||
516 | __FUNCTION__, irq, devname); | ||
517 | } | ||
518 | |||
519 | /* Get a node and stick it onto the right list */ | ||
520 | |||
521 | if (!(node = new_irq_node())) return -ENOMEM; | ||
522 | |||
523 | node->handler = handler; | ||
524 | node->flags = flags; | ||
525 | node->dev_id = dev_id; | ||
526 | node->devname = devname; | ||
527 | node->next = NULL; | ||
528 | mac_insert_irq(&mac_irq_list[irq], node); | ||
529 | |||
530 | /* Now enable the IRQ source */ | ||
531 | |||
532 | mac_enable_irq(irq); | ||
533 | |||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | /* | ||
538 | * Removes an interrupt service routine from an interrupt source. | ||
539 | */ | ||
540 | |||
541 | void mac_free_irq(unsigned int irq, void *dev_id) | ||
542 | { | ||
543 | #ifdef DEBUG_MACINTS | ||
544 | printk ("%s: irq %d freed by %p\n", __FUNCTION__, irq, dev_id); | ||
545 | #endif | ||
546 | |||
547 | if (irq < VIA1_SOURCE_BASE) { | ||
548 | cpu_free_irq(irq, dev_id); | ||
549 | return; | ||
550 | } | ||
551 | |||
552 | if (irq >= NUM_MAC_SOURCES) { | ||
553 | printk ("%s: unknown irq %d freed\n", | ||
554 | __FUNCTION__, irq); | ||
555 | return; | ||
556 | } | ||
557 | |||
558 | mac_delete_irq(&mac_irq_list[irq], dev_id); | ||
559 | |||
560 | /* If the list for this interrupt is */ | ||
561 | /* empty then disable the source. */ | ||
562 | |||
563 | if (!mac_irq_list[irq]) { | ||
564 | mac_disable_irq(irq); | ||
565 | } | ||
566 | } | ||
567 | |||
568 | /* | ||
569 | * Generate a pretty listing for /proc/interrupts | ||
570 | * | ||
571 | * By the time we're called the autovector interrupt list has already been | ||
572 | * generated, so we just need to do the machspec interrupts. | ||
573 | * | ||
574 | * 990506 (jmt) - rewritten to handle chained machspec interrupt handlers. | ||
575 | * Also removed display of num_spurious it is already | ||
576 | * displayed for us as autovector irq 0. | ||
577 | */ | ||
578 | |||
579 | int show_mac_interrupts(struct seq_file *p, void *v) | ||
580 | { | ||
581 | int i; | ||
582 | irq_node_t *node; | ||
583 | char *base; | ||
584 | |||
585 | /* Don't do Nubus interrupts in this loop; we do them separately */ | ||
586 | /* below so that we can print slot numbers instead of IRQ numbers */ | ||
587 | |||
588 | for (i = VIA1_SOURCE_BASE ; i < NUM_MAC_SOURCES ; ++i) { | ||
589 | |||
590 | /* Nonexistant interrupt or nothing registered; skip it. */ | ||
591 | |||
592 | if ((node = mac_irq_list[i]) == NULL) continue; | ||
593 | if (node->flags & IRQ_FLG_STD) continue; | ||
594 | |||
595 | base = ""; | ||
596 | switch(IRQ_SRC(i)) { | ||
597 | case 1: base = "via1"; | ||
598 | break; | ||
599 | case 2: if (oss_present) { | ||
600 | base = "oss"; | ||
601 | } else { | ||
602 | base = "via2"; | ||
603 | } | ||
604 | break; | ||
605 | case 3: | ||
606 | case 4: | ||
607 | case 5: | ||
608 | case 6: if (psc_present) { | ||
609 | base = "psc"; | ||
610 | } else if (oss_present) { | ||
611 | base = "oss"; | ||
612 | } else { | ||
613 | if (IRQ_SRC(i) == 4) base = "scc"; | ||
614 | } | ||
615 | break; | ||
616 | case 7: base = "nbus"; | ||
617 | break; | ||
618 | case 8: base = "bbn"; | ||
619 | break; | ||
620 | } | ||
621 | seq_printf(p, "%4s %2d: %10u ", base, i, kstat_cpu(0).irqs[i]); | ||
622 | |||
623 | do { | ||
624 | if (node->flags & IRQ_FLG_FAST) { | ||
625 | seq_puts(p, "F "); | ||
626 | } else if (node->flags & IRQ_FLG_SLOW) { | ||
627 | seq_puts(p, "S "); | ||
628 | } else { | ||
629 | seq_puts(p, " "); | ||
630 | } | ||
631 | seq_printf(p, "%s\n", node->devname); | ||
632 | if ((node = node->next)) { | ||
633 | seq_puts(p, " "); | ||
634 | } | ||
635 | } while(node); | ||
636 | |||
637 | } | ||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | void mac_default_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
642 | { | ||
643 | #ifdef DEBUG_SPURIOUS | ||
644 | printk("Unexpected IRQ %d on device %p\n", irq, dev_id); | ||
645 | #endif | ||
646 | } | ||
647 | |||
648 | static int num_debug[8]; | ||
649 | |||
650 | irqreturn_t mac_debug_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
651 | { | ||
652 | if (num_debug[irq] < 10) { | ||
653 | printk("DEBUG: Unexpected IRQ %d\n", irq); | ||
654 | num_debug[irq]++; | ||
655 | } | ||
656 | return IRQ_HANDLED; | ||
657 | } | ||
658 | |||
659 | static int in_nmi; | ||
660 | static volatile int nmi_hold; | ||
661 | |||
662 | irqreturn_t mac_nmi_handler(int irq, void *dev_id, struct pt_regs *fp) | ||
663 | { | ||
664 | int i; | ||
665 | /* | ||
666 | * generate debug output on NMI switch if 'debug' kernel option given | ||
667 | * (only works with Penguin!) | ||
668 | */ | ||
669 | |||
670 | in_nmi++; | ||
671 | for (i=0; i<100; i++) | ||
672 | udelay(1000); | ||
673 | |||
674 | if (in_nmi == 1) { | ||
675 | nmi_hold = 1; | ||
676 | printk("... pausing, press NMI to resume ..."); | ||
677 | } else { | ||
678 | printk(" ok!\n"); | ||
679 | nmi_hold = 0; | ||
680 | } | ||
681 | |||
682 | barrier(); | ||
683 | |||
684 | while (nmi_hold == 1) | ||
685 | udelay(1000); | ||
686 | |||
687 | if ( console_loglevel >= 8 ) { | ||
688 | #if 0 | ||
689 | show_state(); | ||
690 | printk("PC: %08lx\nSR: %04x SP: %p\n", fp->pc, fp->sr, fp); | ||
691 | printk("d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", | ||
692 | fp->d0, fp->d1, fp->d2, fp->d3); | ||
693 | printk("d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n", | ||
694 | fp->d4, fp->d5, fp->a0, fp->a1); | ||
695 | |||
696 | if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page) | ||
697 | printk("Corrupted stack page\n"); | ||
698 | printk("Process %s (pid: %d, stackpage=%08lx)\n", | ||
699 | current->comm, current->pid, current->kernel_stack_page); | ||
700 | if (intr_count == 1) | ||
701 | dump_stack((struct frame *)fp); | ||
702 | #else | ||
703 | /* printk("NMI "); */ | ||
704 | #endif | ||
705 | } | ||
706 | in_nmi--; | ||
707 | return IRQ_HANDLED; | ||
708 | } | ||
709 | |||
710 | /* | ||
711 | * Simple routines for masking and unmasking | ||
712 | * SCC interrupts in cases where this can't be | ||
713 | * done in hardware (only the PSC can do that.) | ||
714 | */ | ||
715 | |||
716 | static void scc_irq_enable(int irq) { | ||
717 | int irq_idx = IRQ_IDX(irq); | ||
718 | |||
719 | scc_mask |= (1 << irq_idx); | ||
720 | } | ||
721 | |||
722 | static void scc_irq_disable(int irq) { | ||
723 | int irq_idx = IRQ_IDX(irq); | ||
724 | |||
725 | scc_mask &= ~(1 << irq_idx); | ||
726 | } | ||
727 | |||
728 | /* | ||
729 | * SCC master interrupt handler. We have to do a bit of magic here | ||
730 | * to figure out what channel gave us the interrupt; putting this | ||
731 | * here is cleaner than hacking it into drivers/char/macserial.c. | ||
732 | */ | ||
733 | |||
734 | void mac_scc_dispatch(int irq, void *dev_id, struct pt_regs *regs) | ||
735 | { | ||
736 | volatile unsigned char *scc = (unsigned char *) mac_bi_data.sccbase + 2; | ||
737 | unsigned char reg; | ||
738 | unsigned long flags; | ||
739 | |||
740 | /* Read RR3 from the chip. Always do this on channel A */ | ||
741 | /* This must be an atomic operation so disable irqs. */ | ||
742 | |||
743 | local_irq_save(flags); | ||
744 | *scc = 3; | ||
745 | reg = *scc; | ||
746 | local_irq_restore(flags); | ||
747 | |||
748 | /* Now dispatch. Bits 0-2 are for channel B and */ | ||
749 | /* bits 3-5 are for channel A. We can safely */ | ||
750 | /* ignore the remaining bits here. */ | ||
751 | /* */ | ||
752 | /* Note that we're ignoring scc_mask for now. */ | ||
753 | /* If we actually mask the ints then we tend to */ | ||
754 | /* get hammered by very persistent SCC irqs, */ | ||
755 | /* and since they're autovector interrupts they */ | ||
756 | /* pretty much kill the system. */ | ||
757 | |||
758 | if (reg & 0x38) mac_do_irq_list(IRQ_SCCA, regs); | ||
759 | if (reg & 0x07) mac_do_irq_list(IRQ_SCCB, regs); | ||
760 | } | ||