[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20200927211044.GC2510@latitude>
Date: Sun, 27 Sep 2020 23:10:44 +0200
From: Jonathan Neuschäfer <j.neuschaefer@....net>
To: Uwe Kleine-König <u.kleine-koenig@...gutronix.de>
Cc: Jonathan Neuschäfer <j.neuschaefer@....net>,
linux-kernel@...r.kernel.org,
Alexandre Belloni <alexandre.belloni@...tlin.com>,
Heiko Stuebner <heiko@...ech.de>, linux-pwm@...r.kernel.org,
Linus Walleij <linus.walleij@...aro.org>,
Thierry Reding <thierry.reding@...il.com>,
Fabio Estevam <festevam@...il.com>, linux-rtc@...r.kernel.org,
Arnd Bergmann <arnd@...db.de>,
Mauro Carvalho Chehab <mchehab+huawei@...nel.org>,
Sam Ravnborg <sam@...nborg.org>,
Daniel Palmer <daniel@...f.com>,
Andy Shevchenko <andy.shevchenko@...il.com>,
Andreas Kemnade <andreas@...nade.info>,
NXP Linux Team <linux-imx@....com>,
devicetree@...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>,
Mark Brown <broonie@...nel.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 v3 4/7] pwm: ntxec: Add driver for PWM function in
Netronix EC
On Fri, Sep 25, 2020 at 08:30:37AM +0200, Uwe Kleine-König wrote:
> Hello Jonathan,
[...]
> > +config PWM_NTXEC
> > + tristate "Netronix embedded controller PWM support"
> > + depends on MFD_NTXEC
> > + help
> > + Say yes here if you want to support the PWM output of the embedded
> > + controller found in certain e-book readers designed by the ODM
> > + Netronix.
>
> Is it only me who had to look up what ODM means? If not, maybe spell it
> out?
I'm sure other readers will have the same problem. I'll spell it out.
> > +/*
> > + * 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 * 0x10000 - 1)
>
> The maximal configurable period length is 0xffff, so I would have
> expected MAX_PERIOD_NS to be 0xffff * TIME_BASE_NS?
Due to the division rounding down, TIME_BASE_NS * 0x10000 - 1 would be
the highest input that results in a representable value after the
division, but I'm not sure it otherwise makes sense.
>
> > +static int ntxec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm_dev,
> > + const struct pwm_state *state)
> > +{
> > + struct ntxec_pwm *pwm = pwmchip_to_pwm(pwm_dev->chip);
> > + unsigned int duty = state->duty_cycle;
> > + unsigned int period = state->period;
> > + int res = 0;
> > +
>
> I assume your device only supports normal polarity? If so, please check
> for it here and point out this limitation in the header (in the format
> that is for example used in pwm-sifive.c to make it easy to grep for
> that).
I haven't seen any indication that it supports inverted polarity. I'll
point it out in the header comment, and add a check.
>
> > + if (period > MAX_PERIOD_NS) {
> > + dev_warn(pwm->dev,
> > + "Period is not representable in 16 bits after division by %u: %u\n",
> > + TIME_BASE_NS, period);
>
> No error messages in .apply() please; this might spam the kernel log.
>
> Also the expectation when a too big period is requested is to configure
> for the biggest possible period. So just do:
>
> if (period > MAX_PERIOD_NS) {
> period = MAX_PERIOD_NS;
>
> if (duty > period)
> duty = period;
> }
>
> (or something equivalent).
Okay, I'll adjust it.
> > + /*
> > + * Writing a duty cycle of zone puts the device into a state where
>
> What is "zone"? A mixture of zero and one and so approximately 0.5?
Oops, that's a typo. I just meant "zero".
> > + * writing a higher duty cycle doesn't result in the brightness that it
> > + * usually results in. This can be fixed by cycling the ENABLE register.
> > + *
> > + * As a workaround, write ENABLE=0 when the duty cycle is zero.
> > + */
> > + if (state->enabled && duty != 0) {
> > + res = regmap_write(pwm->ec->regmap, NTXEC_REG_ENABLE, ntxec_reg8(1));
> > + if (res)
> > + return res;
> > +
> > + /* Disable the auto-off timer */
> > + res = regmap_write(pwm->ec->regmap, NTXEC_REG_AUTO_OFF_HI, ntxec_reg8(0xff));
> > + if (res)
> > + return res;
> > +
> > + return regmap_write(pwm->ec->regmap, NTXEC_REG_AUTO_OFF_LO, ntxec_reg8(0xff));
> > + } else {
> > + return regmap_write(pwm->ec->regmap, NTXEC_REG_ENABLE, ntxec_reg8(0));
> > + }
>
> This code is wrong for state->enabled = false.
Why?
> How does the PWM behave when .apply is called? Does it complete the
> currently running period? Can it happen that when you switch from say
>
> .duty_cycle = 900 * TIME_BASE_NS (0x384)
> .period = 1800 * TIME_BASE_NS (0x708)
>
> to
>
> .duty_cycle = 300 * TIME_BASE_NS (0x12c)
> .period = 600 * TIME_BASE_NS (0x258)
>
> that a period with
>
> .duty_cycle = 388 * TIME_BASE_NS (0x184)
> .period = 1800 * TIME_BASE_NS (0x708)
>
> (because only NTXEC_REG_PERIOD_HIGH was written when the new period
> started) or something similar is emitted?
Changes take effect after the low byte is written, so a result like 0x184
in the above example should not happen.
When the period and duty cycle are both changed, it temporarily results
in an inconsistent state:
- period = 1800ns, duty cycle = 900ns
- period = 600ns, duty cycle = 900ns (!)
- period = 600ns, duty cycle = 300ns
The inconsistent state of duty cycle > period is handled gracefully by
the EC and it outputs a 100% duty cycle, as far as I can tell.
I currently don't have a logic analyzer / oscilloscope to measure
whether we get full PWM periods, or some kind of glitch when the new
period starts in the middle of the last one.
> > +}
> > +
> > +static struct pwm_ops ntxec_pwm_ops = {
> > + .apply = ntxec_pwm_apply,
>
> Please implement a .get_state() callback. And enable PWM_DEBUG during
> your tests.
The device doesn't support reading back the PWM state. What should a
driver do in this case?
> > + .owner = THIS_MODULE,
> > +};
> > +
> > +static int ntxec_pwm_probe(struct platform_device *pdev)
> > +{
> > + struct ntxec *ec = dev_get_drvdata(pdev->dev.parent);
> > + struct ntxec_pwm *pwm;
>
> Please don't call this variable pwm. I would expect that a variable with
> this name is of type pwm_device. I would have called it "ddata" (and the
> type would be named ntxec_pwm_ddata for me); another usual name is "priv".
Ok, I'll rename it.
> > + chip->npwm = 1;
> > +
> > + res = pwmchip_add(chip);
> > + if (res < 0)
> > + return res;
> > +
> > + platform_set_drvdata(pdev, pwm);
>
> If you do the platform_set_drvdata earlier you can just do
>
> return pwmchip_add(chip);
Good idea, I'll do that.
Thanks,
Jonathan Neuschäfer
Download attachment "signature.asc" of type "application/pgp-signature" (834 bytes)
Powered by blists - more mailing lists