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

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Nylon Chen and committed by
Uwe Kleine-König
6df3aac7 7dbc4432

+9 -6
+9 -6
drivers/pwm/pwm-sifive.c
··· 118 118 119 119 /* As scale <= 15 the shift operation cannot overflow. */ 120 120 num = (unsigned long long)NSEC_PER_SEC << (PWM_SIFIVE_CMPWIDTH + scale); 121 - ddata->real_period = div64_ul(num, rate); 121 + ddata->real_period = DIV_ROUND_UP_ULL(num, rate); 122 122 dev_dbg(ddata->parent, 123 123 "New real_period = %u ns\n", ddata->real_period); 124 124 } ··· 143 143 state->enabled = false; 144 144 145 145 state->period = ddata->real_period; 146 - state->duty_cycle = 147 - (u64)duty * ddata->real_period >> PWM_SIFIVE_CMPWIDTH; 146 + state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty * ddata->real_period, 147 + (1U << PWM_SIFIVE_CMPWIDTH)); 148 148 state->polarity = PWM_POLARITY_NORMAL; 149 149 150 150 return 0; ··· 159 159 unsigned long long num; 160 160 bool enabled; 161 161 int ret = 0; 162 - u32 frac, inactive; 162 + u64 frac; 163 + u32 inactive; 163 164 164 165 if (state->polarity != PWM_POLARITY_NORMAL) 165 166 return -EINVAL; ··· 179 178 * consecutively 180 179 */ 181 180 num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH); 182 - frac = DIV64_U64_ROUND_CLOSEST(num, state->period); 181 + frac = num; 182 + do_div(frac, state->period); 183 183 /* The hardware cannot generate a 0% duty cycle */ 184 - frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1); 184 + frac = min(frac, (u64)(1U << PWM_SIFIVE_CMPWIDTH) - 1); 185 + /* pwmcmp register must be loaded with the inactive(invert the duty) */ 185 186 inactive = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac; 186 187 187 188 mutex_lock(&ddata->lock);