Re: [PATCH v10 3/3] pwm: Add support for Xilinx AXI Timer

From: Uwe Kleine-König
Date: Tue Nov 23 2021 - 03:42:07 EST


Hello Sean,

On Mon, Nov 22, 2021 at 12:32:19PM -0500, Sean Anderson wrote:
> On 11/19/21 3:43 AM, Uwe Kleine-König wrote:
> > On Thu, Nov 18, 2021 at 04:08:45PM -0500, Sean Anderson wrote:
> > > On 11/18/21 4:28 AM, Uwe Kleine-König wrote:
> > > > On Fri, Nov 12, 2021 at 01:55:04PM -0500, Sean Anderson wrote:
> > > > > [...]
> > > > > + /* cycles has a max of 2^32 + 2 */
> > > > > + return DIV64_U64_ROUND_CLOSEST(cycles * NSEC_PER_SEC,
> > > > > + clk_get_rate(priv->clk));
> > > >
> > > > Please round up here.
> > >
> > > Please document the correct rounding mode you expect. The last time we
> > > discussed this (3 months ago), you only said that rounding down was
> > > incorrect...
> >
> > I think you refer to
> > https://lore.kernel.org/linux-pwm/20210817180407.ru4prwu344dxpynu@xxxxxxxxxxxxxx
> > here, right? I agree that I could have been a bit more explicit here.
> >
> > .apply should first round down .period to the next achievable setting
> > and then .duty_cycle should be round down to the next achievable setting
> > (in combination with the chosen period).
> >
> > To get .apply ∘ .get_state idempotent (i.e. if I apply the result from
> > get_state there are no changes), .get_state has to round up.
> >
> > After our longer discussion about v4 I would have expected that this was
> > already obvious. There you wrote[1]:
> >
> > > * The apply_state function shall only round the requested period down, and
> > > may do so by no more than one unit cycle. If the requested period is
> > > unrepresentable by the PWM, the apply_state function shall return
> > > -ERANGE.
> > > * The apply_state function shall only round the requested duty cycle
> > > down. The apply_state function shall not return an error unless there
> > > is no duty cycle less than the requested duty cycle which is
> > > representable by the PWM.
> > > * After applying a state returned by round_state with apply_state,
> > > get_state must return that state.
> >
> > The requirement to round up is a direct consequence of these three
> > points, which I confirmed (apart from some wording issues).
> >
> > [1] https://lore.kernel.org/linux-pwm/ddd2ad0c-1dff-c437-17a6-4c7be72c2fce@xxxxxxxx
>
> Ok, will fix. But again, a little something in
> Documentation/driver-api/pwm.rst would help a lot.

Ack, will take another attempt to care for that.

> > > > > + period_cycles = min_t(u64, state->period, ULONG_MAX * NSEC_PER_SEC);
> > > > > + period_cycles = mul_u64_u32_div(period_cycles, rate, NSEC_PER_SEC);
> > > > > + if (period_cycles < 2 || period_cycles - 2 > priv->max)
> > > > > + return -ERANGE;
> > > >
> > > > if period_cycles - 2 > priv->max the right reaction is to do
> > > >
> > > > period_cycles = priv->max + 2
> > >
> > > It has been 5 months since we last talked about this, and yet you have
> > > not submitted any patches for a "pwm_round_rate" function. Forgive me if
> > > I am reticent to implement forward compatibility for an API which shows
> > > no signs of appearing.
> >
> > This requirement is not only for round_state. It's also to get some
> > common behaviour for at least new drivers. The primary goal here is to
> > make the result for pwm_apply more predictable.
>
> The behavior you specify is *not* common. No drivers currently round in
> the manner you specify here.

Hmm, if this is true I failed during the reviews before.

Looking through the last added drivers:

- visconti: There is no division involved, so there is no rounding
issue. Also period is round down if a too high value is requested.
- ntxec: There is no get_state callback because the hardware state
cannot be read out. Period is reduced as requested.
- raspberrypi-poe: Rounds up in .get_state() and down in .apply(). (A
bit special as this HW has a fixed period.)
- intel-lgm: Rounds up in .get_state() and down in .apply(). Ditto this
is a fixed-period driver and only too small periods are refused.
- dwc: This is indeed wrong. I didn't review the finally merged
version, but pointed out the driver being wrong in v2
(https://lore.kernel.org/linux-pwm/20200524201116.pc7jmffr6jxlwren@xxxxxxxxxxxxxx)
- keembay: Rounds up in .get_state and dowan in .apply()
(Though the detection of a too high period might be broken?! Didn't
look into the details.)
- pwm-sl28cpld is aligned to this behaviour (though this is a bit of a
special case as there is no rounding involved). Refusing too big
periods (only) is done right in it.
- iqs620a: Is aligned to my request, too
- pwm-sprd: This is wrong, too. My review comments were not addressed
here either (e.g.
https://lore.kernel.org/linux-pwm/20190814150304.x44lalde3cwp67ge@xxxxxxxxxxxxxx)

So while the situation isn't ideal, it's not as worse as you picture it.

> In fact, returning -ERANGE or -EINVAL is
> far more common than attempting to handle this case. If you would like
> new drivers to use a new algorithm, I suggest first converting existing
> drivers. I think it is unreasonable to hold new drivers to a standard
> which no existing driver is held to.

In the past Thierry opposed to adapting existing drivers. :-\
Having said that being more strict for new drivers isn't that uncommon.
For example I expect it wouldn't be possible to get something like
drivers/tty/serial/8250 into mainline today.

> > > > > +static int xilinx_timer_probe(struct platform_device *pdev)
> > > > > +{
> > > > > + int ret;
> > > > > + struct device *dev = &pdev->dev;
> > > > > + struct device_node *np = dev->of_node;
> > > > > + struct xilinx_timer_priv *priv;
> > > > > + struct xilinx_pwm_device *pwm;
> > > >
> > > > The name "pwm" is usually reserved for struct pwm_device pointers. A
> > > > typical name for this would be xlnxpwm or ddata.
> > >
> > > I suppose. However, no variables of struct pwm_device are used in
> > > this driver.
> >
> > Still it provokes wrong expectations when reading
> >
> > platform_set_drvdata(pdev, pwm);
> >
> > in a pwm driver.
>
> The second most common use of this function in drivers/pwm is the above usage.
>
> $ git grep -h platform_set_drvdata v5.15 drivers/pwm/ | sort | uniq -c | sort -n
> 1 platform_set_drvdata(pdev, atmel_pwm);
> 1 platform_set_drvdata(pdev, bpc);
> 1 platform_set_drvdata(pdev, ddata);
> 1 platform_set_drvdata(pdev, ec_pwm);
> 1 platform_set_drvdata(pdev, fpc);
> 1 platform_set_drvdata(pdev, ip);
> 1 platform_set_drvdata(pdev, lpc18xx_pwm);
> 1 platform_set_drvdata(pdev, lpwm);
> 1 platform_set_drvdata(pdev, mdp);
> 1 platform_set_drvdata(pdev, omap);
> 1 platform_set_drvdata(pdev, p);
> 1 platform_set_drvdata(pdev, pwm_chip);
> 1 platform_set_drvdata(pdev, rcar_pwm);
> 1 platform_set_drvdata(pdev, spc);
> 1 platform_set_drvdata(pdev, tcbpwm);
> 1 platform_set_drvdata(pdev, tpm);
> 1 platform_set_drvdata(pdev, tpu);
> 3 platform_set_drvdata(pdev, chip);
> 3 platform_set_drvdata(pdev, priv);
> 4 platform_set_drvdata(pdev, pwm);
> 6 platform_set_drvdata(pdev, pc);

Ack, this are all "old" drivers (i.e. img (2015), stmpe (2016), sun4i
(2015) and tegra(2012); "old" in the sense "before I engaged in pwm
reviews") ISTR that in this question Thierry agrees that only variables
with type struct pwm_chip * should be named pwm.

> With other contenders being "pc", "chip", "pwm_chip", and "p". This used
> to be more common, but several examples have been converted to
> devm_pwmchip_add. To say that such a variable (used once!) "provokes the
> wrong expectations" would be to have expectations misaligned with the
> corpus of existing drivers.

Yeah, I don't oppose to this interpretation. I'm not happy with the code
base. Reworking this is a big effort and difficult with the request to
not break assumption for existing drivers.

> > > > > + u32 pwm_cells, one_timer, width;
> > > > > + void __iomem *regs;
> > > > > +
> > > > > + ret = of_property_read_u32(np, "#pwm-cells", &pwm_cells);
> > > > > + if (ret == -EINVAL)
> > > > > + return -ENODEV;
> > > >
> > > > A comment about why this is done would be great.
> > >
> > > OK. How about:
> > >
> > > /* If there are no #pwm-cells, this binding is for a timer and not a PWM */
> >
> > Fine. Does that mean the timer driver won't bind in the presence of the
> > #pwm-cells property, and the timer driver uses the same compatible?
> > Sounds a bit strange to me.
>
> Correct. See below.
>
> > > > > + /*
> > > > > + * The polarity of the generate outputs must be active high for PWM
> > > >
> > > > s/generate/generated/
> > >
> > > The signals I am referring to are called "GenerateOut0" and
> > > "GenerateOut1".
> >
> > Then maybe:
> >
> > The polarity of the outputs "GenerateOut0" and "GenerateOut1"
> > ...
> >
> > ?
>
> The exact wording of the configuration option is
>
> > Active state of Generate Out signal
>
> with a drop-down to select between "Active High" and "Active Low". So
> the most exact way to specify this would be
>
> The polarity of the Generate Out signals must be...
>
> > > > > +static struct platform_driver xilinx_timer_driver = {
> > > > > + .probe = xilinx_timer_probe,
> > > > > + .remove = xilinx_timer_remove,
> > > > > + .driver = {
> > > > > + .name = "xilinx-timer",
> > > >
> > > > Doesn't this give a wrong impression as this is a PWM driver, not a
> > > > timer driver?
> >
> > This directly relates to the fact that the timer driver and the pwm
> > driver (seem to) bind on the same compatible as already mentioned above.
> > The dt people didn't agree to this yet, did they?
>
> Rob Herring has acked the binding. And switching based on the presence
> of #pwm-cells was his idea in the first place:
>
> > If a PWM, perhaps you want a '#pwm-cells' property which can serve as
> > the hint to configure as a PWM.
>
> As I understand it, the compatible should be the same if the hardware is
> the same. Ideally, this sort of thing would be configurable by userspace
> at runtime, but timers get probed so early that we have to use something
> in the devicetree.
>
> [1] https://lore.kernel.org/linux-pwm/20210513021631.GA878860@xxxxxxxxxxxxxxxxxx/

OK, then my expectation that Rob won't agree was wrong. Fine then.

> > > Perhaps. Though this is the PWM driver for the Xilinx AXI timer, not the
> > > Xilinx AXI PWM.
> >
> > I would be happier with "xilinx-timer-pwm" then.
>
> I've changed it to "xilinx_pwm".

OK.

Best regards
Uwe

--
Pengutronix e.K. | Uwe Kleine-König |
Industrial Linux Solutions | https://www.pengutronix.de/ |

Attachment: signature.asc
Description: PGP signature