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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Uwe,

On 11/19/21 3:43 AM, Uwe Kleine-König wrote:
Hello Sean,

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.

> > +	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. 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.

> > +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);

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.

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

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".

--Sean



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux