/* * arch/mips/philips/nino/irq.c * * 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. * * Interrupt service routines for Philips Nino */ #include #include #include #include #include #include #include #include #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) extern asmlinkage void do_IRQ(int irq, struct pt_regs *regs); static void enable_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); } 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 unsigned int startup_irq6(unsigned int irq) { enable_irq6(irq); return 0; /* Never anything pending */ } 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); } } #define shutdown_irq6 disable_irq6 #define mask_and_ack_irq6 disable_irq6 static void end_irq6(unsigned int irq) { if(!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) enable_irq6(irq); } 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; 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; } /* 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); } static void enable_irq4(unsigned int irq) { 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); } } static unsigned int startup_irq4(unsigned int irq) { enable_irq4(irq); return 0; /* Never anything pending */ } static void disable_irq4(unsigned int irq) { clear_cp0_status(STATUSF_IP4); } #define shutdown_irq4 disable_irq4 #define mask_and_ack_irq4 disable_irq4 static void end_irq4(unsigned int irq) { if(!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) enable_irq4(irq); } static struct hw_interrupt_type irq4_type = { "MIPS", startup_irq4, shutdown_irq4, enable_irq4, disable_irq4, mask_and_ack_irq4, end_irq4, NULL }; void irq4_dispatch(struct pt_regs *regs) { int irq = -1; if(inl(TX3912_INT2_STATUS) & TX3912_INT2_UARTA_TX_BITS) { irq = 2; goto done; } /* 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"); } done: do_IRQ(irq, regs); return; } void irq_bad(struct pt_regs *regs) { /* 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; } /* Set up the external interrupt exception vector */ set_except_vector(0, ninoIRQ); /* Enable high priority interrupts */ outl(TX3912_INT6_ENABLE_GLOBALEN | TX3912_INT6_ENABLE_HIGH_PRIORITY, TX3912_INT6_ENABLE); /* Enable all interrupts */ change_cp0_status(ST0_IM, ALLINTS); } void (*irq_setup)(void); void __init init_IRQ(void) { #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(); }