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
This commit is contained in:
Linus Torvalds
2026-04-23 08:37:07 -07:00
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,