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