linux-pwm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Biju Das <biju.das.jz@bp.renesas.com>
To: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>
Cc: Thierry Reding <thierry.reding@gmail.com>,
	"linux-pwm@vger.kernel.org" <linux-pwm@vger.kernel.org>,
	Geert Uytterhoeven <geert+renesas@glider.be>,
	Prabhakar Mahadev Lad <prabhakar.mahadev-lad.rj@bp.renesas.com>,
	"linux-renesas-soc@vger.kernel.org" 
	<linux-renesas-soc@vger.kernel.org>
Subject: RE: [PATCH v16] pwm: Add Renesas RZ/G2L MTU3a PWM driver
Date: Tue, 16 May 2023 08:14:38 +0000	[thread overview]
Message-ID: <OS0PR01MB592277041AD411D212853FF586799@OS0PR01MB5922.jpnprd01.prod.outlook.com> (raw)
In-Reply-To: <20230516064304.cdiifro7lb7ne2jp@pengutronix.de>

Hi Uwe,

Thanks for the feedback.

> Subject: Re: [PATCH v16] pwm: Add Renesas RZ/G2L MTU3a PWM driver
> 
> Hello Biju,
> 
> here now comes my promised review. Took a bit longer than anticipated,
> sorry for that.

I know you are busy with "Convert to platform remove callback returning void".

> 
> On Tue, Apr 18, 2023 at 11:20:37AM +0100, Biju Das wrote:
> > +static u8 rz_mtu3_pwm_calculate_prescale(struct rz_mtu3_pwm_chip
> *rz_mtu3,
> > +					 u64 period_cycles)
> > +{
> > +	u32 prescaled_period_cycles;
> > +	u8 prescale;
> > +
> > +	prescaled_period_cycles = period_cycles >> 16;
> > +	if (prescaled_period_cycles >= 16)
> > +		prescale = 3;
> > +	else
> > +		prescale = (fls(prescaled_period_cycles) + 1) / 2;
> > +
> > +	return prescale;
> 
> That value is supposed to be written to RZ_MTU3_TCR_TPCS, right? This is
> a 3bit register field and in .get_state() you handle values up to 7. I
> wonder why the max here is 3 only.

I thought, for the initial basic driver, support bit values {0, 1, 2, 3} as
It is same for all MTU channels and later plan to support the complicated 
external and internal clocks as it different for each channels.

Are you ok with this plan? Please let me know.

Eg:

MTU0:
TCR2[2:0]      	: TCR[2:0]
0              	:{4,5,6,7}	:- External clock: counts on {MTCLKA.. MTCLKD} pin input   
{1,2,3,4,5}    	: x		:- Internal clock: P0φ {2, 8, 32, 256, 1024}
6			: x		:- Setting Prohibited
7			: x		:- External clock: counts on MTIOC1A pin input

MTU1:
TCR2[2:0]      	: TCR[2:0]
0              	: {4, 5}	:- External clock: counts on {MTCLKA.. MTCLKB} pin input
0              	: 6  		:- Internal clock: counts on P0φ/256   
0              	: 7		:- Overflow/underflow of MTU2.TCNT   
{1,2,3,4}      	: x	   	:- Internal clock: P0φ {2, 8, 32, 1024}
{5,6,7}		: x		:- Setting Prohibited

MTU2:
TCR2[2:0]      	: TCR[2:0]
0              	: {4, 5,6}	:- External clock: counts on {MTCLKA.. MTCLKC} pin input   
0              	: 7 		:- Internal clock: P0φ/1024   
{1,2,3,4}      	: x	   	:- Internal clock: P0φ/ {2, 8, 32, 256}
{5,6,7}		: x		:- Setting Prohibited


MTU3, MTU4, MTU6, MTU7, and MTU8
TCR2[2:0]      	: TCR[2:0]
0			: {4, 5}	:- Internal clock: P0φ/ {256, 1024}		
0              	: {6, 7}    :- External clock: counts on {MTCLKA.. MTCLKB} pin input   
{1,2,3}        	: x	   	:- Internal clock: P0φ/ {2, 8, 32}
{4,5,6,7}		: x		:- Setting Prohibited


MTU5:
TCR2[2:0]      	: TCR[1:0]
{1, 2, 3, 4, 5}  	:  x	   	:- Internal clock: P0φ/ {2, 8, 32}
6			:  x		:- Setting Prohibited
7			:  x		:- Internal clock: counts on MTIOC1A pin input

> 
> > +}
> > +
> > [...]
> > +static int rz_mtu3_pwm_get_state(struct pwm_chip *chip, struct
> pwm_device *pwm,
> > +				 struct pwm_state *state)
> > +{
> > +	struct rz_mtu3_pwm_chip *rz_mtu3_pwm = to_rz_mtu3_pwm_chip(chip);
> > +
> > +	pm_runtime_get_sync(chip->dev);
> 
> Return value checking?

OK, will switch to error_check version "pm_runtime_resume_and_get()"
as Geert suggested ??

Normally "pm_runtime_get_sync" is non error check version
and user make sure the usage count is balanced.

> 
> > +	state->enabled = rz_mtu3_pwm_is_ch_enabled(rz_mtu3_pwm, pwm-
> >hwpwm);
> > +	if (state->enabled) {
> > +		struct rz_mtu3_pwm_channel *priv;
> > +		u8 prescale, val;
> > +		u16 dc, pv;
> > +		u64 tmp;
> > +
> > +		priv = rz_mtu3_get_channel(rz_mtu3_pwm, pwm->hwpwm);
> > +		if (priv->map->base_pwm_number == pwm->hwpwm)
> > +			rz_mtu3_pwm_read_tgr_registers(priv, RZ_MTU3_TGRA,
> &pv,
> > +						       RZ_MTU3_TGRB, &dc);
> > +		else
> > +			rz_mtu3_pwm_read_tgr_registers(priv, RZ_MTU3_TGRC,
> &pv,
> > +						       RZ_MTU3_TGRD, &dc);
> > +
> > +		val = rz_mtu3_8bit_ch_read(priv->mtu, RZ_MTU3_TCR);
> > +		prescale = FIELD_GET(RZ_MTU3_TCR_TPCS, val);
> > +
> > +		/* With prescale <= 7 and pv <= 0xffff this doesn't
> overflow. */
> > +		tmp = NSEC_PER_SEC * (u64)pv << (2 * prescale);
> > +		state->period = DIV_ROUND_UP_ULL(tmp, rz_mtu3_pwm->rate);
> > +		tmp = NSEC_PER_SEC * (u64)dc << (2 * prescale);
> > +		state->duty_cycle = DIV_ROUND_UP_ULL(tmp, rz_mtu3_pwm-
> >rate);
> > +	}
> > +
> > +	if (state->duty_cycle > state->period)
> > +		state->duty_cycle = state->period;
> 
> Without having assigned a value to duty_cycle and period this looks a
> bit strange. Maybe move it into the if block above?

Agreed. 

> 
> > +	state->polarity = PWM_POLARITY_NORMAL;
> > +	pm_runtime_put(chip->dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static u16 rz_mtu3_pwm_calculate_pv_or_dc(u64 period_or_duty_cycle,
> > +u8 prescale) {
> > +	return (period_or_duty_cycle >> (2 * prescale)) <= U16_MAX ?
> > +		period_or_duty_cycle >> (2 * prescale) : U16_MAX;
> 
> This is equivalent to
> 
> 	return min(period_or_duty_cycle >> (2 * prescale), (u64)U16_MAX);
> 
> I like this variant better, but maybe that's subjective?

OK, will change.

> 
> > +}
> > +
> > +static int rz_mtu3_pwm_config(struct pwm_chip *chip, struct
> pwm_device *pwm,
> > +			      const struct pwm_state *state) {
> > +	struct rz_mtu3_pwm_chip *rz_mtu3_pwm = to_rz_mtu3_pwm_chip(chip);
> > +	struct rz_mtu3_pwm_channel *priv;
> > +	u64 period_cycles;
> > +	u64 duty_cycles;
> > +	u8 prescale;
> > +	u16 pv, dc;
> > +	u8 val;
> > +	u32 ch;
> > +
> > +	priv = rz_mtu3_get_channel(rz_mtu3_pwm, pwm->hwpwm);
> > +	ch = priv - rz_mtu3_pwm->channel_data;
> > +
> > +	period_cycles = mul_u64_u32_div(state->period, rz_mtu3_pwm->rate,
> > +					NSEC_PER_SEC);
> > +	prescale = rz_mtu3_pwm_calculate_prescale(rz_mtu3_pwm,
> > +period_cycles);
> > +
> > +	/*
> > +	 * Prescalar is shared by multiple channels, so prescale can
> > +	 * NOT be modified when there are multiple channels in use with
> > +	 * different settings. Modify prescalar if other PWM is off or
> handle
> > +	 * it, if current prescale value is less than the one we want to
> set.
> > +	 */
> > +	if (rz_mtu3_pwm->enable_count[ch] > 1) {
> > +		if (rz_mtu3_pwm->prescale[ch] > prescale)
> > +			return -EBUSY;
> > +
> > +		prescale = rz_mtu3_pwm->prescale[ch];
> > +	}
> > +
> > +	pv = rz_mtu3_pwm_calculate_pv_or_dc(period_cycles, prescale);
> > +
> > +	duty_cycles = mul_u64_u32_div(state->duty_cycle, rz_mtu3_pwm-
> >rate,
> > +				      NSEC_PER_SEC);
> > +	dc = rz_mtu3_pwm_calculate_pv_or_dc(duty_cycles, prescale);
> > +
> > +	/*
> > +	 * If the PWM channel is disabled, make sure to turn on the clock
> > +	 * before writing the register.
> > +	 */
> > +	if (!pwm->state.enabled)
> > +		pm_runtime_get_sync(chip->dev);
> 
> Error checking?

OK will switch to error check version " pm_runtime_resume_and_get()"??

> 
> > +
> > +	val = RZ_MTU3_TCR_CKEG_RISING | prescale;
> > +
> > +	/* Counter must be stopped while updating TCR register */
> > +	if (rz_mtu3_pwm->prescale[ch] != prescale && rz_mtu3_pwm-
> >enable_count[ch])
> > +		rz_mtu3_disable(priv->mtu);
> > +
> > +	if (priv->map->base_pwm_number == pwm->hwpwm) {
> > +		rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TCR,
> > +				      RZ_MTU3_TCR_CCLR_TGRA | val);
> > +		rz_mtu3_pwm_write_tgr_registers(priv, RZ_MTU3_TGRA, pv,
> > +						RZ_MTU3_TGRB, dc);
> > +	} else {
> > +		rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TCR,
> > +				      RZ_MTU3_TCR_CCLR_TGRC | val);
> > +		rz_mtu3_pwm_write_tgr_registers(priv, RZ_MTU3_TGRC, pv,
> > +						RZ_MTU3_TGRD, dc);
> > +	}
> > +
> > +	if (rz_mtu3_pwm->prescale[ch] != prescale) {
> > +		/*
> > +		 * Prescalar is shared by multiple channels, we cache the
> > +		 * prescalar value from first enabled channel and use the
> same
> > +		 * value for both channels.
> > +		 */
> > +		rz_mtu3_pwm->prescale[ch] = prescale;
> > +
> > +		if (rz_mtu3_pwm->enable_count[ch])
> > +			rz_mtu3_enable(priv->mtu);
> > +	}
> > +
> > +	/* If the PWM is not enabled, turn the clock off again to save
> power. */
> > +	if (!pwm->state.enabled)
> > +		pm_runtime_put(chip->dev);
> > +
> > +	return 0;
> > +}
> 

Cheers,
Biju

  parent reply	other threads:[~2023-05-16  8:14 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-04-18 10:20 [PATCH v16] pwm: Add Renesas RZ/G2L MTU3a PWM driver Biju Das
2023-05-08  7:56 ` Biju Das
2023-05-08  9:24   ` Uwe Kleine-König
2023-05-16  6:43 ` Uwe Kleine-König
2023-05-16  7:12   ` Geert Uytterhoeven
2023-05-16  8:14   ` Biju Das [this message]
2023-05-22  7:43     ` Biju Das
2023-05-22  7:48       ` Uwe Kleine-König
2023-05-22  8:06         ` Biju Das

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=OS0PR01MB592277041AD411D212853FF586799@OS0PR01MB5922.jpnprd01.prod.outlook.com \
    --to=biju.das.jz@bp.renesas.com \
    --cc=geert+renesas@glider.be \
    --cc=linux-pwm@vger.kernel.org \
    --cc=linux-renesas-soc@vger.kernel.org \
    --cc=prabhakar.mahadev-lad.rj@bp.renesas.com \
    --cc=thierry.reding@gmail.com \
    --cc=u.kleine-koenig@pengutronix.de \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).