From: Rosen Penev <rosenp@gmail.com>
To: linux-gpio@vger.kernel.org
Cc: Linus Walleij <linusw@kernel.org>,
Bartosz Golaszewski <brgl@kernel.org>,
Sebastian Andrzej Siewior <bigeasy@linutronix.de>,
Clark Williams <clrkwllms@kernel.org>,
Steven Rostedt <rostedt@goodmis.org>,
linux-kernel@vger.kernel.org (open list),
linux-rt-devel@lists.linux.dev (open list:Real-time Linux
(PREEMPT_RT):Keyword:PREEMPT_RT)
Subject: [PATCHv2] gpio: mvebu: keep resume masks within the irqchip cache
Date: Mon, 14 Sep 2026 17:44:06 -0700 [thread overview]
Message-ID: <20260915004406.115230-1-rosenp@gmail.com> (raw)
mvebu_gpio_resume() writes the edge/level mask registers saved at
suspend time straight back to hardware, bypassing the irqchip's
mask_cache_priv. genirq skips mask_irq() for a line it already
considers masked, so restoring a bit in hardware that genirq thinks
is still masked leaves that line unmasked behind genirq's back. An
asserted level line then has nobody to ack it, and the moment
interrupts are re-enabled the chained handler storms, hanging resume.
AND the restored mask values with the matching irqchip mask cache so
only lines genirq currently considers unmasked are unmasked again.
Read the caches under gc->lock to keep them consistent with the
mask/unmask handlers.
Tested on Helios4 (armhf): 5 suspend cycles woken by magic packet,
no hang; mvebu_gpio_resume() returns in 6 usecs.
Assisted-by: LLM
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
v2: Unlock spinlock before regmap_write.
drivers/gpio/gpio-mvebu.c | 33 +++++++++++++++++++++++++++------
1 file changed, 27 insertions(+), 6 deletions(-)
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index c57758019e92..267e6d5bacd0 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -1049,6 +1049,8 @@ static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state)
static int mvebu_gpio_resume(struct platform_device *pdev)
{
struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev);
+ u32 edge_cache = ~0U, level_cache = ~0U;
+ unsigned long flags;
int i;
regmap_write(mvchip->regs, GPIO_OUT_OFF + mvchip->offset,
@@ -1060,32 +1062,51 @@ static int mvebu_gpio_resume(struct platform_device *pdev)
regmap_write(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset,
mvchip->in_pol_reg);
+ /*
+ * genirq skips mask_irq() for a line it already considers masked, so
+ * unmasking one behind its back leaves an asserted level line that
+ * nobody masks. Restore only bits the irqchip cache still has set.
+ *
+ * Snapshot the caches under the raw spinlock, but release it before
+ * the regmap writes below: regmap_write() takes a sleepable lock on
+ * PREEMPT_RT.
+ */
+ if (mvchip->domain) {
+ struct irq_chip_generic *gc;
+
+ gc = irq_get_domain_generic_chip(mvchip->domain, 0);
+ raw_spin_lock_irqsave(&gc->lock, flags);
+ level_cache = gc->chip_types[0].mask_cache_priv;
+ edge_cache = gc->chip_types[1].mask_cache_priv;
+ raw_spin_unlock_irqrestore(&gc->lock, flags);
+ }
+
switch (mvchip->soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
case MVEBU_GPIO_SOC_VARIANT_A8K:
regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF + mvchip->offset,
- mvchip->edge_mask_regs[0]);
+ mvchip->edge_mask_regs[0] & edge_cache);
regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF + mvchip->offset,
- mvchip->level_mask_regs[0]);
+ mvchip->level_mask_regs[0] & level_cache);
break;
case MVEBU_GPIO_SOC_VARIANT_MV78200:
for (i = 0; i < 2; i++) {
regmap_write(mvchip->regs,
GPIO_EDGE_MASK_MV78200_OFF(i),
- mvchip->edge_mask_regs[i]);
+ mvchip->edge_mask_regs[i] & edge_cache);
regmap_write(mvchip->regs,
GPIO_LEVEL_MASK_MV78200_OFF(i),
- mvchip->level_mask_regs[i]);
+ mvchip->level_mask_regs[i] & level_cache);
}
break;
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
for (i = 0; i < 4; i++) {
regmap_write(mvchip->regs,
GPIO_EDGE_MASK_ARMADAXP_OFF(i),
- mvchip->edge_mask_regs[i]);
+ mvchip->edge_mask_regs[i] & edge_cache);
regmap_write(mvchip->regs,
GPIO_LEVEL_MASK_ARMADAXP_OFF(i),
- mvchip->level_mask_regs[i]);
+ mvchip->level_mask_regs[i] & level_cache);
}
break;
default:
--
2.55.0
next reply other threads:[~2026-09-15 0:44 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-15 0:44 Rosen Penev [this message]
2026-09-15 0:57 ` sashiko-bot
2026-09-15 6:43 ` Sebastian Andrzej Siewior
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260915004406.115230-1-rosenp@gmail.com \
--to=rosenp@gmail.com \
--cc=bigeasy@linutronix.de \
--cc=brgl@kernel.org \
--cc=clrkwllms@kernel.org \
--cc=linusw@kernel.org \
--cc=linux-gpio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rt-devel@lists.linux.dev \
--cc=rostedt@goodmis.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®