All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>
To: "Jonathan Neuschäfer" <j.neuschaefer@gmx.net>,
	"Mark Brown" <broonie@kernel.org>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>,
	Heiko Stuebner <heiko@sntech.de>,
	devicetree@vger.kernel.org,
	Linus Walleij <linus.walleij@linaro.org>,
	Thierry Reding <thierry.reding@gmail.com>,
	Sam Ravnborg <sam@ravnborg.org>,
	linux-rtc@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>,
	Mauro Carvalho Chehab <mchehab+huawei@kernel.org>,
	Fabio Estevam <festevam@gmail.com>,
	Daniel Palmer <daniel@0x0f.com>,
	Andy Shevchenko <andy.shevchenko@gmail.com>,
	Andreas Kemnade <andreas@kemnade.info>,
	NXP Linux Team <linux-imx@nxp.com>,
	linux-pwm@vger.kernel.org, Stephan Gerhold <stephan@gerhold.net>,
	allen <allen.chen@ite.com.tw>,
	Sascha Hauer <s.hauer@pengutronix.de>,
	Lubomir Rintel <lkundrak@v3.sk>, Rob Herring <robh+dt@kernel.org>,
	Lee Jones <lee.jones@linaro.org>,
	linux-arm-kernel@lists.infradead.org,
	Alessandro Zummo <a.zummo@towertech.it>,
	linux-kernel@vger.kernel.org,
	Pengutronix Kernel Team <kernel@pengutronix.de>,
	Heiko Stuebner <heiko.stuebner@theobroma-systems.com>,
	Josua Mayer <josua.mayer@jm0.eu>, Shawn Guo <shawnguo@kernel.org>,
	"David S. Miller" <davem@davemloft.net>
Subject: Re: [PATCH v4 4/7] pwm: ntxec: Add driver for PWM function in Netronix EC
Date: Fri, 27 Nov 2020 08:11:05 +0100	[thread overview]
Message-ID: <20201127071105.k2rb4iykeqevbao5@pengutronix.de> (raw)
In-Reply-To: <20201126231931.GE456020@latitude>

[-- Attachment #1: Type: text/plain, Size: 6745 bytes --]

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/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

WARNING: multiple messages have this Message-ID (diff)
From: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>
To: "Jonathan Neuschäfer" <j.neuschaefer@gmx.net>,
	"Mark Brown" <broonie@kernel.org>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>,
	Heiko Stuebner <heiko@sntech.de>, Sam Ravnborg <sam@ravnborg.org>,
	linux-pwm@vger.kernel.org,
	Linus Walleij <linus.walleij@linaro.org>,
	Thierry Reding <thierry.reding@gmail.com>,
	Fabio Estevam <festevam@gmail.com>,
	linux-rtc@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>,
	Mauro Carvalho Chehab <mchehab+huawei@kernel.org>,
	Lee Jones <lee.jones@linaro.org>, Daniel Palmer <daniel@0x0f.com>,
	Andy Shevchenko <andy.shevchenko@gmail.com>,
	Andreas Kemnade <andreas@kemnade.info>,
	NXP Linux Team <linux-imx@nxp.com>,
	devicetree@vger.kernel.org, Stephan Gerhold <stephan@gerhold.net>,
	allen <allen.chen@ite.com.tw>,
	Sascha Hauer <s.hauer@pengutronix.de>,
	Lubomir Rintel <lkundrak@v3.sk>, Rob Herring <robh+dt@kernel.org>,
	linux-arm-kernel@lists.infradead.org,
	Alessandro Zummo <a.zummo@towertech.it>,
	linux-kernel@vger.kernel.org,
	Pengutronix Kernel Team <kernel@pengutronix.de>,
	Heiko Stuebner <heiko.stuebner@theobroma-systems.com>,
	Josua Mayer <josua.mayer@jm0.eu>, Shawn Guo <shawnguo@kernel.org>,
	"David S. Miller" <davem@davemloft.net>
Subject: Re: [PATCH v4 4/7] pwm: ntxec: Add driver for PWM function in Netronix EC
Date: Fri, 27 Nov 2020 08:11:05 +0100	[thread overview]
Message-ID: <20201127071105.k2rb4iykeqevbao5@pengutronix.de> (raw)
In-Reply-To: <20201126231931.GE456020@latitude>


[-- Attachment #1.1: Type: text/plain, Size: 6745 bytes --]

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/ |

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2020-11-27  7:11 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-11-22 22:27 [PATCH v4 0/7] Netronix embedded controller driver for Kobo and Tolino ebook readers Jonathan Neuschäfer
2020-11-22 22:27 ` Jonathan Neuschäfer
2020-11-22 22:27 ` [PATCH v4 1/7] dt-bindings: Add vendor prefix for Netronix, Inc Jonathan Neuschäfer
2020-11-22 22:27   ` Jonathan Neuschäfer
2020-11-22 22:27 ` [PATCH v4 2/7] dt-bindings: mfd: Add binding for Netronix embedded controller Jonathan Neuschäfer
2020-11-22 22:27   ` Jonathan Neuschäfer
2020-11-22 22:27 ` [PATCH v4 3/7] mfd: Add base driver " Jonathan Neuschäfer
2020-11-22 22:27   ` Jonathan Neuschäfer
2020-12-02 13:05   ` Lee Jones
2020-12-02 13:05     ` Lee Jones
2020-12-02 13:06     ` Lee Jones
2020-12-02 13:06       ` Lee Jones
2020-12-02 14:00     ` Jonathan Neuschäfer
2020-12-02 14:00       ` Jonathan Neuschäfer
2020-12-02 15:09       ` Lee Jones
2020-12-02 15:09         ` Lee Jones
2020-12-02 17:11         ` Jonathan Neuschäfer
2020-12-02 17:11           ` Jonathan Neuschäfer
2020-11-22 22:27 ` [PATCH v4 4/7] pwm: ntxec: Add driver for PWM function in Netronix EC Jonathan Neuschäfer
2020-11-22 22:27   ` Jonathan Neuschäfer
2020-11-24  8:20   ` Uwe Kleine-König
2020-11-24  8:20     ` Uwe Kleine-König
2020-11-26 23:19     ` Jonathan Neuschäfer
2020-11-26 23:19       ` Jonathan Neuschäfer
2020-11-27  7:11       ` Uwe Kleine-König [this message]
2020-11-27  7:11         ` Uwe Kleine-König
2020-11-27 11:08         ` Thierry Reding
2020-11-27 11:08           ` Thierry Reding
2020-11-27 14:23           ` Uwe Kleine-König
2020-11-27 14:23             ` Uwe Kleine-König
2020-11-29 17:59             ` Jonathan Neuschäfer
2020-11-29 17:59               ` Jonathan Neuschäfer
2020-11-29 17:48         ` Jonathan Neuschäfer
2020-11-29 17:48           ` Jonathan Neuschäfer
2020-11-22 22:27 ` [PATCH v4 5/7] rtc: New driver for RTC in Netronix embedded controller Jonathan Neuschäfer
2020-11-22 22:27   ` Jonathan Neuschäfer
2020-11-22 23:10   ` Alexandre Belloni
2020-11-22 23:10     ` Alexandre Belloni
2020-11-23 21:31     ` Jonathan Neuschäfer
2020-11-23 21:31       ` Jonathan Neuschäfer
2020-11-23 21:34       ` Mark Brown
2020-11-23 21:34         ` Mark Brown
2020-11-23 21:38       ` Alexandre Belloni
2020-11-23 21:38         ` Alexandre Belloni
2020-11-22 22:27 ` [PATCH v4 6/7] MAINTAINERS: Add entry for " Jonathan Neuschäfer
2020-11-22 22:27   ` Jonathan Neuschäfer
2020-11-23  0:09 ` [PATCH v4 7/7] ARM: dts: imx50-kobo-aura: Add " Jonathan Neuschäfer
2020-11-23  0:09   ` Jonathan Neuschäfer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201127071105.k2rb4iykeqevbao5@pengutronix.de \
    --to=u.kleine-koenig@pengutronix.de \
    --cc=a.zummo@towertech.it \
    --cc=alexandre.belloni@bootlin.com \
    --cc=allen.chen@ite.com.tw \
    --cc=andreas@kemnade.info \
    --cc=andy.shevchenko@gmail.com \
    --cc=arnd@arndb.de \
    --cc=broonie@kernel.org \
    --cc=daniel@0x0f.com \
    --cc=davem@davemloft.net \
    --cc=devicetree@vger.kernel.org \
    --cc=festevam@gmail.com \
    --cc=heiko.stuebner@theobroma-systems.com \
    --cc=heiko@sntech.de \
    --cc=j.neuschaefer@gmx.net \
    --cc=josua.mayer@jm0.eu \
    --cc=kernel@pengutronix.de \
    --cc=lee.jones@linaro.org \
    --cc=linus.walleij@linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-imx@nxp.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pwm@vger.kernel.org \
    --cc=linux-rtc@vger.kernel.org \
    --cc=lkundrak@v3.sk \
    --cc=mchehab+huawei@kernel.org \
    --cc=robh+dt@kernel.org \
    --cc=s.hauer@pengutronix.de \
    --cc=sam@ravnborg.org \
    --cc=shawnguo@kernel.org \
    --cc=stephan@gerhold.net \
    --cc=thierry.reding@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.