X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fm68k%2Famiga%2Fcia.c;fp=arch%2Fm68k%2Famiga%2Fcia.c;h=9476eb9440f57436365b8de4f6d70e02bb9500c0;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=dbad30054721cf5a1a892f8f23c9c4e3f941c9e3;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/arch/m68k/amiga/cia.c b/arch/m68k/amiga/cia.c index dbad30054..9476eb944 100644 --- a/arch/m68k/amiga/cia.c +++ b/arch/m68k/amiga/cia.c @@ -29,18 +29,21 @@ 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_PORTS, + .handler_irq = IRQ_AMIGA_AUTO_2, .cia_irq = IRQ_AMIGA_CIAA, - .name = "CIAA" + .server_irq = IRQ_AMIGA_PORTS, + .name = "CIAA handler" }, ciab_base = { .cia = &ciab, .int_mask = IF_EXTER, - .handler_irq = IRQ_AMIGA_EXTER, + .handler_irq = IRQ_AMIGA_AUTO_6, .cia_irq = IRQ_AMIGA_CIAB, - .name = "CIAB" + .server_irq = IRQ_AMIGA_EXTER, + .name = "CIAB handler" }; /* @@ -63,11 +66,13 @@ 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; + unsigned char old, tmp; + int i; old = base->icr_mask; base->icr_data |= base->cia->icr; @@ -77,104 +82,99 @@ 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) +{ + 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; + + /* enable the interrupt */ + mask = 1 << irq; + cia_set_irq(base, mask); + cia_able_irq(base, CIA_ICR_SETCLR | mask); + return 0; +} + +void cia_free_irq(struct ciabase *base, unsigned int irq, void *dev_id) +{ + 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); + + base->irq_list[irq].handler = NULL; + base->irq_list[irq].flags = 0; + + cia_able_irq(base, 1 << irq); +} + static irqreturn_t cia_handler(int irq, void *dev_id, struct pt_regs *fp) { struct ciabase *base = (struct ciabase *)dev_id; - int mach_irq; + int mach_irq, i; unsigned char ints; 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 (; ints; mach_irq++, ints >>= 1) { - if (ints & 1) - m68k_handle_int(mach_irq, fp); + 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; } + amiga_do_irq_list(base->server_irq, fp); return IRQ_HANDLED; } -static void cia_enable_irq(unsigned int irq) +void __init cia_init_IRQ(struct ciabase *base) { - unsigned char mask; + int i; - 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); + /* init isr handlers */ + for (i = 0; i < CIA_IRQS; i++) { + base->irq_list[i].handler = NULL; + base->irq_list[i].flags = 0; } -} - -static void cia_disable_irq(unsigned int 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 struct irq_controller cia_irq_controller = { - .name = "cia", - .lock = SPIN_LOCK_UNLOCKED, - .enable = cia_enable_irq, - .disable = cia_disable_irq, -}; + /* clear any pending interrupt and turn off all interrupts */ + cia_set_irq(base, CIA_ICR_ALL); + cia_able_irq(base, CIA_ICR_ALL); -/* - * Override auto irq 2 & 6 and use them as general chain - * for external interrupts, we link the CIA interrupt sources - * into this chain. - */ + /* install CIA handler */ + request_irq(base->handler_irq, cia_handler, 0, base->name, base); -static void auto_enable_irq(unsigned int irq) -{ - switch (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; - } + amiga_custom.intena = IF_SETCLR | base->int_mask; } -static void auto_disable_irq(unsigned int irq) +int cia_get_irq_list(struct ciabase *base, struct seq_file *p) { - switch (irq) { - case IRQ_AUTO_2: - amiga_custom.intena = IF_PORTS; - break; - case IRQ_AUTO_6: - amiga_custom.intena = IF_EXTER; - break; + 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); } -} - -static struct irq_controller auto_irq_controller = { - .name = "auto", - .lock = SPIN_LOCK_UNLOCKED, - .enable = auto_enable_irq, - .disable = auto_disable_irq, -}; - -void __init cia_init_IRQ(struct ciabase *base) -{ - m68k_setup_irq_controller(&cia_irq_controller, 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); - - /* override auto int and install CIA handler */ - m68k_setup_irq_controller(&auto_irq_controller, base->handler_irq, 1); - m68k_irq_startup(base->handler_irq); - request_irq(base->handler_irq, cia_handler, IRQF_SHARED, base->name, base); + return 0; }