Re: [RFC v3 09/12] gpiolib: cdev: Add hardware timestamp clock type
From: Dipen Patel
Date: Tue Dec 07 2021 - 20:40:39 EST
On 12/1/21 4:53 PM, Kent Gibson wrote:
> On Wed, Dec 01, 2021 at 10:01:46AM -0800, Dipen Patel wrote:
>> Hi,
>>
>>
>> On 12/1/21 9:16 AM, Kent Gibson wrote:
>>> On Tue, Nov 30, 2021 at 07:29:20PM -0800, Dipen Patel wrote:
>>>> Hi,
>>>>
>>>> On 11/25/21 5:31 PM, Kent Gibson wrote:
>>>>> On Tue, Nov 23, 2021 at 11:30:36AM -0800, Dipen Patel wrote:
>>>>>> This patch adds new clock type for the GPIO controller which can
>>>>>> timestamp gpio lines in realtime using hardware means. To expose such
>>>>>> functionalities to the userspace, code has been added in this patch
>>>>>> where during line create call, it checks for new clock type and if
>>>>>> requested, calls hardware timestamp related API from gpiolib.c.
>>>>>> During line change event, the HTE subsystem pushes timestamp data
>>>>>> through callbacks.
>>>>>>
>>>>>> Signed-off-by: Dipen Patel <dipenp@xxxxxxxxxx>
>>>>>> Acked-by: Linus Walleij <linus.walleij@xxxxxxxxxx>
>>>>>> ---
>>>>>> Changes in v2:
>>>>>> - Added hte_dir and static structure hte_ts_desc.
>>>>>> - Added callbacks which get invoked by HTE when new data is available.
>>>>>> - Better use of hte_dir and seq from hte_ts_desc.
>>>>>> - Modified sw debounce function to accommodate hardware timestamping.
>>>>>>
>>>>>> drivers/gpio/gpiolib-cdev.c | 161 ++++++++++++++++++++++++++++++++++--
>>>>>> include/uapi/linux/gpio.h | 1 +
>>>>>> 2 files changed, 153 insertions(+), 9 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
>>>>>> index c7b5446d01fd..1736ad54e3ec 100644
>>>>>> --- a/drivers/gpio/gpiolib-cdev.c
>>>>>> +++ b/drivers/gpio/gpiolib-cdev.c
>>>>>> @@ -464,6 +464,12 @@ struct line {
>>>>>> * stale value.
>>>>>> */
>>>>>> unsigned int level;
>>>>>> + /*
>>>>>> + * dir will be touched in HTE callbacks hte_ts_cb_t and
>>>>>> + * hte_ts_threaded_cb_t and they are mutually exclusive. This will be
>>>>>> + * unused when HTE is not supported/disabled.
>>>>>> + */
>>>>>> + enum hte_dir dir;
>>>>>> };
>>>>>>
>>>>> Documentation should be in present tense, so
>>>>>
>>>>> s/will be/is/g
>>>>>
>>>>> Same applies to other patches.
>>>>>
>>>>> Also
>>>>>
>>>>> s/touched/accessed/
>>>>>
>>>>> dir is a poor name for the field. It is the hte edge direction and
>>>>> effectively the line level, so call it hte_edge_dirn or
>>>>> hte_edge_direction or hte_level.
>>>>>
>>>>> And it is placed in a section of the struct documented as "debouncer specific
>>>>> fields", but it is not specfic to the debouncer. Add a "hte specific
>>>>> fields" section if nothing else is suitable.
>>>>>
>>>>>> /**
>>>>>> @@ -518,6 +524,7 @@ struct linereq {
>>>>>> GPIO_V2_LINE_DRIVE_FLAGS | \
>>>>>> GPIO_V2_LINE_EDGE_FLAGS | \
>>>>>> GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME | \
>>>>>> + GPIO_V2_LINE_FLAG_EVENT_CLOCK_HARDWARE | \
>>>>>> GPIO_V2_LINE_BIAS_FLAGS)
>>>>>>
>>>>>> static void linereq_put_event(struct linereq *lr,
>>>>>> @@ -546,6 +553,94 @@ static u64 line_event_timestamp(struct line *line)
>>>>>> return ktime_get_ns();
>>>>>> }
>>>>>>
>>>>>> +static hte_return_t process_hw_ts_thread(void *p)
>>>>>> +{
>>>>>> + struct line *line = p;
>>>>>> + struct linereq *lr = line->req;
>>>>>> + struct gpio_v2_line_event le;
>>>>>> + u64 eflags;
>>>>>> +
>>>>>> + memset(&le, 0, sizeof(le));
>>>>>> +
>>>>>> + le.timestamp_ns = line->timestamp_ns;
>>>>>> + line->timestamp_ns = 0;
>>>>>> +
>>>>> What is the purpose of this zeroing?
>>>>>
>>>>>> + if (line->dir >= HTE_DIR_NOSUPP) {
>>>>>> + eflags = READ_ONCE(line->eflags);
>>>>>> + if (eflags == GPIO_V2_LINE_FLAG_EDGE_BOTH) {
>>>>>> + int level = gpiod_get_value_cansleep(line->desc);
>>>>>> +
>>>>>> + if (level)
>>>>>> + /* Emit low-to-high event */
>>>>>> + le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
>>>>>> + else
>>>>>> + /* Emit high-to-low event */
>>>>>> + le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
>>>>>> + } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) {
>>>>>> + /* Emit low-to-high event */
>>>>>> + le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
>>>>>> + } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) {
>>>>>> + /* Emit high-to-low event */
>>>>>> + le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
>>>>>> + } else {
>>>>>> + return HTE_CB_ERROR;
>>>>>> + }
>>>>>> + } else {
>>>>>> + if (line->dir == HTE_RISING_EDGE_TS)
>>>>>> + le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
>>>>>> + else
>>>>>> + le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
>>>>>> + }
>>>>> The mapping from line->dir to le.id needs to take into account the active
>>>>> low setting for the line.
>>>>>
>>>>> And it might be simpler if the hte_ts_data provided the level, equivalent
>>>>> to gpiod_get_raw_value_cansleep(), rather than an edge direction, so you
>>>>> can provide a common helper to determine the edge given the raw level.
>>>> (So from the level determine the edge?) that sound right specially when
>>>>
>>>> HTE provider has capability to record the edge in that case why bother
>>>>
>>>> getting the level and determine edge?
>>>>
>>>> Calculating the edge from the level makes sense when hte provider does not
>>>>
>>>> have that feature and that is what if (line->dir >= HTE_DIR_NOSUPP) does.
>>>>
>>> As asked in the review of patch 02, do you have an example of hardware that
>>> reports an edge direction rather than NOSUPP?
>> No...
> So you are adding an interface that nothing will currently use.
> Are there plans for hardware that will report the edge, and you are
> laying the groundwork here?
Adding here for the general case should there be provider
available with such feature.
>
>>> Anyway, this is just a naming thing - the information content being passed
>>> is the the same, be it high/low/unknown or rising/falling/unknown.
>>>
>>> If the hardware does report edge direction then it is just one bit, and
>>> that also corresponds to the physical level immediately following the
>>> edge, so no additional conversion required there.
>>>
>>> It would be clearer to pass a level than an edge, as
>>> - a hte edge (hte_dir) could be confused with a cdev edge
>>> (gpio_v2_line_event_id), in mails like this if not in the code.
>>> - cdev will fallback to using the physical level to determine the edge
>>> if the hte can't provide it
>> I believe I have retained all the fallback logic in such case.
>>> - cdev has to perform inversion if active low and it already does that
>>> based on levels
>>>
>>>>>> +
>>>>>> + le.line_seqno = line->line_seqno;
>>>>>> + le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno;
>>>>>> + le.offset = gpio_chip_hwgpio(line->desc);
>>>>>> +
>>>>>> + linereq_put_event(lr, &le);
>>>>>> +
>>>>>> + return HTE_CB_HANDLED;
>>>>>> +}
>>>>>> +
>>>>>> +static hte_return_t process_hw_ts(struct hte_ts_data *ts, void *p)
>>>>>> +{
>>>>>> + struct line *line = p;
>>>>>> + struct linereq *lr = line->req;
>>>>>> +
>>>>>> + if (!ts)
>>>>>> + return HTE_CB_ERROR;
>>>>>> +
>>>>>> + line->timestamp_ns = ts->tsc;
>>>>>> + line->dir = ts->dir;
>>>>>> +
>>>>> The doc for timestamp_ns states:
>>>>>
>>>>> * timestamp_ns and req_seqno are accessed only by
>>>>> * edge_irq_handler() and edge_irq_thread(), which are themselves
>>>>> * mutually exclusive, so no additional protection is necessary.
>>>>>
>>>>> That no longer holds. It is now also accessed here, and in
>>>>> process_hw_ts_thread(), which wont run concurrently with each other or
>>>>> the edge_irq_* handlers, but also in debounce_work_func() which may run
>>>>> concurrently with the others.
>>>>> So timestamp_ns now requires protection from concurrent access.
>>>>>
>>>>>> + /*
>>>>>> + * It is possible that HTE engine detects spurious edges for the
>>>>>> + * lines where software debounce is enabled. This primary callback
>>>>>> + * will be called multiple times in that case. It will be better to
>>>>>> + * let debounce_work_func handle instead of process_hw_ts_thread.
>>>>>> + * The timestamp_ns will be overwritten here which is fine as we are
>>>>>> + * interested in the last value anyway. The debounce_work_func will
>>>>>> + * then just read whatever last line->timestamp_ns is stored. Because
>>>>>> + * this callback can be called multiple times, we are not really
>>>>>> + * interested in ts->seq.
>>>>>> + */
>>>>> Not sure what this is trying to say.
>>>>> Is this the primary callback? Or debounce_irq_handler()?
>>>> This is primary callback called from HTE when it pushes new TS data per line, it
>>>>
>>>> also says so in the second line.
>>>>
>>> Yeah, I probably read that as "The primary callback", but it is
>>> confusing anyway. "This primary callback" implies there is another
>>> primary callback.
>>> Just say "This handler" instead of "This primary callback".
>> Noted...
>>>>> You say you really aren't interested in ts->seq, but the code immediately
>>>>> uses it.
>>>> That is when sw_debounced is not set and whole paragraph is about when
>>>>
>>>> sw_debounced is set.
>>>>
>>> So your whole comment here is about the else case?
>>> Then either put the comment where the else would be, or better yet invert
>>> the logic and return immediately if sw_debounced.
>> Sure...
> Understand, you wont be maintaining this code, even if you intend to.
> Consider the poor unfortunate who will have to deal with your code in
> the future. This is not and should not be a minor consideration.
>
> Sometimes long winded comments only add confusion rather than clarity.
> If the code alone is confusing and requires more than a line or two of
> explanatory comments, excluding function documentation, then you might
> want to rethink your code.
>
> In this case the clearest is probably to restructure the if condition as
> I suggested and simplify or even drop the comment.
>
>>>>> Reword to clarify.
>>>>> And add braces after function names to highlight them, so
>>>>> debounce_work_func().
>>>> Will do.
>>>>>> + if (!READ_ONCE(line->sw_debounced)) {
>>>>>> + line->line_seqno = ts->seq;
>>>>>> +
>>>>>> + /*
>>>>>> + * Increment in this callback incase all the lines in linereq
>>>>>> + * are enabled for hw timestamping. This will work even if
>>>>>> + * subset of lines are enabled for hw timestamping as
>>>>>> + * edge_irq_* callbacks will proceed as usual for them.
>>>>>> + */
>>>>> s/incase/in case/
>>>>>
>>>>> Not sure what the comment is trying to say. There is no check here that
>>>>> the other lines have HTE enabled. And that is not relevant anyway.
>>>>> The edge_irq_* handlers will proceed as usual for those lines NOT
>>>>> enabled for hw timestamping.
>>>>>
>>>>> To clarify, the line_seqno indicates where this event lies in the
>>>>> sequence of events for the line.
>>>>> The request seqno indicates where this event lines in the sequence of
>>>>> events for the request.
>>>>> For a single line request these are the same, hence the minor
>>>>> optimisation of not updating lr->seqno below.
>>>>>
>>>>>> + if (lr->num_lines != 1)
>>>>>> + line->req_seqno = atomic_inc_return(&lr->seqno);
>>>>>> +
>>>>> The req_seqno should be updated corresponding to the change in the
>>>>> line_reqno. That always used to be 1, but no longer if hte can discard
>>>>> events, i.e. skip over line_seqnos.
>>>> HTE does not discard any events, it pushes to clients as soon as its
>>>>
>>>> available through primary callback.
>>> The discarding of events that I am referring to is from your previous
>>> answers that indicated, to me anyway, that there could be gaps in the
>>> ts->seq numbers if the hardware event FIFO overflowed.
>>> Is that not the case?
>> Not in this patch series as the provider I have dealt with does not such
>>
>> feature. ts->seq is software counter in that case.
>>
> The code here has to deal with the general case, not just the one example
> driver you have provided. So in general there COULD be gaps in the
> ts->seq, right?
>
> I do see that using the ts-seq for sw debounced lines is problematic
> though. The debouncer itself will be discarding hte events, but that
> shouldn't be considered a lost event to the user. You could track
> how many events are discarded by the debouncer and subtract those from
> the sequence numbers reported to userspace?
>
>>> And when you say "primary callback", both here and elsewhere, you mean the
>>> cb parameter to gpiod_req_hw_timestamp_ns() and gc->req_hw_timestamp()?
>> yes, cb is primary and tcb (threaded cb) is optional secondary.
>>> Either way, be specific and name the function or parameter, or find a
>>> better term than "primary callback".
>>> In this case "the client's event handler" would be much clearer.
>> Noted...
>>>>> To be consistent, i.e. if events were lost for this line then they were
>>>>> also lost for the requested lines, the lr->seqno should be incremented by
>>>>> the change in line_seqno. Probably with some sanity checks.
>>>>>
>>>>>> + return HTE_RUN_THREADED_CB;
>>>>>> + }
>>>>>> +
>>>>>> + return HTE_CB_HANDLED;
>>>>>> +}
>>>>>> +
>>>>>> static irqreturn_t edge_irq_thread(int irq, void *p)
>>>>>> {
>>>>>> struct line *line = p;
>>>>>> @@ -553,6 +648,10 @@ static irqreturn_t edge_irq_thread(int irq, void *p)
>>>>>> struct gpio_v2_line_event le;
>>>>>> u64 eflags;
>>>>>>
>>>>>> + /* Let process_hw_ts_thread handle */
>>>>>> + if (test_bit(FLAG_EVENT_CLOCK_HARDWARE, &line->desc->flags))
>>>>>> + return IRQ_HANDLED;
>>>>>> +
>>>>> This adds pointless runtime overhead, and for everyone not just hte users.
>>>>> Don't stub out a handler in the handler - stub it out where it is
>>>>> registered by registering a stub handler. Or don't request it at all.
>>>>>
>>>>> So why would gpiolib-cdev be requesting the irq, only to stub out
>>>>> the handlers?
>>>>> If that has a side-effect that hte requires then hte should be taking
>>>>> care of it - it is not gpiolib-cdev's problem.
>>>> - Why stop at moving irq and debounce related stuff to hte then?
>>>>
>>> How about you answer my question before asking your own?
>>> Here I am only questioning why gpiolib-cdev is requesting an interrupt
>>> for a hte line at all. It has no reason to, so hte must have?
>> Commented below in "separation of concern" paragraph.
>>
>>> And where did I suggest moving the "debounce stuff" to hte?
>> Perhaps I misunderstood "That includes the sw debouncer - more on that
>>
>> below." comment.
>>
> The point I was trying to make there was that, for hte enabled lines, the
> sw debouncer should be driven by hte events, not irq events.
> That does not imply nor require moving the debouncer to hte.
>
>>> What I am suggesting is called separation of concerns. In this context
>>> the intent is to look towards abstracting the edge event generation.
>>> Having hte and irq entangled for no apparent reason makes doing that more
>> But in this patch series (i.e. HTE provider), they are entangled as hte provider
>>
>> will only ts the GPIO line which is configured as input irq. That is why I have
>>
>> separated GPIO line config part and TS part. In other words, let cdev handle
>>
>> any line config that userspace has asked and let hte handle ts part if the
>>
>> line config is supported (If line config is not suitable, it will not enable hte, see
>>
>> gpio-tegra186.c function tegra186_gpio_req_hw_ts() in this patch series where
>>
>> check happens for the tegra hte provider).
>>
> Can you explain "hte provider will only ts the GPIO line which is
> configured as input irq"? That sounds like a hte concern to me, or even
> worse a particular hte provider problem.
>
> The user requests a line with edge event detection enabled and with hte
> timestamps.
> Nothing in that requires irq from gpiolib-cdev.
> gpiolib-cdev should use hte as the event source where it would usually
> use irq. If hte requires irq to allow it to generate those events then
> hte should be responsible for requesting the irq.
> In a hypothetical extreme case where gpiolib-cdev only supported hte
> lines there should be no reference to irq in gpiolib-cdev.
> Yet you continue to insist that gpiolib-cdev should request the irq for
> hte.
> What am I missing here?
>
>>> difficult than it needs to be, whereas keeping them separate greatly
>>> simplifies identification of common code suitable for refactoring
>>> subsequently.
>>>
>>> Not sure what to call what you are suggesting.
>>>
>>>> I mean if there is hte provider which can TS GPIO output/input
>>>>
>>>> does it mean hte is responsible for parsing the GPIO line configs, setting them up
>>>>
>>>> (i.e. input or output) as well? Are we not duplicating logic instead of
>>>>
>>>> leveraging gpio-cdev? Does it make sense for the HTE subsystem which not
>>>>
>>>> only TS the GPIOs but other SoC lines?
>>>>
>>>> - What happens to in kernel GPIO HTE client (for example, hte-tegra194-gpio-test.c)?
>>>>
>>>> some clients do more in their IRQ handler than what edge_irq_handler does in which
>>>>
>>>> case it would make sense to have them request irq in their code than through HTE.
>>>>
>>>>> And speaking as to how the whole hte/gpiolib-cdev interface should work,
>>>>> hte should be an edge event generator alternative to irq. So lines with
>>>>> hte enabled should work without any irq calls from gpiolib-cdev.
>>>>> That includes the sw debouncer - more on that below.
>>>>>
>>>>>> /* Do not leak kernel stack to userspace */
>>>>>> memset(&le, 0, sizeof(le));
>>>>>>
>>>>>> @@ -604,6 +703,10 @@ static irqreturn_t edge_irq_handler(int irq, void *p)
>>>>>> struct line *line = p;
>>>>>> struct linereq *lr = line->req;
>>>>>>
>>>>>> + /* Let HTE supplied callbacks handle */
>>>>>> + if (test_bit(FLAG_EVENT_CLOCK_HARDWARE, &line->desc->flags))
>>>>>> + return IRQ_HANDLED;
>>>>>> +
>>>>>> /*
>>>>>> * Just store the timestamp in hardirq context so we get it as
>>>>>> * close in time as possible to the actual event.
>>>>>> @@ -682,14 +785,6 @@ static void debounce_work_func(struct work_struct *work)
>>>>>> /* Do not leak kernel stack to userspace */
>>>>>> memset(&le, 0, sizeof(le));
>>>>>>
>>>>>> - lr = line->req;
>>>>>> - le.timestamp_ns = line_event_timestamp(line);
>>>>>> - le.offset = gpio_chip_hwgpio(line->desc);
>>>>>> - line->line_seqno++;
>>>>>> - le.line_seqno = line->line_seqno;
>>>>>> - le.seqno = (lr->num_lines == 1) ?
>>>>>> - le.line_seqno : atomic_inc_return(&lr->seqno);
>>>>>> -
>>>>>> if (level)
>>>>>> /* Emit low-to-high event */
>>>>>> le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
>>>>>> @@ -697,6 +792,23 @@ static void debounce_work_func(struct work_struct *work)
>>>>>> /* Emit high-to-low event */
>>>>>> le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
>>>>>>
>>>>>> + if (test_bit(FLAG_EVENT_CLOCK_HARDWARE, &line->desc->flags)) {
>>>>>> + le.timestamp_ns = line->timestamp_ns;
>>>>>> + if (line->dir < HTE_DIR_NOSUPP)
>>>>>> + le.id = (line->dir == HTE_RISING_EDGE_TS) ?
>>>>>> + GPIO_V2_LINE_EVENT_RISING_EDGE :
>>>>>> + GPIO_V2_LINE_EVENT_FALLING_EDGE;
>>>>>> + } else {
>>>>>> + le.timestamp_ns = line_event_timestamp(line);
>>>>>> + }
>>>>>> +
>>>>> Move the FLAG_EVENT_CLOCK_HARDWARE check into line_event_timestamp().
>>>>>
>>>>> And the id fudging is necessary because the level returned by
>>>>> gpiod_get_raw_value_cansleep() can disagree with the level from hte?
>>>>> So you are still trying to synchronise events from two streams.
>>>>> And that is still broken.
>>>>> If a hte event occurs between the level being sampled by
>>>>> gpiod_get_raw_value_cansleep() and the line->dir being read then the line
>>>>> will have toggled and you will be reporting the opposite state than the
>>>>> one the debouncer determined was stable. And maybe the wrong timestamp as
>>>>> well.
>>>>>
>>>>> For lines where hte is enabled, the hte should be the source of level for
>>>>> the debouncer, not the raw value. And the mod_delayed_work() that
>>>>> drives the debouncer should be called by a hte handler, not an irq handler.
>>>>>
>>>>> There is also a race on reading the hte timestamp (line->timestamp_ns) and
>>>>> the hte level (line->dir), such that you can get the level from one event
>>>>> the timestamp from another.
>>>>>
>>>>>> + lr = line->req;
>>>>>> + le.offset = gpio_chip_hwgpio(line->desc);
>>>>>> + line->line_seqno++;
>>>>>> + le.line_seqno = line->line_seqno;
>>>>>> + le.seqno = (lr->num_lines == 1) ?
>>>>>> + le.line_seqno : atomic_inc_return(&lr->seqno);
>>>>>> +
>>>>> What is the purpose of moving this block of code moved from before the
>>>>> if (level)?
>>>>>
>>>>>
>>>>>> linereq_put_event(lr, &le);
>>>>>> }
>>>>>>
>>>>>> @@ -891,7 +1003,6 @@ static int gpio_v2_line_flags_validate(u64 flags)
>>>>>> /* Return an error if an unknown flag is set */
>>>>>> if (flags & ~GPIO_V2_LINE_VALID_FLAGS)
>>>>>> return -EINVAL;
>>>>>> -
>>>>> Gratuitous whitespace change.
>>>>>
>>>>>> /*
>>>>>> * Do not allow both INPUT and OUTPUT flags to be set as they are
>>>>>> * contradictory.
>>>>>> @@ -900,6 +1011,11 @@ static int gpio_v2_line_flags_validate(u64 flags)
>>>>>> (flags & GPIO_V2_LINE_FLAG_OUTPUT))
>>>>>> return -EINVAL;
>>>>>>
>>>>>> + /* Only allow one event clock source */
>>>>>> + if ((flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME) &&
>>>>>> + (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HARDWARE))
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> /* Edge detection requires explicit input. */
>>>>>> if ((flags & GPIO_V2_LINE_EDGE_FLAGS) &&
>>>>>> !(flags & GPIO_V2_LINE_FLAG_INPUT))
>>>>>> @@ -992,6 +1108,8 @@ static void gpio_v2_line_config_flags_to_desc_flags(u64 flags,
>>>>>>
>>>>>> assign_bit(FLAG_EVENT_CLOCK_REALTIME, flagsp,
>>>>>> flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME);
>>>>>> + assign_bit(FLAG_EVENT_CLOCK_HARDWARE, flagsp,
>>>>>> + flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HARDWARE);
>>>>>> }
>>>>>>
>>>>>> static long linereq_get_values(struct linereq *lr, void __user *ip)
>>>>>> @@ -1154,6 +1272,21 @@ static long linereq_set_config_unlocked(struct linereq *lr,
>>>>>> return ret;
>>>>>> }
>>>>>>
>>>>>> + /* Check if new config sets hardware assisted clock */
>>>>>> + if (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HARDWARE) {
>>>>>> + ret = gpiod_req_hw_timestamp_ns(desc, process_hw_ts,
>>>>>> + process_hw_ts_thread,
>>>>>> + &lr->lines[i]);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>> Note that the line config is the complete line config, not a delta.
>>>>>
>>>>> What happens when a line that already has hte enabled is reconfigured
>>>>> and still has hte enabled? i.e. what happens when
>>>>> gpiod_req_hw_timestamp_ns() is called for the second time?
>>>> HTE will return without doing anything with error code.
>>>>
>>> But this is not an error case, it is a normal reconfigure of an
>>> attribute other than the hte flag.
>> I assumed when this function is called it will "reset" and not update the configs.
>>
>> If this assumption is wrong, I will correct the logic here.
>>
> The set_config does whatever is required to change the line request
> config from the old to the new. Both old and new are complete snapshots
> of the line request config.
>
> The change should be seamless except for the attributes being changed.
> We certainly do not reset the config and start from scratch - that would
> be little better than the user releasing and re-requesting the lines.
>
> For many attributes we can just apply the new config. But some cases,
> such as a change to active low polarity, are a lot more involved.
> And if hte considers re-requesting the line to be an error then you
> should only make the hte request when the hte flag changes to set.
>
> Cheers,
> Kent.
>
>>> And that will now return an error to userspace?
>>>
>>> Cheers,
>>> Kent.
>>>
>>>>> You provide a comment for the release case below, what of the request
>>>>> case?
>>>>>
>>>>> If you need to check for change then compare the old and new flags, as
>>>>> the polarity_change check does (not visible in the diff here).
>>>>>
>>>>>> + } else {
>>>>>> + /*
>>>>>> + * HTE subsys will do nothing if there is nothing to
>>>>>> + * release.
>>>>>> + */
>>>>>> + gpiod_rel_hw_timestamp_ns(desc);
>>>>>> + }
>>>>>> +
>>>>> Comment will fit on one line.
>>>>>
>>>>> And it would be better to document that the function is idempotent in the
>>>>> function documentation, not everywhere it is used.
>>>>>
>>>>>> blocking_notifier_call_chain(&desc->gdev->notifier,
>>>>>> GPIO_V2_LINE_CHANGED_CONFIG,
>>>>>> desc);
>>>>>> @@ -1409,6 +1542,14 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
>>>>>> flags & GPIO_V2_LINE_EDGE_FLAGS);
>>>>>> if (ret)
>>>>>> goto out_free_linereq;
>>>>>> +
>>>>>> + if (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HARDWARE) {
>>>>>> + ret = gpiod_req_hw_timestamp_ns(desc, process_hw_ts,
>>>>>> + process_hw_ts_thread,
>>>>>> + &lr->lines[i]);
>>>>>> + if (ret)
>>>>>> + goto out_free_linereq;
>>>>>> + }
>>>>>> }
>>>>>>
>>>>>> blocking_notifier_call_chain(&desc->gdev->notifier,
>>>>>> @@ -1959,6 +2100,8 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
>>>>>>
>>>>>> if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &desc->flags))
>>>>>> info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME;
>>>>>> + else if (test_bit(FLAG_EVENT_CLOCK_HARDWARE, &desc->flags))
>>>>>> + info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_HARDWARE;
>>>>>>
>>>>>> debounce_period_us = READ_ONCE(desc->debounce_period_us);
>>>>>> if (debounce_period_us) {
>>>>>> diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h
>>>>>> index eaaea3d8e6b4..d360545b4c21 100644
>>>>>> --- a/include/uapi/linux/gpio.h
>>>>>> +++ b/include/uapi/linux/gpio.h
>>>>>> @@ -80,6 +80,7 @@ enum gpio_v2_line_flag {
>>>>>> GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN = _BITULL(9),
>>>>>> GPIO_V2_LINE_FLAG_BIAS_DISABLED = _BITULL(10),
>>>>>> GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME = _BITULL(11),
>>>>>> + GPIO_V2_LINE_FLAG_EVENT_CLOCK_HARDWARE = _BITULL(12),
>>>>>> };
>>>>>>
>>>>> I'm now thinking this name, "HARDWARE" is too vague, in case other
>>>>> timestamp source alternatives join the fray, and so should be "HTE".
>>>>>
>>>>> Cheers,
>>>>> Kent.