/* * Copyright 2001 MontaVista Software Inc. * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net * * arch/mips/ddb5xxx/ddb5477/irq.c * The irq setup and misc routines for DDB5476. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ #include #include #include #include #include #include #include #include #include /* * IRQ mapping * * 0-7: 8 CPU interrupts * 0 - software interrupt 0 * 1 - software interrupt 1 * 2 - most Vrc5477 interrupts are routed to this pin * 3 - (optional) some other interrupts routed to this pin for debugg * 4 - not used * 5 - not used * 6 - not used * 7 - cpu timer (used by default) * * 8-39: 32 Vrc5477 interrupt sources * (refer to the Vrc5477 manual) */ #define PCI0 DDB_INTPPES0 #define PCI1 DDB_INTPPES1 #define ACTIVE_LOW 1 #define ACTIVE_HIGH 0 #define LEVEL_SENSE 2 #define EDGE_TRIGGER 0 #define INTA 0 #define INTB 1 #define INTC 2 #define INTD 3 #define INTE 4 static inline void set_pci_int_attr(u32 pci, u32 intn, u32 active, u32 trigger) { u32 reg_value; u32 reg_bitmask; reg_value = ddb_in32(pci); reg_bitmask = 0x3 << (intn * 2); reg_value &= ~reg_bitmask; reg_value |= (active | trigger) << (intn * 2); ddb_out32(pci, reg_value); } extern void vrc5477_irq_init(u32 base); extern void mips_cpu_irq_init(u32 base); extern asmlinkage void ddb5477_handle_int(void); void ddb5477_irq_setup(void) { db_run(printk("ddb5477_irq_setup invoked.\n")); /* by default, we disable all interrupts and route all vrc5477 * interrupts to pin 0 (irq 2) */ ddb_out32(DDB_INTCTRL0, 0); ddb_out32(DDB_INTCTRL1, 0); ddb_out32(DDB_INTCTRL2, 0); ddb_out32(DDB_INTCTRL3, 0); clear_cp0_status(0xff00); set_cp0_status(0x0400); /* setup PCI interrupt attributes */ set_pci_int_attr(PCI0, INTA, ACTIVE_LOW, LEVEL_SENSE); set_pci_int_attr(PCI0, INTB, ACTIVE_LOW, LEVEL_SENSE); set_pci_int_attr(PCI0, INTC, ACTIVE_LOW, LEVEL_SENSE); set_pci_int_attr(PCI0, INTD, ACTIVE_LOW, LEVEL_SENSE); set_pci_int_attr(PCI0, INTE, ACTIVE_LOW, LEVEL_SENSE); set_pci_int_attr(PCI1, INTA, ACTIVE_LOW, LEVEL_SENSE); set_pci_int_attr(PCI1, INTB, ACTIVE_LOW, LEVEL_SENSE); set_pci_int_attr(PCI1, INTC, ACTIVE_LOW, LEVEL_SENSE); set_pci_int_attr(PCI1, INTD, ACTIVE_LOW, LEVEL_SENSE); set_pci_int_attr(PCI1, INTE, ACTIVE_LOW, LEVEL_SENSE); /* * for debugging purpose, we enable several error interrupts * and route them to pin 1. (IP3) */ /* cpu parity check - 0 */ ll_vrc5477_irq_route(0, 1); ll_vrc5477_irq_enable(0); /* cpu no-target decode - 1 */ ll_vrc5477_irq_route(1, 1); ll_vrc5477_irq_enable(1); /* local bus read time-out - 7 */ ll_vrc5477_irq_route(7, 1); ll_vrc5477_irq_enable(7); /* PCI SERR# - 14 */ ll_vrc5477_irq_route(14, 1); ll_vrc5477_irq_enable(14); /* PCI internal error - 15 */ ll_vrc5477_irq_route(15, 1); ll_vrc5477_irq_enable(15); /* IOPCI SERR# - 30 */ ll_vrc5477_irq_route(30, 1); ll_vrc5477_irq_enable(30); /* IOPCI internal error - 31 */ ll_vrc5477_irq_route(31, 1); ll_vrc5477_irq_enable(31); /* init all controllers */ mips_cpu_irq_init(0); vrc5477_irq_init(8); /* hook up the first-level interrupt handler */ set_except_vector(0, ddb5477_handle_int); } /* * the first level int-handler will jump here if it is a vrc5477 irq */ #define NUM_5477_IRQS 32 asmlinkage void vrc5477_irq_dispatch(struct pt_regs *regs) { extern unsigned int do_IRQ(int irq, struct pt_regs *regs); u32 intStatus; u32 bitmask; u32 i; db_assert(ddb_in32(DDB_INT2STAT) == 0); db_assert(ddb_in32(DDB_INT3STAT) == 0); db_assert(ddb_in32(DDB_INT4STAT) == 0); db_assert(ddb_in32(DDB_NMISTAT) == 0); if (ddb_in32(DDB_INT1STAT) != 0) { #if defined(CONFIG_DEBUG) vrc5477_show_int_regs(); #endif panic("error interrupt has happened."); } intStatus = ddb_in32(DDB_INT0STAT); for (i=0, bitmask=1; i<= NUM_5477_IRQS; bitmask <<=1, i++) { /* do we need to "and" with the int mask? */ if (intStatus & bitmask) { do_IRQ(8 + i, regs); } } }