diff mbox series

pwm: Prevent a glitch in compat code

Message ID 20210308093600.25455-1-u.kleine-koenig@pengutronix.de
State Changes Requested
Headers show
Series pwm: Prevent a glitch in compat code | expand

Commit Message

Uwe Kleine-König March 8, 2021, 9:36 a.m. UTC
When a PWM is to be disabled, configuring the duty cycle and
period before actually disabling the hardware might result in either a
glitch or a delay. So check for disabling first and return early in this
case.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
 drivers/pwm/core.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

Comments

Thierry Reding March 22, 2021, 11:01 a.m. UTC | #1
On Mon, Mar 08, 2021 at 10:36:00AM +0100, Uwe Kleine-König wrote:
> When a PWM is to be disabled, configuring the duty cycle and
> period before actually disabling the hardware might result in either a
> glitch or a delay. So check for disabling first and return early in this
> case.
> 
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> ---
>  drivers/pwm/core.c | 20 +++++++++++---------
>  1 file changed, 11 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> index 4058d3c86a45..4604ca3e0e62 100644
> --- a/drivers/pwm/core.c
> +++ b/drivers/pwm/core.c
> @@ -597,6 +597,12 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
>  			pwm->state.polarity = state->polarity;
>  		}
>  
> +		if (!state->enabled && pwm->state.enabled) {
> +			chip->ops->disable(chip, pwm);
> +			pwm->state.enabled = false;
> +			return 0;

I don't think we can return early here because otherwise if consumers
happen to modify the period along with the enabled state, the changes
to the period will get lost.

Can we just omit the "return 0;" here?

> +		}
> +
>  		if (state->period != pwm->state.period ||
>  		    state->duty_cycle != pwm->state.duty_cycle) {
>  			int duty_cycle, period;
> @@ -620,16 +626,12 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
>  			pwm->state.period = state->period;
>  		}
>  
> -		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);
> -			}
> +		if (!pwm->state.enabled) {

If we don't return early above, we'll have to change this here as well
to ensure we don't gratuituously enable the PWM. Instead this would
basically become the inverse of what we do for disable above, which is
also nicely symmetrical (i.e. disable, then change period/duty to avoid
a glitch and change period/duty then enable to avoid the glitch).

Thierry
Uwe Kleine-König March 22, 2021, 11:11 a.m. UTC | #2
On Mon, Mar 22, 2021 at 12:01:01PM +0100, Thierry Reding wrote:
> On Mon, Mar 08, 2021 at 10:36:00AM +0100, Uwe Kleine-König wrote:
> > When a PWM is to be disabled, configuring the duty cycle and
> > period before actually disabling the hardware might result in either a
> > glitch or a delay. So check for disabling first and return early in this
> > case.
> > 
> > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > ---
> >  drivers/pwm/core.c | 20 +++++++++++---------
> >  1 file changed, 11 insertions(+), 9 deletions(-)
> > 
> > diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> > index 4058d3c86a45..4604ca3e0e62 100644
> > --- a/drivers/pwm/core.c
> > +++ b/drivers/pwm/core.c
> > @@ -597,6 +597,12 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
> >  			pwm->state.polarity = state->polarity;
> >  		}
> >  
> > +		if (!state->enabled && pwm->state.enabled) {
> > +			chip->ops->disable(chip, pwm);
> > +			pwm->state.enabled = false;
> > +			return 0;
> 
> I don't think we can return early here because otherwise if consumers
> happen to modify the period along with the enabled state, the changes
> to the period will get lost.

This however doesn't matter, because the output of a disabled PWM only
depends on polarity. (And polarity is already cared for.)

And if a driver calls pwm_enable() afterwards (or the equivalent in
terms of pwm_apply_state) the period and duty_cycle will be picked up
correctly.

> Can we just omit the "return 0;" here?

> 
> > +		}
> > +
> >  		if (state->period != pwm->state.period ||
> >  		    state->duty_cycle != pwm->state.duty_cycle) {
> >  			int duty_cycle, period;
> > @@ -620,16 +626,12 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
> >  			pwm->state.period = state->period;
> >  		}
> >  
> > -		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);
> > -			}
> > +		if (!pwm->state.enabled) {
> 
> If we don't return early above, we'll have to change this here as well
> to ensure we don't gratuituously enable the PWM. Instead this would
> basically become the inverse of what we do for disable above, which is
> also nicely symmetrical (i.e. disable, then change period/duty to avoid
> a glitch and change period/duty then enable to avoid the glitch).

Either I don't understand you, or we're already there.

Best regards
Uwe
Uwe Kleine-König April 10, 2021, 9:46 p.m. UTC | #3
On Mon, Mar 22, 2021 at 12:11:31PM +0100, Uwe Kleine-König wrote:
> On Mon, Mar 22, 2021 at 12:01:01PM +0100, Thierry Reding wrote:
> > On Mon, Mar 08, 2021 at 10:36:00AM +0100, Uwe Kleine-König wrote:
> > > When a PWM is to be disabled, configuring the duty cycle and
> > > period before actually disabling the hardware might result in either a
> > > glitch or a delay. So check for disabling first and return early in this
> > > case.
> > > 
> > > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > > ---
> > >  drivers/pwm/core.c | 20 +++++++++++---------
> > >  1 file changed, 11 insertions(+), 9 deletions(-)
> > > 
> > > diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> > > index 4058d3c86a45..4604ca3e0e62 100644
> > > --- a/drivers/pwm/core.c
> > > +++ b/drivers/pwm/core.c
> > > @@ -597,6 +597,12 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
> > >  			pwm->state.polarity = state->polarity;
> > >  		}
> > >  
> > > +		if (!state->enabled && pwm->state.enabled) {
> > > +			chip->ops->disable(chip, pwm);
> > > +			pwm->state.enabled = false;
> > > +			return 0;
> > 
> > I don't think we can return early here because otherwise if consumers
> > happen to modify the period along with the enabled state, the changes
> > to the period will get lost.
> 
> This however doesn't matter, because the output of a disabled PWM only
> depends on polarity. (And polarity is already cared for.)
> 
> And if a driver calls pwm_enable() afterwards (or the equivalent in
> terms of pwm_apply_state) the period and duty_cycle will be picked up
> correctly.

I see you marked my patch as "changes requested" in patchwork. However
I'm convinced your feedback is wrong and so I still think the patch
is safe to be applied unmodified.

Best regards
Uwe
Uwe Kleine-König April 11, 2021, 8:22 a.m. UTC | #4
On Sat, Apr 10, 2021 at 11:46:42PM +0200, Uwe Kleine-König wrote:
> On Mon, Mar 22, 2021 at 12:11:31PM +0100, Uwe Kleine-König wrote:
> > On Mon, Mar 22, 2021 at 12:01:01PM +0100, Thierry Reding wrote:
> > > On Mon, Mar 08, 2021 at 10:36:00AM +0100, Uwe Kleine-König wrote:
> > > > When a PWM is to be disabled, configuring the duty cycle and
> > > > period before actually disabling the hardware might result in either a
> > > > glitch or a delay. So check for disabling first and return early in this
> > > > case.
> > > > 
> > > > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > > > ---
> > > >  drivers/pwm/core.c | 20 +++++++++++---------
> > > >  1 file changed, 11 insertions(+), 9 deletions(-)
> > > > 
> > > > diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> > > > index 4058d3c86a45..4604ca3e0e62 100644
> > > > --- a/drivers/pwm/core.c
> > > > +++ b/drivers/pwm/core.c
> > > > @@ -597,6 +597,12 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
> > > >  			pwm->state.polarity = state->polarity;
> > > >  		}
> > > >  
> > > > +		if (!state->enabled && pwm->state.enabled) {
> > > > +			chip->ops->disable(chip, pwm);
> > > > +			pwm->state.enabled = false;
> > > > +			return 0;
> > > 
> > > I don't think we can return early here because otherwise if consumers
> > > happen to modify the period along with the enabled state, the changes
> > > to the period will get lost.
> > 
> > This however doesn't matter, because the output of a disabled PWM only
> > depends on polarity. (And polarity is already cared for.)
> > 
> > And if a driver calls pwm_enable() afterwards (or the equivalent in
> > terms of pwm_apply_state) the period and duty_cycle will be picked up
> > correctly.
> 
> I see you marked my patch as "changes requested" in patchwork. However
> I'm convinced your feedback is wrong and so I still think the patch
> is safe to be applied unmodified.

After sleeping about this I understood you're right. I will rework the
patch.

Best regards
Uwe
diff mbox series

Patch

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 4058d3c86a45..4604ca3e0e62 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -597,6 +597,12 @@  int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
 			pwm->state.polarity = state->polarity;
 		}
 
+		if (!state->enabled && pwm->state.enabled) {
+			chip->ops->disable(chip, pwm);
+			pwm->state.enabled = false;
+			return 0;
+		}
+
 		if (state->period != pwm->state.period ||
 		    state->duty_cycle != pwm->state.duty_cycle) {
 			int duty_cycle, period;
@@ -620,16 +626,12 @@  int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
 			pwm->state.period = state->period;
 		}
 
-		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);
-			}
+		if (!pwm->state.enabled) {
+			err = chip->ops->enable(chip, pwm);
+			if (err)
+				return err;
 
-			pwm->state.enabled = state->enabled;
+			pwm->state.enabled = true;
 		}
 	}