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.
WARNING: multiple messages have this Message-ID (diff)
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
next prev parent reply other threads:[~2022-11-17 22:05 UTC|newest]
Thread overview: 32+ 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 ` Conor Dooley
2022-11-10 9:35 ` [PATCH v12 1/2] pwm: add microchip soft ip corePWM driver Conor Dooley
2022-11-10 9:35 ` Conor Dooley
2022-11-17 16:49 ` Uwe Kleine-König
2022-11-17 16:49 ` Uwe Kleine-König
2022-11-17 17:38 ` Conor Dooley
2022-11-17 17:38 ` Conor Dooley
2022-11-17 21:04 ` Uwe Kleine-König
2022-11-17 21:04 ` Uwe Kleine-König
2022-11-17 22:03 ` Conor Dooley [this message]
2022-11-17 22:03 ` Conor Dooley
2022-11-21 15:29 ` Conor Dooley
2022-11-21 15:29 ` Conor Dooley
2022-11-30 9:53 ` Conor Dooley
2022-11-30 9:53 ` Conor Dooley
2022-11-30 10:37 ` Uwe Kleine-König
2022-11-30 10:37 ` Uwe Kleine-König
2022-11-30 11:15 ` Conor Dooley
2022-11-30 11:15 ` Conor Dooley
2022-12-05 15:21 ` Conor Dooley
2022-12-05 15:21 ` Conor Dooley
2022-12-05 16:03 ` Uwe Kleine-König
2022-12-05 16:03 ` Uwe Kleine-König
2022-12-05 17:13 ` Conor Dooley
2022-12-05 17:13 ` Conor Dooley
2022-12-05 18:13 ` Uwe Kleine-König
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:35 ` Conor Dooley
2022-11-10 9:38 ` [PATCH v12 0/2] Hey Uwe, all, Conor.Dooley
2022-11-10 9:38 ` 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.