Merge remote-tracking branch 'origin/develop-3.0' into develop-3.0-jb
[firefly-linux-kernel-4.4.55.git] / drivers / gpio / timbgpio.c
index ddd053108a136ce2a41dd346a438622609c7f480..0265872e57d15e666b1365b2954befb9e98bf59b 100644 (file)
@@ -47,6 +47,7 @@ struct timbgpio {
        spinlock_t              lock; /* mutual exclusion */
        struct gpio_chip        gpio;
        int                     irq_base;
+       unsigned long           last_ier;
 };
 
 static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index,
@@ -108,26 +109,34 @@ static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset)
 /*
  * GPIO IRQ
  */
-static void timbgpio_irq_disable(unsigned irq)
+static void timbgpio_irq_disable(struct irq_data *d)
 {
-       struct timbgpio *tgpio = get_irq_chip_data(irq);
-       int offset = irq - tgpio->irq_base;
+       struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);
+       int offset = d->irq - tgpio->irq_base;
+       unsigned long flags;
 
-       timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 0);
+       spin_lock_irqsave(&tgpio->lock, flags);
+       tgpio->last_ier &= ~(1 << offset);
+       iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);
+       spin_unlock_irqrestore(&tgpio->lock, flags);
 }
 
-static void timbgpio_irq_enable(unsigned irq)
+static void timbgpio_irq_enable(struct irq_data *d)
 {
-       struct timbgpio *tgpio = get_irq_chip_data(irq);
-       int offset = irq - tgpio->irq_base;
+       struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);
+       int offset = d->irq - tgpio->irq_base;
+       unsigned long flags;
 
-       timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 1);
+       spin_lock_irqsave(&tgpio->lock, flags);
+       tgpio->last_ier |= 1 << offset;
+       iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);
+       spin_unlock_irqrestore(&tgpio->lock, flags);
 }
 
-static int timbgpio_irq_type(unsigned irq, unsigned trigger)
+static int timbgpio_irq_type(struct irq_data *d, unsigned trigger)
 {
-       struct timbgpio *tgpio = get_irq_chip_data(irq);
-       int offset = irq - tgpio->irq_base;
+       struct timbgpio *tgpio = irq_data_get_irq_chip_data(d);
+       int offset = d->irq - tgpio->irq_base;
        unsigned long flags;
        u32 lvr, flr, bflr = 0;
        u32 ver;
@@ -186,23 +195,31 @@ out:
 
 static void timbgpio_irq(unsigned int irq, struct irq_desc *desc)
 {
-       struct timbgpio *tgpio = get_irq_data(irq);
+       struct timbgpio *tgpio = irq_get_handler_data(irq);
        unsigned long ipr;
        int offset;
 
-       desc->chip->ack(irq);
+       desc->irq_data.chip->irq_ack(irq_get_irq_data(irq));
        ipr = ioread32(tgpio->membase + TGPIO_IPR);
        iowrite32(ipr, tgpio->membase + TGPIO_ICR);
 
+       /*
+        * Some versions of the hardware trash the IER register if more than
+        * one interrupt is received simultaneously.
+        */
+       iowrite32(0, tgpio->membase + TGPIO_IER);
+
        for_each_set_bit(offset, &ipr, tgpio->gpio.ngpio)
                generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset));
+
+       iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);
 }
 
 static struct irq_chip timbgpio_irqchip = {
        .name           = "GPIO",
-       .enable         = timbgpio_irq_enable,
-       .disable        = timbgpio_irq_disable,
-       .set_type       = timbgpio_irq_type,
+       .irq_enable     = timbgpio_irq_enable,
+       .irq_disable    = timbgpio_irq_disable,
+       .irq_set_type   = timbgpio_irq_type,
 };
 
 static int __devinit timbgpio_probe(struct platform_device *pdev)
@@ -274,16 +291,16 @@ static int __devinit timbgpio_probe(struct platform_device *pdev)
                return 0;
 
        for (i = 0; i < pdata->nr_pins; i++) {
-               set_irq_chip_and_handler_name(tgpio->irq_base + i,
+               irq_set_chip_and_handler_name(tgpio->irq_base + i,
                        &timbgpio_irqchip, handle_simple_irq, "mux");
-               set_irq_chip_data(tgpio->irq_base + i, tgpio);
+               irq_set_chip_data(tgpio->irq_base + i, tgpio);
 #ifdef CONFIG_ARM
                set_irq_flags(tgpio->irq_base + i, IRQF_VALID | IRQF_PROBE);
 #endif
        }
 
-       set_irq_data(irq, tgpio);
-       set_irq_chained_handler(irq, timbgpio_irq);
+       irq_set_handler_data(irq, tgpio);
+       irq_set_chained_handler(irq, timbgpio_irq);
 
        return 0;
 
@@ -310,12 +327,12 @@ static int __devexit timbgpio_remove(struct platform_device *pdev)
        if (irq >= 0 && tgpio->irq_base > 0) {
                int i;
                for (i = 0; i < pdata->nr_pins; i++) {
-                       set_irq_chip(tgpio->irq_base + i, NULL);
-                       set_irq_chip_data(tgpio->irq_base + i, NULL);
+                       irq_set_chip(tgpio->irq_base + i, NULL);
+                       irq_set_chip_data(tgpio->irq_base + i, NULL);
                }
 
-               set_irq_handler(irq, NULL);
-               set_irq_data(irq, NULL);
+               irq_set_handler(irq, NULL);
+               irq_set_handler_data(irq, NULL);
        }
 
        err = gpiochip_remove(&tgpio->gpio);