Signed-off-by: Marek Vasut <marex@xxxxxxx>
Cc: Alexandre Torgue <alexandre.torgue@xxxxxxxxxxx>
Cc: Fabien Dessenne <fabien.dessenne@xxxxxxxxxxx>
Cc: Linus Walleij <linus.walleij@xxxxxxxxxx>
Cc: Marc Zyngier <maz@xxxxxxxxxx>
Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Cc: linux-stm32@xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Cc: linux-arm-kernel@xxxxxxxxxxxxxxxxxxx
To: linux-gpio@xxxxxxxxxxxxxxx
---
drivers/pinctrl/stm32/pinctrl-stm32.c | 55 +++++++++++++++++++++------
1 file changed, 44 insertions(+), 11 deletions(-)
diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c
index 242d1c37c6e4b..f4287fc18cf9a 100644
--- a/drivers/pinctrl/stm32/pinctrl-stm32.c
+++ b/drivers/pinctrl/stm32/pinctrl-stm32.c
@@ -10,6 +10,7 @@
#include <linux/gpio/driver.h>
#include <linux/hwspinlock.h>
#include <linux/io.h>
+#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
@@ -95,6 +96,7 @@ struct stm32_gpio_bank {
u32 bank_ioport_nr;
u32 pin_backup[STM32_GPIO_PINS_PER_BANK];
u8 irq_type[STM32_GPIO_PINS_PER_BANK];
+ struct tasklet_struct tasklet;
};
struct stm32_pinctrl {
@@ -307,20 +309,43 @@ static const struct gpio_chip stm32_gpio_template = {
.set_config = gpiochip_generic_config,
};
+static void stm32_gpio_irq_tasklet(struct tasklet_struct *t)
+{
+ struct stm32_gpio_bank *bank = from_tasklet(bank, t, tasklet);
+ struct irq_desc *desc;
+ struct irq_data *data;
+ int irq, pin, level;
+
+ /* Retrigger all LEVEL triggered pins which are still asserted. */
+ for (pin = 0; pin < STM32_GPIO_PINS_PER_BANK; pin++) {
+ if (!(bank->irq_type[pin] & IRQ_TYPE_LEVEL_MASK))
+ continue;
+
+ level = stm32_gpio_get(&bank->gpio_chip, pin);
+ if ((level == 0 && bank->irq_type[pin] == IRQ_TYPE_LEVEL_LOW) ||
+ (level == 1 && bank->irq_type[pin] == IRQ_TYPE_LEVEL_HIGH)) {
+ irq = irq_find_mapping(bank->domain, pin);
+
+ desc = irq_to_desc(irq);
+ if (!desc)
+ continue;
+
+ data = irq_desc_get_irq_data(desc);
+ if (!data)
+ continue;
+
+ irq_chip_retrigger_hierarchy(data);
+ }
+ }
+}
+
static void stm32_gpio_irq_trigger(struct irq_data *d)
{
struct stm32_gpio_bank *bank = d->domain->host_data;
- int level;
-
- /* Do not access the GPIO if this is not LEVEL triggered IRQ. */
- if (!(bank->irq_type[d->hwirq] & IRQ_TYPE_LEVEL_MASK))
- return;
- /* If level interrupt type then retrig */
- level = stm32_gpio_get(&bank->gpio_chip, d->hwirq);
- if ((level == 0 && bank->irq_type[d->hwirq] == IRQ_TYPE_LEVEL_LOW) ||
- (level == 1 && bank->irq_type[d->hwirq] == IRQ_TYPE_LEVEL_HIGH))
- irq_chip_retrigger_hierarchy(d);
+ /* If this is LEVEL triggered interrupt, retrigger in tasklet. */
+ if (bank->irq_type[d->hwirq] & IRQ_TYPE_LEVEL_MASK)
+ tasklet_schedule(&bank->tasklet);
}
static void stm32_gpio_irq_eoi(struct irq_data *d)
@@ -450,6 +475,8 @@ static int stm32_gpio_domain_alloc(struct irq_domain *d,
unsigned long flags;
int ret = 0;
+ tasklet_setup(&bank->tasklet, stm32_gpio_irq_tasklet);
+
/*
* Check first that the IRQ MUX of that line is free.
* gpio irq mux is shared between several banks, protect with a lock
@@ -475,7 +502,11 @@ static int stm32_gpio_domain_alloc(struct irq_domain *d,
irq_domain_set_hwirq_and_chip(d, virq, hwirq, &stm32_gpio_irq_chip,
bank);
- return irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &parent_fwspec);
+ ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &parent_fwspec);
+ if (ret)
+ tasklet_kill(&bank->tasklet);
+
+ return ret;
}
static void stm32_gpio_domain_free(struct irq_domain *d, unsigned int virq,
@@ -486,6 +517,8 @@ static void stm32_gpio_domain_free(struct irq_domain *d, unsigned int virq,
struct irq_data *irq_data = irq_domain_get_irq_data(d, virq);
unsigned long flags, hwirq = irq_data->hwirq;
+ tasklet_kill(&bank->tasklet);
+
irq_domain_free_irqs_common(d, virq, nr_irqs);
spin_lock_irqsave(&pctl->irqmux_lock, flags);