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
This commit is contained in:
Linus Torvalds
2025-07-31 13:27:00 -07:00
2 changed files with 46 additions and 34 deletions

View File

@@ -204,6 +204,15 @@ static int pwm_imx_tpm_apply_hw(struct pwm_chip *chip,
val |= FIELD_PREP(PWM_IMX_TPM_SC_PS, p->prescale);
writel(val, tpm->base + PWM_IMX_TPM_SC);
/*
* if the counter is disabled (CMOD == 0), programming the new
* period length (MOD) will not reset the counter (CNT). If
* CNT.COUNT happens to be bigger than the new MOD value then
* the counter will end up being reset way too late. Therefore,
* manually reset it to 0.
*/
if (!cmod)
writel(0x0, tpm->base + PWM_IMX_TPM_CNT);
/*
* set period count:
* if the PWM is disabled (CMOD[1:0] = 2b00), then MOD register

View File

@@ -121,6 +121,26 @@ static inline void pwm_mediatek_writel(struct pwm_mediatek_chip *chip,
writel(value, chip->regs + chip->soc->reg_offset[num] + offset);
}
static void pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
u32 value;
value = readl(pc->regs);
value |= BIT(pwm->hwpwm);
writel(value, pc->regs);
}
static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
u32 value;
value = readl(pc->regs);
value &= ~BIT(pwm->hwpwm);
writel(value, pc->regs);
}
static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
@@ -150,7 +170,10 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
do_div(resolution, clk_rate);
cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution);
while (cnt_period > 8191) {
if (!cnt_period)
return -EINVAL;
while (cnt_period > 8192) {
resolution *= 2;
clkdiv++;
cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000,
@@ -173,9 +196,16 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
}
cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution);
pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv);
pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period);
pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty);
pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period - 1);
if (cnt_duty) {
pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty - 1);
pwm_mediatek_enable(chip, pwm);
} else {
pwm_mediatek_disable(chip, pwm);
}
out:
pwm_mediatek_clk_disable(chip, pwm);
@@ -183,35 +213,6 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
return ret;
}
static int pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
u32 value;
int ret;
ret = pwm_mediatek_clk_enable(chip, pwm);
if (ret < 0)
return ret;
value = readl(pc->regs);
value |= BIT(pwm->hwpwm);
writel(value, pc->regs);
return 0;
}
static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
u32 value;
value = readl(pc->regs);
value &= ~BIT(pwm->hwpwm);
writel(value, pc->regs);
pwm_mediatek_clk_disable(chip, pwm);
}
static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
@@ -221,8 +222,10 @@ static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
return -EINVAL;
if (!state->enabled) {
if (pwm->state.enabled)
if (pwm->state.enabled) {
pwm_mediatek_disable(chip, pwm);
pwm_mediatek_clk_disable(chip, pwm);
}
return 0;
}
@@ -232,7 +235,7 @@ static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
return err;
if (!pwm->state.enabled)
err = pwm_mediatek_enable(chip, pwm);
err = pwm_mediatek_clk_enable(chip, pwm);
return err;
}