Skip to content

Commit 602a067

Browse files
committed
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
2 parents 9247404 + 65c6f74 commit 602a067

File tree

2 files changed

+46
-34
lines changed

2 files changed

+46
-34
lines changed

drivers/pwm/pwm-imx-tpm.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,15 @@ static int pwm_imx_tpm_apply_hw(struct pwm_chip *chip,
204204
val |= FIELD_PREP(PWM_IMX_TPM_SC_PS, p->prescale);
205205
writel(val, tpm->base + PWM_IMX_TPM_SC);
206206

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);
207216
/*
208217
* set period count:
209218
* if the PWM is disabled (CMOD[1:0] = 2b00), then MOD register

drivers/pwm/pwm-mediatek.c

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,26 @@ static inline void pwm_mediatek_writel(struct pwm_mediatek_chip *chip,
121121
writel(value, chip->regs + chip->soc->reg_offset[num] + offset);
122122
}
123123

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+
124144
static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
125145
int duty_ns, int period_ns)
126146
{
@@ -150,7 +170,10 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
150170
do_div(resolution, clk_rate);
151171

152172
cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution);
153-
while (cnt_period > 8191) {
173+
if (!cnt_period)
174+
return -EINVAL;
175+
176+
while (cnt_period > 8192) {
154177
resolution *= 2;
155178
clkdiv++;
156179
cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000,
@@ -173,45 +196,23 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
173196
}
174197

175198
cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution);
199+
176200
pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv);
177-
pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period);
178-
pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty);
201+
pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period - 1);
202+
203+
if (cnt_duty) {
204+
pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty - 1);
205+
pwm_mediatek_enable(chip, pwm);
206+
} else {
207+
pwm_mediatek_disable(chip, pwm);
208+
}
179209

180210
out:
181211
pwm_mediatek_clk_disable(chip, pwm);
182212

183213
return ret;
184214
}
185215

186-
static int pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm)
187-
{
188-
struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
189-
u32 value;
190-
int ret;
191-
192-
ret = pwm_mediatek_clk_enable(chip, pwm);
193-
if (ret < 0)
194-
return ret;
195-
196-
value = readl(pc->regs);
197-
value |= BIT(pwm->hwpwm);
198-
writel(value, pc->regs);
199-
200-
return 0;
201-
}
202-
203-
static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm)
204-
{
205-
struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
206-
u32 value;
207-
208-
value = readl(pc->regs);
209-
value &= ~BIT(pwm->hwpwm);
210-
writel(value, pc->regs);
211-
212-
pwm_mediatek_clk_disable(chip, pwm);
213-
}
214-
215216
static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
216217
const struct pwm_state *state)
217218
{
@@ -221,8 +222,10 @@ static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
221222
return -EINVAL;
222223

223224
if (!state->enabled) {
224-
if (pwm->state.enabled)
225+
if (pwm->state.enabled) {
225226
pwm_mediatek_disable(chip, pwm);
227+
pwm_mediatek_clk_disable(chip, pwm);
228+
}
226229

227230
return 0;
228231
}
@@ -232,7 +235,7 @@ static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
232235
return err;
233236

234237
if (!pwm->state.enabled)
235-
err = pwm_mediatek_enable(chip, pwm);
238+
err = pwm_mediatek_clk_enable(chip, pwm);
236239

237240
return err;
238241
}

0 commit comments

Comments
 (0)