diff options
author | Fabrice Gasnier <fabrice.gasnier@st.com> | 2018-05-16 03:36:00 -0400 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2018-05-16 04:11:19 -0400 |
commit | ab3a897847834bf3e864fb07b733c444895a24ba (patch) | |
tree | b451c46ce5f7250e455fd71a3598267b8a7f5514 /drivers/pwm/pwm-stm32.c | |
parent | d66ffb91c374fc500fe666645184e278774bad38 (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.c | 63 |
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 | |||
302 | done: | ||
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); |
248 | stop: | 307 | stop: |