diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-05-14 15:03:19 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-05-14 15:03:19 -0700 |
commit | 9d81e2d5a9e4befa119e40742a60c366e15d76ce (patch) | |
tree | f16cc19e854074d8a9844129e24396031b317906 /drivers/pwm/pwm-stm32.c | |
parent | 00fddaf58854717a075f3690c828b61290701e7e (diff) | |
parent | 4817118f257e49b043f3d80f021a327b7e1d796f (diff) |
Merge tag 'pwm/for-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux
Pull pwm updates from Uwe Kleine-König:
"Apart for the normal updates for dt bindings, cleanups and support for
new device variants to existing drivers this completes the conversion
to pwmchip_alloc() which was started in the v6.9 development cycle.
Using pwmchip_alloc() is a precondition to the character device
support which allows easier and faster access to PWM devices. However
there are some issues I want to clean up before including it in
mainline, so this isn't contained here despite it was in next for some
time.
Thanks to Alexandre Mergnat, Binbin Zhou, Dmitry Rokosov, George
Stark, Jerome Brunet and Varshini Rajendran for their contributions.
Further thanks go to AngeloGioacchino Del Regno, Conor Dooley, David
Lechner, Fabrice Gasnier, Florian Fainelli, Guenter Roeck, Gustavo A.
R. Silva, Krzysztof Kozlowski and Rob Herring for valuable patch
review"
* tag 'pwm/for-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux: (34 commits)
pwm: pca9685: Drop explicit initialization of struct i2c_device_id::driver_data to 0
dt-bindings: pwm: snps,dw-apb-timers: Do not require pwm-cells twice
dt-bindings: pwm: mediatek,pwm-disp: Do not require pwm-cells twice
dt-bindings: pwm: mediatek,mt2712: Do not require pwm-cells twice
dt-bindings: pwm: marvell,pxa: Do not require pwm-cells twice
dt-bindings: pwm: google,cros-ec: Do not require pwm-cells twice
dt-bindings: pwm: bcm2835: Do not require pwm-cells twice
pwm: meson: Use mul_u64_u64_div_u64() for frequency calculating
pwm: meson: Add check for error from clk_round_rate()
pwm: meson: Drop unneeded check in .get_state()
dt-bindings: pwm: mediatek,pwm-disp: add compatible for mt8365 SoC
pwm: meson: Add generic compatible for meson8 to sm1
pwm: bcm2835: Drop open coded variant of devm_clk_rate_exclusive_get()
pwm: bcm2835: Introduce a local variable for &pdev->dev
pwm: stm32: Calculate prescaler with a division instead of a loop
pwm: stm32: Fix for settings using period > UINT32_MAX
pwm: stm32: Improve precision of calculation in .apply()
pwm: stm32: Add error messages in .probe()'s error paths
pwm: Make pwmchip_[sg]et_drvdata() a wrapper around dev_set_drvdata()
pwm: Don't check pointer for being non-NULL after use
...
Diffstat (limited to 'drivers/pwm/pwm-stm32.c')
-rw-r--r-- | drivers/pwm/pwm-stm32.c | 60 |
1 files changed, 40 insertions, 20 deletions
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 0c028d17c075..a2f231d13a9f 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -309,29 +309,35 @@ 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; + unsigned long long prd, dty; + unsigned long long prescaler; 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); - prd = div; - - while (div > priv->max_arr) { - prescaler++; - div = prd; - do_div(div, prescaler + 1); - } + /* + * .probe() asserted that clk_get_rate() is not bigger than 1 GHz, so + * the calculations here won't overflow. + * First we need to find the minimal value for prescaler such that + * + * period_ns * clkrate + * ------------------------------ + * NSEC_PER_SEC * (prescaler + 1) + * + * isn't bigger than max_arr. + */ - prd = div; + prescaler = mul_u64_u64_div_u64(period_ns, clk_get_rate(priv->clk), + (u64)NSEC_PER_SEC * priv->max_arr); + if (prescaler > 0) + prescaler -= 1; if (prescaler > MAX_TIM_PSC) return -EINVAL; + prd = mul_u64_u64_div_u64(period_ns, clk_get_rate(priv->clk), + (u64)NSEC_PER_SEC * (prescaler + 1)); + /* * All channels share the same prescaler and counter so when two * channels are active at the same time we can't change them @@ -351,8 +357,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 = prd * duty_ns; - do_div(dty, period_ns); + 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); @@ -648,14 +654,27 @@ static int stm32_pwm_probe(struct platform_device *pdev) priv->max_arr = ddata->max_arr; if (!priv->regmap || !priv->clk) - return -EINVAL; + return dev_err_probe(dev, -EINVAL, "Failed to get %s\n", + priv->regmap ? "clk" : "regmap"); ret = stm32_pwm_probe_breakinputs(priv, np); if (ret) - return ret; + return dev_err_probe(dev, ret, + "Failed to configure breakinputs\n"); 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. */ @@ -664,7 +683,8 @@ static int stm32_pwm_probe(struct platform_device *pdev) ret = devm_pwmchip_add(dev, chip); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, + "Failed to register pwmchip\n"); platform_set_drvdata(pdev, chip); |