From: "Xuyang Dong" <dongxuyang@eswincomputing.com>
To: sashiko-reviews@lists.linux.dev
Cc: ukleinek@kernel.org, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org, ben-linux@fluff.org,
ben.dooks@codethink.co.uk, p.zabel@pengutronix.de,
linux-pwm@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linmin@eswincomputing.com,
pinkesh.vaghela@einfochips.com
Subject: Re: Re: [PATCH v9 3/3] pwm: dwc: add of/platform support
Date: Thu, 2 Jul 2026 17:25:26 +0800 (GMT+08:00) [thread overview]
Message-ID: <7ece6ad0.7ef5.19f2225bb9e.Coremail.dongxuyang@eswincomputing.com> (raw)
In-Reply-To: <20260701005613.662951F000E9@smtp.kernel.org>
>
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
> - [High] Missing CONFIG_PM dependency leads to unclocked MMIO accesses and kernel panics.
> - [High] Bypassing the PM framework during system suspend causes bus faults on devices attached to a PM domain.
> - [High] Reading MMIO registers while the device is in reset causes a bus stall and kernel panic.
> - [Medium] Data race on the shared `dwc->clk_rate` variable.
> --
>
> commit f417f1c0148d8abb6dc7081bbc13d8a85dcc173e
> Author: Xuyang Dong <dongxuyang@eswincomputing.com>
>
> pwm: dwc: add of/platform support
>
> This patch adds platform and OF device tree support for the DesignWare PWM
> controller. It introduces runtime PM support, manages clock enabling and
> disabling based on PWM channel usage, and implements polarity control by
> swapping load count registers.
>
> > diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> > --- a/drivers/pwm/Kconfig
> > +++ b/drivers/pwm/Kconfig
> > @@ -249,6 +249,16 @@ config PWM_DWC
> [ ... ]
> > +config PWM_DWC_OF
> > + tristate "DesignWare PWM Controller (OF bus)"
> > + depends on HAS_IOMEM && (OF || COMPILE_TEST)
>
> [Severity: High]
> Should this include a dependency on PM?
>
> If the kernel is compiled without CONFIG_PM, Kconfig allows building
> PWM_DWC_OF. During probe(), if no PWMs are enabled, the driver explicitly
> disables dwc->clk and dwc->bus_clk and relies on PM runtime to re-enable
> them. Because CONFIG_PM is disabled, pm_runtime_resume_and_get() evaluates to
> a stub that returns 1 (success) without calling the runtime resume callback.
> This leads to unclocked register accesses and kernel panics.
>
Hi Sashiko,
Do not add a dependency on PM. Additionally, the issues below will be
fixed to ensure compatibility with and without PM support.
> > diff --git a/drivers/pwm/pwm-dwc-core.c b/drivers/pwm/pwm-dwc-core.c
> > --- a/drivers/pwm/pwm-dwc-core.c
> > +++ b/drivers/pwm/pwm-dwc-core.c
> > @@ -44,21 +46,73 @@ static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc,
> [ ... ]
> > - high = tmp - 1;
> > + if (dwc->clk)
> > + dwc->clk_rate = clk_get_rate(dwc->clk);
>
> [Severity: Medium]
> Can this write to dwc->clk_rate cause a data race?
>
> The PWM framework serializes operations on a per-channel basis, but not
> across the entire controller. If multiple threads concurrently request state
> changes (apply) or state reads (get_state) on different PWM channels of the
> same controller, they might simultaneously write to this shared variable.
>
The PWM core's chip->nonatomic_lock (per-chip mutex) serializes
all apply() and get_state() calls across all channels on the same
chip. The apparent write to dwc->clk_rate in both
__dwc_pwm_configure_timer() and dwc_pwm_get_state() cannot race
because both code paths are reached only under this lock.
No locking concerns.
> > @@ -121,10 +183,23 @@ static int dwc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
> [ ... ]
> > + if (dwc->clk)
> > + dwc->clk_rate = clk_get_rate(dwc->clk);
>
> [Severity: Medium]
> Does this introduce a similar data race on dwc->clk_rate as the one mentioned
> above in __dwc_pwm_configure_timer()?
>
It is the same as above.
> > diff --git a/drivers/pwm/pwm-dwc-of.c b/drivers/pwm/pwm-dwc-of.c
> > --- /dev/null
> > +++ b/drivers/pwm/pwm-dwc-of.c
> > @@ -0,0 +1,356 @@
> [ ... ]
> > +static int dwc_pwm_plat_probe(struct platform_device *pdev)
> > +{
> [ ... ]
> > + ret = clk_prepare_enable(dwc->clk);
> > + if (ret) {
> > + dev_err(dev, "failed to enable timer clock\n");
> > + goto disable_busclk;
> > + }
> > +
> > + /*
> > + * Check all channels to see if any channel is enabled.
> > + * Read the control register of each channel and extract the enable bit
> > + */
> > + for (i = 0; i < chip->npwm; i++) {
> > + ctrl[i] = dwc_pwm_readl(dwc, DWC_TIM_CTRL(i)) & DWC_TIM_CTRL_EN;
> > + if (ctrl[i])
> > + pwm_en = true;
> > + }
> > +
> > + /*
> > + * Only issue a reset pulse when all channels are disabled, so a PWM
> > + * channel already running (e.g. configured by firmware before Linux
> > + * took over) is left undisturbed.
> > + */
> > + if (!pwm_en) {
> > + ret = reset_control_reset(dwc->rst);
>
> [Severity: High]
> Could this read from DWC_TIM_CTRL(i) cause a bus stall and kernel panic if the
> device is currently in reset?
>
> Module removal explicitly asserts the reset line in dwc_pwm_plat_remove(),
> leaving the device in reset. On a subsequent probe (e.g., reloading the
> module), this loop attempts to read from the MMIO space before the reset is
> deasserted.
>
Deassert reset before reading MMIO registers. The channel-enable
scan reads DWC_TIM_CTRL while the device may still be held in reset
after a previous remove(). Reading MMIO while in reset causes a bus
stall on many SoCs; call reset_control_deassert() before the scan.
> [ ... ]
> > + if (pwm_en) {
> > + pm_runtime_set_active(dev);
> > + for (i = 0; i < chip->npwm; i++) {
> > + if (ctrl[i])
> > + pm_runtime_get_noresume(dev);
> > + }
> > + } else {
> > + clk_disable_unprepare(dwc->clk);
> > + clk_disable_unprepare(dwc->bus_clk);
> > + }
>
> [Severity: High]
> If CONFIG_PM is disabled in the kernel, does gating these clocks here cause
> subsequent unclocked register accesses?
>
> Since pm_runtime_resume_and_get() becomes a stub that returns 1 without
> re-enabling clocks, subsequent operations like apply() or get_state() would
> perform unclocked MMIO accesses leading to panics.
>
Keep clocks enabled when CONFIG_PM is disabled. When CONFIG_PM=n,
pm_runtime callbacks are stubs that never invoke the runtime_resume
hook, so clocks gated in probe are never re-enabled before MMIO
access in apply() and get_state(). Condition the clock gating on
IS_ENABLED(CONFIG_PM), and update the pm_disable error path to
disable clocks when CONFIG_PM=n (they were never gated in that case).
> [ ... ]
> > +static void dwc_pwm_plat_remove(struct platform_device *pdev)
> > +{
> [ ... ]
> > + if (!pm_runtime_status_suspended(&pdev->dev)) {
> > + clk_disable_unprepare(dwc->clk);
> > + clk_disable_unprepare(dwc->bus_clk);
> > + }
>
> [Severity: High]
> If CONFIG_PM is disabled, pm_runtime_status_suspended() will always return
> false. Does this cause a double-disable of the clocks if they were already
> gated in probe?
>
With the above fix, the clocks in probe will not be disabled when
CONFIG_PM=n. This prevents the double-disable issue.
> [ ... ]
> > +static int dwc_pwm_suspend(struct device *dev)
> > +{
> > + struct dwc_pwm_drvdata *data = dev_get_drvdata(dev);
> > + struct pwm_chip *chip = data->chips[0];
> > + struct dwc_pwm *dwc = to_dwc_pwm(chip);
> > + unsigned int idx;
> > + int ret;
> > +
> > + if (pm_runtime_status_suspended(dev)) {
> > + ret = dwc_pwm_runtime_resume(dev);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + for (idx = 0; idx < chip->npwm; idx++) {
> > + dwc->ctx[idx].cnt = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(idx));
>
> [Severity: High]
> Does calling the local dwc_pwm_runtime_resume() directly bypass the PM
> framework?
>
> If the device is attached to a generic power domain (genpd) and is
> runtime-suspended, the PM domain itself might be powered off. Bypassing
> pm_runtime_resume_and_get() means the PM domain is never powered back on,
> and the subsequent dwc_pwm_readl() calls will access unpowered hardware
> causing a bus fault.
>
Use pm_runtime_resume_and_get() instead of calling
dwc_pwm_runtime_resume() directly.
Move the register read/write operations from dwc_pwm_suspend/resume() into
dwc_pwm_runtime_suspend/resume().
Switch system sleep callbacks to pm_runtime_force_suspend() and
pm_runtime_force_resume(), instead of dwc_pwm_suspend() and
dwc_pwm_resume().
Would this approach be more suitable?
Best regards,
Xuyang Dong
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260701004139.347-1-dongxuyang@eswincomputing.com?part=3
prev parent reply other threads:[~2026-07-02 9:25 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-01 0:41 [PATCH v9 0/3] Update designware pwm driver dongxuyang
2026-07-01 0:42 ` [PATCH v9 1/3] dt-bindings: pwm: dwc: Document optional resets property dongxuyang
2026-07-01 0:43 ` [PATCH v9 2/3] dt-bindings: pwm: dwc: Add eswin compatible dongxuyang
2026-07-01 0:43 ` [PATCH v9 3/3] pwm: dwc: add of/platform support dongxuyang
2026-07-01 0:56 ` sashiko-bot
2026-07-02 9:25 ` Xuyang Dong [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=7ece6ad0.7ef5.19f2225bb9e.Coremail.dongxuyang@eswincomputing.com \
--to=dongxuyang@eswincomputing.com \
--cc=ben-linux@fluff.org \
--cc=ben.dooks@codethink.co.uk \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linmin@eswincomputing.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pwm@vger.kernel.org \
--cc=p.zabel@pengutronix.de \
--cc=pinkesh.vaghela@einfochips.com \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.