Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 814138ff authored by Andrew Victor's avatar Andrew Victor Committed by Russell King
Browse files

[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: default avatarAndrew Victor <andrew@sanpeople.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 683c66bf
Loading
Loading
Loading
Loading
+84 −3
Original line number Diff line number Diff line
@@ -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);
}