aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pwm/pwm-stm32.c
diff options
context:
space:
mode:
authorFabrice Gasnier <fabrice.gasnier@st.com>2018-05-16 03:36:00 -0400
committerLee Jones <lee.jones@linaro.org>2018-05-16 04:11:19 -0400
commitab3a897847834bf3e864fb07b733c444895a24ba (patch)
treeb451c46ce5f7250e455fd71a3598267b8a7f5514 /drivers/pwm/pwm-stm32.c
parentd66ffb91c374fc500fe666645184e278774bad38 (diff)
pwm: stm32: Use input prescaler to improve period capture
Using input prescaler, capture unit will trigger DMA once every configurable /2, /4 or /8 events (rising edge). This helps improve period (only) capture accuracy at high rates. Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com> Reviewed-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> Acked-by: Thierry Reding <thierry.reding@gmail.com> Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/pwm/pwm-stm32.c')
-rw-r--r--drivers/pwm/pwm-stm32.c63
1 files changed, 61 insertions, 2 deletions
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c
index 9a50acde1e61..60bfc07c4912 100644
--- a/drivers/pwm/pwm-stm32.c
+++ b/drivers/pwm/pwm-stm32.c
@@ -8,6 +8,7 @@
8 * pwm-atmel.c from Bo Shen 8 * pwm-atmel.c from Bo Shen
9 */ 9 */
10 10
11#include <linux/bitfield.h>
11#include <linux/mfd/stm32-timers.h> 12#include <linux/mfd/stm32-timers.h>
12#include <linux/module.h> 13#include <linux/module.h>
13#include <linux/of.h> 14#include <linux/of.h>
@@ -168,7 +169,7 @@ static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
168 struct stm32_pwm *priv = to_stm32_pwm_dev(chip); 169 struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
169 unsigned long long prd, div, dty; 170 unsigned long long prd, div, dty;
170 unsigned long rate; 171 unsigned long rate;
171 unsigned int psc = 0, scale; 172 unsigned int psc = 0, icpsc, scale;
172 u32 raw_prd, raw_dty; 173 u32 raw_prd, raw_dty;
173 int ret = 0; 174 int ret = 0;
174 175
@@ -222,6 +223,7 @@ static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
222 /* 223 /*
223 * Got a capture. Try to improve accuracy at high rates: 224 * Got a capture. Try to improve accuracy at high rates:
224 * - decrease counter clock prescaler, scale up to max rate. 225 * - decrease counter clock prescaler, scale up to max rate.
226 * - use input prescaler, capture once every /2 /4 or /8 edges.
225 */ 227 */
226 if (raw_prd) { 228 if (raw_prd) {
227 u32 max_arr = priv->max_arr - 0x1000; /* arbitrary margin */ 229 u32 max_arr = priv->max_arr - 0x1000; /* arbitrary margin */
@@ -241,8 +243,65 @@ static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
241 goto stop; 243 goto stop;
242 } 244 }
243 245
246 /* Compute intermediate period not to exceed timeout at low rates */
244 prd = (unsigned long long)raw_prd * (psc + 1) * NSEC_PER_SEC; 247 prd = (unsigned long long)raw_prd * (psc + 1) * NSEC_PER_SEC;
245 result->period = DIV_ROUND_UP_ULL(prd, rate); 248 do_div(prd, rate);
249
250 for (icpsc = 0; icpsc < MAX_TIM_ICPSC ; icpsc++) {
251 /* input prescaler: also keep arbitrary margin */
252 if (raw_prd >= (priv->max_arr - 0x1000) >> (icpsc + 1))
253 break;
254 if (prd >= (tmo_ms * NSEC_PER_MSEC) >> (icpsc + 2))
255 break;
256 }
257
258 if (!icpsc)
259 goto done;
260
261 /* Last chance to improve period accuracy, using input prescaler */
262 regmap_update_bits(priv->regmap,
263 pwm->hwpwm < 2 ? TIM_CCMR1 : TIM_CCMR2,
264 TIM_CCMR_IC1PSC | TIM_CCMR_IC2PSC,
265 FIELD_PREP(TIM_CCMR_IC1PSC, icpsc) |
266 FIELD_PREP(TIM_CCMR_IC2PSC, icpsc));
267
268 ret = stm32_pwm_raw_capture(priv, pwm, tmo_ms, &raw_prd, &raw_dty);
269 if (ret)
270 goto stop;
271
272 if (raw_dty >= (raw_prd >> icpsc)) {
273 /*
274 * We may fall here using input prescaler, when input
275 * capture starts on high side (before falling edge).
276 * Example with icpsc to capture on each 4 events:
277 *
278 * start 1st capture 2nd capture
279 * v v v
280 * ___ _____ _____ _____ _____ ____
281 * TI1..4 |__| |__| |__| |__| |__|
282 * v v . . . . . v v
283 * icpsc1/3: . 0 . 1 . 2 . 3 . 0
284 * icpsc2/4: 0 1 2 3 0
285 * v v v v
286 * CCR1/3 ......t0..............................t2
287 * CCR2/4 ..t1..............................t1'...
288 * . . .
289 * Capture0: .<----------------------------->.
290 * Capture1: .<-------------------------->. .
291 * . . .
292 * Period: .<------> . .
293 * Low side: .<>.
294 *
295 * Result:
296 * - Period = Capture0 / icpsc
297 * - Duty = Period - Low side = Period - (Capture0 - Capture1)
298 */
299 raw_dty = (raw_prd >> icpsc) - (raw_prd - raw_dty);
300 }
301
302done:
303 prd = (unsigned long long)raw_prd * (psc + 1) * NSEC_PER_SEC;
304 result->period = DIV_ROUND_UP_ULL(prd, rate << icpsc);
246 dty = (unsigned long long)raw_dty * (psc + 1) * NSEC_PER_SEC; 305 dty = (unsigned long long)raw_dty * (psc + 1) * NSEC_PER_SEC;
247 result->duty_cycle = DIV_ROUND_UP_ULL(dty, rate); 306 result->duty_cycle = DIV_ROUND_UP_ULL(dty, rate);
248stop: 307stop: