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

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

 



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.

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

Attachment: signature.asc
Description: PGP signature


[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