diff options
author | Uwe Kleine-König <u.kleine-koenig@pengutronix.de> | 2024-03-17 22:52:15 +0100 |
---|---|---|
committer | Uwe Kleine-König <u.kleine-koenig@pengutronix.de> | 2024-04-30 18:57:08 +0200 |
commit | d44d635635a7192c773a75e674a8590a163e879e (patch) | |
tree | a6eb087428fc566755a92aad16d0451c425d4aca /drivers/pwm/pwm-stm32.c | |
parent | pwm: stm32: Improve precision of calculation in .apply() (diff) | |
download | linux-d44d635635a7192c773a75e674a8590a163e879e.tar.xz linux-d44d635635a7192c773a75e674a8590a163e879e.zip |
pwm: stm32: Fix for settings using period > UINT32_MAX
stm32_pwm_config() took the duty_cycle and period values with the type
int, however stm32_pwm_apply() passed u64 values there. Expand the
function parameters to u64 to not discard relevant bits and adapt the
calculations to the wider type.
To ensure the calculations won't overflow, check in .probe() the input
clk doesn't run faster than 1 GHz.
Link: https://lore.kernel.org/r/06b4a650a608d0887d934c1b2b8919e0f78e4db2.1710711976.git.u.kleine-koenig@pengutronix.de
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Diffstat (limited to 'drivers/pwm/pwm-stm32.c')
-rw-r--r-- | drivers/pwm/pwm-stm32.c | 28 |
1 files changed, 20 insertions, 8 deletions
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index fb714936ff8f..ea3d7d9117f2 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -309,16 +309,18 @@ unlock: } static int stm32_pwm_config(struct stm32_pwm *priv, unsigned int ch, - int duty_ns, int period_ns) + u64 duty_ns, u64 period_ns) { unsigned long long prd, div, dty; unsigned int prescaler = 0; u32 ccmr, mask, shift; - /* Period and prescaler values depends on clock rate */ - div = (unsigned long long)clk_get_rate(priv->clk) * period_ns; - - do_div(div, NSEC_PER_SEC); + /* + * .probe() asserted that clk_get_rate() is not bigger than 1 GHz, so + * this won't overflow. + */ + div = mul_u64_u64_div_u64(period_ns, clk_get_rate(priv->clk), + NSEC_PER_SEC); prd = div; while (div > priv->max_arr) { @@ -351,9 +353,8 @@ static int stm32_pwm_config(struct stm32_pwm *priv, unsigned int ch, regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE); /* Calculate the duty cycles */ - dty = (unsigned long long)clk_get_rate(priv->clk) * duty_ns; - do_div(dty, prescaler + 1); - do_div(dty, NSEC_PER_SEC); + dty = mul_u64_u64_div_u64(duty_ns, clk_get_rate(priv->clk), + (u64)NSEC_PER_SEC * (prescaler + 1)); regmap_write(priv->regmap, TIM_CCR1 + 4 * ch, dty); @@ -659,6 +660,17 @@ static int stm32_pwm_probe(struct platform_device *pdev) stm32_pwm_detect_complementary(priv); + ret = devm_clk_rate_exclusive_get(dev, priv->clk); + if (ret) + return dev_err_probe(dev, ret, "Failed to lock clock\n"); + + /* + * With the clk running with not more than 1 GHz the calculations in + * .apply() won't overflow. + */ + if (clk_get_rate(priv->clk) > 1000000000) + return dev_err_probe(dev, -EINVAL, "Failed to lock clock\n"); + chip->ops = &stm32pwm_ops; /* Initialize clock refcount to number of enabled PWM channels. */ |