Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

pwm: tiehrpwm: Fix various off-by-one errors in duty-cycle calculation

In Up-Count Mode the timer is reset to zero one tick after it reaches
TBPRD, so the period length is (TBPRD + 1) * T_TBCLK. This matches both
the documentation and measurements. So the value written to the TBPRD has
to be one less than the calculated period_cycles value.

A complication here is that for a 100% relative duty-cycle the value
written to the CMPx register has to be TBPRD + 1 which might overflow if
TBPRD is 0xffff. To handle that the calculation of the AQCTLx register
has to be moved to ehrpwm_pwm_config() and the edge at CTR = CMPx has to
be skipped.

Additionally the AQCTL_PRD register field has to be 0 because that defines
the hardware's action when the maximal counter value is reached, which is
(as above) one clock tick before the period's end. The period start edge
has to happen when the counter is reset and so is defined in the AQCTL_ZRO
field.

Fixes: 19891b20e7c2 ("pwm: pwm-tiehrpwm: PWM driver support for EHRPWM")
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Link: https://lore.kernel.org/r/dc818c69b7cf05109ecda9ee6b0043a22de757c1.1754927682.git.u.kleine-koenig@baylibre.com
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>

authored by

Uwe Kleine-König and committed by
Uwe Kleine-König
bc7ce5bf 878dbfc1

+58 -85
+58 -85
drivers/pwm/pwm-tiehrpwm.c
··· 36 36 37 37 #define CLKDIV_MAX 7 38 38 #define HSPCLKDIV_MAX 7 39 - #define PERIOD_MAX 0xFFFF 39 + #define PERIOD_MAX 0x10000 40 40 41 41 /* compare module registers */ 42 42 #define CMPA 0x12 ··· 65 65 #define AQCTL_ZRO_FRCHIGH BIT(1) 66 66 #define AQCTL_ZRO_FRCTOGGLE (BIT(1) | BIT(0)) 67 67 68 - #define AQCTL_CHANA_POLNORMAL (AQCTL_CAU_FRCLOW | AQCTL_PRD_FRCHIGH | \ 69 - AQCTL_ZRO_FRCHIGH) 70 - #define AQCTL_CHANA_POLINVERSED (AQCTL_CAU_FRCHIGH | AQCTL_PRD_FRCLOW | \ 71 - AQCTL_ZRO_FRCLOW) 72 - #define AQCTL_CHANB_POLNORMAL (AQCTL_CBU_FRCLOW | AQCTL_PRD_FRCHIGH | \ 73 - AQCTL_ZRO_FRCHIGH) 74 - #define AQCTL_CHANB_POLINVERSED (AQCTL_CBU_FRCHIGH | AQCTL_PRD_FRCLOW | \ 75 - AQCTL_ZRO_FRCLOW) 68 + #define AQCTL_CHANA_POLNORMAL (AQCTL_CAU_FRCLOW | AQCTL_ZRO_FRCHIGH) 69 + #define AQCTL_CHANA_POLINVERSED (AQCTL_CAU_FRCHIGH | AQCTL_ZRO_FRCLOW) 70 + #define AQCTL_CHANB_POLNORMAL (AQCTL_CBU_FRCLOW | AQCTL_ZRO_FRCHIGH) 71 + #define AQCTL_CHANB_POLINVERSED (AQCTL_CBU_FRCHIGH | AQCTL_ZRO_FRCLOW) 76 72 77 73 #define AQSFRC_RLDCSF_MASK (BIT(7) | BIT(6)) 78 74 #define AQSFRC_RLDCSF_ZRO 0 ··· 104 108 unsigned long clk_rate; 105 109 void __iomem *mmio_base; 106 110 unsigned long period_cycles[NUM_PWM_CHANNEL]; 107 - enum pwm_polarity polarity[NUM_PWM_CHANNEL]; 108 111 struct clk *tbclk; 109 112 struct ehrpwm_context ctx; 110 113 }; ··· 172 177 return 1; 173 178 } 174 179 175 - static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan) 176 - { 177 - u16 aqctl_val, aqctl_mask; 178 - unsigned int aqctl_reg; 179 - 180 - /* 181 - * Configure PWM output to HIGH/LOW level on counter 182 - * reaches compare register value and LOW/HIGH level 183 - * on counter value reaches period register value and 184 - * zero value on counter 185 - */ 186 - if (chan == 1) { 187 - aqctl_reg = AQCTLB; 188 - aqctl_mask = AQCTL_CBU_MASK; 189 - 190 - if (pc->polarity[chan] == PWM_POLARITY_INVERSED) 191 - aqctl_val = AQCTL_CHANB_POLINVERSED; 192 - else 193 - aqctl_val = AQCTL_CHANB_POLNORMAL; 194 - } else { 195 - aqctl_reg = AQCTLA; 196 - aqctl_mask = AQCTL_CAU_MASK; 197 - 198 - if (pc->polarity[chan] == PWM_POLARITY_INVERSED) 199 - aqctl_val = AQCTL_CHANA_POLINVERSED; 200 - else 201 - aqctl_val = AQCTL_CHANA_POLNORMAL; 202 - } 203 - 204 - aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK; 205 - ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val); 206 - } 207 - 208 180 /* 209 181 * period_ns = 10^9 * (ps_divval * period_cycles) / PWM_CLK_RATE 210 182 * duty_ns = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE 211 183 */ 212 184 static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 213 - u64 duty_ns, u64 period_ns) 185 + u64 duty_ns, u64 period_ns, enum pwm_polarity polarity) 214 186 { 215 187 struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); 216 188 u32 period_cycles, duty_cycles; 217 189 u16 ps_divval, tb_divval; 218 190 unsigned int i, cmp_reg; 219 191 unsigned long long c; 192 + u16 aqctl_val, aqctl_mask; 193 + unsigned int aqctl_reg; 220 194 221 195 if (period_ns > NSEC_PER_SEC) 222 196 return -ERANGE; ··· 195 231 do_div(c, NSEC_PER_SEC); 196 232 period_cycles = (unsigned long)c; 197 233 198 - if (period_cycles < 1) { 199 - period_cycles = 1; 200 - duty_cycles = 1; 201 - } else { 202 - c = pc->clk_rate; 203 - c = c * duty_ns; 204 - do_div(c, NSEC_PER_SEC); 205 - duty_cycles = (unsigned long)c; 206 - } 234 + c = pc->clk_rate; 235 + c = c * duty_ns; 236 + do_div(c, NSEC_PER_SEC); 237 + duty_cycles = (unsigned long)c; 207 238 208 239 /* 209 240 * Period values should be same for multiple PWM channels as IP uses ··· 230 271 return -EINVAL; 231 272 } 232 273 274 + /* Update period & duty cycle with presacler division */ 275 + period_cycles = period_cycles / ps_divval; 276 + duty_cycles = duty_cycles / ps_divval; 277 + 278 + if (period_cycles < 1) 279 + period_cycles = 1; 280 + 233 281 pm_runtime_get_sync(pwmchip_parent(chip)); 234 282 235 283 /* Update clock prescaler values */ 236 284 ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CLKDIV_MASK, tb_divval); 237 285 238 - /* Update period & duty cycle with presacler division */ 239 - period_cycles = period_cycles / ps_divval; 240 - duty_cycles = duty_cycles / ps_divval; 286 + if (pwm->hwpwm == 1) { 287 + /* Channel 1 configured with compare B register */ 288 + cmp_reg = CMPB; 289 + 290 + aqctl_reg = AQCTLB; 291 + aqctl_mask = AQCTL_CBU_MASK; 292 + 293 + if (polarity == PWM_POLARITY_INVERSED) 294 + aqctl_val = AQCTL_CHANB_POLINVERSED; 295 + else 296 + aqctl_val = AQCTL_CHANB_POLNORMAL; 297 + 298 + /* if duty_cycle is big, don't toggle on CBU */ 299 + if (duty_cycles > period_cycles) 300 + aqctl_val &= ~AQCTL_CBU_MASK; 301 + 302 + } else { 303 + /* Channel 0 configured with compare A register */ 304 + cmp_reg = CMPA; 305 + 306 + aqctl_reg = AQCTLA; 307 + aqctl_mask = AQCTL_CAU_MASK; 308 + 309 + if (polarity == PWM_POLARITY_INVERSED) 310 + aqctl_val = AQCTL_CHANA_POLINVERSED; 311 + else 312 + aqctl_val = AQCTL_CHANA_POLNORMAL; 313 + 314 + /* if duty_cycle is big, don't toggle on CAU */ 315 + if (duty_cycles > period_cycles) 316 + aqctl_val &= ~AQCTL_CAU_MASK; 317 + } 318 + 319 + aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK; 320 + ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val); 241 321 242 322 /* Configure shadow loading on Period register */ 243 323 ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_PRDLD_MASK, TBCTL_PRDLD_SHDW); 244 324 245 - ehrpwm_write(pc->mmio_base, TBPRD, period_cycles); 325 + ehrpwm_write(pc->mmio_base, TBPRD, period_cycles - 1); 246 326 247 327 /* Configure ehrpwm counter for up-count mode */ 248 328 ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK, 249 329 TBCTL_CTRMODE_UP); 250 330 251 - if (pwm->hwpwm == 1) 252 - /* Channel 1 configured with compare B register */ 253 - cmp_reg = CMPB; 254 - else 255 - /* Channel 0 configured with compare A register */ 256 - cmp_reg = CMPA; 257 - 258 - ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); 331 + if (!(duty_cycles > period_cycles)) 332 + ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); 259 333 260 334 pm_runtime_put_sync(pwmchip_parent(chip)); 261 - 262 - return 0; 263 - } 264 - 265 - static int ehrpwm_pwm_set_polarity(struct pwm_chip *chip, 266 - struct pwm_device *pwm, 267 - enum pwm_polarity polarity) 268 - { 269 - struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); 270 - 271 - /* Configuration of polarity in hardware delayed, do at enable */ 272 - pc->polarity[pwm->hwpwm] = polarity; 273 335 274 336 return 0; 275 337 } ··· 318 338 AQSFRC_RLDCSF_ZRO); 319 339 320 340 ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); 321 - 322 - /* Channels polarity can be configured from action qualifier module */ 323 - configure_polarity(pc, pwm->hwpwm); 324 341 325 342 /* Enable TBCLK */ 326 343 ret = clk_enable(pc->tbclk); ··· 383 406 ehrpwm_pwm_disable(chip, pwm); 384 407 enabled = false; 385 408 } 386 - 387 - err = ehrpwm_pwm_set_polarity(chip, pwm, state->polarity); 388 - if (err) 389 - return err; 390 409 } 391 410 392 411 if (!state->enabled) { ··· 391 418 return 0; 392 419 } 393 420 394 - err = ehrpwm_pwm_config(chip, pwm, state->duty_cycle, state->period); 421 + err = ehrpwm_pwm_config(chip, pwm, state->duty_cycle, state->period, state->polarity); 395 422 if (err) 396 423 return err; 397 424