Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * linux/arch/arm/mach-pxa/irq.c |
| 3 | * |
| 4 | * Generic PXA IRQ handling, GPIO IRQ demultiplexing, etc. |
| 5 | * |
| 6 | * Author: Nicolas Pitre |
| 7 | * Created: Jun 15, 2001 |
| 8 | * Copyright: MontaVista Software Inc. |
| 9 | * |
| 10 | * This program is free software; you can redistribute it and/or modify |
| 11 | * it under the terms of the GNU General Public License version 2 as |
| 12 | * published by the Free Software Foundation. |
| 13 | */ |
| 14 | |
| 15 | #include <linux/init.h> |
| 16 | #include <linux/module.h> |
| 17 | #include <linux/interrupt.h> |
eric miao | c0165504 | 2008-01-28 23:00:02 +0000 | [diff] [blame] | 18 | #include <linux/sysdev.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 19 | |
| 20 | #include <asm/hardware.h> |
| 21 | #include <asm/irq.h> |
| 22 | #include <asm/mach/irq.h> |
| 23 | #include <asm/arch/pxa-regs.h> |
| 24 | |
| 25 | #include "generic.h" |
| 26 | |
| 27 | |
| 28 | /* |
| 29 | * This is for peripheral IRQs internal to the PXA chip. |
| 30 | */ |
| 31 | |
| 32 | static void pxa_mask_low_irq(unsigned int irq) |
| 33 | { |
Eric Miao | 486c955 | 2007-06-06 06:22:20 +0100 | [diff] [blame] | 34 | ICMR &= ~(1 << irq); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 35 | } |
| 36 | |
| 37 | static void pxa_unmask_low_irq(unsigned int irq) |
| 38 | { |
Eric Miao | 486c955 | 2007-06-06 06:22:20 +0100 | [diff] [blame] | 39 | ICMR |= (1 << irq); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | } |
| 41 | |
David Brownell | 38c677c | 2006-08-01 22:26:25 +0100 | [diff] [blame] | 42 | static struct irq_chip pxa_internal_chip_low = { |
| 43 | .name = "SC", |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 44 | .ack = pxa_mask_low_irq, |
| 45 | .mask = pxa_mask_low_irq, |
| 46 | .unmask = pxa_unmask_low_irq, |
| 47 | }; |
| 48 | |
Eric Miao | 53665a5 | 2007-06-06 06:36:04 +0100 | [diff] [blame] | 49 | void __init pxa_init_irq_low(void) |
| 50 | { |
| 51 | int irq; |
| 52 | |
| 53 | /* disable all IRQs */ |
| 54 | ICMR = 0; |
| 55 | |
| 56 | /* all IRQs are IRQ, not FIQ */ |
| 57 | ICLR = 0; |
| 58 | |
| 59 | /* only unmasked interrupts kick us out of idle */ |
| 60 | ICCR = 1; |
| 61 | |
| 62 | for (irq = PXA_IRQ(0); irq <= PXA_IRQ(31); irq++) { |
| 63 | set_irq_chip(irq, &pxa_internal_chip_low); |
| 64 | set_irq_handler(irq, handle_level_irq); |
| 65 | set_irq_flags(irq, IRQF_VALID); |
| 66 | } |
| 67 | } |
| 68 | |
eric miao | 2c8086a | 2007-09-11 19:13:17 -0700 | [diff] [blame] | 69 | #if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 70 | |
| 71 | /* |
| 72 | * This is for the second set of internal IRQs as found on the PXA27x. |
| 73 | */ |
| 74 | |
| 75 | static void pxa_mask_high_irq(unsigned int irq) |
| 76 | { |
Eric Miao | 486c955 | 2007-06-06 06:22:20 +0100 | [diff] [blame] | 77 | ICMR2 &= ~(1 << (irq - 32)); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 78 | } |
| 79 | |
| 80 | static void pxa_unmask_high_irq(unsigned int irq) |
| 81 | { |
Eric Miao | 486c955 | 2007-06-06 06:22:20 +0100 | [diff] [blame] | 82 | ICMR2 |= (1 << (irq - 32)); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 83 | } |
| 84 | |
David Brownell | 38c677c | 2006-08-01 22:26:25 +0100 | [diff] [blame] | 85 | static struct irq_chip pxa_internal_chip_high = { |
| 86 | .name = "SC-hi", |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 87 | .ack = pxa_mask_high_irq, |
| 88 | .mask = pxa_mask_high_irq, |
| 89 | .unmask = pxa_unmask_high_irq, |
| 90 | }; |
| 91 | |
Eric Miao | c08b7b3 | 2007-06-06 06:32:38 +0100 | [diff] [blame] | 92 | void __init pxa_init_irq_high(void) |
| 93 | { |
| 94 | int irq; |
| 95 | |
| 96 | ICMR2 = 0; |
| 97 | ICLR2 = 0; |
| 98 | |
| 99 | for (irq = PXA_IRQ(32); irq < PXA_IRQ(64); irq++) { |
| 100 | set_irq_chip(irq, &pxa_internal_chip_high); |
| 101 | set_irq_handler(irq, handle_level_irq); |
| 102 | set_irq_flags(irq, IRQF_VALID); |
| 103 | } |
| 104 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 105 | #endif |
| 106 | |
| 107 | /* |
| 108 | * PXA GPIO edge detection for IRQs: |
| 109 | * IRQs are generated on Falling-Edge, Rising-Edge, or both. |
| 110 | * Use this instead of directly setting GRER/GFER. |
| 111 | */ |
| 112 | |
| 113 | static long GPIO_IRQ_rising_edge[4]; |
| 114 | static long GPIO_IRQ_falling_edge[4]; |
| 115 | static long GPIO_IRQ_mask[4]; |
| 116 | |
| 117 | static int pxa_gpio_irq_type(unsigned int irq, unsigned int type) |
| 118 | { |
| 119 | int gpio, idx; |
| 120 | |
| 121 | gpio = IRQ_TO_GPIO(irq); |
| 122 | idx = gpio >> 5; |
| 123 | |
| 124 | if (type == IRQT_PROBE) { |
| 125 | /* Don't mess with enabled GPIOs using preconfigured edges or |
Guennadi Liakhovetski | e033108 | 2006-06-28 16:42:02 +0100 | [diff] [blame] | 126 | GPIOs set to alternate function or to output during probe */ |
| 127 | if ((GPIO_IRQ_rising_edge[idx] | GPIO_IRQ_falling_edge[idx] | GPDR(gpio)) & |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 128 | GPIO_bit(gpio)) |
| 129 | return 0; |
| 130 | if (GAFR(gpio) & (0x3 << (((gpio) & 0xf)*2))) |
| 131 | return 0; |
| 132 | type = __IRQT_RISEDGE | __IRQT_FALEDGE; |
| 133 | } |
| 134 | |
| 135 | /* printk(KERN_DEBUG "IRQ%d (GPIO%d): ", irq, gpio); */ |
| 136 | |
| 137 | pxa_gpio_mode(gpio | GPIO_IN); |
| 138 | |
| 139 | if (type & __IRQT_RISEDGE) { |
| 140 | /* printk("rising "); */ |
| 141 | __set_bit (gpio, GPIO_IRQ_rising_edge); |
Philipp Zabel | 4fe4a2b | 2007-02-26 01:44:57 +0100 | [diff] [blame] | 142 | } else { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 143 | __clear_bit (gpio, GPIO_IRQ_rising_edge); |
Philipp Zabel | 4fe4a2b | 2007-02-26 01:44:57 +0100 | [diff] [blame] | 144 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 145 | |
| 146 | if (type & __IRQT_FALEDGE) { |
| 147 | /* printk("falling "); */ |
| 148 | __set_bit (gpio, GPIO_IRQ_falling_edge); |
Philipp Zabel | 4fe4a2b | 2007-02-26 01:44:57 +0100 | [diff] [blame] | 149 | } else { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 150 | __clear_bit (gpio, GPIO_IRQ_falling_edge); |
Philipp Zabel | 4fe4a2b | 2007-02-26 01:44:57 +0100 | [diff] [blame] | 151 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 152 | |
| 153 | /* printk("edges\n"); */ |
| 154 | |
| 155 | GRER(gpio) = GPIO_IRQ_rising_edge[idx] & GPIO_IRQ_mask[idx]; |
| 156 | GFER(gpio) = GPIO_IRQ_falling_edge[idx] & GPIO_IRQ_mask[idx]; |
| 157 | return 0; |
| 158 | } |
| 159 | |
| 160 | /* |
| 161 | * GPIO IRQs must be acknowledged. This is for GPIO 0 and 1. |
| 162 | */ |
| 163 | |
| 164 | static void pxa_ack_low_gpio(unsigned int irq) |
| 165 | { |
| 166 | GEDR0 = (1 << (irq - IRQ_GPIO0)); |
| 167 | } |
| 168 | |
David Brownell | 38c677c | 2006-08-01 22:26:25 +0100 | [diff] [blame] | 169 | static struct irq_chip pxa_low_gpio_chip = { |
| 170 | .name = "GPIO-l", |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 171 | .ack = pxa_ack_low_gpio, |
| 172 | .mask = pxa_mask_low_irq, |
| 173 | .unmask = pxa_unmask_low_irq, |
Russell King | 7801907 | 2005-09-04 19:43:13 +0100 | [diff] [blame] | 174 | .set_type = pxa_gpio_irq_type, |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 175 | }; |
| 176 | |
| 177 | /* |
| 178 | * Demux handler for GPIO>=2 edge detect interrupts |
| 179 | */ |
| 180 | |
Russell King | 10dd5ce | 2006-11-23 11:41:32 +0000 | [diff] [blame] | 181 | static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 182 | { |
| 183 | unsigned int mask; |
| 184 | int loop; |
| 185 | |
| 186 | do { |
| 187 | loop = 0; |
| 188 | |
Eric Miao | 4a3dcd3 | 2007-06-06 06:45:18 +0100 | [diff] [blame] | 189 | mask = GEDR0 & GPIO_IRQ_mask[0] & ~3; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 190 | if (mask) { |
| 191 | GEDR0 = mask; |
| 192 | irq = IRQ_GPIO(2); |
| 193 | desc = irq_desc + irq; |
| 194 | mask >>= 2; |
| 195 | do { |
| 196 | if (mask & 1) |
Linus Torvalds | 0cd61b6 | 2006-10-06 10:53:39 -0700 | [diff] [blame] | 197 | desc_handle_irq(irq, desc); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 198 | irq++; |
| 199 | desc++; |
| 200 | mask >>= 1; |
| 201 | } while (mask); |
| 202 | loop = 1; |
| 203 | } |
| 204 | |
Eric Miao | 4a3dcd3 | 2007-06-06 06:45:18 +0100 | [diff] [blame] | 205 | mask = GEDR1 & GPIO_IRQ_mask[1]; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 206 | if (mask) { |
| 207 | GEDR1 = mask; |
| 208 | irq = IRQ_GPIO(32); |
| 209 | desc = irq_desc + irq; |
| 210 | do { |
| 211 | if (mask & 1) |
Linus Torvalds | 0cd61b6 | 2006-10-06 10:53:39 -0700 | [diff] [blame] | 212 | desc_handle_irq(irq, desc); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 213 | irq++; |
| 214 | desc++; |
| 215 | mask >>= 1; |
| 216 | } while (mask); |
| 217 | loop = 1; |
| 218 | } |
| 219 | |
Eric Miao | 4a3dcd3 | 2007-06-06 06:45:18 +0100 | [diff] [blame] | 220 | mask = GEDR2 & GPIO_IRQ_mask[2]; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 221 | if (mask) { |
| 222 | GEDR2 = mask; |
| 223 | irq = IRQ_GPIO(64); |
| 224 | desc = irq_desc + irq; |
| 225 | do { |
| 226 | if (mask & 1) |
Linus Torvalds | 0cd61b6 | 2006-10-06 10:53:39 -0700 | [diff] [blame] | 227 | desc_handle_irq(irq, desc); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 228 | irq++; |
| 229 | desc++; |
| 230 | mask >>= 1; |
| 231 | } while (mask); |
| 232 | loop = 1; |
| 233 | } |
| 234 | |
Eric Miao | 4a3dcd3 | 2007-06-06 06:45:18 +0100 | [diff] [blame] | 235 | mask = GEDR3 & GPIO_IRQ_mask[3]; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 236 | if (mask) { |
| 237 | GEDR3 = mask; |
| 238 | irq = IRQ_GPIO(96); |
| 239 | desc = irq_desc + irq; |
| 240 | do { |
| 241 | if (mask & 1) |
Linus Torvalds | 0cd61b6 | 2006-10-06 10:53:39 -0700 | [diff] [blame] | 242 | desc_handle_irq(irq, desc); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 243 | irq++; |
| 244 | desc++; |
| 245 | mask >>= 1; |
| 246 | } while (mask); |
| 247 | loop = 1; |
| 248 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 249 | } while (loop); |
| 250 | } |
| 251 | |
| 252 | static void pxa_ack_muxed_gpio(unsigned int irq) |
| 253 | { |
| 254 | int gpio = irq - IRQ_GPIO(2) + 2; |
| 255 | GEDR(gpio) = GPIO_bit(gpio); |
| 256 | } |
| 257 | |
| 258 | static void pxa_mask_muxed_gpio(unsigned int irq) |
| 259 | { |
| 260 | int gpio = irq - IRQ_GPIO(2) + 2; |
| 261 | __clear_bit(gpio, GPIO_IRQ_mask); |
| 262 | GRER(gpio) &= ~GPIO_bit(gpio); |
| 263 | GFER(gpio) &= ~GPIO_bit(gpio); |
| 264 | } |
| 265 | |
| 266 | static void pxa_unmask_muxed_gpio(unsigned int irq) |
| 267 | { |
| 268 | int gpio = irq - IRQ_GPIO(2) + 2; |
| 269 | int idx = gpio >> 5; |
| 270 | __set_bit(gpio, GPIO_IRQ_mask); |
| 271 | GRER(gpio) = GPIO_IRQ_rising_edge[idx] & GPIO_IRQ_mask[idx]; |
| 272 | GFER(gpio) = GPIO_IRQ_falling_edge[idx] & GPIO_IRQ_mask[idx]; |
| 273 | } |
| 274 | |
David Brownell | 38c677c | 2006-08-01 22:26:25 +0100 | [diff] [blame] | 275 | static struct irq_chip pxa_muxed_gpio_chip = { |
| 276 | .name = "GPIO", |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 277 | .ack = pxa_ack_muxed_gpio, |
| 278 | .mask = pxa_mask_muxed_gpio, |
| 279 | .unmask = pxa_unmask_muxed_gpio, |
Russell King | 7801907 | 2005-09-04 19:43:13 +0100 | [diff] [blame] | 280 | .set_type = pxa_gpio_irq_type, |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 281 | }; |
| 282 | |
Eric Miao | 348f2e3 | 2007-06-06 06:37:15 +0100 | [diff] [blame] | 283 | void __init pxa_init_irq_gpio(int gpio_nr) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 284 | { |
Eric Miao | 348f2e3 | 2007-06-06 06:37:15 +0100 | [diff] [blame] | 285 | int irq, i; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 286 | |
eric miao | 30f0b40 | 2007-08-29 10:18:47 +0100 | [diff] [blame] | 287 | pxa_last_gpio = gpio_nr - 1; |
| 288 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 289 | /* clear all GPIO edge detects */ |
Eric Miao | 348f2e3 | 2007-06-06 06:37:15 +0100 | [diff] [blame] | 290 | for (i = 0; i < gpio_nr; i += 32) { |
| 291 | GFER(i) = 0; |
| 292 | GRER(i) = 0; |
| 293 | GEDR(i) = GEDR(i); |
| 294 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 295 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 296 | /* GPIO 0 and 1 must have their mask bit always set */ |
| 297 | GPIO_IRQ_mask[0] = 3; |
| 298 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 299 | for (irq = IRQ_GPIO0; irq <= IRQ_GPIO1; irq++) { |
| 300 | set_irq_chip(irq, &pxa_low_gpio_chip); |
Russell King | 10dd5ce | 2006-11-23 11:41:32 +0000 | [diff] [blame] | 301 | set_irq_handler(irq, handle_edge_irq); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 302 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); |
| 303 | } |
| 304 | |
Samuel | fd51bcc | 2007-08-28 19:56:34 +0100 | [diff] [blame] | 305 | for (irq = IRQ_GPIO(2); irq < IRQ_GPIO(gpio_nr); irq++) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 306 | set_irq_chip(irq, &pxa_muxed_gpio_chip); |
Russell King | 10dd5ce | 2006-11-23 11:41:32 +0000 | [diff] [blame] | 307 | set_irq_handler(irq, handle_edge_irq); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 308 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); |
| 309 | } |
| 310 | |
| 311 | /* Install handler for GPIO>=2 edge detect interrupts */ |
| 312 | set_irq_chip(IRQ_GPIO_2_x, &pxa_internal_chip_low); |
| 313 | set_irq_chained_handler(IRQ_GPIO_2_x, pxa_gpio_demux_handler); |
Philipp Zabel | 1c44f5f | 2008-02-04 22:28:22 -0800 | [diff] [blame] | 314 | |
| 315 | pxa_init_gpio(gpio_nr); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 316 | } |
eric miao | c95530c | 2007-08-29 10:22:17 +0100 | [diff] [blame] | 317 | |
| 318 | void __init pxa_init_irq_set_wake(int (*set_wake)(unsigned int, unsigned int)) |
| 319 | { |
| 320 | pxa_internal_chip_low.set_wake = set_wake; |
| 321 | #ifdef CONFIG_PXA27x |
| 322 | pxa_internal_chip_high.set_wake = set_wake; |
| 323 | #endif |
| 324 | pxa_low_gpio_chip.set_wake = set_wake; |
| 325 | pxa_muxed_gpio_chip.set_wake = set_wake; |
| 326 | } |
eric miao | c0165504 | 2008-01-28 23:00:02 +0000 | [diff] [blame] | 327 | |
| 328 | #ifdef CONFIG_PM |
| 329 | static unsigned long saved_icmr[2]; |
| 330 | |
| 331 | static int pxa_irq_suspend(struct sys_device *dev, pm_message_t state) |
| 332 | { |
| 333 | switch (dev->id) { |
| 334 | case 0: |
| 335 | saved_icmr[0] = ICMR; |
| 336 | ICMR = 0; |
| 337 | break; |
| 338 | #if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) |
| 339 | case 1: |
| 340 | saved_icmr[1] = ICMR2; |
| 341 | ICMR2 = 0; |
| 342 | break; |
| 343 | #endif |
| 344 | default: |
| 345 | return -EINVAL; |
| 346 | } |
| 347 | |
| 348 | return 0; |
| 349 | } |
| 350 | |
| 351 | static int pxa_irq_resume(struct sys_device *dev) |
| 352 | { |
| 353 | switch (dev->id) { |
| 354 | case 0: |
| 355 | ICMR = saved_icmr[0]; |
| 356 | ICLR = 0; |
| 357 | ICCR = 1; |
| 358 | break; |
| 359 | #if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) |
| 360 | case 1: |
| 361 | ICMR2 = saved_icmr[1]; |
| 362 | ICLR2 = 0; |
| 363 | break; |
| 364 | #endif |
| 365 | default: |
| 366 | return -EINVAL; |
| 367 | } |
| 368 | |
| 369 | return 0; |
| 370 | } |
| 371 | #else |
| 372 | #define pxa_irq_suspend NULL |
| 373 | #define pxa_irq_resume NULL |
| 374 | #endif |
| 375 | |
| 376 | struct sysdev_class pxa_irq_sysclass = { |
| 377 | .name = "irq", |
| 378 | .suspend = pxa_irq_suspend, |
| 379 | .resume = pxa_irq_resume, |
| 380 | }; |
| 381 | |
| 382 | static int __init pxa_irq_init(void) |
| 383 | { |
| 384 | return sysdev_class_register(&pxa_irq_sysclass); |
| 385 | } |
| 386 | |
| 387 | core_initcall(pxa_irq_init); |