Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

pwm: stm32: Fix calculation of prescaler

A small prescaler is beneficial, as this improves the resolution of the
duty_cycle configuration. However if the prescaler is too small, the
maximal possible period becomes considerably smaller than the requested
value.

One situation where this goes wrong is the following: With a parent
clock rate of 208877930 Hz and max_arr = 0xffff = 65535, a request for
period = 941243 ns currently results in PSC = 1. The value for ARR is
then calculated to

ARR = 941243 * 208877930 / (1000000000 * 2) - 1 = 98301

This value is bigger than 65535 however and so doesn't fit into the
respective register field. In this particular case the PWM was
configured for a period of 313733.4806027616 ns (with ARR = 98301 &
0xffff). Even if ARR was configured to its maximal value, only period =
627495.6861167669 ns would be achievable.

Fix the calculation accordingly and adapt the comment to match the new
algorithm.

With the calculation fixed the above case results in PSC = 2 and so an
actual period of 941229.1667195285 ns.

Fixes: 8002fbeef1e4 ("pwm: stm32: Calculate prescaler with a division instead of a loop")
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Link: https://lore.kernel.org/r/b4d96b79917617434a540df45f20cb5de4142f88.1718979150.git.u.kleine-koenig@baylibre.com
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>

authored by

Uwe Kleine-König and committed by
Uwe Kleine-König
dab8f9f0 c45fcf46

+12 -6
+12 -6
drivers/pwm/pwm-stm32.c
··· 321 321 * First we need to find the minimal value for prescaler such that 322 322 * 323 323 * period_ns * clkrate 324 - * ------------------------------ 324 + * ------------------------------ < max_arr + 1 325 325 * NSEC_PER_SEC * (prescaler + 1) 326 326 * 327 - * isn't bigger than max_arr. 327 + * This equation is equivalent to 328 + * 329 + * period_ns * clkrate 330 + * ---------------------------- < prescaler + 1 331 + * NSEC_PER_SEC * (max_arr + 1) 332 + * 333 + * Using integer division and knowing that the right hand side is 334 + * integer, this is further equivalent to 335 + * 336 + * (period_ns * clkrate) // (NSEC_PER_SEC * (max_arr + 1)) ≤ prescaler 328 337 */ 329 338 330 339 prescaler = mul_u64_u64_div_u64(period_ns, clk_get_rate(priv->clk), 331 - (u64)NSEC_PER_SEC * priv->max_arr); 332 - if (prescaler > 0) 333 - prescaler -= 1; 334 - 340 + (u64)NSEC_PER_SEC * ((u64)priv->max_arr + 1)); 335 341 if (prescaler > MAX_TIM_PSC) 336 342 return -EINVAL; 337 343