diff options
Diffstat (limited to 'drivers/pwm/pwm-imx27.c')
| -rw-r--r-- | drivers/pwm/pwm-imx27.c | 147 | 
1 files changed, 83 insertions, 64 deletions
diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index ae11d8577f18..35a7ac42269c 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -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); -		tmp = NSEC_PER_SEC * (u64)(val); -		state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk); -	} else { -		state->duty_cycle = 0; -	} +	else +		val = imx->duty_cycle; + +	tmp = NSEC_PER_SEC * (u64)(val); +	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);  	if (!state->enabled)  		pwm_imx27_clk_disable_unprepare(chip); @@ -220,63 +230,68 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,  	pwm_get_state(pwm, &cstate); -	if (state->enabled) { -		c = clk_get_rate(imx->clk_per); -		c *= state->period; - -		do_div(c, 1000000000); -		period_cycles = c; - -		prescale = period_cycles / 0x10000 + 1; - -		period_cycles /= prescale; -		c = (unsigned long long)period_cycles * state->duty_cycle; -		do_div(c, state->period); -		duty_cycles = c; - -		/* -		 * according to imx pwm RM, the real period value should be -		 * PERIOD value in PWMPR plus 2. -		 */ -		if (period_cycles > 2) -			period_cycles -= 2; -		else -			period_cycles = 0; - -		/* -		 * Wait for a free FIFO slot if the PWM is already enabled, and -		 * flush the FIFO if the PWM was disabled and is about to be -		 * enabled. -		 */ -		if (cstate.enabled) { -			pwm_imx27_wait_fifo_slot(chip, pwm); -		} else { -			ret = pwm_imx27_clk_prepare_enable(chip); -			if (ret) -				return ret; - -			pwm_imx27_sw_reset(chip); -		} - -		writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); -		writel(period_cycles, imx->mmio_base + MX3_PWMPR); - -		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) | -		     MX3_PWMCR_DBGEN | MX3_PWMCR_EN; - -		if (state->polarity == PWM_POLARITY_INVERSED) -			cr |= FIELD_PREP(MX3_PWMCR_POUTC, -					MX3_PWMCR_POUTC_INVERTED); - -		writel(cr, imx->mmio_base + MX3_PWMCR); -	} else if (cstate.enabled) { -		writel(0, imx->mmio_base + MX3_PWMCR); +	c = clk_get_rate(imx->clk_per); +	c *= state->period; -		pwm_imx27_clk_disable_unprepare(chip); +	do_div(c, 1000000000); +	period_cycles = c; + +	prescale = period_cycles / 0x10000 + 1; + +	period_cycles /= prescale; +	c = (unsigned long long)period_cycles * state->duty_cycle; +	do_div(c, state->period); +	duty_cycles = c; + +	/* +	 * according to imx pwm RM, the real period value should be PERIOD +	 * value in PWMPR plus 2. +	 */ +	if (period_cycles > 2) +		period_cycles -= 2; +	else +		period_cycles = 0; + +	/* +	 * Wait for a free FIFO slot if the PWM is already enabled, and flush +	 * the FIFO if the PWM was disabled and is about to be enabled. +	 */ +	if (cstate.enabled) { +		pwm_imx27_wait_fifo_slot(chip, pwm); +	} else { +		ret = pwm_imx27_clk_prepare_enable(chip); +		if (ret) +			return ret; + +		pwm_imx27_sw_reset(chip);  	} +	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) | +	     MX3_PWMCR_DBGEN; + +	if (state->polarity == PWM_POLARITY_INVERSED) +		cr |= FIELD_PREP(MX3_PWMCR_POUTC, +				MX3_PWMCR_POUTC_INVERTED); + +	if (state->enabled) +		cr |= MX3_PWMCR_EN; + +	writel(cr, imx->mmio_base + MX3_PWMCR); + +	if (!state->enabled && cstate.enabled) +		pwm_imx27_clk_disable_unprepare(chip); +  	return 0;  } @@ -304,9 +319,13 @@ static int pwm_imx27_probe(struct platform_device *pdev)  	imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");  	if (IS_ERR(imx->clk_ipg)) { -		dev_err(&pdev->dev, "getting ipg clock failed with %ld\n", -				PTR_ERR(imx->clk_ipg)); -		return PTR_ERR(imx->clk_ipg); +		int ret = PTR_ERR(imx->clk_ipg); + +		if (ret != -EPROBE_DEFER) +			dev_err(&pdev->dev, +				"getting ipg clock failed with %d\n", +				ret); +		return ret;  	}  	imx->clk_per = devm_clk_get(&pdev->dev, "per");  |