Skip to content

Commit 93b8ddc

Browse files
Sergey Matsievskiylinusw
authored andcommitted
pinctrl: ocelot: fix system hang on level based interrupts
The current implementation only calls chained_irq_enter() and chained_irq_exit() if it detects pending interrupts. ``` for (i = 0; i < info->stride; i++) { uregmap_read(info->map, id_reg + 4 * i, &reg); if (!reg) continue; chained_irq_enter(parent_chip, desc); ``` However, in case of GPIO pin configured in level mode and the parent controller configured in edge mode, GPIO interrupt might be lowered by the hardware. In the result, if the interrupt is short enough, the parent interrupt is still pending while the GPIO interrupt is cleared; chained_irq_enter() never gets called and the system hangs trying to service the parent interrupt. Moving chained_irq_enter() and chained_irq_exit() outside the for loop ensures that they are called even when GPIO interrupt is lowered by the hardware. The similar code with chained_irq_enter() / chained_irq_exit() functions wrapping interrupt checking loop may be found in many other drivers: ``` grep -r -A 10 chained_irq_enter drivers/pinctrl ``` Cc: stable@vger.kernel.org Signed-off-by: Sergey Matsievskiy <matsievskiysv@gmail.com> Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Link: https://lore.kernel.org/20241012105743.12450-2-matsievskiysv@gmail.com Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
1 parent 3fd976a commit 93b8ddc

File tree

1 file changed

+4
-4
lines changed

1 file changed

+4
-4
lines changed

drivers/pinctrl/pinctrl-ocelot.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1955,21 +1955,21 @@ static void ocelot_irq_handler(struct irq_desc *desc)
19551955
unsigned int reg = 0, irq, i;
19561956
unsigned long irqs;
19571957

1958+
chained_irq_enter(parent_chip, desc);
1959+
19581960
for (i = 0; i < info->stride; i++) {
19591961
regmap_read(info->map, id_reg + 4 * i, &reg);
19601962
if (!reg)
19611963
continue;
19621964

1963-
chained_irq_enter(parent_chip, desc);
1964-
19651965
irqs = reg;
19661966

19671967
for_each_set_bit(irq, &irqs,
19681968
min(32U, info->desc->npins - 32 * i))
19691969
generic_handle_domain_irq(chip->irq.domain, irq + 32 * i);
1970-
1971-
chained_irq_exit(parent_chip, desc);
19721970
}
1971+
1972+
chained_irq_exit(parent_chip, desc);
19731973
}
19741974

19751975
static int ocelot_gpiochip_register(struct platform_device *pdev,

0 commit comments

Comments
 (0)