diff options
Diffstat (limited to 'arch/m68k/amiga/cia.c')
| -rw-r--r-- | arch/m68k/amiga/cia.c | 164 |
1 files changed, 85 insertions, 79 deletions
diff --git a/arch/m68k/amiga/cia.c b/arch/m68k/amiga/cia.c index 9476eb9440f..2081b8cd559 100644 --- a/arch/m68k/amiga/cia.c +++ b/arch/m68k/amiga/cia.c @@ -18,6 +18,7 @@ #include <linux/init.h> #include <linux/seq_file.h> #include <linux/interrupt.h> +#include <linux/irq.h> #include <asm/irq.h> #include <asm/amigahw.h> @@ -29,21 +30,18 @@ struct ciabase { unsigned short int_mask; int handler_irq, cia_irq, server_irq; char *name; - irq_handler_t irq_list[CIA_IRQS]; } ciaa_base = { .cia = &ciaa, .int_mask = IF_PORTS, - .handler_irq = IRQ_AMIGA_AUTO_2, + .handler_irq = IRQ_AMIGA_PORTS, .cia_irq = IRQ_AMIGA_CIAA, - .server_irq = IRQ_AMIGA_PORTS, - .name = "CIAA handler" + .name = "CIAA" }, ciab_base = { .cia = &ciab, .int_mask = IF_EXTER, - .handler_irq = IRQ_AMIGA_AUTO_6, + .handler_irq = IRQ_AMIGA_EXTER, .cia_irq = IRQ_AMIGA_CIAB, - .server_irq = IRQ_AMIGA_EXTER, - .name = "CIAB handler" + .name = "CIAB" }; /* @@ -66,13 +64,11 @@ unsigned char cia_set_irq(struct ciabase *base, unsigned char mask) /* * Enable or disable CIA interrupts, return old interrupt mask, - * interrupts will only be enabled if a handler exists */ unsigned char cia_able_irq(struct ciabase *base, unsigned char mask) { - unsigned char old, tmp; - int i; + unsigned char old; old = base->icr_mask; base->icr_data |= base->cia->icr; @@ -82,99 +78,109 @@ unsigned char cia_able_irq(struct ciabase *base, unsigned char mask) else base->icr_mask &= ~mask; base->icr_mask &= CIA_ICR_ALL; - for (i = 0, tmp = 1; i < CIA_IRQS; i++, tmp <<= 1) { - if ((tmp & base->icr_mask) && !base->irq_list[i].handler) { - base->icr_mask &= ~tmp; - base->cia->icr = tmp; - } - } if (base->icr_data & base->icr_mask) amiga_custom.intreq = IF_SETCLR | base->int_mask; return old; } -int cia_request_irq(struct ciabase *base, unsigned int irq, - irqreturn_t (*handler)(int, void *, struct pt_regs *), - unsigned long flags, const char *devname, void *dev_id) +static irqreturn_t cia_handler(int irq, void *dev_id) { - unsigned char mask; - - base->irq_list[irq].handler = handler; - base->irq_list[irq].flags = flags; - base->irq_list[irq].dev_id = dev_id; - base->irq_list[irq].devname = devname; + struct ciabase *base = dev_id; + int mach_irq; + unsigned char ints; - /* enable the interrupt */ - mask = 1 << irq; - cia_set_irq(base, mask); - cia_able_irq(base, CIA_ICR_SETCLR | mask); - return 0; + mach_irq = base->cia_irq; + ints = cia_set_irq(base, CIA_ICR_ALL); + amiga_custom.intreq = base->int_mask; + for (; ints; mach_irq++, ints >>= 1) { + if (ints & 1) + generic_handle_irq(mach_irq); + } + return IRQ_HANDLED; } -void cia_free_irq(struct ciabase *base, unsigned int irq, void *dev_id) +static void cia_irq_enable(struct irq_data *data) { - if (base->irq_list[irq].dev_id != dev_id) - printk("%s: removing probably wrong IRQ %i from %s\n", - __FUNCTION__, base->cia_irq + irq, - base->irq_list[irq].devname); + unsigned int irq = data->irq; + unsigned char mask; - base->irq_list[irq].handler = NULL; - base->irq_list[irq].flags = 0; + if (irq >= IRQ_AMIGA_CIAB) { + mask = 1 << (irq - IRQ_AMIGA_CIAB); + cia_set_irq(&ciab_base, mask); + cia_able_irq(&ciab_base, CIA_ICR_SETCLR | mask); + } else { + mask = 1 << (irq - IRQ_AMIGA_CIAA); + cia_set_irq(&ciaa_base, mask); + cia_able_irq(&ciaa_base, CIA_ICR_SETCLR | mask); + } +} + +static void cia_irq_disable(struct irq_data *data) +{ + unsigned int irq = data->irq; - cia_able_irq(base, 1 << irq); + if (irq >= IRQ_AMIGA_CIAB) + cia_able_irq(&ciab_base, 1 << (irq - IRQ_AMIGA_CIAB)); + else + cia_able_irq(&ciaa_base, 1 << (irq - IRQ_AMIGA_CIAA)); } -static irqreturn_t cia_handler(int irq, void *dev_id, struct pt_regs *fp) +static struct irq_chip cia_irq_chip = { + .name = "cia", + .irq_enable = cia_irq_enable, + .irq_disable = cia_irq_disable, +}; + +/* + * Override auto irq 2 & 6 and use them as general chain + * for external interrupts, we link the CIA interrupt sources + * into this chain. + */ + +static void auto_irq_enable(struct irq_data *data) { - struct ciabase *base = (struct ciabase *)dev_id; - int mach_irq, i; - unsigned char ints; + switch (data->irq) { + case IRQ_AUTO_2: + amiga_custom.intena = IF_SETCLR | IF_PORTS; + break; + case IRQ_AUTO_6: + amiga_custom.intena = IF_SETCLR | IF_EXTER; + break; + } +} - mach_irq = base->cia_irq; - irq = SYS_IRQS + mach_irq; - ints = cia_set_irq(base, CIA_ICR_ALL); - amiga_custom.intreq = base->int_mask; - for (i = 0; i < CIA_IRQS; i++, irq++, mach_irq++) { - if (ints & 1) { - kstat_cpu(0).irqs[irq]++; - base->irq_list[i].handler(mach_irq, base->irq_list[i].dev_id, fp); - } - ints >>= 1; +static void auto_irq_disable(struct irq_data *data) +{ + switch (data->irq) { + case IRQ_AUTO_2: + amiga_custom.intena = IF_PORTS; + break; + case IRQ_AUTO_6: + amiga_custom.intena = IF_EXTER; + break; } - amiga_do_irq_list(base->server_irq, fp); - return IRQ_HANDLED; } +static struct irq_chip auto_irq_chip = { + .name = "auto", + .irq_enable = auto_irq_enable, + .irq_disable = auto_irq_disable, +}; + void __init cia_init_IRQ(struct ciabase *base) { - int i; - - /* init isr handlers */ - for (i = 0; i < CIA_IRQS; i++) { - base->irq_list[i].handler = NULL; - base->irq_list[i].flags = 0; - } + m68k_setup_irq_controller(&cia_irq_chip, handle_simple_irq, + base->cia_irq, CIA_IRQS); /* clear any pending interrupt and turn off all interrupts */ cia_set_irq(base, CIA_ICR_ALL); cia_able_irq(base, CIA_ICR_ALL); - /* install CIA handler */ - request_irq(base->handler_irq, cia_handler, 0, base->name, base); - - amiga_custom.intena = IF_SETCLR | base->int_mask; -} - -int cia_get_irq_list(struct ciabase *base, struct seq_file *p) -{ - int i, j; - - j = base->cia_irq; - for (i = 0; i < CIA_IRQS; i++) { - seq_printf(p, "cia %2d: %10d ", j + i, - kstat_cpu(0).irqs[SYS_IRQS + j + i]); - seq_puts(p, " "); - seq_printf(p, "%s\n", base->irq_list[i].devname); - } - return 0; + /* override auto int and install CIA handler */ + m68k_setup_irq_controller(&auto_irq_chip, handle_simple_irq, + base->handler_irq, 1); + m68k_irq_startup_irq(base->handler_irq); + if (request_irq(base->handler_irq, cia_handler, IRQF_SHARED, + base->name, base)) + pr_err("Couldn't register %s interrupt\n", base->name); } |
