diff options
author | Andrew Victor <andrew@sanpeople.com> | 2006-06-19 10:26:54 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-06-19 10:26:54 -0400 |
commit | 814138ffa488824393d2f49f2720dcd197a7d4cf (patch) | |
tree | 24e86542da43b8e797859a0b085b03a9a3cb5091 /arch/arm/mach-at91rm9200 | |
parent | 683c66bf75ce277b90d658da0c1a0bf1a55cce4c (diff) |
[ARM] 3584/1: AT91RM9200 GPIO suspend/resume support
Patch from Andrew Victor
This patch adds suspend/resume/set_wake support for the AT91RM9200's
GPIO interrupts.
Original patch from David Brownell.
Signed-off-by: Andrew Victor <andrew@sanpeople.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-at91rm9200')
-rw-r--r-- | arch/arm/mach-at91rm9200/gpio.c | 87 |
1 files changed, 84 insertions, 3 deletions
diff --git a/arch/arm/mach-at91rm9200/gpio.c b/arch/arm/mach-at91rm9200/gpio.c index 5ab46274e1a3..3430ea054662 100644 --- a/arch/arm/mach-at91rm9200/gpio.c +++ b/arch/arm/mach-at91rm9200/gpio.c | |||
@@ -213,6 +213,84 @@ EXPORT_SYMBOL(at91_get_gpio_value); | |||
213 | 213 | ||
214 | /*--------------------------------------------------------------------------*/ | 214 | /*--------------------------------------------------------------------------*/ |
215 | 215 | ||
216 | #ifdef CONFIG_PM | ||
217 | |||
218 | static u32 wakeups[BGA_GPIO_BANKS]; | ||
219 | static u32 backups[BGA_GPIO_BANKS]; | ||
220 | |||
221 | static int gpio_irq_set_wake(unsigned pin, unsigned state) | ||
222 | { | ||
223 | unsigned mask = pin_to_mask(pin); | ||
224 | |||
225 | pin -= PIN_BASE; | ||
226 | pin /= 32; | ||
227 | |||
228 | if (unlikely(pin >= BGA_GPIO_BANKS)) | ||
229 | return -EINVAL; | ||
230 | |||
231 | if (state) | ||
232 | wakeups[pin] |= mask; | ||
233 | else | ||
234 | wakeups[pin] &= ~mask; | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | void at91_gpio_suspend(void) | ||
240 | { | ||
241 | int i; | ||
242 | |||
243 | for (i = 0; i < BGA_GPIO_BANKS; i++) { | ||
244 | u32 pio = pio_controller_offset[i]; | ||
245 | |||
246 | /* | ||
247 | * Note: drivers should have disabled GPIO interrupts that | ||
248 | * aren't supposed to be wakeup sources. | ||
249 | * But that is not much good on ARM..... disable_irq() does | ||
250 | * not update the hardware immediately, so the hardware mask | ||
251 | * (IMR) has the wrong value (not current, too much is | ||
252 | * permitted). | ||
253 | * | ||
254 | * Our workaround is to disable all non-wakeup IRQs ... | ||
255 | * which is exactly what correct drivers asked for in the | ||
256 | * first place! | ||
257 | */ | ||
258 | backups[i] = at91_sys_read(pio + PIO_IMR); | ||
259 | at91_sys_write(pio_controller_offset[i] + PIO_IDR, backups[i]); | ||
260 | at91_sys_write(pio_controller_offset[i] + PIO_IER, wakeups[i]); | ||
261 | |||
262 | if (!wakeups[i]) { | ||
263 | disable_irq_wake(AT91_ID_PIOA + i); | ||
264 | at91_sys_write(AT91_PMC_PCDR, 1 << (AT91_ID_PIOA + i)); | ||
265 | } else { | ||
266 | enable_irq_wake(AT91_ID_PIOA + i); | ||
267 | #ifdef CONFIG_PM_DEBUG | ||
268 | printk(KERN_DEBUG "GPIO-%c may wake for %08x\n", "ABCD"[i], wakeups[i]); | ||
269 | #endif | ||
270 | } | ||
271 | } | ||
272 | } | ||
273 | |||
274 | void at91_gpio_resume(void) | ||
275 | { | ||
276 | int i; | ||
277 | |||
278 | for (i = 0; i < BGA_GPIO_BANKS; i++) { | ||
279 | at91_sys_write(pio_controller_offset[i] + PIO_IDR, wakeups[i]); | ||
280 | at91_sys_write(pio_controller_offset[i] + PIO_IER, backups[i]); | ||
281 | } | ||
282 | |||
283 | at91_sys_write(AT91_PMC_PCER, | ||
284 | (1 << AT91_ID_PIOA) | ||
285 | | (1 << AT91_ID_PIOB) | ||
286 | | (1 << AT91_ID_PIOC) | ||
287 | | (1 << AT91_ID_PIOD)); | ||
288 | } | ||
289 | |||
290 | #else | ||
291 | #define gpio_irq_set_wake NULL | ||
292 | #endif | ||
293 | |||
216 | 294 | ||
217 | /* Several AIC controller irqs are dispatched through this GPIO handler. | 295 | /* Several AIC controller irqs are dispatched through this GPIO handler. |
218 | * To use any AT91_PIN_* as an externally triggered IRQ, first call | 296 | * To use any AT91_PIN_* as an externally triggered IRQ, first call |
@@ -252,6 +330,7 @@ static struct irqchip gpio_irqchip = { | |||
252 | .mask = gpio_irq_mask, | 330 | .mask = gpio_irq_mask, |
253 | .unmask = gpio_irq_unmask, | 331 | .unmask = gpio_irq_unmask, |
254 | .set_type = gpio_irq_type, | 332 | .set_type = gpio_irq_type, |
333 | .set_wake = gpio_irq_set_wake, | ||
255 | }; | 334 | }; |
256 | 335 | ||
257 | static void gpio_irq_handler(unsigned irq, struct irqdesc *desc, struct pt_regs *regs) | 336 | static void gpio_irq_handler(unsigned irq, struct irqdesc *desc, struct pt_regs *regs) |
@@ -266,6 +345,7 @@ static void gpio_irq_handler(unsigned irq, struct irqdesc *desc, struct pt_regs | |||
266 | /* temporarily mask (level sensitive) parent IRQ */ | 345 | /* temporarily mask (level sensitive) parent IRQ */ |
267 | desc->chip->ack(irq); | 346 | desc->chip->ack(irq); |
268 | for (;;) { | 347 | for (;;) { |
348 | /* reading ISR acks the pending (edge triggered) GPIO interrupt */ | ||
269 | isr = __raw_readl(pio + PIO_ISR) & __raw_readl(pio + PIO_IMR); | 349 | isr = __raw_readl(pio + PIO_ISR) & __raw_readl(pio + PIO_IMR); |
270 | if (!isr) | 350 | if (!isr) |
271 | break; | 351 | break; |
@@ -315,15 +395,16 @@ void __init at91_gpio_irq_setup(unsigned banks) | |||
315 | set_irq_chipdata(id, controller); | 395 | set_irq_chipdata(id, controller); |
316 | 396 | ||
317 | for (i = 0; i < 32; i++, pin++) { | 397 | for (i = 0; i < 32; i++, pin++) { |
398 | /* | ||
399 | * Can use the "simple" and not "edge" handler since it's | ||
400 | * shorter, and the AIC handles interupts sanely. | ||
401 | */ | ||
318 | set_irq_chip(pin, &gpio_irqchip); | 402 | set_irq_chip(pin, &gpio_irqchip); |
319 | set_irq_handler(pin, do_simple_IRQ); | 403 | set_irq_handler(pin, do_simple_IRQ); |
320 | set_irq_flags(pin, IRQF_VALID); | 404 | set_irq_flags(pin, IRQF_VALID); |
321 | } | 405 | } |
322 | 406 | ||
323 | set_irq_chained_handler(id, gpio_irq_handler); | 407 | set_irq_chained_handler(id, gpio_irq_handler); |
324 | |||
325 | /* enable the PIO peripheral clock */ | ||
326 | at91_sys_write(AT91_PMC_PCER, 1 << id); | ||
327 | } | 408 | } |
328 | pr_info("AT91: %d gpio irqs in %d banks\n", pin - PIN_BASE, banks); | 409 | pr_info("AT91: %d gpio irqs in %d banks\n", pin - PIN_BASE, banks); |
329 | } | 410 | } |