pinctrl: at91: choose appropriate handler for level interrupts
authorBoris BREZILLON <b.brezillon@overkiz.com>
Sat, 20 Jul 2013 14:51:33 +0000 (16:51 +0200)
committerLinus Walleij <linus.walleij@linaro.org>
Wed, 7 Aug 2013 20:30:16 +0000 (22:30 +0200)
The current implementation handle both edge and level interrupts with the
'handle_simple_irq' handler.

Level interrupts are active as long as the pin stays at the configured
level (low or high). In this case we have to use 'handle_level_irq' which
mask the interrupt until the handle has treated it.

Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
Tested-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/pinctrl/pinctrl-at91.c

index b90a3a0ac53432af15a64849a115823de0fedd9b..7cce066c71b6790fd3e4f13f3ff46340fd683e64 100644 (file)
@@ -1241,18 +1241,22 @@ static int alt_gpio_irq_type(struct irq_data *d, unsigned type)
 
        switch (type) {
        case IRQ_TYPE_EDGE_RISING:
+               irq_set_handler(d->irq, handle_simple_irq);
                writel_relaxed(mask, pio + PIO_ESR);
                writel_relaxed(mask, pio + PIO_REHLSR);
                break;
        case IRQ_TYPE_EDGE_FALLING:
+               irq_set_handler(d->irq, handle_simple_irq);
                writel_relaxed(mask, pio + PIO_ESR);
                writel_relaxed(mask, pio + PIO_FELLSR);
                break;
        case IRQ_TYPE_LEVEL_LOW:
+               irq_set_handler(d->irq, handle_level_irq);
                writel_relaxed(mask, pio + PIO_LSR);
                writel_relaxed(mask, pio + PIO_FELLSR);
                break;
        case IRQ_TYPE_LEVEL_HIGH:
+               irq_set_handler(d->irq, handle_level_irq);
                writel_relaxed(mask, pio + PIO_LSR);
                writel_relaxed(mask, pio + PIO_REHLSR);
                break;
@@ -1261,6 +1265,7 @@ static int alt_gpio_irq_type(struct irq_data *d, unsigned type)
                 * disable additional interrupt modes:
                 * fall back to default behavior
                 */
+               irq_set_handler(d->irq, handle_simple_irq);
                writel_relaxed(mask, pio + PIO_AIMDR);
                return 0;
        case IRQ_TYPE_NONE:
@@ -1402,6 +1407,8 @@ static int at91_gpio_irq_map(struct irq_domain *h, unsigned int virq,
                                                        irq_hw_number_t hw)
 {
        struct at91_gpio_chip   *at91_gpio = h->host_data;
+       void __iomem            *pio = at91_gpio->regbase;
+       u32                     mask = 1 << hw;
 
        irq_set_lockdep_class(virq, &gpio_lock_class);
 
@@ -1409,8 +1416,13 @@ static int at91_gpio_irq_map(struct irq_domain *h, unsigned int virq,
         * Can use the "simple" and not "edge" handler since it's
         * shorter, and the AIC handles interrupts sanely.
         */
-       irq_set_chip_and_handler(virq, &gpio_irqchip,
-                                handle_simple_irq);
+       irq_set_chip(virq, &gpio_irqchip);
+       if ((at91_gpio->ops == &at91sam9x5_ops) &&
+           (readl_relaxed(pio + PIO_AIMMR) & mask) &&
+           (readl_relaxed(pio + PIO_ELSR) & mask))
+               irq_set_handler(virq, handle_level_irq);
+       else
+               irq_set_handler(virq, handle_simple_irq);
        set_irq_flags(virq, IRQF_VALID);
        irq_set_chip_data(virq, at91_gpio);