Re: [PATCH] pwm: ipq: fix period calculation
kernel test robot <[email protected]> Sat, 1 Aug 2026 03:29:20 +0800
| Newsgroups | org.kernel.vger.linux-pwm,dev.linux.lists.llvm,dev.linux.lists.oe-kbuild-all,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Hi Stephane, kernel test robot noticed the following build warnings: [auto build test WARNING on linus/master] [also build test WARNING on v7.2-rc5 next-20260731] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Stephane-Lepain/pwm-ipq-fix-period-calculation/20260731-152907 base: linus/master patch link: https://lore.kernel.org/r/20260731070542.155398-1-stephanelepain%40gmail.com patch subject: [PATCH] pwm: ipq: fix period calculation config: arm-randconfig-004-20260731 (https://download.01.org/0day-ci/archive/20260801/[email protected]/config) compiler: clang version 24.0.0git (https://github.com/llvm/llvm-project bacfe2950f8218268fcc0a8765644ea0c15f0360) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260801/[email protected]/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <[email protected]> | Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/ All warnings (new ones prefixed by >>): >> drivers/pwm/pwm-ipq.c:123:25: warning: result of comparison of constant 16000000000 with expression of type 'unsigned long' is always false [-Wtautological-constant-out-of-range-compare] 123 | if (ipq_chip->clk_rate > 16ULL * GIGA) | ~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~ 1 warning generated. vim +123 drivers/pwm/pwm-ipq.c 87 88 static int ipq_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 89 const struct pwm_state *state) 90 { 91 struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip); 92 unsigned int pre_div, pwm_div, best_pre_div, best_pwm_div; 93 u64 period_ns, duty_ns, period_rate, min_diff; 94 unsigned long val = 0; 95 u64 hi_dur; 96 97 if (!state->enabled) { 98 /* clear IPQ_PWM_REG1_ENABLE */ 99 ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, IPQ_PWM_REG1_UPDATE); 100 return 0; 101 } 102 103 if (state->polarity != PWM_POLARITY_NORMAL) 104 return -EINVAL; 105 106 /* 107 * Check the upper and lower bounds for the period as per 108 * hardware limits 109 */ 110 if (state->period < IPQ_PWM_MIN_PERIOD_NS) 111 return -ERANGE; 112 period_ns = min(state->period, IPQ_PWM_MAX_PERIOD_NS); 113 duty_ns = min(state->duty_cycle, period_ns); 114 115 /* 116 * The period spans (pre_div + 1) * (pwm_div + 1) input clocks. Rather 117 * than fixing pwm_div at its maximum (which gives usable duty 118 * resolution only for long periods and collapses to ~0% for short 119 * periods) search for the (pre_div, pwm_div) split whose period best 120 * approximates the request while leaving pwm_div large enough to 121 * resolve the duty cycle. 122 */ > 123 if (ipq_chip->clk_rate > 16ULL * GIGA) 124 return -EINVAL; 125 period_rate = period_ns * ipq_chip->clk_rate; 126 127 best_pre_div = IPQ_PWM_MAX_DIV; 128 best_pwm_div = IPQ_PWM_MAX_DIV; 129 min_diff = period_rate; 130 131 /* 132 * Smaller pre_div than this cannot represent the period (pwm_div would 133 * have to exceed its field), so start the search there. 134 */ 135 pre_div = div64_u64(period_rate, 136 (u64)NSEC_PER_SEC * (IPQ_PWM_MAX_DIV + 1)); 137 138 for (; pre_div <= IPQ_PWM_MAX_DIV; pre_div++) { 139 u64 remainder; 140 141 pwm_div = div64_u64_rem(period_rate, 142 (u64)NSEC_PER_SEC * (pre_div + 1), 143 &remainder); 144 /* pwm_div is unsigned; the swap check below catches underflow */ 145 pwm_div--; 146 147 /* 148 * Swapping pre_div and pwm_div yields the same period but a 149 * larger pwm_div gives finer duty resolution, so once pre_div 150 * exceeds pwm_div every further candidate is strictly worse. 151 */ 152 if (pre_div > pwm_div) 153 break; 154 155 /* need room for 100% duty, where hi_dur == pwm_div + 1 */ 156 if (pwm_div > IPQ_PWM_MAX_DIV - 1) 157 continue; 158 159 if (remainder < min_diff) { 160 best_pre_div = pre_div; 161 best_pwm_div = pwm_div; 162 min_diff = remainder; 163 164 if (min_diff == 0) 165 break; 166 } 167 } 168 169 pre_div = best_pre_div; 170 pwm_div = best_pwm_div; 171 172 /* 173 * If the search found no usable candidate, best_pwm_div is left at 174 * IPQ_PWM_MAX_DIV; cap it so pwm_div + 1 still fits the 16-bit field 175 * and 100% duty remains expressible. 176 */ 177 if (pwm_div > IPQ_PWM_MAX_DIV - 1) 178 pwm_div = IPQ_PWM_MAX_DIV - 1; 179 180 /* 181 * high duration = duty_ratio * (pwm_div + 1) 182 * = duty_ns * clk_rate / ((pre_div + 1) * NSEC_PER_SEC) 183 * 184 * Round to nearest to avoid biasing every duty cycle low, then clamp 185 * to (pwm_div + 1): rounding or a 100% duty request can otherwise push 186 * hi_dur past the period length and overflow the 16-bit HI_DURATION field 187 * (which would alias a full-on request down to a near-zero high time) 188 * and asking the hardware to stay high beyond one period. pwm_div is 189 * at most IPQ_PWM_MAX_DIV - 1, so pwm_div + 1 always fits the field. 190 */ 191 hi_dur = DIV64_U64_ROUND_CLOSEST(duty_ns * ipq_chip->clk_rate, 192 (u64)(pre_div + 1) * NSEC_PER_SEC); 193 if (hi_dur > (u64)pwm_div + 1) 194 hi_dur = (u64)pwm_div + 1; 195 196 val = FIELD_PREP(IPQ_PWM_REG0_HI_DURATION, hi_dur) | 197 FIELD_PREP(IPQ_PWM_REG0_PWM_DIV, pwm_div); 198 ipq_pwm_reg_write(pwm, IPQ_PWM_REG0, val); 199 200 val = FIELD_PREP(IPQ_PWM_REG1_PRE_DIV, pre_div); 201 ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val); 202 203 /* PWM enable toggle needs a separate write to REG1 */ 204 val |= IPQ_PWM_REG1_UPDATE | IPQ_PWM_REG1_ENABLE; 205 ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val); 206 207 return 0; 208 } 209 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki