Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpio: tqmx86: fix broken IRQ_TYPE_EDGE_BOTH interrupt type

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@tq-group.com>
Signed-off-by: Gregor Herburger <gregor.herburger@tq-group.com>
Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
Link: https://lore.kernel.org/r/515324f0491c4d44f4ef49f170354aca002d81ef.1717063994.git.matthias.schiffer@ew.tq-group.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

authored by

Matthias Schiffer and committed by
Bartosz Golaszewski
90dd7de4 08af509e

+42 -4
+42 -4
drivers/gpio/gpio-tqmx86.c
··· 32 32 #define TQMX86_GPII_NONE 0 33 33 #define TQMX86_GPII_FALLING BIT(0) 34 34 #define TQMX86_GPII_RISING BIT(1) 35 + /* Stored in irq_type as a trigger type, but not actually valid as a register 36 + * value, so the name doesn't use "GPII" 37 + */ 38 + #define TQMX86_INT_BOTH (BIT(0) | BIT(1)) 35 39 #define TQMX86_GPII_MASK (BIT(0) | BIT(1)) 36 40 #define TQMX86_GPII_BITS 2 37 41 /* Stored in irq_type with GPII bits */ ··· 117 113 { 118 114 u8 type = TQMX86_GPII_NONE, gpiic; 119 115 120 - if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED) 116 + if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED) { 121 117 type = gpio->irq_type[offset] & TQMX86_GPII_MASK; 118 + 119 + if (type == TQMX86_INT_BOTH) 120 + type = tqmx86_gpio_get(&gpio->chip, offset + TQMX86_NGPO) 121 + ? TQMX86_GPII_FALLING 122 + : TQMX86_GPII_RISING; 123 + } 122 124 123 125 gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC); 124 126 gpiic &= ~(TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS)); ··· 179 169 new_type = TQMX86_GPII_FALLING; 180 170 break; 181 171 case IRQ_TYPE_EDGE_BOTH: 182 - new_type = TQMX86_GPII_FALLING | TQMX86_GPII_RISING; 172 + new_type = TQMX86_INT_BOTH; 183 173 break; 184 174 default: 185 175 return -EINVAL; /* not supported */ ··· 199 189 struct gpio_chip *chip = irq_desc_get_handler_data(desc); 200 190 struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); 201 191 struct irq_chip *irq_chip = irq_desc_get_chip(desc); 202 - unsigned long irq_bits; 203 - int i = 0; 192 + unsigned long irq_bits, flags; 193 + int i; 204 194 u8 irq_status; 205 195 206 196 chained_irq_enter(irq_chip, desc); ··· 209 199 tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS); 210 200 211 201 irq_bits = irq_status; 202 + 203 + raw_spin_lock_irqsave(&gpio->spinlock, flags); 204 + for_each_set_bit(i, &irq_bits, TQMX86_NGPI) { 205 + /* 206 + * Edge-both triggers are implemented by flipping the edge 207 + * trigger after each interrupt, as the controller only supports 208 + * either rising or falling edge triggers, but not both. 209 + * 210 + * Internally, the TQMx86 GPIO controller has separate status 211 + * registers for rising and falling edge interrupts. GPIIC 212 + * configures which bits from which register are visible in the 213 + * interrupt status register GPIIS and defines what triggers the 214 + * parent IRQ line. Writing to GPIIS always clears both rising 215 + * and falling interrupt flags internally, regardless of the 216 + * currently configured trigger. 217 + * 218 + * In consequence, we can cleanly implement the edge-both 219 + * trigger in software by first clearing the interrupt and then 220 + * setting the new trigger based on the current GPIO input in 221 + * tqmx86_gpio_irq_config() - even if an edge arrives between 222 + * reading the input and setting the trigger, we will have a new 223 + * interrupt pending. 224 + */ 225 + if ((gpio->irq_type[i] & TQMX86_GPII_MASK) == TQMX86_INT_BOTH) 226 + tqmx86_gpio_irq_config(gpio, i); 227 + } 228 + raw_spin_unlock_irqrestore(&gpio->spinlock, flags); 229 + 212 230 for_each_set_bit(i, &irq_bits, TQMX86_NGPI) 213 231 generic_handle_domain_irq(gpio->chip.irq.domain, 214 232 i + TQMX86_NGPO);