GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 1 | /* |
| 2 | * linux/arch/unicore32/kernel/irq.c |
| 3 | * |
| 4 | * Code specific to PKUnity SoC and UniCore ISA |
| 5 | * |
| 6 | * Copyright (C) 2001-2010 GUAN Xue-tao |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License version 2 as |
| 10 | * published by the Free Software Foundation. |
| 11 | */ |
| 12 | #include <linux/kernel_stat.h> |
| 13 | #include <linux/module.h> |
| 14 | #include <linux/signal.h> |
| 15 | #include <linux/ioport.h> |
| 16 | #include <linux/interrupt.h> |
| 17 | #include <linux/irq.h> |
| 18 | #include <linux/random.h> |
| 19 | #include <linux/smp.h> |
| 20 | #include <linux/init.h> |
| 21 | #include <linux/seq_file.h> |
| 22 | #include <linux/errno.h> |
| 23 | #include <linux/list.h> |
| 24 | #include <linux/kallsyms.h> |
| 25 | #include <linux/proc_fs.h> |
Rafael J. Wysocki | f98bf4a | 2011-04-26 19:14:47 +0200 | [diff] [blame] | 26 | #include <linux/syscore_ops.h> |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 27 | #include <linux/gpio.h> |
| 28 | |
| 29 | #include <asm/system.h> |
| 30 | #include <mach/hardware.h> |
| 31 | |
| 32 | #include "setup.h" |
| 33 | |
| 34 | /* |
| 35 | * PKUnity GPIO edge detection for IRQs: |
| 36 | * IRQs are generated on Falling-Edge, Rising-Edge, or both. |
| 37 | * Use this instead of directly setting GRER/GFER. |
| 38 | */ |
| 39 | static int GPIO_IRQ_rising_edge; |
| 40 | static int GPIO_IRQ_falling_edge; |
| 41 | static int GPIO_IRQ_mask = 0; |
| 42 | |
| 43 | #define GPIO_MASK(irq) (1 << (irq - IRQ_GPIO0)) |
| 44 | |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 45 | static int puv3_gpio_type(struct irq_data *d, unsigned int type) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 46 | { |
| 47 | unsigned int mask; |
| 48 | |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 49 | if (d->irq < IRQ_GPIOHIGH) |
| 50 | mask = 1 << d->irq; |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 51 | else |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 52 | mask = GPIO_MASK(d->irq); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 53 | |
| 54 | if (type == IRQ_TYPE_PROBE) { |
| 55 | if ((GPIO_IRQ_rising_edge | GPIO_IRQ_falling_edge) & mask) |
| 56 | return 0; |
| 57 | type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; |
| 58 | } |
| 59 | |
| 60 | if (type & IRQ_TYPE_EDGE_RISING) |
| 61 | GPIO_IRQ_rising_edge |= mask; |
| 62 | else |
| 63 | GPIO_IRQ_rising_edge &= ~mask; |
| 64 | if (type & IRQ_TYPE_EDGE_FALLING) |
| 65 | GPIO_IRQ_falling_edge |= mask; |
| 66 | else |
| 67 | GPIO_IRQ_falling_edge &= ~mask; |
| 68 | |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 69 | writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER); |
| 70 | writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 71 | |
| 72 | return 0; |
| 73 | } |
| 74 | |
| 75 | /* |
| 76 | * GPIO IRQs must be acknowledged. This is for IRQs from 0 to 7. |
| 77 | */ |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 78 | static void puv3_low_gpio_ack(struct irq_data *d) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 79 | { |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 80 | writel((1 << d->irq), GPIO_GEDR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 81 | } |
| 82 | |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 83 | static void puv3_low_gpio_mask(struct irq_data *d) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 84 | { |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 85 | writel(readl(INTC_ICMR) & ~(1 << d->irq), INTC_ICMR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 86 | } |
| 87 | |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 88 | static void puv3_low_gpio_unmask(struct irq_data *d) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 89 | { |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 90 | writel(readl(INTC_ICMR) | (1 << d->irq), INTC_ICMR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 91 | } |
| 92 | |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 93 | static int puv3_low_gpio_wake(struct irq_data *d, unsigned int on) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 94 | { |
| 95 | if (on) |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 96 | writel(readl(PM_PWER) | (1 << d->irq), PM_PWER); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 97 | else |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 98 | writel(readl(PM_PWER) & ~(1 << d->irq), PM_PWER); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 99 | return 0; |
| 100 | } |
| 101 | |
| 102 | static struct irq_chip puv3_low_gpio_chip = { |
| 103 | .name = "GPIO-low", |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 104 | .irq_ack = puv3_low_gpio_ack, |
| 105 | .irq_mask = puv3_low_gpio_mask, |
| 106 | .irq_unmask = puv3_low_gpio_unmask, |
| 107 | .irq_set_type = puv3_gpio_type, |
| 108 | .irq_set_wake = puv3_low_gpio_wake, |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 109 | }; |
| 110 | |
| 111 | /* |
| 112 | * IRQ8 (GPIO0 through 27) handler. We enter here with the |
| 113 | * irq_controller_lock held, and IRQs disabled. Decode the IRQ |
| 114 | * and call the handler. |
| 115 | */ |
| 116 | static void |
| 117 | puv3_gpio_handler(unsigned int irq, struct irq_desc *desc) |
| 118 | { |
| 119 | unsigned int mask; |
| 120 | |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 121 | mask = readl(GPIO_GEDR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 122 | do { |
| 123 | /* |
| 124 | * clear down all currently active IRQ sources. |
| 125 | * We will be processing them all. |
| 126 | */ |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 127 | writel(mask, GPIO_GEDR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 128 | |
| 129 | irq = IRQ_GPIO0; |
| 130 | do { |
| 131 | if (mask & 1) |
| 132 | generic_handle_irq(irq); |
| 133 | mask >>= 1; |
| 134 | irq++; |
| 135 | } while (mask); |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 136 | mask = readl(GPIO_GEDR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 137 | } while (mask); |
| 138 | } |
| 139 | |
| 140 | /* |
| 141 | * GPIO0-27 edge IRQs need to be handled specially. |
| 142 | * In addition, the IRQs are all collected up into one bit in the |
| 143 | * interrupt controller registers. |
| 144 | */ |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 145 | static void puv3_high_gpio_ack(struct irq_data *d) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 146 | { |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 147 | unsigned int mask = GPIO_MASK(d->irq); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 148 | |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 149 | writel(mask, GPIO_GEDR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 150 | } |
| 151 | |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 152 | static void puv3_high_gpio_mask(struct irq_data *d) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 153 | { |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 154 | unsigned int mask = GPIO_MASK(d->irq); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 155 | |
| 156 | GPIO_IRQ_mask &= ~mask; |
| 157 | |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 158 | writel(readl(GPIO_GRER) & ~mask, GPIO_GRER); |
| 159 | writel(readl(GPIO_GFER) & ~mask, GPIO_GFER); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 160 | } |
| 161 | |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 162 | static void puv3_high_gpio_unmask(struct irq_data *d) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 163 | { |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 164 | unsigned int mask = GPIO_MASK(d->irq); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 165 | |
| 166 | GPIO_IRQ_mask |= mask; |
| 167 | |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 168 | writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER); |
| 169 | writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 170 | } |
| 171 | |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 172 | static int puv3_high_gpio_wake(struct irq_data *d, unsigned int on) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 173 | { |
| 174 | if (on) |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 175 | writel(readl(PM_PWER) | PM_PWER_GPIOHIGH, PM_PWER); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 176 | else |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 177 | writel(readl(PM_PWER) & ~PM_PWER_GPIOHIGH, PM_PWER); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 178 | return 0; |
| 179 | } |
| 180 | |
| 181 | static struct irq_chip puv3_high_gpio_chip = { |
| 182 | .name = "GPIO-high", |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 183 | .irq_ack = puv3_high_gpio_ack, |
| 184 | .irq_mask = puv3_high_gpio_mask, |
| 185 | .irq_unmask = puv3_high_gpio_unmask, |
| 186 | .irq_set_type = puv3_gpio_type, |
| 187 | .irq_set_wake = puv3_high_gpio_wake, |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 188 | }; |
| 189 | |
| 190 | /* |
| 191 | * We don't need to ACK IRQs on the PKUnity unless they're GPIOs |
| 192 | * this is for internal IRQs i.e. from 8 to 31. |
| 193 | */ |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 194 | static void puv3_mask_irq(struct irq_data *d) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 195 | { |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 196 | writel(readl(INTC_ICMR) & ~(1 << d->irq), INTC_ICMR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 197 | } |
| 198 | |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 199 | static void puv3_unmask_irq(struct irq_data *d) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 200 | { |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 201 | writel(readl(INTC_ICMR) | (1 << d->irq), INTC_ICMR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 202 | } |
| 203 | |
| 204 | /* |
| 205 | * Apart form GPIOs, only the RTC alarm can be a wakeup event. |
| 206 | */ |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 207 | static int puv3_set_wake(struct irq_data *d, unsigned int on) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 208 | { |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 209 | if (d->irq == IRQ_RTCAlarm) { |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 210 | if (on) |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 211 | writel(readl(PM_PWER) | PM_PWER_RTC, PM_PWER); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 212 | else |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 213 | writel(readl(PM_PWER) & ~PM_PWER_RTC, PM_PWER); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 214 | return 0; |
| 215 | } |
| 216 | return -EINVAL; |
| 217 | } |
| 218 | |
| 219 | static struct irq_chip puv3_normal_chip = { |
| 220 | .name = "PKUnity-v3", |
GuanXuetao | 36a8b8c | 2011-02-17 19:15:36 +0800 | [diff] [blame] | 221 | .irq_ack = puv3_mask_irq, |
| 222 | .irq_mask = puv3_mask_irq, |
| 223 | .irq_unmask = puv3_unmask_irq, |
| 224 | .irq_set_wake = puv3_set_wake, |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 225 | }; |
| 226 | |
| 227 | static struct resource irq_resource = { |
| 228 | .name = "irqs", |
GuanXuetao | 1cf46c4 | 2011-03-04 18:07:48 +0800 | [diff] [blame] | 229 | .start = io_v2p(PKUNITY_INTC_BASE), |
| 230 | .end = io_v2p(PKUNITY_INTC_BASE) + 0xFFFFF, |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 231 | }; |
| 232 | |
| 233 | static struct puv3_irq_state { |
| 234 | unsigned int saved; |
| 235 | unsigned int icmr; |
| 236 | unsigned int iclr; |
| 237 | unsigned int iccr; |
| 238 | } puv3_irq_state; |
| 239 | |
Rafael J. Wysocki | f98bf4a | 2011-04-26 19:14:47 +0200 | [diff] [blame] | 240 | static int puv3_irq_suspend(void) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 241 | { |
| 242 | struct puv3_irq_state *st = &puv3_irq_state; |
| 243 | |
| 244 | st->saved = 1; |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 245 | st->icmr = readl(INTC_ICMR); |
| 246 | st->iclr = readl(INTC_ICLR); |
| 247 | st->iccr = readl(INTC_ICCR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 248 | |
| 249 | /* |
| 250 | * Disable all GPIO-based interrupts. |
| 251 | */ |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 252 | writel(readl(INTC_ICMR) & ~(0x1ff), INTC_ICMR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 253 | |
| 254 | /* |
| 255 | * Set the appropriate edges for wakeup. |
| 256 | */ |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 257 | writel(readl(PM_PWER) & GPIO_IRQ_rising_edge, GPIO_GRER); |
| 258 | writel(readl(PM_PWER) & GPIO_IRQ_falling_edge, GPIO_GFER); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 259 | |
| 260 | /* |
| 261 | * Clear any pending GPIO interrupts. |
| 262 | */ |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 263 | writel(readl(GPIO_GEDR), GPIO_GEDR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 264 | |
| 265 | return 0; |
| 266 | } |
| 267 | |
Rafael J. Wysocki | f98bf4a | 2011-04-26 19:14:47 +0200 | [diff] [blame] | 268 | static void puv3_irq_resume(void) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 269 | { |
| 270 | struct puv3_irq_state *st = &puv3_irq_state; |
| 271 | |
| 272 | if (st->saved) { |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 273 | writel(st->iccr, INTC_ICCR); |
| 274 | writel(st->iclr, INTC_ICLR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 275 | |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 276 | writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER); |
| 277 | writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 278 | |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 279 | writel(st->icmr, INTC_ICMR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 280 | } |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 281 | } |
| 282 | |
Rafael J. Wysocki | f98bf4a | 2011-04-26 19:14:47 +0200 | [diff] [blame] | 283 | static struct syscore_ops puv3_irq_syscore_ops = { |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 284 | .suspend = puv3_irq_suspend, |
| 285 | .resume = puv3_irq_resume, |
| 286 | }; |
| 287 | |
Rafael J. Wysocki | f98bf4a | 2011-04-26 19:14:47 +0200 | [diff] [blame] | 288 | static int __init puv3_irq_init_syscore(void) |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 289 | { |
Rafael J. Wysocki | f98bf4a | 2011-04-26 19:14:47 +0200 | [diff] [blame] | 290 | register_syscore_ops(&puv3_irq_syscore_ops); |
| 291 | return 0; |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 292 | } |
| 293 | |
Rafael J. Wysocki | f98bf4a | 2011-04-26 19:14:47 +0200 | [diff] [blame] | 294 | device_initcall(puv3_irq_init_syscore); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 295 | |
| 296 | void __init init_IRQ(void) |
| 297 | { |
| 298 | unsigned int irq; |
| 299 | |
| 300 | request_resource(&iomem_resource, &irq_resource); |
| 301 | |
| 302 | /* disable all IRQs */ |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 303 | writel(0, INTC_ICMR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 304 | |
| 305 | /* all IRQs are IRQ, not REAL */ |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 306 | writel(0, INTC_ICLR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 307 | |
| 308 | /* clear all GPIO edge detects */ |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 309 | writel(FMASK(8, 0) & ~FIELD(1, 1, GPI_SOFF_REQ), GPIO_GPIR); |
| 310 | writel(0, GPIO_GFER); |
| 311 | writel(0, GPIO_GRER); |
| 312 | writel(0x0FFFFFFF, GPIO_GEDR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 313 | |
GuanXuetao | e5abf78 | 2011-02-26 21:21:18 +0800 | [diff] [blame] | 314 | writel(1, INTC_ICCR); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 315 | |
| 316 | for (irq = 0; irq < IRQ_GPIOHIGH; irq++) { |
Thomas Gleixner | e1f5ce81 | 2011-03-24 18:26:16 +0100 | [diff] [blame] | 317 | irq_set_chip(irq, &puv3_low_gpio_chip); |
| 318 | irq_set_handler(irq, handle_edge_irq); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 319 | irq_modify_status(irq, |
| 320 | IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, |
| 321 | 0); |
| 322 | } |
| 323 | |
| 324 | for (irq = IRQ_GPIOHIGH + 1; irq < IRQ_GPIO0; irq++) { |
Thomas Gleixner | e1f5ce81 | 2011-03-24 18:26:16 +0100 | [diff] [blame] | 325 | irq_set_chip(irq, &puv3_normal_chip); |
| 326 | irq_set_handler(irq, handle_level_irq); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 327 | irq_modify_status(irq, |
| 328 | IRQ_NOREQUEST | IRQ_NOAUTOEN, |
| 329 | IRQ_NOPROBE); |
| 330 | } |
| 331 | |
| 332 | for (irq = IRQ_GPIO0; irq <= IRQ_GPIO27; irq++) { |
Thomas Gleixner | e1f5ce81 | 2011-03-24 18:26:16 +0100 | [diff] [blame] | 333 | irq_set_chip(irq, &puv3_high_gpio_chip); |
| 334 | irq_set_handler(irq, handle_edge_irq); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 335 | irq_modify_status(irq, |
| 336 | IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, |
| 337 | 0); |
| 338 | } |
| 339 | |
| 340 | /* |
| 341 | * Install handler for GPIO 0-27 edge detect interrupts |
| 342 | */ |
Thomas Gleixner | e1f5ce81 | 2011-03-24 18:26:16 +0100 | [diff] [blame] | 343 | irq_set_chip(IRQ_GPIOHIGH, &puv3_normal_chip); |
| 344 | irq_set_chained_handler(IRQ_GPIOHIGH, puv3_gpio_handler); |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 345 | |
| 346 | #ifdef CONFIG_PUV3_GPIO |
| 347 | puv3_init_gpio(); |
| 348 | #endif |
| 349 | } |
| 350 | |
GuanXuetao | 752bcb4 | 2011-01-15 18:19:35 +0800 | [diff] [blame] | 351 | /* |
| 352 | * do_IRQ handles all hardware IRQ's. Decoded IRQs should not |
| 353 | * come via this function. Instead, they should provide their |
| 354 | * own 'handler' |
| 355 | */ |
| 356 | asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) |
| 357 | { |
| 358 | struct pt_regs *old_regs = set_irq_regs(regs); |
| 359 | |
| 360 | irq_enter(); |
| 361 | |
| 362 | /* |
| 363 | * Some hardware gives randomly wrong interrupts. Rather |
| 364 | * than crashing, do something sensible. |
| 365 | */ |
| 366 | if (unlikely(irq >= nr_irqs)) { |
| 367 | if (printk_ratelimit()) |
| 368 | printk(KERN_WARNING "Bad IRQ%u\n", irq); |
| 369 | ack_bad_irq(irq); |
| 370 | } else { |
| 371 | generic_handle_irq(irq); |
| 372 | } |
| 373 | |
| 374 | irq_exit(); |
| 375 | set_irq_regs(old_regs); |
| 376 | } |
| 377 | |