| From foo@baz Mon Sep 17 12:33:31 CEST 2018 |
| From: Daniel Kurtz <djkurtz@chromium.org> |
| Date: Mon, 16 Jul 2018 18:57:18 -0600 |
| Subject: pinctrl/amd: only handle irq if it is pending and unmasked |
| |
| From: Daniel Kurtz <djkurtz@chromium.org> |
| |
| [ Upstream commit 8bbed1eef001fdfc0ee9595f64cc4f769d265af4 ] |
| |
| The AMD pinctrl driver demultiplexes GPIO interrupts and fires off their |
| individual handlers. |
| |
| If one of these GPIO irqs is configured as a level interrupt, and its |
| downstream handler is a threaded ONESHOT interrupt, the GPIO interrupt |
| source is masked by handle_level_irq() until the eventual return of the |
| threaded irq handler. During this time the level GPIO interrupt status |
| will still report as high until the actual gpio source is cleared - both |
| in the individual GPIO interrupt status bit (INTERRUPT_STS_OFF) and in |
| its corresponding "WAKE_INT_STATUS_REG" bit. |
| |
| Thus, if another GPIO interrupt occurs during this time, |
| amd_gpio_irq_handler() will see that the (masked-and-not-yet-cleared) |
| level irq is still pending and incorrectly call its handler again. |
| |
| To fix this, have amd_gpio_irq_handler() check for both interrupts status |
| and mask before calling generic_handle_irq(). |
| |
| Note: Is it possible that this bug was the source of the interrupt storm |
| on Ryzen when using chained interrupts before commit ba714a9c1dea85 |
| ("pinctrl/amd: Use regular interrupt instead of chained")? |
| |
| Signed-off-by: Daniel Kurtz <djkurtz@chromium.org> |
| Acked-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Linus Walleij <linus.walleij@linaro.org> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/pinctrl/pinctrl-amd.c | 3 ++- |
| 1 file changed, 2 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/pinctrl/pinctrl-amd.c |
| +++ b/drivers/pinctrl/pinctrl-amd.c |
| @@ -530,7 +530,8 @@ static irqreturn_t amd_gpio_irq_handler( |
| /* Each status bit covers four pins */ |
| for (i = 0; i < 4; i++) { |
| regval = readl(regs + i); |
| - if (!(regval & PIN_IRQ_PENDING)) |
| + if (!(regval & PIN_IRQ_PENDING) || |
| + !(regval & BIT(INTERRUPT_MASK_OFF))) |
| continue; |
| irq = irq_find_mapping(gc->irqdomain, irqnr + i); |
| generic_handle_irq(irq); |