The TQMx86 GPIO controller only supports falling and rising edge triggers, but not both. Fix this by implementing a software both-edge mode that toggles the edge type after every interrupt. Fixes: b868db94a6a7 ("gpio: tqmx86: Add GPIO from for this IO controller") Co-developed-by: Gregor Herburger <gregor.herburger@xxxxxxxxxxxx> Signed-off-by: Gregor Herburger <gregor.herburger@xxxxxxxxxxxx> Signed-off-by: Matthias Schiffer <matthias.schiffer@xxxxxxxxxxxxxxx> --- drivers/gpio/gpio-tqmx86.c | 42 +++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index c957be3341774..400415676ad5d 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -126,9 +126,15 @@ static void _tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int hwirq) unsigned int offset = hwirq - TQMX86_NGPO; u8 type = TQMX86_INT_TRIG_NONE, mask, val; - if (gpio->irq_type[hwirq] & TQMX86_INT_UNMASKED) + if (gpio->irq_type[hwirq] & TQMX86_INT_UNMASKED) { type = gpio->irq_type[hwirq] & TQMX86_INT_TRIG_MASK; + if (type == TQMX86_INT_TRIG_BOTH) + type = tqmx86_gpio_get(&gpio->chip, hwirq) + ? TQMX86_INT_TRIG_FALLING + : TQMX86_INT_TRIG_RISING; + } + mask = TQMX86_GPII_MASK(offset); val = TQMX86_GPII_CONFIG(offset, type); _tqmx86_gpio_update_bits(gpio, TQMX86_GPIIC, mask, val); @@ -200,8 +206,8 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) struct gpio_chip *chip = irq_desc_get_handler_data(desc); struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); struct irq_chip *irq_chip = irq_desc_get_chip(desc); - unsigned long irq_bits; - int i = 0; + unsigned long irq_bits, flags; + int i, hwirq; u8 irq_status; chained_irq_enter(irq_chip, desc); @@ -210,6 +216,36 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc) tqmx86_gpio_write(gpio, TQMX86_GPIIS, irq_status); irq_bits = irq_status; + + raw_spin_lock_irqsave(&gpio->spinlock, flags); + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { + hwirq = i + TQMX86_NGPO; + + /* + * Edge-both triggers are implemented by flipping the edge + * trigger after each interrupt, as the controller only supports + * either rising or falling edge triggers, but not both. + * + * Internally, the TQMx86 GPIO controller has separate status + * registers for rising and falling edge interrupts. GPIIC + * configures which bits from which register are visible in the + * interrupt status register GPIIS and defines what triggers the + * parent IRQ line. Writing to GPIIS always clears both rising + * and falling interrupt flags internally, regardless of the + * currently configured trigger. + * + * In consequence, we can cleanly implement the edge-both + * trigger in software by first clearing the interrupt and then + * setting the new trigger based on the current GPIO input in + * _tqmx86_gpio_irq_config() - even if an edge arrives between + * reading the input and setting the trigger, we will have a new + * interrupt pending. + */ + if ((gpio->irq_type[hwirq] & TQMX86_INT_TRIG_MASK) == TQMX86_INT_TRIG_BOTH) + _tqmx86_gpio_irq_config(gpio, hwirq); + } + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) generic_handle_domain_irq(gpio->chip.irq.domain, i + TQMX86_NGPO); -- TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany Amtsgericht München, HRB 105018 Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider https://www.tq-group.com/