Skip to content

Commit

Permalink
pwm: ep93xx: Ensure configuring period and duty_cycle isn't wrongly s…
Browse files Browse the repository at this point in the history
…kipped

As the last call to ep93xx_pwm_apply() might have exited early if
state->enabled was false, the values for period and duty_cycle stored in
pwm->state might not have been written to hardware and it must be
ensured that they are configured before enabling the PWM.

Fixes: 6d45374 ("pwm: ep93xx: Implement .apply callback")
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
  • Loading branch information
Uwe Kleine-König authored and Thierry Reding committed Jul 8, 2021
1 parent 7d6d4aa commit f4a8e31
Showing 1 changed file with 40 additions and 45 deletions.
85 changes: 40 additions & 45 deletions drivers/pwm/pwm-ep93xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ static int ep93xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
int ret;
struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip);
bool enabled = state->enabled;
void __iomem *base = ep93xx_pwm->base;
unsigned long long c;
unsigned long period_cycles;
unsigned long duty_cycles;
unsigned long term;

if (state->polarity != pwm->state.polarity) {
if (enabled) {
Expand Down Expand Up @@ -97,57 +102,47 @@ static int ep93xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
return 0;
}

if (state->period != pwm->state.period ||
state->duty_cycle != pwm->state.duty_cycle) {
struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip);
void __iomem *base = ep93xx_pwm->base;
unsigned long long c;
unsigned long period_cycles;
unsigned long duty_cycles;
unsigned long term;
/*
* The clock needs to be enabled to access the PWM registers.
* Configuration can be changed at any time.
*/
if (!pwm_is_enabled(pwm)) {
ret = clk_prepare_enable(ep93xx_pwm->clk);
if (ret)
return ret;
}

/*
* The clock needs to be enabled to access the PWM registers.
* Configuration can be changed at any time.
*/
if (!pwm_is_enabled(pwm)) {
ret = clk_prepare_enable(ep93xx_pwm->clk);
if (ret)
return ret;
}
c = clk_get_rate(ep93xx_pwm->clk);
c *= state->period;
do_div(c, 1000000000);
period_cycles = c;

c = period_cycles;
c *= state->duty_cycle;
do_div(c, state->period);
duty_cycles = c;

c = clk_get_rate(ep93xx_pwm->clk);
c *= state->period;
do_div(c, 1000000000);
period_cycles = c;

c = period_cycles;
c *= state->duty_cycle;
do_div(c, state->period);
duty_cycles = c;

if (period_cycles < 0x10000 && duty_cycles < 0x10000) {
term = readw(base + EP93XX_PWMx_TERM_COUNT);

/* Order is important if PWM is running */
if (period_cycles > term) {
writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT);
writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE);
} else {
writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE);
writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT);
}
ret = 0;
if (period_cycles < 0x10000 && duty_cycles < 0x10000) {
term = readw(base + EP93XX_PWMx_TERM_COUNT);

/* Order is important if PWM is running */
if (period_cycles > term) {
writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT);
writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE);
} else {
ret = -EINVAL;
writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE);
writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT);
}
ret = 0;
} else {
ret = -EINVAL;
}

if (!pwm_is_enabled(pwm))
clk_disable_unprepare(ep93xx_pwm->clk);
if (!pwm_is_enabled(pwm))
clk_disable_unprepare(ep93xx_pwm->clk);

if (ret)
return ret;
}
if (ret)
return ret;

if (!enabled) {
ret = clk_prepare_enable(ep93xx_pwm->clk);
Expand Down

0 comments on commit f4a8e31

Please sign in to comment.