Commit 90dd7de4 authored by Matthias Schiffer's avatar Matthias Schiffer Committed by Bartosz Golaszewski
Browse files

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: b868db94 ("gpio: tqmx86: Add GPIO from for this IO controller")
Co-developed-by: default avatarGregor Herburger <gregor.herburger@tq-group.com>
Signed-off-by: default avatarGregor Herburger <gregor.herburger@tq-group.com>
Signed-off-by: default avatarMatthias 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: default avatarBartosz Golaszewski <bartosz.golaszewski@linaro.org>
parent 08af509e
Loading
Loading
Loading
Loading
+42 −4
Original line number Diff line number Diff line
@@ -32,6 +32,10 @@
#define TQMX86_GPII_NONE	0
#define TQMX86_GPII_FALLING	BIT(0)
#define TQMX86_GPII_RISING	BIT(1)
/* Stored in irq_type as a trigger type, but not actually valid as a register
 * value, so the name doesn't use "GPII"
 */
#define TQMX86_INT_BOTH		(BIT(0) | BIT(1))
#define TQMX86_GPII_MASK	(BIT(0) | BIT(1))
#define TQMX86_GPII_BITS	2
/* Stored in irq_type with GPII bits */
@@ -113,9 +117,15 @@ static void tqmx86_gpio_irq_config(struct tqmx86_gpio_data *gpio, int offset)
{
	u8 type = TQMX86_GPII_NONE, gpiic;

	if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED)
	if (gpio->irq_type[offset] & TQMX86_INT_UNMASKED) {
		type = gpio->irq_type[offset] & TQMX86_GPII_MASK;

		if (type == TQMX86_INT_BOTH)
			type = tqmx86_gpio_get(&gpio->chip, offset + TQMX86_NGPO)
				? TQMX86_GPII_FALLING
				: TQMX86_GPII_RISING;
	}

	gpiic = tqmx86_gpio_read(gpio, TQMX86_GPIIC);
	gpiic &= ~(TQMX86_GPII_MASK << (offset * TQMX86_GPII_BITS));
	gpiic |= type << (offset * TQMX86_GPII_BITS);
@@ -169,7 +179,7 @@ static int tqmx86_gpio_irq_set_type(struct irq_data *data, unsigned int type)
		new_type = TQMX86_GPII_FALLING;
		break;
	case IRQ_TYPE_EDGE_BOTH:
		new_type = TQMX86_GPII_FALLING | TQMX86_GPII_RISING;
		new_type = TQMX86_INT_BOTH;
		break;
	default:
		return -EINVAL; /* not supported */
@@ -189,8 +199,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;
	u8 irq_status;

	chained_irq_enter(irq_chip, desc);
@@ -199,6 +209,34 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc)
	tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS);

	irq_bits = irq_status;

	raw_spin_lock_irqsave(&gpio->spinlock, flags);
	for_each_set_bit(i, &irq_bits, TQMX86_NGPI) {
		/*
		 * 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[i] & TQMX86_GPII_MASK) == TQMX86_INT_BOTH)
			tqmx86_gpio_irq_config(gpio, i);
	}
	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);