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
next prev parent 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