Commit a020f22a authored by Paul Cercueil's avatar Paul Cercueil Committed by Thierry Reding

pwm: jz4740: Make PWM start with the active part

The PWM in Ingenic SoCs starts in inactive state until the internal
timer reaches the duty value, then becomes active until the timer
reaches the period value. In theory, we should then use (period - duty)
as the real duty value, as a high duty value would otherwise result in
the PWM pin being inactive most of the time.

This is the reason why the duty value was inverted in the driver until
now, but it still had the problem that it would not start with the
active part.

To address this remaining issue, the common trick is to invert the
duty, and invert the polarity when the PWM is enabled.

Since the duty was already inverted, and we invert it again, we now
program the hardware for the requested duty, and simply invert the
polarity when the PWM is enabled.
Signed-off-by: default avatarPaul Cercueil <paul@crapouillou.net>
Signed-off-by: default avatarThierry Reding <thierry.reding@gmail.com>
parent 9017dc4f
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
* Limitations: * Limitations:
* - The .apply callback doesn't complete the currently running period before * - The .apply callback doesn't complete the currently running period before
* reconfiguring the hardware. * reconfiguring the hardware.
* - Each period starts with the inactive part.
*/ */
#include <linux/clk.h> #include <linux/clk.h>
...@@ -163,7 +162,7 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -163,7 +162,7 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
/* Calculate duty value */ /* Calculate duty value */
tmp = (unsigned long long)rate * state->duty_cycle; tmp = (unsigned long long)rate * state->duty_cycle;
do_div(tmp, NSEC_PER_SEC); do_div(tmp, NSEC_PER_SEC);
duty = period - tmp; duty = tmp;
if (duty >= period) if (duty >= period)
duty = period - 1; duty = period - 1;
...@@ -189,18 +188,26 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -189,18 +188,26 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm), regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD); TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD);
/* Set polarity */ /*
switch (state->polarity) { * Set polarity.
case PWM_POLARITY_NORMAL: *
* The PWM starts in inactive state until the internal timer reaches the
* duty value, then becomes active until the timer reaches the period
* value. In theory, we should then use (period - duty) as the real duty
* value, as a high duty value would otherwise result in the PWM pin
* being inactive most of the time.
*
* Here, we don't do that, and instead invert the polarity of the PWM
* when it is active. This trick makes the PWM start with its active
* state instead of its inactive state.
*/
if ((state->polarity == PWM_POLARITY_NORMAL) ^ state->enabled)
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm), regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
TCU_TCSR_PWM_INITL_HIGH, 0); TCU_TCSR_PWM_INITL_HIGH, 0);
break; else
case PWM_POLARITY_INVERSED:
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm), regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
TCU_TCSR_PWM_INITL_HIGH, TCU_TCSR_PWM_INITL_HIGH,
TCU_TCSR_PWM_INITL_HIGH); TCU_TCSR_PWM_INITL_HIGH);
break;
}
if (state->enabled) if (state->enabled)
jz4740_pwm_enable(chip, pwm); jz4740_pwm_enable(chip, pwm);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment