Commit 602a0672 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
parents 92474049 65c6f742
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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
+37 −34
Original line number Diff line number Diff line
@@ -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,43 +196,21 @@ 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);

out:
	pwm_mediatek_clk_disable(chip, pwm);
	pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv);
	pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period - 1);

	return ret;
	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);
	}

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;
out:
	pwm_mediatek_clk_disable(chip, pwm);

	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,
@@ -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;
}