gpio: gpiolib: Allow GPIO IRQs to lazy disable

With 'commit 461c1a7d47 ("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: 461c1a7d47 ("gpiolib: override irq_enable/disable")
Signed-off-by: Maulik Shah <mkshah@codeaurora.org>
Tested-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Link: https://lore.kernel.org/r/1590253873-11556-2-git-send-email-mkshah@codeaurora.org
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
Maulik Shah 2020-05-23 22:41:10 +05:30 committed by Linus Walleij
parent a34d5e5631
commit a8173820f4
2 changed files with 49 additions and 19 deletions

View File

@ -2471,32 +2471,37 @@ static void gpiochip_irq_relres(struct irq_data *d)
gpiochip_relres_irq(gc, d->hwirq); gpiochip_relres_irq(gc, 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) static void gpiochip_irq_enable(struct irq_data *d)
{ {
struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
gpiochip_enable_irq(gc, d->hwirq); gpiochip_enable_irq(gc, d->hwirq);
if (gc->irq.irq_enable) gc->irq.irq_enable(d);
gc->irq.irq_enable(d);
else
gc->irq.chip->irq_unmask(d);
} }
static void gpiochip_irq_disable(struct irq_data *d) static void gpiochip_irq_disable(struct irq_data *d)
{ {
struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
/* gc->irq.irq_disable(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 (gc->irq.irq_disable)
gc->irq.irq_disable(d);
else if (gc->irq.chip->irq_mask)
gc->irq.chip->irq_mask(d);
gpiochip_disable_irq(gc, d->hwirq); gpiochip_disable_irq(gc, d->hwirq);
} }
@ -2521,10 +2526,22 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc)
"detected irqchip that is shared with multiple gpiochips: please fix the driver.\n"); "detected irqchip that is shared with multiple gpiochips: please fix the driver.\n");
return; return;
} }
gc->irq.irq_enable = irqchip->irq_enable;
gc->irq.irq_disable = irqchip->irq_disable; if (irqchip->irq_disable) {
irqchip->irq_enable = gpiochip_irq_enable; gc->irq.irq_disable = irqchip->irq_disable;
irqchip->irq_disable = gpiochip_irq_disable; irqchip->irq_disable = gpiochip_irq_disable;
} else {
gc->irq.irq_mask = irqchip->irq_mask;
irqchip->irq_mask = gpiochip_irq_mask;
}
if (irqchip->irq_enable) {
gc->irq.irq_enable = irqchip->irq_enable;
irqchip->irq_enable = gpiochip_irq_enable;
} else {
gc->irq.irq_unmask = irqchip->irq_unmask;
irqchip->irq_unmask = gpiochip_irq_unmask;
}
} }
/** /**

View File

@ -253,6 +253,19 @@ struct gpio_irq_chip {
* Store old irq_chip irq_disable callback * Store old irq_chip irq_disable callback
*/ */
void (*irq_disable)(struct irq_data *data); 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);
}; };
/** /**