* [PATCH v21 1/8] coresight: core: refactor ctcu_get_active_port and make it generic
2026-11-02 9:46 [PATCH v21 0/8] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
@ 2026-11-02 9:46 ` Jie Gan
2026-11-02 9:46 ` [PATCH v21 2/8] coresight: tmc: add create/clean functions for etr_buf_list Jie Gan
` (7 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Jie Gan @ 2026-11-02 9:46 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Alexander Shishkin, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Tingwei Zhang, Jie Gan, Bjorn Andersson,
Konrad Dybcio, Yuanfang Zhang, Mao Jinlong, Abel Vesa
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm, devicetree
Remove ctcu_get_active_port from CTCU module and add it to the core
framework.
The port number is crucial for the CTCU device to identify which ETR
it serves. With the port number we can correctly get required parameters
of the CTCU device in TMC module.
Reviewed-by: Mike Leach <mike.leach@arm.com>
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 27 +++++++++++++++++++++++
drivers/hwtracing/coresight/coresight-ctcu-core.c | 21 ++----------------
drivers/hwtracing/coresight/coresight-priv.h | 2 ++
3 files changed, 31 insertions(+), 19 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index d2f9c1fb77d9..32e8b3e51519 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -773,6 +773,33 @@ struct coresight_device *coresight_get_sink(struct coresight_path *path)
}
EXPORT_SYMBOL_GPL(coresight_get_sink);
+/**
+ * coresight_get_in_port: Find the input port number at @remote where the @csdev
+ * device is connected to.
+ *
+ * @csdev: csdev of the device.
+ * @remote: csdev of the remote device which is connected to @csdev.
+ *
+ * Return: port number upon success or -EINVAL for fail.
+ */
+int coresight_get_in_port(struct coresight_device *csdev,
+ struct coresight_device *remote)
+{
+ struct coresight_platform_data *pdata = remote->pdata;
+ int i;
+
+ for (i = 0; i < pdata->nr_inconns; ++i) {
+ if (!pdata->in_conns[i])
+ continue;
+
+ if (pdata->in_conns[i]->src_dev == csdev)
+ return pdata->in_conns[i]->dest_port;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(coresight_get_in_port);
+
u32 coresight_get_sink_id(struct coresight_device *csdev)
{
if (!csdev->ea)
diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c
index 9043cad42f01..739105acb0a1 100644
--- a/drivers/hwtracing/coresight/coresight-ctcu-core.c
+++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c
@@ -116,23 +116,6 @@ static int __ctcu_set_etr_traceid(struct coresight_device *csdev, u8 traceid, in
return 0;
}
-/*
- * Searching the sink device from helper's view in case there are multiple helper devices
- * connected to the sink device.
- */
-static int ctcu_get_active_port(struct coresight_device *sink, struct coresight_device *helper)
-{
- struct coresight_platform_data *pdata = helper->pdata;
- int i;
-
- for (i = 0; i < pdata->nr_inconns; ++i) {
- if (pdata->in_conns[i]->src_dev == sink)
- return pdata->in_conns[i]->dest_port;
- }
-
- return -EINVAL;
-}
-
static int ctcu_set_etr_traceid(struct coresight_device *csdev, struct coresight_path *path,
bool enable)
{
@@ -145,8 +128,8 @@ static int ctcu_set_etr_traceid(struct coresight_device *csdev, struct coresight
return -EINVAL;
}
- port_num = ctcu_get_active_port(sink, csdev);
- if (port_num < 0)
+ port_num = coresight_get_in_port(sink, csdev);
+ if (port_num < 0 || port_num >= ETR_MAX_NUM)
return -EINVAL;
dev_dbg(&csdev->dev, "traceid is %d\n", traceid);
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index dddac946659f..854c0a3cb080 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -155,6 +155,8 @@ void coresight_remove_links(struct coresight_device *orig,
u32 coresight_get_sink_id(struct coresight_device *csdev);
int coresight_path_assign_trace_id(struct coresight_path *path,
enum cs_mode mode);
+int coresight_get_in_port(struct coresight_device *csdev,
+ struct coresight_device *remote);
#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM3X)
int etm_readl_cp14(u32 off, unsigned int *val);
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v21 2/8] coresight: tmc: add create/clean functions for etr_buf_list
2026-11-02 9:46 [PATCH v21 0/8] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
2026-11-02 9:46 ` [PATCH v21 1/8] coresight: core: refactor ctcu_get_active_port and make it generic Jie Gan
@ 2026-11-02 9:46 ` Jie Gan
2026-11-02 9:46 ` [PATCH v21 3/8] coresight: tmc: add a function to switch the ETR to a free etr_buf Jie Gan
` (6 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Jie Gan @ 2026-11-02 9:46 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Alexander Shishkin, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Tingwei Zhang, Jie Gan, Bjorn Andersson,
Konrad Dybcio, Yuanfang Zhang, Mao Jinlong, Abel Vesa
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm, devicetree
Introduce functions for creating and inserting or removing the
etr_buf_node to/from the etr_buf_list.
The byte-cntr functionality requires two etr_buf to receive trace data.
The active etr_buf collects the trace data from source device, while the
byte-cntr reading function accesses the deactivated etr_buf after is
has been filled and synced, transferring data to the userspace.
Reviewed-by: Mike Leach <mike.leach@arm.com>
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-tmc-core.c | 1 +
drivers/hwtracing/coresight/coresight-tmc-etr.c | 168 ++++++++++++++++++++++-
drivers/hwtracing/coresight/coresight-tmc.h | 18 +++
3 files changed, 185 insertions(+), 2 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
index bc5a133ada3e..bc7dd676da47 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-core.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
@@ -835,6 +835,7 @@ static int __tmc_probe(struct device *dev, struct resource *res)
idr_init(&drvdata->idr);
mutex_init(&drvdata->idr_mutex);
dev_list = "tmc_etr";
+ INIT_LIST_HEAD(&drvdata->etr_buf_list);
break;
case TMC_CONFIG_TYPE_ETF:
desc.groups = coresight_etf_groups;
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 361a433e6f0c..eee98a9269df 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1268,7 +1268,9 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
}
- if (drvdata->reading || coresight_get_mode(csdev) == CS_MODE_PERF) {
+ /* The sysfs_buf is owned by the etr_buf_list, don't replace it */
+ if (drvdata->reading || coresight_get_mode(csdev) == CS_MODE_PERF ||
+ !list_empty(&drvdata->etr_buf_list)) {
ret = -EBUSY;
goto out;
}
@@ -1918,6 +1920,167 @@ const struct coresight_ops tmc_etr_cs_ops = {
.panic_ops = &tmc_etr_sync_ops,
};
+/**
+ * tmc_clean_etr_buf_list - clean the etr_buf_list.
+ * @drvdata: driver data of the TMC device.
+ *
+ * Remove all nodes from @drvdata->etr_buf_list and free their buffers.
+ * If a node holds the live sysfs_buf and the sink is enabled in sysfs mode, the
+ * node is removed but the buffer is not freed; ownership stays with
+ * drvdata->sysfs_buf.
+ *
+ * Locking: must not be called with @drvdata->spinlock held. The spinlock is
+ * taken internally to serialise the @drvdata->sysfs_buf accesses and the
+ * removal of the nodes against the ETR sink enable/disable paths and the
+ * generic sysfs read. There must be only one owner of the list at a time.
+ * Must be called from process context: buffers are freed with the lock
+ * released.
+ */
+void tmc_clean_etr_buf_list(struct tmc_drvdata *drvdata)
+{
+ struct etr_buf_node *nd, *next;
+ unsigned long flags;
+
+ list_for_each_entry_safe(nd, next, &drvdata->etr_buf_list, link) {
+ raw_spin_lock_irqsave(&drvdata->spinlock, flags);
+ if (nd->sysfs_buf == drvdata->sysfs_buf) {
+ if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS)
+ /*
+ * The sink is still enabled in sysfs mode. Keep
+ * the live buffer owned by drvdata->sysfs_buf
+ * and only drop the list's reference to it.
+ */
+ nd->sysfs_buf = NULL;
+ else
+ /* Free the buffer below through nd->sysfs_buf */
+ drvdata->sysfs_buf = NULL;
+ }
+ list_del(&nd->link);
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+ /* Free the buffer (NULL is ignored) and the node out of the lock */
+ tmc_etr_free_sysfs_buf(nd->sysfs_buf);
+ kfree(nd);
+ }
+}
+EXPORT_SYMBOL_GPL(tmc_clean_etr_buf_list);
+
+/**
+ * tmc_create_etr_buf_list - create a list to manage the etr_buf_node.
+ * @drvdata: driver data of the TMC device.
+ * @num_nodes: number of nodes want to create with the list.
+ *
+ * Locking: must not be called with @drvdata->spinlock held. The spinlock is
+ * taken internally to serialise the @drvdata->sysfs_buf accesses and every
+ * update of @drvdata->etr_buf_list against the ETR sink enable/disable paths
+ * and the generic sysfs read. There must be only one owner of the list at a
+ * time. Must be called from process context: buffers and nodes are allocated
+ * with the lock released.
+ *
+ * Return: 0 upon success, else the error number: -EBUSY if a generic sysfs
+ * read is in progress, -EINVAL if the sink is not enabled in sysfs mode or
+ * the ETR is in the reserved buffer mode, or the error of the allocation.
+ */
+int tmc_create_etr_buf_list(struct tmc_drvdata *drvdata, int num_nodes)
+{
+ struct etr_buf_node *new_node;
+ struct etr_buf *sysfs_buf;
+ unsigned long flags;
+ int i = 0, ret = 0;
+
+ /* We don't need a list if there is only one node */
+ if (num_nodes < 2)
+ return -EINVAL;
+
+ /*
+ * All the etr_buf of the reserved mode are mapped to the same reserved
+ * region, so they can't be used as separate buffers of the list.
+ */
+ if (drvdata->etr_mode == ETR_MODE_RESRV)
+ return -EINVAL;
+
+ /*
+ * We expect that sysfs_buf in drvdata has already been allocated.
+ * Wrap the live sysfs_buf into the first node so the captured trace
+ * data is preserved. Take the sysfs_buf and add the node under the
+ * lock, so that the ETR sink enable path sees a non-empty list and
+ * doesn't replace the buffer.
+ */
+ new_node = kzalloc_obj(*new_node, GFP_KERNEL);
+ if (!new_node)
+ return -ENOMEM;
+
+ raw_spin_lock_irqsave(&drvdata->spinlock, flags);
+ /* The sysfs_buf is in use by a sysfs read session */
+ if (drvdata->reading) {
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+ kfree(new_node);
+ return -EBUSY;
+ }
+
+ /*
+ * The sink enable path reuses or frees the sysfs_buf of a sink which
+ * isn't enabled, so only wrap it while the sink is enabled in sysfs mode.
+ */
+ if (coresight_get_mode(drvdata->csdev) != CS_MODE_SYSFS) {
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+ kfree(new_node);
+ return -EINVAL;
+ }
+
+ if (drvdata->sysfs_buf) {
+ new_node->sysfs_buf = drvdata->sysfs_buf;
+ new_node->is_free = false;
+ list_add(&new_node->link, &drvdata->etr_buf_list);
+ new_node = NULL;
+ i++;
+ }
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+ /* Free the node if there is no live sysfs_buf to wrap */
+ kfree(new_node);
+
+ while (i < num_nodes) {
+ new_node = kzalloc_obj(*new_node, GFP_KERNEL);
+ if (!new_node) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ /* Allocate the buffer with the lock released */
+ sysfs_buf = tmc_alloc_etr_buf(drvdata, drvdata->size, 0, cpu_to_node(0), NULL);
+ if (IS_ERR(sysfs_buf)) {
+ kfree(new_node);
+ ret = PTR_ERR(sysfs_buf);
+ break;
+ }
+
+ new_node->sysfs_buf = sysfs_buf;
+ /*
+ * The drvdata->sysfs_buf write and the list update need the
+ * spinlock, to serialise against the ETR sink enable/disable
+ * paths.
+ */
+ raw_spin_lock_irqsave(&drvdata->spinlock, flags);
+ /* We don't have an available sysfs_buf in drvdata, set one up */
+ if (!drvdata->sysfs_buf) {
+ drvdata->sysfs_buf = sysfs_buf;
+ new_node->is_free = false;
+ } else {
+ new_node->is_free = true;
+ }
+ list_add_tail(&new_node->link, &drvdata->etr_buf_list);
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+ i++;
+ }
+
+ /* Clean the list if there is an error */
+ if (ret)
+ tmc_clean_etr_buf_list(drvdata);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tmc_create_etr_buf_list);
+
int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
{
int ret = 0;
@@ -1928,7 +2091,8 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
return -EINVAL;
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
- if (drvdata->reading) {
+ /* The sysfs_buf is owned by the etr_buf_list */
+ if (drvdata->reading || !list_empty(&drvdata->etr_buf_list)) {
ret = -EBUSY;
goto out;
}
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 319a354ede9f..7c604b9f92e5 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -208,6 +208,20 @@ struct tmc_resrv_buf {
s64 len;
};
+/**
+ * struct etr_buf_node - node of the etr_buf_list
+ * @sysfs_buf: Allocated sysfs_buf.
+ * @is_free: Indicates whether the buffer is free to choose.
+ * @pos: Offset to the start of the buffer.
+ * @link: list_head of the node.
+ */
+struct etr_buf_node {
+ struct etr_buf *sysfs_buf;
+ bool is_free;
+ loff_t pos;
+ struct list_head link;
+};
+
/**
* struct tmc_drvdata - specifics associated to an TMC component
* @atclk: optional clock for the core parts of the TMC.
@@ -245,6 +259,7 @@ struct tmc_resrv_buf {
* (after crash) by default.
* @crash_mdata: Reserved memory for storing tmc crash metadata.
* Used by ETR/ETF.
+ * @etr_buf_list: List that is used to manage allocated etr_buf.
*/
struct tmc_drvdata {
struct clk *atclk;
@@ -275,6 +290,7 @@ struct tmc_drvdata {
struct etr_buf *perf_buf;
struct tmc_resrv_buf resrv_buf;
struct tmc_resrv_buf crash_mdata;
+ struct list_head etr_buf_list;
};
struct etr_buf_operations {
@@ -447,5 +463,7 @@ struct etr_buf *tmc_etr_get_buffer(struct coresight_device *csdev,
enum cs_mode mode,
struct coresight_path *path);
extern const struct attribute_group coresight_etr_group;
+void tmc_clean_etr_buf_list(struct tmc_drvdata *drvdata);
+int tmc_create_etr_buf_list(struct tmc_drvdata *drvdata, int num_nodes);
#endif
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v21 3/8] coresight: tmc: add a function to switch the ETR to a free etr_buf
2026-11-02 9:46 [PATCH v21 0/8] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
2026-11-02 9:46 ` [PATCH v21 1/8] coresight: core: refactor ctcu_get_active_port and make it generic Jie Gan
2026-11-02 9:46 ` [PATCH v21 2/8] coresight: tmc: add create/clean functions for etr_buf_list Jie Gan
@ 2026-11-02 9:46 ` Jie Gan
2026-11-02 9:46 ` [PATCH v21 4/8] coresight: tmc: introduce tmc_sysfs_ops to wrap sysfs read operations Jie Gan
` (5 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Jie Gan @ 2026-11-02 9:46 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Alexander Shishkin, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Tingwei Zhang, Jie Gan, Bjorn Andersson,
Konrad Dybcio, Yuanfang Zhang, Mao Jinlong, Abel Vesa
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm, devicetree
Add tmc_etr_switch_buf_node() to switch the ETR to a free etr_buf of the
etr_buf_list without disabling the sink. It returns the node of the
switched out etr_buf, so its synced data can be read while the ETR keeps
tracing into the new one.
Stopping the ETR, swapping the etr_buf and restarting the ETR have to be
done in one critical section. Otherwise tmc_disable_etr_sink() can run
in between and leave drvdata->etr_buf out of sync with the hardware.
Provide the sequence as a TMC helper, so callers don't need to know
about the locking of the ETR state.
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-tmc-etr.c | 65 +++++++++++++++++++++++++
drivers/hwtracing/coresight/coresight-tmc.h | 4 ++
2 files changed, 69 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index eee98a9269df..2de31d73a4b4 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1224,6 +1224,71 @@ static void __tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
}
+static void tmc_etr_reset_sysfs_buf(struct tmc_drvdata *drvdata)
+{
+ u32 sts;
+
+ CS_UNLOCK(drvdata->base);
+ tmc_write_rrp(drvdata, drvdata->sysfs_buf->hwaddr);
+ tmc_write_rwp(drvdata, drvdata->sysfs_buf->hwaddr);
+ sts = readl_relaxed(drvdata->base + TMC_STS) & ~TMC_STS_FULL;
+ writel_relaxed(sts, drvdata->base + TMC_STS);
+ CS_LOCK(drvdata->base);
+}
+
+/**
+ * tmc_etr_switch_buf_node - switch the ETR to a free buffer of the etr_buf_list.
+ * @drvdata: drvdata of the TMC device.
+ *
+ * Stop the ETR, hand over the current buffer for reading, switch to a free
+ * buffer and restart the ETR. All of this is done with @drvdata->spinlock held,
+ * so it can't race with the sink disable path.
+ *
+ * Return: the node of the switched out buffer, or NULL if the sink isn't in
+ * sysfs mode or there is no free buffer. The ETR is left untouched in both
+ * cases.
+ */
+struct etr_buf_node *tmc_etr_switch_buf_node(struct tmc_drvdata *drvdata)
+{
+ struct etr_buf_node *nd, *curr_node = NULL, *picked_node = NULL;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&drvdata->spinlock, flags);
+ if (coresight_get_mode(drvdata->csdev) != CS_MODE_SYSFS)
+ goto out;
+
+ list_for_each_entry(nd, &drvdata->etr_buf_list, link) {
+ if (nd->sysfs_buf == drvdata->sysfs_buf)
+ curr_node = nd;
+ else if (!picked_node && nd->is_free)
+ picked_node = nd;
+ }
+
+ if (!curr_node || !picked_node) {
+ curr_node = NULL;
+ goto out;
+ }
+
+ __tmc_etr_disable_hw(drvdata);
+
+ /* The switched out buffer is free again once it has been read */
+ curr_node->is_free = true;
+ curr_node->pos = 0;
+ curr_node->reading = true;
+ picked_node->is_free = false;
+ drvdata->sysfs_buf = picked_node->sysfs_buf;
+ drvdata->etr_buf = picked_node->sysfs_buf;
+
+ tmc_etr_reset_sysfs_buf(drvdata);
+ if (__tmc_etr_enable_hw(drvdata))
+ dev_err(&drvdata->csdev->dev, "Failed to restart the ETR\n");
+out:
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+ return curr_node;
+}
+EXPORT_SYMBOL_GPL(tmc_etr_switch_buf_node);
+
void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
{
__tmc_etr_disable_hw(drvdata);
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 7c604b9f92e5..30b15c38b080 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -212,12 +212,15 @@ struct tmc_resrv_buf {
* struct etr_buf_node - node of the etr_buf_list
* @sysfs_buf: Allocated sysfs_buf.
* @is_free: Indicates whether the buffer is free to choose.
+ * @reading: Indicates the buffer attached to the node is being read by
+ * the user.
* @pos: Offset to the start of the buffer.
* @link: list_head of the node.
*/
struct etr_buf_node {
struct etr_buf *sysfs_buf;
bool is_free;
+ bool reading;
loff_t pos;
struct list_head link;
};
@@ -465,5 +468,6 @@ struct etr_buf *tmc_etr_get_buffer(struct coresight_device *csdev,
extern const struct attribute_group coresight_etr_group;
void tmc_clean_etr_buf_list(struct tmc_drvdata *drvdata);
int tmc_create_etr_buf_list(struct tmc_drvdata *drvdata, int num_nodes);
+struct etr_buf_node *tmc_etr_switch_buf_node(struct tmc_drvdata *drvdata);
#endif
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v21 4/8] coresight: tmc: introduce tmc_sysfs_ops to wrap sysfs read operations
2026-11-02 9:46 [PATCH v21 0/8] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
` (2 preceding siblings ...)
2026-11-02 9:46 ` [PATCH v21 3/8] coresight: tmc: add a function to switch the ETR to a free etr_buf Jie Gan
@ 2026-11-02 9:46 ` Jie Gan
2026-11-02 9:46 ` [PATCH v21 5/8] coresight: etr: add a new function to retrieve the CTCU device Jie Gan
` (4 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Jie Gan @ 2026-11-02 9:46 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Alexander Shishkin, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Tingwei Zhang, Jie Gan, Bjorn Andersson,
Konrad Dybcio, Yuanfang Zhang, Mao Jinlong, Abel Vesa
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm, devicetree
Introduce tmc_sysfs_ops as a wrapper, wrap sysfs read operations,
for reading trace data from the TMC buffer.
Reviewed-by: Mike Leach <mike.leach@arm.com>
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-tmc-core.c | 51 ++++++++++--------------
drivers/hwtracing/coresight/coresight-tmc.h | 15 +++++++
2 files changed, 37 insertions(+), 29 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
index bc7dd676da47..4b40b692be4d 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-core.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
@@ -228,17 +228,10 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata)
{
int ret = 0;
- switch (drvdata->config_type) {
- case TMC_CONFIG_TYPE_ETB:
- case TMC_CONFIG_TYPE_ETF:
- ret = tmc_read_prepare_etb(drvdata);
- break;
- case TMC_CONFIG_TYPE_ETR:
- ret = tmc_read_prepare_etr(drvdata);
- break;
- default:
+ if (drvdata->sysfs_ops)
+ ret = drvdata->sysfs_ops->read_prepare(drvdata);
+ else
ret = -EINVAL;
- }
if (!ret)
dev_dbg(&drvdata->csdev->dev, "TMC read start\n");
@@ -250,17 +243,10 @@ static int tmc_read_unprepare(struct tmc_drvdata *drvdata)
{
int ret = 0;
- switch (drvdata->config_type) {
- case TMC_CONFIG_TYPE_ETB:
- case TMC_CONFIG_TYPE_ETF:
- ret = tmc_read_unprepare_etb(drvdata);
- break;
- case TMC_CONFIG_TYPE_ETR:
- ret = tmc_read_unprepare_etr(drvdata);
- break;
- default:
+ if (drvdata->sysfs_ops)
+ ret = drvdata->sysfs_ops->read_unprepare(drvdata);
+ else
ret = -EINVAL;
- }
if (!ret)
dev_dbg(&drvdata->csdev->dev, "TMC read end\n");
@@ -287,15 +273,7 @@ static int tmc_open(struct inode *inode, struct file *file)
static ssize_t tmc_get_sysfs_trace(struct tmc_drvdata *drvdata, loff_t pos, size_t len,
char **bufpp)
{
- switch (drvdata->config_type) {
- case TMC_CONFIG_TYPE_ETB:
- case TMC_CONFIG_TYPE_ETF:
- return tmc_etb_get_sysfs_trace(drvdata, pos, len, bufpp);
- case TMC_CONFIG_TYPE_ETR:
- return tmc_etr_get_sysfs_trace(drvdata, pos, len, bufpp);
- }
-
- return -EINVAL;
+ return drvdata->sysfs_ops->get_trace_data(drvdata, pos, len, bufpp);
}
static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
@@ -764,6 +742,18 @@ static void register_crash_dev_interface(struct tmc_drvdata *drvdata,
"Valid crash tracedata found\n");
}
+static const struct tmc_sysfs_ops etb_sysfs_ops = {
+ .read_prepare = tmc_read_prepare_etb,
+ .read_unprepare = tmc_read_unprepare_etb,
+ .get_trace_data = tmc_etb_get_sysfs_trace,
+};
+
+static const struct tmc_sysfs_ops etr_sysfs_ops = {
+ .read_prepare = tmc_read_prepare_etr,
+ .read_unprepare = tmc_read_unprepare_etr,
+ .get_trace_data = tmc_etr_get_sysfs_trace,
+};
+
static int __tmc_probe(struct device *dev, struct resource *res)
{
int ret = 0;
@@ -823,6 +813,7 @@ static int __tmc_probe(struct device *dev, struct resource *res)
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
desc.ops = &tmc_etb_cs_ops;
dev_list = "tmc_etb";
+ drvdata->sysfs_ops = &etb_sysfs_ops;
break;
case TMC_CONFIG_TYPE_ETR:
desc.groups = coresight_etr_groups;
@@ -835,6 +826,7 @@ static int __tmc_probe(struct device *dev, struct resource *res)
idr_init(&drvdata->idr);
mutex_init(&drvdata->idr_mutex);
dev_list = "tmc_etr";
+ drvdata->sysfs_ops = &etr_sysfs_ops;
INIT_LIST_HEAD(&drvdata->etr_buf_list);
break;
case TMC_CONFIG_TYPE_ETF:
@@ -844,6 +836,7 @@ static int __tmc_probe(struct device *dev, struct resource *res)
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
desc.ops = &tmc_etf_cs_ops;
dev_list = "tmc_etf";
+ drvdata->sysfs_ops = &etb_sysfs_ops;
break;
default:
pr_err("%s: Unsupported TMC config\n", desc.name);
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 30b15c38b080..36c80918958f 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -263,6 +263,7 @@ struct etr_buf_node {
* @crash_mdata: Reserved memory for storing tmc crash metadata.
* Used by ETR/ETF.
* @etr_buf_list: List that is used to manage allocated etr_buf.
+ * @sysfs_ops: Read operations for the sysfs mode.
*/
struct tmc_drvdata {
struct clk *atclk;
@@ -294,6 +295,20 @@ struct tmc_drvdata {
struct tmc_resrv_buf resrv_buf;
struct tmc_resrv_buf crash_mdata;
struct list_head etr_buf_list;
+ const struct tmc_sysfs_ops *sysfs_ops;
+};
+
+/**
+ * struct tmc_sysfs_ops - read operations for TMC and its helper devices
+ * @read_prepare: prepare operation.
+ * @read_unprepare: unprepare operation.
+ * @get_trace_data: read operation.
+ */
+struct tmc_sysfs_ops {
+ int (*read_prepare)(struct tmc_drvdata *drvdata);
+ int (*read_unprepare)(struct tmc_drvdata *drvdata);
+ ssize_t (*get_trace_data)(struct tmc_drvdata *drvdata, loff_t pos,
+ size_t len, char **bufpp);
};
struct etr_buf_operations {
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v21 5/8] coresight: etr: add a new function to retrieve the CTCU device
2026-11-02 9:46 [PATCH v21 0/8] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
` (3 preceding siblings ...)
2026-11-02 9:46 ` [PATCH v21 4/8] coresight: tmc: introduce tmc_sysfs_ops to wrap sysfs read operations Jie Gan
@ 2026-11-02 9:46 ` Jie Gan
2026-11-02 9:46 ` [PATCH v21 6/8] dt-bindings: arm: add an interrupt property for Coresight CTCU Jie Gan
` (3 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Jie Gan @ 2026-11-02 9:46 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Alexander Shishkin, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Tingwei Zhang, Jie Gan, Bjorn Andersson,
Konrad Dybcio, Yuanfang Zhang, Mao Jinlong, Abel Vesa
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm, devicetree
Add tmc_etr_get_ctcu_device function to find the ptr of the
coresight_device of the CTCU device if the CTCU device is connected to
the TMC ETR device.
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-tmc-etr.c | 24 ++++++++++++++++++++++++
drivers/hwtracing/coresight/coresight-tmc.h | 1 +
2 files changed, 25 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 2de31d73a4b4..bf4c1d8f7b2e 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -865,6 +865,30 @@ tmc_etr_get_catu_device(struct tmc_drvdata *drvdata)
}
EXPORT_SYMBOL_GPL(tmc_etr_get_catu_device);
+/*
+ * TMC ETR could be connected to a CTCU device, which can provide ATID filter
+ * and byte-cntr service. This is represented by the output port of the TMC
+ * (ETR) connected to the input port of the CTCU.
+ *
+ * Returns : coresight_device ptr for the CTCU device if a CTCU is found.
+ * : NULL otherwise.
+ */
+struct coresight_device *
+tmc_etr_get_ctcu_device(struct tmc_drvdata *drvdata)
+{
+ struct coresight_device *etr = drvdata->csdev;
+ union coresight_dev_subtype ctcu_subtype = {
+ .helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_CTCU
+ };
+
+ if (!IS_ENABLED(CONFIG_CORESIGHT_CTCU))
+ return NULL;
+
+ return coresight_find_output_type(etr->pdata, CORESIGHT_DEV_TYPE_HELPER,
+ ctcu_subtype);
+}
+EXPORT_SYMBOL_GPL(tmc_etr_get_ctcu_device);
+
static const struct etr_buf_operations *etr_buf_ops[] = {
[ETR_MODE_FLAT] = &etr_flat_buf_ops,
[ETR_MODE_ETR_SG] = &etr_sg_buf_ops,
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 36c80918958f..2345bdbb0b9d 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -474,6 +474,7 @@ static inline uint32_t find_crash_tracedata_crc(struct tmc_drvdata *drvdata,
}
struct coresight_device *tmc_etr_get_catu_device(struct tmc_drvdata *drvdata);
+struct coresight_device *tmc_etr_get_ctcu_device(struct tmc_drvdata *drvdata);
void tmc_etr_set_catu_ops(const struct etr_buf_operations *catu);
void tmc_etr_remove_catu_ops(void);
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v21 6/8] dt-bindings: arm: add an interrupt property for Coresight CTCU
2026-11-02 9:46 [PATCH v21 0/8] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
` (4 preceding siblings ...)
2026-11-02 9:46 ` [PATCH v21 5/8] coresight: etr: add a new function to retrieve the CTCU device Jie Gan
@ 2026-11-02 9:46 ` Jie Gan
2026-11-02 9:46 ` [PATCH v21 7/8] coresight: ctcu: enable byte-cntr for TMC ETR devices Jie Gan
` (2 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Jie Gan @ 2026-11-02 9:46 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Alexander Shishkin, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Tingwei Zhang, Jie Gan, Bjorn Andersson,
Konrad Dybcio, Yuanfang Zhang, Mao Jinlong, Abel Vesa
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
devicetree, Krzysztof Kozlowski
Add an interrupt property to CTCU device. The interrupt will be triggered
when the data size in the ETR buffer exceeds the threshold of the
BYTECNTRVAL register. Programming a threshold in the BYTECNTRVAL register
of CTCU device will enable the interrupt.
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Reviewed-by: Mike Leach <mike.leach@arm.com>
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
Documentation/devicetree/bindings/arm/qcom,coresight-ctcu.yaml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/qcom,coresight-ctcu.yaml b/Documentation/devicetree/bindings/arm/qcom,coresight-ctcu.yaml
index 335a8e1a5a9e..decb404e2ab1 100644
--- a/Documentation/devicetree/bindings/arm/qcom,coresight-ctcu.yaml
+++ b/Documentation/devicetree/bindings/arm/qcom,coresight-ctcu.yaml
@@ -49,6 +49,11 @@ properties:
items:
- const: apb
+ interrupts:
+ items:
+ - description: Interrupt for the ETR device connected to in-port0.
+ - description: Interrupt for the ETR device connected to in-port1.
+
label:
description:
Description of a coresight device.
@@ -70,6 +75,8 @@ additionalProperties: false
examples:
- |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
ctcu@1001000 {
compatible = "qcom,sa8775p-ctcu";
reg = <0x1001000 0x1000>;
@@ -77,6 +84,9 @@ examples:
clocks = <&aoss_qmp>;
clock-names = "apb";
+ interrupts = <GIC_SPI 270 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 262 IRQ_TYPE_EDGE_RISING>;
+
in-ports {
#address-cells = <1>;
#size-cells = <0>;
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v21 7/8] coresight: ctcu: enable byte-cntr for TMC ETR devices
2026-11-02 9:46 [PATCH v21 0/8] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
` (5 preceding siblings ...)
2026-11-02 9:46 ` [PATCH v21 6/8] dt-bindings: arm: add an interrupt property for Coresight CTCU Jie Gan
@ 2026-11-02 9:46 ` Jie Gan
2026-11-02 9:46 ` [PATCH v21 8/8] arm64: dts: qcom: lemans: add interrupts to CTCU device Jie Gan
2026-11-10 1:28 ` [PATCH v21 0/8] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
8 siblings, 0 replies; 10+ messages in thread
From: Jie Gan @ 2026-11-02 9:46 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Alexander Shishkin, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Tingwei Zhang, Jie Gan, Bjorn Andersson,
Konrad Dybcio, Yuanfang Zhang, Mao Jinlong, Abel Vesa
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm, devicetree
The byte-cntr function provided by the CTCU device is used to transfer data
from the ETR buffer to the userspace. An interrupt is triggered if the data
size exceeds the threshold set in the BYTECNTRVAL register. The interrupt
handler counts the number of triggered interruptions and the read function
will read the data from the synced ETR buffer.
When the current etr_buf is nearly full or the timeout is triggered, the
ETR is switched to the other etr_buf of the etr_buf_list and the synced
etr_buf becomes available for reading, while the ETR keeps tracing. Once
the trace has been stopped, the last etr_buf is read by the generic sysfs
read of the TMC.
The byte-cntr read is exclusive with the generic sysfs read of the TMC. It
is used only when it is enabled for the ETR, otherwise the TMC falls back
to its original read.
Byte-cntr workflow:
start -> ctcu_enable(ctcu_byte_cntr_start) -> tmc_enable_etr_sink ->
tmc_read_prepare_etr(jump to tmc_read_prepare_byte_cntr) ->
tmc_etr_get_sysfs_trace(jump to tmc_byte_cntr_get_data) ->
tmc_disable_etr_sink -> ctcu_disable(ctcu_byte_cntr_stop) ->
tmc_read_unprepare_etr(jump to tmc_read_unprepare_byte_cntr) -> finish
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
.../ABI/testing/sysfs-bus-coresight-devices-ctcu | 9 +
drivers/hwtracing/coresight/Makefile | 2 +-
.../hwtracing/coresight/coresight-ctcu-byte-cntr.c | 400 +++++++++++++++++++++
drivers/hwtracing/coresight/coresight-ctcu-core.c | 163 ++++++++-
drivers/hwtracing/coresight/coresight-ctcu.h | 88 ++++-
drivers/hwtracing/coresight/coresight-tmc-core.c | 3 +-
drivers/hwtracing/coresight/coresight-tmc-etr.c | 87 ++++-
drivers/hwtracing/coresight/coresight-tmc.h | 5 +
8 files changed, 729 insertions(+), 28 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu b/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu
new file mode 100644
index 000000000000..08dbc8e6b2ec
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu
@@ -0,0 +1,9 @@
+What: /sys/bus/coresight/devices/<ctcu-name>/irq_enabled[0:1]
+Date: October 2026
+KernelVersion: 7.5
+Contact: Tingwei Zhang <tingwei.zhang@oss.qualcomm.com>; Jinlong Mao <jinlong.mao@oss.qualcomm.com>; Jie Gan <jie.gan@oss.qualcomm.com>
+Description:
+ (RW) Configure the flag to enable interrupt to count data during CTCU enablement.
+ An interrupt is generated when the data size exceeds the value set in the IRQ register.
+ 0 : disable
+ 1 : enable
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index ab16d06783a5..821a1b06b20c 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -55,5 +55,5 @@ coresight-cti-y := coresight-cti-core.o coresight-cti-platform.o \
obj-$(CONFIG_ULTRASOC_SMB) += ultrasoc-smb.o
obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
obj-$(CONFIG_CORESIGHT_CTCU) += coresight-ctcu.o
-coresight-ctcu-y := coresight-ctcu-core.o
+coresight-ctcu-y := coresight-ctcu-core.o coresight-ctcu-byte-cntr.o
obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) += coresight-kunit-tests.o
diff --git a/drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c b/drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c
new file mode 100644
index 000000000000..bff87e776a75
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/coresight.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/of_irq.h>
+#include <linux/uaccess.h>
+
+#include "coresight-ctcu.h"
+#include "coresight-priv.h"
+#include "coresight-tmc.h"
+
+/*
+ * The byte-cntr sysfs ops are shared by all the CTCU devices, so they are
+ * registered by the first CTCU with byte-cntr and removed with the last one.
+ */
+static DEFINE_MUTEX(byte_cntr_ops_lock);
+static int byte_cntr_ops_users;
+
+static void ctcu_byte_cntr_put_ops(void *data)
+{
+ guard(mutex)(&byte_cntr_ops_lock);
+ if (!--byte_cntr_ops_users)
+ tmc_etr_reset_byte_cntr_sysfs_ops();
+}
+
+static irqreturn_t byte_cntr_handler(int irq, void *data)
+{
+ struct ctcu_byte_cntr *byte_cntr_data = data;
+
+ atomic_inc(&byte_cntr_data->irq_cnt);
+ wake_up(&byte_cntr_data->wq);
+
+ return IRQ_HANDLED;
+}
+
+static void ctcu_cfg_byte_cntr_reg(struct ctcu_drvdata *drvdata, u32 val,
+ u32 offset)
+{
+ /* A one value for IRQCTRL register represents 8 bytes */
+ ctcu_program_register(drvdata, val / 8, offset);
+}
+
+static struct ctcu_byte_cntr *ctcu_get_byte_cntr(struct coresight_device *ctcu,
+ struct coresight_device *etr)
+{
+ struct ctcu_byte_cntr *byte_cntr_data;
+ struct ctcu_drvdata *drvdata;
+ int port;
+
+ /* The ETR may not be connected to a CTCU device */
+ if (!ctcu)
+ return NULL;
+
+ drvdata = dev_get_drvdata(ctcu->dev.parent);
+ port = coresight_get_in_port(etr, ctcu);
+ if (port < 0 || port >= ETR_MAX_NUM)
+ return NULL;
+
+ byte_cntr_data = &drvdata->byte_cntr_data[port];
+ /* The port is not initialized by ctcu_byte_cntr_init() */
+ if (!byte_cntr_data->ctcu_drvdata)
+ return NULL;
+
+ return byte_cntr_data;
+}
+
+/*
+ * ctcu_byte_cntr_get_data() - reads data from the deactivated and filled buffer.
+ * The byte-cntr reading work reads data from the deactivated and filled buffer.
+ * The read operation waits for a buffer to become available, either filled or
+ * upon timeout, and then reads trace data from the synced buffer.
+ */
+static ssize_t tmc_byte_cntr_get_data(struct tmc_drvdata *etr_drvdata, loff_t pos,
+ size_t len, char **bufpp)
+{
+ struct coresight_device *ctcu = tmc_etr_get_ctcu_device(etr_drvdata);
+ struct device *dev = &etr_drvdata->csdev->dev;
+ struct ctcu_byte_cntr *byte_cntr_data;
+ struct etr_buf *sysfs_buf;
+ atomic_t *irq_cnt;
+ ssize_t actual;
+ int ret;
+
+ /* No byte-cntr read session, let the caller use the generic read */
+ byte_cntr_data = ctcu_get_byte_cntr(ctcu, etr_drvdata->csdev);
+ if (!byte_cntr_data || !byte_cntr_data->irq_enabled ||
+ !byte_cntr_data->reading)
+ return -EINVAL;
+
+ irq_cnt = &byte_cntr_data->irq_cnt;
+
+wait_buffer:
+ if (!byte_cntr_data->buf_node) {
+ ret = wait_event_interruptible_timeout(byte_cntr_data->wq,
+ (atomic_read(irq_cnt) >= MAX_IRQ_CNT - 1) ||
+ !byte_cntr_data->enable,
+ BYTE_CNTR_TIMEOUT);
+ if (ret < 0)
+ return ret;
+ /*
+ * The current etr_buf is almost full or timeout is triggered,
+ * so switch the buffer and mark the switched buffer as reading.
+ */
+ if (byte_cntr_data->enable) {
+ byte_cntr_data->buf_node =
+ tmc_etr_switch_buf_node(etr_drvdata);
+ if (!byte_cntr_data->buf_node) {
+ /*
+ * The sink has stopped, let the generic read
+ * handle the last etr_buf.
+ */
+ if (coresight_get_mode(etr_drvdata->csdev) != CS_MODE_SYSFS)
+ return 0;
+
+ dev_err(dev, "Switch buffer failed for the byte-cntr\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Get the interrupt count of the switched out etr_buf
+ * and reset it for the next etr_buf.
+ */
+ if (atomic_xchg(irq_cnt, 0) >= MAX_IRQ_CNT)
+ dev_warn(dev, "Data overwrite happened\n");
+ } else {
+ /* Exit byte-cntr reading */
+ return 0;
+ }
+ }
+
+ pos = byte_cntr_data->buf_node->pos;
+ sysfs_buf = byte_cntr_data->buf_node->sysfs_buf;
+ actual = tmc_etr_read_sysfs_buf(sysfs_buf, pos, len, bufpp);
+ if (actual <= 0) {
+ /* Reset buf_node upon reading is finished or failed */
+ byte_cntr_data->buf_node->reading = false;
+ byte_cntr_data->buf_node = NULL;
+
+ /*
+ * Nothing in the buffer, waiting for the next buffer
+ * to be filled.
+ */
+ if (actual == 0)
+ goto wait_buffer;
+ }
+
+ return actual;
+}
+
+static int tmc_read_prepare_byte_cntr(struct tmc_drvdata *etr_drvdata)
+{
+ struct coresight_device *ctcu = tmc_etr_get_ctcu_device(etr_drvdata);
+ struct ctcu_byte_cntr *byte_cntr_data;
+ unsigned long flags;
+ int ret = 0;
+
+ /* byte-cntr is operating with SYSFS mode being enabled only */
+ if (coresight_get_mode(etr_drvdata->csdev) != CS_MODE_SYSFS)
+ return -EINVAL;
+
+ byte_cntr_data = ctcu_get_byte_cntr(ctcu, etr_drvdata->csdev);
+ if (!byte_cntr_data)
+ return -EINVAL;
+
+ raw_spin_lock_irqsave(&byte_cntr_data->spin_lock, flags);
+ /*
+ * irq_enabled can only be changed with the lock held and reading
+ * cleared, so check it here to keep it stable once reading is set.
+ */
+ if (!byte_cntr_data->irq_enabled) {
+ raw_spin_unlock_irqrestore(&byte_cntr_data->spin_lock, flags);
+ return -EINVAL;
+ }
+
+ if (byte_cntr_data->reading) {
+ raw_spin_unlock_irqrestore(&byte_cntr_data->spin_lock, flags);
+ return -EBUSY;
+ }
+
+ /* byte_cntr_data->enable may race with ctcu_platform_remove() */
+ if (!byte_cntr_data->enable) {
+ raw_spin_unlock_irqrestore(&byte_cntr_data->spin_lock, flags);
+ return -ENODEV;
+ }
+
+ /*
+ * Pin the module for the whole read session, so that rmmod fails with
+ * -EBUSY instead of blocking in ctcu_platform_remove() until the file
+ * is closed.
+ */
+ if (!try_module_get(THIS_MODULE)) {
+ raw_spin_unlock_irqrestore(&byte_cntr_data->spin_lock, flags);
+ return -ENODEV;
+ }
+
+ reinit_completion(&byte_cntr_data->session_done);
+ byte_cntr_data->reading = true;
+ raw_spin_unlock_irqrestore(&byte_cntr_data->spin_lock, flags);
+
+ /* Setup an available etr_buf_list for byte-cntr */
+ ret = tmc_create_etr_buf_list(etr_drvdata, 2);
+ if (ret)
+ goto err_reading;
+
+ /*
+ * The byte-cntr register is configured and the interrupts are counted
+ * since the path was enabled, so irq_cnt is not reset here.
+ */
+ scoped_guard(raw_spinlock_irqsave, &byte_cntr_data->spin_lock)
+ byte_cntr_data->buf_node = NULL;
+ /* enable_irq_wake() may sleep on slow-bus irqchips, call it unlocked */
+ enable_irq_wake(byte_cntr_data->irq);
+
+ return 0;
+
+err_reading:
+ /*
+ * reading was set before the sleeping allocations, so
+ * ctcu_platform_remove() may be waiting for the session to end. Complete
+ * the session with reading cleared in one critical section, so that a
+ * new session can't start in between.
+ */
+ scoped_guard(raw_spinlock_irqsave, &byte_cntr_data->spin_lock) {
+ byte_cntr_data->reading = false;
+ complete_all(&byte_cntr_data->session_done);
+ }
+
+ module_put(THIS_MODULE);
+ return ret;
+}
+
+static int tmc_read_unprepare_byte_cntr(struct tmc_drvdata *etr_drvdata)
+{
+ struct coresight_device *ctcu = tmc_etr_get_ctcu_device(etr_drvdata);
+ struct ctcu_byte_cntr *byte_cntr_data;
+
+ /*
+ * Do the unprepare operation only when the byte_cntr_data->reading
+ * is truly set
+ */
+ byte_cntr_data = ctcu_get_byte_cntr(ctcu, etr_drvdata->csdev);
+ if (!byte_cntr_data || !byte_cntr_data->irq_enabled ||
+ !byte_cntr_data->reading)
+ return -EINVAL;
+
+ tmc_clean_etr_buf_list(etr_drvdata);
+
+ /* disable_irq_wake() may sleep on slow-bus irqchips, call it unlocked */
+ disable_irq_wake(byte_cntr_data->irq);
+
+ /*
+ * The byte-cntr register is not accessed here: the trace path, which
+ * keeps the CTCU powered, may have been released already. It is reset
+ * when the path is disabled.
+ */
+ scoped_guard(raw_spinlock_irqsave, &byte_cntr_data->spin_lock) {
+ byte_cntr_data->buf_node = NULL;
+ byte_cntr_data->reading = false;
+ /*
+ * Complete the session with reading cleared in one critical
+ * section, so that a new session can't start in between.
+ * ctcu_platform_remove() may free byte_cntr_data after this,
+ * don't use it anymore.
+ */
+ complete_all(&byte_cntr_data->session_done);
+ }
+
+ module_put(THIS_MODULE);
+
+ return 0;
+}
+
+static const struct tmc_sysfs_ops byte_cntr_sysfs_ops = {
+ .read_prepare = tmc_read_prepare_byte_cntr,
+ .read_unprepare = tmc_read_unprepare_byte_cntr,
+ .get_trace_data = tmc_byte_cntr_get_data,
+};
+
+/* Start the byte-cntr function when the path is enabled. */
+void ctcu_byte_cntr_start(struct coresight_device *csdev, struct coresight_path *path)
+{
+ struct coresight_device *sink = coresight_get_sink(path);
+ struct tmc_drvdata *etr_drvdata;
+ struct ctcu_byte_cntr *byte_cntr_data;
+
+ byte_cntr_data = ctcu_get_byte_cntr(csdev, sink);
+ if (!byte_cntr_data)
+ return;
+
+ etr_drvdata = dev_get_drvdata(sink->dev.parent);
+ guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
+ /*
+ * Don't start byte-cntr function when irq_enabled is not set, or when
+ * the CTCU device is being removed.
+ */
+ if (!byte_cntr_data->irq_enabled || byte_cntr_data->removing)
+ return;
+
+ /*
+ * A new trace starts with an empty etr_buf. Don't reset the count if
+ * the byte-cntr is already started by another path of the same sink.
+ */
+ if (!byte_cntr_data->enable)
+ atomic_set(&byte_cntr_data->irq_cnt, 0);
+
+ /*
+ * Configure the byte-cntr register to enable IRQ while the path is
+ * enabled, the CTCU is powered then. The configured size is 5% of the
+ * buffer_size.
+ */
+ ctcu_cfg_byte_cntr_reg(byte_cntr_data->ctcu_drvdata,
+ etr_drvdata->size / MAX_IRQ_CNT,
+ byte_cntr_data->irq_ctrl_offset);
+ byte_cntr_data->enable = true;
+}
+
+/* Stop the byte-cntr function when the path is disabled. */
+void ctcu_byte_cntr_stop(struct coresight_device *csdev, struct coresight_path *path)
+{
+ struct coresight_device *sink = coresight_get_sink(path);
+ struct ctcu_byte_cntr *byte_cntr_data;
+
+ if (coresight_get_mode(sink) == CS_MODE_SYSFS)
+ return;
+
+ byte_cntr_data = ctcu_get_byte_cntr(csdev, sink);
+ if (!byte_cntr_data)
+ return;
+
+ scoped_guard(raw_spinlock_irqsave, &byte_cntr_data->spin_lock) {
+ /*
+ * Configure the byte-cntr register to disable IRQ before the
+ * path is released, the CTCU is still powered.
+ */
+ if (byte_cntr_data->enable)
+ ctcu_cfg_byte_cntr_reg(byte_cntr_data->ctcu_drvdata, 0,
+ byte_cntr_data->irq_ctrl_offset);
+ byte_cntr_data->enable = false;
+ }
+
+ /* Wake up the blocked reader, which exits the byte-cntr reading */
+ wake_up(&byte_cntr_data->wq);
+}
+
+void ctcu_byte_cntr_init(struct device *dev, struct ctcu_drvdata *drvdata, int etr_num)
+{
+ struct ctcu_byte_cntr *byte_cntr_data;
+ struct device_node *nd = dev->of_node;
+ int irq_num, ret, i, irq_registered = 0;
+
+ for (i = 0; i < etr_num; i++) {
+ byte_cntr_data = &drvdata->byte_cntr_data[i];
+ /*
+ * Initialize the lock and the waitqueue before requesting the
+ * IRQ, and for every port as ctcu_platform_remove() uses them
+ * even if the IRQ of the port is not registered.
+ */
+ byte_cntr_data->ctcu_drvdata = drvdata;
+ init_waitqueue_head(&byte_cntr_data->wq);
+ raw_spin_lock_init(&byte_cntr_data->spin_lock);
+ init_completion(&byte_cntr_data->session_done);
+ /* No read session in progress */
+ complete_all(&byte_cntr_data->session_done);
+
+ irq_num = of_irq_get(nd, i);
+ if (irq_num <= 0) {
+ dev_err(dev, "Failed to get IRQ from DT for port%d\n", i);
+ continue;
+ }
+
+ ret = devm_request_irq(dev, irq_num, byte_cntr_handler,
+ IRQF_TRIGGER_RISING,
+ dev_name(dev), byte_cntr_data);
+ if (ret) {
+ dev_err(dev, "Failed to register IRQ for port%d\n", i);
+ continue;
+ }
+
+ byte_cntr_data->irq = irq_num;
+ irq_registered++;
+ }
+
+ if (!irq_registered)
+ return;
+
+ scoped_guard(mutex, &byte_cntr_ops_lock) {
+ tmc_etr_set_byte_cntr_sysfs_ops(&byte_cntr_sysfs_ops);
+ byte_cntr_ops_users++;
+ }
+
+ /* Drop the ops user when the CTCU device is removed or probe fails */
+ if (devm_add_action_or_reset(dev, ctcu_byte_cntr_put_ops, NULL))
+ dev_err(dev, "Failed to add the byte-cntr devm action\n");
+}
diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c
index 739105acb0a1..cce4e4d76da0 100644
--- a/drivers/hwtracing/coresight/coresight-ctcu-core.c
+++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2024-2026 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/clk.h>
@@ -18,6 +19,7 @@
#include "coresight-ctcu.h"
#include "coresight-priv.h"
+#include "coresight-tmc.h"
#define ctcu_writel(drvdata, val, offset) __raw_writel((val), drvdata->base + offset)
#define ctcu_readl(drvdata, offset) __raw_readl(drvdata->base + offset)
@@ -43,17 +45,21 @@
#define CTCU_ATID_REG_BIT(traceid) (traceid % 32)
#define CTCU_ATID_REG_SIZE 0x10
+#define CTCU_ETR0_IRQCTRL 0x6c
+#define CTCU_ETR1_IRQCTRL 0x70
#define CTCU_ETR0_ATID0 0xf8
#define CTCU_ETR1_ATID0 0x108
static const struct ctcu_etr_config sa8775p_etr_cfgs[] = {
{
- .atid_offset = CTCU_ETR0_ATID0,
- .port_num = 0,
+ .atid_offset = CTCU_ETR0_ATID0,
+ .irq_ctrl_offset = CTCU_ETR0_IRQCTRL,
+ .port_num = 0,
},
{
- .atid_offset = CTCU_ETR1_ATID0,
- .port_num = 1,
+ .atid_offset = CTCU_ETR1_ATID0,
+ .irq_ctrl_offset = CTCU_ETR1_IRQCTRL,
+ .port_num = 1,
},
};
@@ -62,6 +68,93 @@ static const struct ctcu_config sa8775p_cfgs = {
.num_etr_config = ARRAY_SIZE(sa8775p_etr_cfgs),
};
+/*
+ * The register access is unlocked and locked in the same way as the ATID
+ * register, so serialise them with drvdata->spin_lock. Otherwise one side may
+ * lock the registers between the unlock and the write of the other side.
+ */
+void ctcu_program_register(struct ctcu_drvdata *drvdata, u32 val, u32 offset)
+{
+ guard(raw_spinlock_irqsave)(&drvdata->spin_lock);
+ CS_UNLOCK(drvdata->base);
+ ctcu_writel(drvdata, val, offset);
+ CS_LOCK(drvdata->base);
+}
+
+static ssize_t irq_enabled_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ctcu_byte_cntr_irq_attribute *irq_attr =
+ container_of(attr, struct ctcu_byte_cntr_irq_attribute, attr);
+ struct ctcu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ u8 port = irq_attr->port;
+
+ if (!drvdata->byte_cntr_data[port].irq_ctrl_offset)
+ return -EINVAL;
+
+ return sysfs_emit(buf, "%u\n",
+ (unsigned int)drvdata->byte_cntr_data[port].irq_enabled);
+}
+
+static ssize_t irq_enabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct ctcu_byte_cntr_irq_attribute *irq_attr =
+ container_of(attr, struct ctcu_byte_cntr_irq_attribute, attr);
+ struct ctcu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ u8 port = irq_attr->port;
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ guard(raw_spinlock_irqsave)(&drvdata->byte_cntr_data[port].spin_lock);
+ if (drvdata->byte_cntr_data[port].reading)
+ return -EBUSY;
+ else if (drvdata->byte_cntr_data[port].irq_ctrl_offset)
+ drvdata->byte_cntr_data[port].irq_enabled = !!val;
+
+ return size;
+}
+
+static umode_t irq_enabled_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device_attribute *dev_attr =
+ container_of(attr, struct device_attribute, attr);
+ struct ctcu_byte_cntr_irq_attribute *irq_attr =
+ container_of(dev_attr, struct ctcu_byte_cntr_irq_attribute, attr);
+ struct device *dev = kobj_to_dev(kobj);
+ struct ctcu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ u8 port = irq_attr->port;
+
+ /* Expose irq_enabled only for the ports with a registered IRQ */
+ if (drvdata && drvdata->byte_cntr_data[port].irq_ctrl_offset &&
+ drvdata->byte_cntr_data[port].irq)
+ return attr->mode;
+
+ return 0;
+}
+
+static struct attribute *ctcu_attrs[] = {
+ ctcu_byte_cntr_irq_rw(0),
+ ctcu_byte_cntr_irq_rw(1),
+ NULL,
+};
+
+static struct attribute_group ctcu_attr_grp = {
+ .attrs = ctcu_attrs,
+ .is_visible = irq_enabled_is_visible,
+};
+
+static const struct attribute_group *ctcu_attr_grps[] = {
+ &ctcu_attr_grp,
+ NULL,
+};
+
static void ctcu_program_atid_register(struct ctcu_drvdata *drvdata, u32 reg_offset,
u8 bit, bool enable)
{
@@ -140,11 +233,23 @@ static int ctcu_set_etr_traceid(struct coresight_device *csdev, struct coresight
static int ctcu_enable(struct coresight_device *csdev, enum cs_mode mode,
struct coresight_path *path)
{
- return ctcu_set_etr_traceid(csdev, path, true);
+ int ret;
+
+ ret = ctcu_set_etr_traceid(csdev, path, true);
+ /*
+ * Byte-cntr works in sysfs mode only. Start it once the trace ID is set,
+ * as ctcu_disable() won't be called to stop it if the enabling failed.
+ */
+ if (!ret && mode == CS_MODE_SYSFS)
+ ctcu_byte_cntr_start(csdev, path);
+
+ return ret;
}
static int ctcu_disable(struct coresight_device *csdev, struct coresight_path *path)
{
+ ctcu_byte_cntr_stop(csdev, path);
+
return ctcu_set_etr_traceid(csdev, path, false);
}
@@ -195,7 +300,10 @@ static int ctcu_probe(struct platform_device *pdev)
for (i = 0; i < cfgs->num_etr_config; i++) {
etr_cfg = &cfgs->etr_cfgs[i];
drvdata->atid_offset[i] = etr_cfg->atid_offset;
+ drvdata->byte_cntr_data[i].irq_ctrl_offset =
+ etr_cfg->irq_ctrl_offset;
}
+ ctcu_byte_cntr_init(dev, drvdata, cfgs->num_etr_config);
}
}
@@ -209,6 +317,7 @@ static int ctcu_probe(struct platform_device *pdev)
desc.dev = dev;
desc.ops = &ctcu_ops;
desc.access = CSDEV_ACCESS_IOMEM(base);
+ desc.groups = ctcu_attr_grps;
raw_spin_lock_init(&drvdata->spin_lock);
drvdata->csdev = coresight_register(&desc);
@@ -244,10 +353,52 @@ static int ctcu_platform_probe(struct platform_device *pdev)
static void ctcu_platform_remove(struct platform_device *pdev)
{
struct ctcu_drvdata *drvdata = platform_get_drvdata(pdev);
+ struct ctcu_byte_cntr *byte_cntr_data;
+ unsigned long flags;
+ int i;
if (WARN_ON(!drvdata))
return;
+ /*
+ * Signal all active byte-cntr readers to exit, then wait for them to
+ * finish before resetting the ops pointer and freeing driver data.
+ * Without this, a reader blocked in wait_event_interruptible_timeout()
+ * would access the freed ctcu_drvdata wait-queue head (use-after-free).
+ *
+ * The wait ends only when the userspace closes the tmc_etr file, so an
+ * unbind with the file left open blocks until then. Unloading the module
+ * is rejected with -EBUSY while a read session is open, so only a manual
+ * unbind can block here.
+ */
+ for (i = 0; i < ETR_MAX_NUM; i++) {
+ byte_cntr_data = &drvdata->byte_cntr_data[i];
+ /* The port was not initialized by ctcu_byte_cntr_init() */
+ if (!byte_cntr_data->irq)
+ continue;
+
+ raw_spin_lock_irqsave(&byte_cntr_data->spin_lock, flags);
+ /*
+ * Set enable=false to signal teardown to racing readers, and
+ * removing=true to keep a new path from enabling byte-cntr again.
+ */
+ byte_cntr_data->enable = false;
+ byte_cntr_data->removing = true;
+ raw_spin_unlock_irqrestore(&byte_cntr_data->spin_lock, flags);
+
+ /* Wake up the blocked reader, then wait for the session to end */
+ wake_up_all(&byte_cntr_data->wq);
+ wait_for_completion(&byte_cntr_data->session_done);
+
+ /*
+ * The ending session completes it with the lock held. Take the
+ * lock once more, to be sure that it has released the lock
+ * before byte_cntr_data is freed.
+ */
+ raw_spin_lock_irqsave(&byte_cntr_data->spin_lock, flags);
+ raw_spin_unlock_irqrestore(&byte_cntr_data->spin_lock, flags);
+ }
+
ctcu_remove(pdev);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/hwtracing/coresight/coresight-ctcu.h b/drivers/hwtracing/coresight/coresight-ctcu.h
index e9594c38dd91..60dded125ece 100644
--- a/drivers/hwtracing/coresight/coresight-ctcu.h
+++ b/drivers/hwtracing/coresight/coresight-ctcu.h
@@ -1,23 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2024-2026 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _CORESIGHT_CTCU_H
#define _CORESIGHT_CTCU_H
+
+#include <linux/completion.h>
+#include <linux/time.h>
#include "coresight-trace-id.h"
/* Maximum number of supported ETR devices for a single CTCU. */
#define ETR_MAX_NUM 2
+#define BYTE_CNTR_TIMEOUT (3 * HZ)
+#define MAX_IRQ_CNT 20
+
/**
- * struct ctcu_etr_config
+ * struct ctcu_etr_config - ETR specific configuration of the CTCU device
* @atid_offset: offset to the ATID0 Register.
- * @port_num: in-port number of CTCU device that connected to ETR.
+ * @port_num: in-port number of the CTCU device that connected to ETR.
+ * @irq_ctrl_offset: offset to the BYTECNTRVAL register.
*/
struct ctcu_etr_config {
const u32 atid_offset;
const u32 port_num;
+ const u32 irq_ctrl_offset;
};
struct ctcu_config {
@@ -25,15 +34,72 @@ struct ctcu_config {
int num_etr_config;
};
-struct ctcu_drvdata {
- void __iomem *base;
- struct clk *apb_clk;
- struct device *dev;
- struct coresight_device *csdev;
+/**
+ * struct ctcu_byte_cntr - byte-cntr data of a port of the CTCU device
+ * @enable: indicates that byte_cntr function is enabled or not.
+ * @irq_enabled: indicates that the interruption is enabled.
+ * @reading: indicates that byte_cntr is reading.
+ * @removing: indicates that the CTCU device is being removed.
+ * @irq: allocated number of the IRQ.
+ * @irq_cnt: IRQ count number of the triggered interruptions.
+ * @wq: waitqueue for reading data from ETR buffer.
+ * @spin_lock: spinlock of the byte_cntr_data.
+ * @session_done: completed when there is no read session in progress.
+ * @irq_ctrl_offset: offset to the BYTECNTVAL Register.
+ * @ctcu_drvdata: drvdata of the CTCU device.
+ * @buf_node: etr_buf_node for reading.
+ */
+struct ctcu_byte_cntr {
+ bool enable;
+ bool irq_enabled;
+ bool reading;
+ bool removing;
+ int irq;
+ atomic_t irq_cnt;
+ wait_queue_head_t wq;
raw_spinlock_t spin_lock;
- u32 atid_offset[ETR_MAX_NUM];
- /* refcnt for each traceid of each sink */
- u8 traceid_refcnt[ETR_MAX_NUM][CORESIGHT_TRACE_ID_RES_TOP];
+ struct completion session_done;
+ u32 irq_ctrl_offset;
+ struct ctcu_drvdata *ctcu_drvdata;
+ struct etr_buf_node *buf_node;
};
+struct ctcu_drvdata {
+ void __iomem *base;
+ struct clk *apb_clk;
+ struct device *dev;
+ struct coresight_device *csdev;
+ struct ctcu_byte_cntr byte_cntr_data[ETR_MAX_NUM];
+ raw_spinlock_t spin_lock;
+ u32 atid_offset[ETR_MAX_NUM];
+ /* refcnt for each traceid of each sink */
+ u8 traceid_refcnt[ETR_MAX_NUM][CORESIGHT_TRACE_ID_RES_TOP];
+};
+
+/**
+ * struct ctcu_byte_cntr_irq_attribute - sysfs attribute of the byte-cntr IRQ
+ * @attr: The device attribute.
+ * @port: port number.
+ */
+struct ctcu_byte_cntr_irq_attribute {
+ struct device_attribute attr;
+ u8 port;
+};
+
+#define ctcu_byte_cntr_irq_rw(port) \
+ (&((struct ctcu_byte_cntr_irq_attribute[]) { \
+ { \
+ __ATTR(irq_enabled##port, 0644, irq_enabled_show, \
+ irq_enabled_store), \
+ port, \
+ } \
+ })[0].attr.attr)
+
+void ctcu_program_register(struct ctcu_drvdata *drvdata, u32 val, u32 offset);
+
+/* Byte-cntr functions */
+void ctcu_byte_cntr_start(struct coresight_device *csdev, struct coresight_path *path);
+void ctcu_byte_cntr_stop(struct coresight_device *csdev, struct coresight_path *path);
+void ctcu_byte_cntr_init(struct device *dev, struct ctcu_drvdata *drvdata, int port_num);
+
#endif
diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
index 4b40b692be4d..6ad09995ba87 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-core.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
@@ -293,7 +293,8 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
return -EFAULT;
}
- *ppos += actual;
+ if (!tmc_etr_update_buf_node_pos(drvdata, actual))
+ *ppos += actual;
dev_dbg(&drvdata->csdev->dev, "%zu bytes copied\n", actual);
return actual;
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index bf4c1d8f7b2e..20d6bc60a446 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1168,6 +1168,9 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
return rc;
}
+/* Shared by the CTCU devices, set by the first one and reset by the last one. */
+static const struct tmc_sysfs_ops *byte_cntr_sysfs_ops;
+
/*
* Return the available trace data in the buffer (starts at etr_buf->offset,
* limited by etr_buf->len) from @pos, with a maximum limit of @len,
@@ -1178,23 +1181,44 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
* We are protected here by drvdata->reading != 0, which ensures the
* sysfs_buf stays alive.
*/
-ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
- loff_t pos, size_t len, char **bufpp)
+ssize_t tmc_etr_read_sysfs_buf(struct etr_buf *sysfs_buf, loff_t pos,
+ size_t len, char **bufpp)
{
s64 offset;
ssize_t actual = len;
- struct etr_buf *etr_buf = drvdata->sysfs_buf;
- if (pos + actual > etr_buf->len)
- actual = etr_buf->len - pos;
+ if (pos + actual > sysfs_buf->len)
+ actual = sysfs_buf->len - pos;
if (actual <= 0)
return actual;
/* Compute the offset from which we read the data */
- offset = etr_buf->offset + pos;
- if (offset >= etr_buf->size)
- offset -= etr_buf->size;
- return tmc_etr_buf_get_data(etr_buf, offset, actual, bufpp);
+ offset = sysfs_buf->offset + pos;
+ if (offset >= sysfs_buf->size)
+ offset -= sysfs_buf->size;
+ return tmc_etr_buf_get_data(sysfs_buf, offset, actual, bufpp);
+}
+EXPORT_SYMBOL_GPL(tmc_etr_read_sysfs_buf);
+
+ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
+ loff_t pos, size_t len, char **bufpp)
+{
+ ssize_t ret;
+ const struct tmc_sysfs_ops *byte_cntr_ops = READ_ONCE(byte_cntr_sysfs_ops);
+
+ if (byte_cntr_ops) {
+ ret = byte_cntr_ops->get_trace_data(drvdata, pos, len, bufpp);
+ /*
+ * -EINVAL means byte-cntr has no active read session. 0 means
+ * byte-cntr has no more data because the sink has stopped, the
+ * last etr_buf is read by the generic read. Anything else is
+ * the result of the byte-cntr session.
+ */
+ if (ret != -EINVAL && ret != 0)
+ return ret;
+ }
+
+ return tmc_etr_read_sysfs_buf(drvdata->sysfs_buf, pos, len, bufpp);
}
static struct etr_buf *
@@ -2170,15 +2194,54 @@ int tmc_create_etr_buf_list(struct tmc_drvdata *drvdata, int num_nodes)
}
EXPORT_SYMBOL_GPL(tmc_create_etr_buf_list);
+void tmc_etr_set_byte_cntr_sysfs_ops(const struct tmc_sysfs_ops *sysfs_ops)
+{
+ WRITE_ONCE(byte_cntr_sysfs_ops, sysfs_ops);
+}
+EXPORT_SYMBOL_GPL(tmc_etr_set_byte_cntr_sysfs_ops);
+
+void tmc_etr_reset_byte_cntr_sysfs_ops(void)
+{
+ WRITE_ONCE(byte_cntr_sysfs_ops, NULL);
+}
+EXPORT_SYMBOL_GPL(tmc_etr_reset_byte_cntr_sysfs_ops);
+
+bool tmc_etr_update_buf_node_pos(struct tmc_drvdata *drvdata, ssize_t size)
+{
+ struct etr_buf_node *nd, *next;
+
+ if (drvdata->config_type != TMC_CONFIG_TYPE_ETR)
+ return false;
+
+ list_for_each_entry_safe(nd, next, &drvdata->etr_buf_list, link) {
+ if (nd && nd->reading) {
+ nd->pos += size;
+ return true;
+ }
+ }
+
+ return false;
+}
+
int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
{
int ret = 0;
unsigned long flags;
+ const struct tmc_sysfs_ops *byte_cntr_ops;
/* config types are set a boot time and never change */
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
return -EINVAL;
+ byte_cntr_ops = READ_ONCE(byte_cntr_sysfs_ops);
+ if (byte_cntr_ops) {
+ ret = byte_cntr_ops->read_prepare(drvdata);
+ if (!ret || ret == -EBUSY)
+ return ret;
+
+ ret = 0;
+ }
+
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
/* The sysfs_buf is owned by the etr_buf_list */
if (drvdata->reading || !list_empty(&drvdata->etr_buf_list)) {
@@ -2211,11 +2274,17 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
{
unsigned long flags;
struct etr_buf *sysfs_buf = NULL;
+ const struct tmc_sysfs_ops *byte_cntr_ops;
/* config types are set a boot time and never change */
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
return -EINVAL;
+ byte_cntr_ops = READ_ONCE(byte_cntr_sysfs_ops);
+ if (byte_cntr_ops)
+ if (!byte_cntr_ops->read_unprepare(drvdata))
+ return 0;
+
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
/* RE-enable the TMC if need be */
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 2345bdbb0b9d..af89825ec041 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -484,6 +484,11 @@ struct etr_buf *tmc_etr_get_buffer(struct coresight_device *csdev,
extern const struct attribute_group coresight_etr_group;
void tmc_clean_etr_buf_list(struct tmc_drvdata *drvdata);
int tmc_create_etr_buf_list(struct tmc_drvdata *drvdata, int num_nodes);
+void tmc_etr_set_byte_cntr_sysfs_ops(const struct tmc_sysfs_ops *sysfs_ops);
+void tmc_etr_reset_byte_cntr_sysfs_ops(void);
struct etr_buf_node *tmc_etr_switch_buf_node(struct tmc_drvdata *drvdata);
+bool tmc_etr_update_buf_node_pos(struct tmc_drvdata *drvdata, ssize_t size);
+ssize_t tmc_etr_read_sysfs_buf(struct etr_buf *sysfs_buf, loff_t pos,
+ size_t len, char **bufpp);
#endif
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v21 8/8] arm64: dts: qcom: lemans: add interrupts to CTCU device
2026-11-02 9:46 [PATCH v21 0/8] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
` (6 preceding siblings ...)
2026-11-02 9:46 ` [PATCH v21 7/8] coresight: ctcu: enable byte-cntr for TMC ETR devices Jie Gan
@ 2026-11-02 9:46 ` Jie Gan
2026-11-10 1:28 ` [PATCH v21 0/8] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
8 siblings, 0 replies; 10+ messages in thread
From: Jie Gan @ 2026-11-02 9:46 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Alexander Shishkin, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Tingwei Zhang, Jie Gan, Bjorn Andersson,
Konrad Dybcio, Yuanfang Zhang, Mao Jinlong, Abel Vesa
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
devicetree, Konrad Dybcio
Add interrupts to enable byte-cntr function for TMC ETR devices.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/lemans.dtsi | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/lemans.dtsi b/arch/arm64/boot/dts/qcom/lemans.dtsi
index 1fdb72fa5518..852a1142100f 100644
--- a/arch/arm64/boot/dts/qcom/lemans.dtsi
+++ b/arch/arm64/boot/dts/qcom/lemans.dtsi
@@ -3132,6 +3132,9 @@ ctcu@4001000 {
clocks = <&aoss_qmp>;
clock-names = "apb";
+ interrupts = <GIC_SPI 270 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 262 IRQ_TYPE_EDGE_RISING>;
+
in-ports {
#address-cells = <1>;
#size-cells = <0>;
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH v21 0/8] coresight: ctcu: Enable byte-cntr function for TMC ETR
2026-11-02 9:46 [PATCH v21 0/8] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
` (7 preceding siblings ...)
2026-11-02 9:46 ` [PATCH v21 8/8] arm64: dts: qcom: lemans: add interrupts to CTCU device Jie Gan
@ 2026-11-10 1:28 ` Jie Gan
8 siblings, 0 replies; 10+ messages in thread
From: Jie Gan @ 2026-11-10 1:28 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Alexander Shishkin, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Tingwei Zhang, Bjorn Andersson, Konrad Dybcio,
Yuanfang Zhang, Mao Jinlong, Abel Vesa
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
devicetree, Krzysztof Kozlowski, Konrad Dybcio
On 11/2/2026 5:46 PM, Jie Gan wrote:
> The byte-cntr function provided by the CTCU device is used to count the
> trace data entering the ETR. An interrupt is triggered if the data size
> exceeds the threshold set in the BYTECNTRVAL register. The interrupt
> handler counts the number of triggered interruptions.
>
> Based on this concept, the irq_cnt can be used to determine whether
> the etr_buf is full. The ETR device will be disabled when the active
> etr_buf is nearly full or a timeout occurs. The nearly full buffer will
> be switched to background after synced. A new buffer will be picked from
> the etr_buf_list, then restart the ETR device.
>
> The byte-cntr reading functions can access data from the synced and
> deactivated buffer, transferring trace data from the etr_buf to userspace
> without stopping the ETR device.
>
> The byte-cntr read operation has integrated with the file node tmc_etr,
> for example:
> /dev/tmc_etr0
> /dev/tmc_etr1
>
> There are two scenarios for the tmc_etr file node with byte-cntr function:
> 1. BYTECNTRVAL register is configured and byte-cntr is enabled -> byte-cntr read
> 2. BYTECNTRVAL register is reset or byte-cntr is disabled -> original behavior
>
> Shell commands to enable byte-cntr reading for etr0:
> echo 1 > /sys/bus/coresight/devices/ctcu0/irq_enabled0
> echo 1 > /sys/bus/coresight/devices/tmc_etr0/enable_sink
> echo 1 > /sys/bus/coresight/devices/etm0/enable_source
> cat /dev/tmc_etr0
>
> Reset the BYTECNTR register for etr0:
> echo 0 > /sys/bus/coresight/devices/ctcu0/irq_enabled0
>
I am going through the comments from Sashiko. I will send V22 to address
the valuable comments from Sashiko.
Thanks,
Jie
> ---
> Changes in v21:
> 1. coresight: tmc: add create/clean functions for etr_buf_list:
> - Make the byte-cntr read and the generic sysfs read of the TMC
> exclusive: tmc_create_etr_buf_list() returns -EBUSY if a generic
> read is in progress and tmc_read_prepare_etr() returns -EBUSY if the
> etr_buf_list is not empty.
> - Wrap the live sysfs_buf into the list only while the sink is enabled
> in sysfs mode, and keep it in tmc_clean_etr_buf_list() only in that
> mode, as the sink enable path may reuse or free the sysfs_buf
> otherwise.
> - Don't create the list in the reserved buffer mode, as all the
> etr_buf of this mode share the same reserved region.
> - Fixed the kernel-doc of struct etr_buf_node and
> tmc_create_etr_buf_list().
> 2. coresight: tmc: add a function to switch the ETR to a free etr_buf (new):
> - Move the buffer switch out of the byte-cntr patch to a TMC helper,
> which stops the ETR, swaps the etr_buf and restarts the ETR in one
> critical section. The switch was done in three separate critical
> sections before, so tmc_disable_etr_sink() could run in between and
> leave drvdata->etr_buf out of sync with the hardware.
> 3. coresight: ctcu: enable byte-cntr for TMC ETR devices:
> - Fixed the drain of the last etr_buf after the trace is stopped:
> tmc_etr_get_sysfs_trace() falls back to the generic read when
> byte-cntr has no more data (v20 returned EOF and lost the last
> etr_buf). Other results of the byte-cntr read, such as the one of a
> signal, are returned as they are.
> - Program and reset the byte-cntr register when the path is enabled
> and disabled, instead of in the read prepare/unprepare. The CTCU may
> be powered off once the trace path is released, and accessing its
> register from the read unprepare, which runs when the file is
> closed after the trace is stopped, hangs the bus.
> - Serialise the CTCU register programming with the ATID programming
> with drvdata->spin_lock, as both unlock and lock the same register.
> - Fixed the use-after-free of the byte-cntr data between
> ctcu_platform_remove() and the read unprepare: the remove waits for
> a completion which is the last access of the read unprepare, and a
> new path can't start byte-cntr once the device is being removed.
> - Wake up the blocked reader once the byte-cntr is stopped, instead of
> waiting for the timeout.
> - Start byte-cntr in ctcu_enable() only in sysfs mode and after the
> trace ID is set.
> - Check the interrupt returned by of_irq_get() against 0.
> - Report the interrupt overwrite of the etr_buf which is handed over
> for reading, instead of the one which is tracing.
> - Make byte_cntr_sysfs_ops static and fixed the kernel-doc of the
> structs in coresight-ctcu.h.
> - Count the byte-cntr interrupts since the trace is started, and not reset
> the count when the read session starts, so the first etr_buf is switched
> at the right fill level for a reader which opens after the trace started.
> - Ignore the ports which are not initialized by ctcu_byte_cntr_init().
> - Updated the commit message.
> - Link to v20: https://lore.kernel.org/r/20261030-enable-byte-cntr-for-ctcu-v20-0-bd5c3ac9f916@oss.qualcomm.com
>
> Changes in v20:
> 1. coresight: core: refactor ctcu_get_active_port and make it generic:
> - Check the port number returned by coresight_get_in_port() against
> ETR_MAX_NUM in ctcu_set_etr_traceid() to avoid an out-of-bounds
> access of the per-port arrays with a bad DT port number.
> 2. coresight: tmc: add create/clean functions for etr_buf_list:
> - Take drvdata->sysfs_buf and add the first node to etr_buf_list in
> one critical section, and do all etr_buf_list updates (add and
> delete) with drvdata->spinlock held.
> - Return -EBUSY from tmc_etr_get_sysfs_buffer() while etr_buf_list is
> not empty, so the ETR sink enable path cannot free or replace a
> buffer that is still owned by the list during a byte-cntr session.
> 3. coresight: ctcu: enable byte-cntr for TMC ETR devices:
> - Fixed a NULL pointer dereference in ctcu_get_byte_cntr() when the
> opened ETR is not connected to a CTCU device, since the byte-cntr
> sysfs ops are global to all the TMC ETRs.
> - Fixed the sysfs read fallback: tmc_byte_cntr_get_data() now returns
> -EINVAL unless a byte-cntr read session is active, and
> tmc_etr_get_sysfs_trace() falls back to the generic read only for
> -EINVAL. Errors and EOF of the byte-cntr read are returned as-is
> instead of reading the live buffer.
> - Fixed a race between the buffer switch and the sink disable path:
> tmc_etr_enable_disable_hw() now checks the sink mode with
> drvdata->spinlock held, so the ETR is not restarted after the sink
> has been disabled.
> - tmc_read_prepare_byte_cntr(): check irq_enabled with the
> byte_cntr_data->spin_lock held, and clear reading under the lock
> and wake up the waiter on the buffer list allocation failure path.
> - Pin the module with try_module_get() for the duration of a byte-cntr
> read session, so unloading the module fails with -EBUSY instead of
> blocking in ctcu_platform_remove(). Documented that a manual unbind
> still waits for the tmc_etr file to be closed.
> - Dropped IRQF_SHARED from the byte-cntr interrupt: the handler does
> not check the interrupt source.
> - Read curr_buf with drvdata->spinlock held and handle a missing
> current node in ctcu_byte_cntr_switch_buffer().
> - Initialize the lock and waitqueue of every port before requesting
> its interrupt, skip the ports without a registered IRQ in
> ctcu_platform_remove(), and only expose irq_enabled for the ports
> with a registered IRQ.
> - Refcount the byte-cntr sysfs ops registration across CTCU devices
> with a devm action, so removing one CTCU no longer disables
> byte-cntr for the others. Replaced the single-CTCU-instance comment
> on byte_cntr_sysfs_ops accordingly.
> - Link to v19: https://lore.kernel.org/r/20260625-enable-byte-cntr-for-ctcu-v19-0-8fbbf22e8381@oss.qualcomm.com
>
> Changes in v19:
> 1. coresight: tmc: add create/clean functions for etr_buf_list:
> - Simplified the kernel-doc "Locking:" note on tmc_create_etr_buf_list()
> and tmc_clean_etr_buf_list() to state only the required contract
> (caller must guarantee exclusive access to drvdata->etr_buf_list),
> dropping the over-specific call-path details.
> - Lock the required code block in create/clean functions.
> 2. coresight: ctcu: enable byte-cntr for TMC ETR devices:
> - Fixed a stale reading-state / double-free when irq_enabled was
> cleared between prepare and unprepare: the byte-cntr read_unprepare
> would return an error and tmc_read_unprepare_etr() would fall back
> to the normal unprepare path, freeing drvdata->sysfs_buf while it
> was still owned by the byte-cntr buffer list. irq_enabled_store()
> now returns -EBUSY while byte_cntr_data->reading is set, so the flag
> can no longer change mid-session, and tmc_read_unprepare_byte_cntr()
> additionally guards on byte_cntr_data->reading.
> - Moved enable_irq_wake()/disable_irq_wake() out of the
> byte_cntr_data->spin_lock critical section in
> tmc_read_prepare_byte_cntr()/tmc_read_unprepare_byte_cntr().
> irq_set_irq_wake() may sleep on slow-bus irqchips, so it must not
> be called under a raw spinlock. The threshold IRQ is already
> disabled by the in-lock register write before disable_irq_wake() on
> the unprepare path, so no wake event can race in the gap.
> - Added a comment on the byte_cntr_sysfs_ops pointer documenting the
> single-CTCU-instance-per-system assumption.
> - Link to v18: https://lore.kernel.org/r/20260507-enable-byte-cntr-for-ctcu-v18-0-2b2d590463a3@oss.qualcomm.com
>
> Changes in v18:
> 1. add a NULL check for the in_conns instance in patch 1.
> 2. fix a bug in patch 2: the tmc_alloc_etr_buf never return NULL and the
> previous check for the return value is incorrect.
> 3. add more kernel_doc description for tmc_clean_etr_buf_list function
> in patch 2
> - Link to v17: https://lore.kernel.org/r/20260421-enable-byte-cntr-for-ctcu-v17-0-9cf36ff55fc0@oss.qualcomm.com
>
> Changes in v17:
> 1. fix race issue during allocat buffer.
> 2. fix user after free issue observed when remove module.
> - Link to v16: https://lore.kernel.org/r/20260323-enable-byte-cntr-for-ctcu-v16-0-7a413d211b8d@oss.qualcomm.com
>
> Changes in v16:
> 1. Remove lock/unlock processes in patch "coresight: tmc: add create/clean
> functions for etr_buf_list" because we are allocating/freeing memory.
> - Link to v15: https://lore.kernel.org/r/20260313-enable-byte-cntr-for-ctcu-v15-0-1777f14ed319@oss.qualcomm.com
>
> Changes in v15:
> 1. add lockdep_assert_held in patch "coresight: tmc: add create/clean
> functions for etr_buf_list"
> 2. optimize tmc_clean_etr_buf_list function
> 3. optimize the patch "enable byte-cntr for TMC ETR devices" according
> to Suzuki's comments
> - call byte_cntr_sysfs_ops from etr_sysfs_ops
> - optimize the lock usage in all functions
> - remove the buf_node parameter in etr_drvdata, move it to
> byte_cntr_data
> - move the tmc_reset_sysfs_buf function to tmc-etr.c
> - add a read flag to struct etr_buf_node to allow updating pos while
> traversing etr_buf_list during data reads.
> Link to v14: https://lore.kernel.org/r/20260309-enable-byte-cntr-for-ctcu-v14-0-c08823e5a8e6@oss.qualcomm.com
>
> Changes in V14:
> 1. Drop the patch: integrate byte-cntr's sysfs_ops with tmc sysfs file_ops
> 2. Replace tmc_sysfs_ops with byte_cntr_sysfs_ops in byte_cntr_start
> function and restore etr_sysfs_ops in byte_cntr_unprepare function.
> 3. Remove redundant checks in byte‑cntr functions.
> Link to V13: https://lore.kernel.org/all/20260223-enable-byte-cntr-for-ctcu-v13-0-9cb44178b250@oss.qualcomm.com/
>
> Changes in v13:
> 1. initilize the byte_cntr_data->raw_spin_lock before using.
> 2. replace kzalloc with kzalloc_obj.
> Link to V12: https://lore.kernel.org/all/20260203-enable-byte-cntr-for-ctcu-v12-0-7bf81b86b70e@oss.qualcomm.com/
>
> Changes in v12:
> 1. Add a new function for retrieving the CTCU's coresight_dev instead of
> refactor the existing function.
> Link to v11: https://lore.kernel.org/r/20260126-enable-byte-cntr-for-ctcu-v11-0-c0af66ba15cf@oss.qualcomm.com
>
> Changes in v11:
> 1. Correct the description in patch1 for the function coresight_get_in_port.
> 2. Renaming the sysfs_ops to tmc_sysfs_ops per Suzuki's suggestion.
> Link to v10: https://lore.kernel.org/r/20260122-enable-byte-cntr-for-ctcu-v10-0-22978e3c169f@oss.qualcomm.com
>
> Changes in v10:
> 1. fix a free memory issue that is reported by robot for patch 2.
> Link to v9: https://lore.kernel.org/r/20251224-enable-byte-cntr-for-ctcu-v9-0-886c4496fed4@oss.qualcomm.com
>
> Changes in v9:
> 1. Drop the patch: add a new API to retrieve the helper device
> 2. Add a new patch to refactor the tmc_etr_get_catu_device function,
> making it generic to support all types of helper devices associated with ETR.
> 3. Optimizing the code for creating irq_threshold sysfs node.
> 4. Remove interrupt-name property and obtain the IRQ based on the
> in-port number.
> Link to v8: https://lore.kernel.org/r/20251211-enable-byte-cntr-for-ctcu-v8-0-3e12ff313191@oss.qualcomm.com
>
> Changes in V8:
> 1. Optimizing the patch 1 and patch 2 according to Suzuki's comments.
> 2. Combine the patch 3 and patch 4 together.
> 3. Rename the interrupt-name to prevent confusion, for example:etr0->etrirq0.
> Link to V7 - https://lore.kernel.org/all/20251013-enable-byte-cntr-for-ctcu-v7-0-e1e8f41e15dd@oss.qualcomm.com/
>
> Changes in V7:
> 1. rebased on tag next-20251010
> 2. updated info for sysfs node document
> Link to V6 - https://lore.kernel.org/all/20250908-enable-byte-cntr-for-tmc-v6-0-1db9e621441a@oss.qualcomm.com/
>
> Changes in V6:
> 1. rebased on next-20250905.
> 2. fixed the issue that the dtsi file has re-named from sa8775p.dtsi to
> lemans.dtsi.
> 3. fixed some minor issues about comments.
> Link to V5 - https://lore.kernel.org/all/20250812083731.549-1-jie.gan@oss.qualcomm.com/
>
> Changes in V5:
> 1. Add Mike's reviewed-by tag for patchset 1,2,5.
> 2. Remove the function pointer added to helper_ops according to Mike's
> comment, it also results the patchset has been removed.
> 3. Optimizing the paired create/clean functions for etr_buf_list.
> 4. Remove the unneeded parameter "reading" from the etr_buf_node.
> Link to V4 - https://lore.kernel.org/all/20250725100806.1157-1-jie.gan@oss.qualcomm.com/
>
> Changes in V4:
> 1. Rename the function to coresight_get_in_port_dest regarding to Mike's
> comment (patch 1/10).
> 2. Add lock to protect the connections regarding to Mike's comment
> (patch 2/10).
> 3. Move all byte-cntr functions to coresight-ctcu-byte-cntr file.
> 4. Add tmc_read_ops to wrap all read operations for TMC device.
> 5. Add a function in helper_ops to check whether the byte-cntr is
> enabkled.
> 6. Call byte-cntr's read_ops if byte-cntr is enabled when reading data
> from the sysfs node.
> Link to V3 resend - https://lore.kernel.org/all/20250714063109.591-1-jie.gan@oss.qualcomm.com/
>
> Changes in V3 resend:
> 1. rebased on next-20250711.
> Link to V3 - https://lore.kernel.org/all/20250624060438.7469-1-jie.gan@oss.qualcomm.com/
>
> Changes in V3:
> 1. The previous solution has been deprecated.
> 2. Add a etr_buf_list to manage allcated etr buffers.
> 3. Add a logic to switch buffer for ETR.
> 4. Add read functions to read trace data from synced etr buffer.
> Link to V2 - https://lore.kernel.org/all/20250410013330.3609482-1-jie.gan@oss.qualcomm.com/
>
> Changes in V2:
> 1. Removed the independent file node /dev/byte_cntr.
> 2. Integrated the byte-cntr's file operations with current ETR file
> node.
> 3. Optimized the driver code of the CTCU that associated with byte-cntr.
> 4. Add kernel document for the export API tmc_etr_get_rwp_offset.
> 5. Optimized the way to read the rwp_offset according to Mike's
> suggestion.
> 6. Removed the dependency of the dts patch.
> Link to V1 - https://lore.kernel.org/all/20250310090407.2069489-1-quic_jiegan@quicinc.com/
>
> Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
>
> ---
> Jie Gan (8):
> coresight: core: refactor ctcu_get_active_port and make it generic
> coresight: tmc: add create/clean functions for etr_buf_list
> coresight: tmc: add a function to switch the ETR to a free etr_buf
> coresight: tmc: introduce tmc_sysfs_ops to wrap sysfs read operations
> coresight: etr: add a new function to retrieve the CTCU device
> dt-bindings: arm: add an interrupt property for Coresight CTCU
> coresight: ctcu: enable byte-cntr for TMC ETR devices
> arm64: dts: qcom: lemans: add interrupts to CTCU device
>
> .../ABI/testing/sysfs-bus-coresight-devices-ctcu | 9 +
> .../bindings/arm/qcom,coresight-ctcu.yaml | 10 +
> arch/arm64/boot/dts/qcom/lemans.dtsi | 3 +
> drivers/hwtracing/coresight/Makefile | 2 +-
> drivers/hwtracing/coresight/coresight-core.c | 27 ++
> .../hwtracing/coresight/coresight-ctcu-byte-cntr.c | 400 +++++++++++++++++++++
> drivers/hwtracing/coresight/coresight-ctcu-core.c | 184 ++++++++--
> drivers/hwtracing/coresight/coresight-ctcu.h | 88 ++++-
> drivers/hwtracing/coresight/coresight-priv.h | 2 +
> drivers/hwtracing/coresight/coresight-tmc-core.c | 55 ++-
> drivers/hwtracing/coresight/coresight-tmc-etr.c | 344 +++++++++++++++++-
> drivers/hwtracing/coresight/coresight-tmc.h | 43 +++
> 12 files changed, 1089 insertions(+), 78 deletions(-)
> ---
> base-commit: a39c950806213f6861d00f90729acaaa21f2d95a
> change-id: 20260309-enable-byte-cntr-for-ctcu-ff86e6198b7f
>
> Best regards,
^ permalink raw reply [flat|nested] 10+ messages in thread