[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <8cd080ef-e1f3-4752-8f92-d61c5fd321b5@baylibre.com>
Date: Wed, 22 May 2024 16:06:00 -0400
From: Trevor Gamblin <tgamblin@...libre.com>
To: Uwe Kleine-König <u.kleine-koenig@...gutronix.de>
Cc: linux-pwm@...r.kernel.org, linux-kernel@...r.kernel.org,
michael.hennerich@...log.com, nuno.sa@...log.com, dlechner@...libre.com
Subject: Re: [PATCH 1/2 v3] pwm: add duty offset support
On 2024-05-22 11:53 a.m., Uwe Kleine-König wrote:
> Hello Trevor,
>
> On Tue, May 21, 2024 at 03:49:15PM -0400, Trevor Gamblin wrote:
>> Some PWM chips support a "phase" or "duty_offset" feature. This patch
>> continues adding support for configuring this property in the PWM
>> subsystem.
>>
>> Functions duty_offset_show(), duty_offset_store(), and
>> pwm_get_duty_offset() are added to match what exists for duty_cycle.
>>
>> Add a check to disallow applying a state with both inversed polarity and
>> a nonzero duty_offset.
>>
>> Also add duty_offset to TP_printk in include/trace/events/pwm.h so that
>> it is reported with other properties when using the event tracing pipe
>> for debug.
>>
>> Signed-off-by: Trevor Gamblin <tgamblin@...libre.com>
>> ---
>> v3 changes:
>> * rebased on top of latest pwm/for-next
>> * removed changes related to cdev to match current pwm tree
>> * fixed minor whitespace issue caught by checkpatch
>>
>> v2 changes:
>> * Address feedback for driver in v1:
>> * Remove line setting supports_offset flag in pwm_chip, since that has
>> been removed from the struct in core.c.
>>
>> ---
>> drivers/pwm/core.c | 79 +++++++++++++++++++++++++++++++++++---
>> include/linux/pwm.h | 15 ++++++++
>> include/trace/events/pwm.h | 6 ++-
>> 3 files changed, 93 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
>> index 18574857641e..2ebfc7f3de8a 100644
>> --- a/drivers/pwm/core.c
>> +++ b/drivers/pwm/core.c
>> @@ -62,6 +62,7 @@ static void pwm_apply_debug(struct pwm_device *pwm,
>> */
>> if (s1.enabled && s1.polarity != state->polarity) {
>> s2.polarity = state->polarity;
>> + s2.duty_offset = s1.duty_cycle;
> s/duty_cycle/duty_offset/
Thanks for the catch.
>
>> s2.duty_cycle = s1.period - s1.duty_cycle;
>> s2.period = s1.period;
>> s2.enabled = s1.enabled;
>> @@ -103,6 +104,23 @@ static void pwm_apply_debug(struct pwm_device *pwm,
>> state->duty_cycle, state->period,
>> s2.duty_cycle, s2.period);
>>
>> + if (state->enabled &&
>> + last->polarity == state->polarity &&
>> + last->period == s2.period &&
>> + last->duty_offset > s2.duty_offset &&
>> + last->duty_offset <= state->duty_offset)
>> + dev_warn(pwmchip_parent(chip),
>> + ".apply didn't pick the best available duty offset (requested: %llu/%llu, applied: %llu/%llu, possible: %llu/%llu)\n",
>> + state->duty_offset, state->period,
> Does it make sense to emit $duty_offset/$period here? Establishing a
> consistent way to write this would be nice. Something like:
>
> $duty_cycle/$period [+$duty_offset]
>
> maybe?
I like that. I'll clean it up.
>
>> + s2.duty_offset, s2.period,
>> + last->duty_offset, last->period);
>> +
>> + if (state->enabled && state->duty_offset < s2.duty_offset)
>> + dev_warn(pwmchip_parent(chip),
>> + ".apply is supposed to round down duty_offset (requested: %llu/%llu, applied: %llu/%llu)\n",
>> + state->duty_offset, state->period,
>> + s2.duty_offset, s2.period);
>> +
>> if (!state->enabled && s2.enabled && s2.duty_cycle > 0)
>> dev_warn(pwmchip_parent(chip),
>> "requested disabled, but yielded enabled with duty > 0\n");
>> @@ -126,12 +144,13 @@ static void pwm_apply_debug(struct pwm_device *pwm,
>> if (s1.enabled != last->enabled ||
>> s1.polarity != last->polarity ||
>> (s1.enabled && s1.period != last->period) ||
>> + (s1.enabled && s1.duty_offset != last->duty_offset) ||
>> (s1.enabled && s1.duty_cycle != last->duty_cycle)) {
>> dev_err(pwmchip_parent(chip),
>> - ".apply is not idempotent (ena=%d pol=%d %llu/%llu) -> (ena=%d pol=%d %llu/%llu)\n",
>> + ".apply is not idempotent (ena=%d pol=%d %llu/%llu/%llu) -> (ena=%d pol=%d %llu/%llu/%llu)\n",
>> s1.enabled, s1.polarity, s1.duty_cycle, s1.period,
>> - last->enabled, last->polarity, last->duty_cycle,
>> - last->period);
>> + s1.duty_offset, last->enabled, last->polarity,
>> + last->duty_cycle, last->period, last->duty_offset);
>> }
>> }
>>
>> @@ -146,13 +165,24 @@ static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state)
>> int err;
>>
>> if (!pwm || !state || !state->period ||
>> - state->duty_cycle > state->period)
>> + state->duty_cycle > state->period ||
>> + state->duty_offset > state->period)
>> return -EINVAL;
>>
>> chip = pwm->chip;
>>
>> + /*
>> + * There is no need to set duty_offset with inverse polarity,
>> + * since signals with duty_offset values greater than 0.5 *
>> + * period can equivalently be represented by an inverted signal
>> + * without offset.
> This isn't exact. The equation is:
>
> state_with_offset.period = inverted_state.period
> state_with_offset.duty_cycle = inverted_state.period - inverted_state.duty_cycle
> state_with_offset.duty_offset = inverted_state.duty_cycle
>
> And with duty_offset you can express more wave-forms than with
> inversion.
Thanks for the clarification, I'll change this too.
>
>> + */
>> + if (state->polarity == PWM_POLARITY_INVERSED && state->duty_offset)
>> + return -EINVAL;
>> +
>> if (state->period == pwm->state.period &&
>> state->duty_cycle == pwm->state.duty_cycle &&
>> + state->duty_offset == pwm->state.duty_offset &&
>> state->polarity == pwm->state.polarity &&
>> state->enabled == pwm->state.enabled &&
>> state->usage_power == pwm->state.usage_power)
> While I like the added expressiveness of having .duty_offset, I think we
> shouldn't let the low-level drivers face both .duty_offset and
> .polarity.
>
> I suggest to add a new callback similar to .apply that gets passed a
> variant of pwm_state that only has
>
> u64 period
> u64 duty_cycle
> u64 duty_offset
>
> period = 0 then signals disable. Implementers are then supposed to first
> round down period to a possible period (> 0), then duty_cycle to a
> possible duty_cycle for the picked period and then duty_offset to a
> possible duty_offset with the picked period and duty_cycle.
>
> Then there is a single code location that handles the translation
> between state with polarity and state with duty_offset in the core,
> instead of case switching in each lowlevel driver. And I wouldn't
> add .duty_offset to the sysfs interface, to lure people into using the
> character device support which has several advantages over the sysfs
> API. (One reasonable justification is that we cannot remove polarity
> there, and we also cannot report polarity = normal + some duty_offset
> without possibly breaking assumptions in sysfs users.)
Makes sense. On a related note, will your pwm/chardev branch be merged soon?
>
> What is missing in my plan is a nice name for the new struct pwm_state
> and the .apply() (and matching .get_state()) callback. Maybe pwm_nstate,
> .apply_nstate() and .get_nstate()? n for "new", which however won't age
> nicely. Maybe it also makes sense to add a .round_nstate() in the same
> go. We'd have to touch all drivers anyhow and implementing a rounding
> callback (that has similar semantics to clk_round_rate() for clocks,
> i.e. it reports what would be configured for a given (n)state) isn't
> much more work. With rounding in place we could also request that
> .apply_nstate() only succeeds if rounding down decrements the values by
> less than 1, which gives still more control. (The more lax variant can
> then be implemented by first passing an nstate to .round_nstate and then
> .apply_nstate that one.)
Instead of "new", what about something like "raw", "base", "signal", or
"waveform"? I think those (even abbreviated) might make it more clear
what the purpose of the new struct is. "wstate" seems to be fairly
unique to me - a quick grep caught other uses of the string only in:
- tools/testing/selftests/net/mptcp/mptcp_connect.c
- arch/sparc/include/asm/hibernate.h
- arch/sparc/kernel/asm-offsets.c
Thanks again for the feedback. I'll spend some time thinking about this
and aim to come back with a v4 soon.
Trevor
>
> Best regards
> Uwe
>
Powered by blists - more mailing lists