[PATCH 1/2 v2][SRU][OEM-5.6] gpio: gpiolib: Allow GPIO IRQs to lazy disable

Koba Ko koba.ko at canonical.com
Tue Jul 21 08:48:13 UTC 2020


From: Maulik Shah <mkshah at codeaurora.org>

BugLink: https://bugs.launchpad.net/bugs/1888331     

With 'commit 461c1a7d4733 ("gpiolib: override irq_enable/disable")' gpiolib
overrides irqchip's irq_enable and irq_disable callbacks. If irq_disable
callback is implemented then genirq takes unlazy path to disable irq.

Underlying irqchip may not want to implement irq_disable callback to lazy
disable irq when client drivers invokes disable_irq(). By overriding
irq_disable callback, gpiolib ends up always unlazy disabling IRQ.

Allow gpiolib to lazy disable IRQs by overriding irq_disable callback only
if irqchip implemented irq_disable. In cases where irq_disable is not
implemented irq_mask is overridden. Similarly override irq_enable callback
only if irqchip implemented irq_enable otherwise irq_unmask is overridden.

Fixes: 461c1a7d4733 ("gpiolib: override irq_enable/disable")
Signed-off-by: Maulik Shah <mkshah at codeaurora.org>
Tested-by: Hans Verkuil <hverkuil-cisco at xs4all.nl>
Link: https://lore.kernel.org/r/1590253873-11556-2-git-send-email-mkshah@codeaurora.org
Signed-off-by: Linus Walleij <linus.walleij at linaro.org>
(cherry picked from commit a8173820f441ab3e2a45c4bb66b70da9a57a349e)
Signed-off-by: Koba Ko <koba.ko at canonical.com>
---
 drivers/gpio/gpiolib.c      | 57 ++++++++++++++++++++++++-------------
 include/linux/gpio/driver.h | 13 +++++++++
 2 files changed, 50 insertions(+), 20 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 2f350e3df965..501b75641628 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2291,32 +2291,37 @@ static void gpiochip_irq_relres(struct irq_data *d)
 	gpiochip_relres_irq(chip, d->hwirq);
 }
 
+static void gpiochip_irq_mask(struct irq_data *d)
+{
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+
+       if (gc->irq.irq_mask)
+               gc->irq.irq_mask(d);
+       gpiochip_disable_irq(gc, d->hwirq);
+}
+
+static void gpiochip_irq_unmask(struct irq_data *d)
+{
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+
+       gpiochip_enable_irq(gc, d->hwirq);
+       if (gc->irq.irq_unmask)
+               gc->irq.irq_unmask(d);
+}
+
 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);
+	chip->irq.irq_enable(d);
 }
 
 static void gpiochip_irq_disable(struct irq_data *d)
 {
 	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
-
-	/*
-	 * Since we override .irq_disable() we need to mimic the
-	 * behaviour of __irq_disable() in irq/chip.c.
-	 * First call .irq_disable() if it exists, else mimic the
-	 * behaviour of mask_irq() which calls .irq_mask() if
-	 * it exists.
-	 */
-	if (chip->irq.irq_disable)
-		chip->irq.irq_disable(d);
-	else if (chip->irq.chip->irq_mask)
-		chip->irq.chip->irq_mask(d);
+	
+	chip->irq.irq_disable(d);
 	gpiochip_disable_irq(chip, d->hwirq);
 }
 
@@ -2341,10 +2346,22 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gpiochip)
 			  "detected irqchip that is shared with multiple gpiochips: please fix the driver.\n");
 		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;
+	
+	if (irqchip->irq_disable) {
+	        gpiochip->irq.irq_disable = irqchip->irq_disable;
+	        irqchip->irq_disable = gpiochip_irq_disable;
+	} else {
+	        gpiochip->irq.irq_mask = irqchip->irq_mask;
+	        irqchip->irq_mask = gpiochip_irq_mask;
+	}
+	
+	if (irqchip->irq_enable) {
+	        gpiochip->irq.irq_enable = irqchip->irq_enable;
+	        irqchip->irq_enable = gpiochip_irq_enable;
+	} else {
+	        gpiochip->irq.irq_unmask = irqchip->irq_unmask;
+	        irqchip->irq_unmask = gpiochip_irq_unmask;
+	}
 }
 
 /**
diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h
index 6ef05bccc0a6..1cafdee668e1 100644
--- a/include/linux/gpio/driver.h
+++ b/include/linux/gpio/driver.h
@@ -253,6 +253,19 @@ struct gpio_irq_chip {
 	 * Store old irq_chip irq_disable callback
 	 */
 	void		(*irq_disable)(struct irq_data *data);
+	/**
+	 * @irq_unmask:
+	 *
+	 * Store old irq_chip irq_unmask callback
+	 */
+	void		(*irq_unmask)(struct irq_data *data);
+
+	/**
+	 * @irq_mask:
+	 *
+	 * Store old irq_chip irq_mask callback
+	 */
+	void		(*irq_mask)(struct irq_data *data);
 };
 
 /**
-- 
2.25.1




More information about the kernel-team mailing list