aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-at91rm9200/gpio.c
diff options
context:
space:
mode:
authorAndrew Victor <andrew@sanpeople.com>2006-06-19 10:26:54 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2006-06-19 10:26:54 -0400
commit814138ffa488824393d2f49f2720dcd197a7d4cf (patch)
tree24e86542da43b8e797859a0b085b03a9a3cb5091 /arch/arm/mach-at91rm9200/gpio.c
parent683c66bf75ce277b90d658da0c1a0bf1a55cce4c (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/gpio.c')
-rw-r--r--arch/arm/mach-at91rm9200/gpio.c87
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
218static u32 wakeups[BGA_GPIO_BANKS];
219static u32 backups[BGA_GPIO_BANKS];
220
221static 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
239void 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
274void 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
257static void gpio_irq_handler(unsigned irq, struct irqdesc *desc, struct pt_regs *regs) 336static 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}