diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-23 08:37:07 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-23 08:37:07 -0700 |
| commit | 8841842cc9be68e0f670323e697c9b3214831d6a (patch) | |
| tree | e94bc221c74de403a0e8a707d8a5ba7fb81f3104 | |
| parent | df7bf834a135e1f5bcbeecbec07605fa5d92f0bd (diff) | |
| parent | 68637b68afcc3cb4d56aca14a3a1d1b47b879369 (diff) | |
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
| -rw-r--r-- | drivers/pwm/pwm-atmel-tcb.c | 38 | ||||
| -rw-r--r-- | drivers/pwm/pwm-stm32.c | 22 |
2 files changed, 46 insertions, 14 deletions
diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index f9ff78ba122d..3d30aeab507e 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -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); diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 2594fb771b04..935257a890b0 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -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, |
