| /* |
| * Amiga Linux interrupt handling code |
| * |
| * This file is subject to the terms and conditions of the GNU General Public |
| * License. See the file COPYING in the main directory of this archive |
| * for more details. |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/interrupt.h> |
| #include <linux/errno.h> |
| #include <linux/irq.h> |
| |
| #include <asm/irq.h> |
| #include <asm/traps.h> |
| #include <asm/amigahw.h> |
| #include <asm/amigaints.h> |
| #include <asm/amipcmcia.h> |
| |
| |
| /* |
| * Enable/disable a particular machine specific interrupt source. |
| * Note that this may affect other interrupts in case of a shared interrupt. |
| * This function should only be called for a _very_ short time to change some |
| * internal data, that may not be changed by the interrupt at the same time. |
| */ |
| |
| static void amiga_irq_enable(struct irq_data *data) |
| { |
| amiga_custom.intena = IF_SETCLR | (1 << (data->irq - IRQ_USER)); |
| } |
| |
| static void amiga_irq_disable(struct irq_data *data) |
| { |
| amiga_custom.intena = 1 << (data->irq - IRQ_USER); |
| } |
| |
| static struct irq_chip amiga_irq_chip = { |
| .name = "amiga", |
| .irq_enable = amiga_irq_enable, |
| .irq_disable = amiga_irq_disable, |
| }; |
| |
| |
| /* |
| * The builtin Amiga hardware interrupt handlers. |
| */ |
| |
| static void ami_int1(struct irq_desc *desc) |
| { |
| unsigned short ints = amiga_custom.intreqr & amiga_custom.intenar; |
| |
| /* if serial transmit buffer empty, interrupt */ |
| if (ints & IF_TBE) { |
| amiga_custom.intreq = IF_TBE; |
| generic_handle_irq(IRQ_AMIGA_TBE); |
| } |
| |
| /* if floppy disk transfer complete, interrupt */ |
| if (ints & IF_DSKBLK) { |
| amiga_custom.intreq = IF_DSKBLK; |
| generic_handle_irq(IRQ_AMIGA_DSKBLK); |
| } |
| |
| /* if software interrupt set, interrupt */ |
| if (ints & IF_SOFT) { |
| amiga_custom.intreq = IF_SOFT; |
| generic_handle_irq(IRQ_AMIGA_SOFT); |
| } |
| } |
| |
| static void ami_int3(struct irq_desc *desc) |
| { |
| unsigned short ints = amiga_custom.intreqr & amiga_custom.intenar; |
| |
| /* if a blitter interrupt */ |
| if (ints & IF_BLIT) { |
| amiga_custom.intreq = IF_BLIT; |
| generic_handle_irq(IRQ_AMIGA_BLIT); |
| } |
| |
| /* if a copper interrupt */ |
| if (ints & IF_COPER) { |
| amiga_custom.intreq = IF_COPER; |
| generic_handle_irq(IRQ_AMIGA_COPPER); |
| } |
| |
| /* if a vertical blank interrupt */ |
| if (ints & IF_VERTB) { |
| amiga_custom.intreq = IF_VERTB; |
| generic_handle_irq(IRQ_AMIGA_VERTB); |
| } |
| } |
| |
| static void ami_int4(struct irq_desc *desc) |
| { |
| unsigned short ints = amiga_custom.intreqr & amiga_custom.intenar; |
| |
| /* if audio 0 interrupt */ |
| if (ints & IF_AUD0) { |
| amiga_custom.intreq = IF_AUD0; |
| generic_handle_irq(IRQ_AMIGA_AUD0); |
| } |
| |
| /* if audio 1 interrupt */ |
| if (ints & IF_AUD1) { |
| amiga_custom.intreq = IF_AUD1; |
| generic_handle_irq(IRQ_AMIGA_AUD1); |
| } |
| |
| /* if audio 2 interrupt */ |
| if (ints & IF_AUD2) { |
| amiga_custom.intreq = IF_AUD2; |
| generic_handle_irq(IRQ_AMIGA_AUD2); |
| } |
| |
| /* if audio 3 interrupt */ |
| if (ints & IF_AUD3) { |
| amiga_custom.intreq = IF_AUD3; |
| generic_handle_irq(IRQ_AMIGA_AUD3); |
| } |
| } |
| |
| static void ami_int5(struct irq_desc *desc) |
| { |
| unsigned short ints = amiga_custom.intreqr & amiga_custom.intenar; |
| |
| /* if serial receive buffer full interrupt */ |
| if (ints & IF_RBF) { |
| /* acknowledge of IF_RBF must be done by the serial interrupt */ |
| generic_handle_irq(IRQ_AMIGA_RBF); |
| } |
| |
| /* if a disk sync interrupt */ |
| if (ints & IF_DSKSYN) { |
| amiga_custom.intreq = IF_DSKSYN; |
| generic_handle_irq(IRQ_AMIGA_DSKSYN); |
| } |
| } |
| |
| |
| /* |
| * void amiga_init_IRQ(void) |
| * |
| * Parameters: None |
| * |
| * Returns: Nothing |
| * |
| * This function should be called during kernel startup to initialize |
| * the amiga IRQ handling routines. |
| */ |
| |
| void __init amiga_init_IRQ(void) |
| { |
| m68k_setup_irq_controller(&amiga_irq_chip, handle_simple_irq, IRQ_USER, |
| AMI_STD_IRQS); |
| |
| irq_set_chained_handler(IRQ_AUTO_1, ami_int1); |
| irq_set_chained_handler(IRQ_AUTO_3, ami_int3); |
| irq_set_chained_handler(IRQ_AUTO_4, ami_int4); |
| irq_set_chained_handler(IRQ_AUTO_5, ami_int5); |
| |
| /* turn off PCMCIA interrupts */ |
| if (AMIGAHW_PRESENT(PCMCIA)) |
| gayle.inten = GAYLE_IRQ_IDE; |
| |
| /* turn off all interrupts and enable the master interrupt bit */ |
| amiga_custom.intena = 0x7fff; |
| amiga_custom.intreq = 0x7fff; |
| amiga_custom.intena = IF_SETCLR | IF_INTEN; |
| |
| cia_init_IRQ(&ciaa_base); |
| cia_init_IRQ(&ciab_base); |
| } |