Linux GPIO subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Birk Skyum" <birk.skyum@pm.me>
Cc: linux-gpio@vger.kernel.org
Subject: Re: [PATCH v3] pinctrl: qcom: Restore the pin configuration after hibernation
Date: Wed, 21 Oct 2026 20:15:20 +0000	[thread overview]
Message-ID: <sashiko-outbox-180634@kernel.org> (raw)
In-Reply-To: <20261021195250.83540-1-birk.skyum@pm.me>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] pinctrl: qcom: msm_pinctrl_restore_noirq() enables IRQ before clearing status
--

commit d8439285386d0a36cbc72eb8f4753391eb0fd921
Author: Birk Skyum <birk.skyum@pm.me>

pinctrl: qcom: Restore the pin configuration after hibernation

This commit adds freeze_noirq and restore_noirq hibernation callbacks to
pinctrl-msm to save and restore the control, output, and interrupt
configuration registers for pins in use by the kernel as well as the SDC
pads. It also wires up msm_pinctrl_dev_pm_ops in the x1e80100 pinctrl
driver.

> diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
> index 27f06bbc113c9..8ddb4261bf89a 100644
> --- a/drivers/pinctrl/qcom/pinctrl-msm.c
> +++ b/drivers/pinctrl/qcom/pinctrl-msm.c

[ ... ]

> +static int msm_pinctrl_restore_noirq(struct device *dev)
> +{
> +	struct msm_pinctrl *pctrl = dev_get_drvdata(dev);
> +	const struct msm_pingroup *g;
> +	struct msm_pin_state *s;
> +	unsigned int i;
> +	u32 val;
> +
> +	guard(raw_spinlock_irqsave)(&pctrl->lock);
> +
> +	for (i = 0; i < pctrl->soc->ngroups; i++) {
> +		g = &pctrl->soc->groups[i];
> +		s = &pctrl->saved[i];
> +		if (!s->saved)
> +			continue;
> +
> +		if (i >= pctrl->chip.ngpio) {
> +			msm_writel_ctl(s->ctl, pctrl, g);
> +			continue;
> +		}
> +
> +		/* Set the level before the pin starts driving it. */
> +		val = msm_readl_io(pctrl, g);
> +		val &= ~BIT(g->out_bit);
> +		val |= s->io & BIT(g->out_bit);
> +		msm_writel_io(val, pctrl, g);
> +
> +		msm_writel_ctl(s->ctl, pctrl, g);
> +		if (!g->intr_cfg_reg)
> +			continue;
> +
> +		if (msm_pin_has_own_intr_target(pctrl, g))
> +			msm_writel_intr_target(s->intr_target, pctrl, g);
> +		val = msm_readl_intr_cfg(pctrl, g);
> +		msm_writel_intr_cfg(s->intr_cfg, pctrl, g);
> +
> +		/*
> +		 * Changing the configuration can latch an interrupt. One that
> +		 * is latched with the configuration unchanged is a real one.
> +		 */
> +		if (val != s->intr_cfg)
> +			msm_ack_intr_status(pctrl, g);

[Severity: Medium]
Can writing s->intr_cfg before calling msm_ack_intr_status() in
msm_pinctrl_restore_noirq() in drivers/pinctrl/qcom/pinctrl-msm.c assert a
spurious interrupt on the TLMM summary IRQ line?

In msm_gpio_irq_set_type(), writing intr_cfg before clearing the latched
status is noted as safe only because the interrupt is masked:

drivers/pinctrl/qcom/pinctrl-msm.c:msm_gpio_irq_set_type() {
	...
	/*
	 * The first time we set RAW_STATUS_EN it could trigger an interrupt.
	 * Clear the interrupt.  This is safe because we have
	 * IRQCHIP_SET_TYPE_MASKED. When changing the interrupt type, we could
	 * also still have a non-matching interrupt latched, so clear whenever
	 * making changes to the interrupt configuration.
	 */
	if (val != oldval)
		msm_ack_intr_status(pctrl, g);
	...
}

During hibernation freeze, device_wakeup_arm_wake_irqs() and
suspend_device_irqs() -> suspend_device_irq() in kernel/irq/pm.c run before
msm_pinctrl_freeze_noirq(). For any GPIO IRQ that is not in
pctrl->skip_wake_irqs and has wakeup enabled (or uses IRQF_NO_SUSPEND),
suspend_device_irq() leaves the line unmasked:

kernel/irq/pm.c:suspend_device_irq() {
	if (!desc->action || irq_desc_is_chained(desc) ||
	    desc->no_suspend_depth)
		return false;

	if (irqd_is_wakeup_set(irqd)) {
		irqd_set(irqd, IRQD_WAKEUP_ARMED);

		if ((chipflags & IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND) &&
		     irqd_irq_disabled(irqd)) {
			__enable_irq(desc);
			irqd_set(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND);
		}
		return true;
	}
	...
}

As a result, msm_pinctrl_freeze_noirq() saves s->intr_cfg with both
BIT(g->intr_raw_status_bit) and BIT(g->intr_enable_bit) set, and bit i
remains set in pctrl->enabled_irqs.

On hibernation restore in create_image() in kernel/power/hibernate.c,
local_irq_enable() and pm_sleep_enable_secondary_cpus() run before
dpm_resume_start(PMSG_RESTORE) -> dpm_resume_noirq():

kernel/power/hibernate.c:create_image() {
	...
 Enable_irqs:
	system_state = SYSTEM_RUNNING;
	local_irq_enable();

 Enable_cpus:
	pm_sleep_enable_secondary_cpus();
	...
	dpm_resume_start(in_suspend ?
		(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
	...
}

Because the parent TLMM summary IRQ (pctrl->irq) is a chained handler
(msm_gpio_irq_handler), suspend_device_irq() skips it via
irq_desc_is_chained(), leaving it active and unmasked at the GIC while
secondary CPUs are online with IRQs enabled.

When msm_pinctrl_restore_noirq() writes s->intr_cfg with
BIT(g->intr_enable_bit) set before calling msm_ack_intr_status(), any
status bit latched by toggling RAW_STATUS_EN or changing detection/polarity
bits can immediately assert the summary IRQ. Since msm_gpio_irq_handler()
does not take pctrl->lock, another CPU servicing the summary IRQ could
either observe the bogus status bit and dispatch a spurious interrupt, or
observe handled == 0 after msm_ack_intr_status() clears it and call
handle_bad_irq().

Would it be safer to write s->intr_cfg with BIT(g->intr_enable_bit) masked
first, call msm_ack_intr_status() if the configuration changed, and only
then set BIT(g->intr_enable_bit) if it was saved as enabled?

> +	}
> +
> +	return 0;
> +}

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261021195250.83540-1-birk.skyum@pm.me?part=1

      reply	other threads:[~2026-10-21 20:15 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-21 19:52 [PATCH v3] pinctrl: qcom: Restore the pin configuration after hibernation Birk Skyum
2026-10-21 20:15 ` sashiko-bot [this message]

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=sashiko-outbox-180634@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=birk.skyum@pm.me \
    --cc=linux-gpio@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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