public inbox for linux-riscv@lists.infradead.org
 help / color / mirror / Atom feed
From: Conor Dooley <conor@kernel.org>
To: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>
Cc: Conor Dooley <conor.dooley@microchip.com>,
	Thierry Reding <thierry.reding@gmail.com>,
	Daire McNamara <daire.mcnamara@microchip.com>,
	linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
	linux-riscv@lists.infradead.org
Subject: Re: [PATCH v12 1/2] pwm: add microchip soft ip corePWM driver
Date: Thu, 17 Nov 2022 22:03:13 +0000	[thread overview]
Message-ID: <Y3avobkvYK3ydKTS@spud> (raw)
In-Reply-To: <20221117210433.n5j7upqqksld42mu@pengutronix.de>

On Thu, Nov 17, 2022 at 10:04:33PM +0100, Uwe Kleine-König wrote:
> On Thu, Nov 17, 2022 at 05:38:26PM +0000, Conor Dooley wrote:
> > On Thu, Nov 17, 2022 at 05:49:50PM +0100, Uwe Kleine-König wrote:
> > > Hello Conor,
> > 
> > Hello Uwe,
> > 
> > > On Thu, Nov 10, 2022 at 09:35:12AM +0000, Conor Dooley wrote:
> > > > [...]
> > > > +
> > > > +static void mchp_core_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm,
> > > > +				 bool enable, u64 period)
> > > > +{
> > > > +	struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip);
> > > > +	u8 channel_enable, reg_offset, shift;
> > > > +
> > > > +	/*
> > > > +	 * There are two adjacent 8 bit control regs, the lower reg controls
> > > > +	 * 0-7 and the upper reg 8-15. Check if the pwm is in the upper reg
> > > > +	 * and if so, offset by the bus width.
> > > > +	 */
> > > > +	reg_offset = MCHPCOREPWM_EN(pwm->hwpwm >> 3);
> > > > +	shift = pwm->hwpwm & 7;
> > > > +
> > > > +	channel_enable = readb_relaxed(mchp_core_pwm->base + reg_offset);
> > > > +	channel_enable &= ~(1 << shift);
> > > > +	channel_enable |= (enable << shift);
> > > > +
> > > > +	writel_relaxed(channel_enable, mchp_core_pwm->base + reg_offset);
> > > > +	mchp_core_pwm->channel_enabled &= ~BIT(pwm->hwpwm);
> > > > +	mchp_core_pwm->channel_enabled |= enable << pwm->hwpwm;
> > > > +
> > > > +	/*
> > > > +	 * Notify the block to update the waveform from the shadow registers.
> > > > +	 * The updated values will not appear on the bus until they have been
> > > > +	 * applied to the waveform at the beginning of the next period. We must
> > > > +	 * write these registers and wait for them to be applied before
> > > > +	 * considering the channel enabled.
> > > > +	 * If the delay is under 1 us, sleep for at least 1 us anyway.
> > > > +	 */
> > > > +	if (mchp_core_pwm->sync_update_mask & (1 << pwm->hwpwm)) {
> > > > +		u64 delay;
> > > > +
> > > > +		delay = div_u64(period, 1000u) ? : 1u;
> > > > +		writel_relaxed(1U, mchp_core_pwm->base + MCHPCOREPWM_SYNC_UPD);
> > > > +		usleep_range(delay, delay * 2);
> > > > +	}
> > > 
> > > In some cases the delay could be prevented. e.g. when going from one
> > > disabled state to another. If you don't want to complicate the driver
> > > here, maybe point it out in a comment at least?
> > 
> > Maybe this is my naivity talking, but I'd rather wait. Is there not the
> > chance that we re-enter pwm_apply() before the update has actually gone
> > through?
> 
> My idea was to do something like that:
> 
> 	int mchp_core_pwm_apply(....)
> 	{
> 		if (mchp_core_pwm->sync_update_mask & (1 << pwm->hwpwm)) {
> 			/*
> 			 * We're still waiting for an update, don't
> 			 * interfer until it's completed.
> 			 */
> 			while (readl_relaxed(mchp_core_pwm->base + MCHPCOREPWM_SYNC_UPD)) {
> 				cpu_relax();
> 				if (waited_unreasonably_long())
> 					return -ETIMEOUT;
> 			}
> 		}
> 
> 		update_period_and_duty(...);
> 		return 0;
> 	}
> 
> This way you don't have to wait at all if the calls to pwm_apply() are
> infrequent. Of course this only works this way, if you can determine if
> there is a pending update.

Ah I think I get what you mean now about waiting for completion &
reading the bit. I don't know off the top of my head if that bit is
readable. Docs say that they're R/W but I don't know if that means that
an AXI read works or if the value is actually readable. I'll try
something like this if I can.

> From a simplicity POV always waiting is fine. But if you consider
> periods of say 5s, letting a call to pwm_apply() do a sleep between 5
> and 10 seconds at the end is quite long and blocks the caller
> considerably.

Yeah, I know. At the end of the day, you're the one familiar with what
PWM consumers expect. If things go the wait-but-maybe-exit-early
direction I think I'll add something to the limitations to cover that.

> > IIRC, but I'll have to confirm it, when the "shadow registers" are
> > enabled reads show the values that the hardware is using rather than the
> > values that are queued in the shadow registers. I'd rather avoid that
> > sort of mess and always sleep.
> > 
> > Now that I think of it, the reason I moved to unconditionally sleeping
> > was that if I turned on the PWM debugging it'd get tripped up. When it
> > tried to read the state, it got the old one rather than what'd just been
> > written.
> > 
> > Pasting my comment from above:
> > > > +	/*
> > > > +	 * Notify the block to update the waveform from the shadow registers.
> > > > +	 * The updated values will not appear on the bus until they have been
> > 
> > By "bus" in this statement, I meant on the AXI/AHB etc bus that the IP
> > core is connected to the CPUs on rather than the output. Perhaps my
> > wording of the comment could be improved and replace the word "bus" with
> > some wording containing "CPU" instead. "The updated values will not
> > appear to the CPU until" maybe.
> 
> I'd write: Reading the registers yields the currently implemented
> settings, the new ones are only readable once the current period ended.

Cool, will use that so.

> > I can also add some words relating to unconditionally sleeping w.r.t to
> > disabled states.
> > 
> > > > +	 * applied to the waveform at the beginning of the next period. We must
> > > > +	 * write these registers and wait for them to be applied before
> > > > +	 * considering the channel enabled.
> > > > +	 * If the delay is under 1 us, sleep for at least 1 us anyway.
> > > > +	 */
> > 
> > > It's not well defined if pwm_apply should only return when the new
> > > setting is actually active. (e.g. mxs doesn't wait)
> > > So I wonder: Are there any hardware restrictions between setting the
> > > SYNC_UPD flag and modifying the registers for duty and period? (I assume
> > > writing a new duty and period might then result in a glitch if the
> > > period just ends between the two writes.) Can you check if the hardware
> > > waits on such a completion, e.g. by reading that register?
> > 
> > Not entirely sure by what you mean: "waits on such a completion".
> 
> I wanted to say that it's okish to return from .apply() without the
> sleep and so return to the caller before the requested setting appears
> on the output. At least your driver wouldn't be the first to do it that
> way.

I'd be more comfortable with it if the readable registers didn't hold
the old value. 

> > The hardware updates the registers at the first end-of-period after
> > SYNC_UPD is set. Don't write the bit, nothing happens. From the docs:
> > 
> > > > A shadow register holds all values and writes them when the SYNC_UPDATE
> > > > register is set to 1. In other words, for all channel synchronous
> > > > updates, write a "1" to the SYNC_UPDATE register after writing to all
> > > > the channel registers.
> > 
> > The docs also say:
> > > > SYNC_UPDATE: When this bit is set to "1" and SHADOW_REG_EN
> > > > is selected, all POSEDGE and NEGEDGE registers are updated
> > > > synchronously. Synchronous updates to the PWM waveform occur only
> > > > when SHADOW_REG_EN is asserted and SYNC_UPDATE is set to “1”.
> > > >
> > > > When this bit is set to "0", all the POSEDGE and NEGEDGE registers
> > > > are updated asynchronously
> > 
> > The second statement is at best vague (if the this bit in "when this
> > bit" refers to the bit in SHADOW_REG_EN) or contradictory at worse.
> > I suspect it's the former meaning, as shadow registers are a per-channel
> > thing. I suppose I have to go get some docs changed, **sigh**. It
> > doesn't make all that much sense to me, SHADOW_REG_EN is a RTL parameter
> > not a register that can be accessed from the AXI interface.
> > 
> > Anyways, back to the topic at hand.. if you were to do the following
> > (in really pseudocode form..):
> > 	write(SYNC_UPD)
> > 	write(period)
> > 	<end-of-period>
> > 	write(duty)
> > 
> > Then the duty cycle would not get updated, ever. At least, per doc
> > comment #1 & my "experimental" data. The RTL is rather dumb, since
> > AFAICT, this is meant to be cheap to implement in FPGA fabric.
> > Hence the default core configuration option is no shadow registers
> > & just immediately updates the output, waveform glitches be damned.
> > 
> > Hopefully that all helps?
> 
> I already understood it that way. I hope I was able to clarify my
> thoughts above.

Yeah, I think you did!

Thanks again,
Conor.


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

  reply	other threads:[~2022-11-17 22:03 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-10  9:35 [PATCH v12 0/2] Hey Uwe, all, Conor Dooley
2022-11-10  9:35 ` [PATCH v12 1/2] pwm: add microchip soft ip corePWM driver Conor Dooley
2022-11-17 16:49   ` Uwe Kleine-König
2022-11-17 17:38     ` Conor Dooley
2022-11-17 21:04       ` Uwe Kleine-König
2022-11-17 22:03         ` Conor Dooley [this message]
2022-11-21 15:29           ` Conor Dooley
2022-11-30  9:53             ` Conor Dooley
2022-11-30 10:37               ` Uwe Kleine-König
2022-11-30 11:15                 ` Conor Dooley
2022-12-05 15:21                 ` Conor Dooley
2022-12-05 16:03                   ` Uwe Kleine-König
2022-12-05 17:13                     ` Conor Dooley
2022-12-05 18:13                       ` Uwe Kleine-König
2022-11-10  9:35 ` [PATCH v12 2/2] MAINTAINERS: add pwm to PolarFire SoC entry Conor Dooley
2022-11-10  9:38 ` [PATCH v12 0/2] Hey Uwe, all, Conor.Dooley

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=Y3avobkvYK3ydKTS@spud \
    --to=conor@kernel.org \
    --cc=conor.dooley@microchip.com \
    --cc=daire.mcnamara@microchip.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pwm@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --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