From 77965c98cffe41994dce3389c4aae80e2072f098 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 1 Jul 2021 09:29:25 +0200 Subject: pwm: Move legacy driver handling into a dedicated function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no change in behaviour, only some code is moved from pwm_apply_state to a separate function. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 130 ++++++++++++++++++++++++++++------------------------- 1 file changed, 70 insertions(+), 60 deletions(-) (limited to 'drivers/pwm/core.c') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index fb04a439462c..c4bbe12cd850 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -522,6 +522,64 @@ static void pwm_apply_state_debug(struct pwm_device *pwm, } } +static int pwm_apply_legacy(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + int err; + + /* + * FIXME: restore the initial state in case of error. + */ + if (state->polarity != pwm->state.polarity) { + if (!chip->ops->set_polarity) + return -EINVAL; + + /* + * Changing the polarity of a running PWM is only allowed when + * the PWM driver implements ->apply(). + */ + if (pwm->state.enabled) { + chip->ops->disable(chip, pwm); + + /* + * Update pwm->state already here in case + * .set_polarity() or another callback depend on that. + */ + pwm->state.enabled = false; + } + + err = chip->ops->set_polarity(chip, pwm, state->polarity); + if (err) + return err; + + pwm->state.polarity = state->polarity; + } + + if (state->period != pwm->state.period || + state->duty_cycle != pwm->state.duty_cycle) { + err = chip->ops->config(pwm->chip, pwm, + state->duty_cycle, + state->period); + if (err) + return err; + + pwm->state.period = state->period; + pwm->state.duty_cycle = state->duty_cycle; + } + + if (state->enabled != pwm->state.enabled) { + if (!pwm->state.enabled) { + err = chip->ops->enable(chip, pwm); + if (err) + return err; + } else { + chip->ops->disable(chip, pwm); + } + } + + return 0; +} + /** * pwm_apply_state() - atomically apply a new state to a PWM device * @pwm: PWM device @@ -554,70 +612,22 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) state->usage_power == pwm->state.usage_power) return 0; - if (chip->ops->apply) { + if (chip->ops->apply) err = chip->ops->apply(chip, pwm, state); - if (err) - return err; - - trace_pwm_apply(pwm, state); - - pwm->state = *state; - - /* - * only do this after pwm->state was applied as some - * implementations of .get_state depend on this - */ - pwm_apply_state_debug(pwm, state); - } else { - /* - * FIXME: restore the initial state in case of error. - */ - if (state->polarity != pwm->state.polarity) { - if (!chip->ops->set_polarity) - return -EINVAL; - - /* - * Changing the polarity of a running PWM is - * only allowed when the PWM driver implements - * ->apply(). - */ - if (pwm->state.enabled) { - chip->ops->disable(chip, pwm); - pwm->state.enabled = false; - } - - err = chip->ops->set_polarity(chip, pwm, - state->polarity); - if (err) - return err; - - pwm->state.polarity = state->polarity; - } - - if (state->period != pwm->state.period || - state->duty_cycle != pwm->state.duty_cycle) { - err = chip->ops->config(pwm->chip, pwm, - state->duty_cycle, - state->period); - if (err) - return err; + else + err = pwm_apply_legacy(chip, pwm, state); + if (err) + return err; - pwm->state.duty_cycle = state->duty_cycle; - pwm->state.period = state->period; - } + trace_pwm_apply(pwm, state); - if (state->enabled != pwm->state.enabled) { - if (state->enabled) { - err = chip->ops->enable(chip, pwm); - if (err) - return err; - } else { - chip->ops->disable(chip, pwm); - } + pwm->state = *state; - pwm->state.enabled = state->enabled; - } - } + /* + * only do this after pwm->state was applied as some + * implementations of .get_state depend on this + */ + pwm_apply_state_debug(pwm, state); return 0; } -- cgit From 92f69e582e15bf281ff1ab3ccc7abdd8392550a3 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 1 Jul 2021 09:29:26 +0200 Subject: pwm: Prevent a glitch for legacy drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a running PWM is reconfigured to disabled calling the ->config() callback before disabling the hardware might result in a glitch where the (maybe) new period and duty_cycle are visible on the output before disabling the hardware. So handle disabling before calling ->config(). Also exit early in this case which is possible because period and duty_cycle don't matter for disabled PWMs. In return however ->config has to be called even if state->period == pwm->state.period && state->duty_cycle != pwm->state.duty_cycle because setting these might have been skipped in the previous call. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) (limited to 'drivers/pwm/core.c') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index c4bbe12cd850..dedf38a81bf9 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -555,26 +555,33 @@ static int pwm_apply_legacy(struct pwm_chip *chip, struct pwm_device *pwm, pwm->state.polarity = state->polarity; } - if (state->period != pwm->state.period || - state->duty_cycle != pwm->state.duty_cycle) { - err = chip->ops->config(pwm->chip, pwm, - state->duty_cycle, - state->period); - if (err) - return err; + if (!state->enabled) { + if (pwm->state.enabled) + chip->ops->disable(chip, pwm); - pwm->state.period = state->period; - pwm->state.duty_cycle = state->duty_cycle; + return 0; } - if (state->enabled != pwm->state.enabled) { - if (!pwm->state.enabled) { - err = chip->ops->enable(chip, pwm); - if (err) - return err; - } else { - chip->ops->disable(chip, pwm); - } + /* + * We cannot skip calling ->config even if state->period == + * pwm->state.period && state->duty_cycle == pwm->state.duty_cycle + * because we might have exited early in the last call to + * pwm_apply_state because of !state->enabled and so the two values in + * pwm->state might not be configured in hardware. + */ + err = chip->ops->config(pwm->chip, pwm, + state->duty_cycle, + state->period); + if (err) + return err; + + pwm->state.period = state->period; + pwm->state.duty_cycle = state->duty_cycle; + + if (!pwm->state.enabled) { + err = chip->ops->enable(chip, pwm); + if (err) + return err; } return 0; -- cgit From e45a178e9e285c86265611a56705a1e6444037e3 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 1 Jul 2021 09:29:27 +0200 Subject: pwm: Restore initial state if a legacy callback fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is not entirely accurate to go back to the initial state after e.g. .enable() failed, as .config() still modified the hardware, but this same inconsistency exists for drivers that implement .apply(). Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/pwm/core.c') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index dedf38a81bf9..57b8cedfec3f 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -526,10 +526,8 @@ static int pwm_apply_legacy(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { int err; + struct pwm_state initial_state = pwm->state; - /* - * FIXME: restore the initial state in case of error. - */ if (state->polarity != pwm->state.polarity) { if (!chip->ops->set_polarity) return -EINVAL; @@ -550,7 +548,7 @@ static int pwm_apply_legacy(struct pwm_chip *chip, struct pwm_device *pwm, err = chip->ops->set_polarity(chip, pwm, state->polarity); if (err) - return err; + goto rollback; pwm->state.polarity = state->polarity; } @@ -573,7 +571,7 @@ static int pwm_apply_legacy(struct pwm_chip *chip, struct pwm_device *pwm, state->duty_cycle, state->period); if (err) - return err; + goto rollback; pwm->state.period = state->period; pwm->state.duty_cycle = state->duty_cycle; @@ -581,10 +579,14 @@ static int pwm_apply_legacy(struct pwm_chip *chip, struct pwm_device *pwm, if (!pwm->state.enabled) { err = chip->ops->enable(chip, pwm); if (err) - return err; + goto rollback; } return 0; + +rollback: + pwm->state = initial_state; + return err; } /** -- cgit