--- zzzz-none-000/linux-2.4.17/arch/mips/philips/nino/irq.c 2001-09-09 17:43:02.000000000 +0000 +++ sangam-fb-322/linux-2.4.17/arch/mips/philips/nino/irq.c 2004-11-24 13:22:40.000000000 +0000 @@ -1,314 +1,267 @@ /* - * linux/arch/mips/philips/nino/irq.c + * arch/mips/philips/nino/irq.c * - * Copyright (C) 1992 Linus Torvalds - * Copyright (C) 1999 Harald Koerfgen - * Copyright (C) 2000 Pavel Machek (pavel@suse.cz) * Copyright (C) 2001 Steven J. Hill (sjhill@realitydiluted.com) - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * Generic interrupt handler for Philips Nino. + * + * Interrupt service routines for Philips Nino */ -#include #include -#include -#include #include -#include #include -#include -#include -#include -#include - -#include -#include +#include #include #include #include -#include #include -unsigned long spurious_count = 0; +#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) -irq_cpustat_t irq_stat [NR_CPUS]; +extern asmlinkage void do_IRQ(int irq, struct pt_regs *regs); -static inline void mask_irq(unsigned int irq_nr) +static void enable_irq6(unsigned int irq) { - switch (irq_nr) { - case 0: /* Periodic Timer Interrupt */ - IntClear5 = INT5_PERIODICINT; - IntClear6 = INT6_PERIODICINT; - IntEnable6 &= ~INT6_PERIODICINT; - break; + if(irq == 0) { + outl(inl(TX3912_INT6_ENABLE) | + TX3912_INT6_ENABLE_PRIORITYMASK_PERINT, + TX3912_INT6_ENABLE); + outl(inl(TX3912_INT5_ENABLE) | TX3912_INT5_PERINT, + TX3912_INT5_ENABLE); + } + if(irq == 3) { + outl(inl(TX3912_INT6_ENABLE) | + TX3912_INT6_ENABLE_PRIORITYMASK_UARTARXINT, + TX3912_INT6_ENABLE); + outl(inl(TX3912_INT2_ENABLE) | TX3912_INT2_UARTA_RX_BITS, + TX3912_INT2_ENABLE); + } +} - case 3: - /* Serial port receive interrupt */ - break; +static unsigned int startup_irq6(unsigned int irq) +{ + enable_irq6(irq); - case 2: - /* Serial port transmit interrupt */ - break; + return 0; /* Never anything pending */ +} - default: - printk( "Attempt to mask unknown IRQ %d?\n", irq_nr ); +static void disable_irq6(unsigned int irq) +{ + if(irq == 0) { + outl(inl(TX3912_INT6_ENABLE) & + ~TX3912_INT6_ENABLE_PRIORITYMASK_PERINT, + TX3912_INT6_ENABLE); + outl(inl(TX3912_INT5_ENABLE) & ~TX3912_INT5_PERINT, + TX3912_INT5_ENABLE); + outl(inl(TX3912_INT5_CLEAR) | TX3912_INT5_PERINT, + TX3912_INT5_CLEAR); + } + if(irq == 3) { + outl(inl(TX3912_INT6_ENABLE) & + ~TX3912_INT6_ENABLE_PRIORITYMASK_UARTARXINT, + TX3912_INT6_ENABLE); + outl(inl(TX3912_INT2_ENABLE) & ~TX3912_INT2_UARTA_RX_BITS, + TX3912_INT2_ENABLE); } } -static inline void unmask_irq(unsigned int irq_nr) +#define shutdown_irq6 disable_irq6 +#define mask_and_ack_irq6 disable_irq6 + +static void end_irq6(unsigned int irq) { - switch (irq_nr) { - case 0: - IntEnable6 |= INT6_PERIODICINT; - break; + if(!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_irq6(irq); +} - case 3: - /* Serial port receive interrupt */ - break; +static struct hw_interrupt_type irq6_type = { + "MIPS", + startup_irq6, + shutdown_irq6, + enable_irq6, + disable_irq6, + mask_and_ack_irq6, + end_irq6, + NULL +}; + +void irq6_dispatch(struct pt_regs *regs) +{ + int irq = -1; - case 2: - /* Serial port transmit interrupt */ - break; + if((inl(TX3912_INT6_STATUS) & TX3912_INT6_STATUS_INTVEC_UARTARXINT) == + TX3912_INT6_STATUS_INTVEC_UARTARXINT) { + irq = 3; + goto done; + } + if ((inl(TX3912_INT6_STATUS) & TX3912_INT6_STATUS_INTVEC_PERINT) == + TX3912_INT6_STATUS_INTVEC_PERINT) { + irq = 0; + goto done; + } - default: - printk( "Attempt to unmask unknown IRQ %d?\n", irq_nr ); + /* if irq == -1, then interrupt was cleared or is invalid */ + if (irq == -1) { + panic("Unhandled High Priority PR31700 Interrupt = 0x%08x", + inl(TX3912_INT6_STATUS)); } + +done: + do_IRQ(irq, regs); } -void disable_irq(unsigned int irq_nr) +static void enable_irq4(unsigned int irq) { - unsigned long flags; - - save_and_cli(flags); - mask_irq(irq_nr); - restore_flags(flags); + set_cp0_status(STATUSF_IP4); + if (irq == 2) { + outl(inl(TX3912_INT2_CLEAR) | TX3912_INT2_UARTA_TX_BITS, + TX3912_INT2_CLEAR); + outl(inl(TX3912_INT2_ENABLE) | TX3912_INT2_UARTA_TX_BITS, + TX3912_INT2_ENABLE); + } } -void enable_irq(unsigned int irq_nr) +static unsigned int startup_irq4(unsigned int irq) { - unsigned long flags; + enable_irq4(irq); - save_and_cli(flags); - unmask_irq(irq_nr); - restore_flags(flags); + return 0; /* Never anything pending */ } -/* - * Pointers to the low-level handlers: first the general ones, then the - * fast ones, then the bad ones. - */ -extern void interrupt(void); - -static struct irqaction *irq_action[NR_IRQS] = +static void disable_irq4(unsigned int irq) { - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL -}; + clear_cp0_status(STATUSF_IP4); +} -int get_irq_list(char *buf) -{ - int i, len = 0; - struct irqaction *action; +#define shutdown_irq4 disable_irq4 +#define mask_and_ack_irq4 disable_irq4 - for (i = 0; i < NR_IRQS; i++) { - action = irq_action[i]; - if (!action) - continue; - len += sprintf(buf + len, "%2d: %8d %c %s", - i, kstat.irqs[0][i], - (action->flags & SA_INTERRUPT) ? '+' : ' ', - action->name); - for (action = action->next; action; action = action->next) { - len += sprintf(buf + len, ",%s %s", - (action->flags & SA_INTERRUPT) ? " +" : "", - action->name); - } - len += sprintf(buf + len, "\n"); - } - return len; +static void end_irq4(unsigned int irq) +{ + if(!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_irq4(irq); } -atomic_t __mips_bh_counter; +static struct hw_interrupt_type irq4_type = { + "MIPS", + startup_irq4, + shutdown_irq4, + enable_irq4, + disable_irq4, + mask_and_ack_irq4, + end_irq4, + NULL +}; -/* - * do_IRQ handles IRQ's that have been installed without the - * SA_INTERRUPT flag: it uses the full signal-handling return - * and runs with other interrupts enabled. All relatively slow - * IRQ's should use this format: notably the keyboard/timer - * routines. - */ -asmlinkage void do_IRQ(int irq, struct pt_regs *regs) +void irq4_dispatch(struct pt_regs *regs) { - struct irqaction *action; - int do_random, cpu; + int irq = -1; - if (irq == 20) { - if (IntStatus2 & 0xfffff00) { - if (IntStatus2 & 0x0f000000) - return do_IRQ(2, regs); - } - } - - cpu = smp_processor_id(); - irq_enter(cpu, irq); - kstat.irqs[cpu][irq]++; - - if (irq == 20) { - printk("20 %08lx %08lx\n %08lx %08lx\n %08lx\n", - IntStatus1, IntStatus2, IntStatus3, - IntStatus4, IntStatus5 ); - printk("20 %08lx %08lx\n %08lx %08lx\n %08lx\n", - IntEnable1, IntEnable2, IntEnable3, - IntEnable4, IntEnable5 ); - - } - - mask_irq(irq); - action = *(irq + irq_action); - if (action) { - if (!(action->flags & SA_INTERRUPT)) - __sti(); - do_random = 0; - do { - do_random |= action->flags; - action->handler(irq, action->dev_id, regs); - action = action->next; - } while (action); - if (do_random & SA_SAMPLE_RANDOM) - add_interrupt_randomness(irq); - unmask_irq(irq); - __cli(); - } else { - IntClear1 = ~0; - IntClear3 = ~0; - IntClear4 = ~0; - IntClear5 = ~0; - unmask_irq(irq); - } - irq_exit(cpu, irq); + if(inl(TX3912_INT2_STATUS) & TX3912_INT2_UARTA_TX_BITS) { + irq = 2; + goto done; + } - /* unmasking and bottom half handling is done magically for us. */ -} + /* if irq == -1, then interrupt was cleared or is invalid */ + if (irq == -1) { + printk("PR31700 Interrupt Status Register 1 = 0x%08x\n", + inl(TX3912_INT1_STATUS)); + printk("PR31700 Interrupt Status Register 2 = 0x%08x\n", + inl(TX3912_INT2_STATUS)); + printk("PR31700 Interrupt Status Register 3 = 0x%08x\n", + inl(TX3912_INT3_STATUS)); + printk("PR31700 Interrupt Status Register 4 = 0x%08x\n", + inl(TX3912_INT4_STATUS)); + printk("PR31700 Interrupt Status Register 5 = 0x%08x\n", + inl(TX3912_INT5_STATUS)); + panic("Unhandled Low Priority PR31700 Interrupt"); + } -/* - * Idea is to put all interrupts - * in a single table and differenciate them just by number. - */ -int setup_nino_irq(int irq, struct irqaction *new) -{ - int shared = 0; - struct irqaction *old, **p; - unsigned long flags; - - p = irq_action + irq; - if ((old = *p) != NULL) { - /* Can't share interrupts unless both agree to */ - if (!(old->flags & new->flags & SA_SHIRQ)) - return -EBUSY; - - /* Can't share interrupts unless both are same type */ - if ((old->flags ^ new->flags) & SA_INTERRUPT) - return -EBUSY; - - /* add new interrupt at end of irq queue */ - do { - p = &old->next; - old = *p; - } while (old); - shared = 1; - } - if (new->flags & SA_SAMPLE_RANDOM) - rand_initialize_irq(irq); - - save_and_cli(flags); - *p = new; - - if (!shared) { - unmask_irq(irq); - } - restore_flags(flags); - return 0; -} - -int request_irq(unsigned int irq, - void (*handler) (int, void *, struct pt_regs *), - unsigned long irqflags, - const char *devname, - void *dev_id) -{ - int retval; - struct irqaction *action; - - if (irq >= NR_IRQS) - return -EINVAL; - if (!handler) - return -EINVAL; - - action = (struct irqaction *) kmalloc(sizeof(struct irqaction), GFP_KERNEL); - if (!action) - return -ENOMEM; - - action->handler = handler; - action->flags = irqflags; - action->mask = 0; - action->name = devname; - action->next = NULL; - action->dev_id = dev_id; - - retval = setup_nino_irq(irq, action); - - if (retval) - kfree(action); - return retval; +done: + do_IRQ(irq, regs); + return; } -void free_irq(unsigned int irq, void *dev_id) +void irq_bad(struct pt_regs *regs) { - struct irqaction *action, **p; - unsigned long flags; + /* This should never happen */ + printk(" CAUSE register = 0x%08lx\n", regs->cp0_cause); + printk("STATUS register = 0x%08lx\n", regs->cp0_status); + printk(" EPC register = 0x%08lx\n", regs->cp0_epc); + panic("Stray interrupt, spinning..."); +} + +void __init nino_irq_setup(void) +{ + extern asmlinkage void ninoIRQ(void); + extern void init_generic_irq(void); + + unsigned int i; + + /* Disable all hardware interrupts */ + change_cp0_status(ST0_IM, 0x00); + + /* Clear interrupts */ + outl(0xffffffff, TX3912_INT1_CLEAR); + outl(0xffffffff, TX3912_INT2_CLEAR); + outl(0xffffffff, TX3912_INT3_CLEAR); + outl(0xffffffff, TX3912_INT4_CLEAR); + outl(0xffffffff, TX3912_INT5_CLEAR); + + /* + * Disable all PR31700 interrupts. We let the various + * device drivers in the system register themselves + * and set the proper hardware bits. + */ + outl(0x00000000, TX3912_INT1_ENABLE); + outl(0x00000000, TX3912_INT2_ENABLE); + outl(0x00000000, TX3912_INT3_ENABLE); + outl(0x00000000, TX3912_INT4_ENABLE); + outl(0x00000000, TX3912_INT5_ENABLE); + + /* Initialize IRQ vector table */ + init_generic_irq(); + + /* Initialize IRQ action handlers */ + for (i = 0; i < 16; i++) { + hw_irq_controller *handler = NULL; + if (i == 0 || i == 3) + handler = &irq6_type; + else + handler = &irq4_type; + + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = 0; + irq_desc[i].depth = 1; + irq_desc[i].handler = handler; + } - if (irq >= NR_IRQS) { - printk(KERN_CRIT __FUNCTION__ ": trying to free IRQ%d\n", irq); - return; - } - for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) { - if (action->dev_id != dev_id) - continue; - - /* Found it - now free it */ - save_and_cli(flags); - *p = action->next; - if (!irq[irq_action]) - mask_irq(irq); - restore_flags(flags); - kfree(action); - return; - } - printk(KERN_CRIT __FUNCTION__ ": trying to free free IRQ%d\n", irq); -} + /* Set up the external interrupt exception vector */ + set_except_vector(0, ninoIRQ); -unsigned long probe_irq_on(void) -{ - /* TODO */ - return 0; -} + /* Enable high priority interrupts */ + outl(TX3912_INT6_ENABLE_GLOBALEN | TX3912_INT6_ENABLE_HIGH_PRIORITY, + TX3912_INT6_ENABLE); -int probe_irq_off(unsigned long irqs) -{ - /* TODO */ - return 0; + /* Enable all interrupts */ + change_cp0_status(ST0_IM, ALLINTS); } +void (*irq_setup)(void); + void __init init_IRQ(void) { - irq_setup(); +#ifdef CONFIG_REMOTE_DEBUG + extern void breakpoint(void); + extern void set_debug_traps(void); + + printk("Wait for gdb client connection ...\n"); + set_debug_traps(); + breakpoint(); +#endif + + /* Invoke board-specific irq setup */ + irq_setup(); }