mirror of https://github.com/torvalds/linux.git
pwm: sifive: Fix rounding and idempotency issues in apply and get_state
This fix ensures consistent rounding and avoids mismatches between applied and reported PWM values that could trigger false idempotency failures in debug checks This change ensures: - real_period is now calculated using DIV_ROUND_UP_ULL() to avoid underestimation. - duty_cycle is rounded up to match the fractional computation in apply() - apply() truncates the result to compensate for get_state's rounding up logic These fixes resolve issues like: .apply is supposed to round down duty_cycle (requested: 360/504000, applied: 361/504124) .apply is not idempotent (ena=1 pol=0 1739692/4032985) -> (ena=1 pol=0 1739630/4032985) Reported-by: kernel test robot <lkp@intel.com> Closes: https://lore.kernel.org/oe-kbuild-all/202505080303.dBfU5YMS-lkp@intel.com/ Co-developed-by: Zong Li <zong.li@sifive.com> Signed-off-by: Zong Li <zong.li@sifive.com> Signed-off-by: Nylon Chen <nylon.chen@sifive.com> Link: https://lore.kernel.org/r/20250529035341.51736-4-nylon.chen@sifive.com Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
This commit is contained in:
parent
7dbc4432ea
commit
6df3aac763
|
|
@ -118,7 +118,7 @@ static void pwm_sifive_update_clock(struct pwm_sifive_ddata *ddata,
|
||||||
|
|
||||||
/* As scale <= 15 the shift operation cannot overflow. */
|
/* As scale <= 15 the shift operation cannot overflow. */
|
||||||
num = (unsigned long long)NSEC_PER_SEC << (PWM_SIFIVE_CMPWIDTH + scale);
|
num = (unsigned long long)NSEC_PER_SEC << (PWM_SIFIVE_CMPWIDTH + scale);
|
||||||
ddata->real_period = div64_ul(num, rate);
|
ddata->real_period = DIV_ROUND_UP_ULL(num, rate);
|
||||||
dev_dbg(ddata->parent,
|
dev_dbg(ddata->parent,
|
||||||
"New real_period = %u ns\n", ddata->real_period);
|
"New real_period = %u ns\n", ddata->real_period);
|
||||||
}
|
}
|
||||||
|
|
@ -143,8 +143,8 @@ static int pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
state->enabled = false;
|
state->enabled = false;
|
||||||
|
|
||||||
state->period = ddata->real_period;
|
state->period = ddata->real_period;
|
||||||
state->duty_cycle =
|
state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty * ddata->real_period,
|
||||||
(u64)duty * ddata->real_period >> PWM_SIFIVE_CMPWIDTH;
|
(1U << PWM_SIFIVE_CMPWIDTH));
|
||||||
state->polarity = PWM_POLARITY_NORMAL;
|
state->polarity = PWM_POLARITY_NORMAL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -159,7 +159,8 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
unsigned long long num;
|
unsigned long long num;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
u32 frac, inactive;
|
u64 frac;
|
||||||
|
u32 inactive;
|
||||||
|
|
||||||
if (state->polarity != PWM_POLARITY_NORMAL)
|
if (state->polarity != PWM_POLARITY_NORMAL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
@ -178,9 +179,11 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
* consecutively
|
* consecutively
|
||||||
*/
|
*/
|
||||||
num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH);
|
num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH);
|
||||||
frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
|
frac = num;
|
||||||
|
do_div(frac, state->period);
|
||||||
/* The hardware cannot generate a 0% duty cycle */
|
/* The hardware cannot generate a 0% duty cycle */
|
||||||
frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
|
frac = min(frac, (u64)(1U << PWM_SIFIVE_CMPWIDTH) - 1);
|
||||||
|
/* pwmcmp register must be loaded with the inactive(invert the duty) */
|
||||||
inactive = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
|
inactive = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
|
||||||
|
|
||||||
mutex_lock(&ddata->lock);
|
mutex_lock(&ddata->lock);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue