Re: [PATCH 2/2] pwm: Add support for RZ/G2L GPT

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

 



Hello,

On Wed, Jun 01, 2022 at 07:25:28PM +0000, Biju Das wrote:
> > On Tue, May 10, 2022 at 03:42:59PM +0100, Biju Das wrote:
> > > +	clk_rate = clk_get_rate(pc->clk);
> > > +	c = clk_rate * period_ns;
> > 
> > This might overflow (once you keep period_ns as u64).
> OK, the logic is changed like below to avoid overflow.
> 
> 	freq = div_u64(clk_get_rate(pc->clk), 1000000);
> 	period_cycles = div_u64(freq * period_ns, 1000);

This might still overflow for big period_ns and freq > 1.

Better use mul_u64_u64_div_u64 and limit clkrate to prevent an overflow.
This yields a higher precision without overflow. Something like:

	rate = clk_get_rate(pc->clk);

	/*
	 * Refuse clk rates > 1 GHz to prevent overflowing the following
	 * calculation.
	 */
	if (rate > NSEC_PER_SEC)
		return -EINVAL;

	period_cycles = mul_u64_u64_div_u64(rate, period_ns, NSEC_PER_SEC);


> > > +	period_cycles = div_u64(c, NANO);
> > 
> > Please use NSEC_PER_SEC here.
> > 
> > > +
> > > +	if (period_cycles < 1)
> > > +		period_cycles = 1;
> > > +
> > > +	prescale = -1;
> > > +	/* prescale 1, 4, 16, 64, 256 and 1024 */
> > > +	for (i = 0, prod = 1; i < 6; i++) {
> > > +		if ((period_cycles / GTPR_MAX_VALUE * prod) == 0) {
> > > +			prescale = i;
> > > +			break;
> > > +		}
> > > +
> > > +		prod *= 4;
> > > +	}
> > 
> > This would be better understandable if you used:
> > 
> > 	for (i = 0; i < 6; i++) {
> > 		prod = 1 << (2 * i);
> > 		...
> > 
> > 	}
> > 
> > Have you tested this? The division by GTPR_MAX_VALUE (= 0xFFFFFFFF) looks
> > suspicious. Unless I'm missing something
> > 
> > 	if ((period_cycles / GTPR_MAX_VALUE * prod) == 0)
> > 
> > is equivalent to:
> > 
> > 	if (period_cycles < GTPR_MAX_VALUE)
> > 
> > . Is this really what you want here?
> 
> On the next version, I have changed the logic to check this condition in caller function.
> 
> 	if (period_cycles > 1UL * GTPR_MAX_VALUE * GPT_MAX_PRESCALE_VAL) {
> 		dev_warn(chip->dev, "ch=%d period exceed limit\n", pwm->hwpwm);
> 		return -EINVAL;
> 	}

Please always round down period, that is, if the request it's too big to
implement, do the biggest period that is possible.

With GTPR_MAX_VALUE = 0xffffffff and unsigned long being 32 bit on some
arch, this isn't a sensible test.

> > > +	/* Set counting mode */
> > > +	rzg2l_gpt_write(pc, GTUDDTYC, UP_COUNTING);
> > > +	/* Set period */
> > > +	rzg2l_gpt_write(pc, GTPR, pv);
> > > +
> > > +	/* Enable pin output */
> > > +	rzg2l_gpt_modify(pc, GTIOR, pwm->ph->mask, pwm->ph->value);
> > > +
> > > +	/* Set duty cycle */
> > > +	rzg2l_gpt_write(pc, pwm->ph->duty_reg_offset, dc);
> > > +
> > > +	/* Set initial value for counter */
> > > +	rzg2l_gpt_write(pc, GTCNT, 0);
> > > +	/* Set no buffer operation */
> > > +	rzg2l_gpt_write(pc, GTBER, 0);
> > 
> > How does the output behave on reprogramming? Does it complete the currently
> > programmed period? Please document this behaviour as e.g.
> > drivers/pwm/pwm-sl28cpld.c does.
> 
> Mode and Prescalar must be set, only when the GTCNT is stopped.
> This condition will document.

Please document this at the top of the driver. Take e.g. pwm-sifive.c as
a template. (And please stick to the format used there for easy
greppability, i.e. use "Limitations:" and a list of items with no empty
line between them.)

> > > +	return 0;
> > > +}
> > > +
> > > +static int rzg2l_gpt_enable(struct rzg2l_gpt_chip *pc) {
> > > +	/* Start count */
> > > +	rzg2l_gpt_modify(pc, GTCR, GTCR_CST, GTCR_CST);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static void rzg2l_gpt_disable(struct rzg2l_gpt_chip *pc) {
> > > +	/* Stop count */
> > > +	rzg2l_gpt_modify(pc, GTCR, GTCR_CST, 0);
> > 
> > Same question here: How does the hardware behave? Does it complete the
> > currently running period? How does the output behave? (Typical candidates
> > are: freeze at the level where it is currently, constant 0,
> > high-z.)
> 
> It is set to output low during stop.

OK, that should go to the Limitations section, too (even though it's not
a limitation).

> > > +}
> > > +
> > > +static int rzg2l_gpt_apply(struct pwm_chip *chip, struct pwm_device
> > *pwm,
> > > +			   const struct pwm_state *state)
> > > +{
> > > +	struct rzg2l_gpt_chip *pc = to_rzg2l_gpt_chip(chip);
> > > +	int ret;
> > 
> > As you don't support different polarities, there is the following
> > missing:
> 
> There is a plan to add polarity in later version.
> 
> > 
> > 	if (state->polarity != PWM_POLARITY_NORMAL)
> > 		return -EINVAL;
> 
> Agree, will add this check in initial version.

Does it output low or inactive level on disable if inversed polarity is
configured?

Best regards
Uwe

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

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Linux Samsung SOC]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux