summaryrefslogtreecommitdiff
path: root/drivers/pwm/pwm-stm32.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pwm/pwm-stm32.c')
-rw-r--r--drivers/pwm/pwm-stm32.c60
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);