Commit 2cfb8983 authored by Daniel Drake's avatar Daniel Drake Committed by Greg Kroah-Hartman

pinctrl: amd: disable spurious-firing GPIO IRQs

[ Upstream commit d21b8adb ]

When cold-booting Asus X434DA, GPIO 7 is found to be already configured
as an interrupt, and the GPIO level is found to be in a state that
causes the interrupt to fire.

As soon as pinctrl-amd probes, this interrupt fires and invokes
amd_gpio_irq_handler(). The IRQ is acked, but no GPIO-IRQ handler was
invoked, so the GPIO level being unchanged just causes another interrupt
to fire again immediately after.

This results in an interrupt storm causing this platform to hang
during boot, right after pinctrl-amd is probed.

Detect this situation and disable the GPIO interrupt when this happens.
This enables the affected platform to boot as normal. GPIO 7 actually is
the I2C touchpad interrupt line, and later on, i2c-multitouch loads and
re-enables this interrupt when it is ready to handle it.

Instead of this approach, I considered disabling all GPIO interrupts at
probe time, however that seems a little risky, and I also confirmed that
Windows does not seem to have this behaviour: the same 41 GPIO IRQs are
enabled under both Linux and Windows, which is a far larger collection
than the GPIOs referenced by the DSDT on this platform.
Signed-off-by: default avatarDaniel Drake <drake@endlessm.com>
Link: https://lore.kernel.org/r/20190814090540.7152-1-drake@endlessm.comSigned-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 274d7acb
...@@ -569,15 +569,25 @@ static irqreturn_t amd_gpio_irq_handler(int irq, void *dev_id) ...@@ -569,15 +569,25 @@ static irqreturn_t amd_gpio_irq_handler(int irq, void *dev_id)
!(regval & BIT(INTERRUPT_MASK_OFF))) !(regval & BIT(INTERRUPT_MASK_OFF)))
continue; continue;
irq = irq_find_mapping(gc->irq.domain, irqnr + i); irq = irq_find_mapping(gc->irq.domain, irqnr + i);
generic_handle_irq(irq); if (irq != 0)
generic_handle_irq(irq);
/* Clear interrupt. /* Clear interrupt.
* We must read the pin register again, in case the * We must read the pin register again, in case the
* value was changed while executing * value was changed while executing
* generic_handle_irq() above. * generic_handle_irq() above.
* If we didn't find a mapping for the interrupt,
* disable it in order to avoid a system hang caused
* by an interrupt storm.
*/ */
raw_spin_lock_irqsave(&gpio_dev->lock, flags); raw_spin_lock_irqsave(&gpio_dev->lock, flags);
regval = readl(regs + i); regval = readl(regs + i);
if (irq == 0) {
regval &= ~BIT(INTERRUPT_ENABLE_OFF);
dev_dbg(&gpio_dev->pdev->dev,
"Disabling spurious GPIO IRQ %d\n",
irqnr + i);
}
writel(regval, regs + i); writel(regval, regs + i);
raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags);
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment