X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Farm%2Fmach-at91rm9200%2Fgpio.c;h=af22659c8a2895a4c90b66a2f8203790e6e509f4;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=0e396feec4686d9b4a3a8376e187df03aded439f;hpb=76828883507a47dae78837ab5dec5a5b4513c667;p=linux-2.6.git diff --git a/arch/arm/mach-at91rm9200/gpio.c b/arch/arm/mach-at91rm9200/gpio.c index 0e396feec..af22659c8 100644 --- a/arch/arm/mach-at91rm9200/gpio.c +++ b/arch/arm/mach-at91rm9200/gpio.c @@ -9,22 +9,25 @@ * (at your option) any later version. */ +#include #include +#include +#include #include #include #include #include -#include -#include +#include +#include #include -static const u32 pio_controller_offset[4] = { - AT91_PIOA, - AT91_PIOB, - AT91_PIOC, - AT91_PIOD, -}; +#include "generic.h" + + +static struct at91_gpio_bank *gpio; +static int gpio_banks; + static inline void __iomem *pin_to_controller(unsigned pin) { @@ -32,8 +35,8 @@ static inline void __iomem *pin_to_controller(unsigned pin) pin -= PIN_BASE; pin /= 32; - if (likely(pin < BGA_GPIO_BANKS)) - return sys_base + pio_controller_offset[pin]; + if (likely(pin < gpio_banks)) + return sys_base + gpio[pin].offset; return NULL; } @@ -178,7 +181,6 @@ EXPORT_SYMBOL(at91_set_multi_drive); /*--------------------------------------------------------------------------*/ - /* * assuming the pin is muxed as a gpio output, set its value. */ @@ -213,6 +215,69 @@ EXPORT_SYMBOL(at91_get_gpio_value); /*--------------------------------------------------------------------------*/ +#ifdef CONFIG_PM + +static u32 wakeups[MAX_GPIO_BANKS]; +static u32 backups[MAX_GPIO_BANKS]; + +static int gpio_irq_set_wake(unsigned pin, unsigned state) +{ + unsigned mask = pin_to_mask(pin); + unsigned bank = (pin - PIN_BASE) / 32; + + if (unlikely(bank >= MAX_GPIO_BANKS)) + return -EINVAL; + + if (state) + wakeups[bank] |= mask; + else + wakeups[bank] &= ~mask; + + set_irq_wake(gpio[bank].id, state); + + return 0; +} + +void at91_gpio_suspend(void) +{ + int i; + + for (i = 0; i < gpio_banks; i++) { + u32 pio = gpio[i].offset; + + backups[i] = at91_sys_read(pio + PIO_IMR); + at91_sys_write(pio + PIO_IDR, backups[i]); + at91_sys_write(pio + PIO_IER, wakeups[i]); + + if (!wakeups[i]) + clk_disable(gpio[i].clock); + else { +#ifdef CONFIG_PM_DEBUG + printk(KERN_DEBUG "GPIO-%c may wake for %08x\n", 'A'+i, wakeups[i]); +#endif + } + } +} + +void at91_gpio_resume(void) +{ + int i; + + for (i = 0; i < gpio_banks; i++) { + u32 pio = gpio[i].offset; + + if (!wakeups[i]) + clk_enable(gpio[i].clock); + + at91_sys_write(pio + PIO_IDR, wakeups[i]); + at91_sys_write(pio + PIO_IER, backups[i]); + } +} + +#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 @@ -248,34 +313,37 @@ static int gpio_irq_type(unsigned pin, unsigned type) return (type == IRQT_BOTHEDGE) ? 0 : -EINVAL; } -static struct irqchip gpio_irqchip = { +static struct irq_chip gpio_irqchip = { + .name = "GPIO", .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) +static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) { unsigned pin; - struct irqdesc *gpio; + struct irq_desc *gpio; void __iomem *pio; u32 isr; - pio = (void __force __iomem *) desc->chipdata; + pio = get_irq_chip_data(irq); /* 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; - pin = (unsigned) desc->data; + pin = (unsigned) get_irq_data(irq); gpio = &irq_desc[pin]; while (isr) { if (isr & 1) { - if (unlikely(gpio->disable_depth)) { + if (unlikely(gpio->depth)) { /* * The core ARM interrupt handler lazily disables IRQs so * another IRQ must be generated before it actually gets @@ -284,7 +352,7 @@ static void gpio_irq_handler(unsigned irq, struct irqdesc *desc, struct pt_regs gpio_irq_mask(pin); } else - gpio->handle(pin, gpio, regs); + desc_handle_irq(pin, gpio); } pin++; gpio++; @@ -295,35 +363,52 @@ static void gpio_irq_handler(unsigned irq, struct irqdesc *desc, struct pt_regs /* now it may re-trigger */ } -/* call this from board-specific init_irq */ -void __init at91_gpio_irq_setup(unsigned banks) +/*--------------------------------------------------------------------------*/ + +/* + * Called from the processor-specific init to enable GPIO interrupt support. + */ +void __init at91_gpio_irq_setup(void) { - unsigned pioc, pin, id; + unsigned pioc, pin; - if (banks > 4) - banks = 4; - for (pioc = 0, pin = PIN_BASE, id = AT91_ID_PIOA; - pioc < banks; - pioc++, id++) { + for (pioc = 0, pin = PIN_BASE; + pioc < gpio_banks; + pioc++) { void __iomem *controller; + unsigned id = gpio[pioc].id; unsigned i; - controller = (void __iomem *) AT91_VA_BASE_SYS + pio_controller_offset[pioc]; + clk_enable(gpio[pioc].clock); /* enable PIO controller's clock */ + + controller = (void __iomem *) AT91_VA_BASE_SYS + gpio[pioc].offset; __raw_writel(~0, controller + PIO_IDR); set_irq_data(id, (void *) pin); - set_irq_chipdata(id, (void __force *) controller); + set_irq_chip_data(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_handler(pin, handle_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); + pr_info("AT91: %d gpio irqs in %d banks\n", pin - PIN_BASE, gpio_banks); +} + +/* + * Called from the processor-specific init to enable GPIO pin support. + */ +void __init at91_gpio_init(struct at91_gpio_bank *data, int nr_banks) +{ + BUG_ON(nr_banks > MAX_GPIO_BANKS); + + gpio = data; + gpio_banks = nr_banks; }