[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20201127071105.k2rb4iykeqevbao5@pengutronix.de>
Date: Fri, 27 Nov 2020 08:11:05 +0100
From: Uwe Kleine-König <u.kleine-koenig@...gutronix.de>
To: Jonathan Neuschäfer <j.neuschaefer@....net>,
Mark Brown <broonie@...nel.org>
Cc: Alexandre Belloni <alexandre.belloni@...tlin.com>,
Heiko Stuebner <heiko@...ech.de>, devicetree@...r.kernel.org,
Linus Walleij <linus.walleij@...aro.org>,
Thierry Reding <thierry.reding@...il.com>,
Sam Ravnborg <sam@...nborg.org>, linux-rtc@...r.kernel.org,
Arnd Bergmann <arnd@...db.de>,
Mauro Carvalho Chehab <mchehab+huawei@...nel.org>,
Fabio Estevam <festevam@...il.com>,
Daniel Palmer <daniel@...f.com>,
Andy Shevchenko <andy.shevchenko@...il.com>,
Andreas Kemnade <andreas@...nade.info>,
NXP Linux Team <linux-imx@....com>,
linux-pwm@...r.kernel.org, Stephan Gerhold <stephan@...hold.net>,
allen <allen.chen@....com.tw>,
Sascha Hauer <s.hauer@...gutronix.de>,
Lubomir Rintel <lkundrak@...sk>,
Rob Herring <robh+dt@...nel.org>,
Lee Jones <lee.jones@...aro.org>,
linux-arm-kernel@...ts.infradead.org,
Alessandro Zummo <a.zummo@...ertech.it>,
linux-kernel@...r.kernel.org,
Pengutronix Kernel Team <kernel@...gutronix.de>,
Heiko Stuebner <heiko.stuebner@...obroma-systems.com>,
Josua Mayer <josua.mayer@....eu>,
Shawn Guo <shawnguo@...nel.org>,
"David S. Miller" <davem@...emloft.net>
Subject: Re: [PATCH v4 4/7] pwm: ntxec: Add driver for PWM function in
Netronix EC
Hello Jonathan,
On Fri, Nov 27, 2020 at 12:19:31AM +0100, Jonathan Neuschäfer wrote:
> On Tue, Nov 24, 2020 at 09:20:19AM +0100, Uwe Kleine-König wrote:
> > On Sun, Nov 22, 2020 at 11:27:36PM +0100, Jonathan Neuschäfer wrote:
> [...]
> > > +/*
> > > + * The time base used in the EC is 8MHz, or 125ns. Period and duty cycle are
> > > + * measured in this unit.
> > > + */
> > > +#define TIME_BASE_NS 125
> > > +
> > > +/*
> > > + * The maximum input value (in nanoseconds) is determined by the time base and
> > > + * the range of the hardware registers that hold the converted value.
> > > + * It fits into 32 bits, so we can do our calculations in 32 bits as well.
> > > + */
> > > +#define MAX_PERIOD_NS (TIME_BASE_NS * 0xffff)
> > > +
> > > +static int ntxec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm_dev,
> > > + const struct pwm_state *state)
> > > +{
> > > + struct ntxec_pwm *priv = pwmchip_to_priv(pwm_dev->chip);
> > > + unsigned int duty = state->duty_cycle;
> > > + unsigned int period = state->period;
> >
> > state->duty_cycle and state->period are u64, so you're losing
> > information here. Consider state->duty_cycle = 0x100000001 and
> > state->period = 0x200000001.
>
> Oh, good point, I didn't notice the truncation.
>
> The reason I picked unsigned int was to avoid a 64-bit division;
> I suppose I can do something like this:
>
> period = (u32)period / TIME_BASE_NS;
> duty = (u32)duty / TIME_BASE_NS;
You can do that after you checked period > MAX_PERIOD_NS below, yes.
Something like:
if (state->polarity != PWM_POLARITY_NORMAL)
return -EINVAL;
if (state->period > MAX_PERIOD_NS) {
period = MAX_PERIOD_NS;
else
period = state->period;
if (state->duty_cycle > period)
duty_cycle = period;
else
duty_cycle = state->duty_cycle;
should work with even keeping the local variables as unsigned int.
> > > + int res = 0;
> > > +
> > > + if (state->polarity != PWM_POLARITY_NORMAL)
> > > + return -EINVAL;
> > > +
> > > + if (period > MAX_PERIOD_NS) {
> > > + period = MAX_PERIOD_NS;
> > > +
> > > + if (duty > period)
> > > + duty = period;
> > > + }
> > > +
> > > + period /= TIME_BASE_NS;
> > > + duty /= TIME_BASE_NS;
> > > +
> > > + res = regmap_write(priv->ec->regmap, NTXEC_REG_PERIOD_HIGH, ntxec_reg8(period >> 8));
> > > + if (res)
> > > + return res;
> >
> > I wonder if you can add some logic to the regmap in the mfd driver such
> > that ntxec_reg8 isn't necessary for all users.
>
> I think that would involve:
>
> 1. adding custom register access functions to the regmap, which decide
> based on the register number whether a register needs 8-bit or 16-bit
> access. So far I have avoided information about registers into the
> main driver, when the registers are only used in the sub-drivers.
>
> or
>
> 2. switching the regmap configuration to little endian, which would be
> advantageous for 8-bit registers, inconsequential for 16-bit
> registers that consist of independent high and low halves, and wrong
> for the 16-bit registers 0x41, which reads the battery voltage ADC
> value. It is also different from how the vendor kernel treats 16-bit
> registers.
>
> Perhaps there is another option that I haven't considered yet.
I don't know enough about regmap to teach you something here. But maybe
Mark has an idea. (I promoted him from Cc: to To:, maybe he will
notice.)
> > > + res = regmap_write(priv->ec->regmap, NTXEC_REG_PERIOD_LOW, ntxec_reg8(period));
> > > + if (res)
> > > + return res;
> > > +
> > > + res = regmap_write(priv->ec->regmap, NTXEC_REG_DUTY_HIGH, ntxec_reg8(duty >> 8));
> > > + if (res)
> > > + return res;
> > > +
> > > + res = regmap_write(priv->ec->regmap, NTXEC_REG_DUTY_LOW, ntxec_reg8(duty));
> > > + if (res)
> > > + return res;
> >
> > I think I already asked, but I don't remember the reply: What happens to
> > the output between these writes? A comment here about this would be
> > suitable.
>
> I will add something like the following:
>
> /*
> * Changes to the period and duty cycle take effect as soon as the
> * corresponding low byte is written, so the hardware may be configured
> * to an inconsistent state after the period is written and before the
> * duty cycle is fully written. If, in such a case, the old duty cycle
> * is longer than the new period, the EC will output 100% for a moment.
> */
Is the value pair taken over by hardware atomically? That is, is it
really "will" in your last line, or only "might". (E.g. when changing
from duty_cycle, period = 1000, 2000 to 500, 800 and a new cycle begins
after reducing period, the new duty_cycle is probably written before the
counter reaches 500. Do we get a 100% cycle here?)
Other than that the info is fine. Make sure to point this out in the
Limitations paragraph at the top of the driver please, too.
> > > + .apply = ntxec_pwm_apply,
> >
> > /*
> > * The current state cannot be read out, so there is no .get_state
> > * callback.
> > */
> >
> > Hmm, at least you could provice a .get_state() callback that reports the
> > setting that was actually implemented for in the last call to .apply()?
>
> Yes... I see two options:
>
> 1. Caching the state in the driver's private struct. I'm not completely
> convinced of the value, given that the information is mostly
> available in the PWM core already (except for the adjustments that
> the driver makes).
>
> 2. Writing the adjusted state back into pwm_dev->state (via pwm_set_*).
> This seems a bit dirty.
2. isn't a good option. Maybe regmap caches this stuff anyhow for 1. (or
can be made doing that)?
> > @Thierry: Do you have concerns here? Actually it would be more effective
> > to have a callback (like .apply()) that modfies its pwm_state
> > accordingly. (Some drivers did that in the past, but I changed that to
> > get an uniform behaviour in 71523d1812aca61e32e742e87ec064e3d8c615e1.)
> > The downside is that people have to understand that concept to properly
> > use it. I'm torn about the right approach.
>
> General guidance for such cases when the state can't be read back from
> the hardware would be appreciated.
Yes, improving the documentation would be great here. Thierry, can you
please comment on
https://lore.kernel.org/r/20191209213233.29574-2-u.kleine-koenig@pengutronix.de
which I'm waiting on before describing our understanding in more detail.
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-König |
Industrial Linux Solutions | https://www.pengutronix.de/ |
Download attachment "signature.asc" of type "application/pgp-signature" (489 bytes)
Powered by blists - more mailing lists