Re: [PATCH v4] gpio: pca953x: Add Maxim MAX7313 PWM support

From: Miquel Raynal
Date: Mon Dec 16 2019 - 04:14:24 EST


Hi Uwe,

Uwe Kleine-KÃnig <u.kleine-koenig@xxxxxxxxxxxxxx> wrote on Mon, 16 Dec
2019 09:54:24 +0100:

> Hi Miquel,
>
> On Mon, Dec 16, 2019 at 09:39:55AM +0100, Miquel Raynal wrote:
> > Uwe Kleine-KÃnig <u.kleine-koenig@xxxxxxxxxxxxxx> wrote on Thu, 12 Dec
> > 2019 22:14:34 +0100:
> >
> > > On Fri, Nov 29, 2019 at 08:10:23PM +0100, Miquel Raynal wrote:
> > > > +static int max7313_pwm_apply(struct pwm_chip *chip,
> > > > + struct pwm_device *pwm,
> > > > + const struct pwm_state *state)
> > > > +{
> > > > + struct max7313_pwm *max_pwm = to_max7313_pwm(chip);
> > > > + struct pca953x_chip *pca_chip = to_pca953x(max_pwm);
> > > > + unsigned int intensity, active;
> > > > + int ret = 0;
> > > > +
> > > > + if (!state->enabled ||
> > > > + state->period < PWM_PERIOD_NS ||
> > > > + state->polarity != PWM_POLARITY_NORMAL)
> > > > + return -EINVAL;
> > > > +
> > > > + /* Convert the duty-cycle to be in the [0;16] range */
> > > > + intensity = DIV_ROUND_DOWN_ULL(state->duty_cycle,
> > > > + state->period / PWM_DC_STATES);
> > >
> > > this looks wrong. The period must not have an influence on the selection
> > > of the duty_cycle (other than duty_cycle < period). So given that the
> > > period is fixed at 31.25 ms, the result of
> > >
> > > pwm_apply_state(pwm, { .enabled = 1, .period = 2s, .duty_cycle = 16 ms })
> > >
> > > must be the same as for
> > >
> > > pwm_apply_state(pwm, { .enabled = 1, .period = 3s, .duty_cycle = 16 ms })
> >
> > This was not my understanding of our previous discussion and, again, I
> > don't really understand this choice but if the framework works like
> > that we shall probably continue with this logic. However, all this
> > should probably be explained directly in the kernel doc of each core
> > helper, that would help a lot.
>
> I agree. There is a pending doc patch and once Thierry applies it I plan
> to add these details.

Great!

>
> The idea is to make the policy simple (both computational and to
> understand). With each policy there are strange corner cases, so for
> sure you can come up with examples that yield results that are way off
> from the request. The idea is that drivers all implement the same policy
> and then create helper functions to match the different consumer needs.

I fully understand and comply with this.

The above logic was not the first one that would have came off my mind
but it is 100% true that it keeps the computation easy (= less bugs, =
quicker) and has probably been much more pondered than mine.

>
> > > . Also dividing by a division looses precision.
> >
> > I agree but this is a problem with fixed point calculations. Maybe I
> > could first multiply the numerator by a factor of 100 or 1000 to
> > minimize the error. Do you have a better idea?
>
> intensity = DIV_ROUND_DOWN_ULL((unsigned long long)state->duty_cycle * PWM_DC_STATES, state->period);
>
> should be both more exact and cheaper to calculate. (But this is
> somewhat moot given that state->period shouldn't be there.)

I feel stupid now - let's put it on monday mood. Of course it's more
accurate this way.

> (And in general you have to care for overflowing, but I think that's not
> a problem in practise here as struct pwm_state::duty_cycle is an int
> (still), and PWM_DC_STATES is small.)

Do you plan to change duty_cycle type?

Right now the multiplication cannot be over 500 000 which is totally
okay for a s32 but not for a s16 for instance.

> > > > +static void max7313_pwm_get_state(struct pwm_chip *chip,
> > > > + struct pwm_device *pwm,
> > > > + struct pwm_state *state)
> > > > +{
> > > > + struct max7313_pwm *max_pwm = to_max7313_pwm(chip);
> > > > + struct pca953x_chip *pca_chip = to_pca953x(max_pwm);
> > > > + u8 intensity;
> > > > +
> > > > + state->enabled = true;
> > > > + state->period = PWM_PERIOD_NS;
> > > > + state->polarity = PWM_POLARITY_NORMAL;
> > > > +
> > > > + intensity = max7313_pwm_get_intensity(pca_chip, pwm->hwpwm);
> > > > + state->duty_cycle = intensity * PWM_OFFSET_NS;
> > >
> > > I think you need to take into account the blink phase bit.
> >
> > It is already done by .pwm_get_intensity itself, no?
>
> Ah, possible, I admin I didn't look deep enough to catch it there.

No problem, thanks anyway for all the careful reviews!

Thanks,
MiquÃl