Re: [PATCH v4 2/2] pwm: Add Loongson PWM controller support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Uwe:

On Sat, Jul 6, 2024 at 9:25 PM Uwe Kleine-König
<u.kleine-koenig@xxxxxxxxxxxx> wrote:
>
> Hello Binbin,
>
> On Sat, Jul 06, 2024 at 03:08:30PM +0600, Binbin Zhou wrote:
> > Hi Uwe:
> >
> > Thanks for your reply.
> >
> > On Sat, Jul 6, 2024 at 5:26 AM Uwe Kleine-König
> > <u.kleine-koenig@xxxxxxxxxxxx> wrote:
> > >
> > > Hello,
> > >
> > > On Mon, May 27, 2024 at 03:51:12PM +0800, Binbin Zhou wrote:
> > > > +static int pwm_loongson_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> > > > +                           const struct pwm_state *state)
> > > > +{
> > > > +     int ret;
> > > > +     u64 period, duty_cycle;
> > > > +     bool enabled = pwm->state.enabled;
> > > > +
> > > > +     period = min(state->period, NANOHZ_PER_HZ);
> > > > +     duty_cycle = min(state->duty_cycle, NANOHZ_PER_HZ);
> > > > +
> > > > +     if (state->polarity != pwm->state.polarity) {
> > > > +             if (enabled) {
> > > > +                     pwm_loongson_disable(chip, pwm);
> > > > +                     enabled = false;
> > > > +             }
> > > > +
> > > > +             ret = pwm_loongson_set_polarity(chip, pwm, state->polarity);
> > > > +             if (ret)
> > > > +                     return ret;
> > > > +     }
> > > > +
> > > > +     if (!state->enabled) {
> > > > +             if (enabled)
> > > > +                     pwm_loongson_disable(chip, pwm);
> > > > +             return 0;
> > > > +     }
> > >
> > > Given that the configured polarity isn't relevant for a disabled PWM, I
> > > suggest to swap these two if blocks. However then you have to be a bit
> > > more careful for the polarity check because otherwise the following
> > > series of commands yields wrong results:
> > >
> > >         pwm_apply_might_sleep(pwm, {.duty_cycle = D, .period = P, .polarity = PWM_POLARITY_NORMAL, .enabled = true});
> > >         pwm_apply_might_sleep(pwm, {.duty_cycle = D, .period = P, .polarity = PWM_POLARITY_INVERSED, .enabled = false});
> > >         pwm_apply_might_sleep(pwm, {.duty_cycle = D, .period = P, .polarity = PWM_POLARITY_INVERSED, .enabled = true});
> > >
> >
> > Yes, we'd better make sure pwm is enabled first.
> > I will swap the two if blocks.
> >
> > > > +     ret = pwm_loongson_config(chip, pwm, duty_cycle, period);
> > > > +     if (ret)
> > > > +             return ret;
> > > > +
> > > > +     if (!enabled)
> > > > +             ret = pwm_loongson_enable(chip, pwm);
> > > > +
> > > > +     return ret;
> > > > +}
> > > > +
> > > > +static int pwm_loongson_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
> > > > +                               struct pwm_state *state)
> > > > +{
> > > > +     u32 duty, period, ctrl;
> > > > +     struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip);
> > > > +
> > > > +     /* duty_cycle = ddata->duty * NSEC_PER_SEC / ddata->clk_rate */
> > > > +     duty = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_DUTY);
> > > > +     state->duty_cycle = mul_u64_u64_div_u64(duty, NSEC_PER_SEC, ddata->clk_rate);
> > > > +
> > > > +     /* period = ddata->period * NSEC_PER_SEC / ddata->clk_rate */
> > > > +     period = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_PERIOD);
> > > > +     state->period = mul_u64_u64_div_u64(period, NSEC_PER_SEC, ddata->clk_rate);
> > > > +
> > > > +     ctrl = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL);
> > > > +     state->polarity = (ctrl & LOONGSON_PWM_CTRL_INVERT) ? PWM_POLARITY_INVERSED :
> > > > +                       PWM_POLARITY_NORMAL;
> > > > +     state->enabled = (ctrl & LOONGSON_PWM_CTRL_EN) ? true : false;
> > > > +
> > > > +     return 0;
> > >
> > > You didn't test extensively with PWM_DEBUG enabled, right? You need to
> > > round up the divisions here otherwise you get strange rounding results:
> > >
> > > Consider ddata->clk_rate = 1594323. When a state with .period = 20000 is
> > > applied, LOONGSON_PWM_REG_PERIOD is assigned 31.
> > > Calling .get_state() in this situation gives .period = 19443. Reapplying
> > > .period = 19443 results in LOONGSON_PWM_REG_PERIOD := 30. Iterating this
> > > further yields:
> > >
> > >  - .period = 18816
> > >  - LOONGSON_PWM_REG_PERIOD := 29
> > >  - .period = 18189
> > >  - LOONGSON_PWM_REG_PERIOD := 28
> > >  - ...
> > >
> > Yes, I'm very sorry I didn't do extensive testing with PWM_DEBUG enabled.
> > Here I do need to use DIV64_U64_ROUND_UP().
> >
> > Below:
> >
> >         /* duty_cycle = ddata->duty * NSEC_PER_SEC / ddata->clk_rate */
> >         duty = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_DUTY);
> >         state->duty_cycle = DIV64_U64_ROUND_UP((u64)duty *
> > NSEC_PER_SEC, ddata->clk_rate);
> >
> >         /* period = ddata->period * NSEC_PER_SEC / ddata->clk_rate */
> >         period = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_PERIOD);
> >         state->period = DIV64_U64_ROUND_UP((u64)period * NSEC_PER_SEC,
> > ddata->clk_rate);
> >
> >
> > I'd also like to ask which tests I still need to do to make sure the
> > driver is more complete?
>
> There is no official lower limit on tests. But the goal is that your
> driver behaves correctly and some of the typical errors can be catched
> by enabling PWM_DEBUG and then doing the following tests for sensible
> values of A and B:
>
>         # Sequence of increasing periods
>         for period in A ... B:
>                 configure 0/period
>
>         # Sequence of decreasing periods
>         for period in A ... B:
>                 configure 0/(B + A - period)
>
>         for period in some set:
>                 # Sequence of increasing duty length
>                 for duty_cycle in 0 ... period:
>                         configure duty_cycle/period
>
>                 # Sequence of decreasing duty length
>                 for duty_cycle in 0 ... period:
>                         configure (period - duty_cycle)/period
>
> That should give you a good coverage.

Thanks for your advice.
I would use this approach to test extensively, trying to make sure
there are no typical errors.

Thanks.
Binbin
>
> The idea of extensive testing on your side is also: Review capacities
> are a scarce resource and you suffer from that because it takes some
> patience between you sending a patch and a maintainer coming around to
> review it. If your code is well tested, there is less for the
> maintainers to find and so I save time because there are less revisions
> and they mature quicker. The direct consequence for you and the others
> waiting for my attention should be obvious. (This also means: If you
> look into other's patch submissions and point out the things you already
> learned to them, you also save maintainer time and so might get their
> attention earlier yourself.)
>
> I'm working on a bigger change for the pwm subsystem. One of the results
> will be a new userspace API. I intend to create a test program that does
> the above tests, so in the foreseeable future testing should get easier.
> (In return writing a lowlevel driver will become a bit harder, as the
> requirements for precision increase.)
>
> Best regards
> Uwe





[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux