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

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

Pull pwm fixes from Uwe Kleine-König:
"Two fixes for the mediatek and the imx-tpm driver. Both are old
(v4.12-rc1 and v5.2-rc1 respectively).

The mediatek issue is that both period and duty_cycle were configured
to higher values than requested. For most applications the period part
is no tragedy, but a PWM that is configured for duty_cycle = 0 should
really emit a constant inactive signal. That was noticed by an LED not
being completely off in this case (two commits for one fix: a
preparatory one and the actual fix in the second one).

For the imx-tpm PWM driver the fixed issue is that the first period is
quite a bit too long under some circumstances. So it might take up to
UINT32_MAX << 7 clock ticks until the PWM starts toggling. With an
assumed input clock rate of 166 MHz (completely made up) that's 55
minutes"

* tag 'pwm/for-6.17-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux:
pwm: imx-tpm: Reset counter if CMOD is 0
pwm: mediatek: Fix duty and period setting
pwm: mediatek: Handle hardware enable and clock enable separately

+46 -34
+9
drivers/pwm/pwm-imx-tpm.c
··· 205 205 writel(val, tpm->base + PWM_IMX_TPM_SC); 206 206 207 207 /* 208 + * if the counter is disabled (CMOD == 0), programming the new 209 + * period length (MOD) will not reset the counter (CNT). If 210 + * CNT.COUNT happens to be bigger than the new MOD value then 211 + * the counter will end up being reset way too late. Therefore, 212 + * manually reset it to 0. 213 + */ 214 + if (!cmod) 215 + writel(0x0, tpm->base + PWM_IMX_TPM_CNT); 216 + /* 208 217 * set period count: 209 218 * if the PWM is disabled (CMOD[1:0] = 2b00), then MOD register 210 219 * is updated when MOD register is written.
+37 -34
drivers/pwm/pwm-mediatek.c
··· 121 121 writel(value, chip->regs + chip->soc->reg_offset[num] + offset); 122 122 } 123 123 124 + static void pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm) 125 + { 126 + struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 127 + u32 value; 128 + 129 + value = readl(pc->regs); 130 + value |= BIT(pwm->hwpwm); 131 + writel(value, pc->regs); 132 + } 133 + 134 + static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm) 135 + { 136 + struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 137 + u32 value; 138 + 139 + value = readl(pc->regs); 140 + value &= ~BIT(pwm->hwpwm); 141 + writel(value, pc->regs); 142 + } 143 + 124 144 static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, 125 145 int duty_ns, int period_ns) 126 146 { ··· 170 150 do_div(resolution, clk_rate); 171 151 172 152 cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution); 173 - while (cnt_period > 8191) { 153 + if (!cnt_period) 154 + return -EINVAL; 155 + 156 + while (cnt_period > 8192) { 174 157 resolution *= 2; 175 158 clkdiv++; 176 159 cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, ··· 196 173 } 197 174 198 175 cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution); 176 + 199 177 pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); 200 - pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period); 201 - pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty); 178 + pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period - 1); 179 + 180 + if (cnt_duty) { 181 + pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty - 1); 182 + pwm_mediatek_enable(chip, pwm); 183 + } else { 184 + pwm_mediatek_disable(chip, pwm); 185 + } 202 186 203 187 out: 204 188 pwm_mediatek_clk_disable(chip, pwm); 205 189 206 190 return ret; 207 - } 208 - 209 - static int pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm) 210 - { 211 - struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 212 - u32 value; 213 - int ret; 214 - 215 - ret = pwm_mediatek_clk_enable(chip, pwm); 216 - if (ret < 0) 217 - return ret; 218 - 219 - value = readl(pc->regs); 220 - value |= BIT(pwm->hwpwm); 221 - writel(value, pc->regs); 222 - 223 - return 0; 224 - } 225 - 226 - static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm) 227 - { 228 - struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 229 - u32 value; 230 - 231 - value = readl(pc->regs); 232 - value &= ~BIT(pwm->hwpwm); 233 - writel(value, pc->regs); 234 - 235 - pwm_mediatek_clk_disable(chip, pwm); 236 191 } 237 192 238 193 static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm, ··· 222 221 return -EINVAL; 223 222 224 223 if (!state->enabled) { 225 - if (pwm->state.enabled) 224 + if (pwm->state.enabled) { 226 225 pwm_mediatek_disable(chip, pwm); 226 + pwm_mediatek_clk_disable(chip, pwm); 227 + } 227 228 228 229 return 0; 229 230 } ··· 235 232 return err; 236 233 237 234 if (!pwm->state.enabled) 238 - err = pwm_mediatek_enable(chip, pwm); 235 + err = pwm_mediatek_clk_enable(chip, pwm); 239 236 240 237 return err; 241 238 }