From: Hans Verkuil <hans.verkuil@xxxxxxxxx> Rather than completely replacing any existing irq_request/release_resources irq_chip callbacks, remember the driver's version and call it from our override. Two drivers that select GPIOLIB_IRQCHIP and set these callbacks had to be changed, otherwise you would get into an infinite loop. Signed-off-by: Hans Verkuil <hans.verkuil@xxxxxxxxx> Cc: Patrice Chotard <patrice.chotard@xxxxxx> Cc: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx> Cc: Mika Westerberg <mika.westerberg@xxxxxxxxxxxxxxx> --- drivers/gpio/gpiolib.c | 60 ++++++++++++++++----------- drivers/pinctrl/intel/pinctrl-intel.c | 32 -------------- drivers/pinctrl/pinctrl-st.c | 11 +---- include/linux/gpio/driver.h | 14 +++++++ 4 files changed, 50 insertions(+), 67 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index cbab0e744de0..d2c1e2cccc65 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1815,17 +1815,40 @@ static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) static int gpiochip_irq_reqres(struct irq_data *d) { struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + int ret; - return gpiochip_reqres_irq(chip, d->hwirq); + ret = gpiochip_reqres_irq(chip, d->hwirq); + if (ret || !chip->irq.irq_reqres) + return ret; + ret = chip->irq.irq_reqres(d); + if (ret) + gpiochip_relres_irq(chip, d->hwirq); + return ret; } static void gpiochip_irq_relres(struct irq_data *d) { struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + if (chip->irq.irq_relres) + chip->irq.irq_relres(d); gpiochip_relres_irq(chip, d->hwirq); } +static void gpiochip_set_irq_hooks(struct gpio_chip *gpiochip) +{ + struct irq_chip *irqchip = gpiochip->irq.chip; + + if (WARN_ON(irqchip->irq_request_resources == gpiochip_irq_reqres)) + return; + + gpiochip->irq.irq_reqres = irqchip->irq_request_resources; + gpiochip->irq.irq_relres = irqchip->irq_release_resources; + + irqchip->irq_request_resources = gpiochip_irq_reqres; + irqchip->irq_release_resources = gpiochip_irq_relres; +} + /** * gpiochip_add_irqchip() - adds an IRQ chip to a GPIO chip * @gpiochip: the GPIO chip to add the IRQ chip to @@ -1884,16 +1907,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; @@ -1909,6 +1922,8 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, } } + gpiochip_set_irq_hooks(gpiochip); + acpi_gpiochip_request_interrupts(gpiochip); return 0; @@ -1922,11 +1937,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; @@ -1950,11 +1966,13 @@ 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_request_resources == gpiochip_irq_reqres) { + irqchip->irq_request_resources = gpiochip->irq.irq_reqres; + irqchip->irq_release_resources = gpiochip->irq.irq_relres; } + gpiochip->irq.irq_reqres = NULL; + gpiochip->irq.irq_relres = NULL; + gpiochip->irq.chip = NULL; gpiochip_irqchip_free_valid_mask(gpiochip); } @@ -2043,15 +2061,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); diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index 62b009b27eda..3d0bd7b99725 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -872,36 +872,6 @@ static int intel_gpio_to_pin(struct intel_pinctrl *pctrl, unsigned offset, return -EINVAL; } -static int intel_gpio_irq_reqres(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct intel_pinctrl *pctrl = gpiochip_get_data(gc); - int pin; - int ret; - - pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), NULL, NULL); - if (pin >= 0) { - ret = gpiochip_lock_as_irq(gc, pin); - if (ret) { - dev_err(pctrl->dev, "unable to lock HW IRQ %d for IRQ\n", - pin); - return ret; - } - } - return 0; -} - -static void intel_gpio_irq_relres(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct intel_pinctrl *pctrl = gpiochip_get_data(gc); - int pin; - - pin = intel_gpio_to_pin(pctrl, irqd_to_hwirq(d), NULL, NULL); - if (pin >= 0) - gpiochip_unlock_as_irq(gc, pin); -} - static void intel_gpio_irq_ack(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); @@ -1117,8 +1087,6 @@ static irqreturn_t intel_gpio_irq(int irq, void *data) static struct irq_chip intel_gpio_irqchip = { .name = "intel-gpio", - .irq_request_resources = intel_gpio_irq_reqres, - .irq_release_resources = intel_gpio_irq_relres, .irq_enable = intel_gpio_irq_enable, .irq_ack = intel_gpio_irq_ack, .irq_mask = intel_gpio_irq_mask, diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c index 0966bb0bf71f..02a45bcd79c1 100644 --- a/drivers/pinctrl/pinctrl-st.c +++ b/drivers/pinctrl/pinctrl-st.c @@ -1290,15 +1290,7 @@ static int st_gpio_irq_request_resources(struct irq_data *d) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); st_gpio_direction_input(gc, d->hwirq); - - return gpiochip_lock_as_irq(gc, d->hwirq); -} - -static void st_gpio_irq_release_resources(struct irq_data *d) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - - gpiochip_unlock_as_irq(gc, d->hwirq); + return 0; } static int st_gpio_irq_set_type(struct irq_data *d, unsigned type) @@ -1456,7 +1448,6 @@ static const struct gpio_chip st_gpio_template = { static struct irq_chip st_gpio_irqchip = { .name = "GPIO", .irq_request_resources = st_gpio_irq_request_resources, - .irq_release_resources = st_gpio_irq_release_resources, .irq_disable = st_gpio_irq_mask, .irq_mask = st_gpio_irq_mask, .irq_unmask = st_gpio_irq_unmask, diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 380251955ad1..b445b083e568 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -138,6 +138,20 @@ struct gpio_irq_chip { * will allocate and map all IRQs during initialization. */ unsigned int first; + + /** + * @irq_reqres: + * + * Store old irq_chip irq_request_resources callback + */ + int (*irq_reqres)(struct irq_data *d); + + /** + * @irq_relres: + * + * Store old irq_chip irq_release_resources callback + */ + void (*irq_relres)(struct irq_data *d); }; static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) -- 2.18.0