diff mbox series

[v2,2/2] pwm: sifive: change the PWM controlled LED algorithm

Message ID 20230130093229.27489-3-nylon.chen@sifive.com
State Changes Requested
Headers show
Series Change PWM-controlled LED pin active mode and algorithm | expand

Commit Message

Nylon Chen Jan. 30, 2023, 9:32 a.m. UTC
The `frac` variable represents the pulse inactive time, and the result of
this algorithm is the pulse active time. Therefore, we must reverse the
result.

The reference is SiFive FU740-C000 Manual[0].

[0]: https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf

Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
---
 drivers/pwm/pwm-sifive.c | 1 +
 1 file changed, 1 insertion(+)

Comments

Geert Uytterhoeven Jan. 30, 2023, 9:53 a.m. UTC | #1
Hi Nylon,

On Mon, Jan 30, 2023 at 10:32 AM Nylon Chen <nylon.chen@sifive.com> wrote:
> The `frac` variable represents the pulse inactive time, and the result of
> this algorithm is the pulse active time. Therefore, we must reverse the
> result.
>
> The reference is SiFive FU740-C000 Manual[0].
>
> [0]: https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf
>
> Signed-off-by: Nylon Chen <nylon.chen@sifive.com>

Thanks for your patch!

> --- a/drivers/pwm/pwm-sifive.c
> +++ b/drivers/pwm/pwm-sifive.c
> @@ -158,6 +158,7 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>         frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
>         /* The hardware cannot generate a 100% duty cycle */
>         frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
> +       frac = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;

Shouldn't the inversion be done before the hardware limitation fixup?

>
>         mutex_lock(&ddata->lock);
>         if (state->period != ddata->approx_period) {

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Uwe Kleine-König Jan. 30, 2023, 10:17 a.m. UTC | #2
On Mon, Jan 30, 2023 at 05:32:29PM +0800, Nylon Chen wrote:
> The `frac` variable represents the pulse inactive time, and the result of
> this algorithm is the pulse active time. Therefore, we must reverse the
> result.
> 
> The reference is SiFive FU740-C000 Manual[0].
> 
> [0]: https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf
> 
> Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
> ---
>  drivers/pwm/pwm-sifive.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
> index 62b6acc6373d..a5eda165d071 100644
> --- a/drivers/pwm/pwm-sifive.c
> +++ b/drivers/pwm/pwm-sifive.c
> @@ -158,6 +158,7 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>  	frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
>  	/* The hardware cannot generate a 100% duty cycle */
>  	frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
> +	frac = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;

The same problem exists in pwm_sifive_get_state(), doesn't it?

As fixing this is an interruptive change anyhow, this is the opportunity
to align the driver to the rules tested by PWM_DEBUG.

The problems I see in the driver (only checked quickly, so I might be
wrong):

 - state->period != ddata->approx_period isn't necessarily a problem. If
   state->period > ddata->real_period that's fine and the driver should
   continue

 - frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
   is wrong for two reasons:
   it should round down and use the real period.

Best regards
Uwe
Nylon Chen Feb. 1, 2023, 8:51 a.m. UTC | #3
Hi Geert,

Thanks for your reply.

Geert Uytterhoeven <geert@linux-m68k.org> 於 2023年1月30日 週一 下午5:53寫道:
>
> Hi Nylon,
>
> On Mon, Jan 30, 2023 at 10:32 AM Nylon Chen <nylon.chen@sifive.com> wrote:
> > The `frac` variable represents the pulse inactive time, and the result of
> > this algorithm is the pulse active time. Therefore, we must reverse the
> > result.
> >
> > The reference is SiFive FU740-C000 Manual[0].
> >
> > [0]: https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf
> >
> > Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
>
> Thanks for your patch!
>
> > --- a/drivers/pwm/pwm-sifive.c
> > +++ b/drivers/pwm/pwm-sifive.c
> > @@ -158,6 +158,7 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> >         frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
> >         /* The hardware cannot generate a 100% duty cycle */
> >         frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
> > +       frac = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
>
> Shouldn't the inversion be done before the hardware limitation fixup?
I think your inference is correct, I will use it.

thanks a lot.
>
> >
> >         mutex_lock(&ddata->lock);
> >         if (state->period != ddata->approx_period) {
>
> Gr{oetje,eeting}s,
>
>                         Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
>                                 -- Linus Torvalds
Nylon Chen Feb. 1, 2023, 8:56 a.m. UTC | #4
Hi Uwe,

Thanks for your reply.

Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 於 2023年1月30日 週一 下午6:17寫道:
>
> On Mon, Jan 30, 2023 at 05:32:29PM +0800, Nylon Chen wrote:
> > The `frac` variable represents the pulse inactive time, and the result of
> > this algorithm is the pulse active time. Therefore, we must reverse the
> > result.
> >
> > The reference is SiFive FU740-C000 Manual[0].
> >
> > [0]: https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf
> >
> > Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
> > ---
> >  drivers/pwm/pwm-sifive.c | 1 +
> >  1 file changed, 1 insertion(+)
> >
> > diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
> > index 62b6acc6373d..a5eda165d071 100644
> > --- a/drivers/pwm/pwm-sifive.c
> > +++ b/drivers/pwm/pwm-sifive.c
> > @@ -158,6 +158,7 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> >       frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
> >       /* The hardware cannot generate a 100% duty cycle */
> >       frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
> > +     frac = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
>
> The same problem exists in pwm_sifive_get_state(), doesn't it?
>
> As fixing this is an interruptive change anyhow, this is the opportunity
> to align the driver to the rules tested by PWM_DEBUG.
>
> The problems I see in the driver (only checked quickly, so I might be
> wrong):
>
>  - state->period != ddata->approx_period isn't necessarily a problem. If
>    state->period > ddata->real_period that's fine and the driver should
>    continue
>
>  - frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
>    is wrong for two reasons:
>    it should round down and use the real period.
>
I need a little time to clarify your assumptions. If possible, I will
make similar changes.

e.g.
rounddown(num, state->period);
if (state->period < ddata->approx_period)
    ...

thanks a lot.






> Best regards
> Uwe
>
> --
> Pengutronix e.K.                           | Uwe Kleine-König            |
> Industrial Linux Solutions                 | https://www.pengutronix.de/ |
Nylon Chen Feb. 3, 2023, 8:06 a.m. UTC | #5
Hi Uwe,

Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 於 2023年1月30日 週一 下午6:17寫道:
>
> On Mon, Jan 30, 2023 at 05:32:29PM +0800, Nylon Chen wrote:
> > The `frac` variable represents the pulse inactive time, and the result of
> > this algorithm is the pulse active time. Therefore, we must reverse the
> > result.
> >
> > The reference is SiFive FU740-C000 Manual[0].
> >
> > [0]: https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf
> >
> > Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
> > ---
> >  drivers/pwm/pwm-sifive.c | 1 +
> >  1 file changed, 1 insertion(+)
> >
> > diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
> > index 62b6acc6373d..a5eda165d071 100644
> > --- a/drivers/pwm/pwm-sifive.c
> > +++ b/drivers/pwm/pwm-sifive.c
> > @@ -158,6 +158,7 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> >       frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
> >       /* The hardware cannot generate a 100% duty cycle */
> >       frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
> > +     frac = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
>
> The same problem exists in pwm_sifive_get_state(), doesn't it?
>
> As fixing this is an interruptive change anyhow, this is the opportunity
> to align the driver to the rules tested by PWM_DEBUG.
>
> The problems I see in the driver (only checked quickly, so I might be
> wrong):
>

>  - state->period != ddata->approx_period isn't necessarily a problem. If
>    state->period > ddata->real_period that's fine and the driver should
>    continue
>
>  - frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
>    is wrong for two reasons:
>    it should round down and use the real period.
are you mean state->period is a redundancy variable so we can use
ddata->real_period directly?

it seems reasonable, but I don't get your point, why do we need to
change the algorithm to DIV_ROUND_DOWN_ULL() and change the if-else
condition.

frac = DIV_ROUND_DOWN_ULL(num, ddata->real_period);
if (state->period < ddata->approx_period) {
    ...
}

>
> Best regards
> Uwe
>
> --
> Pengutronix e.K.                           | Uwe Kleine-König            |
> Industrial Linux Solutions                 | https://www.pengutronix.de/ |
Nylon Chen Feb. 21, 2023, 5:54 a.m. UTC | #6
Hi Uwe,

Nylon Chen <nylon.chen@sifive.com> 於 2023年2月3日 週五 下午4:06寫道:
>
> Hi Uwe,
>
> Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 於 2023年1月30日 週一 下午6:17寫道:
> >
> > On Mon, Jan 30, 2023 at 05:32:29PM +0800, Nylon Chen wrote:
> > > The `frac` variable represents the pulse inactive time, and the result of
> > > this algorithm is the pulse active time. Therefore, we must reverse the
> > > result.
> > >
> > > The reference is SiFive FU740-C000 Manual[0].
> > >
> > > [0]: https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf
> > >
> > > Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
> > > ---
> > >  drivers/pwm/pwm-sifive.c | 1 +
> > >  1 file changed, 1 insertion(+)
> > >
> > > diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
> > > index 62b6acc6373d..a5eda165d071 100644
> > > --- a/drivers/pwm/pwm-sifive.c
> > > +++ b/drivers/pwm/pwm-sifive.c
> > > @@ -158,6 +158,7 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> > >       frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
> > >       /* The hardware cannot generate a 100% duty cycle */
> > >       frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
> > > +     frac = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
> >
> > The same problem exists in pwm_sifive_get_state(), doesn't it?
> >
> > As fixing this is an interruptive change anyhow, this is the opportunity
> > to align the driver to the rules tested by PWM_DEBUG.
> >
> > The problems I see in the driver (only checked quickly, so I might be
> > wrong):
> >
>
> >  - state->period != ddata->approx_period isn't necessarily a problem. If
> >    state->period > ddata->real_period that's fine and the driver should
> >    continue
> >
> >  - frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
> >    is wrong for two reasons:
> >    it should round down and use the real period.
I have some results from my observations regarding the questions you raised.

I don't know if what we are thinking is the same thing.

If my assumptions are different from yours, please let me know. Thanks.
> are you mean state->period is a redundancy variable so we can use
> ddata->real_period directly?
>
> it seems reasonable, but I don't get your point, why do we need to
> change the algorithm to DIV_ROUND_DOWN_ULL() and change the if-else
> condition.
>
> frac = DIV_ROUND_DOWN_ULL(num, ddata->real_period);
> if (state->period < ddata->approx_period) {
>     ...
> }
>
> >
> > Best regards
> > Uwe
> >
> > --
> > Pengutronix e.K.                           | Uwe Kleine-König            |
> > Industrial Linux Solutions                 | https://www.pengutronix.de/ |
Uwe Kleine-König March 1, 2023, 9:20 a.m. UTC | #7
Hello Nylon,

On Wed, Feb 01, 2023 at 04:56:42PM +0800, Nylon Chen wrote:
> Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 於 2023年1月30日 週一 下午6:17寫道:
> > On Mon, Jan 30, 2023 at 05:32:29PM +0800, Nylon Chen wrote:
> > > The `frac` variable represents the pulse inactive time, and the result of
> > > this algorithm is the pulse active time. Therefore, we must reverse the
> > > result.
> > >
> > > The reference is SiFive FU740-C000 Manual[0].
> > >
> > > [0]: https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf
> > >
> > > Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
> > > ---
> > >  drivers/pwm/pwm-sifive.c | 1 +
> > >  1 file changed, 1 insertion(+)
> > >
> > > diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
> > > index 62b6acc6373d..a5eda165d071 100644
> > > --- a/drivers/pwm/pwm-sifive.c
> > > +++ b/drivers/pwm/pwm-sifive.c
> > > @@ -158,6 +158,7 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> > >       frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
> > >       /* The hardware cannot generate a 100% duty cycle */
> > >       frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
> > > +     frac = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
> >
> > The same problem exists in pwm_sifive_get_state(), doesn't it?
> >
> > As fixing this is an interruptive change anyhow, this is the opportunity
> > to align the driver to the rules tested by PWM_DEBUG.
> >
> > The problems I see in the driver (only checked quickly, so I might be
> > wrong):
> >
> >  - state->period != ddata->approx_period isn't necessarily a problem. If
> >    state->period > ddata->real_period that's fine and the driver should
> >    continue
> >
> >  - frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
> >    is wrong for two reasons:
> >    it should round down and use the real period.
> >
> I need a little time to clarify your assumptions. If possible, I will
> make similar changes.
> 
> e.g.
> rounddown(num, state->period);
> if (state->period < ddata->approx_period)
>     ...

the idea is that for a given request apply should do the following to
select the hardware setting:

 - Check polarity, if the hardware doesn't support it, return -EINVAL.
   (A period always starts with the active phase for the duration of
   duty_cycle. For normal polarity active = high.)
 - Pick the biggest period length possible that is not bigger than the
   requested period.
 - For the picked period, select the biggest duty_cycle possible that is
   not bigger than the requested duty_cycle.

Then if possible switch to the selected setting in an atomic step.

Does this clearify your doubts?

Best regards
Uwe
Nylon Chen March 2, 2023, 10:41 a.m. UTC | #8
Hi Uwe

Thanks for your reply.

Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 於 2023年3月1日 週三 下午5:21寫道:
>
> Hello Nylon,
>
> On Wed, Feb 01, 2023 at 04:56:42PM +0800, Nylon Chen wrote:
> > Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 於 2023年1月30日 週一 下午6:17寫道:
> > > On Mon, Jan 30, 2023 at 05:32:29PM +0800, Nylon Chen wrote:
> > > > The `frac` variable represents the pulse inactive time, and the result of
> > > > this algorithm is the pulse active time. Therefore, we must reverse the
> > > > result.
> > > >
> > > > The reference is SiFive FU740-C000 Manual[0].
> > > >
> > > > [0]: https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf
> > > >
> > > > Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
> > > > ---
> > > >  drivers/pwm/pwm-sifive.c | 1 +
> > > >  1 file changed, 1 insertion(+)
> > > >
> > > > diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
> > > > index 62b6acc6373d..a5eda165d071 100644
> > > > --- a/drivers/pwm/pwm-sifive.c
> > > > +++ b/drivers/pwm/pwm-sifive.c
> > > > @@ -158,6 +158,7 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> > > >       frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
> > > >       /* The hardware cannot generate a 100% duty cycle */
> > > >       frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
> > > > +     frac = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
> > >
> > > The same problem exists in pwm_sifive_get_state(), doesn't it?
> > >
> > > As fixing this is an interruptive change anyhow, this is the opportunity
> > > to align the driver to the rules tested by PWM_DEBUG.
> > >
> > > The problems I see in the driver (only checked quickly, so I might be
> > > wrong):
> > >
> > >  - state->period != ddata->approx_period isn't necessarily a problem. If
> > >    state->period > ddata->real_period that's fine and the driver should
> > >    continue
> > >
> > >  - frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
> > >    is wrong for two reasons:
> > >    it should round down and use the real period.
> > >
> > I need a little time to clarify your assumptions. If possible, I will
> > make similar changes.
> >
> > e.g.
> > rounddown(num, state->period);
> > if (state->period < ddata->approx_period)
> >     ...
>
> the idea is that for a given request apply should do the following to
> select the hardware setting:
>
>  - Check polarity, if the hardware doesn't support it, return -EINVAL.
>    (A period always starts with the active phase for the duration of
>    duty_cycle. For normal polarity active = high.)
>  - Pick the biggest period length possible that is not bigger than the
>    requested period.
>  - For the picked period, select the biggest duty_cycle possible that is
>    not bigger than the requested duty_cycle.
>
> Then if possible switch to the selected setting in an atomic step.
>
> Does this clearify your doubts?
I need a little time to clarify your assumptions. Thanks again.
>
> Best regards
> Uwe
>
> --
> Pengutronix e.K.                           | Uwe Kleine-König            |
> Industrial Linux Solutions                 | https://www.pengutronix.de/ |
diff mbox series

Patch

diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
index 62b6acc6373d..a5eda165d071 100644
--- a/drivers/pwm/pwm-sifive.c
+++ b/drivers/pwm/pwm-sifive.c
@@ -158,6 +158,7 @@  static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
 	/* The hardware cannot generate a 100% duty cycle */
 	frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
+	frac = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
 
 	mutex_lock(&ddata->lock);
 	if (state->period != ddata->approx_period) {