Merge tag 'pwm/for-6.15-rc2-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux

Pull pwm fixes from Uwe Kleine-König:
"A set of fixes for pwm core and various drivers

The first three patches handle clk_get_rate() returning 0 (which might
happen for example if the CCF is disabled). The first of these was
found because this triggered a warning with clang, the two others by
looking for similar issues in other drivers.

The remaining three fixes address issues in the new waveform pwm API.
Now that I worked on this a bit more, the finer details and corner
cases are better understood and the code is fixed accordingly"

* tag 'pwm/for-6.15-rc2-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux:
pwm: axi-pwmgen: Let .round_waveform_tohw() signal when request was rounded up
pwm: stm32: Search an appropriate duty_cycle if period cannot be modified
pwm: Let pwm_set_waveform() succeed even if lowlevel driver rounded up
pwm: fsl-ftm: Handle clk_get_rate() returning 0
pwm: rcar: Improve register calculation
pwm: mediatek: Prevent divide-by-zero in pwm_mediatek_config()

+7 -6
drivers/pwm/core.c
··· 322 322 const struct pwm_ops *ops = chip->ops; 323 323 char wfhw[WFHWSIZE]; 324 324 struct pwm_waveform wf_rounded; 325 - int err; 325 + int err, ret_tohw; 326 326 327 327 BUG_ON(WFHWSIZE < ops->sizeof_wfhw); 328 328 ··· 332 332 if (!pwm_wf_valid(wf)) 333 333 return -EINVAL; 334 334 335 - err = __pwm_round_waveform_tohw(chip, pwm, wf, &wfhw); 336 - if (err) 337 - return err; 335 + ret_tohw = __pwm_round_waveform_tohw(chip, pwm, wf, &wfhw); 336 + if (ret_tohw < 0) 337 + return ret_tohw; 338 338 339 339 if ((IS_ENABLED(CONFIG_PWM_DEBUG) || exact) && wf->period_length_ns) { 340 340 err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_rounded); 341 341 if (err) 342 342 return err; 343 343 344 - if (IS_ENABLED(CONFIG_PWM_DEBUG) && !pwm_check_rounding(wf, &wf_rounded)) 344 + if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_tohw == 0 && !pwm_check_rounding(wf, &wf_rounded)) 345 345 dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n", 346 346 wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, 347 347 wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns); ··· 382 382 wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns, 383 383 wf_set.duty_length_ns, wf_set.period_length_ns, wf_set.duty_offset_ns); 384 384 } 385 - return 0; 385 + 386 + return ret_tohw; 386 387 } 387 388 388 389 /**
+7 -3
drivers/pwm/pwm-axi-pwmgen.c
··· 75 75 { 76 76 struct axi_pwmgen_waveform *wfhw = _wfhw; 77 77 struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip); 78 + int ret = 0; 78 79 79 80 if (wf->period_length_ns == 0) { 80 81 *wfhw = (struct axi_pwmgen_waveform){ ··· 92 91 if (wfhw->period_cnt == 0) { 93 92 /* 94 93 * The specified period is too short for the hardware. 95 - * Let's round .duty_cycle down to 0 to get a (somewhat) 96 - * valid result. 94 + * So round up .period_cnt to 1 (i.e. the smallest 95 + * possible period). With .duty_cycle and .duty_offset 96 + * being less than or equal to .period, their rounded 97 + * value must be 0. 97 98 */ 98 99 wfhw->period_cnt = 1; 99 100 wfhw->duty_cycle_cnt = 0; 100 101 wfhw->duty_offset_cnt = 0; 102 + ret = 1; 101 103 } else { 102 104 wfhw->duty_cycle_cnt = min_t(u64, 103 105 mul_u64_u32_div(wf->duty_length_ns, ddata->clk_rate_hz, NSEC_PER_SEC), ··· 115 111 pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, 116 112 ddata->clk_rate_hz, wfhw->period_cnt, wfhw->duty_cycle_cnt, wfhw->duty_offset_cnt); 117 113 118 - return 0; 114 + return ret; 119 115 } 120 116 121 117 static int axi_pwmgen_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm,
+6
drivers/pwm/pwm-fsl-ftm.c
··· 118 118 unsigned long long exval; 119 119 120 120 rate = clk_get_rate(fpc->clk[fpc->period.clk_select]); 121 + if (rate >> fpc->period.clk_ps == 0) 122 + return 0; 123 + 121 124 exval = ticks; 122 125 exval *= 1000000000UL; 123 126 do_div(exval, rate >> fpc->period.clk_ps); ··· 192 189 193 190 unsigned int period = fpc->period.mod_period + 1; 194 191 unsigned int period_ns = fsl_pwm_ticks_to_ns(fpc, period); 192 + 193 + if (!period_ns) 194 + return 0; 195 195 196 196 duty = (unsigned long long)duty_ns * period; 197 197 do_div(duty, period_ns);
+6 -2
drivers/pwm/pwm-mediatek.c
··· 121 121 struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 122 122 u32 clkdiv = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH, 123 123 reg_thres = PWMTHRES; 124 + unsigned long clk_rate; 124 125 u64 resolution; 125 126 int ret; 126 127 127 128 ret = pwm_mediatek_clk_enable(chip, pwm); 128 - 129 129 if (ret < 0) 130 130 return ret; 131 + 132 + clk_rate = clk_get_rate(pc->clk_pwms[pwm->hwpwm]); 133 + if (!clk_rate) 134 + return -EINVAL; 131 135 132 136 /* Make sure we use the bus clock and not the 26MHz clock */ 133 137 if (pc->soc->has_ck_26m_sel) ··· 139 135 140 136 /* Using resolution in picosecond gets accuracy higher */ 141 137 resolution = (u64)NSEC_PER_SEC * 1000; 142 - do_div(resolution, clk_get_rate(pc->clk_pwms[pwm->hwpwm])); 138 + do_div(resolution, clk_rate); 143 139 144 140 cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution); 145 141 while (cnt_period > 8191) {
+13 -11
drivers/pwm/pwm-rcar.c
··· 8 8 * - The hardware cannot generate a 0% duty cycle. 9 9 */ 10 10 11 + #include <linux/bitfield.h> 11 12 #include <linux/clk.h> 12 13 #include <linux/err.h> 13 14 #include <linux/io.h> ··· 103 102 rcar_pwm_write(rp, value, RCAR_PWMCR); 104 103 } 105 104 106 - static int rcar_pwm_set_counter(struct rcar_pwm_chip *rp, int div, int duty_ns, 107 - int period_ns) 105 + static int rcar_pwm_set_counter(struct rcar_pwm_chip *rp, int div, u64 duty_ns, 106 + u64 period_ns) 108 107 { 109 - unsigned long long one_cycle, tmp; /* 0.01 nanoseconds */ 108 + unsigned long long tmp; 110 109 unsigned long clk_rate = clk_get_rate(rp->clk); 111 110 u32 cyc, ph; 112 111 113 - one_cycle = NSEC_PER_SEC * 100ULL << div; 114 - do_div(one_cycle, clk_rate); 112 + /* div <= 24 == RCAR_PWM_MAX_DIVISION, so the shift doesn't overflow. */ 113 + tmp = mul_u64_u64_div_u64(period_ns, clk_rate, (u64)NSEC_PER_SEC << div); 114 + if (tmp > FIELD_MAX(RCAR_PWMCNT_CYC0_MASK)) 115 + tmp = FIELD_MAX(RCAR_PWMCNT_CYC0_MASK); 115 116 116 - tmp = period_ns * 100ULL; 117 - do_div(tmp, one_cycle); 118 - cyc = (tmp << RCAR_PWMCNT_CYC0_SHIFT) & RCAR_PWMCNT_CYC0_MASK; 117 + cyc = FIELD_PREP(RCAR_PWMCNT_CYC0_MASK, tmp); 119 118 120 - tmp = duty_ns * 100ULL; 121 - do_div(tmp, one_cycle); 122 - ph = tmp & RCAR_PWMCNT_PH0_MASK; 119 + tmp = mul_u64_u64_div_u64(duty_ns, clk_rate, (u64)NSEC_PER_SEC << div); 120 + if (tmp > FIELD_MAX(RCAR_PWMCNT_PH0_MASK)) 121 + tmp = FIELD_MAX(RCAR_PWMCNT_PH0_MASK); 122 + ph = FIELD_PREP(RCAR_PWMCNT_PH0_MASK, tmp); 123 123 124 124 /* Avoid prohibited setting */ 125 125 if (cyc == 0 || ph == 0)
+3 -9
drivers/pwm/pwm-stm32.c
··· 103 103 if (ret) 104 104 goto out; 105 105 106 - /* 107 - * calculate the best value for ARR for the given PSC, refuse if 108 - * the resulting period gets bigger than the requested one. 109 - */ 110 106 arr = mul_u64_u64_div_u64(wf->period_length_ns, rate, 111 107 (u64)NSEC_PER_SEC * (wfhw->psc + 1)); 112 108 if (arr <= wfhw->arr) { 113 109 /* 114 - * requested period is small than the currently 110 + * requested period is smaller than the currently 115 111 * configured and unchangable period, report back the smallest 116 - * possible period, i.e. the current state; Initialize 117 - * ccr to anything valid. 112 + * possible period, i.e. the current state and return 1 113 + * to indicate the wrong rounding direction. 118 114 */ 119 - wfhw->ccr = 0; 120 115 ret = 1; 121 - goto out; 122 116 } 123 117 124 118 } else {