| /* |
| * 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); |
| } |