diff mbox series

[RFCv3,3/4] gpiolib: override irq_enable/disable

Message ID 20180831143744.126063-4-hverkuil@xs4all.nl
State New
Headers show
Series gpiolib: track irq enabled/disabled state | expand

Commit Message

Hans Verkuil Aug. 31, 2018, 2:37 p.m. UTC
From: Hans Verkuil <hans.verkuil@cisco.com>

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>
---
 drivers/gpio/gpiolib.c      | 86 ++++++++++++++++++++++++++-----------
 include/linux/gpio/driver.h |  3 ++
 2 files changed, 65 insertions(+), 24 deletions(-)

Comments

Linus Walleij Sept. 5, 2018, 10:28 a.m. UTC | #1
On Fri, Aug 31, 2018 at 4:37 PM Hans Verkuil <hverkuil@xs4all.nl> wrote:

> From: Hans Verkuil <hans.verkuil@cisco.com>
>
> 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>

Overall I'm a big fan of this as you already know!

> +       /*
> +        * It is possible for a driver to override this, but only if the
> +        * alternative functions are both implemented.
> +        */
> +       if (!irqchip->irq_request_resources &&
> +           !irqchip->irq_release_resources) {
> +               irqchip->irq_request_resources = gpiochip_irq_reqres;
> +               irqchip->irq_release_resources = gpiochip_irq_relres;
> +       }
> +
> +       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;

Actully the latter method, to use local copies of the enable/disable
function pointers in gpiochip->irq is what we should use also
for the request/release_resources callbacks.

The chips that actually use local irq_request_resources and
irq_release resources AND GPIOLIB_IRQCHIP are limited to:

drivers/gpio/gpio-dwapb.c
drivers/pinctrl/intel/pinctrl-intel.c
drivers/pinctrl/pinctrl-st.c
drivers/pinctrl/qcom/pinctrl-msm.c

As far as I can tell.

They probably all have some boilerplate related to reimplementing
part of the core functions.

But we can fix that in a separate patch after this.

Yours,
Linus Walleij
Hans Verkuil (hansverk) Sept. 5, 2018, 12:20 p.m. UTC | #2
On 09/05/18 12:28, Linus Walleij wrote:
> On Fri, Aug 31, 2018 at 4:37 PM Hans Verkuil <hverkuil@xs4all.nl> wrote:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> 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>
> 
> Overall I'm a big fan of this as you already know!
> 
>> +       /*
>> +        * It is possible for a driver to override this, but only if the
>> +        * alternative functions are both implemented.
>> +        */
>> +       if (!irqchip->irq_request_resources &&
>> +           !irqchip->irq_release_resources) {
>> +               irqchip->irq_request_resources = gpiochip_irq_reqres;
>> +               irqchip->irq_release_resources = gpiochip_irq_relres;
>> +       }
>> +
>> +       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;
> 
> Actully the latter method, to use local copies of the enable/disable
> function pointers in gpiochip->irq is what we should use also
> for the request/release_resources callbacks.

I'll change this.

Regards,

	Hans

> 
> The chips that actually use local irq_request_resources and
> irq_release resources AND GPIOLIB_IRQCHIP are limited to:
> 
> drivers/gpio/gpio-dwapb.c
> drivers/pinctrl/intel/pinctrl-intel.c
> drivers/pinctrl/pinctrl-st.c
> drivers/pinctrl/qcom/pinctrl-msm.c
> 
> As far as I can tell.
> 
> They probably all have some boilerplate related to reimplementing
> part of the core functions.
> 
> But we can fix that in a separate patch after this.
> 
> Yours,
> Linus Walleij
>
Hans Verkuil Sept. 7, 2018, 8:19 a.m. UTC | #3
On 09/05/2018 12:28 PM, Linus Walleij wrote:
> On Fri, Aug 31, 2018 at 4:37 PM Hans Verkuil <hverkuil@xs4all.nl> wrote:
> 
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> 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>
> 
> Overall I'm a big fan of this as you already know!
> 
>> +       /*
>> +        * It is possible for a driver to override this, but only if the
>> +        * alternative functions are both implemented.
>> +        */
>> +       if (!irqchip->irq_request_resources &&
>> +           !irqchip->irq_release_resources) {
>> +               irqchip->irq_request_resources = gpiochip_irq_reqres;
>> +               irqchip->irq_release_resources = gpiochip_irq_relres;
>> +       }
>> +
>> +       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;
> 
> Actully the latter method, to use local copies of the enable/disable
> function pointers in gpiochip->irq is what we should use also
> for the request/release_resources callbacks.
> 
> The chips that actually use local irq_request_resources and
> irq_release resources AND GPIOLIB_IRQCHIP are limited to:
> 
> drivers/gpio/gpio-dwapb.c

Are you sure? I don't see this one selecting GPIOLIB_IRQCHIP.

> drivers/pinctrl/intel/pinctrl-intel.c
> drivers/pinctrl/pinctrl-st.c
> drivers/pinctrl/qcom/pinctrl-msm.c

Ditto for the qcom driver.

The ST and Intel drivers do use it and those two driver needs
updating.

Regards,

	Hans

> 
> As far as I can tell.
> 
> They probably all have some boilerplate related to reimplementing
> part of the core functions.
> 
> But we can fix that in a separate patch after this.
> 
> Yours,
> Linus Walleij
>
diff mbox series

Patch

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 2ad58e63ce70..49a32f45eac6 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -86,6 +86,8 @@  static int gpiochip_add_irqchip(struct gpio_chip *gpiochip,
 static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
 static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip);
 static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip);
+static void gpiochip_irq_enable(struct irq_data *d);
+static void gpiochip_irq_disable(struct irq_data *d);
 static int gpiochip_irq_reqres(struct irq_data *d);
 static void gpiochip_irq_relres(struct irq_data *d);
 
@@ -1814,6 +1816,30 @@  static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)
 	return irq_create_mapping(chip->irq.domain, offset);
 }
 
+static void gpiochip_set_irq_hooks(struct gpio_chip *gpiochip)
+{
+	struct irq_chip *irqchip = gpiochip->irq.chip;
+
+	if (WARN_ON(irqchip->irq_enable == gpiochip_irq_enable))
+		return;
+
+	/*
+	 * It is possible for a driver to override this, but only if the
+	 * alternative functions are both implemented.
+	 */
+	if (!irqchip->irq_request_resources &&
+	    !irqchip->irq_release_resources) {
+		irqchip->irq_request_resources = gpiochip_irq_reqres;
+		irqchip->irq_release_resources = gpiochip_irq_relres;
+	}
+
+	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;
+}
+
 /**
  * gpiochip_add_irqchip() - adds an IRQ chip to a GPIO chip
  * @gpiochip: the GPIO chip to add the IRQ chip to
@@ -1872,16 +1898,6 @@  static int gpiochip_add_irqchip(struct gpio_chip *gpiochip,
 	if (!gpiochip->irq.domain)
 		return -EINVAL;
 
-	/*
-	 * It is possible for a driver to override this, but only if the
-	 * alternative functions are both implemented.
-	 */
-	if (!irqchip->irq_request_resources &&
-	    !irqchip->irq_release_resources) {
-		irqchip->irq_request_resources = gpiochip_irq_reqres;
-		irqchip->irq_release_resources = gpiochip_irq_relres;
-	}
-
 	if (gpiochip->irq.parent_handler) {
 		void *data = gpiochip->irq.parent_handler_data ?: gpiochip;
 
@@ -1897,6 +1913,8 @@  static int gpiochip_add_irqchip(struct gpio_chip *gpiochip,
 		}
 	}
 
+	gpiochip_set_irq_hooks(gpiochip);
+
 	acpi_gpiochip_request_interrupts(gpiochip);
 
 	return 0;
@@ -1910,11 +1928,12 @@  static int gpiochip_add_irqchip(struct gpio_chip *gpiochip,
  */
 static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
 {
+	struct irq_chip *irqchip = gpiochip->irq.chip;
 	unsigned int offset;
 
 	acpi_gpiochip_free_interrupts(gpiochip);
 
-	if (gpiochip->irq.chip && gpiochip->irq.parent_handler) {
+	if (irqchip && gpiochip->irq.parent_handler) {
 		struct gpio_irq_chip *irq = &gpiochip->irq;
 		unsigned int i;
 
@@ -1938,11 +1957,16 @@  static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
 		irq_domain_remove(gpiochip->irq.domain);
 	}
 
-	if (gpiochip->irq.chip) {
-		gpiochip->irq.chip->irq_request_resources = NULL;
-		gpiochip->irq.chip->irq_release_resources = NULL;
-		gpiochip->irq.chip = NULL;
+	if (irqchip &&
+	    irqchip->irq_enable == gpiochip_irq_enable) {
+		irqchip->irq_request_resources = NULL;
+		irqchip->irq_release_resources = NULL;
+		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);
 }
@@ -2031,15 +2055,7 @@  int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip,
 		return -EINVAL;
 	}
 
-	/*
-	 * It is possible for a driver to override this, but only if the
-	 * alternative functions are both implemented.
-	 */
-	if (!irqchip->irq_request_resources &&
-	    !irqchip->irq_release_resources) {
-		irqchip->irq_request_resources = gpiochip_irq_reqres;
-		irqchip->irq_release_resources = gpiochip_irq_relres;
-	}
+	gpiochip_set_irq_hooks(gpiochip);
 
 	acpi_gpiochip_request_interrupts(gpiochip);
 
@@ -3329,6 +3345,28 @@  void gpiochip_enable_irq(struct gpio_chip *chip, unsigned int offset)
 }
 EXPORT_SYMBOL_GPL(gpiochip_enable_irq);
 
+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);
+}
+
 bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset)
 {
 	if (offset >= chip->ngpio)
diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h
index 84449e95587a..46a896773448 100644
--- a/include/linux/gpio/driver.h
+++ b/include/linux/gpio/driver.h
@@ -138,6 +138,9 @@  struct gpio_irq_chip {
 	 * will allocate and map all IRQs during initialization.
 	 */
 	unsigned int first;
+
+	void		(*irq_enable)(struct irq_data *data);
+	void		(*irq_disable)(struct irq_data *data);
 };
 
 static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip)