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 | |
| 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>
| -rw-r--r-- | drivers/pwm/pwm-stm32.c | 63 | ||||
| -rw-r--r-- | include/linux/mfd/stm32-timers.h | 1 |
2 files changed, 62 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: |
diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h index d46f5500928e..9da1d7ece079 100644 --- a/include/linux/mfd/stm32-timers.h +++ b/include/linux/mfd/stm32-timers.h | |||
| @@ -82,6 +82,7 @@ | |||
| 82 | #define TIM_DCR_DBL GENMASK(12, 8) /* DMA burst len */ | 82 | #define TIM_DCR_DBL GENMASK(12, 8) /* DMA burst len */ |
| 83 | 83 | ||
| 84 | #define MAX_TIM_PSC 0xFFFF | 84 | #define MAX_TIM_PSC 0xFFFF |
| 85 | #define MAX_TIM_ICPSC 0x3 | ||
| 85 | #define TIM_CR2_MMS_SHIFT 4 | 86 | #define TIM_CR2_MMS_SHIFT 4 |
| 86 | #define TIM_CR2_MMS2_SHIFT 20 | 87 | #define TIM_CR2_MMS2_SHIFT 20 |
| 87 | #define TIM_SMCR_TS_SHIFT 4 | 88 | #define TIM_SMCR_TS_SHIFT 4 |
