mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-30 07:34:12 +08:00
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>
This commit is contained in:
parent
d66ffb91c3
commit
ab3a897847
@ -8,6 +8,7 @@
|
||||
* pwm-atmel.c from Bo Shen
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/mfd/stm32-timers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
@ -168,7 +169,7 @@ static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
|
||||
unsigned long long prd, div, dty;
|
||||
unsigned long rate;
|
||||
unsigned int psc = 0, scale;
|
||||
unsigned int psc = 0, icpsc, scale;
|
||||
u32 raw_prd, raw_dty;
|
||||
int ret = 0;
|
||||
|
||||
@ -222,6 +223,7 @@ static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
/*
|
||||
* Got a capture. Try to improve accuracy at high rates:
|
||||
* - decrease counter clock prescaler, scale up to max rate.
|
||||
* - use input prescaler, capture once every /2 /4 or /8 edges.
|
||||
*/
|
||||
if (raw_prd) {
|
||||
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,
|
||||
goto stop;
|
||||
}
|
||||
|
||||
/* Compute intermediate period not to exceed timeout at low rates */
|
||||
prd = (unsigned long long)raw_prd * (psc + 1) * NSEC_PER_SEC;
|
||||
result->period = DIV_ROUND_UP_ULL(prd, rate);
|
||||
do_div(prd, rate);
|
||||
|
||||
for (icpsc = 0; icpsc < MAX_TIM_ICPSC ; icpsc++) {
|
||||
/* input prescaler: also keep arbitrary margin */
|
||||
if (raw_prd >= (priv->max_arr - 0x1000) >> (icpsc + 1))
|
||||
break;
|
||||
if (prd >= (tmo_ms * NSEC_PER_MSEC) >> (icpsc + 2))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!icpsc)
|
||||
goto done;
|
||||
|
||||
/* Last chance to improve period accuracy, using input prescaler */
|
||||
regmap_update_bits(priv->regmap,
|
||||
pwm->hwpwm < 2 ? TIM_CCMR1 : TIM_CCMR2,
|
||||
TIM_CCMR_IC1PSC | TIM_CCMR_IC2PSC,
|
||||
FIELD_PREP(TIM_CCMR_IC1PSC, icpsc) |
|
||||
FIELD_PREP(TIM_CCMR_IC2PSC, icpsc));
|
||||
|
||||
ret = stm32_pwm_raw_capture(priv, pwm, tmo_ms, &raw_prd, &raw_dty);
|
||||
if (ret)
|
||||
goto stop;
|
||||
|
||||
if (raw_dty >= (raw_prd >> icpsc)) {
|
||||
/*
|
||||
* We may fall here using input prescaler, when input
|
||||
* capture starts on high side (before falling edge).
|
||||
* Example with icpsc to capture on each 4 events:
|
||||
*
|
||||
* start 1st capture 2nd capture
|
||||
* v v v
|
||||
* ___ _____ _____ _____ _____ ____
|
||||
* TI1..4 |__| |__| |__| |__| |__|
|
||||
* v v . . . . . v v
|
||||
* icpsc1/3: . 0 . 1 . 2 . 3 . 0
|
||||
* icpsc2/4: 0 1 2 3 0
|
||||
* v v v v
|
||||
* CCR1/3 ......t0..............................t2
|
||||
* CCR2/4 ..t1..............................t1'...
|
||||
* . . .
|
||||
* Capture0: .<----------------------------->.
|
||||
* Capture1: .<-------------------------->. .
|
||||
* . . .
|
||||
* Period: .<------> . .
|
||||
* Low side: .<>.
|
||||
*
|
||||
* Result:
|
||||
* - Period = Capture0 / icpsc
|
||||
* - Duty = Period - Low side = Period - (Capture0 - Capture1)
|
||||
*/
|
||||
raw_dty = (raw_prd >> icpsc) - (raw_prd - raw_dty);
|
||||
}
|
||||
|
||||
done:
|
||||
prd = (unsigned long long)raw_prd * (psc + 1) * NSEC_PER_SEC;
|
||||
result->period = DIV_ROUND_UP_ULL(prd, rate << icpsc);
|
||||
dty = (unsigned long long)raw_dty * (psc + 1) * NSEC_PER_SEC;
|
||||
result->duty_cycle = DIV_ROUND_UP_ULL(dty, rate);
|
||||
stop:
|
||||
|
@ -82,6 +82,7 @@
|
||||
#define TIM_DCR_DBL GENMASK(12, 8) /* DMA burst len */
|
||||
|
||||
#define MAX_TIM_PSC 0xFFFF
|
||||
#define MAX_TIM_ICPSC 0x3
|
||||
#define TIM_CR2_MMS_SHIFT 4
|
||||
#define TIM_CR2_MMS2_SHIFT 20
|
||||
#define TIM_SMCR_TS_SHIFT 4
|
||||
|
Loading…
Reference in New Issue
Block a user