From: Conor Dooley <conor.dooley@microchip.com>
To: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>
Cc: Conor Dooley <conor@kernel.org>,
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 v13 1/2] pwm: add microchip soft ip corePWM driver
Date: Wed, 11 Jan 2023 08:00:51 +0000 [thread overview]
Message-ID: <Y75ss+aSCjx7E9SZ@wendy> (raw)
In-Reply-To: <20230111070250.w7egzcufa4waxg2n@pengutronix.de>
[-- Attachment #1: Type: text/plain, Size: 7514 bytes --]
Hey Uwe,
On Wed, Jan 11, 2023 at 08:02:50AM +0100, Uwe Kleine-König wrote:
> On Wed, Jan 11, 2023 at 12:15:29AM +0000, Conor Dooley wrote:
> > On Tue, Jan 10, 2023 at 11:48:05PM +0100, Uwe Kleine-König wrote:
> > > On Wed, Dec 21, 2022 at 11:29:12AM +0000, Conor Dooley wrote:
> > > > From: Conor Dooley <conor.dooley@microchip.com>
> >
> > > > + delay_us = DIV_ROUND_UP_ULL(remaining_ns, NSEC_PER_USEC);
> > > > + if ((delay_us / 1000) > MAX_UDELAY_MS)
> > > > + msleep(delay_us / 1000 + 1);
> > >
> > > Is this better than
> > >
> > > msleep(DIV_ROUND_UP(delay_us, 1000);
> > >
> > > ? Also I wonder about your usage of MAX_UDELAY_MS. This is about
> >
> > I probably started hacking on the example you gave and didn't notice
> > the U. What I have here is ~what you suggested last time.
>
> A series with (up to now) 13 revisions and long delays between the
> review rounds (which are mostly attributed to my time schedule) is
> difficult to handle on both sides. Some repetition isn't easy to prevent
> in such a case. Sorry for that.
It is what it is, you've only got so much time :)
> > > udelay() but you're using usleep_range()?
> > >
> > > > + else
> > > > + usleep_range(delay_us, delay_us * 2);
> > >
> > > I wonder if there isn't a function that implements something like
> > >
> > > wait_until(mchp_core_pwm->update_timestamp);
> > >
> > > which would be a bit nicer than doing this by hand. Maybe fsleep()?
> >
> > That'd be fsleep(delay_us), but does at least clean up some of the
> > messing.
> >
> > > > +static void mchp_core_pwm_apply_duty(struct pwm_chip *chip, struct pwm_device *pwm,
> > > > + const struct pwm_state *state, u64 duty_steps,
> > > > + u8 period_steps)
> > > > +{
> > > > + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip);
> > > > + u8 posedge, negedge;
> > > > + u8 period_steps_val = PREG_TO_VAL(period_steps);
> > > > +
> > > > + /*
> > > > + * Setting posedge == negedge doesn't yield a constant output,
> > > > + * so that's an unsuitable setting to model duty_steps = 0.
> > > > + * In that case set the unwanted edge to a value that never
> > > > + * triggers.
> > > > + */
> > > > + if (state->polarity == PWM_POLARITY_INVERSED) {
> > > > + negedge = !duty_steps ? period_steps_val : 0u;
> > >
> > > IMHO
> > >
> > > negedge = duty_steps ? 0 : period_steps_val;
> > >
> > > is a bit easier to parse.
> > >
> > > > + posedge = duty_steps;
> > > > + } else {
> > > > + posedge = !duty_steps ? period_steps_val : 0u;
> > > > + negedge = duty_steps;
> > > > + }
> > >
> > > The following code is equivalent:
> > >
> > > u8 first_edge = 0, second_edge = duty_steps;
> > >
> > > /*
> > > * Setting posedge == negedge doesn't yield a constant output,
> > > * so that's an unsuitable setting to model duty_steps = 0.
> > > * In that case set the unwanted edge to a value that never
> > > * triggers.
> > > */
> > > if (duty_steps == 0)
> > > first_edge = period_steps_val;
> > >
> > > if (state->polarity == PWM_POLARITY_INVERSED) {
> > > negedge = first_edge;
> > > posedge = second_edge;
> > > } else {
> > > posedge = first_edge;
> > > negedge = second_edge;
> > > }
> > >
> > > I'm not sure if it's easier to understand. What do you think?
> >
> > Despite having used them, I dislike ternary statements.
>
> My variant is a bit longer and uses more variables, but has less
> repetition. I don't expect a relevant change on the generated code. I
> slightly prefer my variant, but I let you choose which one you prefer.
Yah, I prefer anything that doesn't have ternarys in it.
> > > > + writel_relaxed(posedge, mchp_core_pwm->base + MCHPCOREPWM_POSEDGE(pwm->hwpwm));
> > > > + writel_relaxed(negedge, mchp_core_pwm->base + MCHPCOREPWM_NEGEDGE(pwm->hwpwm));
> > > > +}
> > > > +
> > > > +static void mchp_core_pwm_calc_period(const struct pwm_state *state, unsigned long clk_rate,
> > > > + u16 *prescale, u8 *period_steps)
> > > > +{
> > > > + u64 tmp;
> > > > +
> > > > + /*
> > > > + * Calculate the period cycles and prescale values.
> > > > + * The registers are each 8 bits wide & multiplied to compute the period
> > > > + * using the formula:
> > > > + * (clock_period) * (prescale + 1) * (period_steps + 1)
> > > > + * so the maximum period that can be generated is 0x10000 times the
> > > > + * period of the input clock.
> > > > + * However, due to the design of the "hardware", it is not possible to
> > > > + * attain a 100% duty cycle if the full range of period_steps is used.
> > > > + * Therefore period_steps is restricted to 0xFE and the maximum multiple
> > > > + * of the clock period attainable is 0xFF00.
> > > > + */
> > > > + tmp = mul_u64_u64_div_u64(state->period, clk_rate, NSEC_PER_SEC);
> > > > +
> > > > + /*
> > > > + * The hardware adds one to the register value, so decrement by one to
> > > > + * account for the offset
> > > > + */
> > > > + if (tmp >= MCHPCOREPWM_PERIOD_MAX) {
> > > > + *prescale = MCHPCOREPWM_PRESCALE_MAX - 1;
> > > > + *period_steps = MCHPCOREPWM_PERIOD_STEPS_MAX - 1;
> > > > +
> > > > + return;
> > > > + }
> > > > +
> > > > + *prescale = div_u64(tmp, MCHPCOREPWM_PERIOD_STEPS_MAX);
> > > > + /* PREG_TO_VAL() can produce a value larger than UINT8_MAX */
> > > > + *period_steps = div_u64(tmp, PREG_TO_VAL(*prescale)) - 1;
> > >
> > > This looks wrong, but I didn't think long about that. Did we discuss
> > > this already and/or are you sure this is correct?
> >
> > We did discuss it previously AFAICT;
> > https://lore.kernel.org/linux-pwm/896d73ac-05af-8673-8379-29011800be83@microchip.com/
> >
> > [...]
> > Unfortunately, I don't think I am seeing what you're seeing.
>
> Well, the calculation lands in the right ballpark for sure, but if my
> intuition is right, it's not as exact as it could be. I need some time
> with pencil and paper ...
>
> > [...]
> > Perhaps I need to watch a lecture on how to write a PWM driver since I
> > am clearly no good at it, given the 15 revisions. Do you know of any?
>
> I'm not aware of such a lecture.
I thought you were doing one at FOSDEM!
> I'm willing to take the blame for some
> of the revisions because I'm very picky and the math involved here isn't
> trivial.
I'd rather the maths was right. Fixing it up front is better than trying
to debug it from a customer complaint - so thanks for that.
And the maths was very naive to begin with, although I only submitted it
in the summer, I think I wrote the driver prior to having upstreamed a
single patch, and I think that showed in the maths.
> And I sometimes wonder about myself pointing out an issue in
> (say) v5 which was there unnoticed already in v1.
I dunno. I feel bad if I do that too - but if the problem you didn't
notice earlier on is a bug, rather than some sort of style comment, I
don't see why you wouldn't call it out.
> In sum a patch series going through such a high number of revisions is
> mostly a good sign.
Or that it was poor to begin with & barely improved over time...
> In the end we can be sure that the merged code is
> checked deep-rootedly and that both you and me have a certain amount of
> endurance. :-)
I wasn't really blaming you for the number of revisions, the number of
silly mistakes I've made really irritates me.
Thanks,
Conor.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
WARNING: multiple messages have this Message-ID (diff)
From: Conor Dooley <conor.dooley@microchip.com>
To: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>
Cc: Conor Dooley <conor@kernel.org>,
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 v13 1/2] pwm: add microchip soft ip corePWM driver
Date: Wed, 11 Jan 2023 08:00:51 +0000 [thread overview]
Message-ID: <Y75ss+aSCjx7E9SZ@wendy> (raw)
In-Reply-To: <20230111070250.w7egzcufa4waxg2n@pengutronix.de>
[-- Attachment #1.1: Type: text/plain, Size: 7514 bytes --]
Hey Uwe,
On Wed, Jan 11, 2023 at 08:02:50AM +0100, Uwe Kleine-König wrote:
> On Wed, Jan 11, 2023 at 12:15:29AM +0000, Conor Dooley wrote:
> > On Tue, Jan 10, 2023 at 11:48:05PM +0100, Uwe Kleine-König wrote:
> > > On Wed, Dec 21, 2022 at 11:29:12AM +0000, Conor Dooley wrote:
> > > > From: Conor Dooley <conor.dooley@microchip.com>
> >
> > > > + delay_us = DIV_ROUND_UP_ULL(remaining_ns, NSEC_PER_USEC);
> > > > + if ((delay_us / 1000) > MAX_UDELAY_MS)
> > > > + msleep(delay_us / 1000 + 1);
> > >
> > > Is this better than
> > >
> > > msleep(DIV_ROUND_UP(delay_us, 1000);
> > >
> > > ? Also I wonder about your usage of MAX_UDELAY_MS. This is about
> >
> > I probably started hacking on the example you gave and didn't notice
> > the U. What I have here is ~what you suggested last time.
>
> A series with (up to now) 13 revisions and long delays between the
> review rounds (which are mostly attributed to my time schedule) is
> difficult to handle on both sides. Some repetition isn't easy to prevent
> in such a case. Sorry for that.
It is what it is, you've only got so much time :)
> > > udelay() but you're using usleep_range()?
> > >
> > > > + else
> > > > + usleep_range(delay_us, delay_us * 2);
> > >
> > > I wonder if there isn't a function that implements something like
> > >
> > > wait_until(mchp_core_pwm->update_timestamp);
> > >
> > > which would be a bit nicer than doing this by hand. Maybe fsleep()?
> >
> > That'd be fsleep(delay_us), but does at least clean up some of the
> > messing.
> >
> > > > +static void mchp_core_pwm_apply_duty(struct pwm_chip *chip, struct pwm_device *pwm,
> > > > + const struct pwm_state *state, u64 duty_steps,
> > > > + u8 period_steps)
> > > > +{
> > > > + struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip);
> > > > + u8 posedge, negedge;
> > > > + u8 period_steps_val = PREG_TO_VAL(period_steps);
> > > > +
> > > > + /*
> > > > + * Setting posedge == negedge doesn't yield a constant output,
> > > > + * so that's an unsuitable setting to model duty_steps = 0.
> > > > + * In that case set the unwanted edge to a value that never
> > > > + * triggers.
> > > > + */
> > > > + if (state->polarity == PWM_POLARITY_INVERSED) {
> > > > + negedge = !duty_steps ? period_steps_val : 0u;
> > >
> > > IMHO
> > >
> > > negedge = duty_steps ? 0 : period_steps_val;
> > >
> > > is a bit easier to parse.
> > >
> > > > + posedge = duty_steps;
> > > > + } else {
> > > > + posedge = !duty_steps ? period_steps_val : 0u;
> > > > + negedge = duty_steps;
> > > > + }
> > >
> > > The following code is equivalent:
> > >
> > > u8 first_edge = 0, second_edge = duty_steps;
> > >
> > > /*
> > > * Setting posedge == negedge doesn't yield a constant output,
> > > * so that's an unsuitable setting to model duty_steps = 0.
> > > * In that case set the unwanted edge to a value that never
> > > * triggers.
> > > */
> > > if (duty_steps == 0)
> > > first_edge = period_steps_val;
> > >
> > > if (state->polarity == PWM_POLARITY_INVERSED) {
> > > negedge = first_edge;
> > > posedge = second_edge;
> > > } else {
> > > posedge = first_edge;
> > > negedge = second_edge;
> > > }
> > >
> > > I'm not sure if it's easier to understand. What do you think?
> >
> > Despite having used them, I dislike ternary statements.
>
> My variant is a bit longer and uses more variables, but has less
> repetition. I don't expect a relevant change on the generated code. I
> slightly prefer my variant, but I let you choose which one you prefer.
Yah, I prefer anything that doesn't have ternarys in it.
> > > > + writel_relaxed(posedge, mchp_core_pwm->base + MCHPCOREPWM_POSEDGE(pwm->hwpwm));
> > > > + writel_relaxed(negedge, mchp_core_pwm->base + MCHPCOREPWM_NEGEDGE(pwm->hwpwm));
> > > > +}
> > > > +
> > > > +static void mchp_core_pwm_calc_period(const struct pwm_state *state, unsigned long clk_rate,
> > > > + u16 *prescale, u8 *period_steps)
> > > > +{
> > > > + u64 tmp;
> > > > +
> > > > + /*
> > > > + * Calculate the period cycles and prescale values.
> > > > + * The registers are each 8 bits wide & multiplied to compute the period
> > > > + * using the formula:
> > > > + * (clock_period) * (prescale + 1) * (period_steps + 1)
> > > > + * so the maximum period that can be generated is 0x10000 times the
> > > > + * period of the input clock.
> > > > + * However, due to the design of the "hardware", it is not possible to
> > > > + * attain a 100% duty cycle if the full range of period_steps is used.
> > > > + * Therefore period_steps is restricted to 0xFE and the maximum multiple
> > > > + * of the clock period attainable is 0xFF00.
> > > > + */
> > > > + tmp = mul_u64_u64_div_u64(state->period, clk_rate, NSEC_PER_SEC);
> > > > +
> > > > + /*
> > > > + * The hardware adds one to the register value, so decrement by one to
> > > > + * account for the offset
> > > > + */
> > > > + if (tmp >= MCHPCOREPWM_PERIOD_MAX) {
> > > > + *prescale = MCHPCOREPWM_PRESCALE_MAX - 1;
> > > > + *period_steps = MCHPCOREPWM_PERIOD_STEPS_MAX - 1;
> > > > +
> > > > + return;
> > > > + }
> > > > +
> > > > + *prescale = div_u64(tmp, MCHPCOREPWM_PERIOD_STEPS_MAX);
> > > > + /* PREG_TO_VAL() can produce a value larger than UINT8_MAX */
> > > > + *period_steps = div_u64(tmp, PREG_TO_VAL(*prescale)) - 1;
> > >
> > > This looks wrong, but I didn't think long about that. Did we discuss
> > > this already and/or are you sure this is correct?
> >
> > We did discuss it previously AFAICT;
> > https://lore.kernel.org/linux-pwm/896d73ac-05af-8673-8379-29011800be83@microchip.com/
> >
> > [...]
> > Unfortunately, I don't think I am seeing what you're seeing.
>
> Well, the calculation lands in the right ballpark for sure, but if my
> intuition is right, it's not as exact as it could be. I need some time
> with pencil and paper ...
>
> > [...]
> > Perhaps I need to watch a lecture on how to write a PWM driver since I
> > am clearly no good at it, given the 15 revisions. Do you know of any?
>
> I'm not aware of such a lecture.
I thought you were doing one at FOSDEM!
> I'm willing to take the blame for some
> of the revisions because I'm very picky and the math involved here isn't
> trivial.
I'd rather the maths was right. Fixing it up front is better than trying
to debug it from a customer complaint - so thanks for that.
And the maths was very naive to begin with, although I only submitted it
in the summer, I think I wrote the driver prior to having upstreamed a
single patch, and I think that showed in the maths.
> And I sometimes wonder about myself pointing out an issue in
> (say) v5 which was there unnoticed already in v1.
I dunno. I feel bad if I do that too - but if the problem you didn't
notice earlier on is a bug, rather than some sort of style comment, I
don't see why you wouldn't call it out.
> In sum a patch series going through such a high number of revisions is
> mostly a good sign.
Or that it was poor to begin with & barely improved over time...
> In the end we can be sure that the merged code is
> checked deep-rootedly and that both you and me have a certain amount of
> endurance. :-)
I wasn't really blaming you for the number of revisions, the number of
silly mistakes I've made really irritates me.
Thanks,
Conor.
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
[-- Attachment #2: Type: text/plain, Size: 161 bytes --]
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
next prev parent reply other threads:[~2023-01-11 8:02 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-12-21 11:29 [PATCH v13 0/2] Microchip Soft IP corePWM driver Conor Dooley
2022-12-21 11:29 ` Conor Dooley
2022-12-21 11:29 ` [PATCH v13 1/2] pwm: add microchip soft ip " Conor Dooley
2022-12-21 11:29 ` Conor Dooley
2023-01-10 22:48 ` Uwe Kleine-König
2023-01-10 22:48 ` Uwe Kleine-König
2023-01-11 0:15 ` Conor Dooley
2023-01-11 0:15 ` Conor Dooley
2023-01-11 7:02 ` Uwe Kleine-König
2023-01-11 7:02 ` Uwe Kleine-König
2023-01-11 8:00 ` Conor Dooley [this message]
2023-01-11 8:00 ` Conor Dooley
2023-01-11 9:15 ` Uwe Kleine-König
2023-01-11 9:15 ` Uwe Kleine-König
2023-01-11 9:24 ` Uwe Kleine-König
2023-01-11 9:24 ` Uwe Kleine-König
2023-01-12 12:01 ` Uwe Kleine-König
2023-01-12 12:01 ` Uwe Kleine-König
2023-01-12 15:10 ` Conor Dooley
2023-01-12 15:10 ` Conor Dooley
2023-01-20 19:40 ` Conor Dooley
2023-01-20 19:40 ` Conor Dooley
2023-02-13 12:45 ` Conor Dooley
2023-02-13 12:45 ` Conor Dooley
2022-12-21 11:29 ` [PATCH v13 2/2] MAINTAINERS: add pwm to PolarFire SoC entry Conor Dooley
2022-12-21 11:29 ` Conor Dooley
2023-01-10 22:50 ` Uwe Kleine-König
2023-01-10 22:50 ` Uwe Kleine-König
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=Y75ss+aSCjx7E9SZ@wendy \
--to=conor.dooley@microchip.com \
--cc=conor@kernel.org \
--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.