Devicetree
 help / color / mirror / Atom feed
From: Andrea della Porta <andrea.porta@suse.com>
To: sashiko-reviews@lists.linux.dev
Cc: Andrea della Porta <andrea.porta@suse.com>,
	devicetree@vger.kernel.org, conor+dt@kernel.org, robh@kernel.org,
	ukleinek@kernel.org
Subject: Re: [PATCH v7 2/3] pwm: rp1: Add RP1 PWM controller driver
Date: Fri, 24 Jul 2026 17:04:04 +0200	[thread overview]
Message-ID: <amN-5B5POX3rNb29@apocalypse> (raw)
In-Reply-To: <20260720100212.BEF1D1F000E9@smtp.kernel.org>

Hi,

On 10:02 Mon 20 Jul     , sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [High] Missing `.remove` callback leads to Use-After-Free in the PWM and Syscon cores and leaks clock resources on driver unbind.
> - [High] Register context loss on suspend/resume silently breaks PWM output.
> - [High] Unclamped hardware duty ticks lead to integer underflow in the PWM core for normal polarity.
> --
> 
> --- Patch [2]: [PATCH v7 2/3] pwm: rp1: Add RP1 PWM controller driver ---
> commit 0c98703d857577902e57d25b42a3d46dfbde5795
> Author: Naushir Patuck <naush@raspberrypi.com>
> 
> pwm: rp1: Add RP1 PWM controller driver
> 
> The Raspberry Pi RP1 southbridge features an embedded PWM controller with
> 4 output channels, alongside an RPM interface to read the fan speed on the
> Raspberry Pi 5. Add the supporting driver.
> 
> > diff --git a/drivers/pwm/pwm-rp1.c b/drivers/pwm/pwm-rp1.c
> > new file mode 100644
> > --- /dev/null
> > +++ b/drivers/pwm/pwm-rp1.c
> 
> [ ... ]
> 
> > +static int rp1_pwm_round_waveform_fromhw(struct pwm_chip *chip,
> > +					 struct pwm_device *pwm,
> > +					 const void *_wfhw,
> > +					 struct pwm_waveform *wf)
> > +{
> > +	struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> > +	const struct rp1_pwm_waveform *wfhw = _wfhw;
> > +	u64 clk_rate = rp1->clk_rate;
> > +	u64 ticks;
> > +
> > +	*wf = (struct pwm_waveform){ };
> > +
> > +	if (!wfhw->enabled)
> > +		return 0;
> > +
> > +	wf->period_length_ns = DIV_ROUND_UP_ULL(((u64)wfhw->period_ticks + 1) * NSEC_PER_SEC,
> > +						clk_rate);
> > +
> > +	if (!wfhw->inverted_polarity) {
> > +		wf->duty_length_ns = DIV_ROUND_UP_ULL((u64)wfhw->duty_ticks * NSEC_PER_SEC,
> > +						      (u32)clk_rate);
> > +	} else {
> 
> [Severity: High]
> Does the normal polarity case need to clamp the duty ticks similar to the
> inverted polarity case?
> 
> If the bootloader configures a continuous-high state where the raw hardware
> duty ticks exceed the period ticks + 1, this path calculates a duty_length_ns
> that is strictly greater than the period_length_ns.
> 
> When the PWM core subsequently reads this initial state, pwm_wf2state()
> handles duty > period by treating it as an inverted polarity and calculating
> duty_cycle = period - duty_length_ns. This subtraction can silently
> underflow, causing the core to interpret the hardware state as having a badly
> corrupted duty cycle.

The conditional right after the opening bracket of this else branch (see below
for the code) is there just for that: clamping the duty to sane values. So it
already takes care of that.

> 
> > +		if (wfhw->duty_ticks > (u64)wfhw->period_ticks + 1) {
> > +			/* 100% duty cycle case */
> > +			ticks = 0;
> > +		} else {
> 
> [ ... ]
> 
> > +static int rp1_pwm_write_waveform(struct pwm_chip *chip,
> > +				  struct pwm_device *pwm,
> > +				  const void *_wfhw)
> > +{
> > +	struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> > +	const struct rp1_pwm_waveform *wfhw = _wfhw;
> > +	u32 value, ctrl;
> > +
> > +	/* set polarity */
> > +	regmap_read(rp1->regmap, RP1_PWM_CHAN_CTRL(pwm->hwpwm), &value);
> > +	if (!wfhw->inverted_polarity)
> > +		value &= ~RP1_PWM_CHAN_CTRL_POLARITY;
> > +	else
> > +		value |= RP1_PWM_CHAN_CTRL_POLARITY;
> > +	regmap_write(rp1->regmap, RP1_PWM_CHAN_CTRL(pwm->hwpwm), value);
> 
> [Severity: High]
> Could the PWM controller lose its hardware configuration after a system
> suspend and resume?
> 
> During rp1_pwm_request(), the channel control register is initialized with
> essential bits like RP1_PWM_CHAN_CTRL_DEFAULT, which includes the
> MODE_TE_MS and FIFO_POP_MASK configurations.
> 
> If the hardware loses power during suspend, the registers reset. On resume,
> when a consumer re-applies its state, this regmap_read() will read the wiped
> hardware register. The driver will then update only the polarity bit and
> write it back, failing to restore the required operational mode bits that
> were initialized during the request phase.

I think I've already answered this here: 
https://lore.kernel.org/all/aiGAINsTG8VZLn28@apocalypse/
so I guess Sashiko is not sotring the entire history as
context, so I'll reply it here:

AFAIK the RP1 is never powered-off, so just shutting down the clock should be ok
and no register are reset to default.

> 
> > +
> > +	/* early exit if disabled */
> 
> [ ... ]
> 
> > +static struct platform_driver rp1_pwm_driver = {
> > +	.probe = rp1_pwm_probe,
> > +	/*
> > +	 * .remove is not needed because this driver cannot be removed/unbound
> > +	 * cause it relies on syscon which cannot be removed
> > +	 */
> > +	.driver = {
> > +		.name = "rp1-pwm",
> 
> [Severity: High]
> Is it safe to omit the remove callback here?
> 
> Even with suppress_bind_attrs set on the driver, if the parent RP1 PCIe
> device is removed, reset, or unbound via sysfs, it will forcibly unbind all
> child devices.
> 
> Without a remove callback to run cleanup, pwmchip_remove() is never called
> and the clock is left enabled. Because the backing memory is devm-managed,
> the devres subsystem will eventually free the pwm_chip and regmap memory.
> This can leave dangling pointers in the core pwm_chips IDR and the global
> syscon_list, which could result in a use-after-free.

This is true. Uwe, I guess I should reintroduce the .remove callback,
what do you think?

Thanks,
Andrea

> 
> > +		.of_match_table = rp1_pwm_of_match,
> > +		.pm = pm_ptr(&rp1_pwm_pm_ops),
> > +		.suppress_bind_attrs = true,
> > +	},
> > +};
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/cover.1784539788.git.andrea.porta@suse.com?part=2

  reply	other threads:[~2026-07-24 15:00 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-20  9:44 [PATCH v7 0/3] Add RP1 PWM controller support Andrea della Porta
2026-07-20  9:44 ` [PATCH v7 1/3] dt-bindings: pwm: Add Raspberry Pi RP1 PWM controller Andrea della Porta
2026-07-20  9:50   ` sashiko-bot
2026-07-24 14:16     ` Andrea della Porta
2026-07-20  9:44 ` [PATCH v7 2/3] pwm: rp1: Add RP1 PWM controller driver Andrea della Porta
2026-07-20 10:02   ` sashiko-bot
2026-07-24 15:04     ` Andrea della Porta [this message]
2026-08-25 18:42       ` Gary Guo
2026-09-04 13:53         ` Andrea della Porta
2026-09-04 14:05           ` Gary Guo
2026-09-04 16:42             ` Andrea della Porta
2026-09-03 20:36   ` Christophe JAILLET
2026-09-04 16:31     ` Andrea della Porta
2026-07-20  9:44 ` [PATCH v7 3/3] arm64: dts: broadcom: rpi-5: Add RP1 PWM node Andrea della Porta
2026-09-03 20:11 ` [PATCH v7 0/3] Add RP1 PWM controller support Florian Fainelli

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=amN-5B5POX3rNb29@apocalypse \
    --to=andrea.porta@suse.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=ukleinek@kernel.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