Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (C) 2001 MontaVista Software Inc. |
| 3 | * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net |
| 4 | * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) |
| 5 | * |
| 6 | * linux/arch/mips/vr4181/common/irq.c |
| 7 | * Completely re-written to use the new irq.c |
| 8 | * |
| 9 | * Credits to Bradley D. LaRonde and Michael Klar for writing the original |
| 10 | * irq.c file which was derived from the common irq.c file. |
| 11 | * |
| 12 | * This file is subject to the terms and conditions of the GNU General Public |
| 13 | * License. See the file "COPYING" in the main directory of this archive |
| 14 | * for more details. |
| 15 | */ |
| 16 | #include <linux/types.h> |
| 17 | #include <linux/init.h> |
| 18 | #include <linux/kernel_stat.h> |
| 19 | #include <linux/signal.h> |
| 20 | #include <linux/sched.h> |
| 21 | #include <linux/interrupt.h> |
| 22 | #include <linux/slab.h> |
| 23 | #include <linux/random.h> |
| 24 | |
| 25 | #include <asm/irq.h> |
| 26 | #include <asm/mipsregs.h> |
| 27 | #include <asm/gdb-stub.h> |
| 28 | |
| 29 | #include <asm/vr4181/vr4181.h> |
| 30 | |
| 31 | /* |
| 32 | * Strategy: |
| 33 | * |
| 34 | * We essentially have three irq controllers, CPU, system, and gpio. |
| 35 | * |
| 36 | * CPU irq controller is taken care by arch/mips/kernel/irq_cpu.c and |
| 37 | * CONFIG_IRQ_CPU config option. |
| 38 | * |
| 39 | * We here provide sys_irq and gpio_irq controller code. |
| 40 | */ |
| 41 | |
| 42 | static int sys_irq_base; |
| 43 | static int gpio_irq_base; |
| 44 | |
| 45 | /* ---------------------- sys irq ------------------------ */ |
| 46 | static void |
| 47 | sys_irq_enable(unsigned int irq) |
| 48 | { |
| 49 | irq -= sys_irq_base; |
| 50 | if (irq < 16) { |
| 51 | *VR4181_MSYSINT1REG |= (u16)(1 << irq); |
| 52 | } else { |
| 53 | irq -= 16; |
| 54 | *VR4181_MSYSINT2REG |= (u16)(1 << irq); |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | static void |
| 59 | sys_irq_disable(unsigned int irq) |
| 60 | { |
| 61 | irq -= sys_irq_base; |
| 62 | if (irq < 16) { |
| 63 | *VR4181_MSYSINT1REG &= ~((u16)(1 << irq)); |
| 64 | } else { |
| 65 | irq -= 16; |
| 66 | *VR4181_MSYSINT2REG &= ~((u16)(1 << irq)); |
| 67 | } |
| 68 | |
| 69 | } |
| 70 | |
| 71 | static unsigned int |
| 72 | sys_irq_startup(unsigned int irq) |
| 73 | { |
| 74 | sys_irq_enable(irq); |
| 75 | return 0; |
| 76 | } |
| 77 | |
| 78 | #define sys_irq_shutdown sys_irq_disable |
| 79 | #define sys_irq_ack sys_irq_disable |
| 80 | |
| 81 | static void |
| 82 | sys_irq_end(unsigned int irq) |
| 83 | { |
| 84 | if(!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) |
| 85 | sys_irq_enable(irq); |
| 86 | } |
| 87 | |
| 88 | static hw_irq_controller sys_irq_controller = { |
| 89 | "vr4181_sys_irq", |
| 90 | sys_irq_startup, |
| 91 | sys_irq_shutdown, |
| 92 | sys_irq_enable, |
| 93 | sys_irq_disable, |
| 94 | sys_irq_ack, |
| 95 | sys_irq_end, |
| 96 | NULL /* no affinity stuff for UP */ |
| 97 | }; |
| 98 | |
| 99 | /* ---------------------- gpio irq ------------------------ */ |
| 100 | /* gpio irq lines use reverse logic */ |
| 101 | static void |
| 102 | gpio_irq_enable(unsigned int irq) |
| 103 | { |
| 104 | irq -= gpio_irq_base; |
| 105 | *VR4181_GPINTMSK &= ~((u16)(1 << irq)); |
| 106 | } |
| 107 | |
| 108 | static void |
| 109 | gpio_irq_disable(unsigned int irq) |
| 110 | { |
| 111 | irq -= gpio_irq_base; |
| 112 | *VR4181_GPINTMSK |= (u16)(1 << irq); |
| 113 | } |
| 114 | |
| 115 | static unsigned int |
| 116 | gpio_irq_startup(unsigned int irq) |
| 117 | { |
| 118 | gpio_irq_enable(irq); |
| 119 | |
| 120 | irq -= gpio_irq_base; |
| 121 | *VR4181_GPINTEN |= (u16)(1 << irq ); |
| 122 | |
| 123 | return 0; |
| 124 | } |
| 125 | |
| 126 | static void |
| 127 | gpio_irq_shutdown(unsigned int irq) |
| 128 | { |
| 129 | gpio_irq_disable(irq); |
| 130 | |
| 131 | irq -= gpio_irq_base; |
| 132 | *VR4181_GPINTEN &= ~((u16)(1 << irq )); |
| 133 | } |
| 134 | |
| 135 | static void |
| 136 | gpio_irq_ack(unsigned int irq) |
| 137 | { |
| 138 | u16 irqtype; |
| 139 | u16 irqshift; |
| 140 | |
| 141 | gpio_irq_disable(irq); |
| 142 | |
| 143 | /* we clear interrupt if it is edge triggered */ |
| 144 | irq -= gpio_irq_base; |
| 145 | if (irq < 8) { |
| 146 | irqtype = *VR4181_GPINTTYPL; |
| 147 | irqshift = 2 << (irq*2); |
| 148 | } else { |
| 149 | irqtype = *VR4181_GPINTTYPH; |
| 150 | irqshift = 2 << ((irq-8)*2); |
| 151 | } |
| 152 | if ( ! (irqtype & irqshift) ) { |
| 153 | *VR4181_GPINTSTAT = (u16) (1 << irq); |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | static void |
| 158 | gpio_irq_end(unsigned int irq) |
| 159 | { |
| 160 | if(!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) |
| 161 | gpio_irq_enable(irq); |
| 162 | } |
| 163 | |
| 164 | static hw_irq_controller gpio_irq_controller = { |
| 165 | "vr4181_gpio_irq", |
| 166 | gpio_irq_startup, |
| 167 | gpio_irq_shutdown, |
| 168 | gpio_irq_enable, |
| 169 | gpio_irq_disable, |
| 170 | gpio_irq_ack, |
| 171 | gpio_irq_end, |
| 172 | NULL /* no affinity stuff for UP */ |
| 173 | }; |
| 174 | |
| 175 | /* --------------------- IRQ init stuff ---------------------- */ |
| 176 | |
| 177 | extern asmlinkage void vr4181_handle_irq(void); |
| 178 | extern void breakpoint(void); |
| 179 | extern int setup_irq(unsigned int irq, struct irqaction *irqaction); |
| 180 | extern void mips_cpu_irq_init(u32 irq_base); |
| 181 | |
| 182 | static struct irqaction cascade = |
| 183 | { no_action, SA_INTERRUPT, CPU_MASK_NONE, "cascade", NULL, NULL }; |
| 184 | static struct irqaction reserved = |
| 185 | { no_action, SA_INTERRUPT, CPU_MASK_NONE, "cascade", NULL, NULL }; |
| 186 | |
| 187 | void __init arch_init_irq(void) |
| 188 | { |
| 189 | int i; |
| 190 | |
| 191 | set_except_vector(0, vr4181_handle_irq); |
| 192 | |
| 193 | /* init CPU irqs */ |
| 194 | mips_cpu_irq_init(VR4181_CPU_IRQ_BASE); |
| 195 | |
| 196 | /* init sys irqs */ |
| 197 | sys_irq_base = VR4181_SYS_IRQ_BASE; |
| 198 | for (i=sys_irq_base; i < sys_irq_base + VR4181_NUM_SYS_IRQ; i++) { |
| 199 | irq_desc[i].status = IRQ_DISABLED; |
| 200 | irq_desc[i].action = NULL; |
| 201 | irq_desc[i].depth = 1; |
| 202 | irq_desc[i].handler = &sys_irq_controller; |
| 203 | } |
| 204 | |
| 205 | /* init gpio irqs */ |
| 206 | gpio_irq_base = VR4181_GPIO_IRQ_BASE; |
| 207 | for (i=gpio_irq_base; i < gpio_irq_base + VR4181_NUM_GPIO_IRQ; i++) { |
| 208 | irq_desc[i].status = IRQ_DISABLED; |
| 209 | irq_desc[i].action = NULL; |
| 210 | irq_desc[i].depth = 1; |
| 211 | irq_desc[i].handler = &gpio_irq_controller; |
| 212 | } |
| 213 | |
| 214 | /* Default all ICU IRQs to off ... */ |
| 215 | *VR4181_MSYSINT1REG = 0; |
| 216 | *VR4181_MSYSINT2REG = 0; |
| 217 | |
| 218 | /* We initialize the level 2 ICU registers to all bits disabled. */ |
| 219 | *VR4181_MPIUINTREG = 0; |
| 220 | *VR4181_MAIUINTREG = 0; |
| 221 | *VR4181_MKIUINTREG = 0; |
| 222 | |
| 223 | /* disable all GPIO intrs */ |
| 224 | *VR4181_GPINTMSK = 0xffff; |
| 225 | |
| 226 | /* vector handler. What these do is register the IRQ as non-sharable */ |
| 227 | setup_irq(VR4181_IRQ_INT0, &cascade); |
| 228 | setup_irq(VR4181_IRQ_GIU, &cascade); |
| 229 | |
| 230 | /* |
| 231 | * RTC interrupts are interesting. They have two destinations. |
| 232 | * One is at sys irq controller, and the other is at CPU IP3 and IP4. |
| 233 | * RTC timer is used as system timer. |
| 234 | * We enable them here, but timer routine will register later |
| 235 | * with CPU IP3/IP4. |
| 236 | */ |
| 237 | setup_irq(VR4181_IRQ_RTCL1, &reserved); |
| 238 | setup_irq(VR4181_IRQ_RTCL2, &reserved); |
| 239 | } |