RE: [PATCH V4 2/5] pwm: Add i.MX TPM PWM driver support
From: Anson Huang
Date: Mon Mar 18 2019 - 06:08:31 EST
Hi, Uwe
Best Regards!
Anson Huang
> -----Original Message-----
> From: Uwe Kleine-KÃnig [mailto:u.kleine-koenig@xxxxxxxxxxxxxx]
> Sent: 2019å3æ18æ 16:08
> To: Anson Huang <anson.huang@xxxxxxx>
> Cc: thierry.reding@xxxxxxxxx; robh+dt@xxxxxxxxxx; mark.rutland@xxxxxxx;
> shawnguo@xxxxxxxxxx; s.hauer@xxxxxxxxxxxxxx; kernel@xxxxxxxxxxxxxx;
> festevam@xxxxxxxxx; linux@xxxxxxxxxxxxxxx; otavio@xxxxxxxxxxxxxxxx;
> stefan@xxxxxxxx; Leonard Crestez <leonard.crestez@xxxxxxx>;
> schnitzeltony@xxxxxxxxx; jan.tuerk@xxxxxxxxxxx; Robin Gong
> <yibin.gong@xxxxxxx>; linux-pwm@xxxxxxxxxxxxxxx;
> devicetree@xxxxxxxxxxxxxxx; linux-arm-kernel@xxxxxxxxxxxxxxxxxxx; linux-
> kernel@xxxxxxxxxxxxxxx; dl-linux-imx <linux-imx@xxxxxxx>
> Subject: Re: [PATCH V4 2/5] pwm: Add i.MX TPM PWM driver support
>
> On Mon, Mar 18, 2019 at 07:41:02AM +0000, Anson Huang wrote:
> > Hi,Uwe
> > > > + val &= ~PWM_IMX_TPM_SC_CMOD;
> > > > + writel(val, tpm->base + PWM_IMX_TPM_SC);
> > >
> > > As this interrupts the output, please only do it if necessary.
> >
> > OK, will do it ONLY when it is enabled previously.
>
> I think you only need to do that when the value actually changes.
OK, I will save the period/div count and ONLY do it when the value actually changes.
>
> > > > + /* set duty counter */
> > > > + tmp = readl(tpm->base + PWM_IMX_TPM_MOD) &
> > > > +PWM_IMX_TPM_MOD_MOD_MASK;
> > >
> > > I recommend storing this value in driver data.
> >
> > NOT quite understand, as we did NOT use it in other places except the
> > get_state, just reading the register once should be OK there.
>
> I had the impression it is used more than once. Will look again in the next
> iteration. But also note that shadowing the value might already be beneficial
> for a single call site as driver data might occupy more RAM than necessary
> anyhow and reading from RAM is faster than from the hardware's register.
> Probably this is not a fast path, so not worth the optimisation?!
OK, will save it in driver data and avoid accessing register again.
>
> > > I wonder why MSA and MSB are two bits instead of making this a field
> > > of width 2 with 2b10 meaning PWM mode. But maybe it's just me not
> > > understanding the independent semantic of these two bits?
> >
> > I think making them a field makes more sense, but anyway we just
> > follow the RM.
>
> If it makes the driver easier to understand (which I think it does) feel free to
> derivate from the RM. Just add a comment to the definition, then it's fine.
OK, I will change the register definition and a comment for it.
>
> > > Reading the reference manual I'd say in PWM mode the semantic of
> > > ELSA and ELSB is:
> > >
> > > On counter reload set the output to ELSB
> > > On counter match set the output to ELSA
> > >
> > > Noting that in a comment would ease understanding the code here.
> >
> > I added below comment for PWM modes:
> >
> > 137 /*
> > 138 * set polarity (for edge-aligned PWM modes)
> > 139 *
> > 140 * CPWMS MSB:MSA ELSB:ELSA Mode Configuration
> > 141 * 0 10 10 PWM High-true pulse
> > 142 * 0 10 00 PWM Reserved
> > 143 * 0 10 01 PWM Low-true pulse
> > 144 * 0 10 11 PWM Reserved
> > 145 *
> > 146 * High-true pulse: clear output on counter match, set output on
> > 147 * counter reload, set output when counter first enabled or paused.
> > 148 *
> > 149 * Low-true pulse: set output on counter match, clear output on
> > 150 * counter reload, clear output when counter first enabled or
> paused.
> > 151 */
>
> I stumbled over "high-true" and "low-true" in the RM, too. In my bubble this
> is an uncommon wording. I'd write instead:
>
> /*
> * set polarity
> *
> * ELSB:ELSA = 2b10 yields normal polarity behaviour, ELSB:ELSA
> * = 2b01 yields inversed polarity. The other values are
> * reserved.
> */
>
> And don't write about CPWM, MSA and MSB which are always used with
> fixed values anyhow in the driver.
>
OK.
> > > > + /* disable channel */
> > > > + writel(PWM_IMX_TPM_CnSC_CHF,
> > > > + tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm));
> > >
> > > Clearing CHF doens't disable the channel as I read the manual.
> >
> > This write clears CHF as well as writing other bits 0, to disable the
> > output. Maybe I can explicitly clear MSA/MSB/ELSA/ELSB to avoid
> confusion.
>
> Ah, I misinterpreted the value written.
>
> > > > +static int pwm_imx_tpm_apply(struct pwm_chip *chip, struct
> pwm_device *pwm,
> > > > + struct pwm_state *state)
> > > > +{
> > > > + struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
> > > > + struct pwm_state curstate;
> > > > + u32 duty_cycle = state->duty_cycle;
> > > > + int ret;
> > > > +
> > > > + pwm_imx_tpm_get_state(chip, pwm, &curstate);
> > > > +
> > > > + mutex_lock(&tpm->lock);
> > >
> > > What should this lock protect? Does it hurt if the state changes
> > > between pwm_imx_tpm_get_state releasing the lock and
> > > pwm_imx_tpm_apply taking it?
> >
> > The idea is to protect the share resourced by multiple channels, but I
> > think I can make the mutex_lock includes get_state and remove the lock in
> get_state function.
> A
> You might need it in .get_state to return a consistent state to the caller. In
> this case just introduce an unlocked variant of .get_state to share code
> between the two functions.
>
> And BTW the question was honest. I'm not entirely sure that you need to
> hold the lock.
Agreed, if the different channel configuration ONLY access its own register, NOT
any shared registers, then I think this lock is unnecessary.
>
> > > > + */
> > > > + if (tpm->user_count != 1)
> > > > + return -EBUSY;
> > >
> > > Ideally if say period = 37 is requested but currently we have period
> > > =
> > > 36 and configuring 37 would result in 36 anyhow, don't return EBUSY.
> >
> > I think here the protection is just for making sure that is there are
> > multiple users, period can NOT be changed, since all channels will be
> impacted.
>
> I think you misunderstood what I intended to say here.
>
> Consider that in the case that there is only a single PWM in use configuring
> for a period of 37 ns actually yields 36 ns because the hardware cannot
> provide 37 ns and 36 ns is the best match.
>
> Then if a second user comes and requests 37 ns, the result here is, that the
> second user gets the -EBUSY. This is ridiculous however because the request
> is denied even though the period is already configured for the length that
> would be configured if the second user were the only one.
Ah, if I understand correctly, I think I can change it to, if second user try to set a period
different from first user, then return -EBUSY, otherwise, return success.
>
> > > > + tpm->chip.dev = &pdev->dev;
> > > > + tpm->chip.ops = &imx_tpm_pwm_ops;
> > > > + tpm->chip.base = -1;
> > > > + tpm->chip.npwm = PWM_IMX_TPM_MAX_CHANNEL_NUM;
> > >
> > > This is wrong, as some only have 2 channels?
> >
> > I saw we can get channel number from register, will read register to
> > determine the channel number, but for the channel config and status
> > saved in struct, I will still use the MAX channel number to define the array.
>
> Yeah, that is sensible.
I will resend V5 patch set, since there are some mis-understanding for previous comments.
Thanks,
Anson.
>
> Best regards
> Uwe
>
> --
> Pengutronix e.K. | Uwe Kleine-KÃnig |
> Industrial Linux Solutions |
> https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.p
> engutronix.de%2F&data=02%7C01%7Canson.huang%40nxp.com%7C31
> d10d3f4a7c46eea9d708d6ab78d965%7C686ea1d3bc2b4c6fa92cd99c5c30163
> 5%7C0%7C0%7C636884932859808816&sdata=LD2D%2BwEhNKFllK2FaI
> Wvjttmre0YPV%2BXwv7sdvkZSpo%3D&reserved=0 |