diff options
Diffstat (limited to 'arch/mips/vr4181/common/irq.c')
-rw-r--r-- | arch/mips/vr4181/common/irq.c | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/arch/mips/vr4181/common/irq.c b/arch/mips/vr4181/common/irq.c new file mode 100644 index 000000000000..2cdf77c5cb3e --- /dev/null +++ b/arch/mips/vr4181/common/irq.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) + * + * linux/arch/mips/vr4181/common/irq.c + * Completely re-written to use the new irq.c + * + * Credits to Bradley D. LaRonde and Michael Klar for writing the original + * irq.c file which was derived from the common irq.c file. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/types.h> +#include <linux/init.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/random.h> + +#include <asm/irq.h> +#include <asm/mipsregs.h> +#include <asm/gdb-stub.h> + +#include <asm/vr4181/vr4181.h> + +/* + * Strategy: + * + * We essentially have three irq controllers, CPU, system, and gpio. + * + * CPU irq controller is taken care by arch/mips/kernel/irq_cpu.c and + * CONFIG_IRQ_CPU config option. + * + * We here provide sys_irq and gpio_irq controller code. + */ + +static int sys_irq_base; +static int gpio_irq_base; + +/* ---------------------- sys irq ------------------------ */ +static void +sys_irq_enable(unsigned int irq) +{ + irq -= sys_irq_base; + if (irq < 16) { + *VR4181_MSYSINT1REG |= (u16)(1 << irq); + } else { + irq -= 16; + *VR4181_MSYSINT2REG |= (u16)(1 << irq); + } +} + +static void +sys_irq_disable(unsigned int irq) +{ + irq -= sys_irq_base; + if (irq < 16) { + *VR4181_MSYSINT1REG &= ~((u16)(1 << irq)); + } else { + irq -= 16; + *VR4181_MSYSINT2REG &= ~((u16)(1 << irq)); + } + +} + +static unsigned int +sys_irq_startup(unsigned int irq) +{ + sys_irq_enable(irq); + return 0; +} + +#define sys_irq_shutdown sys_irq_disable +#define sys_irq_ack sys_irq_disable + +static void +sys_irq_end(unsigned int irq) +{ + if(!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) + sys_irq_enable(irq); +} + +static hw_irq_controller sys_irq_controller = { + "vr4181_sys_irq", + sys_irq_startup, + sys_irq_shutdown, + sys_irq_enable, + sys_irq_disable, + sys_irq_ack, + sys_irq_end, + NULL /* no affinity stuff for UP */ +}; + +/* ---------------------- gpio irq ------------------------ */ +/* gpio irq lines use reverse logic */ +static void +gpio_irq_enable(unsigned int irq) +{ + irq -= gpio_irq_base; + *VR4181_GPINTMSK &= ~((u16)(1 << irq)); +} + +static void +gpio_irq_disable(unsigned int irq) +{ + irq -= gpio_irq_base; + *VR4181_GPINTMSK |= (u16)(1 << irq); +} + +static unsigned int +gpio_irq_startup(unsigned int irq) +{ + gpio_irq_enable(irq); + + irq -= gpio_irq_base; + *VR4181_GPINTEN |= (u16)(1 << irq ); + + return 0; +} + +static void +gpio_irq_shutdown(unsigned int irq) +{ + gpio_irq_disable(irq); + + irq -= gpio_irq_base; + *VR4181_GPINTEN &= ~((u16)(1 << irq )); +} + +static void +gpio_irq_ack(unsigned int irq) +{ + u16 irqtype; + u16 irqshift; + + gpio_irq_disable(irq); + + /* we clear interrupt if it is edge triggered */ + irq -= gpio_irq_base; + if (irq < 8) { + irqtype = *VR4181_GPINTTYPL; + irqshift = 2 << (irq*2); + } else { + irqtype = *VR4181_GPINTTYPH; + irqshift = 2 << ((irq-8)*2); + } + if ( ! (irqtype & irqshift) ) { + *VR4181_GPINTSTAT = (u16) (1 << irq); + } +} + +static void +gpio_irq_end(unsigned int irq) +{ + if(!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) + gpio_irq_enable(irq); +} + +static hw_irq_controller gpio_irq_controller = { + "vr4181_gpio_irq", + gpio_irq_startup, + gpio_irq_shutdown, + gpio_irq_enable, + gpio_irq_disable, + gpio_irq_ack, + gpio_irq_end, + NULL /* no affinity stuff for UP */ +}; + +/* --------------------- IRQ init stuff ---------------------- */ + +extern asmlinkage void vr4181_handle_irq(void); +extern void breakpoint(void); +extern int setup_irq(unsigned int irq, struct irqaction *irqaction); +extern void mips_cpu_irq_init(u32 irq_base); + +static struct irqaction cascade = + { no_action, SA_INTERRUPT, CPU_MASK_NONE, "cascade", NULL, NULL }; +static struct irqaction reserved = + { no_action, SA_INTERRUPT, CPU_MASK_NONE, "cascade", NULL, NULL }; + +void __init arch_init_irq(void) +{ + int i; + + set_except_vector(0, vr4181_handle_irq); + + /* init CPU irqs */ + mips_cpu_irq_init(VR4181_CPU_IRQ_BASE); + + /* init sys irqs */ + sys_irq_base = VR4181_SYS_IRQ_BASE; + for (i=sys_irq_base; i < sys_irq_base + VR4181_NUM_SYS_IRQ; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].handler = &sys_irq_controller; + } + + /* init gpio irqs */ + gpio_irq_base = VR4181_GPIO_IRQ_BASE; + for (i=gpio_irq_base; i < gpio_irq_base + VR4181_NUM_GPIO_IRQ; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].handler = &gpio_irq_controller; + } + + /* Default all ICU IRQs to off ... */ + *VR4181_MSYSINT1REG = 0; + *VR4181_MSYSINT2REG = 0; + + /* We initialize the level 2 ICU registers to all bits disabled. */ + *VR4181_MPIUINTREG = 0; + *VR4181_MAIUINTREG = 0; + *VR4181_MKIUINTREG = 0; + + /* disable all GPIO intrs */ + *VR4181_GPINTMSK = 0xffff; + + /* vector handler. What these do is register the IRQ as non-sharable */ + setup_irq(VR4181_IRQ_INT0, &cascade); + setup_irq(VR4181_IRQ_GIU, &cascade); + + /* + * RTC interrupts are interesting. They have two destinations. + * One is at sys irq controller, and the other is at CPU IP3 and IP4. + * RTC timer is used as system timer. + * We enable them here, but timer routine will register later + * with CPU IP3/IP4. + */ + setup_irq(VR4181_IRQ_RTCL1, &reserved); + setup_irq(VR4181_IRQ_RTCL2, &reserved); +} |