[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>
This commit is contained in:
Andrew Victor 2006-06-19 15:26:54 +01:00 коммит произвёл Russell King
Родитель 683c66bf75
Коммит 814138ffa4
1 изменённых файлов: 84 добавлений и 3 удалений

Просмотреть файл

@ -213,6 +213,84 @@ EXPORT_SYMBOL(at91_get_gpio_value);
/*--------------------------------------------------------------------------*/
#ifdef CONFIG_PM
static u32 wakeups[BGA_GPIO_BANKS];
static u32 backups[BGA_GPIO_BANKS];
static int gpio_irq_set_wake(unsigned pin, unsigned state)
{
unsigned mask = pin_to_mask(pin);
pin -= PIN_BASE;
pin /= 32;
if (unlikely(pin >= BGA_GPIO_BANKS))
return -EINVAL;
if (state)
wakeups[pin] |= mask;
else
wakeups[pin] &= ~mask;
return 0;
}
void at91_gpio_suspend(void)
{
int i;
for (i = 0; i < BGA_GPIO_BANKS; i++) {
u32 pio = pio_controller_offset[i];
/*
* Note: drivers should have disabled GPIO interrupts that
* aren't supposed to be wakeup sources.
* But that is not much good on ARM..... disable_irq() does
* not update the hardware immediately, so the hardware mask
* (IMR) has the wrong value (not current, too much is
* permitted).
*
* Our workaround is to disable all non-wakeup IRQs ...
* which is exactly what correct drivers asked for in the
* first place!
*/
backups[i] = at91_sys_read(pio + PIO_IMR);
at91_sys_write(pio_controller_offset[i] + PIO_IDR, backups[i]);
at91_sys_write(pio_controller_offset[i] + PIO_IER, wakeups[i]);
if (!wakeups[i]) {
disable_irq_wake(AT91_ID_PIOA + i);
at91_sys_write(AT91_PMC_PCDR, 1 << (AT91_ID_PIOA + i));
} else {
enable_irq_wake(AT91_ID_PIOA + i);
#ifdef CONFIG_PM_DEBUG
printk(KERN_DEBUG "GPIO-%c may wake for %08x\n", "ABCD"[i], wakeups[i]);
#endif
}
}
}
void at91_gpio_resume(void)
{
int i;
for (i = 0; i < BGA_GPIO_BANKS; i++) {
at91_sys_write(pio_controller_offset[i] + PIO_IDR, wakeups[i]);
at91_sys_write(pio_controller_offset[i] + PIO_IER, backups[i]);
}
at91_sys_write(AT91_PMC_PCER,
(1 << AT91_ID_PIOA)
| (1 << AT91_ID_PIOB)
| (1 << AT91_ID_PIOC)
| (1 << AT91_ID_PIOD));
}
#else
#define gpio_irq_set_wake NULL
#endif
/* Several AIC controller irqs are dispatched through this GPIO handler.
* To use any AT91_PIN_* as an externally triggered IRQ, first call
@ -252,6 +330,7 @@ static struct irqchip gpio_irqchip = {
.mask = gpio_irq_mask,
.unmask = gpio_irq_unmask,
.set_type = gpio_irq_type,
.set_wake = gpio_irq_set_wake,
};
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
/* temporarily mask (level sensitive) parent IRQ */
desc->chip->ack(irq);
for (;;) {
/* reading ISR acks the pending (edge triggered) GPIO interrupt */
isr = __raw_readl(pio + PIO_ISR) & __raw_readl(pio + PIO_IMR);
if (!isr)
break;
@ -315,15 +395,16 @@ void __init at91_gpio_irq_setup(unsigned banks)
set_irq_chipdata(id, controller);
for (i = 0; i < 32; i++, pin++) {
/*
* Can use the "simple" and not "edge" handler since it's
* shorter, and the AIC handles interupts sanely.
*/
set_irq_chip(pin, &gpio_irqchip);
set_irq_handler(pin, do_simple_IRQ);
set_irq_flags(pin, IRQF_VALID);
}
set_irq_chained_handler(id, gpio_irq_handler);
/* enable the PIO peripheral clock */
at91_sys_write(AT91_PMC_PCER, 1 << id);
}
pr_info("AT91: %d gpio irqs in %d banks\n", pin - PIN_BASE, banks);
}