162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2021-2022 NVIDIA Corporation
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Dipen Patel <dipenp@nvidia.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/err.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/mutex.h>
1462306a36Sopenharmony_ci#include <linux/uaccess.h>
1562306a36Sopenharmony_ci#include <linux/hte.h>
1662306a36Sopenharmony_ci#include <linux/delay.h>
1762306a36Sopenharmony_ci#include <linux/debugfs.h>
1862306a36Sopenharmony_ci#include <linux/device.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define HTE_TS_NAME_LEN		10
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* Global list of the HTE devices */
2362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(hte_lock);
2462306a36Sopenharmony_cistatic LIST_HEAD(hte_devices);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cienum {
2762306a36Sopenharmony_ci	HTE_TS_REGISTERED,
2862306a36Sopenharmony_ci	HTE_TS_REQ,
2962306a36Sopenharmony_ci	HTE_TS_DISABLE,
3062306a36Sopenharmony_ci	HTE_TS_QUEUE_WK,
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/**
3462306a36Sopenharmony_ci * struct hte_ts_info - Information related to requested timestamp.
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * @xlated_id: Timestamp ID as understood between HTE subsys and HTE provider,
3762306a36Sopenharmony_ci * See xlate callback API.
3862306a36Sopenharmony_ci * @flags: Flags holding state information.
3962306a36Sopenharmony_ci * @hte_cb_flags: Callback related flags.
4062306a36Sopenharmony_ci * @seq: Timestamp sequence counter.
4162306a36Sopenharmony_ci * @line_name: HTE allocated line name.
4262306a36Sopenharmony_ci * @free_attr_name: If set, free the attr name.
4362306a36Sopenharmony_ci * @cb: A nonsleeping callback function provided by clients.
4462306a36Sopenharmony_ci * @tcb: A secondary sleeping callback function provided by clients.
4562306a36Sopenharmony_ci * @dropped_ts: Dropped timestamps.
4662306a36Sopenharmony_ci * @slock: Spin lock to synchronize between disable/enable,
4762306a36Sopenharmony_ci * request/release APIs.
4862306a36Sopenharmony_ci * @cb_work: callback workqueue, used when tcb is specified.
4962306a36Sopenharmony_ci * @req_mlock: Lock during timestamp request/release APIs.
5062306a36Sopenharmony_ci * @ts_dbg_root: Root for the debug fs.
5162306a36Sopenharmony_ci * @gdev: HTE abstract device that this timestamp information belongs to.
5262306a36Sopenharmony_ci * @cl_data: Client specific data.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistruct hte_ts_info {
5562306a36Sopenharmony_ci	u32 xlated_id;
5662306a36Sopenharmony_ci	unsigned long flags;
5762306a36Sopenharmony_ci	unsigned long hte_cb_flags;
5862306a36Sopenharmony_ci	u64 seq;
5962306a36Sopenharmony_ci	char *line_name;
6062306a36Sopenharmony_ci	bool free_attr_name;
6162306a36Sopenharmony_ci	hte_ts_cb_t cb;
6262306a36Sopenharmony_ci	hte_ts_sec_cb_t tcb;
6362306a36Sopenharmony_ci	atomic_t dropped_ts;
6462306a36Sopenharmony_ci	spinlock_t slock;
6562306a36Sopenharmony_ci	struct work_struct cb_work;
6662306a36Sopenharmony_ci	struct mutex req_mlock;
6762306a36Sopenharmony_ci	struct dentry *ts_dbg_root;
6862306a36Sopenharmony_ci	struct hte_device *gdev;
6962306a36Sopenharmony_ci	void *cl_data;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/**
7362306a36Sopenharmony_ci * struct hte_device - HTE abstract device
7462306a36Sopenharmony_ci * @nlines: Number of entities this device supports.
7562306a36Sopenharmony_ci * @ts_req: Total number of entities requested.
7662306a36Sopenharmony_ci * @sdev: Device used at various debug prints.
7762306a36Sopenharmony_ci * @dbg_root: Root directory for debug fs.
7862306a36Sopenharmony_ci * @list: List node to store hte_device for each provider.
7962306a36Sopenharmony_ci * @chip: HTE chip providing this HTE device.
8062306a36Sopenharmony_ci * @owner: helps prevent removal of modules when in use.
8162306a36Sopenharmony_ci * @ei: Timestamp information.
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_cistruct hte_device {
8462306a36Sopenharmony_ci	u32 nlines;
8562306a36Sopenharmony_ci	atomic_t ts_req;
8662306a36Sopenharmony_ci	struct device *sdev;
8762306a36Sopenharmony_ci	struct dentry *dbg_root;
8862306a36Sopenharmony_ci	struct list_head list;
8962306a36Sopenharmony_ci	struct hte_chip *chip;
9062306a36Sopenharmony_ci	struct module *owner;
9162306a36Sopenharmony_ci	struct hte_ts_info ei[];
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic struct dentry *hte_root;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int __init hte_subsys_dbgfs_init(void)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	/* creates /sys/kernel/debug/hte/ */
10162306a36Sopenharmony_ci	hte_root = debugfs_create_dir("hte", NULL);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return 0;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_cisubsys_initcall(hte_subsys_dbgfs_init);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic void hte_chip_dbgfs_init(struct hte_device *gdev)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	const struct hte_chip *chip = gdev->chip;
11062306a36Sopenharmony_ci	const char *name = chip->name ? chip->name : dev_name(chip->dev);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	gdev->dbg_root = debugfs_create_dir(name, hte_root);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	debugfs_create_atomic_t("ts_requested", 0444, gdev->dbg_root,
11562306a36Sopenharmony_ci				&gdev->ts_req);
11662306a36Sopenharmony_ci	debugfs_create_u32("total_ts", 0444, gdev->dbg_root,
11762306a36Sopenharmony_ci			   &gdev->nlines);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic void hte_ts_dbgfs_init(const char *name, struct hte_ts_info *ei)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	if (!ei->gdev->dbg_root || !name)
12362306a36Sopenharmony_ci		return;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	ei->ts_dbg_root = debugfs_create_dir(name, ei->gdev->dbg_root);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	debugfs_create_atomic_t("dropped_timestamps", 0444, ei->ts_dbg_root,
12862306a36Sopenharmony_ci				&ei->dropped_ts);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#else
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void hte_chip_dbgfs_init(struct hte_device *gdev)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic void hte_ts_dbgfs_init(const char *name, struct hte_ts_info *ei)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#endif
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/**
14462306a36Sopenharmony_ci * hte_ts_put() - Release and disable timestamp for the given desc.
14562306a36Sopenharmony_ci *
14662306a36Sopenharmony_ci * @desc: timestamp descriptor.
14762306a36Sopenharmony_ci *
14862306a36Sopenharmony_ci * Context: debugfs_remove_recursive() function call may use sleeping locks,
14962306a36Sopenharmony_ci *	    not suitable from atomic context.
15062306a36Sopenharmony_ci * Returns: 0 on success or a negative error code on failure.
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_ciint hte_ts_put(struct hte_ts_desc *desc)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	int ret = 0;
15562306a36Sopenharmony_ci	unsigned long flag;
15662306a36Sopenharmony_ci	struct hte_device *gdev;
15762306a36Sopenharmony_ci	struct hte_ts_info *ei;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (!desc)
16062306a36Sopenharmony_ci		return -EINVAL;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	ei = desc->hte_data;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (!ei || !ei->gdev)
16562306a36Sopenharmony_ci		return -EINVAL;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	gdev = ei->gdev;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	mutex_lock(&ei->req_mlock);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (unlikely(!test_bit(HTE_TS_REQ, &ei->flags) &&
17262306a36Sopenharmony_ci	    !test_bit(HTE_TS_REGISTERED, &ei->flags))) {
17362306a36Sopenharmony_ci		dev_info(gdev->sdev, "id:%d is not requested\n",
17462306a36Sopenharmony_ci			 desc->attr.line_id);
17562306a36Sopenharmony_ci		ret = -EINVAL;
17662306a36Sopenharmony_ci		goto unlock;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (unlikely(!test_bit(HTE_TS_REQ, &ei->flags) &&
18062306a36Sopenharmony_ci	    test_bit(HTE_TS_REGISTERED, &ei->flags))) {
18162306a36Sopenharmony_ci		dev_info(gdev->sdev, "id:%d is registered but not requested\n",
18262306a36Sopenharmony_ci			 desc->attr.line_id);
18362306a36Sopenharmony_ci		ret = -EINVAL;
18462306a36Sopenharmony_ci		goto unlock;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (test_bit(HTE_TS_REQ, &ei->flags) &&
18862306a36Sopenharmony_ci	    !test_bit(HTE_TS_REGISTERED, &ei->flags)) {
18962306a36Sopenharmony_ci		clear_bit(HTE_TS_REQ, &ei->flags);
19062306a36Sopenharmony_ci		desc->hte_data = NULL;
19162306a36Sopenharmony_ci		ret = 0;
19262306a36Sopenharmony_ci		goto mod_put;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	ret = gdev->chip->ops->release(gdev->chip, desc, ei->xlated_id);
19662306a36Sopenharmony_ci	if (ret) {
19762306a36Sopenharmony_ci		dev_err(gdev->sdev, "id: %d free failed\n",
19862306a36Sopenharmony_ci			desc->attr.line_id);
19962306a36Sopenharmony_ci		goto unlock;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	kfree(ei->line_name);
20362306a36Sopenharmony_ci	if (ei->free_attr_name)
20462306a36Sopenharmony_ci		kfree_const(desc->attr.name);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	debugfs_remove_recursive(ei->ts_dbg_root);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	spin_lock_irqsave(&ei->slock, flag);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (test_bit(HTE_TS_QUEUE_WK, &ei->flags)) {
21162306a36Sopenharmony_ci		spin_unlock_irqrestore(&ei->slock, flag);
21262306a36Sopenharmony_ci		flush_work(&ei->cb_work);
21362306a36Sopenharmony_ci		spin_lock_irqsave(&ei->slock, flag);
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	atomic_dec(&gdev->ts_req);
21762306a36Sopenharmony_ci	atomic_set(&ei->dropped_ts, 0);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	ei->seq = 1;
22062306a36Sopenharmony_ci	ei->flags = 0;
22162306a36Sopenharmony_ci	desc->hte_data = NULL;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ei->slock, flag);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	ei->cb = NULL;
22662306a36Sopenharmony_ci	ei->tcb = NULL;
22762306a36Sopenharmony_ci	ei->cl_data = NULL;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cimod_put:
23062306a36Sopenharmony_ci	module_put(gdev->owner);
23162306a36Sopenharmony_ciunlock:
23262306a36Sopenharmony_ci	mutex_unlock(&ei->req_mlock);
23362306a36Sopenharmony_ci	dev_dbg(gdev->sdev, "release id: %d\n", desc->attr.line_id);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return ret;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hte_ts_put);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic int hte_ts_dis_en_common(struct hte_ts_desc *desc, bool en)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	u32 ts_id;
24262306a36Sopenharmony_ci	struct hte_device *gdev;
24362306a36Sopenharmony_ci	struct hte_ts_info *ei;
24462306a36Sopenharmony_ci	int ret;
24562306a36Sopenharmony_ci	unsigned long flag;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (!desc)
24862306a36Sopenharmony_ci		return -EINVAL;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	ei = desc->hte_data;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (!ei || !ei->gdev)
25362306a36Sopenharmony_ci		return -EINVAL;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	gdev = ei->gdev;
25662306a36Sopenharmony_ci	ts_id = desc->attr.line_id;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	mutex_lock(&ei->req_mlock);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (!test_bit(HTE_TS_REGISTERED, &ei->flags)) {
26162306a36Sopenharmony_ci		dev_dbg(gdev->sdev, "id:%d is not registered", ts_id);
26262306a36Sopenharmony_ci		ret = -EUSERS;
26362306a36Sopenharmony_ci		goto out;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	spin_lock_irqsave(&ei->slock, flag);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (en) {
26962306a36Sopenharmony_ci		if (!test_bit(HTE_TS_DISABLE, &ei->flags)) {
27062306a36Sopenharmony_ci			ret = 0;
27162306a36Sopenharmony_ci			goto out_unlock;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		spin_unlock_irqrestore(&ei->slock, flag);
27562306a36Sopenharmony_ci		ret = gdev->chip->ops->enable(gdev->chip, ei->xlated_id);
27662306a36Sopenharmony_ci		if (ret) {
27762306a36Sopenharmony_ci			dev_warn(gdev->sdev, "id: %d enable failed\n",
27862306a36Sopenharmony_ci				 ts_id);
27962306a36Sopenharmony_ci			goto out;
28062306a36Sopenharmony_ci		}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		spin_lock_irqsave(&ei->slock, flag);
28362306a36Sopenharmony_ci		clear_bit(HTE_TS_DISABLE, &ei->flags);
28462306a36Sopenharmony_ci	} else {
28562306a36Sopenharmony_ci		if (test_bit(HTE_TS_DISABLE, &ei->flags)) {
28662306a36Sopenharmony_ci			ret = 0;
28762306a36Sopenharmony_ci			goto out_unlock;
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		spin_unlock_irqrestore(&ei->slock, flag);
29162306a36Sopenharmony_ci		ret = gdev->chip->ops->disable(gdev->chip, ei->xlated_id);
29262306a36Sopenharmony_ci		if (ret) {
29362306a36Sopenharmony_ci			dev_warn(gdev->sdev, "id: %d disable failed\n",
29462306a36Sopenharmony_ci				 ts_id);
29562306a36Sopenharmony_ci			goto out;
29662306a36Sopenharmony_ci		}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci		spin_lock_irqsave(&ei->slock, flag);
29962306a36Sopenharmony_ci		set_bit(HTE_TS_DISABLE, &ei->flags);
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ciout_unlock:
30362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ei->slock, flag);
30462306a36Sopenharmony_ciout:
30562306a36Sopenharmony_ci	mutex_unlock(&ei->req_mlock);
30662306a36Sopenharmony_ci	return ret;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci/**
31062306a36Sopenharmony_ci * hte_disable_ts() - Disable timestamp on given descriptor.
31162306a36Sopenharmony_ci *
31262306a36Sopenharmony_ci * The API does not release any resources associated with desc.
31362306a36Sopenharmony_ci *
31462306a36Sopenharmony_ci * @desc: ts descriptor, this is the same as returned by the request API.
31562306a36Sopenharmony_ci *
31662306a36Sopenharmony_ci * Context: Holds mutex lock, not suitable from atomic context.
31762306a36Sopenharmony_ci * Returns: 0 on success or a negative error code on failure.
31862306a36Sopenharmony_ci */
31962306a36Sopenharmony_ciint hte_disable_ts(struct hte_ts_desc *desc)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	return hte_ts_dis_en_common(desc, false);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hte_disable_ts);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/**
32662306a36Sopenharmony_ci * hte_enable_ts() - Enable timestamp on given descriptor.
32762306a36Sopenharmony_ci *
32862306a36Sopenharmony_ci * @desc: ts descriptor, this is the same as returned by the request API.
32962306a36Sopenharmony_ci *
33062306a36Sopenharmony_ci * Context: Holds mutex lock, not suitable from atomic context.
33162306a36Sopenharmony_ci * Returns: 0 on success or a negative error code on failure.
33262306a36Sopenharmony_ci */
33362306a36Sopenharmony_ciint hte_enable_ts(struct hte_ts_desc *desc)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	return hte_ts_dis_en_common(desc, true);
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hte_enable_ts);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic void hte_do_cb_work(struct work_struct *w)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	unsigned long flag;
34262306a36Sopenharmony_ci	struct hte_ts_info *ei = container_of(w, struct hte_ts_info, cb_work);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (unlikely(!ei->tcb))
34562306a36Sopenharmony_ci		return;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	ei->tcb(ei->cl_data);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	spin_lock_irqsave(&ei->slock, flag);
35062306a36Sopenharmony_ci	clear_bit(HTE_TS_QUEUE_WK, &ei->flags);
35162306a36Sopenharmony_ci	spin_unlock_irqrestore(&ei->slock, flag);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic int __hte_req_ts(struct hte_ts_desc *desc, hte_ts_cb_t cb,
35562306a36Sopenharmony_ci			hte_ts_sec_cb_t tcb, void *data)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	int ret;
35862306a36Sopenharmony_ci	struct hte_device *gdev;
35962306a36Sopenharmony_ci	struct hte_ts_info *ei = desc->hte_data;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	gdev = ei->gdev;
36262306a36Sopenharmony_ci	/*
36362306a36Sopenharmony_ci	 * There is a chance that multiple consumers requesting same entity,
36462306a36Sopenharmony_ci	 * lock here.
36562306a36Sopenharmony_ci	 */
36662306a36Sopenharmony_ci	mutex_lock(&ei->req_mlock);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (test_bit(HTE_TS_REGISTERED, &ei->flags) ||
36962306a36Sopenharmony_ci	    !test_bit(HTE_TS_REQ, &ei->flags)) {
37062306a36Sopenharmony_ci		dev_dbg(gdev->chip->dev, "id:%u req failed\n",
37162306a36Sopenharmony_ci			desc->attr.line_id);
37262306a36Sopenharmony_ci		ret = -EUSERS;
37362306a36Sopenharmony_ci		goto unlock;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	ei->cb = cb;
37762306a36Sopenharmony_ci	ei->tcb = tcb;
37862306a36Sopenharmony_ci	if (tcb)
37962306a36Sopenharmony_ci		INIT_WORK(&ei->cb_work, hte_do_cb_work);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	ret = gdev->chip->ops->request(gdev->chip, desc, ei->xlated_id);
38262306a36Sopenharmony_ci	if (ret < 0) {
38362306a36Sopenharmony_ci		dev_err(gdev->chip->dev, "ts request failed\n");
38462306a36Sopenharmony_ci		goto unlock;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	ei->cl_data = data;
38862306a36Sopenharmony_ci	ei->seq = 1;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	atomic_inc(&gdev->ts_req);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	ei->line_name = NULL;
39362306a36Sopenharmony_ci	if (!desc->attr.name) {
39462306a36Sopenharmony_ci		ei->line_name = kzalloc(HTE_TS_NAME_LEN, GFP_KERNEL);
39562306a36Sopenharmony_ci		if (ei->line_name)
39662306a36Sopenharmony_ci			scnprintf(ei->line_name, HTE_TS_NAME_LEN, "ts_%u",
39762306a36Sopenharmony_ci				  desc->attr.line_id);
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	hte_ts_dbgfs_init(desc->attr.name == NULL ?
40162306a36Sopenharmony_ci			  ei->line_name : desc->attr.name, ei);
40262306a36Sopenharmony_ci	set_bit(HTE_TS_REGISTERED, &ei->flags);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	dev_dbg(gdev->chip->dev, "id: %u, xlated id:%u",
40562306a36Sopenharmony_ci		desc->attr.line_id, ei->xlated_id);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	ret = 0;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ciunlock:
41062306a36Sopenharmony_ci	mutex_unlock(&ei->req_mlock);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	return ret;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic int hte_bind_ts_info_locked(struct hte_ts_info *ei,
41662306a36Sopenharmony_ci				   struct hte_ts_desc *desc, u32 x_id)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	int ret = 0;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	mutex_lock(&ei->req_mlock);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (test_bit(HTE_TS_REQ, &ei->flags)) {
42362306a36Sopenharmony_ci		dev_dbg(ei->gdev->chip->dev, "id:%u is already requested\n",
42462306a36Sopenharmony_ci			desc->attr.line_id);
42562306a36Sopenharmony_ci		ret = -EUSERS;
42662306a36Sopenharmony_ci		goto out;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	set_bit(HTE_TS_REQ, &ei->flags);
43062306a36Sopenharmony_ci	desc->hte_data = ei;
43162306a36Sopenharmony_ci	ei->xlated_id = x_id;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ciout:
43462306a36Sopenharmony_ci	mutex_unlock(&ei->req_mlock);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return ret;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic struct hte_device *of_node_to_htedevice(struct device_node *np)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct hte_device *gdev;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	spin_lock(&hte_lock);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	list_for_each_entry(gdev, &hte_devices, list)
44662306a36Sopenharmony_ci		if (gdev->chip && gdev->chip->dev &&
44762306a36Sopenharmony_ci		    device_match_of_node(gdev->chip->dev, np)) {
44862306a36Sopenharmony_ci			spin_unlock(&hte_lock);
44962306a36Sopenharmony_ci			return gdev;
45062306a36Sopenharmony_ci		}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	spin_unlock(&hte_lock);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return ERR_PTR(-ENODEV);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic struct hte_device *hte_find_dev_from_linedata(struct hte_ts_desc *desc)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	struct hte_device *gdev;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	spin_lock(&hte_lock);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	list_for_each_entry(gdev, &hte_devices, list)
46462306a36Sopenharmony_ci		if (gdev->chip && gdev->chip->match_from_linedata) {
46562306a36Sopenharmony_ci			if (!gdev->chip->match_from_linedata(gdev->chip, desc))
46662306a36Sopenharmony_ci				continue;
46762306a36Sopenharmony_ci			spin_unlock(&hte_lock);
46862306a36Sopenharmony_ci			return gdev;
46962306a36Sopenharmony_ci		}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	spin_unlock(&hte_lock);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return ERR_PTR(-ENODEV);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci/**
47762306a36Sopenharmony_ci * of_hte_req_count - Return the number of entities to timestamp.
47862306a36Sopenharmony_ci *
47962306a36Sopenharmony_ci * The function returns the total count of the requested entities to timestamp
48062306a36Sopenharmony_ci * by parsing device tree.
48162306a36Sopenharmony_ci *
48262306a36Sopenharmony_ci * @dev: The HTE consumer.
48362306a36Sopenharmony_ci *
48462306a36Sopenharmony_ci * Returns: Positive number on success, -ENOENT if no entries,
48562306a36Sopenharmony_ci * -EINVAL for other errors.
48662306a36Sopenharmony_ci */
48762306a36Sopenharmony_ciint of_hte_req_count(struct device *dev)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	int count;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (!dev || !dev->of_node)
49262306a36Sopenharmony_ci		return -EINVAL;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	count = of_count_phandle_with_args(dev->of_node, "timestamps",
49562306a36Sopenharmony_ci					   "#timestamp-cells");
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return count ? count : -ENOENT;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_hte_req_count);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic inline struct hte_device *hte_get_dev(struct hte_ts_desc *desc)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	return hte_find_dev_from_linedata(desc);
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic struct hte_device *hte_of_get_dev(struct device *dev,
50762306a36Sopenharmony_ci					 struct hte_ts_desc *desc,
50862306a36Sopenharmony_ci					 int index,
50962306a36Sopenharmony_ci					 struct of_phandle_args *args,
51062306a36Sopenharmony_ci					 bool *free_name)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	int ret;
51362306a36Sopenharmony_ci	struct device_node *np;
51462306a36Sopenharmony_ci	char *temp;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (!dev->of_node)
51762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	np = dev->of_node;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (!of_property_present(np, "timestamp-names")) {
52262306a36Sopenharmony_ci		/* Let hte core construct it during request time */
52362306a36Sopenharmony_ci		desc->attr.name = NULL;
52462306a36Sopenharmony_ci	} else {
52562306a36Sopenharmony_ci		ret = of_property_read_string_index(np, "timestamp-names",
52662306a36Sopenharmony_ci						    index, &desc->attr.name);
52762306a36Sopenharmony_ci		if (ret) {
52862306a36Sopenharmony_ci			pr_err("can't parse \"timestamp-names\" property\n");
52962306a36Sopenharmony_ci			return ERR_PTR(ret);
53062306a36Sopenharmony_ci		}
53162306a36Sopenharmony_ci		*free_name = false;
53262306a36Sopenharmony_ci		if (desc->attr.name) {
53362306a36Sopenharmony_ci			temp = skip_spaces(desc->attr.name);
53462306a36Sopenharmony_ci			if (!*temp)
53562306a36Sopenharmony_ci				desc->attr.name = NULL;
53662306a36Sopenharmony_ci		}
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	ret = of_parse_phandle_with_args(np, "timestamps", "#timestamp-cells",
54062306a36Sopenharmony_ci					 index, args);
54162306a36Sopenharmony_ci	if (ret) {
54262306a36Sopenharmony_ci		pr_err("%s(): can't parse \"timestamps\" property\n",
54362306a36Sopenharmony_ci		       __func__);
54462306a36Sopenharmony_ci		return ERR_PTR(ret);
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	of_node_put(args->np);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	return of_node_to_htedevice(args->np);
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci/**
55362306a36Sopenharmony_ci * hte_ts_get() - The function to initialize and obtain HTE desc.
55462306a36Sopenharmony_ci *
55562306a36Sopenharmony_ci * The function initializes the consumer provided HTE descriptor. If consumer
55662306a36Sopenharmony_ci * has device tree node, index is used to parse the line id and other details.
55762306a36Sopenharmony_ci * The function needs to be called before using any request APIs.
55862306a36Sopenharmony_ci *
55962306a36Sopenharmony_ci * @dev: HTE consumer/client device, used in case of parsing device tree node.
56062306a36Sopenharmony_ci * @desc: Pre-allocated timestamp descriptor.
56162306a36Sopenharmony_ci * @index: The index will be used as an index to parse line_id from the
56262306a36Sopenharmony_ci * device tree node if node is present.
56362306a36Sopenharmony_ci *
56462306a36Sopenharmony_ci * Context: Holds mutex lock.
56562306a36Sopenharmony_ci * Returns: Returns 0 on success or negative error code on failure.
56662306a36Sopenharmony_ci */
56762306a36Sopenharmony_ciint hte_ts_get(struct device *dev, struct hte_ts_desc *desc, int index)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	struct hte_device *gdev;
57062306a36Sopenharmony_ci	struct hte_ts_info *ei;
57162306a36Sopenharmony_ci	const struct fwnode_handle *fwnode;
57262306a36Sopenharmony_ci	struct of_phandle_args args;
57362306a36Sopenharmony_ci	u32 xlated_id;
57462306a36Sopenharmony_ci	int ret;
57562306a36Sopenharmony_ci	bool free_name = false;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (!desc)
57862306a36Sopenharmony_ci		return -EINVAL;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	fwnode = dev ? dev_fwnode(dev) : NULL;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	if (is_of_node(fwnode))
58362306a36Sopenharmony_ci		gdev = hte_of_get_dev(dev, desc, index, &args, &free_name);
58462306a36Sopenharmony_ci	else
58562306a36Sopenharmony_ci		gdev = hte_get_dev(desc);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if (IS_ERR(gdev)) {
58862306a36Sopenharmony_ci		pr_err("%s() no hte dev found\n", __func__);
58962306a36Sopenharmony_ci		return PTR_ERR(gdev);
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	if (!try_module_get(gdev->owner))
59362306a36Sopenharmony_ci		return -ENODEV;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (!gdev->chip) {
59662306a36Sopenharmony_ci		pr_err("%s(): requested id does not have provider\n",
59762306a36Sopenharmony_ci		       __func__);
59862306a36Sopenharmony_ci		ret = -ENODEV;
59962306a36Sopenharmony_ci		goto put;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	if (is_of_node(fwnode)) {
60362306a36Sopenharmony_ci		if (!gdev->chip->xlate_of)
60462306a36Sopenharmony_ci			ret = -EINVAL;
60562306a36Sopenharmony_ci		else
60662306a36Sopenharmony_ci			ret = gdev->chip->xlate_of(gdev->chip, &args,
60762306a36Sopenharmony_ci						   desc, &xlated_id);
60862306a36Sopenharmony_ci	} else {
60962306a36Sopenharmony_ci		if (!gdev->chip->xlate_plat)
61062306a36Sopenharmony_ci			ret = -EINVAL;
61162306a36Sopenharmony_ci		else
61262306a36Sopenharmony_ci			ret = gdev->chip->xlate_plat(gdev->chip, desc,
61362306a36Sopenharmony_ci						     &xlated_id);
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (ret < 0)
61762306a36Sopenharmony_ci		goto put;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	ei = &gdev->ei[xlated_id];
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	ret = hte_bind_ts_info_locked(ei, desc, xlated_id);
62262306a36Sopenharmony_ci	if (ret)
62362306a36Sopenharmony_ci		goto put;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	ei->free_attr_name = free_name;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	return 0;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ciput:
63062306a36Sopenharmony_ci	module_put(gdev->owner);
63162306a36Sopenharmony_ci	return ret;
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hte_ts_get);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic void __devm_hte_release_ts(void *res)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	hte_ts_put(res);
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci/**
64162306a36Sopenharmony_ci * hte_request_ts_ns() - The API to request and enable hardware timestamp in
64262306a36Sopenharmony_ci * nanoseconds.
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci * The entity is provider specific for example, GPIO lines, signals, buses
64562306a36Sopenharmony_ci * etc...The API allocates necessary resources and enables the timestamp.
64662306a36Sopenharmony_ci *
64762306a36Sopenharmony_ci * @desc: Pre-allocated and initialized timestamp descriptor.
64862306a36Sopenharmony_ci * @cb: Callback to push the timestamp data to consumer.
64962306a36Sopenharmony_ci * @tcb: Optional callback. If its provided, subsystem initializes
65062306a36Sopenharmony_ci * workqueue. It is called when cb returns HTE_RUN_SECOND_CB.
65162306a36Sopenharmony_ci * @data: Client data, used during cb and tcb callbacks.
65262306a36Sopenharmony_ci *
65362306a36Sopenharmony_ci * Context: Holds mutex lock.
65462306a36Sopenharmony_ci * Returns: Returns 0 on success or negative error code on failure.
65562306a36Sopenharmony_ci */
65662306a36Sopenharmony_ciint hte_request_ts_ns(struct hte_ts_desc *desc, hte_ts_cb_t cb,
65762306a36Sopenharmony_ci		      hte_ts_sec_cb_t tcb, void *data)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	int ret;
66062306a36Sopenharmony_ci	struct hte_ts_info *ei;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (!desc || !desc->hte_data || !cb)
66362306a36Sopenharmony_ci		return -EINVAL;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	ei = desc->hte_data;
66662306a36Sopenharmony_ci	if (!ei || !ei->gdev)
66762306a36Sopenharmony_ci		return -EINVAL;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	ret = __hte_req_ts(desc, cb, tcb, data);
67062306a36Sopenharmony_ci	if (ret < 0) {
67162306a36Sopenharmony_ci		dev_err(ei->gdev->chip->dev,
67262306a36Sopenharmony_ci			"failed to request id: %d\n", desc->attr.line_id);
67362306a36Sopenharmony_ci		return ret;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	return 0;
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hte_request_ts_ns);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci/**
68162306a36Sopenharmony_ci * devm_hte_request_ts_ns() - Resource managed API to request and enable
68262306a36Sopenharmony_ci * hardware timestamp in nanoseconds.
68362306a36Sopenharmony_ci *
68462306a36Sopenharmony_ci * The entity is provider specific for example, GPIO lines, signals, buses
68562306a36Sopenharmony_ci * etc...The API allocates necessary resources and enables the timestamp. It
68662306a36Sopenharmony_ci * deallocates and disables automatically when the consumer exits.
68762306a36Sopenharmony_ci *
68862306a36Sopenharmony_ci * @dev: HTE consumer/client device.
68962306a36Sopenharmony_ci * @desc: Pre-allocated and initialized timestamp descriptor.
69062306a36Sopenharmony_ci * @cb: Callback to push the timestamp data to consumer.
69162306a36Sopenharmony_ci * @tcb: Optional callback. If its provided, subsystem initializes
69262306a36Sopenharmony_ci * workqueue. It is called when cb returns HTE_RUN_SECOND_CB.
69362306a36Sopenharmony_ci * @data: Client data, used during cb and tcb callbacks.
69462306a36Sopenharmony_ci *
69562306a36Sopenharmony_ci * Context: Holds mutex lock.
69662306a36Sopenharmony_ci * Returns: Returns 0 on success or negative error code on failure.
69762306a36Sopenharmony_ci */
69862306a36Sopenharmony_ciint devm_hte_request_ts_ns(struct device *dev, struct hte_ts_desc *desc,
69962306a36Sopenharmony_ci			   hte_ts_cb_t cb, hte_ts_sec_cb_t tcb,
70062306a36Sopenharmony_ci			   void *data)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	int err;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (!dev)
70562306a36Sopenharmony_ci		return -EINVAL;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	err = hte_request_ts_ns(desc, cb, tcb, data);
70862306a36Sopenharmony_ci	if (err)
70962306a36Sopenharmony_ci		return err;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	err = devm_add_action_or_reset(dev, __devm_hte_release_ts, desc);
71262306a36Sopenharmony_ci	if (err)
71362306a36Sopenharmony_ci		return err;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	return 0;
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_hte_request_ts_ns);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci/**
72062306a36Sopenharmony_ci * hte_init_line_attr() - Initialize line attributes.
72162306a36Sopenharmony_ci *
72262306a36Sopenharmony_ci * Zeroes out line attributes and initializes with provided arguments.
72362306a36Sopenharmony_ci * The function needs to be called before calling any consumer facing
72462306a36Sopenharmony_ci * functions.
72562306a36Sopenharmony_ci *
72662306a36Sopenharmony_ci * @desc: Pre-allocated timestamp descriptor.
72762306a36Sopenharmony_ci * @line_id: line id.
72862306a36Sopenharmony_ci * @edge_flags: edge flags related to line_id.
72962306a36Sopenharmony_ci * @name: name of the line.
73062306a36Sopenharmony_ci * @data: line data related to line_id.
73162306a36Sopenharmony_ci *
73262306a36Sopenharmony_ci * Context: Any.
73362306a36Sopenharmony_ci * Returns: 0 on success or negative error code for the failure.
73462306a36Sopenharmony_ci */
73562306a36Sopenharmony_ciint hte_init_line_attr(struct hte_ts_desc *desc, u32 line_id,
73662306a36Sopenharmony_ci		       unsigned long edge_flags, const char *name, void *data)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	if (!desc)
73962306a36Sopenharmony_ci		return -EINVAL;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	memset(&desc->attr, 0, sizeof(desc->attr));
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	desc->attr.edge_flags = edge_flags;
74462306a36Sopenharmony_ci	desc->attr.line_id = line_id;
74562306a36Sopenharmony_ci	desc->attr.line_data = data;
74662306a36Sopenharmony_ci	if (name) {
74762306a36Sopenharmony_ci		name =  kstrdup_const(name, GFP_KERNEL);
74862306a36Sopenharmony_ci		if (!name)
74962306a36Sopenharmony_ci			return -ENOMEM;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	desc->attr.name = name;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	return 0;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hte_init_line_attr);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci/**
75962306a36Sopenharmony_ci * hte_get_clk_src_info() - Get the clock source information for a ts
76062306a36Sopenharmony_ci * descriptor.
76162306a36Sopenharmony_ci *
76262306a36Sopenharmony_ci * @desc: ts descriptor, same as returned from request API.
76362306a36Sopenharmony_ci * @ci: The API fills this structure with the clock information data.
76462306a36Sopenharmony_ci *
76562306a36Sopenharmony_ci * Context: Any context.
76662306a36Sopenharmony_ci * Returns: 0 on success else negative error code on failure.
76762306a36Sopenharmony_ci */
76862306a36Sopenharmony_ciint hte_get_clk_src_info(const struct hte_ts_desc *desc,
76962306a36Sopenharmony_ci			 struct hte_clk_info *ci)
77062306a36Sopenharmony_ci{
77162306a36Sopenharmony_ci	struct hte_chip *chip;
77262306a36Sopenharmony_ci	struct hte_ts_info *ei;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (!desc || !desc->hte_data || !ci) {
77562306a36Sopenharmony_ci		pr_debug("%s:%d\n", __func__, __LINE__);
77662306a36Sopenharmony_ci		return -EINVAL;
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	ei = desc->hte_data;
78062306a36Sopenharmony_ci	if (!ei->gdev || !ei->gdev->chip)
78162306a36Sopenharmony_ci		return -EINVAL;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	chip = ei->gdev->chip;
78462306a36Sopenharmony_ci	if (!chip->ops->get_clk_src_info)
78562306a36Sopenharmony_ci		return -EOPNOTSUPP;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	return chip->ops->get_clk_src_info(chip, ci);
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hte_get_clk_src_info);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci/**
79262306a36Sopenharmony_ci * hte_push_ts_ns() - Push timestamp data in nanoseconds.
79362306a36Sopenharmony_ci *
79462306a36Sopenharmony_ci * It is used by the provider to push timestamp data.
79562306a36Sopenharmony_ci *
79662306a36Sopenharmony_ci * @chip: The HTE chip, used during the registration.
79762306a36Sopenharmony_ci * @xlated_id: entity id understood by both subsystem and provider, this is
79862306a36Sopenharmony_ci * obtained from xlate callback during request API.
79962306a36Sopenharmony_ci * @data: timestamp data.
80062306a36Sopenharmony_ci *
80162306a36Sopenharmony_ci * Returns: 0 on success or a negative error code on failure.
80262306a36Sopenharmony_ci */
80362306a36Sopenharmony_ciint hte_push_ts_ns(const struct hte_chip *chip, u32 xlated_id,
80462306a36Sopenharmony_ci		   struct hte_ts_data *data)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	enum hte_return ret;
80762306a36Sopenharmony_ci	int st = 0;
80862306a36Sopenharmony_ci	struct hte_ts_info *ei;
80962306a36Sopenharmony_ci	unsigned long flag;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	if (!chip || !data || !chip->gdev)
81262306a36Sopenharmony_ci		return -EINVAL;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	if (xlated_id >= chip->nlines)
81562306a36Sopenharmony_ci		return -EINVAL;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	ei = &chip->gdev->ei[xlated_id];
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	spin_lock_irqsave(&ei->slock, flag);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	/* timestamp sequence counter */
82262306a36Sopenharmony_ci	data->seq = ei->seq++;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	if (!test_bit(HTE_TS_REGISTERED, &ei->flags) ||
82562306a36Sopenharmony_ci	    test_bit(HTE_TS_DISABLE, &ei->flags)) {
82662306a36Sopenharmony_ci		dev_dbg(chip->dev, "Unknown timestamp push\n");
82762306a36Sopenharmony_ci		atomic_inc(&ei->dropped_ts);
82862306a36Sopenharmony_ci		st = -EINVAL;
82962306a36Sopenharmony_ci		goto unlock;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	ret = ei->cb(data, ei->cl_data);
83362306a36Sopenharmony_ci	if (ret == HTE_RUN_SECOND_CB && ei->tcb) {
83462306a36Sopenharmony_ci		queue_work(system_unbound_wq, &ei->cb_work);
83562306a36Sopenharmony_ci		set_bit(HTE_TS_QUEUE_WK, &ei->flags);
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ciunlock:
83962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ei->slock, flag);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	return st;
84262306a36Sopenharmony_ci}
84362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hte_push_ts_ns);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_cistatic int hte_register_chip(struct hte_chip *chip)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	struct hte_device *gdev;
84862306a36Sopenharmony_ci	u32 i;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	if (!chip || !chip->dev || !chip->dev->of_node)
85162306a36Sopenharmony_ci		return -EINVAL;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (!chip->ops || !chip->ops->request || !chip->ops->release) {
85462306a36Sopenharmony_ci		dev_err(chip->dev, "Driver needs to provide ops\n");
85562306a36Sopenharmony_ci		return -EINVAL;
85662306a36Sopenharmony_ci	}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	gdev = kzalloc(struct_size(gdev, ei, chip->nlines), GFP_KERNEL);
85962306a36Sopenharmony_ci	if (!gdev)
86062306a36Sopenharmony_ci		return -ENOMEM;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	gdev->chip = chip;
86362306a36Sopenharmony_ci	chip->gdev = gdev;
86462306a36Sopenharmony_ci	gdev->nlines = chip->nlines;
86562306a36Sopenharmony_ci	gdev->sdev = chip->dev;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	for (i = 0; i < chip->nlines; i++) {
86862306a36Sopenharmony_ci		gdev->ei[i].gdev = gdev;
86962306a36Sopenharmony_ci		mutex_init(&gdev->ei[i].req_mlock);
87062306a36Sopenharmony_ci		spin_lock_init(&gdev->ei[i].slock);
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	if (chip->dev->driver)
87462306a36Sopenharmony_ci		gdev->owner = chip->dev->driver->owner;
87562306a36Sopenharmony_ci	else
87662306a36Sopenharmony_ci		gdev->owner = THIS_MODULE;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	of_node_get(chip->dev->of_node);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	INIT_LIST_HEAD(&gdev->list);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	spin_lock(&hte_lock);
88362306a36Sopenharmony_ci	list_add_tail(&gdev->list, &hte_devices);
88462306a36Sopenharmony_ci	spin_unlock(&hte_lock);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	hte_chip_dbgfs_init(gdev);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	dev_dbg(chip->dev, "Added hte chip\n");
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	return 0;
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_cistatic int hte_unregister_chip(struct hte_chip *chip)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	struct hte_device *gdev;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	if (!chip)
89862306a36Sopenharmony_ci		return -EINVAL;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	gdev = chip->gdev;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	spin_lock(&hte_lock);
90362306a36Sopenharmony_ci	list_del(&gdev->list);
90462306a36Sopenharmony_ci	spin_unlock(&hte_lock);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	gdev->chip = NULL;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	of_node_put(chip->dev->of_node);
90962306a36Sopenharmony_ci	debugfs_remove_recursive(gdev->dbg_root);
91062306a36Sopenharmony_ci	kfree(gdev);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	dev_dbg(chip->dev, "Removed hte chip\n");
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	return 0;
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_cistatic void _hte_devm_unregister_chip(void *chip)
91862306a36Sopenharmony_ci{
91962306a36Sopenharmony_ci	hte_unregister_chip(chip);
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci/**
92362306a36Sopenharmony_ci * devm_hte_register_chip() - Resource managed API to register HTE chip.
92462306a36Sopenharmony_ci *
92562306a36Sopenharmony_ci * It is used by the provider to register itself with the HTE subsystem.
92662306a36Sopenharmony_ci * The unregistration is done automatically when the provider exits.
92762306a36Sopenharmony_ci *
92862306a36Sopenharmony_ci * @chip: the HTE chip to add to subsystem.
92962306a36Sopenharmony_ci *
93062306a36Sopenharmony_ci * Returns: 0 on success or a negative error code on failure.
93162306a36Sopenharmony_ci */
93262306a36Sopenharmony_ciint devm_hte_register_chip(struct hte_chip *chip)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	int err;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	err = hte_register_chip(chip);
93762306a36Sopenharmony_ci	if (err)
93862306a36Sopenharmony_ci		return err;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	err = devm_add_action_or_reset(chip->dev, _hte_devm_unregister_chip,
94162306a36Sopenharmony_ci				       chip);
94262306a36Sopenharmony_ci	if (err)
94362306a36Sopenharmony_ci		return err;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	return 0;
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_hte_register_chip);
948