2 * linux/arch/m68k/amiga/amiints.c -- Amiga Linux interrupt handling code
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
8 * 11/07/96: rewritten interrupt handling, irq lists are exists now only for
9 * this sources where it makes sense (VERTB/PORTS/EXTER) and you must
10 * be careful that dev_id for this sources is unique since this the
11 * only possibility to distinguish between different handlers for
12 * free_irq. irq lists also have different irq flags:
13 * - IRQ_FLG_FAST: handler is inserted at top of list (after other
15 * - IRQ_FLG_SLOW: handler is inserted at bottom of list and before
16 * they're executed irq level is set to the previous
17 * one, but handlers don't need to be reentrant, if
18 * reentrance occurred, slow handlers will be just
20 * The whole interrupt handling for CIAs is moved to cia.c
23 * 07/08/99: rewamp of the interrupt handling - we now have two types of
24 * interrupts, normal and fast handlers, fast handlers being
25 * marked with SA_INTERRUPT and runs with all other interrupts
26 * disabled. Normal interrupts disable their own source but
27 * run with all other interrupt sources enabled.
28 * PORTS and EXTER interrupts are always shared even if the
29 * drivers do not explicitly mark this when calling
30 * request_irq which they really should do.
31 * This is similar to the way interrupts are handled on all
32 * other architectures and makes a ton of sense besides
33 * having the advantage of making it easier to share
38 #include <linux/types.h>
39 #include <linux/kernel.h>
40 #include <linux/sched.h>
41 #include <linux/kernel_stat.h>
42 #include <linux/init.h>
44 #include <asm/system.h>
46 #include <asm/traps.h>
47 #include <asm/amigahw.h>
48 #include <asm/amigaints.h>
49 #include <asm/amipcmcia.h>
51 extern int cia_request_irq(struct ciabase
*base
,int irq
,
52 void (*handler
)(int, void *, struct pt_regs
*),
53 unsigned long flags
, const char *devname
, void *dev_id
);
54 extern void cia_free_irq(struct ciabase
*base
, unsigned int irq
, void *dev_id
);
55 extern void cia_init_IRQ(struct ciabase
*base
);
56 extern int cia_get_irq_list(struct ciabase
*base
, char *buf
);
58 /* irq node variables for amiga interrupt sources */
59 static irq_node_t
*ami_irq_list
[AMI_STD_IRQS
];
61 unsigned short ami_intena_vals
[AMI_STD_IRQS
] = {
62 IF_VERTB
, IF_COPER
, IF_AUD0
, IF_AUD1
, IF_AUD2
, IF_AUD3
, IF_BLIT
,
63 IF_DSKSYN
, IF_DSKBLK
, IF_RBF
, IF_TBE
, IF_SOFT
, IF_PORTS
, IF_EXTER
65 static const unsigned char ami_servers
[AMI_STD_IRQS
] = {
66 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
69 static short ami_ablecount
[AMI_IRQS
];
71 static void ami_badint(int irq
, void *dev_id
, struct pt_regs
*fp
)
77 * void amiga_init_IRQ(void)
83 * This function should be called during kernel startup to initialize
84 * the amiga IRQ handling routines.
87 void __init
amiga_init_IRQ(void)
91 /* initialize handlers */
92 for (i
= 0; i
< AMI_STD_IRQS
; i
++) {
94 ami_irq_list
[i
] = NULL
;
96 ami_irq_list
[i
] = new_irq_node();
97 ami_irq_list
[i
]->handler
= ami_badint
;
98 ami_irq_list
[i
]->flags
= 0;
99 ami_irq_list
[i
]->dev_id
= NULL
;
100 ami_irq_list
[i
]->devname
= NULL
;
101 ami_irq_list
[i
]->next
= NULL
;
104 for (i
= 0; i
< AMI_IRQS
; i
++)
105 ami_ablecount
[i
] = 0;
107 /* turn off PCMCIA interrupts */
108 if (AMIGAHW_PRESENT(PCMCIA
))
109 pcmcia_disable_irq();
111 /* turn off all interrupts and enable the master interrupt bit */
112 custom
.intena
= 0x7fff;
113 custom
.intreq
= 0x7fff;
114 custom
.intena
= IF_SETCLR
| IF_INTEN
;
116 cia_init_IRQ(&ciaa_base
);
117 cia_init_IRQ(&ciab_base
);
120 static inline int amiga_insert_irq(irq_node_t
**list
, irq_node_t
*node
)
126 printk("%s: Warning: dev_id of %s is zero\n",
127 __FUNCTION__
, node
->devname
);
134 if (node
->flags
& SA_INTERRUPT
) {
135 if (node
->flags
& SA_SHIRQ
)
138 * There should never be more than one
140 while (cur
&& cur
->flags
& SA_INTERRUPT
) {
154 restore_flags(flags
);
158 static inline void amiga_delete_irq(irq_node_t
**list
, void *dev_id
)
166 for (node
= *list
; node
; list
= &node
->next
, node
= *list
) {
167 if (node
->dev_id
== dev_id
) {
169 /* Mark it as free. */
170 node
->handler
= NULL
;
171 restore_flags(flags
);
175 restore_flags(flags
);
176 printk ("%s: tried to remove invalid irq\n", __FUNCTION__
);
180 * amiga_request_irq : add an interrupt service routine for a particular
181 * machine specific interrupt source.
182 * If the addition was successful, it returns 0.
185 int amiga_request_irq(unsigned int irq
,
186 void (*handler
)(int, void *, struct pt_regs
*),
187 unsigned long flags
, const char *devname
, void *dev_id
)
192 if (irq
>= AMI_IRQS
) {
193 printk ("%s: Unknown IRQ %d from %s\n", __FUNCTION__
,
198 if (irq
>= IRQ_AMIGA_AUTO
)
199 return sys_request_irq(irq
- IRQ_AMIGA_AUTO
, handler
,
200 flags
, devname
, dev_id
);
202 if (irq
>= IRQ_AMIGA_CIAB
)
203 return cia_request_irq(&ciab_base
, irq
- IRQ_AMIGA_CIAB
,
204 handler
, flags
, devname
, dev_id
);
206 if (irq
>= IRQ_AMIGA_CIAA
)
207 return cia_request_irq(&ciaa_base
, irq
- IRQ_AMIGA_CIAA
,
208 handler
, flags
, devname
, dev_id
);
211 * IRQ_AMIGA_PORTS & IRQ_AMIGA_EXTER defaults to shared,
212 * we could add a check here for the SA_SHIRQ flag but all drivers
213 * should be aware of sharing anyway.
215 if (ami_servers
[irq
]) {
216 if (!(node
= new_irq_node()))
218 node
->handler
= handler
;
220 node
->dev_id
= dev_id
;
221 node
->devname
= devname
;
223 error
= amiga_insert_irq(&ami_irq_list
[irq
], node
);
225 ami_irq_list
[irq
]->handler
= handler
;
226 ami_irq_list
[irq
]->flags
= flags
;
227 ami_irq_list
[irq
]->dev_id
= dev_id
;
228 ami_irq_list
[irq
]->devname
= devname
;
231 /* enable the interrupt */
232 if (irq
< IRQ_AMIGA_PORTS
&& !ami_ablecount
[irq
])
233 custom
.intena
= IF_SETCLR
| ami_intena_vals
[irq
];
238 void amiga_free_irq(unsigned int irq
, void *dev_id
)
240 if (irq
>= AMI_IRQS
) {
241 printk ("%s: Unknown IRQ %d\n", __FUNCTION__
, irq
);
245 if (irq
>= IRQ_AMIGA_AUTO
)
246 sys_free_irq(irq
- IRQ_AMIGA_AUTO
, dev_id
);
248 if (irq
>= IRQ_AMIGA_CIAB
) {
249 cia_free_irq(&ciab_base
, irq
- IRQ_AMIGA_CIAB
, dev_id
);
253 if (irq
>= IRQ_AMIGA_CIAA
) {
254 cia_free_irq(&ciaa_base
, irq
- IRQ_AMIGA_CIAA
, dev_id
);
258 if (ami_servers
[irq
]) {
259 amiga_delete_irq(&ami_irq_list
[irq
], dev_id
);
260 /* if server list empty, disable the interrupt */
261 if (!ami_irq_list
[irq
] && irq
< IRQ_AMIGA_PORTS
)
262 custom
.intena
= ami_intena_vals
[irq
];
264 if (ami_irq_list
[irq
]->dev_id
!= dev_id
)
265 printk("%s: removing probably wrong IRQ %d from %s\n",
266 __FUNCTION__
, irq
, ami_irq_list
[irq
]->devname
);
267 ami_irq_list
[irq
]->handler
= ami_badint
;
268 ami_irq_list
[irq
]->flags
= 0;
269 ami_irq_list
[irq
]->dev_id
= NULL
;
270 ami_irq_list
[irq
]->devname
= NULL
;
271 custom
.intena
= ami_intena_vals
[irq
];
276 * Enable/disable a particular machine specific interrupt source.
277 * Note that this may affect other interrupts in case of a shared interrupt.
278 * This function should only be called for a _very_ short time to change some
279 * internal data, that may not be changed by the interrupt at the same time.
280 * ami_(enable|disable)_irq calls may also be nested.
283 void amiga_enable_irq(unsigned int irq
)
285 if (irq
>= AMI_IRQS
) {
286 printk("%s: Unknown IRQ %d\n", __FUNCTION__
, irq
);
290 if (--ami_ablecount
[irq
])
293 /* No action for auto-vector interrupts */
294 if (irq
>= IRQ_AMIGA_AUTO
){
295 printk("%s: Trying to enable auto-vector IRQ %i\n",
296 __FUNCTION__
, irq
- IRQ_AMIGA_AUTO
);
300 if (irq
>= IRQ_AMIGA_CIAB
) {
301 cia_set_irq(&ciab_base
, (1 << (irq
- IRQ_AMIGA_CIAB
)));
302 cia_able_irq(&ciab_base
, CIA_ICR_SETCLR
|
303 (1 << (irq
- IRQ_AMIGA_CIAB
)));
307 if (irq
>= IRQ_AMIGA_CIAA
) {
308 cia_set_irq(&ciaa_base
, (1 << (irq
- IRQ_AMIGA_CIAA
)));
309 cia_able_irq(&ciaa_base
, CIA_ICR_SETCLR
|
310 (1 << (irq
- IRQ_AMIGA_CIAA
)));
314 /* enable the interrupt */
315 custom
.intena
= IF_SETCLR
| ami_intena_vals
[irq
];
318 void amiga_disable_irq(unsigned int irq
)
320 if (irq
>= AMI_IRQS
) {
321 printk("%s: Unknown IRQ %d\n", __FUNCTION__
, irq
);
325 if (ami_ablecount
[irq
]++)
328 /* No action for auto-vector interrupts */
329 if (irq
>= IRQ_AMIGA_AUTO
) {
330 printk("%s: Trying to disable auto-vector IRQ %i\n",
331 __FUNCTION__
, irq
- IRQ_AMIGA_AUTO
);
335 if (irq
>= IRQ_AMIGA_CIAB
) {
336 cia_able_irq(&ciab_base
, 1 << (irq
- IRQ_AMIGA_CIAB
));
340 if (irq
>= IRQ_AMIGA_CIAA
) {
341 cia_able_irq(&ciaa_base
, 1 << (irq
- IRQ_AMIGA_CIAA
));
345 /* disable the interrupt */
346 custom
.intena
= ami_intena_vals
[irq
];
349 inline void amiga_do_irq(int irq
, struct pt_regs
*fp
)
351 kstat
.irqs
[0][SYS_IRQS
+ irq
]++;
352 ami_irq_list
[irq
]->handler(irq
, ami_irq_list
[irq
]->dev_id
, fp
);
355 void amiga_do_irq_list(int irq
, struct pt_regs
*fp
, struct irq_server
*server
)
357 irq_node_t
*node
, *slow_nodes
;
358 unsigned short flags
, intena
;
360 kstat
.irqs
[0][SYS_IRQS
+ irq
]++;
362 server
->reentrance
= 1;
364 intena
= ami_intena_vals
[irq
];
365 custom
.intreq
= intena
;
367 /* serve fast handler if present - there can only be one of these */
368 node
= ami_irq_list
[irq
];
371 * Timer interrupts show up like this
378 if (node
&& (node
->flags
& SA_INTERRUPT
)) {
381 node
->handler(irq
, node
->dev_id
, fp
);
382 restore_flags(flags
);
389 * Disable the interrupt source in question and reenable all
390 * other interrupts. No interrupt handler should ever touch
391 * the intena flags directly!
393 custom
.intena
= intena
;
395 #if 0 /* def CPU_M68060_ONLY */
398 restore_flags((flags
& ~0x0700) | (fp
->sr
& 0x0700));
403 for (; node
; node
= node
->next
)
404 node
->handler(irq
, node
->dev_id
, fp
);
406 if (!server
->reentrance
) {
408 restore_flags(flags
);
409 custom
.intena
= IF_SETCLR
| intena
;
413 server
->reentrance
= 0;
419 * The builtin Amiga hardware interrupt handlers.
422 static void ami_int1(int irq
, void *dev_id
, struct pt_regs
*fp
)
424 unsigned short ints
= custom
.intreqr
& custom
.intenar
;
426 /* if serial transmit buffer empty, interrupt */
428 custom
.intreq
= IF_TBE
;
429 amiga_do_irq(IRQ_AMIGA_TBE
, fp
);
432 /* if floppy disk transfer complete, interrupt */
433 if (ints
& IF_DSKBLK
) {
434 custom
.intreq
= IF_DSKBLK
;
435 amiga_do_irq(IRQ_AMIGA_DSKBLK
, fp
);
438 /* if software interrupt set, interrupt */
439 if (ints
& IF_SOFT
) {
440 custom
.intreq
= IF_SOFT
;
441 amiga_do_irq(IRQ_AMIGA_SOFT
, fp
);
445 static void ami_int3(int irq
, void *dev_id
, struct pt_regs
*fp
)
447 unsigned short ints
= custom
.intreqr
& custom
.intenar
;
448 static struct irq_server server
= {0, 0};
450 /* if a blitter interrupt */
451 if (ints
& IF_BLIT
) {
452 custom
.intreq
= IF_BLIT
;
453 amiga_do_irq(IRQ_AMIGA_BLIT
, fp
);
456 /* if a copper interrupt */
457 if (ints
& IF_COPER
) {
458 custom
.intreq
= IF_COPER
;
459 amiga_do_irq(IRQ_AMIGA_COPPER
, fp
);
462 /* if a vertical blank interrupt */
464 amiga_do_irq_list(IRQ_AMIGA_VERTB
, fp
, &server
);
467 static void ami_int4(int irq
, void *dev_id
, struct pt_regs
*fp
)
469 unsigned short ints
= custom
.intreqr
& custom
.intenar
;
471 /* if audio 0 interrupt */
472 if (ints
& IF_AUD0
) {
473 custom
.intreq
= IF_AUD0
;
474 amiga_do_irq(IRQ_AMIGA_AUD0
, fp
);
477 /* if audio 1 interrupt */
478 if (ints
& IF_AUD1
) {
479 custom
.intreq
= IF_AUD1
;
480 amiga_do_irq(IRQ_AMIGA_AUD1
, fp
);
483 /* if audio 2 interrupt */
484 if (ints
& IF_AUD2
) {
485 custom
.intreq
= IF_AUD2
;
486 amiga_do_irq(IRQ_AMIGA_AUD2
, fp
);
489 /* if audio 3 interrupt */
490 if (ints
& IF_AUD3
) {
491 custom
.intreq
= IF_AUD3
;
492 amiga_do_irq(IRQ_AMIGA_AUD3
, fp
);
496 static void ami_int5(int irq
, void *dev_id
, struct pt_regs
*fp
)
498 unsigned short ints
= custom
.intreqr
& custom
.intenar
;
500 /* if serial receive buffer full interrupt */
502 /* acknowledge of IF_RBF must be done by the serial interrupt */
503 amiga_do_irq(IRQ_AMIGA_RBF
, fp
);
506 /* if a disk sync interrupt */
507 if (ints
& IF_DSKSYN
) {
508 custom
.intreq
= IF_DSKSYN
;
509 amiga_do_irq(IRQ_AMIGA_DSKSYN
, fp
);
513 static void ami_int7(int irq
, void *dev_id
, struct pt_regs
*fp
)
515 panic ("level 7 interrupt received\n");
518 void (*amiga_default_handler
[SYS_IRQS
])(int, void *, struct pt_regs
*) = {
519 ami_badint
, ami_int1
, ami_badint
, ami_int3
,
520 ami_int4
, ami_int5
, ami_badint
, ami_int7
523 int amiga_get_irq_list(char *buf
)
528 for (i
= 0; i
< AMI_STD_IRQS
; i
++) {
529 if (!(node
= ami_irq_list
[i
]))
531 len
+= sprintf(buf
+len
, "ami %2d: %10u ", i
,
532 kstat
.irqs
[0][SYS_IRQS
+ i
]);
534 if (node
->flags
& SA_INTERRUPT
)
535 len
+= sprintf(buf
+len
, "F ");
537 len
+= sprintf(buf
+len
, " ");
538 len
+= sprintf(buf
+len
, "%s\n", node
->devname
);
539 if ((node
= node
->next
))
540 len
+= sprintf(buf
+len
, " ");
544 len
+= cia_get_irq_list(&ciaa_base
, buf
+len
);
545 len
+= cia_get_irq_list(&ciab_base
, buf
+len
);