diff options
author | Nicolas Boichat <drinkcat@chromium.org> | 2019-04-28 23:55:14 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2019-06-26 10:09:50 -0400 |
commit | 35594bc7cecf3a78504b590e350570e8f4d7779e (patch) | |
tree | 8c936b1d05dae686097e53f3a7464a57e5c748ea | |
parent | 4b36082e2e09c2769710756390d54cfca563ed96 (diff) |
pinctrl: mediatek: Ignore interrupts that are wake only during resume
Before suspending, mtk-eint would set the interrupt mask to the
one in wake_mask. However, some of these interrupts may not have a
corresponding interrupt handler, or the interrupt may be disabled.
On resume, the eint irq handler would trigger nevertheless,
and irq/pm.c:irq_pm_check_wakeup would be called, which would
try to call irq_disable. However, if the interrupt is not enabled
(irqd_irq_disabled(&desc->irq_data) is true), the call does nothing,
and the interrupt is left enabled in the eint driver.
Especially for level-sensitive interrupts, this will lead to an
interrupt storm on resume.
If we detect that an interrupt is only in wake_mask, but not in
cur_mask, we can just mask it out immediately (as mtk_eint_resume
would do anyway at a later stage in the resume sequence, when
restoring cur_mask).
Fixes: bf22ff45bed6 ("genirq: Avoid unnecessary low level irq function calls")
Signed-off-by: Nicolas Boichat <drinkcat@chromium.org>
Acked-by: Sean Wang <sean.wang@kernel.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r-- | drivers/pinctrl/mediatek/mtk-eint.c | 16 |
1 files changed, 15 insertions, 1 deletions
diff --git a/drivers/pinctrl/mediatek/mtk-eint.c b/drivers/pinctrl/mediatek/mtk-eint.c index f464f8cd274b..737385e86beb 100644 --- a/drivers/pinctrl/mediatek/mtk-eint.c +++ b/drivers/pinctrl/mediatek/mtk-eint.c | |||
@@ -318,7 +318,7 @@ static void mtk_eint_irq_handler(struct irq_desc *desc) | |||
318 | struct irq_chip *chip = irq_desc_get_chip(desc); | 318 | struct irq_chip *chip = irq_desc_get_chip(desc); |
319 | struct mtk_eint *eint = irq_desc_get_handler_data(desc); | 319 | struct mtk_eint *eint = irq_desc_get_handler_data(desc); |
320 | unsigned int status, eint_num; | 320 | unsigned int status, eint_num; |
321 | int offset, index, virq; | 321 | int offset, mask_offset, index, virq; |
322 | void __iomem *reg = mtk_eint_get_offset(eint, 0, eint->regs->stat); | 322 | void __iomem *reg = mtk_eint_get_offset(eint, 0, eint->regs->stat); |
323 | int dual_edge, start_level, curr_level; | 323 | int dual_edge, start_level, curr_level; |
324 | 324 | ||
@@ -328,10 +328,24 @@ static void mtk_eint_irq_handler(struct irq_desc *desc) | |||
328 | status = readl(reg); | 328 | status = readl(reg); |
329 | while (status) { | 329 | while (status) { |
330 | offset = __ffs(status); | 330 | offset = __ffs(status); |
331 | mask_offset = eint_num >> 5; | ||
331 | index = eint_num + offset; | 332 | index = eint_num + offset; |
332 | virq = irq_find_mapping(eint->domain, index); | 333 | virq = irq_find_mapping(eint->domain, index); |
333 | status &= ~BIT(offset); | 334 | status &= ~BIT(offset); |
334 | 335 | ||
336 | /* | ||
337 | * If we get an interrupt on pin that was only required | ||
338 | * for wake (but no real interrupt requested), mask the | ||
339 | * interrupt (as would mtk_eint_resume do anyway later | ||
340 | * in the resume sequence). | ||
341 | */ | ||
342 | if (eint->wake_mask[mask_offset] & BIT(offset) && | ||
343 | !(eint->cur_mask[mask_offset] & BIT(offset))) { | ||
344 | writel_relaxed(BIT(offset), reg - | ||
345 | eint->regs->stat + | ||
346 | eint->regs->mask_set); | ||
347 | } | ||
348 | |||
335 | dual_edge = eint->dual_edge[index]; | 349 | dual_edge = eint->dual_edge[index]; |
336 | if (dual_edge) { | 350 | if (dual_edge) { |
337 | /* | 351 | /* |