1 /* Rename a few functions. */
2 #define amiga_request_irq request_irq
3 #define amiga_free_irq free_irq
6 * linux/arch/m68k/amiga/amiints.c -- Amiga Linux interrupt handling code
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file COPYING in the main directory of this archive
12 * 11/07/96: rewritten interrupt handling, irq lists are exists now only for
13 * this sources where it makes sense (VERTB/PORTS/EXTER) and you must
14 * be careful that dev_id for this sources is unique since this the
15 * only possibility to distinguish between different handlers for
16 * free_irq. irq lists also have different irq flags:
17 * - IRQ_FLG_FAST: handler is inserted at top of list (after other
19 * - IRQ_FLG_SLOW: handler is inserted at bottom of list and before
20 * they're executed irq level is set to the previous
21 * one, but handlers don't need to be reentrant, if
22 * reentrance occurred, slow handlers will be just
24 * The whole interrupt handling for CIAs is moved to cia.c
28 #include <linux/config.h>
29 #include <linux/types.h>
30 #include <linux/kernel.h>
31 #include <linux/sched.h>
32 #include <linux/kernel_stat.h>
33 #include <linux/init.h>
35 #include <asm/system.h>
37 #include <asm/traps.h>
38 #include <asm/amigahw.h>
39 #include <asm/amigaints.h>
40 #include <asm/amipcmcia.h>
43 #include <asm/amigappc.h>
46 extern int cia_request_irq(struct ciabase
*base
,int irq
,
47 void (*handler
)(int, void *, struct pt_regs
*),
48 unsigned long flags
, const char *devname
, void *dev_id
);
49 extern void cia_free_irq(struct ciabase
*base
, unsigned int irq
, void *dev_id
);
50 extern void cia_init_IRQ(struct ciabase
*base
);
51 extern int cia_get_irq_list(struct ciabase
*base
, char *buf
);
53 /* irq node variables for amiga interrupt sources */
54 static irq_node_t
*ami_irq_list
[AMI_STD_IRQS
];
56 unsigned short ami_intena_vals
[AMI_STD_IRQS
] = {
57 IF_VERTB
, IF_COPER
, IF_AUD0
, IF_AUD1
, IF_AUD2
, IF_AUD3
, IF_BLIT
,
58 IF_DSKSYN
, IF_DSKBLK
, IF_RBF
, IF_TBE
, IF_SOFT
, IF_PORTS
, IF_EXTER
60 static const unsigned char ami_servers
[AMI_STD_IRQS
] = {
61 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
64 static short ami_ablecount
[AMI_IRQS
];
66 static void ami_badint(int irq
, void *dev_id
, struct pt_regs
*fp
)
72 * void amiga_init_IRQ(void)
78 * This function should be called during kernel startup to initialize
79 * the amiga IRQ handling routines.
82 __initfunc(void amiga_init_IRQ(void))
86 /* initialize handlers */
87 for (i
= 0; i
< AMI_STD_IRQS
; i
++) {
89 ami_irq_list
[i
] = NULL
;
91 ami_irq_list
[i
] = new_irq_node();
92 ami_irq_list
[i
]->handler
= ami_badint
;
93 ami_irq_list
[i
]->flags
= IRQ_FLG_STD
;
94 ami_irq_list
[i
]->dev_id
= NULL
;
95 ami_irq_list
[i
]->devname
= NULL
;
96 ami_irq_list
[i
]->next
= NULL
;
99 for (i
= 0; i
< AMI_IRQS
; i
++)
100 ami_ablecount
[i
] = 0;
102 /* turn off PCMCIA interrupts */
103 if (AMIGAHW_PRESENT(PCMCIA
))
104 pcmcia_disable_irq();
106 /* turn off all interrupts... */
107 custom
.intena
= 0x7fff;
108 custom
.intreq
= 0x7fff;
111 /* Clear any inter-CPU interupt requests. Circumvents bug in
112 Blizzard IPL emulation HW (or so it appears). */
113 APUS_WRITE(APUS_INT_LVL
, INTLVL_SETRESET
| INTLVL_MASK
);
115 /* Init IPL emulation. */
116 APUS_WRITE(APUS_REG_INT
, REGINT_INTMASTER
| REGINT_ENABLEIPL
);
117 APUS_WRITE(APUS_IPL_EMU
, IPLEMU_DISABLEINT
);
118 APUS_WRITE(APUS_IPL_EMU
, IPLEMU_SETRESET
| IPLEMU_IPLMASK
);
120 /* ... and enable the master interrupt bit */
121 custom
.intena
= IF_SETCLR
| IF_INTEN
;
123 cia_init_IRQ(&ciaa_base
);
124 cia_init_IRQ(&ciab_base
);
127 static inline void amiga_insert_irq(irq_node_t
**list
, irq_node_t
*node
)
133 printk("%s: Warning: dev_id of %s is zero\n",
134 __FUNCTION__
, node
->devname
);
141 if (node
->flags
& IRQ_FLG_FAST
) {
142 node
->flags
&= ~IRQ_FLG_SLOW
;
143 while (cur
&& cur
->flags
& IRQ_FLG_FAST
) {
147 } else if (node
->flags
& IRQ_FLG_SLOW
) {
153 while (cur
&& !(cur
->flags
& IRQ_FLG_SLOW
)) {
162 restore_flags(flags
);
165 static inline void amiga_delete_irq(irq_node_t
**list
, void *dev_id
)
173 for (node
= *list
; node
; list
= &node
->next
, node
= *list
) {
174 if (node
->dev_id
== dev_id
) {
176 /* Mark it as free. */
177 node
->handler
= NULL
;
178 restore_flags(flags
);
182 restore_flags(flags
);
183 printk ("%s: tried to remove invalid irq\n", __FUNCTION__
);
187 * amiga_request_irq : add an interrupt service routine for a particular
188 * machine specific interrupt source.
189 * If the addition was successful, it returns 0.
192 int amiga_request_irq(unsigned int irq
, void (*handler
)(int, void *, struct pt_regs
*),
193 unsigned long flags
, const char *devname
, void *dev_id
)
197 if (irq
>= AMI_IRQS
) {
198 printk ("%s: Unknown IRQ %d from %s\n", __FUNCTION__
, irq
, devname
);
202 if (irq
>= IRQ_AMIGA_AUTO
)
203 return sys_request_irq(irq
- IRQ_AMIGA_AUTO
, handler
,
204 flags
, devname
, dev_id
);
206 if (irq
>= IRQ_AMIGA_CIAB
)
207 return cia_request_irq(&ciab_base
, irq
- IRQ_AMIGA_CIAB
,
208 handler
, flags
, devname
, dev_id
);
210 if (irq
>= IRQ_AMIGA_CIAA
)
211 return cia_request_irq(&ciaa_base
, irq
- IRQ_AMIGA_CIAA
,
212 handler
, flags
, devname
, dev_id
);
214 if (ami_servers
[irq
]) {
215 if (!(node
= new_irq_node()))
217 node
->handler
= handler
;
219 node
->dev_id
= dev_id
;
220 node
->devname
= devname
;
222 amiga_insert_irq(&ami_irq_list
[irq
], node
);
224 if (!(ami_irq_list
[irq
]->flags
& IRQ_FLG_STD
)) {
225 if (ami_irq_list
[irq
]->flags
& IRQ_FLG_LOCK
) {
226 printk("%s: IRQ %d from %s is not replaceable\n",
227 __FUNCTION__
, irq
, ami_irq_list
[irq
]->devname
);
230 if (!(flags
& IRQ_FLG_REPLACE
)) {
231 printk("%s: %s can't replace IRQ %d from %s\n",
232 __FUNCTION__
, devname
, irq
, ami_irq_list
[irq
]->devname
);
236 ami_irq_list
[irq
]->handler
= handler
;
237 ami_irq_list
[irq
]->flags
= flags
;
238 ami_irq_list
[irq
]->dev_id
= dev_id
;
239 ami_irq_list
[irq
]->devname
= devname
;
242 /* enable the interrupt */
243 if (irq
< IRQ_AMIGA_PORTS
&& !ami_ablecount
[irq
])
244 custom
.intena
= IF_SETCLR
| ami_intena_vals
[irq
];
249 void amiga_free_irq(unsigned int irq
, void *dev_id
)
251 if (irq
>= AMI_IRQS
) {
252 printk ("%s: Unknown IRQ %d\n", __FUNCTION__
, irq
);
256 if (irq
>= IRQ_AMIGA_AUTO
)
257 sys_free_irq(irq
- IRQ_AMIGA_AUTO
, dev_id
);
259 if (irq
>= IRQ_AMIGA_CIAB
) {
260 cia_free_irq(&ciab_base
, irq
- IRQ_AMIGA_CIAB
, dev_id
);
264 if (irq
>= IRQ_AMIGA_CIAA
) {
265 cia_free_irq(&ciaa_base
, irq
- IRQ_AMIGA_CIAA
, dev_id
);
269 if (ami_servers
[irq
]) {
270 amiga_delete_irq(&ami_irq_list
[irq
], dev_id
);
271 /* if server list empty, disable the interrupt */
272 if (!ami_irq_list
[irq
] && irq
< IRQ_AMIGA_PORTS
)
273 custom
.intena
= ami_intena_vals
[irq
];
275 if (ami_irq_list
[irq
]->dev_id
!= dev_id
)
276 printk("%s: removing probably wrong IRQ %d from %s\n",
277 __FUNCTION__
, irq
, ami_irq_list
[irq
]->devname
);
278 ami_irq_list
[irq
]->handler
= ami_badint
;
279 ami_irq_list
[irq
]->flags
= IRQ_FLG_STD
;
280 ami_irq_list
[irq
]->dev_id
= NULL
;
281 ami_irq_list
[irq
]->devname
= NULL
;
282 custom
.intena
= ami_intena_vals
[irq
];
287 * Enable/disable a particular machine specific interrupt source.
288 * Note that this may affect other interrupts in case of a shared interrupt.
289 * This function should only be called for a _very_ short time to change some
290 * internal data, that may not be changed by the interrupt at the same time.
291 * ami_(enable|disable)_irq calls may also be nested.
294 void amiga_enable_irq(unsigned int irq
)
296 if (irq
>= AMI_IRQS
) {
297 printk("%s: Unknown IRQ %d\n", __FUNCTION__
, irq
);
301 if (--ami_ablecount
[irq
])
304 /* No action for auto-vector interrupts */
305 if (irq
>= IRQ_AMIGA_AUTO
){
306 printk("%s: Trying to enable auto-vector IRQ %i\n",
307 __FUNCTION__
, irq
- IRQ_AMIGA_AUTO
);
311 if (irq
>= IRQ_AMIGA_CIAB
) {
312 cia_set_irq(&ciab_base
, (1 << (irq
- IRQ_AMIGA_CIAB
)));
313 cia_able_irq(&ciab_base
, CIA_ICR_SETCLR
|
314 (1 << (irq
- IRQ_AMIGA_CIAB
)));
318 if (irq
>= IRQ_AMIGA_CIAA
) {
319 cia_set_irq(&ciaa_base
, (1 << (irq
- IRQ_AMIGA_CIAA
)));
320 cia_able_irq(&ciaa_base
, CIA_ICR_SETCLR
|
321 (1 << (irq
- IRQ_AMIGA_CIAA
)));
325 /* enable the interrupt */
326 custom
.intena
= IF_SETCLR
| ami_intena_vals
[irq
];
329 void amiga_disable_irq(unsigned int irq
)
331 if (irq
>= AMI_IRQS
) {
332 printk("%s: Unknown IRQ %d\n", __FUNCTION__
, irq
);
336 if (ami_ablecount
[irq
]++)
339 /* No action for auto-vector interrupts */
340 if (irq
>= IRQ_AMIGA_AUTO
) {
341 printk("%s: Trying to disable auto-vector IRQ %i\n",
342 __FUNCTION__
, irq
- IRQ_AMIGA_AUTO
);
346 if (irq
>= IRQ_AMIGA_CIAB
) {
347 cia_able_irq(&ciab_base
, 1 << (irq
- IRQ_AMIGA_CIAB
));
351 if (irq
>= IRQ_AMIGA_CIAA
) {
352 cia_able_irq(&ciaa_base
, 1 << (irq
- IRQ_AMIGA_CIAA
));
356 /* disable the interrupt */
357 custom
.intena
= ami_intena_vals
[irq
];
360 inline void amiga_do_irq(int irq
, struct pt_regs
*fp
)
362 kstat
.irqs
[0][SYS_IRQS
+ irq
]++;
363 ami_irq_list
[irq
]->handler(irq
, ami_irq_list
[irq
]->dev_id
, fp
);
366 void amiga_do_irq_list(int irq
, struct pt_regs
*fp
, struct irq_server
*server
)
368 irq_node_t
*node
, *slow_nodes
;
369 unsigned short flags
;
371 kstat
.irqs
[0][SYS_IRQS
+ irq
]++;
373 server
->reentrance
= 1;
374 /* serve first fast and normal handlers */
375 for (node
= ami_irq_list
[irq
];
376 node
&& (!(node
->flags
& IRQ_FLG_SLOW
));
378 node
->handler(irq
, node
->dev_id
, fp
);
379 custom
.intreq
= ami_intena_vals
[irq
];
385 APUS_WRITE(APUS_IPL_EMU
, IPLEMU_SETRESET
| IPLEMU_DISABLEINT
);
386 APUS_WRITE(APUS_IPL_EMU
, IPLEMU_IPLMASK
);
387 APUS_WRITE(APUS_IPL_EMU
, (IPLEMU_SETRESET
388 | (~(fp
->mq
) & IPLEMU_IPLMASK
)));
389 APUS_WRITE(APUS_IPL_EMU
, IPLEMU_DISABLEINT
);
392 restore_flags((flags
& ~0x0700) | (fp
->sr
& 0x0700));
394 /* if slow handlers exists, serve them now */
397 for (; node
; node
= node
->next
)
398 node
->handler(irq
, node
->dev_id
, fp
);
399 /* if reentrance occurred, serve slow handlers again */
400 custom
.intena
= ami_intena_vals
[irq
];
401 if (!server
->reentrance
) {
403 custom
.intena
= IF_SETCLR
| ami_intena_vals
[irq
];
406 server
->reentrance
= 0;
407 custom
.intena
= IF_SETCLR
| ami_intena_vals
[irq
];
413 * The builtin Amiga hardware interrupt handlers.
416 static void ami_int1(int irq
, void *dev_id
, struct pt_regs
*fp
)
418 unsigned short ints
= custom
.intreqr
& custom
.intenar
;
420 /* if serial transmit buffer empty, interrupt */
422 custom
.intreq
= IF_TBE
;
423 amiga_do_irq(IRQ_AMIGA_TBE
, fp
);
426 /* if floppy disk transfer complete, interrupt */
427 if (ints
& IF_DSKBLK
) {
428 custom
.intreq
= IF_DSKBLK
;
429 amiga_do_irq(IRQ_AMIGA_DSKBLK
, fp
);
432 /* if software interrupt set, interrupt */
433 if (ints
& IF_SOFT
) {
434 custom
.intreq
= IF_SOFT
;
435 amiga_do_irq(IRQ_AMIGA_SOFT
, fp
);
439 static void ami_int3(int irq
, void *dev_id
, struct pt_regs
*fp
)
441 unsigned short ints
= custom
.intreqr
& custom
.intenar
;
442 static struct irq_server server
= {0, 0};
444 /* if a blitter interrupt */
445 if (ints
& IF_BLIT
) {
446 custom
.intreq
= IF_BLIT
;
447 amiga_do_irq(IRQ_AMIGA_BLIT
, fp
);
450 /* if a copper interrupt */
451 if (ints
& IF_COPER
) {
452 custom
.intreq
= IF_COPER
;
453 amiga_do_irq(IRQ_AMIGA_COPPER
, fp
);
456 /* if a vertical blank interrupt */
458 amiga_do_irq_list(IRQ_AMIGA_VERTB
, fp
, &server
);
461 static void ami_int4(int irq
, void *dev_id
, struct pt_regs
*fp
)
463 unsigned short ints
= custom
.intreqr
& custom
.intenar
;
465 /* if audio 0 interrupt */
466 if (ints
& IF_AUD0
) {
467 custom
.intreq
= IF_AUD0
;
468 amiga_do_irq(IRQ_AMIGA_AUD0
, fp
);
471 /* if audio 1 interrupt */
472 if (ints
& IF_AUD1
) {
473 custom
.intreq
= IF_AUD1
;
474 amiga_do_irq(IRQ_AMIGA_AUD1
, fp
);
477 /* if audio 2 interrupt */
478 if (ints
& IF_AUD2
) {
479 custom
.intreq
= IF_AUD2
;
480 amiga_do_irq(IRQ_AMIGA_AUD2
, fp
);
483 /* if audio 3 interrupt */
484 if (ints
& IF_AUD3
) {
485 custom
.intreq
= IF_AUD3
;
486 amiga_do_irq(IRQ_AMIGA_AUD3
, fp
);
490 static void ami_int5(int irq
, void *dev_id
, struct pt_regs
*fp
)
492 unsigned short ints
= custom
.intreqr
& custom
.intenar
;
494 /* if serial receive buffer full interrupt */
496 /* acknowledge of IF_RBF must be done by the serial interrupt */
497 amiga_do_irq(IRQ_AMIGA_RBF
, fp
);
500 /* if a disk sync interrupt */
501 if (ints
& IF_DSKSYN
) {
502 custom
.intreq
= IF_DSKSYN
;
503 amiga_do_irq(IRQ_AMIGA_DSKSYN
, fp
);
507 static void ami_int7(int irq
, void *dev_id
, struct pt_regs
*fp
)
509 panic ("level 7 interrupt received\n");
512 void (*amiga_default_handler
[SYS_IRQS
])(int, void *, struct pt_regs
*) = {
513 ami_badint
, ami_int1
, ami_badint
, ami_int3
,
514 ami_int4
, ami_int5
, ami_badint
, ami_int7
517 int amiga_get_irq_list(char *buf
)
522 for (i
= 0; i
< AMI_STD_IRQS
; i
++) {
523 if (!(node
= ami_irq_list
[i
]))
525 if (node
->flags
& IRQ_FLG_STD
)
527 len
+= sprintf(buf
+len
, "ami %2d: %10u ", i
,
528 kstat
.irqs
[0][SYS_IRQS
+ i
]);
530 if (ami_servers
[i
]) {
531 if (node
->flags
& IRQ_FLG_FAST
)
532 len
+= sprintf(buf
+len
, "F ");
533 else if (node
->flags
& IRQ_FLG_SLOW
)
534 len
+= sprintf(buf
+len
, "S ");
536 len
+= sprintf(buf
+len
, " ");
538 if (node
->flags
& IRQ_FLG_LOCK
)
539 len
+= sprintf(buf
+len
, "L ");
541 len
+= sprintf(buf
+len
, " ");
543 len
+= sprintf(buf
+len
, "%s\n", node
->devname
);
544 if ((node
= node
->next
))
545 len
+= sprintf(buf
+len
, " ");
549 len
+= cia_get_irq_list(&ciaa_base
, buf
+len
);
550 len
+= cia_get_irq_list(&ciab_base
, buf
+len
);