gpiolib: override irq_enable/disable
authorHans Verkuil <hans.verkuil@cisco.com>
Sat, 8 Sep 2018 09:23:17 +0000 (11:23 +0200)
committerLinus Walleij <linus.walleij@linaro.org>
Mon, 10 Sep 2018 06:56:38 +0000 (08:56 +0200)
When using the gpiolib irqchip helpers install irq_enable/disable
hooks for the irqchip to ensure that gpiolib knows when the irq
is enabled or disabled, allowing drivers to disable the irq and then
use it as an output pin, and later switch the direction to input and
re-enable the irq.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/gpio/gpiolib.c
include/linux/gpio/driver.h

index e52fa72f13d7939045d32cbf1e67868a23da1e7f..efce534a269bc0ebc84d73696144b2f6ae3473f3 100644 (file)
@@ -1826,6 +1826,28 @@ static void gpiochip_irq_relres(struct irq_data *d)
        gpiochip_relres_irq(chip, d->hwirq);
 }
 
+static void gpiochip_irq_enable(struct irq_data *d)
+{
+       struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+       gpiochip_enable_irq(chip, d->hwirq);
+       if (chip->irq.irq_enable)
+               chip->irq.irq_enable(d);
+       else
+               chip->irq.chip->irq_unmask(d);
+}
+
+static void gpiochip_irq_disable(struct irq_data *d)
+{
+       struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+       if (chip->irq.irq_disable)
+               chip->irq.irq_disable(d);
+       else
+               chip->irq.chip->irq_mask(d);
+       gpiochip_disable_irq(chip, d->hwirq);
+}
+
 static void gpiochip_set_irq_hooks(struct gpio_chip *gpiochip)
 {
        struct irq_chip *irqchip = gpiochip->irq.chip;
@@ -1835,6 +1857,12 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gpiochip)
                irqchip->irq_request_resources = gpiochip_irq_reqres;
                irqchip->irq_release_resources = gpiochip_irq_relres;
        }
+       if (WARN_ON(gpiochip->irq.irq_enable))
+               return;
+       gpiochip->irq.irq_enable = irqchip->irq_enable;
+       gpiochip->irq.irq_disable = irqchip->irq_disable;
+       irqchip->irq_enable = gpiochip_irq_enable;
+       irqchip->irq_disable = gpiochip_irq_disable;
 }
 
 /**
@@ -1954,11 +1982,18 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
                irq_domain_remove(gpiochip->irq.domain);
        }
 
-       if (irqchip &&
-           irqchip->irq_request_resources == gpiochip_irq_reqres) {
-               irqchip->irq_request_resources = NULL;
-               irqchip->irq_release_resources = NULL;
+       if (irqchip) {
+               if (irqchip->irq_request_resources == gpiochip_irq_reqres) {
+                       irqchip->irq_request_resources = NULL;
+                       irqchip->irq_release_resources = NULL;
+               }
+               if (irqchip->irq_enable == gpiochip_irq_enable) {
+                       irqchip->irq_enable = gpiochip->irq.irq_enable;
+                       irqchip->irq_disable = gpiochip->irq.irq_disable;
+               }
        }
+       gpiochip->irq.irq_enable = NULL;
+       gpiochip->irq.irq_disable = NULL;
        gpiochip->irq.chip = NULL;
 
        gpiochip_irqchip_free_valid_mask(gpiochip);
index 9052ccd399fdb636ad0dbf8aba13fdb7d724c8cf..d8dcd0e44cab843c68f2a5178024d271b83508bb 100644 (file)
@@ -144,6 +144,20 @@ struct gpio_irq_chip {
         * will allocate and map all IRQs during initialization.
         */
        unsigned int first;
+
+       /**
+        * @irq_enable:
+        *
+        * Store old irq_chip irq_enable callback
+        */
+       void            (*irq_enable)(struct irq_data *data);
+
+       /**
+        * @irq_disable:
+        *
+        * Store old irq_chip irq_disable callback
+        */
+       void            (*irq_disable)(struct irq_data *data);
 };
 
 static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip)