Hi Jonathan, I got some time to implement RFC version 2 while doing so I have a follow up comment inline regarding clock source comment of yours. Best Regards, Dipen Patel On 8/1/21 9:13 AM, Jonathan Cameron wrote: > On Tue, 27 Jul 2021 21:38:45 -0700 > Dipen Patel <dipenp@xxxxxxxxxx> wrote: > >> On 7/4/21 1:15 PM, Jonathan Cameron wrote: >>> On Fri, 25 Jun 2021 16:55:23 -0700 >>> Dipen Patel <dipenp@xxxxxxxxxx> wrote: >>> >>>> Some devices can timestamp system lines/signals/Buses in real-time >>>> using the hardware counter or other hardware means which can give >>>> finer granularity and help avoid jitter introduced by software means >>>> of timestamping. To utilize such functionality there has to be >>>> framework where such devices can register themselves as producers or >>>> providers so that the consumers or clients devices can request specific >>>> line from the providers. This patch introduces such subsystem as >>>> hardware timestamping engine (HTE). >>>> >>>> It provides below APIs for the provider: >>>> - hte_register_chip() -- To register the HTE chip. >>>> - hte_unregister_chip() -- To unregister the HTE chip. >>>> - hte_push_ts_ns_atomic() -- To push timestamp data into HTE subsystem. >>>> >>>> It provides below APIs for the consumer: >>>> - of_hte_request_ts() -- To request timestamp functionality. >>>> - devm_of_hte_request_ts() -- Managed version of the above. >>>> - hte_req_ts_by_dt_node() -- To request timestamp functionality by >>>> using HTE provider dt node. >>>> - devm_hte_release_ts() -- The managed version to release timestamp >>>> functionality and associated resources. >>>> - hte_retrieve_ts_ns() -- To retrieve timestamps. >>>> - hte_retrieve_ts_ns_wait() -- Same as above but blocking version. >>>> - hte_enable_ts() -- To disable timestamp functionality. >>>> - hte_disable_ts() -- To enable timestamp functionality. >>>> - hte_available_ts() -- To query available timestamp data. >>>> - hte_release_ts() -- To release timestamp functionality and its >>>> associated resources. >>>> - hte_get_clk_src_info() -- To query clock source information from >>>> the provider >>>> >>>> It provides centralized software buffer management per requested id to >>>> store the timestamp data for the consumers as below: >>>> - hte_set_buf_len() -- To set the buffer length. >>>> - hte_get_buf_len() -- To get the buffer length. >>>> - hte_set_buf_watermark() -- To set the software threshold/watermark. >>>> - hte_get_buf_watermark() -- To get the software threshold/watermark. >>>> >>>> The detail about parameters and API usage are described in each >>>> functions definitions in drivers/hte/hte.c file. >>>> >>>> The patch adds compilation support in Makefile and menu options in >>>> Kconfig. >>>> >>>> Signed-off-by: Dipen Patel <dipenp@xxxxxxxxxx> >>> Hi Dipen, this isn't a particularly thorough review as I'm still getting my head >>> around what this is doing + it is an RFC :) >> Thanks for the review comments. My responses inline. > You are welcome, some follow up responses inline. > I've tried to crop this down a bit so only kept the bits we are discussing. > >>>> + >>>> +static int hte_ts_dis_en_common(struct hte_ts_desc *desc, bool en) >>>> +{ >>>> + u32 ts_id; >>>> + struct hte_device *gdev; >>>> + struct hte_ts_info *ei; >>>> + int ret; >>>> + >>>> + if (!desc) >>>> + return -EINVAL; >>>> + >>>> + ei = (struct hte_ts_info *)desc->data_subsys; >>> As above, no need to cast - though it rather implies the type of data_subsys >>> should not be void *. >> desc is public facing structure, I wanted to make subsystem related >> >> information opaque that is why I had it void *. >> > you can keep it opaque, just have a forwards definition of > struct hte_ts_desc; > which just means it is defined somewhere. You can have that in the header with > the definition hidden away. > > It will only need to have a visible complete definition when you dereference it inside > the the core. > > Mind you, I'm suggesting allowing it to be embedded in another structure anyway which > would require you to have it exposed. Perhaps this desire to keep it opaque is > a reason to not take that suggestion but it isn't relevant for this one. > > >>>> + */ >>>> +struct hte_ts_desc *devm_of_hte_request_ts(struct device *dev, >>>> + const char *label, >>>> + void (*cb)(enum hte_notify n)) >>>> +{ >>>> + >>>> + struct hte_ts_desc **ptr, *desc; >>>> + >>>> + ptr = devres_alloc(__devm_hte_release_ts, sizeof(*ptr), GFP_KERNEL); >>> Superficially looks like you might get way with just calling dev_add_action_or_reset() in here >>> and avoid this boilerplate. A lot of cases that looked like this got cleaned up in the >>> last kernel cycle. >> I based my patches from linux-next/master. Not sure if that has >> >> dev_add_action_or_reset > typo on my part was meant to be > > devm_add_action_or_reset() > >>>> + >>>> +/** >>>> + * hte_req_ts_by_dt_node() - Request entity to monitor by passing HTE device >>>> + * node directly, where meaning of the entity is provider specific, for example >>>> + * lines, signals, GPIOs, buses etc... >>>> + * >>>> + * @of_node: HTE provider device node. >>>> + * @id: entity id to monitor, this id belongs to HTE provider of_node. >>>> + * @cb: Optional callback to notify. >>>> + * >>>> + * Context: Holds mutex lock, can not be called from atomic context. >>> What mutex and why? If it is one you can check is held even better. >> ___hte_req_ts holds the mutex lock to serialize multiple consumers >> >> requesting same entity. > Add that detail to the comment. > >>> >>>> + * Returns: ts descriptor on success or error pointers. >>>> + */ >>>> +struct hte_ts_desc *hte_req_ts_by_dt_node(struct device_node *of_node, >>>> + unsigned int id, >>>> + void (*cb)(enum hte_notify n)) >>>> +{ >>>> + struct hte_device *gdev; >>>> + struct hte_ts_desc *desc; >>>> + int ret; >>>> + u32 xlated_id; >>>> + >>>> + gdev = of_node_to_htedevice(of_node); >>>> + if (IS_ERR(gdev)) >>>> + return ERR_PTR(-ENOTSUPP); >>>> + >>>> + if (!gdev->chip || !gdev->chip->ops) >>>> + return ERR_PTR(-ENOTSUPP); >>>> + >>>> + desc = kzalloc(sizeof(*desc), GFP_KERNEL); >>>> + if (!desc) { >>>> + ret = -ENOMEM; >>>> + goto out_put_device; >>>> + } >>> Pass a desc pointer into this function rather than allocating the structure >>> in here. That lets the caller embed that structure inside one of it's own >>> structures if it wants to, resulting in fewer small allocations which is always good. >>> >>> It's far from obvious that the caller needs to free desc. >> Are you suggesting to shift burden of allocation/deallocation (static or dynamic) >> >> at client/consumer side? > It's been a while so I've forgotten how this works, but 'probably' yes... > If a function creates some sort of record (of fixed known size and type) then > letting that be passed in + filled in by the function is normally more efficient > than having an allocation in here. Chances are the consumer will just have > it embedded in an existing state structure and not need to do any explicit > allocation / deallocation. Disadvantage is you can't keep it opaque. > >>> >>>> + >>>> + desc->con_id = id; >>>> + ret = gdev->chip->xlate(gdev->chip, NULL, desc, &xlated_id); >>>> + if (ret < 0) { >>>> + dev_err(gdev->chip->dev, >>>> + "failed to xlate id: %d\n", id); >>>> + goto out_free_desc; >>>> + } >>>> + >>>> + ret = ___hte_req_ts(gdev, desc, xlated_id, cb); >>>> + if (ret < 0) { >>>> + dev_err(gdev->chip->dev, >>>> + "failed to request id: %d\n", id); >>>> + goto out_free_desc; >>>> + } >>>> + >>>> + return desc; >>>> + >>>> +out_free_desc: >>>> + kfree(desc); >>>> + >>>> +out_put_device: >>>> + return ERR_PTR(ret); >>>> +} >>>> +EXPORT_SYMBOL_GPL(hte_req_ts_by_dt_node); >>>> + >>>> +/** >>>> + * hte_get_clk_src_info() - Consumer calls this API to query clock source >>>> + * information of the desc. >>>> + * >>>> + * @desc: ts descriptor, same as returned from request API. >>>> + * >>>> + * Context: Any context. >>>> + * Returns: 0 on success else negative error code on failure. >>>> + */ >>>> +int hte_get_clk_src_info(const struct hte_ts_desc *desc, >>>> + struct hte_clk_info *ci) >>>> +{ >>>> + struct hte_chip *chip; >>>> + struct hte_ts_info *ei; >>>> + >>>> + if (!desc || !desc->data_subsys || !ci) { >>>> + pr_debug("%s:%d\n", __func__, __LINE__); >>>> + return -EINVAL; >>>> + } >>>> + >>>> + ei = desc->data_subsys; >>>> + if (!ei || !ei->gdev || !ei->gdev->chip) >>>> + return -EINVAL; >>>> + >>>> + chip = ei->gdev->chip; >>>> + if (!chip->ops->get_clk_src_info) >>>> + return -ENOTSUPP; >>>> + >>>> + return chip->ops->get_clk_src_info(chip, ci); >>>> +} >>>> +EXPORT_SYMBOL_GPL(hte_get_clk_src_info); >>>> + >>>> +static inline void hte_add_to_device_list(struct hte_device *gdev) >>>> +{ >>>> + struct hte_device *prev; >>> Needs to take an appropriate lock as you may have concurrent calls. >> There is spin_lock held from register API from where this gets >> called. > Great. I'd missed that. > >>> >>>> + >>>> + if (list_empty(&hte_devices)) { >>>> + list_add_tail(&gdev->list, &hte_devices); >>> Needs a comment. I've no idea why you might want to only add it if there were >>> no other hte_devices already there. >>> >>>> + return; >>>> + } >>>> + >>>> + prev = list_last_entry(&hte_devices, struct hte_device, list); >>> Why woud you do this? >> Thanks for pointing out. I definitely missed cleaning this up. Now, I will >> >> remove this function in next RFC version as one line can be added directly >> >> in register API. >> >>> >>>> + list_add_tail(&gdev->list, &hte_devices); >>>> +} >>>> + >>>> +/** >>>> + * hte_push_ts_ns_atomic() - Used by the provider to push timestamp in nano >>>> + * seconds i.e data->tsc will be in ns, it is assumed that provider will be >>>> + * using this API from its ISR or atomic context. >>>> + * >>>> + * @chip: The HTE chip, used during the registration. >>>> + * @xlated_id: entity id understood by both subsystem and provider, usually this >>>> + * is obtained from xlate callback during request API. >>>> + * @data: timestamp data. >>>> + * @n: Size of the data. >>>> + * >>>> + * Context: Atomic. >>>> + * Returns: 0 on success or a negative error code on failure. >>>> + */ >>>> +int hte_push_ts_ns_atomic(const struct hte_chip *chip, u32 xlated_id, >>>> + struct hte_ts_data *data, size_t n) >>>> +{ >>>> + unsigned int ret; >>>> + bool notify; >>>> + size_t el_avail; >>>> + struct hte_ts_buf *buffer; >>>> + struct hte_ts_info *ei; >>>> + >>>> + if (!chip || !data || !chip->gdev) >>>> + return -EINVAL; >>>> + >>>> + if (xlated_id > chip->nlines) >>>> + return -EINVAL; >>>> + >>>> + ei = &chip->gdev->ei[xlated_id]; >>>> + >>>> + if (!test_bit(HTE_TS_REGISTERED, &ei->flags) || >>>> + test_bit(HTE_TS_DISABLE, &ei->flags)) { >>>> + dev_dbg(chip->dev, "Unknown timestamp push\n"); >>>> + return -EINVAL; >>>> + } >>>> + >>>> + /* timestamp sequence counter, start from 0 */ >>>> + data->seq = ei->seq++; >>>> + >>>> + buffer = ei->buf; >>>> + el_avail = buffer->access->el_available(buffer); >>>> + ret = buffer->access->store(buffer, data, n); >>> If we are doing this from the hte core, why is buffer definition in the scope of the >>> drivers rather than the core? That seems backwards to me. >> I do not understand this comment. The buffer definition is in scope of hte core >> >> as it is the only entity that manages it. > I think I figured that out later and forgot to come back and edit this comment. > However... > > In that case, why is it an ops function? Don't introduce abstraction > until you need it. Will be simpler and easier to review if you just > call those functions directly for now. e.g. > > ret = hs_ts_store_to_buf(buffer, data, n); > > Chances are you'll never introduce another buffer choice. > For a long time I thought we'd have both fifo and ring options in IIO > but it turned out no one really cared. We do have an ops structure, but > that's because in IIO the buffer interface is used for two things: > 1) Pushing to a kfifo that is going to userspace. > 2) Pushing to a callback function owned by a consumer. > and there is a rather fiddly data demux on the front end to ensure each > of those only gets the data requested via that path - at least with timestamps > there is only one type of data! > > Hmm, thinking about this raises an interesting question. > Why do we want a kfifo here at all for HTE? You could > just call a callback function registered by the consumer of that > kfifo directly. If that consumer then wants to buffer then of > course it can, but it not (perhaps it only cares about the latest > value and will drop the rest) then it can chose not to. Maybe > it's just gathering stats rather than caring about individual > timestamps? Probably lots of other things that might happen in > the consumer that I've not thought of. We need a buffer if > userspace becomes involved, but here IIRC that's not (yet) true. > >>> >>>> + if (ret != n) { >>>> + atomic_inc(&ei->dropped_ts); >>>> + if (ei->cb) >>>> + ei->cb(HTE_TS_DROPPED); >>>> + return -ENOMEM; >>>> + } > ... > >>> >>>> + >>>> +/** >>>> + * struct hte_ts_data - HTE timestamp data. >>>> + * The provider uses and fills timestamp related details during push_timestamp >>>> + * API call. The consumer uses during retrieve_timestamp API call. >>>> + * >>>> + * @tsc: Timestamp value. >>>> + * @seq: Sequence counter of the timestamps. >>>> + * @dir: Direction of the event at the time of timestamp. >>>> + */ >>>> +struct hte_ts_data { >>>> + u64 tsc; >>>> + u64 seq; >>>> + int dir; >>>> +}; >>>> + >>>> +/** >>>> + * struct hte_clk_info - Clock source info that HTE provider uses. >>>> + * The provider uses hardware clock as a source to timestamp real time. This >>>> + * structure presents the clock information to consumers. >>>> + * >>>> + * @hz: Clock rate in HZ, for example 1KHz clock = 1000. >>>> + * @type: Clock type. CLOCK_* types. >>> So this is something we got a it wrong in IIO. It's much better to define >>> a subset of clocks that can be potentially used. There are some that make >>> absolutely no sense and consumers really don't want to have to deal with them. >> Is there anything I have to change here? > Yes - specify which clocks would make sense. You might not need to explicitly > allow only those, but that might also be worthwhile. Otherwise, the chances are > you'll end up with a bunch of special purpose code in consumers on the basis > they might get CLOCK_TAI or similar and have to deal with it. > As for exactly which clocks do make sense, that's one which may take some figuring > out. Probably REALTIME, MONOTONIC and BOOTTIME depending on whether you care > what happens when the time of the system gets adjusted, or whether it carries > on measuring time across suspend. Very application dependent but there are some > you can definitely rule out. Don't repeat my mistake of leaving it vague > (which incidentally was a follow up to picking a silly clock to use for timestamps > before we allowed it to be configured). I believe your comment is under assumption that providers have choice in selecting clock source to timestamp in turns clients have it as well. For now, the provider I have implemented has single clock source and hence I only implemented get_clock* hook that provider implement and client can retrieve that information. I guess I can always implement set_clock* hook as well for the future providers which support multiple clock sources. Please let me if I missed your point. >>> >>>> + */ >>>> +struct hte_clk_info { >>>> + u64 hz; >>>> + clockid_t type; >>>> +}; >>>> + >>>> +/** >>>> + * HTE subsystem notifications for the consumers. >>>> + * >>>> + * @HTE_TS_AVAIL: Timestamps available notification. >>>> + * @HTE_TS_DROPPED: Timestamps dropped notification. >>> Something I've missed so far is whether drops are in a kfifo or a ring >>> fashion. I'm guess that's stated somewhere, but it might be useful to have >>> it here. >> Dropped are from kfifo if kfifo does not have space. > Ok, perhaps expand the comment? > > ... > >>> >>>> + * >>>> + * xlated_id parameter is used to communicate between HTE subsystem and the >>>> + * providers. It is the same id returned during xlate API call and translated >>>> + * by the provider. This may be helpful as both subsystem and provider locate >>>> + * the requested entity in constant time, where entity could be anything from >>>> + * lines, signals, events, buses etc.. that providers support. >>>> + */ >>>> +struct hte_ops { >>>> + int (*request)(struct hte_chip *chip, u32 xlated_id); >>>> + int (*release)(struct hte_chip *chip, u32 xlated_id); >>>> + int (*enable)(struct hte_chip *chip, u32 xlated_id); >>>> + int (*disable)(struct hte_chip *chip, u32 xlated_id); >>>> + int (*get_clk_src_info)(struct hte_chip *chip, >>>> + struct hte_clk_info *ci); >>>> +}; >>>> + >>>> +/** >>>> + * struct hte_chip - Abstract HTE chip structure. >>>> + * @name: functional name of the HTE IP block. >>>> + * @dev: device providing the HTE. >>> Unclear naming. Is this the parent device, or one associated with the HTE itself? >>> I'm guessing today you don't have one associated with the HTE, but it is plausible you >>> might gain on in future to make it fit nicely in the device model as a function of another >>> device. >> This is provider's device, could be &pdev->dev or any dev provider deems fit hence the >> >> generic name. > Ok, for now this works as a name, but I wonder if you will end up growing another > layer in the device model as would happen for majority of subsystems. > You may end up doing so when adding support to query the provider via a handle > in the dt of the consumer. It could probably be avoided, but throwing this into > a class might make your life easier as you can use more standard infrastructure. > >>> >>>> + * @ops: callbacks for this HTE. >>>> + * @nlines: number of lines/signals supported by this chip. >>>> + * @xlate: Callback which translates consumer supplied logical ids to >>>> + * physical ids, return from 0 for the success and negative for the >>>> + * failures. It stores (0 to @nlines) in xlated_id parameter for the success. >>>> + * @of_hte_n_cells: Number of cells used to form the HTE specifier. >>>> + * @gdev: HTE subsystem abstract device, internal to the HTE subsystem. >>>> + * @data: chip specific private data. >>>> + */ >>>> +struct hte_chip { >>>> + const char *name; >>>> + struct device *dev; >>>> + const struct hte_ops *ops; >>>> + u32 nlines; >>>> + int (*xlate)(struct hte_chip *gc, >>>> + const struct of_phandle_args *args, >>>> + struct hte_ts_desc *desc, u32 *xlated_id); >>>> + u8 of_hte_n_cells; >>>> + >>>> + /* only used internally by the HTE framework */ >>>> + struct hte_device *gdev; >>>> + void *data; >>>> +}; > ... > > Jonathan