xref: /linux/drivers/hte/hte.c (revision 902dea62b3dda532172c0f4d0410fe595266b22c)
131ab09b4SDipen Patel // SPDX-License-Identifier: GPL-2.0
231ab09b4SDipen Patel /*
331ab09b4SDipen Patel  * Copyright (c) 2021-2022 NVIDIA Corporation
431ab09b4SDipen Patel  *
531ab09b4SDipen Patel  * Author: Dipen Patel <dipenp@nvidia.com>
631ab09b4SDipen Patel  */
731ab09b4SDipen Patel 
831ab09b4SDipen Patel #include <linux/kernel.h>
931ab09b4SDipen Patel #include <linux/module.h>
1031ab09b4SDipen Patel #include <linux/err.h>
1131ab09b4SDipen Patel #include <linux/slab.h>
1231ab09b4SDipen Patel #include <linux/of.h>
1331ab09b4SDipen Patel #include <linux/of_device.h>
1431ab09b4SDipen Patel #include <linux/mutex.h>
1531ab09b4SDipen Patel #include <linux/uaccess.h>
1631ab09b4SDipen Patel #include <linux/hte.h>
1731ab09b4SDipen Patel #include <linux/delay.h>
1831ab09b4SDipen Patel #include <linux/debugfs.h>
1931ab09b4SDipen Patel 
2031ab09b4SDipen Patel #define HTE_TS_NAME_LEN		10
2131ab09b4SDipen Patel 
2231ab09b4SDipen Patel /* Global list of the HTE devices */
2331ab09b4SDipen Patel static DEFINE_SPINLOCK(hte_lock);
2431ab09b4SDipen Patel static LIST_HEAD(hte_devices);
2531ab09b4SDipen Patel 
2631ab09b4SDipen Patel enum {
2731ab09b4SDipen Patel 	HTE_TS_REGISTERED,
2831ab09b4SDipen Patel 	HTE_TS_REQ,
2931ab09b4SDipen Patel 	HTE_TS_DISABLE,
3031ab09b4SDipen Patel 	HTE_TS_QUEUE_WK,
3131ab09b4SDipen Patel };
3231ab09b4SDipen Patel 
3331ab09b4SDipen Patel /**
3431ab09b4SDipen Patel  * struct hte_ts_info - Information related to requested timestamp.
3531ab09b4SDipen Patel  *
3631ab09b4SDipen Patel  * @xlated_id: Timestamp ID as understood between HTE subsys and HTE provider,
3731ab09b4SDipen Patel  * See xlate callback API.
3831ab09b4SDipen Patel  * @flags: Flags holding state information.
3931ab09b4SDipen Patel  * @hte_cb_flags: Callback related flags.
4031ab09b4SDipen Patel  * @seq: Timestamp sequence counter.
4131ab09b4SDipen Patel  * @line_name: HTE allocated line name.
4231ab09b4SDipen Patel  * @free_attr_name: If set, free the attr name.
4331ab09b4SDipen Patel  * @cb: A nonsleeping callback function provided by clients.
4431ab09b4SDipen Patel  * @tcb: A secondary sleeping callback function provided by clients.
4531ab09b4SDipen Patel  * @dropped_ts: Dropped timestamps.
4631ab09b4SDipen Patel  * @slock: Spin lock to synchronize between disable/enable,
4731ab09b4SDipen Patel  * request/release APIs.
4831ab09b4SDipen Patel  * @cb_work: callback workqueue, used when tcb is specified.
4931ab09b4SDipen Patel  * @req_mlock: Lock during timestamp request/release APIs.
5031ab09b4SDipen Patel  * @ts_dbg_root: Root for the debug fs.
5131ab09b4SDipen Patel  * @gdev: HTE abstract device that this timestamp information belongs to.
5231ab09b4SDipen Patel  * @cl_data: Client specific data.
5331ab09b4SDipen Patel  */
5431ab09b4SDipen Patel struct hte_ts_info {
5531ab09b4SDipen Patel 	u32 xlated_id;
5631ab09b4SDipen Patel 	unsigned long flags;
5731ab09b4SDipen Patel 	unsigned long hte_cb_flags;
5831ab09b4SDipen Patel 	u64 seq;
5931ab09b4SDipen Patel 	char *line_name;
6031ab09b4SDipen Patel 	bool free_attr_name;
6131ab09b4SDipen Patel 	hte_ts_cb_t cb;
6231ab09b4SDipen Patel 	hte_ts_sec_cb_t tcb;
6331ab09b4SDipen Patel 	atomic_t dropped_ts;
6431ab09b4SDipen Patel 	spinlock_t slock;
6531ab09b4SDipen Patel 	struct work_struct cb_work;
6631ab09b4SDipen Patel 	struct mutex req_mlock;
6731ab09b4SDipen Patel 	struct dentry *ts_dbg_root;
6831ab09b4SDipen Patel 	struct hte_device *gdev;
6931ab09b4SDipen Patel 	void *cl_data;
7031ab09b4SDipen Patel };
7131ab09b4SDipen Patel 
7231ab09b4SDipen Patel /**
7331ab09b4SDipen Patel  * struct hte_device - HTE abstract device
7431ab09b4SDipen Patel  * @nlines: Number of entities this device supports.
7531ab09b4SDipen Patel  * @ts_req: Total number of entities requested.
7631ab09b4SDipen Patel  * @sdev: Device used at various debug prints.
7731ab09b4SDipen Patel  * @dbg_root: Root directory for debug fs.
7831ab09b4SDipen Patel  * @list: List node to store hte_device for each provider.
7931ab09b4SDipen Patel  * @chip: HTE chip providing this HTE device.
8031ab09b4SDipen Patel  * @owner: helps prevent removal of modules when in use.
8131ab09b4SDipen Patel  * @ei: Timestamp information.
8231ab09b4SDipen Patel  */
8331ab09b4SDipen Patel struct hte_device {
8431ab09b4SDipen Patel 	u32 nlines;
8531ab09b4SDipen Patel 	atomic_t ts_req;
8631ab09b4SDipen Patel 	struct device *sdev;
8731ab09b4SDipen Patel 	struct dentry *dbg_root;
8831ab09b4SDipen Patel 	struct list_head list;
8931ab09b4SDipen Patel 	struct hte_chip *chip;
9031ab09b4SDipen Patel 	struct module *owner;
9131ab09b4SDipen Patel 	struct hte_ts_info ei[];
9231ab09b4SDipen Patel };
9331ab09b4SDipen Patel 
9431ab09b4SDipen Patel #ifdef CONFIG_DEBUG_FS
9531ab09b4SDipen Patel 
9631ab09b4SDipen Patel static struct dentry *hte_root;
9731ab09b4SDipen Patel 
9831ab09b4SDipen Patel static int __init hte_subsys_dbgfs_init(void)
9931ab09b4SDipen Patel {
10031ab09b4SDipen Patel 	/* creates /sys/kernel/debug/hte/ */
10131ab09b4SDipen Patel 	hte_root = debugfs_create_dir("hte", NULL);
10231ab09b4SDipen Patel 
10331ab09b4SDipen Patel 	return 0;
10431ab09b4SDipen Patel }
10531ab09b4SDipen Patel subsys_initcall(hte_subsys_dbgfs_init);
10631ab09b4SDipen Patel 
10731ab09b4SDipen Patel static void hte_chip_dbgfs_init(struct hte_device *gdev)
10831ab09b4SDipen Patel {
10931ab09b4SDipen Patel 	const struct hte_chip *chip = gdev->chip;
11031ab09b4SDipen Patel 	const char *name = chip->name ? chip->name : dev_name(chip->dev);
11131ab09b4SDipen Patel 
11231ab09b4SDipen Patel 	gdev->dbg_root = debugfs_create_dir(name, hte_root);
11331ab09b4SDipen Patel 
11431ab09b4SDipen Patel 	debugfs_create_atomic_t("ts_requested", 0444, gdev->dbg_root,
11531ab09b4SDipen Patel 				&gdev->ts_req);
11631ab09b4SDipen Patel 	debugfs_create_u32("total_ts", 0444, gdev->dbg_root,
11731ab09b4SDipen Patel 			   &gdev->nlines);
11831ab09b4SDipen Patel }
11931ab09b4SDipen Patel 
12031ab09b4SDipen Patel static void hte_ts_dbgfs_init(const char *name, struct hte_ts_info *ei)
12131ab09b4SDipen Patel {
12231ab09b4SDipen Patel 	if (!ei->gdev->dbg_root || !name)
12331ab09b4SDipen Patel 		return;
12431ab09b4SDipen Patel 
12531ab09b4SDipen Patel 	ei->ts_dbg_root = debugfs_create_dir(name, ei->gdev->dbg_root);
12631ab09b4SDipen Patel 
12731ab09b4SDipen Patel 	debugfs_create_atomic_t("dropped_timestamps", 0444, ei->ts_dbg_root,
12831ab09b4SDipen Patel 				&ei->dropped_ts);
12931ab09b4SDipen Patel }
13031ab09b4SDipen Patel 
13131ab09b4SDipen Patel #else
13231ab09b4SDipen Patel 
13331ab09b4SDipen Patel static void hte_chip_dbgfs_init(struct hte_device *gdev)
13431ab09b4SDipen Patel {
13531ab09b4SDipen Patel }
13631ab09b4SDipen Patel 
13731ab09b4SDipen Patel static void hte_ts_dbgfs_init(const char *name, struct hte_ts_info *ei)
13831ab09b4SDipen Patel {
13931ab09b4SDipen Patel }
14031ab09b4SDipen Patel 
14131ab09b4SDipen Patel #endif
14231ab09b4SDipen Patel 
14331ab09b4SDipen Patel /**
14431ab09b4SDipen Patel  * hte_ts_put() - Release and disable timestamp for the given desc.
14531ab09b4SDipen Patel  *
14631ab09b4SDipen Patel  * @desc: timestamp descriptor.
14731ab09b4SDipen Patel  *
14831ab09b4SDipen Patel  * Context: debugfs_remove_recursive() function call may use sleeping locks,
14931ab09b4SDipen Patel  *	    not suitable from atomic context.
15031ab09b4SDipen Patel  * Returns: 0 on success or a negative error code on failure.
15131ab09b4SDipen Patel  */
15231ab09b4SDipen Patel int hte_ts_put(struct hte_ts_desc *desc)
15331ab09b4SDipen Patel {
15431ab09b4SDipen Patel 	int ret = 0;
15531ab09b4SDipen Patel 	unsigned long flag;
15631ab09b4SDipen Patel 	struct hte_device *gdev;
15731ab09b4SDipen Patel 	struct hte_ts_info *ei;
15831ab09b4SDipen Patel 
15931ab09b4SDipen Patel 	if (!desc)
16031ab09b4SDipen Patel 		return -EINVAL;
16131ab09b4SDipen Patel 
16231ab09b4SDipen Patel 	ei = desc->hte_data;
16331ab09b4SDipen Patel 
16431ab09b4SDipen Patel 	if (!ei || !ei->gdev)
16531ab09b4SDipen Patel 		return -EINVAL;
16631ab09b4SDipen Patel 
16731ab09b4SDipen Patel 	gdev = ei->gdev;
16831ab09b4SDipen Patel 
16931ab09b4SDipen Patel 	mutex_lock(&ei->req_mlock);
17031ab09b4SDipen Patel 
17131ab09b4SDipen Patel 	if (unlikely(!test_bit(HTE_TS_REQ, &ei->flags) &&
17231ab09b4SDipen Patel 	    !test_bit(HTE_TS_REGISTERED, &ei->flags))) {
17331ab09b4SDipen Patel 		dev_info(gdev->sdev, "id:%d is not requested\n",
17431ab09b4SDipen Patel 			 desc->attr.line_id);
17531ab09b4SDipen Patel 		ret = -EINVAL;
17631ab09b4SDipen Patel 		goto unlock;
17731ab09b4SDipen Patel 	}
17831ab09b4SDipen Patel 
17931ab09b4SDipen Patel 	if (unlikely(!test_bit(HTE_TS_REQ, &ei->flags) &&
18031ab09b4SDipen Patel 	    test_bit(HTE_TS_REGISTERED, &ei->flags))) {
18131ab09b4SDipen Patel 		dev_info(gdev->sdev, "id:%d is registered but not requested\n",
18231ab09b4SDipen Patel 			 desc->attr.line_id);
18331ab09b4SDipen Patel 		ret = -EINVAL;
18431ab09b4SDipen Patel 		goto unlock;
18531ab09b4SDipen Patel 	}
18631ab09b4SDipen Patel 
18731ab09b4SDipen Patel 	if (test_bit(HTE_TS_REQ, &ei->flags) &&
18831ab09b4SDipen Patel 	    !test_bit(HTE_TS_REGISTERED, &ei->flags)) {
18931ab09b4SDipen Patel 		clear_bit(HTE_TS_REQ, &ei->flags);
19031ab09b4SDipen Patel 		desc->hte_data = NULL;
19131ab09b4SDipen Patel 		ret = 0;
19231ab09b4SDipen Patel 		goto mod_put;
19331ab09b4SDipen Patel 	}
19431ab09b4SDipen Patel 
19531ab09b4SDipen Patel 	ret = gdev->chip->ops->release(gdev->chip, desc, ei->xlated_id);
19631ab09b4SDipen Patel 	if (ret) {
19731ab09b4SDipen Patel 		dev_err(gdev->sdev, "id: %d free failed\n",
19831ab09b4SDipen Patel 			desc->attr.line_id);
19931ab09b4SDipen Patel 		goto unlock;
20031ab09b4SDipen Patel 	}
20131ab09b4SDipen Patel 
20231ab09b4SDipen Patel 	kfree(ei->line_name);
20331ab09b4SDipen Patel 	if (ei->free_attr_name)
20431ab09b4SDipen Patel 		kfree_const(desc->attr.name);
20531ab09b4SDipen Patel 
20631ab09b4SDipen Patel 	debugfs_remove_recursive(ei->ts_dbg_root);
20731ab09b4SDipen Patel 
20831ab09b4SDipen Patel 	spin_lock_irqsave(&ei->slock, flag);
20931ab09b4SDipen Patel 
21031ab09b4SDipen Patel 	if (test_bit(HTE_TS_QUEUE_WK, &ei->flags)) {
21131ab09b4SDipen Patel 		spin_unlock_irqrestore(&ei->slock, flag);
21231ab09b4SDipen Patel 		flush_work(&ei->cb_work);
21331ab09b4SDipen Patel 		spin_lock_irqsave(&ei->slock, flag);
21431ab09b4SDipen Patel 	}
21531ab09b4SDipen Patel 
21631ab09b4SDipen Patel 	atomic_dec(&gdev->ts_req);
21731ab09b4SDipen Patel 	atomic_set(&ei->dropped_ts, 0);
21831ab09b4SDipen Patel 
21931ab09b4SDipen Patel 	ei->seq = 1;
22031ab09b4SDipen Patel 	ei->flags = 0;
22131ab09b4SDipen Patel 	desc->hte_data = NULL;
22231ab09b4SDipen Patel 
22331ab09b4SDipen Patel 	spin_unlock_irqrestore(&ei->slock, flag);
22431ab09b4SDipen Patel 
22531ab09b4SDipen Patel 	ei->cb = NULL;
22631ab09b4SDipen Patel 	ei->tcb = NULL;
22731ab09b4SDipen Patel 	ei->cl_data = NULL;
22831ab09b4SDipen Patel 
22931ab09b4SDipen Patel mod_put:
23031ab09b4SDipen Patel 	module_put(gdev->owner);
23131ab09b4SDipen Patel unlock:
23231ab09b4SDipen Patel 	mutex_unlock(&ei->req_mlock);
23331ab09b4SDipen Patel 	dev_dbg(gdev->sdev, "release id: %d\n", desc->attr.line_id);
23431ab09b4SDipen Patel 
23531ab09b4SDipen Patel 	return ret;
23631ab09b4SDipen Patel }
23731ab09b4SDipen Patel EXPORT_SYMBOL_GPL(hte_ts_put);
23831ab09b4SDipen Patel 
23931ab09b4SDipen Patel static int hte_ts_dis_en_common(struct hte_ts_desc *desc, bool en)
24031ab09b4SDipen Patel {
24131ab09b4SDipen Patel 	u32 ts_id;
24231ab09b4SDipen Patel 	struct hte_device *gdev;
24331ab09b4SDipen Patel 	struct hte_ts_info *ei;
24431ab09b4SDipen Patel 	int ret;
24531ab09b4SDipen Patel 	unsigned long flag;
24631ab09b4SDipen Patel 
24731ab09b4SDipen Patel 	if (!desc)
24831ab09b4SDipen Patel 		return -EINVAL;
24931ab09b4SDipen Patel 
25031ab09b4SDipen Patel 	ei = desc->hte_data;
25131ab09b4SDipen Patel 
25231ab09b4SDipen Patel 	if (!ei || !ei->gdev)
25331ab09b4SDipen Patel 		return -EINVAL;
25431ab09b4SDipen Patel 
25531ab09b4SDipen Patel 	gdev = ei->gdev;
25631ab09b4SDipen Patel 	ts_id = desc->attr.line_id;
25731ab09b4SDipen Patel 
25831ab09b4SDipen Patel 	mutex_lock(&ei->req_mlock);
25931ab09b4SDipen Patel 
26031ab09b4SDipen Patel 	if (!test_bit(HTE_TS_REGISTERED, &ei->flags)) {
26131ab09b4SDipen Patel 		dev_dbg(gdev->sdev, "id:%d is not registered", ts_id);
26231ab09b4SDipen Patel 		ret = -EUSERS;
26331ab09b4SDipen Patel 		goto out;
26431ab09b4SDipen Patel 	}
26531ab09b4SDipen Patel 
26631ab09b4SDipen Patel 	spin_lock_irqsave(&ei->slock, flag);
26731ab09b4SDipen Patel 
26831ab09b4SDipen Patel 	if (en) {
26931ab09b4SDipen Patel 		if (!test_bit(HTE_TS_DISABLE, &ei->flags)) {
27031ab09b4SDipen Patel 			ret = 0;
27131ab09b4SDipen Patel 			goto out_unlock;
27231ab09b4SDipen Patel 		}
27331ab09b4SDipen Patel 
27431ab09b4SDipen Patel 		spin_unlock_irqrestore(&ei->slock, flag);
27531ab09b4SDipen Patel 		ret = gdev->chip->ops->enable(gdev->chip, ei->xlated_id);
27631ab09b4SDipen Patel 		if (ret) {
27731ab09b4SDipen Patel 			dev_warn(gdev->sdev, "id: %d enable failed\n",
27831ab09b4SDipen Patel 				 ts_id);
27931ab09b4SDipen Patel 			goto out;
28031ab09b4SDipen Patel 		}
28131ab09b4SDipen Patel 
28231ab09b4SDipen Patel 		spin_lock_irqsave(&ei->slock, flag);
28331ab09b4SDipen Patel 		clear_bit(HTE_TS_DISABLE, &ei->flags);
28431ab09b4SDipen Patel 	} else {
28531ab09b4SDipen Patel 		if (test_bit(HTE_TS_DISABLE, &ei->flags)) {
28631ab09b4SDipen Patel 			ret = 0;
28731ab09b4SDipen Patel 			goto out_unlock;
28831ab09b4SDipen Patel 		}
28931ab09b4SDipen Patel 
29031ab09b4SDipen Patel 		spin_unlock_irqrestore(&ei->slock, flag);
29131ab09b4SDipen Patel 		ret = gdev->chip->ops->disable(gdev->chip, ei->xlated_id);
29231ab09b4SDipen Patel 		if (ret) {
29331ab09b4SDipen Patel 			dev_warn(gdev->sdev, "id: %d disable failed\n",
29431ab09b4SDipen Patel 				 ts_id);
29531ab09b4SDipen Patel 			goto out;
29631ab09b4SDipen Patel 		}
29731ab09b4SDipen Patel 
29831ab09b4SDipen Patel 		spin_lock_irqsave(&ei->slock, flag);
29931ab09b4SDipen Patel 		set_bit(HTE_TS_DISABLE, &ei->flags);
30031ab09b4SDipen Patel 	}
30131ab09b4SDipen Patel 
30231ab09b4SDipen Patel out_unlock:
30331ab09b4SDipen Patel 	spin_unlock_irqrestore(&ei->slock, flag);
30431ab09b4SDipen Patel out:
30531ab09b4SDipen Patel 	mutex_unlock(&ei->req_mlock);
30631ab09b4SDipen Patel 	return ret;
30731ab09b4SDipen Patel }
30831ab09b4SDipen Patel 
30931ab09b4SDipen Patel /**
31031ab09b4SDipen Patel  * hte_disable_ts() - Disable timestamp on given descriptor.
31131ab09b4SDipen Patel  *
31231ab09b4SDipen Patel  * The API does not release any resources associated with desc.
31331ab09b4SDipen Patel  *
31431ab09b4SDipen Patel  * @desc: ts descriptor, this is the same as returned by the request API.
31531ab09b4SDipen Patel  *
31631ab09b4SDipen Patel  * Context: Holds mutex lock, not suitable from atomic context.
31731ab09b4SDipen Patel  * Returns: 0 on success or a negative error code on failure.
31831ab09b4SDipen Patel  */
31931ab09b4SDipen Patel int hte_disable_ts(struct hte_ts_desc *desc)
32031ab09b4SDipen Patel {
32131ab09b4SDipen Patel 	return hte_ts_dis_en_common(desc, false);
32231ab09b4SDipen Patel }
32331ab09b4SDipen Patel EXPORT_SYMBOL_GPL(hte_disable_ts);
32431ab09b4SDipen Patel 
32531ab09b4SDipen Patel /**
32631ab09b4SDipen Patel  * hte_enable_ts() - Enable timestamp on given descriptor.
32731ab09b4SDipen Patel  *
32831ab09b4SDipen Patel  * @desc: ts descriptor, this is the same as returned by the request API.
32931ab09b4SDipen Patel  *
33031ab09b4SDipen Patel  * Context: Holds mutex lock, not suitable from atomic context.
33131ab09b4SDipen Patel  * Returns: 0 on success or a negative error code on failure.
33231ab09b4SDipen Patel  */
33331ab09b4SDipen Patel int hte_enable_ts(struct hte_ts_desc *desc)
33431ab09b4SDipen Patel {
33531ab09b4SDipen Patel 	return hte_ts_dis_en_common(desc, true);
33631ab09b4SDipen Patel }
33731ab09b4SDipen Patel EXPORT_SYMBOL_GPL(hte_enable_ts);
33831ab09b4SDipen Patel 
33931ab09b4SDipen Patel static void hte_do_cb_work(struct work_struct *w)
34031ab09b4SDipen Patel {
34131ab09b4SDipen Patel 	unsigned long flag;
34231ab09b4SDipen Patel 	struct hte_ts_info *ei = container_of(w, struct hte_ts_info, cb_work);
34331ab09b4SDipen Patel 
34431ab09b4SDipen Patel 	if (unlikely(!ei->tcb))
34531ab09b4SDipen Patel 		return;
34631ab09b4SDipen Patel 
34731ab09b4SDipen Patel 	ei->tcb(ei->cl_data);
34831ab09b4SDipen Patel 
34931ab09b4SDipen Patel 	spin_lock_irqsave(&ei->slock, flag);
35031ab09b4SDipen Patel 	clear_bit(HTE_TS_QUEUE_WK, &ei->flags);
35131ab09b4SDipen Patel 	spin_unlock_irqrestore(&ei->slock, flag);
35231ab09b4SDipen Patel }
35331ab09b4SDipen Patel 
35431ab09b4SDipen Patel static int __hte_req_ts(struct hte_ts_desc *desc, hte_ts_cb_t cb,
35531ab09b4SDipen Patel 			hte_ts_sec_cb_t tcb, void *data)
35631ab09b4SDipen Patel {
35731ab09b4SDipen Patel 	int ret;
35831ab09b4SDipen Patel 	struct hte_device *gdev;
35931ab09b4SDipen Patel 	struct hte_ts_info *ei = desc->hte_data;
36031ab09b4SDipen Patel 
36131ab09b4SDipen Patel 	gdev = ei->gdev;
36231ab09b4SDipen Patel 	/*
36331ab09b4SDipen Patel 	 * There is a chance that multiple consumers requesting same entity,
36431ab09b4SDipen Patel 	 * lock here.
36531ab09b4SDipen Patel 	 */
36631ab09b4SDipen Patel 	mutex_lock(&ei->req_mlock);
36731ab09b4SDipen Patel 
36831ab09b4SDipen Patel 	if (test_bit(HTE_TS_REGISTERED, &ei->flags) ||
36931ab09b4SDipen Patel 	    !test_bit(HTE_TS_REQ, &ei->flags)) {
37031ab09b4SDipen Patel 		dev_dbg(gdev->chip->dev, "id:%u req failed\n",
37131ab09b4SDipen Patel 			desc->attr.line_id);
37231ab09b4SDipen Patel 		ret = -EUSERS;
37331ab09b4SDipen Patel 		goto unlock;
37431ab09b4SDipen Patel 	}
37531ab09b4SDipen Patel 
37631ab09b4SDipen Patel 	ei->cb = cb;
37731ab09b4SDipen Patel 	ei->tcb = tcb;
37831ab09b4SDipen Patel 	if (tcb)
37931ab09b4SDipen Patel 		INIT_WORK(&ei->cb_work, hte_do_cb_work);
38031ab09b4SDipen Patel 
38131ab09b4SDipen Patel 	ret = gdev->chip->ops->request(gdev->chip, desc, ei->xlated_id);
38231ab09b4SDipen Patel 	if (ret < 0) {
38331ab09b4SDipen Patel 		dev_err(gdev->chip->dev, "ts request failed\n");
38431ab09b4SDipen Patel 		goto unlock;
38531ab09b4SDipen Patel 	}
38631ab09b4SDipen Patel 
38731ab09b4SDipen Patel 	ei->cl_data = data;
38831ab09b4SDipen Patel 	ei->seq = 1;
38931ab09b4SDipen Patel 
39031ab09b4SDipen Patel 	atomic_inc(&gdev->ts_req);
39131ab09b4SDipen Patel 
39231ab09b4SDipen Patel 	ei->line_name = NULL;
39331ab09b4SDipen Patel 	if (!desc->attr.name) {
39431ab09b4SDipen Patel 		ei->line_name = kzalloc(HTE_TS_NAME_LEN, GFP_KERNEL);
39531ab09b4SDipen Patel 		if (ei->line_name)
39631ab09b4SDipen Patel 			scnprintf(ei->line_name, HTE_TS_NAME_LEN, "ts_%u",
39731ab09b4SDipen Patel 				  desc->attr.line_id);
39831ab09b4SDipen Patel 	}
39931ab09b4SDipen Patel 
40031ab09b4SDipen Patel 	hte_ts_dbgfs_init(desc->attr.name == NULL ?
40131ab09b4SDipen Patel 			  ei->line_name : desc->attr.name, ei);
40231ab09b4SDipen Patel 	set_bit(HTE_TS_REGISTERED, &ei->flags);
40331ab09b4SDipen Patel 
40431ab09b4SDipen Patel 	dev_dbg(gdev->chip->dev, "id: %u, xlated id:%u",
40531ab09b4SDipen Patel 		desc->attr.line_id, ei->xlated_id);
40631ab09b4SDipen Patel 
40731ab09b4SDipen Patel 	ret = 0;
40831ab09b4SDipen Patel 
40931ab09b4SDipen Patel unlock:
41031ab09b4SDipen Patel 	mutex_unlock(&ei->req_mlock);
41131ab09b4SDipen Patel 
41231ab09b4SDipen Patel 	return ret;
41331ab09b4SDipen Patel }
41431ab09b4SDipen Patel 
41531ab09b4SDipen Patel static int hte_bind_ts_info_locked(struct hte_ts_info *ei,
41631ab09b4SDipen Patel 				   struct hte_ts_desc *desc, u32 x_id)
41731ab09b4SDipen Patel {
41831ab09b4SDipen Patel 	int ret = 0;
41931ab09b4SDipen Patel 
42031ab09b4SDipen Patel 	mutex_lock(&ei->req_mlock);
42131ab09b4SDipen Patel 
42231ab09b4SDipen Patel 	if (test_bit(HTE_TS_REQ, &ei->flags)) {
42331ab09b4SDipen Patel 		dev_dbg(ei->gdev->chip->dev, "id:%u is already requested\n",
42431ab09b4SDipen Patel 			desc->attr.line_id);
42531ab09b4SDipen Patel 		ret = -EUSERS;
42631ab09b4SDipen Patel 		goto out;
42731ab09b4SDipen Patel 	}
42831ab09b4SDipen Patel 
42931ab09b4SDipen Patel 	set_bit(HTE_TS_REQ, &ei->flags);
43031ab09b4SDipen Patel 	desc->hte_data = ei;
43131ab09b4SDipen Patel 	ei->xlated_id = x_id;
43231ab09b4SDipen Patel 
43331ab09b4SDipen Patel out:
43431ab09b4SDipen Patel 	mutex_unlock(&ei->req_mlock);
43531ab09b4SDipen Patel 
43631ab09b4SDipen Patel 	return ret;
43731ab09b4SDipen Patel }
43831ab09b4SDipen Patel 
43931ab09b4SDipen Patel static struct hte_device *of_node_to_htedevice(struct device_node *np)
44031ab09b4SDipen Patel {
44131ab09b4SDipen Patel 	struct hte_device *gdev;
44231ab09b4SDipen Patel 
44331ab09b4SDipen Patel 	spin_lock(&hte_lock);
44431ab09b4SDipen Patel 
44531ab09b4SDipen Patel 	list_for_each_entry(gdev, &hte_devices, list)
44631ab09b4SDipen Patel 		if (gdev->chip && gdev->chip->dev &&
44731ab09b4SDipen Patel 		    gdev->chip->dev->of_node == np) {
44831ab09b4SDipen Patel 			spin_unlock(&hte_lock);
44931ab09b4SDipen Patel 			return gdev;
45031ab09b4SDipen Patel 		}
45131ab09b4SDipen Patel 
45231ab09b4SDipen Patel 	spin_unlock(&hte_lock);
45331ab09b4SDipen Patel 
45431ab09b4SDipen Patel 	return ERR_PTR(-ENODEV);
45531ab09b4SDipen Patel }
45631ab09b4SDipen Patel 
45731ab09b4SDipen Patel static struct hte_device *hte_find_dev_from_linedata(struct hte_ts_desc *desc)
45831ab09b4SDipen Patel {
45931ab09b4SDipen Patel 	struct hte_device *gdev;
46031ab09b4SDipen Patel 
46131ab09b4SDipen Patel 	spin_lock(&hte_lock);
46231ab09b4SDipen Patel 
46331ab09b4SDipen Patel 	list_for_each_entry(gdev, &hte_devices, list)
46431ab09b4SDipen Patel 		if (gdev->chip && gdev->chip->match_from_linedata) {
46531ab09b4SDipen Patel 			if (!gdev->chip->match_from_linedata(gdev->chip, desc))
46631ab09b4SDipen Patel 				continue;
46731ab09b4SDipen Patel 			spin_unlock(&hte_lock);
46831ab09b4SDipen Patel 			return gdev;
46931ab09b4SDipen Patel 		}
47031ab09b4SDipen Patel 
47131ab09b4SDipen Patel 	spin_unlock(&hte_lock);
47231ab09b4SDipen Patel 
47331ab09b4SDipen Patel 	return ERR_PTR(-ENODEV);
47431ab09b4SDipen Patel }
47531ab09b4SDipen Patel 
47631ab09b4SDipen Patel /**
47731ab09b4SDipen Patel  * of_hte_req_count - Return the number of entities to timestamp.
47831ab09b4SDipen Patel  *
47931ab09b4SDipen Patel  * The function returns the total count of the requested entities to timestamp
48031ab09b4SDipen Patel  * by parsing device tree.
48131ab09b4SDipen Patel  *
48231ab09b4SDipen Patel  * @dev: The HTE consumer.
48331ab09b4SDipen Patel  *
48431ab09b4SDipen Patel  * Returns: Positive number on success, -ENOENT if no entries,
48531ab09b4SDipen Patel  * -EINVAL for other errors.
48631ab09b4SDipen Patel  */
48731ab09b4SDipen Patel int of_hte_req_count(struct device *dev)
48831ab09b4SDipen Patel {
48931ab09b4SDipen Patel 	int count;
49031ab09b4SDipen Patel 
49131ab09b4SDipen Patel 	if (!dev || !dev->of_node)
49231ab09b4SDipen Patel 		return -EINVAL;
49331ab09b4SDipen Patel 
49431ab09b4SDipen Patel 	count = of_count_phandle_with_args(dev->of_node, "timestamps",
49531ab09b4SDipen Patel 					   "#timestamp-cells");
49631ab09b4SDipen Patel 
49731ab09b4SDipen Patel 	return count ? count : -ENOENT;
49831ab09b4SDipen Patel }
49931ab09b4SDipen Patel EXPORT_SYMBOL_GPL(of_hte_req_count);
50031ab09b4SDipen Patel 
50131ab09b4SDipen Patel static inline struct hte_device *hte_get_dev(struct hte_ts_desc *desc)
50231ab09b4SDipen Patel {
50331ab09b4SDipen Patel 	return hte_find_dev_from_linedata(desc);
50431ab09b4SDipen Patel }
50531ab09b4SDipen Patel 
50631ab09b4SDipen Patel static struct hte_device *hte_of_get_dev(struct device *dev,
50731ab09b4SDipen Patel 					 struct hte_ts_desc *desc,
50831ab09b4SDipen Patel 					 int index,
50931ab09b4SDipen Patel 					 struct of_phandle_args *args,
51031ab09b4SDipen Patel 					 bool *free_name)
51131ab09b4SDipen Patel {
51231ab09b4SDipen Patel 	int ret;
51331ab09b4SDipen Patel 	struct device_node *np;
51431ab09b4SDipen Patel 	char *temp;
51531ab09b4SDipen Patel 
51631ab09b4SDipen Patel 	if (!dev->of_node)
51731ab09b4SDipen Patel 		return ERR_PTR(-EINVAL);
51831ab09b4SDipen Patel 
51931ab09b4SDipen Patel 	np = dev->of_node;
52031ab09b4SDipen Patel 
521*902dea62SRob Herring 	if (!of_property_present(np, "timestamp-names")) {
52231ab09b4SDipen Patel 		/* Let hte core construct it during request time */
52331ab09b4SDipen Patel 		desc->attr.name = NULL;
52431ab09b4SDipen Patel 	} else {
52531ab09b4SDipen Patel 		ret = of_property_read_string_index(np, "timestamp-names",
52631ab09b4SDipen Patel 						    index, &desc->attr.name);
52731ab09b4SDipen Patel 		if (ret) {
52831ab09b4SDipen Patel 			pr_err("can't parse \"timestamp-names\" property\n");
52931ab09b4SDipen Patel 			return ERR_PTR(ret);
53031ab09b4SDipen Patel 		}
53131ab09b4SDipen Patel 		*free_name = false;
53231ab09b4SDipen Patel 		if (desc->attr.name) {
53331ab09b4SDipen Patel 			temp = skip_spaces(desc->attr.name);
53431ab09b4SDipen Patel 			if (!*temp)
53531ab09b4SDipen Patel 				desc->attr.name = NULL;
53631ab09b4SDipen Patel 		}
53731ab09b4SDipen Patel 	}
53831ab09b4SDipen Patel 
53931ab09b4SDipen Patel 	ret = of_parse_phandle_with_args(np, "timestamps", "#timestamp-cells",
54031ab09b4SDipen Patel 					 index, args);
54131ab09b4SDipen Patel 	if (ret) {
54231ab09b4SDipen Patel 		pr_err("%s(): can't parse \"timestamps\" property\n",
54331ab09b4SDipen Patel 		       __func__);
54431ab09b4SDipen Patel 		return ERR_PTR(ret);
54531ab09b4SDipen Patel 	}
54631ab09b4SDipen Patel 
54731ab09b4SDipen Patel 	of_node_put(args->np);
54831ab09b4SDipen Patel 
54931ab09b4SDipen Patel 	return of_node_to_htedevice(args->np);
55031ab09b4SDipen Patel }
55131ab09b4SDipen Patel 
55231ab09b4SDipen Patel /**
55331ab09b4SDipen Patel  * hte_ts_get() - The function to initialize and obtain HTE desc.
55431ab09b4SDipen Patel  *
55531ab09b4SDipen Patel  * The function initializes the consumer provided HTE descriptor. If consumer
55631ab09b4SDipen Patel  * has device tree node, index is used to parse the line id and other details.
55731ab09b4SDipen Patel  * The function needs to be called before using any request APIs.
55831ab09b4SDipen Patel  *
55931ab09b4SDipen Patel  * @dev: HTE consumer/client device, used in case of parsing device tree node.
56031ab09b4SDipen Patel  * @desc: Pre-allocated timestamp descriptor.
56131ab09b4SDipen Patel  * @index: The index will be used as an index to parse line_id from the
56231ab09b4SDipen Patel  * device tree node if node is present.
56331ab09b4SDipen Patel  *
56431ab09b4SDipen Patel  * Context: Holds mutex lock.
56531ab09b4SDipen Patel  * Returns: Returns 0 on success or negative error code on failure.
56631ab09b4SDipen Patel  */
56731ab09b4SDipen Patel int hte_ts_get(struct device *dev, struct hte_ts_desc *desc, int index)
56831ab09b4SDipen Patel {
56931ab09b4SDipen Patel 	struct hte_device *gdev;
57031ab09b4SDipen Patel 	struct hte_ts_info *ei;
57131ab09b4SDipen Patel 	const struct fwnode_handle *fwnode;
57231ab09b4SDipen Patel 	struct of_phandle_args args;
57331ab09b4SDipen Patel 	u32 xlated_id;
57431ab09b4SDipen Patel 	int ret;
575348b10b0SDan Carpenter 	bool free_name = false;
57631ab09b4SDipen Patel 
57731ab09b4SDipen Patel 	if (!desc)
57831ab09b4SDipen Patel 		return -EINVAL;
57931ab09b4SDipen Patel 
58031ab09b4SDipen Patel 	fwnode = dev ? dev_fwnode(dev) : NULL;
58131ab09b4SDipen Patel 
58231ab09b4SDipen Patel 	if (is_of_node(fwnode))
58331ab09b4SDipen Patel 		gdev = hte_of_get_dev(dev, desc, index, &args, &free_name);
58431ab09b4SDipen Patel 	else
58531ab09b4SDipen Patel 		gdev = hte_get_dev(desc);
58631ab09b4SDipen Patel 
58731ab09b4SDipen Patel 	if (IS_ERR(gdev)) {
58831ab09b4SDipen Patel 		pr_err("%s() no hte dev found\n", __func__);
58931ab09b4SDipen Patel 		return PTR_ERR(gdev);
59031ab09b4SDipen Patel 	}
59131ab09b4SDipen Patel 
59231ab09b4SDipen Patel 	if (!try_module_get(gdev->owner))
59331ab09b4SDipen Patel 		return -ENODEV;
59431ab09b4SDipen Patel 
59531ab09b4SDipen Patel 	if (!gdev->chip) {
59631ab09b4SDipen Patel 		pr_err("%s(): requested id does not have provider\n",
59731ab09b4SDipen Patel 		       __func__);
59831ab09b4SDipen Patel 		ret = -ENODEV;
59931ab09b4SDipen Patel 		goto put;
60031ab09b4SDipen Patel 	}
60131ab09b4SDipen Patel 
60231ab09b4SDipen Patel 	if (is_of_node(fwnode)) {
60331ab09b4SDipen Patel 		if (!gdev->chip->xlate_of)
60431ab09b4SDipen Patel 			ret = -EINVAL;
60531ab09b4SDipen Patel 		else
60631ab09b4SDipen Patel 			ret = gdev->chip->xlate_of(gdev->chip, &args,
60731ab09b4SDipen Patel 						   desc, &xlated_id);
60831ab09b4SDipen Patel 	} else {
60931ab09b4SDipen Patel 		if (!gdev->chip->xlate_plat)
61031ab09b4SDipen Patel 			ret = -EINVAL;
61131ab09b4SDipen Patel 		else
61231ab09b4SDipen Patel 			ret = gdev->chip->xlate_plat(gdev->chip, desc,
61331ab09b4SDipen Patel 						     &xlated_id);
61431ab09b4SDipen Patel 	}
61531ab09b4SDipen Patel 
61631ab09b4SDipen Patel 	if (ret < 0)
61731ab09b4SDipen Patel 		goto put;
61831ab09b4SDipen Patel 
61931ab09b4SDipen Patel 	ei = &gdev->ei[xlated_id];
62031ab09b4SDipen Patel 
62131ab09b4SDipen Patel 	ret = hte_bind_ts_info_locked(ei, desc, xlated_id);
62231ab09b4SDipen Patel 	if (ret)
62331ab09b4SDipen Patel 		goto put;
62431ab09b4SDipen Patel 
62531ab09b4SDipen Patel 	ei->free_attr_name = free_name;
62631ab09b4SDipen Patel 
62731ab09b4SDipen Patel 	return 0;
62831ab09b4SDipen Patel 
62931ab09b4SDipen Patel put:
63031ab09b4SDipen Patel 	module_put(gdev->owner);
63131ab09b4SDipen Patel 	return ret;
63231ab09b4SDipen Patel }
63331ab09b4SDipen Patel EXPORT_SYMBOL_GPL(hte_ts_get);
63431ab09b4SDipen Patel 
63531ab09b4SDipen Patel static void __devm_hte_release_ts(void *res)
63631ab09b4SDipen Patel {
63731ab09b4SDipen Patel 	hte_ts_put(res);
63831ab09b4SDipen Patel }
63931ab09b4SDipen Patel 
64031ab09b4SDipen Patel /**
64131ab09b4SDipen Patel  * hte_request_ts_ns() - The API to request and enable hardware timestamp in
64231ab09b4SDipen Patel  * nanoseconds.
64331ab09b4SDipen Patel  *
64431ab09b4SDipen Patel  * The entity is provider specific for example, GPIO lines, signals, buses
64531ab09b4SDipen Patel  * etc...The API allocates necessary resources and enables the timestamp.
64631ab09b4SDipen Patel  *
64731ab09b4SDipen Patel  * @desc: Pre-allocated and initialized timestamp descriptor.
64831ab09b4SDipen Patel  * @cb: Callback to push the timestamp data to consumer.
64931ab09b4SDipen Patel  * @tcb: Optional callback. If its provided, subsystem initializes
65031ab09b4SDipen Patel  * workqueue. It is called when cb returns HTE_RUN_SECOND_CB.
65131ab09b4SDipen Patel  * @data: Client data, used during cb and tcb callbacks.
65231ab09b4SDipen Patel  *
65331ab09b4SDipen Patel  * Context: Holds mutex lock.
65431ab09b4SDipen Patel  * Returns: Returns 0 on success or negative error code on failure.
65531ab09b4SDipen Patel  */
65631ab09b4SDipen Patel int hte_request_ts_ns(struct hte_ts_desc *desc, hte_ts_cb_t cb,
65731ab09b4SDipen Patel 		      hte_ts_sec_cb_t tcb, void *data)
65831ab09b4SDipen Patel {
65931ab09b4SDipen Patel 	int ret;
66031ab09b4SDipen Patel 	struct hte_ts_info *ei;
66131ab09b4SDipen Patel 
66231ab09b4SDipen Patel 	if (!desc || !desc->hte_data || !cb)
66331ab09b4SDipen Patel 		return -EINVAL;
66431ab09b4SDipen Patel 
66531ab09b4SDipen Patel 	ei = desc->hte_data;
66631ab09b4SDipen Patel 	if (!ei || !ei->gdev)
66731ab09b4SDipen Patel 		return -EINVAL;
66831ab09b4SDipen Patel 
66931ab09b4SDipen Patel 	ret = __hte_req_ts(desc, cb, tcb, data);
67031ab09b4SDipen Patel 	if (ret < 0) {
67131ab09b4SDipen Patel 		dev_err(ei->gdev->chip->dev,
67231ab09b4SDipen Patel 			"failed to request id: %d\n", desc->attr.line_id);
67331ab09b4SDipen Patel 		return ret;
67431ab09b4SDipen Patel 	}
67531ab09b4SDipen Patel 
67631ab09b4SDipen Patel 	return 0;
67731ab09b4SDipen Patel }
67831ab09b4SDipen Patel EXPORT_SYMBOL_GPL(hte_request_ts_ns);
67931ab09b4SDipen Patel 
68031ab09b4SDipen Patel /**
68131ab09b4SDipen Patel  * devm_hte_request_ts_ns() - Resource managed API to request and enable
68231ab09b4SDipen Patel  * hardware timestamp in nanoseconds.
68331ab09b4SDipen Patel  *
68431ab09b4SDipen Patel  * The entity is provider specific for example, GPIO lines, signals, buses
68531ab09b4SDipen Patel  * etc...The API allocates necessary resources and enables the timestamp. It
68631ab09b4SDipen Patel  * deallocates and disables automatically when the consumer exits.
68731ab09b4SDipen Patel  *
68831ab09b4SDipen Patel  * @dev: HTE consumer/client device.
68931ab09b4SDipen Patel  * @desc: Pre-allocated and initialized timestamp descriptor.
69031ab09b4SDipen Patel  * @cb: Callback to push the timestamp data to consumer.
69131ab09b4SDipen Patel  * @tcb: Optional callback. If its provided, subsystem initializes
69231ab09b4SDipen Patel  * workqueue. It is called when cb returns HTE_RUN_SECOND_CB.
69331ab09b4SDipen Patel  * @data: Client data, used during cb and tcb callbacks.
69431ab09b4SDipen Patel  *
69531ab09b4SDipen Patel  * Context: Holds mutex lock.
69631ab09b4SDipen Patel  * Returns: Returns 0 on success or negative error code on failure.
69731ab09b4SDipen Patel  */
69831ab09b4SDipen Patel int devm_hte_request_ts_ns(struct device *dev, struct hte_ts_desc *desc,
69931ab09b4SDipen Patel 			   hte_ts_cb_t cb, hte_ts_sec_cb_t tcb,
70031ab09b4SDipen Patel 			   void *data)
70131ab09b4SDipen Patel {
70231ab09b4SDipen Patel 	int err;
70331ab09b4SDipen Patel 
70431ab09b4SDipen Patel 	if (!dev)
70531ab09b4SDipen Patel 		return -EINVAL;
70631ab09b4SDipen Patel 
70731ab09b4SDipen Patel 	err = hte_request_ts_ns(desc, cb, tcb, data);
70831ab09b4SDipen Patel 	if (err)
70931ab09b4SDipen Patel 		return err;
71031ab09b4SDipen Patel 
71131ab09b4SDipen Patel 	err = devm_add_action_or_reset(dev, __devm_hte_release_ts, desc);
71231ab09b4SDipen Patel 	if (err)
71331ab09b4SDipen Patel 		return err;
71431ab09b4SDipen Patel 
71531ab09b4SDipen Patel 	return 0;
71631ab09b4SDipen Patel }
71731ab09b4SDipen Patel EXPORT_SYMBOL_GPL(devm_hte_request_ts_ns);
71831ab09b4SDipen Patel 
71931ab09b4SDipen Patel /**
72031ab09b4SDipen Patel  * hte_init_line_attr() - Initialize line attributes.
72131ab09b4SDipen Patel  *
72231ab09b4SDipen Patel  * Zeroes out line attributes and initializes with provided arguments.
72331ab09b4SDipen Patel  * The function needs to be called before calling any consumer facing
72431ab09b4SDipen Patel  * functions.
72531ab09b4SDipen Patel  *
72631ab09b4SDipen Patel  * @desc: Pre-allocated timestamp descriptor.
72731ab09b4SDipen Patel  * @line_id: line id.
72831ab09b4SDipen Patel  * @edge_flags: edge flags related to line_id.
72931ab09b4SDipen Patel  * @name: name of the line.
73031ab09b4SDipen Patel  * @data: line data related to line_id.
73131ab09b4SDipen Patel  *
73231ab09b4SDipen Patel  * Context: Any.
73331ab09b4SDipen Patel  * Returns: 0 on success or negative error code for the failure.
73431ab09b4SDipen Patel  */
73531ab09b4SDipen Patel int hte_init_line_attr(struct hte_ts_desc *desc, u32 line_id,
73631ab09b4SDipen Patel 		       unsigned long edge_flags, const char *name, void *data)
73731ab09b4SDipen Patel {
73831ab09b4SDipen Patel 	if (!desc)
73931ab09b4SDipen Patel 		return -EINVAL;
74031ab09b4SDipen Patel 
74131ab09b4SDipen Patel 	memset(&desc->attr, 0, sizeof(desc->attr));
74231ab09b4SDipen Patel 
74331ab09b4SDipen Patel 	desc->attr.edge_flags = edge_flags;
74431ab09b4SDipen Patel 	desc->attr.line_id = line_id;
74531ab09b4SDipen Patel 	desc->attr.line_data = data;
74631ab09b4SDipen Patel 	if (name) {
74731ab09b4SDipen Patel 		name =  kstrdup_const(name, GFP_KERNEL);
74831ab09b4SDipen Patel 		if (!name)
74931ab09b4SDipen Patel 			return -ENOMEM;
75031ab09b4SDipen Patel 	}
75131ab09b4SDipen Patel 
75231ab09b4SDipen Patel 	desc->attr.name = name;
75331ab09b4SDipen Patel 
75431ab09b4SDipen Patel 	return 0;
75531ab09b4SDipen Patel }
75631ab09b4SDipen Patel EXPORT_SYMBOL_GPL(hte_init_line_attr);
75731ab09b4SDipen Patel 
75831ab09b4SDipen Patel /**
75931ab09b4SDipen Patel  * hte_get_clk_src_info() - Get the clock source information for a ts
76031ab09b4SDipen Patel  * descriptor.
76131ab09b4SDipen Patel  *
76231ab09b4SDipen Patel  * @desc: ts descriptor, same as returned from request API.
76331ab09b4SDipen Patel  * @ci: The API fills this structure with the clock information data.
76431ab09b4SDipen Patel  *
76531ab09b4SDipen Patel  * Context: Any context.
76631ab09b4SDipen Patel  * Returns: 0 on success else negative error code on failure.
76731ab09b4SDipen Patel  */
76831ab09b4SDipen Patel int hte_get_clk_src_info(const struct hte_ts_desc *desc,
76931ab09b4SDipen Patel 			 struct hte_clk_info *ci)
77031ab09b4SDipen Patel {
77131ab09b4SDipen Patel 	struct hte_chip *chip;
77231ab09b4SDipen Patel 	struct hte_ts_info *ei;
77331ab09b4SDipen Patel 
77431ab09b4SDipen Patel 	if (!desc || !desc->hte_data || !ci) {
77531ab09b4SDipen Patel 		pr_debug("%s:%d\n", __func__, __LINE__);
77631ab09b4SDipen Patel 		return -EINVAL;
77731ab09b4SDipen Patel 	}
77831ab09b4SDipen Patel 
77931ab09b4SDipen Patel 	ei = desc->hte_data;
78031ab09b4SDipen Patel 	if (!ei->gdev || !ei->gdev->chip)
78131ab09b4SDipen Patel 		return -EINVAL;
78231ab09b4SDipen Patel 
78331ab09b4SDipen Patel 	chip = ei->gdev->chip;
78431ab09b4SDipen Patel 	if (!chip->ops->get_clk_src_info)
78531ab09b4SDipen Patel 		return -EOPNOTSUPP;
78631ab09b4SDipen Patel 
78731ab09b4SDipen Patel 	return chip->ops->get_clk_src_info(chip, ci);
78831ab09b4SDipen Patel }
78931ab09b4SDipen Patel EXPORT_SYMBOL_GPL(hte_get_clk_src_info);
79031ab09b4SDipen Patel 
79131ab09b4SDipen Patel /**
79231ab09b4SDipen Patel  * hte_push_ts_ns() - Push timestamp data in nanoseconds.
79331ab09b4SDipen Patel  *
79431ab09b4SDipen Patel  * It is used by the provider to push timestamp data.
79531ab09b4SDipen Patel  *
79631ab09b4SDipen Patel  * @chip: The HTE chip, used during the registration.
79731ab09b4SDipen Patel  * @xlated_id: entity id understood by both subsystem and provider, this is
79831ab09b4SDipen Patel  * obtained from xlate callback during request API.
79931ab09b4SDipen Patel  * @data: timestamp data.
80031ab09b4SDipen Patel  *
80131ab09b4SDipen Patel  * Returns: 0 on success or a negative error code on failure.
80231ab09b4SDipen Patel  */
80331ab09b4SDipen Patel int hte_push_ts_ns(const struct hte_chip *chip, u32 xlated_id,
80431ab09b4SDipen Patel 		   struct hte_ts_data *data)
80531ab09b4SDipen Patel {
80631ab09b4SDipen Patel 	enum hte_return ret;
80731ab09b4SDipen Patel 	int st = 0;
80831ab09b4SDipen Patel 	struct hte_ts_info *ei;
80931ab09b4SDipen Patel 	unsigned long flag;
81031ab09b4SDipen Patel 
81131ab09b4SDipen Patel 	if (!chip || !data || !chip->gdev)
81231ab09b4SDipen Patel 		return -EINVAL;
81331ab09b4SDipen Patel 
814e30b64a3SDan Carpenter 	if (xlated_id >= chip->nlines)
81531ab09b4SDipen Patel 		return -EINVAL;
81631ab09b4SDipen Patel 
81731ab09b4SDipen Patel 	ei = &chip->gdev->ei[xlated_id];
81831ab09b4SDipen Patel 
81931ab09b4SDipen Patel 	spin_lock_irqsave(&ei->slock, flag);
82031ab09b4SDipen Patel 
82131ab09b4SDipen Patel 	/* timestamp sequence counter */
82231ab09b4SDipen Patel 	data->seq = ei->seq++;
82331ab09b4SDipen Patel 
82431ab09b4SDipen Patel 	if (!test_bit(HTE_TS_REGISTERED, &ei->flags) ||
82531ab09b4SDipen Patel 	    test_bit(HTE_TS_DISABLE, &ei->flags)) {
82631ab09b4SDipen Patel 		dev_dbg(chip->dev, "Unknown timestamp push\n");
82731ab09b4SDipen Patel 		atomic_inc(&ei->dropped_ts);
82831ab09b4SDipen Patel 		st = -EINVAL;
82931ab09b4SDipen Patel 		goto unlock;
83031ab09b4SDipen Patel 	}
83131ab09b4SDipen Patel 
83231ab09b4SDipen Patel 	ret = ei->cb(data, ei->cl_data);
83331ab09b4SDipen Patel 	if (ret == HTE_RUN_SECOND_CB && ei->tcb) {
83431ab09b4SDipen Patel 		queue_work(system_unbound_wq, &ei->cb_work);
83531ab09b4SDipen Patel 		set_bit(HTE_TS_QUEUE_WK, &ei->flags);
83631ab09b4SDipen Patel 	}
83731ab09b4SDipen Patel 
83831ab09b4SDipen Patel unlock:
83931ab09b4SDipen Patel 	spin_unlock_irqrestore(&ei->slock, flag);
84031ab09b4SDipen Patel 
84131ab09b4SDipen Patel 	return st;
84231ab09b4SDipen Patel }
84331ab09b4SDipen Patel EXPORT_SYMBOL_GPL(hte_push_ts_ns);
84431ab09b4SDipen Patel 
84531ab09b4SDipen Patel static int hte_register_chip(struct hte_chip *chip)
84631ab09b4SDipen Patel {
84731ab09b4SDipen Patel 	struct hte_device *gdev;
84831ab09b4SDipen Patel 	u32 i;
84931ab09b4SDipen Patel 
85031ab09b4SDipen Patel 	if (!chip || !chip->dev || !chip->dev->of_node)
85131ab09b4SDipen Patel 		return -EINVAL;
85231ab09b4SDipen Patel 
85331ab09b4SDipen Patel 	if (!chip->ops || !chip->ops->request || !chip->ops->release) {
85431ab09b4SDipen Patel 		dev_err(chip->dev, "Driver needs to provide ops\n");
85531ab09b4SDipen Patel 		return -EINVAL;
85631ab09b4SDipen Patel 	}
85731ab09b4SDipen Patel 
85831ab09b4SDipen Patel 	gdev = kzalloc(struct_size(gdev, ei, chip->nlines), GFP_KERNEL);
85931ab09b4SDipen Patel 	if (!gdev)
86031ab09b4SDipen Patel 		return -ENOMEM;
86131ab09b4SDipen Patel 
86231ab09b4SDipen Patel 	gdev->chip = chip;
86331ab09b4SDipen Patel 	chip->gdev = gdev;
86431ab09b4SDipen Patel 	gdev->nlines = chip->nlines;
86531ab09b4SDipen Patel 	gdev->sdev = chip->dev;
86631ab09b4SDipen Patel 
86731ab09b4SDipen Patel 	for (i = 0; i < chip->nlines; i++) {
86831ab09b4SDipen Patel 		gdev->ei[i].gdev = gdev;
86931ab09b4SDipen Patel 		mutex_init(&gdev->ei[i].req_mlock);
87031ab09b4SDipen Patel 		spin_lock_init(&gdev->ei[i].slock);
87131ab09b4SDipen Patel 	}
87231ab09b4SDipen Patel 
87331ab09b4SDipen Patel 	if (chip->dev->driver)
87431ab09b4SDipen Patel 		gdev->owner = chip->dev->driver->owner;
87531ab09b4SDipen Patel 	else
87631ab09b4SDipen Patel 		gdev->owner = THIS_MODULE;
87731ab09b4SDipen Patel 
87831ab09b4SDipen Patel 	of_node_get(chip->dev->of_node);
87931ab09b4SDipen Patel 
88031ab09b4SDipen Patel 	INIT_LIST_HEAD(&gdev->list);
88131ab09b4SDipen Patel 
88231ab09b4SDipen Patel 	spin_lock(&hte_lock);
88331ab09b4SDipen Patel 	list_add_tail(&gdev->list, &hte_devices);
88431ab09b4SDipen Patel 	spin_unlock(&hte_lock);
88531ab09b4SDipen Patel 
88631ab09b4SDipen Patel 	hte_chip_dbgfs_init(gdev);
88731ab09b4SDipen Patel 
88831ab09b4SDipen Patel 	dev_dbg(chip->dev, "Added hte chip\n");
88931ab09b4SDipen Patel 
89031ab09b4SDipen Patel 	return 0;
89131ab09b4SDipen Patel }
89231ab09b4SDipen Patel 
89331ab09b4SDipen Patel static int hte_unregister_chip(struct hte_chip *chip)
89431ab09b4SDipen Patel {
89531ab09b4SDipen Patel 	struct hte_device *gdev;
89631ab09b4SDipen Patel 
89731ab09b4SDipen Patel 	if (!chip)
89831ab09b4SDipen Patel 		return -EINVAL;
89931ab09b4SDipen Patel 
90031ab09b4SDipen Patel 	gdev = chip->gdev;
90131ab09b4SDipen Patel 
90231ab09b4SDipen Patel 	spin_lock(&hte_lock);
90331ab09b4SDipen Patel 	list_del(&gdev->list);
90431ab09b4SDipen Patel 	spin_unlock(&hte_lock);
90531ab09b4SDipen Patel 
90631ab09b4SDipen Patel 	gdev->chip = NULL;
90731ab09b4SDipen Patel 
90831ab09b4SDipen Patel 	of_node_put(chip->dev->of_node);
90931ab09b4SDipen Patel 	debugfs_remove_recursive(gdev->dbg_root);
91031ab09b4SDipen Patel 	kfree(gdev);
91131ab09b4SDipen Patel 
91231ab09b4SDipen Patel 	dev_dbg(chip->dev, "Removed hte chip\n");
91331ab09b4SDipen Patel 
91431ab09b4SDipen Patel 	return 0;
91531ab09b4SDipen Patel }
91631ab09b4SDipen Patel 
91731ab09b4SDipen Patel static void _hte_devm_unregister_chip(void *chip)
91831ab09b4SDipen Patel {
91931ab09b4SDipen Patel 	hte_unregister_chip(chip);
92031ab09b4SDipen Patel }
92131ab09b4SDipen Patel 
92231ab09b4SDipen Patel /**
92331ab09b4SDipen Patel  * devm_hte_register_chip() - Resource managed API to register HTE chip.
92431ab09b4SDipen Patel  *
92531ab09b4SDipen Patel  * It is used by the provider to register itself with the HTE subsystem.
92631ab09b4SDipen Patel  * The unregistration is done automatically when the provider exits.
92731ab09b4SDipen Patel  *
92831ab09b4SDipen Patel  * @chip: the HTE chip to add to subsystem.
92931ab09b4SDipen Patel  *
93031ab09b4SDipen Patel  * Returns: 0 on success or a negative error code on failure.
93131ab09b4SDipen Patel  */
93231ab09b4SDipen Patel int devm_hte_register_chip(struct hte_chip *chip)
93331ab09b4SDipen Patel {
93431ab09b4SDipen Patel 	int err;
93531ab09b4SDipen Patel 
93631ab09b4SDipen Patel 	err = hte_register_chip(chip);
93731ab09b4SDipen Patel 	if (err)
93831ab09b4SDipen Patel 		return err;
93931ab09b4SDipen Patel 
94031ab09b4SDipen Patel 	err = devm_add_action_or_reset(chip->dev, _hte_devm_unregister_chip,
94131ab09b4SDipen Patel 				       chip);
94231ab09b4SDipen Patel 	if (err)
94331ab09b4SDipen Patel 		return err;
94431ab09b4SDipen Patel 
94531ab09b4SDipen Patel 	return 0;
94631ab09b4SDipen Patel }
94731ab09b4SDipen Patel EXPORT_SYMBOL_GPL(devm_hte_register_chip);
948