After commit 7346e7a058a2 ("pwm: stm32: Always do lazy disabling"), polarity changes are ignored. Updates to the TIMx_CCER CCxP bits are ignored by the hardware when the master output is enabled via the TIMx_BDTR MOE bit.
Handle polarity changes by temporarily disabling the PWM when required, in line with apply() implementations used by other PWM drivers.
Fixes: 7346e7a058a2 ("pwm: stm32: Always do lazy disabling") Cc: stable@vger.kernel.org Signed-off-by: Sean Nyekjaer sean@geanix.com --- This patch is only applicable for stable tree's <= 6.12 How to explicitly state that and what is the procedure? --- drivers/pwm/pwm-stm32.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index eb24054f9729734da21eb96f2e37af03339e3440..d5f79e87a0653e1710d46e6bf9268a59638943fe 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -452,15 +452,23 @@ static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
enabled = pwm->state.enabled;
+ if (state->polarity != pwm->state.polarity) { + if (enabled) { + stm32_pwm_disable(priv, pwm->hwpwm); + enabled = false; + } + + ret = stm32_pwm_set_polarity(priv, pwm->hwpwm, state->polarity); + if (ret) + return ret; + } + if (!state->enabled) { if (enabled) stm32_pwm_disable(priv, pwm->hwpwm); return 0; }
- if (state->polarity != pwm->state.polarity) - stm32_pwm_set_polarity(priv, pwm->hwpwm, state->polarity); - ret = stm32_pwm_config(priv, pwm->hwpwm, state->duty_cycle, state->period); if (ret)
--- base-commit: eb18504ca5cf1e6a76a752b73daf0ef51de3551b change-id: 20260105-stm32-pwm-91cb843680f4
Best regards,
Hello Sean,
On Tue, Jan 06, 2026 at 08:01:57AM +0100, Sean Nyekjaer wrote:
After commit 7346e7a058a2 ("pwm: stm32: Always do lazy disabling"), polarity changes are ignored. Updates to the TIMx_CCER CCxP bits are ignored by the hardware when the master output is enabled via the TIMx_BDTR MOE bit.
Handle polarity changes by temporarily disabling the PWM when required, in line with apply() implementations used by other PWM drivers.
Fixes: 7346e7a058a2 ("pwm: stm32: Always do lazy disabling") Cc: stable@vger.kernel.org Signed-off-by: Sean Nyekjaer sean@geanix.com
This patch is only applicable for stable tree's <= 6.12 How to explicitly state that and what is the procedure?
I haven't checked in detail yet but I wonder if the problem also exists in newer kernels. Also I think that changing the polarity with the hardware on happend before 7346e7a058a2; in that case you blamed the wrong commit.
So even if we decide to apply a small targetted fix for the issue you report to stable without an equivalent commit in mainline (due to the rework the driver saw in v6.13-rc1~157^2~9^2~3 ("pwm: stm32: Implementation of the waveform callbacks")), I refuse to do that if the problem still exists in mainline.
drivers/pwm/pwm-stm32.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index eb24054f9729734da21eb96f2e37af03339e3440..d5f79e87a0653e1710d46e6bf9268a59638943fe 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -452,15 +452,23 @@ static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, enabled = pwm->state.enabled;
- if (state->polarity != pwm->state.polarity) {
if (enabled) {stm32_pwm_disable(priv, pwm->hwpwm);enabled = false;}ret = stm32_pwm_set_polarity(priv, pwm->hwpwm, state->polarity);if (ret)return ret;- }
- if (!state->enabled) { if (enabled) stm32_pwm_disable(priv, pwm->hwpwm); return 0; }
- if (state->polarity != pwm->state.polarity)
stm32_pwm_set_polarity(priv, pwm->hwpwm, state->polarity);- ret = stm32_pwm_config(priv, pwm->hwpwm, state->duty_cycle, state->period); if (ret)
I would prefer the following change:
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index eb24054f9729..5f118c20f1ca 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -452,12 +452,16 @@ static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
enabled = pwm->state.enabled;
- if (!state->enabled) { + /* The hardware must be disabled to honor polarity changes. */ + if (!state->enabled || state->polarity != pwm->state.polarity) { if (enabled) stm32_pwm_disable(priv, pwm->hwpwm); - return 0; + enabled = false; }
+ if (!state->enabled) + return 0; + if (state->polarity != pwm->state.polarity) stm32_pwm_set_polarity(priv, pwm->hwpwm, state->polarity);
Maybe it's just me, but I think the resulting code is simpler with this hunk.
I have hardware using this driver, will set it up later this week for testing.
Best regards Uwe
Hi Uwe,
On Tue, Jan 06, 2026 at 11:22:57AM +0100, Uwe Kleine-König wrote:
Hello Sean,
On Tue, Jan 06, 2026 at 08:01:57AM +0100, Sean Nyekjaer wrote:
After commit 7346e7a058a2 ("pwm: stm32: Always do lazy disabling"), polarity changes are ignored. Updates to the TIMx_CCER CCxP bits are ignored by the hardware when the master output is enabled via the TIMx_BDTR MOE bit.
Handle polarity changes by temporarily disabling the PWM when required, in line with apply() implementations used by other PWM drivers.
Fixes: 7346e7a058a2 ("pwm: stm32: Always do lazy disabling") Cc: stable@vger.kernel.org Signed-off-by: Sean Nyekjaer sean@geanix.com
This patch is only applicable for stable tree's <= 6.12 How to explicitly state that and what is the procedure?
I haven't checked in detail yet but I wonder if the problem also exists in newer kernels. Also I think that changing the polarity with the hardware on happend before 7346e7a058a2; in that case you blamed the wrong commit.
For your reference i bisected to that commit.
So even if we decide to apply a small targetted fix for the issue you report to stable without an equivalent commit in mainline (due to the rework the driver saw in v6.13-rc1~157^2~9^2~3 ("pwm: stm32: Implementation of the waveform callbacks")), I refuse to do that if the problem still exists in mainline.
I have tried to boot stable/master 6.19.0-rc4, my backlight is on! In stm32_pwm_write_waveform() TIMx_CCER is set before MOE is set.
drivers/pwm/pwm-stm32.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index eb24054f9729734da21eb96f2e37af03339e3440..d5f79e87a0653e1710d46e6bf9268a59638943fe 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -452,15 +452,23 @@ static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, enabled = pwm->state.enabled;
- if (state->polarity != pwm->state.polarity) {
if (enabled) {stm32_pwm_disable(priv, pwm->hwpwm);enabled = false;}ret = stm32_pwm_set_polarity(priv, pwm->hwpwm, state->polarity);if (ret)return ret;- }
- if (!state->enabled) { if (enabled) stm32_pwm_disable(priv, pwm->hwpwm); return 0; }
- if (state->polarity != pwm->state.polarity)
stm32_pwm_set_polarity(priv, pwm->hwpwm, state->polarity);- ret = stm32_pwm_config(priv, pwm->hwpwm, state->duty_cycle, state->period); if (ret)
I would prefer the following change:
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index eb24054f9729..5f118c20f1ca 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -452,12 +452,16 @@ static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, enabled = pwm->state.enabled;
- if (!state->enabled) {
- /* The hardware must be disabled to honor polarity changes. */
- if (!state->enabled || state->polarity != pwm->state.polarity) { if (enabled) stm32_pwm_disable(priv, pwm->hwpwm);
return 0;
}enabled = false;
- if (!state->enabled)
return 0;- if (state->polarity != pwm->state.polarity) stm32_pwm_set_polarity(priv, pwm->hwpwm, state->polarity);
Maybe it's just me, but I think the resulting code is simpler with this hunk.
Fine with me, I just looked at the other PWM drivers and copy/pasted from them :)
I have hardware using this driver, will set it up later this week for testing.
Very cool, looking forward to hear if you can re-produce.
Best regards Uwe
/Sean
Hey Sean,
On Tue, Jan 06, 2026 at 11:30:34AM +0000, Sean Nyekjaer wrote:
On Tue, Jan 06, 2026 at 11:22:57AM +0100, Uwe Kleine-König wrote:
On Tue, Jan 06, 2026 at 08:01:57AM +0100, Sean Nyekjaer wrote:
After commit 7346e7a058a2 ("pwm: stm32: Always do lazy disabling"), polarity changes are ignored. Updates to the TIMx_CCER CCxP bits are ignored by the hardware when the master output is enabled via the TIMx_BDTR MOE bit.
[...] I have hardware using this driver, will set it up later this week for testing.
Very cool, looking forward to hear if you can re-produce.
I cannot. I have:
# uname -r 6.11.0-rc1-00028-geb18504ca5cf-dirty
(the -dirty is only from enabling the pwm for my machine, no driver changes)
# cat /sys/kernel/debug/pwm 0: platform/40001000.timer:pwm, 4 PWM devices ... pwm-3 (sysfs ): requested enabled period: 313720 ns duty: 10000 ns polarity: normal
and pulseview/sigrok detects 3.187251% with a period of 313.8 µs.
After
echo inversed > /sys/class/pwm/pwmchip0/pwm3/polarity
the output changes to
# cat /sys/kernel/debug/pwm 0: platform/40001000.timer:pwm, 4 PWM devices ... pwm-3 (sysfs ): requested enabled period: 313720 ns duty: 10000 ns polarity: inverse
and pulseview/sigrok claims 96.812749% with a period of 313.8 µs. So the polarity change happend as expected.
This is on an st,stm32mp135f-dk board.
Where is the difference to your observations?
Best regards Uwe
Hi Uwe,
On Wed, Jan 07, 2026 at 04:54:46PM +0100, Uwe Kleine-König wrote:
Hey Sean,
On Tue, Jan 06, 2026 at 11:30:34AM +0000, Sean Nyekjaer wrote:
On Tue, Jan 06, 2026 at 11:22:57AM +0100, Uwe Kleine-König wrote:
On Tue, Jan 06, 2026 at 08:01:57AM +0100, Sean Nyekjaer wrote:
After commit 7346e7a058a2 ("pwm: stm32: Always do lazy disabling"), polarity changes are ignored. Updates to the TIMx_CCER CCxP bits are ignored by the hardware when the master output is enabled via the TIMx_BDTR MOE bit.
[...] I have hardware using this driver, will set it up later this week for testing.
Very cool, looking forward to hear if you can re-produce.
I cannot. I have:
# uname -r 6.11.0-rc1-00028-geb18504ca5cf-dirty
(the -dirty is only from enabling the pwm for my machine, no driver changes)
# cat /sys/kernel/debug/pwm 0: platform/40001000.timer:pwm, 4 PWM devices ... pwm-3 (sysfs ): requested enabled period: 313720 ns duty: 10000 ns polarity: normal
and pulseview/sigrok detects 3.187251% with a period of 313.8 µs.
After
echo inversed > /sys/class/pwm/pwmchip0/pwm3/polarity
the output changes to
# cat /sys/kernel/debug/pwm 0: platform/40001000.timer:pwm, 4 PWM devices ... pwm-3 (sysfs ): requested enabled period: 313720 ns duty: 10000 ns polarity: inverse
and pulseview/sigrok claims 96.812749% with a period of 313.8 µs. So the polarity change happend as expected.
This is on an st,stm32mp135f-dk board.
Where is the difference to your observations?
Thanks for taking a look! I'm using the PWM for a backlight. With this [0] in my dts:
[0]: backlight: backlight { compatible = "pwm-backlight"; pwms = <&pwm4 0 125000 PWM_POLARITY_INVERTED>; brightness-levels = <102 235 255>; default-brightness-level = <80>; num-interpolated-steps = <100>; enable-gpios = <&gpiof 12 GPIO_ACTIVE_LOW>; status = "okay"; };
Maybe that is doing something differently.
/Sean
linux-stable-mirror@lists.linaro.org