pwm: Two driver fixes

After having added some more code to libpwm checking the pwm rounding
 rules for the userspace interface I spotted an issue in the pwm-stm32
 driver where in some cases involving inverted polarity the wrong
 hardware settings for the duty offset are chosen. I think it has little
 practical effect because the duty offset is in most cases an artificial
 property of the output waveform. Still it's relevant to get this fixed
 because this driver serves as a reference implementation for the still
 young waveform API.
 
 The second fix addresses a sleep-in-atomic issue in the pwm-atmel-tcb
 driver.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEP4GsaTp6HlmJrf7Tj4D7WH0S/k4FAmnpynoACgkQj4D7WH0S
 /k4NnwgAuxWsy1gj6zvU14lg/5ejZih5S1RmvHDrFLQJWyV8kXGhbLoWnZZZ1FLT
 Oo2V5dwRcJEPty6Yw/E8SV5KVcnGwQd3UrmuqQTPHwwLaff08KrZB4DoLtfLXAiN
 QyVmz1n/kaid3VD4EbUYFvv5zCqMcrk4nZGanEm1o1VLn739AhsI/lpfAcRAoHaT
 ySHUqo2l8sN1vtAY9Av/8DBP8r8RzQmdxVHL0hG/rD2Rw4tIoVPYwU2eyD+s7DwB
 Vou/ssqwibofoQoJfbESNbFdJJYEQZmsiHPDTIGZCAJoJOpTMbHxaBJZqWIzs4jv
 I5CAgi9FQq1N3SeSl2E8yoWbsPkb6g==
 =W/Xm
 -----END PGP SIGNATURE-----

Merge tag 'pwm/fixes-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux

Pull pwm fixes from Uwe Kleine-König:
 "Two driver fixes

  After having added some more code to libpwm checking the pwm rounding
  rules for the userspace interface I spotted an issue in the pwm-stm32
  driver where in some cases involving inverted polarity the wrong
  hardware settings for the duty offset are chosen. I think it has
  little practical effect because the duty offset is in most cases an
  artificial property of the output waveform. Still it's relevant to get
  this fixed because this driver serves as a reference implementation
  for the still young waveform API.

  The second fix addresses a sleep-in-atomic issue in the pwm-atmel-tcb
  driver"

* tag 'pwm/fixes-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux:
  pwm: atmel-tcb: Cache clock rates and mark chip as atomic
  pwm: stm32: Fix rounding issue for requests with inverted polarity
master
Linus Torvalds 2026-04-23 08:37:07 -07:00
commit 8841842cc9
2 changed files with 46 additions and 14 deletions

View File

@ -50,6 +50,8 @@ struct atmel_tcb_pwm_chip {
spinlock_t lock;
u8 channel;
u8 width;
unsigned long rate;
unsigned long slow_rate;
struct regmap *regmap;
struct clk *clk;
struct clk *gclk;
@ -266,7 +268,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int slowclk = 0;
unsigned period;
unsigned duty;
unsigned rate = clk_get_rate(tcbpwmc->clk);
unsigned long rate = tcbpwmc->rate;
unsigned long long min;
unsigned long long max;
@ -294,7 +296,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
*/
if (i == ARRAY_SIZE(atmel_tcb_divisors)) {
i = slowclk;
rate = clk_get_rate(tcbpwmc->slow_clk);
rate = tcbpwmc->slow_rate;
min = div_u64(NSEC_PER_SEC, rate);
max = min << tcbpwmc->width;
@ -431,24 +433,49 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)
}
chip->ops = &atmel_tcb_pwm_ops;
chip->atomic = true;
tcbpwmc->channel = channel;
tcbpwmc->width = config->counter_width;
err = clk_prepare_enable(tcbpwmc->slow_clk);
err = clk_prepare_enable(tcbpwmc->clk);
if (err)
goto err_gclk;
err = clk_prepare_enable(tcbpwmc->slow_clk);
if (err)
goto err_disable_clk;;
err = clk_rate_exclusive_get(tcbpwmc->clk);
if (err)
goto err_disable_slow_clk;
err = clk_rate_exclusive_get(tcbpwmc->slow_clk);
if (err)
goto err_clk_unlock;
tcbpwmc->rate = clk_get_rate(tcbpwmc->clk);
tcbpwmc->slow_rate = clk_get_rate(tcbpwmc->slow_clk);
spin_lock_init(&tcbpwmc->lock);
err = pwmchip_add(chip);
if (err < 0)
goto err_disable_clk;
goto err_slow_clk_unlock;
platform_set_drvdata(pdev, chip);
return 0;
err_slow_clk_unlock:
clk_rate_exclusive_put(tcbpwmc->slow_clk);
err_clk_unlock:
clk_rate_exclusive_put(tcbpwmc->clk);
err_disable_clk:
clk_disable_unprepare(tcbpwmc->clk);
err_disable_slow_clk:
clk_disable_unprepare(tcbpwmc->slow_clk);
err_gclk:
@ -470,6 +497,9 @@ static void atmel_tcb_pwm_remove(struct platform_device *pdev)
pwmchip_remove(chip);
clk_rate_exclusive_put(tcbpwmc->slow_clk);
clk_rate_exclusive_put(tcbpwmc->clk);
clk_disable_unprepare(tcbpwmc->clk);
clk_disable_unprepare(tcbpwmc->slow_clk);
clk_put(tcbpwmc->gclk);
clk_put(tcbpwmc->clk);

View File

@ -68,7 +68,7 @@ static int stm32_pwm_round_waveform_tohw(struct pwm_chip *chip,
struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
unsigned int ch = pwm->hwpwm;
unsigned long rate;
u64 ccr, duty;
u64 duty_ticks, offset_ticks;
int ret;
if (wf->period_length_ns == 0) {
@ -164,23 +164,25 @@ static int stm32_pwm_round_waveform_tohw(struct pwm_chip *chip,
wfhw->arr = min_t(u64, arr, priv->max_arr) - 1;
}
duty = mul_u64_u64_div_u64(wf->duty_length_ns, rate,
(u64)NSEC_PER_SEC * (wfhw->psc + 1));
duty = min_t(u64, duty, wfhw->arr + 1);
duty_ticks = mul_u64_u64_div_u64(wf->duty_length_ns, rate,
(u64)NSEC_PER_SEC * (wfhw->psc + 1));
duty_ticks = min_t(u64, duty_ticks, wfhw->arr + 1);
if (wf->duty_length_ns && wf->duty_offset_ns &&
wf->duty_length_ns + wf->duty_offset_ns >= wf->period_length_ns) {
offset_ticks = mul_u64_u64_div_u64(wf->duty_offset_ns, rate,
(u64)NSEC_PER_SEC * (wfhw->psc + 1));
offset_ticks = min_t(u64, offset_ticks, wfhw->arr + 1);
if (duty_ticks && offset_ticks &&
duty_ticks + offset_ticks >= wfhw->arr + 1) {
wfhw->ccer |= TIM_CCER_CCxP(ch + 1);
if (priv->have_complementary_output)
wfhw->ccer |= TIM_CCER_CCxNP(ch + 1);
ccr = wfhw->arr + 1 - duty;
wfhw->ccr = wfhw->arr + 1 - duty_ticks;
} else {
ccr = duty;
wfhw->ccr = duty_ticks;
}
wfhw->ccr = min_t(u64, ccr, wfhw->arr + 1);
out:
dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] @%lu -> CCER: %08x, PSC: %08x, ARR: %08x, CCR: %08x\n",
pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,