[ARM] 4739/1: at91sam9263: make gpio bank C and D irqs work
authorDavid Brownell <david-b@pacbell.net>
Fri, 4 Jan 2008 17:30:24 +0000 (18:30 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Mon, 4 Feb 2008 13:16:39 +0000 (13:16 +0000)
On the at91sam9263, IRQs for GPIO banks C and D don't currently work.
This is because banks C, D, and E share one clock and toplevel IRQ, but
the AT91 code setting up and handling GPIO IRQs expects no sharing.
This patch:

 - Fixes GPIO IRQ setup and handling to cope with GPIO banks that are
   shared like on sam9263 chips, by setting up a list of those banks
   and making the IRQ dispatching logic scan that list.

 - Precomputes the address of each bank's registers, saving it with
   other per-bank data so that it no longer needs to be constantly
   recomputed during IRQs and other GPIO operations.  That shrinks
   hot-path code, while helping the GPIO bank irq updates.

 - Fixes a minor bug where IRQ_TYPE_NONE was wrongly rejected (it just
   means "use the default", which is "both edges" here).

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Andrew Victor <linux@maxim.org.za>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/mach-at91/generic.h
arch/arm/mach-at91/gpio.c

index b5daf7f5e011a797cc6b88f75ae9b9170a451174..7b9ce7a336b0ad897ec21fe0ff956598e8f5a867 100644 (file)
@@ -47,6 +47,9 @@ extern void at91_irq_resume(void);
 #define AT91RM9200_BGA         4       /* AT91RM9200 BGA package has 4 banks */
 
 struct at91_gpio_bank {
+       unsigned chipbase;              /* bank's first GPIO number */
+       void __iomem *regbase;          /* base of register bank */
+       struct at91_gpio_bank *next;    /* bank sharing same IRQ/clock/... */
        unsigned short id;              /* peripheral ID */
        unsigned long offset;           /* offset from system peripheral base */
        struct clk *clock;              /* associated clock */
index 6aeddd68d8af040e048489d4ebff70fa94a79663..f629c2b5f0c544bbec401964e6f7c13550e1e851 100644 (file)
@@ -33,12 +33,10 @@ static int gpio_banks;
 
 static inline void __iomem *pin_to_controller(unsigned pin)
 {
-       void __iomem *sys_base = (void __iomem *) AT91_VA_BASE_SYS;
-
        pin -= PIN_BASE;
        pin /= 32;
        if (likely(pin < gpio_banks))
-               return sys_base + gpio[pin].offset;
+               return gpio[pin].regbase;
 
        return NULL;
 }
@@ -294,11 +292,11 @@ void at91_gpio_suspend(void)
        int i;
 
        for (i = 0; i < gpio_banks; i++) {
-               u32 pio = gpio[i].offset;
+               void __iomem    *pio = gpio[i].regbase;
 
-               backups[i] = at91_sys_read(pio + PIO_IMR);
-               at91_sys_write(pio + PIO_IDR, backups[i]);
-               at91_sys_write(pio + PIO_IER, wakeups[i]);
+               backups[i] = __raw_readl(pio + PIO_IMR);
+               __raw_writel(backups[i], pio + PIO_IDR);
+               __raw_writel(wakeups[i], pio + PIO_IER);
 
                if (!wakeups[i])
                        clk_disable(gpio[i].clock);
@@ -315,13 +313,13 @@ void at91_gpio_resume(void)
        int i;
 
        for (i = 0; i < gpio_banks; i++) {
-               u32 pio = gpio[i].offset;
+               void __iomem    *pio = gpio[i].regbase;
 
                if (!wakeups[i])
                        clk_enable(gpio[i].clock);
 
-               at91_sys_write(pio + PIO_IDR, wakeups[i]);
-               at91_sys_write(pio + PIO_IER, backups[i]);
+               __raw_writel(wakeups[i], pio + PIO_IDR);
+               __raw_writel(backups[i], pio + PIO_IER);
        }
 }
 
@@ -361,7 +359,13 @@ static void gpio_irq_unmask(unsigned pin)
 
 static int gpio_irq_type(unsigned pin, unsigned type)
 {
-       return (type == IRQT_BOTHEDGE) ? 0 : -EINVAL;
+       switch (type) {
+       case IRQ_TYPE_NONE:
+       case IRQ_TYPE_EDGE_BOTH:
+               return 0;
+       default:
+               return -EINVAL;
+       }
 }
 
 static struct irq_chip gpio_irqchip = {
@@ -376,20 +380,30 @@ static void gpio_irq_handler(unsigned irq, struct irq_desc *desc)
 {
        unsigned        pin;
        struct irq_desc *gpio;
+       struct at91_gpio_bank *bank;
        void __iomem    *pio;
        u32             isr;
 
-       pio = get_irq_chip_data(irq);
+       bank = get_irq_chip_data(irq);
+       pio = bank->regbase;
 
        /* temporarily mask (level sensitive) parent IRQ */
        desc->chip->ack(irq);
        for (;;) {
-               /* reading ISR acks the pending (edge triggered) GPIO interrupt */
+               /* Reading ISR acks pending (edge triggered) GPIO interrupts.
+                * When there none are pending, we're finished unless we need
+                * to process multiple banks (like ID_PIOCDE on sam9263).
+                */
                isr = __raw_readl(pio + PIO_ISR) & __raw_readl(pio + PIO_IMR);
-               if (!isr)
-                       break;
+               if (!isr) {
+                       if (!bank->next)
+                               break;
+                       bank = bank->next;
+                       pio = bank->regbase;
+                       continue;
+               }
 
-               pin = (unsigned) get_irq_data(irq);
+               pin = bank->chipbase;
                gpio = &irq_desc[pin];
 
                while (isr) {
@@ -481,24 +495,21 @@ postcore_initcall(at91_gpio_debugfs_init);
  */
 void __init at91_gpio_irq_setup(void)
 {
-       unsigned        pioc, pin;
+       unsigned                pioc, pin;
+       struct at91_gpio_bank   *this, *prev;
 
-       for (pioc = 0, pin = PIN_BASE;
-                       pioc < gpio_banks;
-                       pioc++) {
-               void __iomem    *controller;
-               unsigned        id = gpio[pioc].id;
+       for (pioc = 0, pin = PIN_BASE, this = gpio, prev = NULL;
+                       pioc++ < gpio_banks;
+                       prev = this, this++) {
+               unsigned        id = this->id;
                unsigned        i;
 
-               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);
+               /* enable PIO controller's clock */
+               clk_enable(this->clock);
 
-               set_irq_data(id, (void *) pin);
-               set_irq_chip_data(id, controller);
+               __raw_writel(~0, this->regbase + PIO_IDR);
 
-               for (i = 0; i < 32; i++, pin++) {
+               for (i = 0, pin = this->chipbase; i < 32; i++, pin++) {
                        /*
                         * Can use the "simple" and not "edge" handler since it's
                         * shorter, and the AIC handles interrupts sanely.
@@ -508,6 +519,14 @@ void __init at91_gpio_irq_setup(void)
                        set_irq_flags(pin, IRQF_VALID);
                }
 
+               /* The toplevel handler handles one bank of GPIOs, except
+                * AT91SAM9263_ID_PIOCDE handles three... PIOC is first in
+                * the list, so we only set up that handler.
+                */
+               if (prev && prev->next == this)
+                       continue;
+
+               set_irq_chip_data(id, this);
                set_irq_chained_handler(id, gpio_irq_handler);
        }
        pr_info("AT91: %d gpio irqs in %d banks\n", pin - PIN_BASE, gpio_banks);
@@ -518,8 +537,20 @@ void __init at91_gpio_irq_setup(void)
  */
 void __init at91_gpio_init(struct at91_gpio_bank *data, int nr_banks)
 {
+       unsigned                i;
+       struct at91_gpio_bank   *last;
+
        BUG_ON(nr_banks > MAX_GPIO_BANKS);
 
        gpio = data;
        gpio_banks = nr_banks;
+
+       for (i = 0, last = NULL; i < nr_banks; i++, last = data, data++) {
+               data->chipbase = PIN_BASE + i * 32;
+               data->regbase = data->offset + (void __iomem *)AT91_VA_BASE_SYS;
+
+               /* AT91SAM9263_ID_PIOCDE groups PIOC, PIOD, PIOE */
+               if (last && last->id == data->id)
+                       last->next = data;
+       }
 }