From 6df3aac763fa995260ab0545c1e54f0c21b2feb8 Mon Sep 17 00:00:00 2001 From: Nylon Chen Date: Thu, 29 May 2025 11:53:41 +0800 Subject: [PATCH] pwm: sifive: Fix rounding and idempotency issues in apply and get_state MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 Closes: https://lore.kernel.org/oe-kbuild-all/202505080303.dBfU5YMS-lkp@intel.com/ Co-developed-by: Zong Li Signed-off-by: Zong Li Signed-off-by: Nylon Chen Link: https://lore.kernel.org/r/20250529035341.51736-4-nylon.chen@sifive.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-sifive.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c index f3694801d3eed..4a07315b07442 100644 --- a/drivers/pwm/pwm-sifive.c +++ b/drivers/pwm/pwm-sifive.c @@ -118,7 +118,7 @@ static void pwm_sifive_update_clock(struct pwm_sifive_ddata *ddata, /* As scale <= 15 the shift operation cannot overflow. */ 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, "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->period = ddata->real_period; - state->duty_cycle = - (u64)duty * ddata->real_period >> PWM_SIFIVE_CMPWIDTH; + state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty * ddata->real_period, + (1U << PWM_SIFIVE_CMPWIDTH)); state->polarity = PWM_POLARITY_NORMAL; return 0; @@ -159,7 +159,8 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long long num; bool enabled; int ret = 0; - u32 frac, inactive; + u64 frac; + u32 inactive; if (state->polarity != PWM_POLARITY_NORMAL) return -EINVAL; @@ -178,9 +179,11 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm, * consecutively */ 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 */ - 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; mutex_lock(&ddata->lock); -- 2.47.2