Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Andrea della Porta" <andrea.porta@suse.com>
Cc: conor+dt@kernel.org, devicetree@vger.kernel.org, robh@kernel.org
Subject: Re: [PATCH v4 2/3] pwm: rp1: Add RP1 PWM controller driver
Date: Wed, 03 Jun 2026 15:47:15 +0000	[thread overview]
Message-ID: <20260603154716.1B5C41F00893@smtp.kernel.org> (raw)
In-Reply-To: <8eef956a5eb473f051bbda89ec4c9991c1b47de2.1780498640.git.andrea.porta@suse.com>

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [High] Missing `pwmchip_remove()` on the `of_syscon_register_regmap()` error path leaves a dangling PWM chip.
- [High] When disabling a PWM channel, the driver fails to clear the polarity bit, causing the output to erroneously remain HIGH if it was previously inverted.
- [High] Registering a devm-managed regmap with the permanent syscon registry creates a teardown asymmetry.
- [High] Hardware channel control registers (`CHAN_CTRL`) are not restored across system suspend/resume.
- [Medium] Non-monotonic period limit checking in `rp1_pwm_round_waveform_tohw()` causes larger requested periods to result in smaller hardware periods.
- [Medium] Calculating `period_length_ns` from hardware state suffers from a 32-bit integer overflow before casting to u64, resulting in a zero period.
- [Medium] Calculating inverted polarity duty length underflows if the hardware is configured for a 100% duty cycle.
- [Medium] Returning a positive integer (1) from `round_waveform_tohw` incorrectly propagates as an error.
--

commit 90d2019e94496c6949f2d04c2605c6a197b16d6a
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
> index 0000000000000..052c3ae770569
> --- /dev/null
> +++ b/drivers/pwm/pwm-rp1.c
[ ... ]
> +static int rp1_pwm_round_waveform_tohw(struct pwm_chip *chip,
> +				       struct pwm_device *pwm,
> +				       const struct pwm_waveform *wf,
> +				       void *_wfhw)
> +{
> +	struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> +	u64 period_ticks, duty_ticks, offset_ticks;
> +	struct rp1_pwm_waveform *wfhw = _wfhw;
> +	u64 clk_rate = rp1->clk_rate;
> +	int ret = 0;
> +
> +	if (!wf->period_length_ns) {
> +		wfhw->enabled = false;
> +		return 0;
> +	}
> +
> +	period_ticks = mul_u64_u64_div_u64(wf->period_length_ns, clk_rate, NSEC_PER_SEC);
> +
> +	/*
> +	 * The period is limited to U32_MAX - 1
> +	 * to allow 100% duty cycle.
> +	 */
> +	if (period_ticks > U32_MAX) {
> +		period_ticks = U32_MAX - 1;

[Severity: Medium]
Does this limit check break monotonicity for large periods?

If the requested period translates to U32_MAX, this condition is false and
the period becomes U32_MAX - 1.

If it translates to U32_MAX + 1, it becomes U32_MAX - 1, which is then
subtracted by 1 later, yielding U32_MAX - 2. This makes a larger requested
period result in a smaller actual hardware period.

> +	} else if (!period_ticks) {
> +		period_ticks = 2;
> +		ret = 1;
> +	}

[Severity: Medium]
Is returning a positive integer here intentional?

The PWM core subsystem expects round_waveform_tohw callbacks to return 0 on
success or a negative error code on failure. Returning 1 could propagate as
an error up to consumer drivers, breaking operation for very small periods.

> +
> +	duty_ticks = mul_u64_u64_div_u64(wf->duty_length_ns, clk_rate, NSEC_PER_SEC);
[ ... ]
> +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;
> +	u32 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);

[Severity: Medium]
Could this encounter a 32-bit integer overflow?

Since wfhw->period_ticks is a 32-bit integer, the addition of 1 happens in
32-bit space before the cast to u64.

If the hardware register was configured to 0xffffffff by the bootloader for
a 100% duty cycle, 0xffffffff + 1 wraps around to 0, producing a period of 0.

Should the cast be placed before the addition?

> +
> +	if (!wfhw->inverted_polarity) {
> +		wf->duty_length_ns = DIV_ROUND_UP_ULL((u64)wfhw->duty_ticks * NSEC_PER_SEC,
> +						      clk_rate);
> +	} else {
> +		ticks = wfhw->period_ticks + 1 - wfhw->duty_ticks;
> +		wf->duty_length_ns = DIV_ROUND_UP_ULL((u64)ticks * NSEC_PER_SEC, clk_rate);

[Severity: Medium]
Will this subtraction underflow if the hardware is configured for a 100%
duty cycle?

The driver allows duty_ticks to exceed period_ticks + 1 to achieve a 100%
duty cycle.

If inverted polarity is active and duty_ticks is strictly larger than
period_ticks + 1, this unsigned 32-bit subtraction will wrap around to a
massive integer, corrupting the duty_length_ns.

> +		wf->duty_offset_ns = wf->period_length_ns - wf->duty_length_ns;
> +	}
> +
> +	return 0;
> +}
> +
> +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;
> +
> +	/* early exit if disabled */
> +	regmap_read(rp1->regmap, RP1_PWM_GLB_CTRL, &ctrl);
> +	if (!wfhw->enabled) {
> +		ctrl &= ~RP1_PWM_GLB_CTRL_CHANNEL_ENABLE(pwm->hwpwm);
> +		goto exit_disable;
> +	}

[Severity: High]
Does skipping the polarity update here cause the output to erroneously
remain HIGH while disabled?

The early exit skips the read-modify-write block that updates
RP1_PWM_CHAN_CTRL_POLARITY.

The driver notes state that when disabled, the output is driven to 1 if
polarity is inversed. If a channel previously configured with inverted
polarity is disabled, it seems it will continually output a HIGH signal,
violating the PWM framework expectation.

> +
> +	/* set period and duty cycle */
> +	regmap_write(rp1->regmap,
[ ... ]
> +static int rp1_pwm_probe(struct platform_device *pdev)
> +{
[ ... ]
> +	chip->ops = &rp1_pwm_ops;
> +
> +	platform_set_drvdata(pdev, chip);
> +
> +	ret = pwmchip_add(chip);
> +	if (ret) {
> +		dev_err_probe(dev, ret, "Failed to register PWM chip\n");
> +		goto err_disable_clk;
> +	}
> +
> +	ret = of_syscon_register_regmap(np, rp1->regmap);
> +	if (ret) {
> +		dev_err_probe(dev, ret, "Failed to register syscon\n");
> +		goto err_disable_clk;
> +	}

[Severity: High]
Is there a missing call to pwmchip_remove() on this error path?

If of_syscon_register_regmap() fails, the function jumps to err_disable_clk
and returns an error.

Since the chip was allocated with devm_pwmchip_alloc(), devres will free
the memory, but the PWM subsystem will retain pointers to the freed chip in
its IDR mapping, potentially leading to a use-after-free.

[Severity: High]
Also, does registering a devm-managed regmap with the permanent syscon
registry create a teardown asymmetry?

The regmap is allocated with devm_regmap_init_mmio(). If the underlying
device is hotplug removed, devres will automatically free the regmap memory.

However, the syscon framework does not provide an unregister function,
leaving it with a dangling pointer in its static list that could cause a
use-after-free upon subsequent lookups.

> +
> +	return 0;
> +
> +err_disable_clk:
> +	clk_disable_unprepare(rp1->clk);
> +
> +	return ret;
> +}
[ ... ]
> +static int rp1_pwm_resume(struct device *dev)
> +{
> +	struct pwm_chip *chip = dev_get_drvdata(dev);
> +	struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> +	int ret;
> +
> +	ret = clk_prepare_enable(rp1->clk);
> +	if (ret) {
> +		dev_err(dev, "Failed to enable clock on resume: %pe\n", ERR_PTR(ret));
> +		return ret;
> +	}
> +
> +	rp1->clk_enabled = true;
> +
> +	return 0;
> +}

[Severity: High]
Should the hardware channel control registers (CHAN_CTRL) be restored across
resume?

When the system suspends, the southbridge may lose power and reset its
registers. During probe, these registers are initialized with required
configuration bits like MODE_TE_MS and FIFO_POP_MASK.

This resume function merely re-enables the clock. Because the regmap is not
configured to cache and restore registers, a subsequent write might update
only the polarity bit, leaving the channel mode at 0 and breaking PWM
output.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1780498640.git.andrea.porta@suse.com?part=2

  reply	other threads:[~2026-06-03 15:47 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-03 15:27 [PATCH v4 0/3] Add RP1 PWM controller support Andrea della Porta
2026-06-03 15:27 ` [PATCH v4 1/3] dt-bindings: pwm: Add Raspberry Pi RP1 PWM controller Andrea della Porta
2026-06-03 15:34   ` sashiko-bot
2026-06-03 15:27 ` [PATCH v4 2/3] pwm: rp1: Add RP1 PWM controller driver Andrea della Porta
2026-06-03 15:47   ` sashiko-bot [this message]
2026-06-03 15:27 ` [PATCH v4 3/3] arm64: dts: broadcom: rpi-5: Add RP1 PWM node Andrea della Porta

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=20260603154716.1B5C41F00893@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=andrea.porta@suse.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=robh@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