lkml.org 
[lkml]   [2019]   [Feb]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
From
SubjectRe: [PATCH v8 1/6] pwm: extend PWM framework with PWM modes
Date


On 06.02.2019 10:24, Uwe Kleine-König wrote:
> Hello Thierry,
>
> On Wed, Feb 06, 2019 at 12:01:26AM +0100, Thierry Reding wrote:
>> On Mon, Jan 07, 2019 at 11:10:40PM +0100, Uwe Kleine-König wrote:
>>> On Mon, Jan 07, 2019 at 09:30:55AM +0000, Claudiu.Beznea@microchip.com wrote:
>>>> On 05.01.2019 23:05, Uwe Kleine-König wrote:
>>>>> On Thu, Jan 03, 2019 at 01:29:44PM +0000, Claudiu.Beznea@microchip.com wrote:
>>>>>> From: Claudiu Beznea <claudiu.beznea@microchip.com>
>>>>>>
>>>>>> Add basic PWM modes: normal and complementary. These modes should
>>>>>> differentiate the single output PWM channels from two outputs PWM
>>>>>> channels. These modes could be set as follow:
>>>>>> 1. PWM channels with one output per channel:
>>>>>> - normal mode
>>>>>> 2. PWM channels with two outputs per channel:
>>>>>> - normal mode
>>>>>> - complementary mode
>>>>>> Since users could use a PWM channel with two output as one output PWM
>>>>>> channel, the PWM normal mode is allowed to be set for PWM channels with
>>>>>> two outputs; in fact PWM normal mode should be supported by all PWMs.
>>>>>
>>>>> I still think that my suggestion that I sent in reply to your v5 using
>>>>> .alt_duty_cycle and .alt_offset is the better one as it is more generic.
>>>>
>>>> I like it better my way, I explained myself why.
>>>
>>> I couldn't really follow your argument though. You seemed to acknowledge
>>> that using .alt_duty_cycle and .alt_offset is more generic. Then you
>>> wrote that the push-pull mode is hardware generated on Atmel with some
>>> implementation details. IMHO these implementation details shouldn't be
>>> part of the PWM API and atmel's .apply should look as follows:
>>>
>>> if (state->alt_duty_cycle == 0) {
>>>
>>> ... configure for normal mode ...
>>>
>>> } else if (state->duty_cycle == state->alt_duty_cycle &&
>>> state->alt_offset == state->period / 2) {
>>>
>>> ... configure for push pull mode ...
>>>
>>> } else if (state->duty_cycle + state->alt_duty_cycle == state->period &&
>>> state->alt_offset == state->duty_cycle) {
>>>
>>> ... configure for complementary mode ...
>>>
>>> } else {
>>> return -EINVAL;
>>> }
>>>
>>> If it turns out to be a common pattern, we can add helper functions à la
>>> pwm_is_complementary_mode(state) and
>>> pwm_set_complementary_mode(state, period, duty_cycle). This allows to
>>> have a generic way to describe a wide range of wave forms in a uniform
>>> way in the API (which is good) and each driver implements the parts of
>>> this range that it can support.
>>
>> I think this is going to be the rule rather than the exception, so I'd
>> expect we'll see these helpers used in pretty much all drivers that
>> support more than just the normal mode.
>
> If you intended to contradict me here: You didn't. I have the same
> expectation.
>
>> But I really don't see the point in having consumers jump through hoops
>> to set one of the standard modes just to have the driver jump through
>> more hoops to determine which mode was meant.
>
> I think my approach is more natural and not more complicated at all. In
> all modes where this secondary output makes sense both outputs share the
> period length. In all modes both outputs have a falling and a raising
> edge each. Let's assume we support
>
> - normal mode (one output, secondary (if available) inactive)
> - complementary mode (secondary output is the inverse of primary
> output)
> - push-pull mode (primary output only does every second active phase,
> the secondy output does the ones that are skiped by the primary one)
> - complementary mode with deadtime (like above but there is a pause
> where both signals are inactive at the switch points, so the active
> phase of the secondary output is $deadtime_pre + $deadtime_post
> shorter than the primary output's inactive phase).
>
> To describe these modes we need with the approach suggested by Claudiu
> the following defines:
>
> enum mode {
> NORMAL,
> COMPLEMENTARY,
> PUSH_PULL
> PUSH_PULL_WITH_DEADTIME

I see push-pull mode as a particular mode of complementary mode with
dead-times equal to a half of a period.

I don't get the meaning of push-pull with dead-times, there is already
there a deadtime pre, post value equal with 1/2 of period.

> }
>
> struct pwm_state {
> unsigned int period;
> unsigned int duty_cycle;
> enum pwm_polarity polarity;
> bool enabled;
> enum mode mode;
> unsigned int deadtime_pre;
> unsigned int deadtime_post;
> }
>
> This has the following downsides:
>
> - The period in push-pull mode is somehow wrong because the signal
> combination repeats only every 2x $period ns. (I guess this is an
> implementation detail of the atmel hardware that leaks into the API
> here.)

As far as I'm concern of the PWM push-pull mode it is a specific PWM
working mode, not related to Atmel, which can be used to drive half-bridge
converters.

>
> - There is redundancy in the description:
>
> { .period = U, .duty_cycle = V, .polarity = W, .enabled = true, .mode = PUSH_PULL, .deadtime_pre = DC, .deadtime_post = DC }
>
> is the same as
>
> { .period = U, .duty_cycle = V, .polarity = W, .enabled = true, .mode = PUSH_PULL_WITH_DEADTIME, .deadtime_pre = 0, .deadtime_post = 0 }
>
> .
>
> This is all more sane with my suggestion, and pwm_state is smaller with
> my approach. .period has always the same meaning and for a device that
> supports secondary mode .alt_offset and .alt_period always have the same
> semantic. (Opposed to .deadtime_X that only matter sometimes.)
>
> Also I don't see hoops for the implementing pwm driver: Assume it only
> supports normal and complementary mode. The difference is:
>
> - With Claudiu's approach:
>
> switch (state->mode) {
> case NORMAL:
> ... do normal ...
> break;
> case COMPLEMENTARY:
> ... do complementary ...
> break;
> default:
> return -ESOMETHING;
> break;
> }
>
> - with my approach:
>
> if (pwm_is_normal_mode(state) {
> ... do normal ...
> } else if (pwm_is_complementary_mode(state) {
> .. do complementary ...
> } else {
> return -ESOMETHING;
> }
>
> So I don't see a hoop apart from needed some pwm_is_XX_mode helpers in
> the core. Moreover for a flexible hardware that supports the full range
> (e.g. the hifive one where a driver is currently under discussion if
> only one pwm cell is implemented as I suggested in my review) the
> implementation is simpler with my approach it just looks as follows:
>
> configure(period, duty_cycle, alt_offset, alt_period)
>
> instead of
>
> switch (state->mode) {
> case NORMAL:
> configure(period, duty_cycle, 0, 0);
> break;
> case COMPLEMENTARY:
> configure(period, duty_cycle, duty_cycle, period - duty_cycle);
> break;
> case PUSH_PULL:
> configure(2 * period, duty_cycle, period, duty_cycle);
> break;
> case PUSH_PULL_WITH_DEADTIME:
> configure(2 * period, duty_cycle, period + deadtime_pre,
> duty_cycle - deadtime_pre - deadtime_post);
> break;
> default:
> return -ESOMETHING;
> break;
> }
>
>> There are only so many modes and I have never seen hardware that
>> actually implements the kind of fine-grained control that would be
>> possible with your proposal.
>
> That there is hardware that actually implements all the flexibility that
> is available is second-order. (But as said above, the hifive
> implementation can do it. And I think the ST implementation I saw some
> time ago can do it, too; I didn't recheck though.) The key here is to
> have a natural description of the intended waveform. And describing it
> using a mode and additional parameters depending on the mode is more
> complex than two additional parameters that can cover all waveforms.
>
> That not all drivers can implement all waveforms that consumers might
> request is common to both approaches.
>
>> The goal of an API is to abstract, but .alt_duty_cycle and .alt_offset
>> would be an inversion of API abstraction.
>
> No, the goal of an API is to give a way that is easy and natural to let
> consumers request all the stuff they might want. And if there is a
> single set of parameters that describes a broad subset of waveforms with
> parameters that you can measure in the wave form that is better than to
> separate waveforms into categories (modes) and implement each of these
> with their own parameter set. And then your categorisation might not
> match the capabilities of some hardware. Consider a device that can
> implement PUSH_PULL_WITH_DEADTIME but only with .deadtime_pre =
> .deadtime_post.
>
> That the API has to abstract is actually bad because it limits users.
> If a consumer wants push-pull mode with dead time and the hardware
> supports that but the API has abstracted that away, that's bad.
> If a consumer doesn't care if the configured duty cycle is already
> active when pwm_apply_state() returns or is only scheduled in the
> hardware for after the current period ends, this forces a delay on the
> consumer because the abstraction is that the configured wave form is
> already on the wire on return.
>
> Abstraction is necessary to cover different hardware implementations and
> allow users to handle these in a uniform way. So from a consumer's POV
> an abstraction that doesn't limit the accessible capabilities of the
> hardware is optimal.
>
> Using .alt_offset and .alt_period is an abstraction that limits less and
> so gives more possibilities to the consumers.
>
>> That is, we'd be requiring the drivers to abstract the inputs of the
>> API, which is the wrong way around.
>
> That is a normal "problem" for drivers. The driver gets a request and it
> has to determine if it can implement that. And if this is done using a
> comparison of .mode to known "good" values or by using a helper function
> that compares .alt_offset to .period is an implementation detail that
> doesn't matter much.
>
>>>>> I don't repeat what I wrote there assuming you still remember or are
>>>>> willing to look it up at
>>>>> e.g. https://www.spinics.net/lists/linux-pwm/msg08174.html (in the 2nd half
>>>>> of my mail).
>>>>
>>>> Yes, I remember it.
>>>
>>> I expected that, my words were more directed to Thierry than you.
>>>
>>>>> Also I think that if the capabilities function is the way forward adding
>>>>> support to detect availability of polarity inversion should be
>>>>> considered.
>>>>
>>>> Yep, why not. But it should be done in a different patch. It is not related
>>>> to this series.
>>>
>>> Yes, given that polarity already exists, this would be a good
>>> opportunity to introduce the capability function for that and only
>>> afterwards add the new use case with modes. (But having said this, read
>>> further as I think that this capability function is a bad idea.)
>>
>> I don't think we need to require this. The series is already big enough
>> as it is and has been in the works for long enough. There's no harm in
>> integrating polarity support into the capability function later on.
>
> I think "the series is long enough in the works" is not an argument to
> stop pointing out weaknesses. The harm that is done in not adding
> polarity support now is that it adds another thing to the todo list of
> things that are started a bit and need to be completed in the future.
>
> And the harm in adding underdone stuff to an API if there are known
> weaknesses is more work later.
>
>>>>> This would also be an opportunity to split the introduction
>>>>> of the capabilities function and the introduction of complementary mode.
>>>>> (But my personal preference would be to just let .apply fail when an
>>>>> unsupported configuration is requested.)
>>>>
>>>> .apply fails when something wrong is requested.
>>>
>>> If my controller doesn't support a second output is it "wrong" to
>>> request complementary mode? I'd say yes. So you have to catch that in
>>> .apply anyhow and there is little benefit to be able to ask the
>>> controller if it supports it beforehand.
>>>
>>> I don't have a provable statistic at hand, but my feeling is that quite
>>> some users of the i2c frame work get it wrong to first check the
>>> capabilities and only then try to use them. This is at least error prone
>>> and harder to use than the apply function returning an error code.
>>> And on the driver side the upside is to have all stuff related to which
>>> wave form can be generated and which cannot is a single place. (Just
>>> consider "inverted complementary mode". Theoretically this should work
>>> if your controller supports complementary mode and inverted mode. If you
>>> now have a driver for a controller that can do both, but not at the same
>>> time, the separation gets ugly. OK, this is a constructed example, but
>>> in my experience something like that happens earlier or later.)
>>
>> I think capabilities are useful in order to be able to implement
>> fallbacks in consumer drivers. Sure the same thing could be implemented
>> by trying to apply one state first and then downgrade and retry on
>> failure and so on, but sometimes it's more convenient to know what's
>> possible and determine what's the correct solution upfront.
>
> For me there is no big difference between:
>
> Oh, the driver cannot do inversed polarity, I have to come up
> with something else.
>
> and
>
> Oh, the driver can only implement periods that are powers of two
> of the input clk, I have to come up with something else.
>
> and
>
> Oh, I requested a duty cycle of 89 ns, but the hardware can only
> do 87 or 90 ns so I have to come up with something else.
>
> With capabilities you can only cover the first of these. With an
> approach similar to clk_round_rate you can easily cover all.
>
> Best regards
> Uwe
>
\
 
 \ /
  Last update: 2019-02-13 16:41    [W:2.462 / U:0.012 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site