Commit a3597d6c authored by Thierry Reding's avatar Thierry Reding
Browse files

pwm: imx27: Cache duty cycle register value



The hardware register containing the duty cycle value cannot be accessed
when the PWM is disabled. This causes the ->get_state() callback to read
back a duty cycle value of 0, which can confuse consumer drivers.

Tested-by: default avatarMichal Vokáč <michal.vokac@ysoft.com>
Tested-by: default avatarAdam Ford <aford173@gmail.com>
Signed-off-by: default avatarThierry Reding <thierry.reding@gmail.com>
parent 1db37f95
Loading
Loading
Loading
Loading
+24 −7
Original line number Diff line number Diff line
@@ -85,6 +85,13 @@ struct pwm_imx27_chip {
	struct clk	*clk_per;
	void __iomem	*mmio_base;
	struct pwm_chip	chip;

	/*
	 * The driver cannot read the current duty cycle from the hardware if
	 * the hardware is disabled. Cache the last programmed duty cycle
	 * value to return in that case.
	 */
	unsigned int duty_cycle;
};

#define to_pwm_imx27_chip(chip)	container_of(chip, struct pwm_imx27_chip, chip)
@@ -155,14 +162,17 @@ static void pwm_imx27_get_state(struct pwm_chip *chip,
	tmp = NSEC_PER_SEC * (u64)(period + 2);
	state->period = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);

	/* PWMSAR can be read only if PWM is enabled */
	if (state->enabled) {
	/*
	 * PWMSAR can be read only if PWM is enabled. If the PWM is disabled,
	 * use the cached value.
	 */
	if (state->enabled)
		val = readl(imx->mmio_base + MX3_PWMSAR);
	else
		val = imx->duty_cycle;

	tmp = NSEC_PER_SEC * (u64)(val);
	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);
	} else {
		state->duty_cycle = 0;
	}

	if (!state->enabled)
		pwm_imx27_clk_disable_unprepare(chip);
@@ -261,6 +271,13 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
		writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
		writel(period_cycles, imx->mmio_base + MX3_PWMPR);

		/*
		 * Store the duty cycle for future reference in cases where
		 * the MX3_PWMSAR register can't be read (i.e. when the PWM
		 * is disabled).
		 */
		imx->duty_cycle = duty_cycles;

		cr = MX3_PWMCR_PRESCALER_SET(prescale) |
		     MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEN | MX3_PWMCR_WAITEN |
		     FIELD_PREP(MX3_PWMCR_CLKSRC, MX3_PWMCR_CLKSRC_IPG_HIGH) |