diff mbox series

[1/4] pwm: pca9685: remove unused duty_cycle struct element

Message ID 20200226135229.24929-1-matthias.schiffer@ew.tq-group.com
State Accepted
Headers show
Series [1/4] pwm: pca9685: remove unused duty_cycle struct element | expand

Commit Message

Matthias Schiffer Feb. 26, 2020, 1:52 p.m. UTC
duty_cycle was only set, never read.

Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
---
 drivers/pwm/pwm-pca9685.c | 4 ----
 1 file changed, 4 deletions(-)

Comments

Uwe Kleine-König Feb. 26, 2020, 3:10 p.m. UTC | #1
Hello Matthias,

as you seem to have this hardware on your desk, it would be great if you
could answer the following questions:

 - Does the hardware complete the currently running period before
   applying a new setting?
 - Is this racy somehow (i.e. can it happen that when going from
   duty_cycle/period = 1000/5000 to duty_cycle/period = 4000/10000 the
   output is 1000/10000 (or 4000/5000) for one cycle)?
 - Does the hardware complete the currently running period before
   .enabled = false is configured?
 - How does the output pin behave on a disabled PWM. (Usual candidates
   are: freeze where is just happens to be, constant inactive and
   High-Z).

Best regards
Uwe
Matthias Schiffer Feb. 26, 2020, 5:03 p.m. UTC | #2
On Wed, 2020-02-26 at 16:10 +0100, Uwe Kleine-König wrote:
> Hello Matthias,
> 
> as you seem to have this hardware on your desk, it would be great if
> you
> could answer the following questions:
> 
>  - Does the hardware complete the currently running period before
>    applying a new setting?

The datasheet claims:

> Because the loading of the LEDn_ON and LEDn_OFF registers is via the
> I 2 C-bus, and
> asynchronous to the internal oscillator, we want to ensure that we do
> not see any visual
> artifacts of changing the ON and OFF values. This is achieved by
> updating the changes at
> the end of the LOW cycle.

My interpretation is that the hardware will complete its period before
applying the new settings. I might check with a scope tomorrow-ish.


>  - Is this racy somehow (i.e. can it happen that when going from
>    duty_cycle/period = 1000/5000 to duty_cycle/period = 4000/10000
> the
>    output is 1000/10000 (or 4000/5000) for one cycle)?

It currently is racy. It should be possible to fix that either by
updating all 4 registers in a single I2C write, or by using the "update
on ACK" mode which requires all 4 registers to be updated before the
new setting is applied (I'm not sure if this mode would require using a
single I2C write as well though).


>  - Does the hardware complete the currently running period before
>    .enabled = false is configured?

As my interpretation is that new settings are applied asynchronously, I
assume that the final running period is completed after .enabled is set
to false.

>  - How does the output pin behave on a disabled PWM. (Usual
> candidates
>    are: freeze where is just happens to be, constant inactive and
>    High-Z).

Constant inactive. This is also the case when the chip is put into
sleep mode. Note that the interpretation of "inactive" depends in the
invert flag in the MODE2 register.

As it turns out, this driver is broken in yet another way I didn't find
before: For changing the global prescaler the chip needs to be put into
sleep mode, but the driver doesn't follow the restart sequence
described in the datasheet when waking it back up. In consequence,
changing the period of one PWM does not only modify the period of all
PWMs (which is bad enough, but can't be avoided with this hardware),
but it also leaves all PWMs disabled...

As this hardware only has a single prescaler for all PWMs, should
changing the period for individual PWMs even be allowed at all? Maybe
only when all other PWMs are inactive?

I could imagine setting it in DTS instead (but I'm not sure what to do
about non-OF users of this driver, for example when configured via
ACPI).

Regards,
Matthias



> 
> Best regards
> Uwe
>
Uwe Kleine-König Feb. 26, 2020, 7:21 p.m. UTC | #3
On Wed, Feb 26, 2020 at 06:03:02PM +0100, Matthias Schiffer wrote:
> On Wed, 2020-02-26 at 16:10 +0100, Uwe Kleine-König wrote:
> > Hello Matthias,
> > 
> > as you seem to have this hardware on your desk, it would be great if
> > you
> > could answer the following questions:
> > 
> >  - Does the hardware complete the currently running period before
> >    applying a new setting?
> 
> The datasheet claims:
> 
> > Because the loading of the LEDn_ON and LEDn_OFF registers is via the
> > I 2 C-bus, and
> > asynchronous to the internal oscillator, we want to ensure that we do
> > not see any visual
> > artifacts of changing the ON and OFF values. This is achieved by
> > updating the changes at
> > the end of the LOW cycle.
> 
> My interpretation is that the hardware will complete its period before
> applying the new settings. I might check with a scope tomorrow-ish.

I agree given that you can update duty_cycle and period in a single
write as you considered below. Maybe it is worth playing with small
periods and a slow i2c bus speed (or hijack the bus by simulating a
clock stretch).
 
> >  - Is this racy somehow (i.e. can it happen that when going from
> >    duty_cycle/period = 1000/5000 to duty_cycle/period = 4000/10000 the
> >    output is 1000/10000 (or 4000/5000) for one cycle)?
> 
> It currently is racy. It should be possible to fix that either by
> updating all 4 registers in a single I2C write, or by using the "update
> on ACK" mode which requires all 4 registers to be updated before the
> new setting is applied (I'm not sure if this mode would require using a
> single I2C write as well though).

I can offer a second pair of eyeballs to interpret the datasheet. Will
take a look tomorrow.

> >  - Does the hardware complete the currently running period before
> >    .enabled = false is configured?
> 
> As my interpretation is that new settings are applied asynchronously, I
> assume that the final running period is completed after .enabled is set
> to false.
> 
> >  - How does the output pin behave on a disabled PWM. (Usual candidates
> >    are: freeze where is just happens to be, constant inactive and
> >    High-Z).
> 
> Constant inactive. This is also the case when the chip is put into
> sleep mode. Note that the interpretation of "inactive" depends in the
> invert flag in the MODE2 register.

This is optimal.

> As it turns out, this driver is broken in yet another way I didn't find
> before: For changing the global prescaler the chip needs to be put into
> sleep mode, but the driver doesn't follow the restart sequence
> described in the datasheet when waking it back up. In consequence,
> changing the period of one PWM does not only modify the period of all
> PWMs (which is bad enough, but can't be avoided with this hardware),
> but it also leaves all PWMs disabled...
> 
> As this hardware only has a single prescaler for all PWMs, should
> changing the period for individual PWMs even be allowed at all? Maybe
> only when all other PWMs are inactive?

yes, that is the general approach. Please document this in a
Limitiations: paragraph. See drivers/pwm/pwm-imx-tpm.c which has a
similar problem.
 
> I could imagine setting it in DTS instead (but I'm not sure what to do
> about non-OF users of this driver, for example when configured via
> ACPI).

I don't like fixing the period in the device tree. This isn't a hardware
property and it is less flexible than possible.

Best regards
Uwe
Matthias Schiffer Feb. 28, 2020, 1:26 p.m. UTC | #4
On Wed, 2020-02-26 at 20:21 +0100, Uwe Kleine-König wrote:
> On Wed, Feb 26, 2020 at 06:03:02PM +0100, Matthias Schiffer wrote:
> > On Wed, 2020-02-26 at 16:10 +0100, Uwe Kleine-König wrote:
> > > Hello Matthias,
> > > 
> > > as you seem to have this hardware on your desk, it would be great
> > > if
> > > you
> > > could answer the following questions:
> > > 
> > >  - Does the hardware complete the currently running period before
> > >    applying a new setting?
> > 
> > The datasheet claims:
> > 
> > > Because the loading of the LEDn_ON and LEDn_OFF registers is via
> > > the
> > > I 2 C-bus, and
> > > asynchronous to the internal oscillator, we want to ensure that
> > > we do
> > > not see any visual
> > > artifacts of changing the ON and OFF values. This is achieved by
> > > updating the changes at
> > > the end of the LOW cycle.
> > 
> > My interpretation is that the hardware will complete its period
> > before
> > applying the new settings. I might check with a scope tomorrow-ish.
> 
> I agree given that you can update duty_cycle and period in a single
> write as you considered below. Maybe it is worth playing with small
> periods and a slow i2c bus speed (or hijack the bus by simulating a
> clock stretch).
>  
> > >  - Is this racy somehow (i.e. can it happen that when going from
> > >    duty_cycle/period = 1000/5000 to duty_cycle/period =
> > > 4000/10000 the
> > >    output is 1000/10000 (or 4000/5000) for one cycle)?
> > 
> > It currently is racy. It should be possible to fix that either by
> > updating all 4 registers in a single I2C write, or by using the
> > "update
> > on ACK" mode which requires all 4 registers to be updated before
> > the
> > new setting is applied (I'm not sure if this mode would require
> > using a
> > single I2C write as well though).
> 
> I can offer a second pair of eyeballs to interpret the datasheet.
> Will
> take a look tomorrow.
> 
> > >  - Does the hardware complete the currently running period before
> > >    .enabled = false is configured?
> > 
> > As my interpretation is that new settings are applied
> > asynchronously, I
> > assume that the final running period is completed after .enabled is
> > set
> > to false.
> > 
> > >  - How does the output pin behave on a disabled PWM. (Usual
> > > candidates
> > >    are: freeze where is just happens to be, constant inactive and
> > >    High-Z).
> > 
> > Constant inactive. This is also the case when the chip is put into
> > sleep mode. Note that the interpretation of "inactive" depends in
> > the
> > invert flag in the MODE2 register.
> 
> This is optimal.
> 
> > As it turns out, this driver is broken in yet another way I didn't
> > find
> > before: For changing the global prescaler the chip needs to be put
> > into
> > sleep mode, but the driver doesn't follow the restart sequence
> > described in the datasheet when waking it back up. In consequence,
> > changing the period of one PWM does not only modify the period of
> > all
> > PWMs (which is bad enough, but can't be avoided with this
> > hardware),
> > but it also leaves all PWMs disabled...
> > 
> > As this hardware only has a single prescaler for all PWMs, should
> > changing the period for individual PWMs even be allowed at all?
> > Maybe
> > only when all other PWMs are inactive?
> 
> yes, that is the general approach. Please document this in a
> Limitiations: paragraph. See drivers/pwm/pwm-imx-tpm.c which has a
> similar problem.

This raises the question what to do about the GPIO mode supported by
the driver: While the period does not affect GPIO usage of PWMs,
changing the period would put the chip in sleep mode and thus briefly
disable active GPIOs. I assume that this should preclude changing the
period when there are any PWMs requsted as GPIOs, but now the order in
which things are initialized becomes crucial:

- All references to PWMs of the same PCA9685 must specify the same
period
- When requesting a PWM as GPIO, no period can be specified
=> When a PWM referenced as GPIO is requested before the first actual
PWM, setting the correct period becomes impossible.

I can't think of a nice solution that doesn't require serious rework -
maybe we need at least an optional period property in DTS to support
this case? This could either be implemented as a default period or a
fixed period.

A more elaborate solution could be to remove the GPIO code from PCA9685
and implement a generic GPIO-PWM driver instead that could be
configured in DTS (again, I have no idea how to support non-DTS
platforms). Unfortunately, I assume I won't have time to realize such a
solution myself.

Matthias


>  
> > I could imagine setting it in DTS instead (but I'm not sure what to
> > do
> > about non-OF users of this driver, for example when configured via
> > ACPI).
> 
> I don't like fixing the period in the device tree. This isn't a
> hardware
> property and it is less flexible than possible.
> 
> Best regards
> Uwe
>
Thierry Reding March 30, 2020, 1:07 p.m. UTC | #5
On Wed, Feb 26, 2020 at 02:52:26PM +0100, Matthias Schiffer wrote:
> duty_cycle was only set, never read.
> 
> Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
> ---
>  drivers/pwm/pwm-pca9685.c | 4 ----
>  1 file changed, 4 deletions(-)

Applied, thanks.

Thierry
Andy Shevchenko March 30, 2020, 1:18 p.m. UTC | #6
On Mon, Mar 30, 2020 at 4:09 PM Thierry Reding <thierry.reding@gmail.com> wrote:
>
> On Wed, Feb 26, 2020 at 02:52:26PM +0100, Matthias Schiffer wrote:
> > duty_cycle was only set, never read.
> >
> > Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
> > ---
> >  drivers/pwm/pwm-pca9685.c | 4 ----
> >  1 file changed, 4 deletions(-)
>
> Applied, thanks.

I'm not sure this patch is correct.
We already have broken GPIO in this driver. Do we need more breakage?
Clemens Gruber March 30, 2020, 3:12 p.m. UTC | #7
Hi,

On Wed, Feb 26, 2020 at 06:03:02PM +0100, Matthias Schiffer wrote:
> As it turns out, this driver is broken in yet another way I didn't find
> before: For changing the global prescaler the chip needs to be put into
> sleep mode, but the driver doesn't follow the restart sequence
> described in the datasheet when waking it back up. In consequence,
> changing the period of one PWM does not only modify the period of all
> PWMs (which is bad enough, but can't be avoided with this hardware),
> but it also leaves all PWMs disabled...

I am unable to reproduce this: If I set a specific duty cycle on a
channel and then change the period, the channel stays active.
I can see the brightness of an LED decrease if I increase the period.

This is expected, because after the SLEEP bit is set, we wait for
500usecs and then write to the LED ON/OFF registers.
This leaves the channel enabled with the new period (but with old
duty_ns value => different ratio)

A few years ago, I played around with the idea of remembering the
duty_ns to period_ns ratio and setting it accordingly after a period
change, possibly also with a shortcut of setting the RESTART bit if the
ratio did not change. Maybe after the switch to the atomic API, this
would be a nice improvement.

Best regards,
Clemens
Thierry Reding March 30, 2020, 4:02 p.m. UTC | #8
On Mon, Mar 30, 2020 at 04:18:22PM +0300, Andy Shevchenko wrote:
> On Mon, Mar 30, 2020 at 4:09 PM Thierry Reding <thierry.reding@gmail.com> wrote:
> >
> > On Wed, Feb 26, 2020 at 02:52:26PM +0100, Matthias Schiffer wrote:
> > > duty_cycle was only set, never read.
> > >
> > > Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
> > > ---
> > >  drivers/pwm/pwm-pca9685.c | 4 ----
> > >  1 file changed, 4 deletions(-)
> >
> > Applied, thanks.
> 
> I'm not sure this patch is correct.

What makes you say that? If you look at the code, the driver sets this
field to either 0 or some duty cycle value but ends up never using it.
Why would it be wrong to remove that code?

> We already have broken GPIO in this driver. Do we need more breakage?

My understanding is that nobody was able to pinpoint exactly when this
regressed, or if this only worked by accident to begin with. It sounds
like Clemens has a way of testing this driver, so perhaps we can solve
that GPIO issue while we're at it.

The last discussion on this seems to have been around the time when you
posted a fix for it:

    https://patchwork.ozlabs.org/patch/1156012/

But then Sven had concerns that that also wasn't guaranteed to work:

    https://lkml.org/lkml/2019/6/2/73

So I think we could either apply your patch to restore the old behaviour
which I assume you tested, so at least it seems to work in practice,
even if there's still a potential race that Sven pointed out in the
above link.

I'd prefer something alternative because it's obviously confusing and
completely undocumented. Mika had already proposed something that's a
little bit better, though still somewhat confusing.

Oh... actually reading further through those threads there seems to be a
patch from Sven that was reviewed by Mika but then nothing happened:

	https://lkml.org/lkml/2019/6/4/1039

with the corresponding patchwork URL:

	https://patchwork.ozlabs.org/patch/1110083/

Andy, Clemens, do you have a way of testing the GPIO functionality of
this driver? If so, it'd be great if you could check the above patch
from Sven to fix PWM/GPIO interop.

Thierry
Clemens Gruber March 30, 2020, 4:10 p.m. UTC | #9
On Mon, Mar 30, 2020 at 06:02:38PM +0200, Thierry Reding wrote:
> On Mon, Mar 30, 2020 at 04:18:22PM +0300, Andy Shevchenko wrote:
> > On Mon, Mar 30, 2020 at 4:09 PM Thierry Reding <thierry.reding@gmail.com> wrote:
> > >
> > > On Wed, Feb 26, 2020 at 02:52:26PM +0100, Matthias Schiffer wrote:
> > > > duty_cycle was only set, never read.
> > > >
> > > > Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
> > > > ---
> > > >  drivers/pwm/pwm-pca9685.c | 4 ----
> > > >  1 file changed, 4 deletions(-)
> > >
> > > Applied, thanks.
> > 
> > I'm not sure this patch is correct.
> 
> What makes you say that? If you look at the code, the driver sets this
> field to either 0 or some duty cycle value but ends up never using it.
> Why would it be wrong to remove that code?
> 
> > We already have broken GPIO in this driver. Do we need more breakage?
> 
> My understanding is that nobody was able to pinpoint exactly when this
> regressed, or if this only worked by accident to begin with. It sounds
> like Clemens has a way of testing this driver, so perhaps we can solve
> that GPIO issue while we're at it.
> 
> The last discussion on this seems to have been around the time when you
> posted a fix for it:
> 
>     https://patchwork.ozlabs.org/patch/1156012/
> 
> But then Sven had concerns that that also wasn't guaranteed to work:
> 
>     https://lkml.org/lkml/2019/6/2/73
> 
> So I think we could either apply your patch to restore the old behaviour
> which I assume you tested, so at least it seems to work in practice,
> even if there's still a potential race that Sven pointed out in the
> above link.
> 
> I'd prefer something alternative because it's obviously confusing and
> completely undocumented. Mika had already proposed something that's a
> little bit better, though still somewhat confusing.
> 
> Oh... actually reading further through those threads there seems to be a
> patch from Sven that was reviewed by Mika but then nothing happened:
> 
> 	https://lkml.org/lkml/2019/6/4/1039
> 
> with the corresponding patchwork URL:
> 
> 	https://patchwork.ozlabs.org/patch/1110083/
> 
> Andy, Clemens, do you have a way of testing the GPIO functionality of
> this driver? If so, it'd be great if you could check the above patch
> from Sven to fix PWM/GPIO interop.

Yes. I'll have a look and report back in a few days.

Clemens
Clemens Gruber April 1, 2020, 4:36 p.m. UTC | #10
On Mon, Mar 30, 2020 at 06:02:38PM +0200, Thierry Reding wrote:
> On Mon, Mar 30, 2020 at 04:18:22PM +0300, Andy Shevchenko wrote:
> > On Mon, Mar 30, 2020 at 4:09 PM Thierry Reding <thierry.reding@gmail.com> wrote:
> > >
> > > On Wed, Feb 26, 2020 at 02:52:26PM +0100, Matthias Schiffer wrote:
> > > > duty_cycle was only set, never read.
> > > >
> > > > Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
> > > > ---
> > > >  drivers/pwm/pwm-pca9685.c | 4 ----
> > > >  1 file changed, 4 deletions(-)
> > >
> > > Applied, thanks.
> > 
> > I'm not sure this patch is correct.
> 
> What makes you say that? If you look at the code, the driver sets this
> field to either 0 or some duty cycle value but ends up never using it.
> Why would it be wrong to remove that code?
> 
> > We already have broken GPIO in this driver. Do we need more breakage?
> 
> My understanding is that nobody was able to pinpoint exactly when this
> regressed, or if this only worked by accident to begin with. It sounds
> like Clemens has a way of testing this driver, so perhaps we can solve
> that GPIO issue while we're at it.
> 
> The last discussion on this seems to have been around the time when you
> posted a fix for it:
> 
>     https://patchwork.ozlabs.org/patch/1156012/
> 
> But then Sven had concerns that that also wasn't guaranteed to work:
> 
>     https://lkml.org/lkml/2019/6/2/73
> 
> So I think we could either apply your patch to restore the old behaviour
> which I assume you tested, so at least it seems to work in practice,
> even if there's still a potential race that Sven pointed out in the
> above link.
> 
> I'd prefer something alternative because it's obviously confusing and
> completely undocumented. Mika had already proposed something that's a
> little bit better, though still somewhat confusing.
> 
> Oh... actually reading further through those threads there seems to be a
> patch from Sven that was reviewed by Mika but then nothing happened:
> 
> 	https://lkml.org/lkml/2019/6/4/1039
> 
> with the corresponding patchwork URL:
> 
> 	https://patchwork.ozlabs.org/patch/1110083/
> 
> Andy, Clemens, do you have a way of testing the GPIO functionality of
> this driver? If so, it'd be great if you could check the above patch
> from Sven to fix PWM/GPIO interop.

Looks good. Tested it today and I can no longer reproduce the issues
when switching between PWM and GPIO modes.
It did not apply cleanly on the current mainline or for-next branch, so
I'll send a fixed up version of the patch with my Tested-by tag shortly.

I noticed an unrelated issue when disabling and enabling the channel
though, for which I will either send a patch or maybe try to convert the
driver to the atomic API first and then look if it is still a problem.
(Issue is that if you disable the channel, the LED_OFF counter is
cleared, which means you have to reconfigure the duty cycle after
reenabling. It's probably better if only the FULL_OFF bit is toggled in
enable/disable as it has precedence over the others anyway and then the
previous state would not be changed..?)

Clemens
Thierry Reding April 1, 2020, 5:45 p.m. UTC | #11
On Wed, Apr 01, 2020 at 06:36:40PM +0200, Clemens Gruber wrote:
> On Mon, Mar 30, 2020 at 06:02:38PM +0200, Thierry Reding wrote:
> > On Mon, Mar 30, 2020 at 04:18:22PM +0300, Andy Shevchenko wrote:
> > > On Mon, Mar 30, 2020 at 4:09 PM Thierry Reding <thierry.reding@gmail.com> wrote:
> > > >
> > > > On Wed, Feb 26, 2020 at 02:52:26PM +0100, Matthias Schiffer wrote:
> > > > > duty_cycle was only set, never read.
> > > > >
> > > > > Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
> > > > > ---
> > > > >  drivers/pwm/pwm-pca9685.c | 4 ----
> > > > >  1 file changed, 4 deletions(-)
> > > >
> > > > Applied, thanks.
> > > 
> > > I'm not sure this patch is correct.
> > 
> > What makes you say that? If you look at the code, the driver sets this
> > field to either 0 or some duty cycle value but ends up never using it.
> > Why would it be wrong to remove that code?
> > 
> > > We already have broken GPIO in this driver. Do we need more breakage?
> > 
> > My understanding is that nobody was able to pinpoint exactly when this
> > regressed, or if this only worked by accident to begin with. It sounds
> > like Clemens has a way of testing this driver, so perhaps we can solve
> > that GPIO issue while we're at it.
> > 
> > The last discussion on this seems to have been around the time when you
> > posted a fix for it:
> > 
> >     https://patchwork.ozlabs.org/patch/1156012/
> > 
> > But then Sven had concerns that that also wasn't guaranteed to work:
> > 
> >     https://lkml.org/lkml/2019/6/2/73
> > 
> > So I think we could either apply your patch to restore the old behaviour
> > which I assume you tested, so at least it seems to work in practice,
> > even if there's still a potential race that Sven pointed out in the
> > above link.
> > 
> > I'd prefer something alternative because it's obviously confusing and
> > completely undocumented. Mika had already proposed something that's a
> > little bit better, though still somewhat confusing.
> > 
> > Oh... actually reading further through those threads there seems to be a
> > patch from Sven that was reviewed by Mika but then nothing happened:
> > 
> > 	https://lkml.org/lkml/2019/6/4/1039
> > 
> > with the corresponding patchwork URL:
> > 
> > 	https://patchwork.ozlabs.org/patch/1110083/
> > 
> > Andy, Clemens, do you have a way of testing the GPIO functionality of
> > this driver? If so, it'd be great if you could check the above patch
> > from Sven to fix PWM/GPIO interop.
> 
> Looks good. Tested it today and I can no longer reproduce the issues
> when switching between PWM and GPIO modes.
> It did not apply cleanly on the current mainline or for-next branch, so
> I'll send a fixed up version of the patch with my Tested-by tag shortly.

Awesome, thank you!

> I noticed an unrelated issue when disabling and enabling the channel
> though, for which I will either send a patch or maybe try to convert the
> driver to the atomic API first and then look if it is still a problem.
> (Issue is that if you disable the channel, the LED_OFF counter is
> cleared, which means you have to reconfigure the duty cycle after
> reenabling. It's probably better if only the FULL_OFF bit is toggled in
> enable/disable as it has precedence over the others anyway and then the
> previous state would not be changed..?)

Converting to the atomic API would certainly be beneficial because it
gives you much more control over what you want to program to hardware.

Other than that, yes, if ->enable() called after ->disable() doesn't put
the PWM into the same state that it was before ->disable(), then that'd
be a bug. From what you're saying this will make the driver only work if
there's a ->config() call after ->disable(). That's wrong.

So yes, setting only that LED_FULL bit in ->disable() and clearing it in
->enable() sounds like a better option than the current implementation.

Thierry
Matthias Schiffer April 2, 2020, 7:10 a.m. UTC | #12
On Wed, 2020-04-01 at 19:45 +0200, Thierry Reding wrote:
> * PGP Signed by an unknown key
> 
> On Wed, Apr 01, 2020 at 06:36:40PM +0200, Clemens Gruber wrote:
> > On Mon, Mar 30, 2020 at 06:02:38PM +0200, Thierry Reding wrote:
> > > On Mon, Mar 30, 2020 at 04:18:22PM +0300, Andy Shevchenko wrote:
> > > > On Mon, Mar 30, 2020 at 4:09 PM Thierry Reding <
> > > > thierry.reding@gmail.com> wrote:
> > > > > 
> > > > > On Wed, Feb 26, 2020 at 02:52:26PM +0100, Matthias Schiffer
> > > > > wrote:
> > > > > > duty_cycle was only set, never read.
> > > > > > 
> > > > > > Signed-off-by: Matthias Schiffer <
> > > > > > matthias.schiffer@ew.tq-group.com>
> > > > > > ---
> > > > > >  drivers/pwm/pwm-pca9685.c | 4 ----
> > > > > >  1 file changed, 4 deletions(-)
> > > > > 
> > > > > Applied, thanks.
> > > > 
> > > > I'm not sure this patch is correct.
> > > 
> > > What makes you say that? If you look at the code, the driver sets
> > > this
> > > field to either 0 or some duty cycle value but ends up never
> > > using it.
> > > Why would it be wrong to remove that code?
> > > 
> > > > We already have broken GPIO in this driver. Do we need more
> > > > breakage?
> > > 
> > > My understanding is that nobody was able to pinpoint exactly when
> > > this
> > > regressed, or if this only worked by accident to begin with. It
> > > sounds
> > > like Clemens has a way of testing this driver, so perhaps we can
> > > solve
> > > that GPIO issue while we're at it.
> > > 
> > > The last discussion on this seems to have been around the time
> > > when you
> > > posted a fix for it:
> > > 
> > >     https://patchwork.ozlabs.org/patch/1156012/
> > > 
> > > But then Sven had concerns that that also wasn't guaranteed to
> > > work:
> > > 
> > >     https://lkml.org/lkml/2019/6/2/73
> > > 
> > > So I think we could either apply your patch to restore the old
> > > behaviour
> > > which I assume you tested, so at least it seems to work in
> > > practice,
> > > even if there's still a potential race that Sven pointed out in
> > > the
> > > above link.
> > > 
> > > I'd prefer something alternative because it's obviously confusing
> > > and
> > > completely undocumented. Mika had already proposed something
> > > that's a
> > > little bit better, though still somewhat confusing.
> > > 
> > > Oh... actually reading further through those threads there seems
> > > to be a
> > > patch from Sven that was reviewed by Mika but then nothing
> > > happened:
> > > 
> > > 	https://lkml.org/lkml/2019/6/4/1039
> > > 
> > > with the corresponding patchwork URL:
> > > 
> > > 	https://patchwork.ozlabs.org/patch/1110083/
> > > 
> > > Andy, Clemens, do you have a way of testing the GPIO
> > > functionality of
> > > this driver? If so, it'd be great if you could check the above
> > > patch
> > > from Sven to fix PWM/GPIO interop.
> > 
> > Looks good. Tested it today and I can no longer reproduce the
> > issues
> > when switching between PWM and GPIO modes.
> > It did not apply cleanly on the current mainline or for-next
> > branch, so
> > I'll send a fixed up version of the patch with my Tested-by tag
> > shortly.
> 
> Awesome, thank you!
> 
> > I noticed an unrelated issue when disabling and enabling the
> > channel
> > though, for which I will either send a patch or maybe try to
> > convert the
> > driver to the atomic API first and then look if it is still a
> > problem.
> > (Issue is that if you disable the channel, the LED_OFF counter is
> > cleared, which means you have to reconfigure the duty cycle after
> > reenabling. It's probably better if only the FULL_OFF bit is
> > toggled in
> > enable/disable as it has precedence over the others anyway and then
> > the
> > previous state would not be changed..?)
> 
> Converting to the atomic API would certainly be beneficial because it
> gives you much more control over what you want to program to
> hardware.
> 
> Other than that, yes, if ->enable() called after ->disable() doesn't
> put
> the PWM into the same state that it was before ->disable(), then
> that'd
> be a bug. From what you're saying this will make the driver only work
> if
> there's a ->config() call after ->disable(). That's wrong.
> 
> So yes, setting only that LED_FULL bit in ->disable() and clearing it
> in
> ->enable() sounds like a better option than the current
> implementation.

I think what you're seeing is the same bug that prompted me to send my
patchset - patch 4/4 moves to the atomic API and changes the logic to
fix the interaction between config() and enable()/disable(). As it
seems like 2/4 will have to be reverted though and my 4/4 depends on
the cleanup of 2/4 and 3/4 (and 3/4 and 4/4 still need some work as
well), feel free to have a jab at the issue yourself, as I don't know
when I'll be able to get back to it.

Thanks,
Matthias


> 
> Thierry
> 
> * Unknown Key
> * 0x7F3EB3A1
Sven Van Asbroeck April 3, 2020, 11:47 p.m. UTC | #13
On Wed, Feb 26, 2020 at 12:04 PM Matthias Schiffer
<matthias.schiffer@ew.tq-group.com> wrote:
>
> >  - Is this racy somehow (i.e. can it happen that when going from
> >    duty_cycle/period = 1000/5000 to duty_cycle/period = 4000/10000
> > the
> >    output is 1000/10000 (or 4000/5000) for one cycle)?
>
> It currently is racy. It should be possible to fix that either by
> updating all 4 registers in a single I2C write, or by using the "update
> on ACK" mode which requires all 4 registers to be updated before the
> new setting is applied (I'm not sure if this mode would require using a
> single I2C write as well though).

Matthias, did you verify experimentally that changing the period is racy?

Looking at the datasheet and driver code, it shouldn't be. This is because
the OFF time is set as a proportion of the counter range. When the period
changes from 5000 to 10000, then 5000*20%/5000 and 10000*20%/10000
will result in the same 20% ratio (disregarding rounding errors).

This is documented at the beginning of the driver:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pwm/pwm-pca9685.c?h=v5.6#n25

Should we move that comment to pwm_config(), so future versions of
ourselves won't overlook it?
Sven Van Asbroeck April 3, 2020, 11:50 p.m. UTC | #14
On Mon, Mar 30, 2020 at 11:12 AM Clemens Gruber
<clemens.gruber@pqgruber.com> wrote:
>
> On Wed, Feb 26, 2020 at 06:03:02PM +0100, Matthias Schiffer wrote:
> > As it turns out, this driver is broken in yet another way I didn't find
> > before: For changing the global prescaler the chip needs to be put into
> > sleep mode, but the driver doesn't follow the restart sequence
> > described in the datasheet when waking it back up. In consequence,
> > changing the period of one PWM does not only modify the period of all
> > PWMs (which is bad enough, but can't be avoided with this hardware),
> > but it also leaves all PWMs disabled...
>
> I am unable to reproduce this: If I set a specific duty cycle on a
> channel and then change the period, the channel stays active.
> I can see the brightness of an LED decrease if I increase the period.

What happens when pwm channels 0 and 1 are both enabled, and
you change the pwm period of channel 0. Does channel 1 remain
on?
Clemens Gruber April 4, 2020, 5:35 p.m. UTC | #15
Hi,

On Fri, Apr 03, 2020 at 07:50:07PM -0400, Sven Van Asbroeck wrote:
> On Mon, Mar 30, 2020 at 11:12 AM Clemens Gruber
> <clemens.gruber@pqgruber.com> wrote:
> >
> > On Wed, Feb 26, 2020 at 06:03:02PM +0100, Matthias Schiffer wrote:
> > > As it turns out, this driver is broken in yet another way I didn't find
> > > before: For changing the global prescaler the chip needs to be put into
> > > sleep mode, but the driver doesn't follow the restart sequence
> > > described in the datasheet when waking it back up. In consequence,
> > > changing the period of one PWM does not only modify the period of all
> > > PWMs (which is bad enough, but can't be avoided with this hardware),
> > > but it also leaves all PWMs disabled...
> >
> > I am unable to reproduce this: If I set a specific duty cycle on a
> > channel and then change the period, the channel stays active.
> > I can see the brightness of an LED decrease if I increase the period.
> 
> What happens when pwm channels 0 and 1 are both enabled, and
> you change the pwm period of channel 0. Does channel 1 remain
> on?

Yes. Both channels remain on.

Let's say I configure a period of 5ms for both channels 0 and 1, as well
as a duty cycle of 4ms, meaning a relative duty cycle of 80%.
If I then increase the period of channel 0 to 10ms, there will be a
relative duty cycle of 40% on channel 0, but channel 1 will remain at a
relative duty cycle of 80%.
This is due to the relative nature of the internal ON/OFF times. For
the channel with the period change however, we recalculate the duty_ns
to period_ns ratio and reprogram the ON/OFF registers, because the user
might have already given us a different duty cycle in .config / .apply.

As the user is setting the duty cycle in nanoseconds, it makes sense
that the relative duty cycle decreases in an absolute period increase.
As for the behavior that the other channels remain at the same relative
duty cycle: Not sure how we can avoid this, other than reprogramming all
15 other channels if one of them is changed and that's not really
acceptable, I think.

Clemens
Sven Van Asbroeck April 4, 2020, 8:17 p.m. UTC | #16
On Sat, Apr 4, 2020 at 1:35 PM Clemens Gruber
<clemens.gruber@pqgruber.com> wrote:
>
> As the user is setting the duty cycle in nanoseconds, it makes sense
> that the relative duty cycle decreases in an absolute period increase.
> As for the behavior that the other channels remain at the same relative
> duty cycle: Not sure how we can avoid this, other than reprogramming all
> 15 other channels if one of them is changed and that's not really
> acceptable, I think.

Thank you for the explanation, Clemens.

Yes, it does make sense that the relative duty cycle changes when we change
the period. A relative duty cycle of duty_cycle / period is what the user would
expect to see.

It also kind-of makes sense that the relative duty cycles of the other
pwm channels
do not change: after all, the user is not touching them, so would not expect
them to change.

However, the following does not make sense to me. Imagine pwm0 and pwm1
are both active and at 50%: period=5000000, duty_cycle=2500000. Then, change
the period on pwm0:

$ echo 10000000 > pwm0/period

Then pwm0 gets dimmer (makes sense) and pwm1 keeps the same relative duty
cycle (makes sense). However, if we now read out sysfs for pwm1, we get:

$ echo pwm1/period
5000000 (wrong!)
$ echo pwm1/duty_cycle
2500000 (wrong! although relative duty cycle is correct)

Although the pwm1 period has changed, the API calls do not reflect this.
This makes it next to impossible for users to know what the current period
is set to.

Moving to the atomic API won't help, because .get_state is called only
once, when the chip is registered.

It does look like we have a square peg (this chip) in a round hole (the
standard assumptions the pwm core makes) ?
Thierry Reding April 6, 2020, 9:51 a.m. UTC | #17
On Sat, Apr 04, 2020 at 04:17:00PM -0400, Sven Van Asbroeck wrote:
> On Sat, Apr 4, 2020 at 1:35 PM Clemens Gruber
> <clemens.gruber@pqgruber.com> wrote:
> >
> > As the user is setting the duty cycle in nanoseconds, it makes sense
> > that the relative duty cycle decreases in an absolute period increase.
> > As for the behavior that the other channels remain at the same relative
> > duty cycle: Not sure how we can avoid this, other than reprogramming all
> > 15 other channels if one of them is changed and that's not really
> > acceptable, I think.
> 
> Thank you for the explanation, Clemens.
> 
> Yes, it does make sense that the relative duty cycle changes when we change
> the period. A relative duty cycle of duty_cycle / period is what the user would
> expect to see.
> 
> It also kind-of makes sense that the relative duty cycles of the other
> pwm channels
> do not change: after all, the user is not touching them, so would not expect
> them to change.
> 
> However, the following does not make sense to me. Imagine pwm0 and pwm1
> are both active and at 50%: period=5000000, duty_cycle=2500000. Then, change
> the period on pwm0:
> 
> $ echo 10000000 > pwm0/period
> 
> Then pwm0 gets dimmer (makes sense) and pwm1 keeps the same relative duty
> cycle (makes sense). However, if we now read out sysfs for pwm1, we get:
> 
> $ echo pwm1/period
> 5000000 (wrong!)
> $ echo pwm1/duty_cycle
> 2500000 (wrong! although relative duty cycle is correct)
> 
> Although the pwm1 period has changed, the API calls do not reflect this.
> This makes it next to impossible for users to know what the current period
> is set to.
> 
> Moving to the atomic API won't help, because .get_state is called only
> once, when the chip is registered.

The .get_state() callback is actually called when the PWM is requested,
which could be much later than when the chip is registered. That doesn't
change the fact that it would be useless for this case, though.

> It does look like we have a square peg (this chip) in a round hole (the
> standard assumptions the pwm core makes) ?

There are other chips where a single period is shared across multiple
PWM channels. Typically what we do there is once a period is configured
for a given channel, all subsequent PWM channel configurations must use
the same period, or otherwise the driver will return an error code.

See for example:

  - stm32_pwm_config() in drivers/pwm/pwm-stm32.c
  - lpc18xx_pwm_config() in drivers/pwm/pwm-lpc18xx-sct.c
  - pwm_imx_tpm_apply_hw() in drivers/pwm/pwm-imx-tpm.c
  - fsl_pwm_apply_config() in drivers/pwm/pwm-fsl-ftm.c

The rationale behind that is that we must not change a PWM configuration
without a consumer having explicitly requested it.

It seems like PCA9685 is somewhere halfway between in that it will
actually keep the same duty-cycle/period ratio, but that doesn't mean it
is automatically okay to do this. The problem is that the duty-cycle to
period ratio is only relevant in some cases. If all you care about is
the power output of the PWM signal, which admittedly seems to be about
95% of all cases, then yes, this behaviour would be okay. But what if we
have a consumer that relies on a particular width of the PWM pulse in
absolute terms rather than relative to the period?

Thierry
Matthias Schiffer April 7, 2020, 1 p.m. UTC | #18
On Mon, 2020-04-06 at 11:51 +0200, Thierry Reding wrote:
> 
> On Sat, Apr 04, 2020 at 04:17:00PM -0400, Sven Van Asbroeck wrote:
> > 
> > It does look like we have a square peg (this chip) in a round hole
> > (the
> > standard assumptions the pwm core makes) ?
> 
> There are other chips where a single period is shared across multiple
> PWM channels. Typically what we do there is once a period is
> configured
> for a given channel, all subsequent PWM channel configurations must
> use
> the same period, or otherwise the driver will return an error code.
> 
> See for example:
> 
>   - stm32_pwm_config() in drivers/pwm/pwm-stm32.c
>   - lpc18xx_pwm_config() in drivers/pwm/pwm-lpc18xx-sct.c
>   - pwm_imx_tpm_apply_hw() in drivers/pwm/pwm-imx-tpm.c
>   - fsl_pwm_apply_config() in drivers/pwm/pwm-fsl-ftm.c
> 
> The rationale behind that is that we must not change a PWM
> configuration
> without a consumer having explicitly requested it.

These implementations don't deal with the issue in a consistent way
either though:

1) stm32_pwm_config() only checks for channels that are actually
enabled, regardless if they're requested

2) lpc18xx_pwm_config() checks requested channels instead

"2)" seems more correct to me, as the parameters of a requested channel
can't be changed by another user, but it seems to prevent certain
sequences. I don't have a good grasp of the the usual PWM request
control flow - is it correct that the PWM state is not updated with the
PWM args from OF when the PWM is requested? I only see
pwm_adjust_config() doing something like that, which is not used in
many places (and nothing preventing races) - but I might be overlooking
something.

Meaning, is it possible that two drivers request PWMs from the same
pwmchip of type "2)" (or even a single driver using two PWMs) without
configuring them right away may get into a situation where neither can
set up a period at all even when all users want to use the same period?

Matthias
Matthias Schiffer April 7, 2020, 2:46 p.m. UTC | #19
On Fri, 2020-04-03 at 19:47 -0400, Sven Van Asbroeck wrote:
> On Wed, Feb 26, 2020 at 12:04 PM Matthias Schiffer
> <matthias.schiffer@ew.tq-group.com> wrote:
> > 
> > >  - Is this racy somehow (i.e. can it happen that when going from
> > >    duty_cycle/period = 1000/5000 to duty_cycle/period =
> > > 4000/10000
> > > the
> > >    output is 1000/10000 (or 4000/5000) for one cycle)?
> > 
> > It currently is racy. It should be possible to fix that either by
> > updating all 4 registers in a single I2C write, or by using the
> > "update
> > on ACK" mode which requires all 4 registers to be updated before
> > the
> > new setting is applied (I'm not sure if this mode would require
> > using a
> > single I2C write as well though).
> 
> Matthias, did you verify experimentally that changing the period is
> racy?
> 
> Looking at the datasheet and driver code, it shouldn't be. This is
> because
> the OFF time is set as a proportion of the counter range. When the
> period
> changes from 5000 to 10000, then 5000*20%/5000 and 10000*20%/10000
> will result in the same 20% ratio (disregarding rounding errors).
> 
> This is documented at the beginning of the driver:
> 
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pwm/pwm-pca9685.c?h=v5.6#n25
> 
> Should we move that comment to pwm_config(), so future versions of
> ourselves won't overlook it?

You are right, this results in the same ratio - the absolute on/off
times may be wrong for a moment though when the period is changed.

In the attached image, I have changed the period, but kept the absolute
duty cycle fixed (note: this is in inverted mode, so the duty cycle
controls the low time). It can be seen that after a too long high time
(chip is in sleep mode) one period with too long low time follows (new
period, old relative duty cycle), before the counter is reprogrammed to
match the previous absolute duty cycle.

I don't care too much about the details of the behaviour, as I only
control LEDs using this chip and don't need to change the period after
initial setup, but we should accurately document the shortcomings of
the hardware and the driver (when we have decided how to fix some of
the driver issues).

Matthias
Matthias Schiffer April 8, 2020, 8 a.m. UTC | #20
On Tue, 2020-04-07 at 16:46 +0200, Matthias Schiffer wrote:
> On Fri, 2020-04-03 at 19:47 -0400, Sven Van Asbroeck wrote:
> > On Wed, Feb 26, 2020 at 12:04 PM Matthias Schiffer
> > <matthias.schiffer@ew.tq-group.com> wrote:
> > > 
> > > >  - Is this racy somehow (i.e. can it happen that when going
> > > > from
> > > >    duty_cycle/period = 1000/5000 to duty_cycle/period =
> > > > 4000/10000
> > > > the
> > > >    output is 1000/10000 (or 4000/5000) for one cycle)?
> > > 
> > > It currently is racy. It should be possible to fix that either by
> > > updating all 4 registers in a single I2C write, or by using the
> > > "update
> > > on ACK" mode which requires all 4 registers to be updated before
> > > the
> > > new setting is applied (I'm not sure if this mode would require
> > > using a
> > > single I2C write as well though).
> > 
> > Matthias, did you verify experimentally that changing the period is
> > racy?
> > 
> > Looking at the datasheet and driver code, it shouldn't be. This is
> > because
> > the OFF time is set as a proportion of the counter range. When the
> > period
> > changes from 5000 to 10000, then 5000*20%/5000 and 10000*20%/10000
> > will result in the same 20% ratio (disregarding rounding errors).
> > 
> > This is documented at the beginning of the driver:
> > 
> 
> 
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pwm/pwm-pca9685.c?h=v5.6#n25
> > 
> > Should we move that comment to pwm_config(), so future versions of
> > ourselves won't overlook it?
> 
> You are right, this results in the same ratio - the absolute on/off
> times may be wrong for a moment though when the period is changed.
> 
> In the attached image, I have changed the period, but kept the
> absolute
> duty cycle fixed (note: this is in inverted mode, so the duty cycle
> controls the low time). It can be seen that after a too long high
> time
> (chip is in sleep mode) one period with too long low time follows
> (new
> period, old relative duty cycle), before the counter is reprogrammed
> to
> match the previous absolute duty cycle.
> 
> I don't care too much about the details of the behaviour, as I only
> control LEDs using this chip and don't need to change the period
> after
> initial setup, but we should accurately document the shortcomings of
> the hardware and the driver (when we have decided how to fix some of
> the driver issues).
> 
> Matthias

And another kind of race condition that should be possible, although I
haven't seen it in practice:

High and low bits of the OFF counter currently aren't programmed
atomically, so with unlucky timing we get a cycle using new lower 8
bits with old upper 4 bits of the duty cycle.
Sven Van Asbroeck April 9, 2020, 11:42 a.m. UTC | #21
On Mon, Apr 6, 2020 at 5:51 AM Thierry Reding <thierry.reding@gmail.com> wrote:
>
> There are other chips where a single period is shared across multiple
> PWM channels. Typically what we do there is once a period is configured
> for a given channel, all subsequent PWM channel configurations must use
> the same period, or otherwise the driver will return an error code.

Thank you for the clarification Thierry. Do you think this method would be
appropriate for this chip?
diff mbox series

Patch

diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index b07bdca3d510..19ac97108a64 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -69,7 +69,6 @@ 
 struct pca9685 {
 	struct pwm_chip chip;
 	struct regmap *regmap;
-	int duty_ns;
 	int period_ns;
 #if IS_ENABLED(CONFIG_GPIOLIB)
 	struct mutex lock;
@@ -272,8 +271,6 @@  static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 		}
 	}
 
-	pca->duty_ns = duty_ns;
-
 	if (duty_ns < 1) {
 		if (pwm->hwpwm >= PCA9685_MAXCHAN)
 			reg = PCA9685_ALL_LED_OFF_H;
@@ -449,7 +446,6 @@  static int pca9685_pwm_probe(struct i2c_client *client,
 			ret);
 		return ret;
 	}
-	pca->duty_ns = 0;
 	pca->period_ns = PCA9685_DEFAULT_PERIOD;
 
 	i2c_set_clientdata(client, pca);