Linux ARM-MSM sub-architecture
 help / color / mirror / Atom feed
* [PATCH v14 0/7] coresight: ctcu: Enable byte-cntr function for TMC ETR
@ 2026-03-09  9:47 Jie Gan
  2026-03-09  9:47 ` [PATCH v14 1/7] coresight: core: refactor ctcu_get_active_port and make it generic Jie Gan
                   ` (6 more replies)
  0 siblings, 7 replies; 15+ messages in thread
From: Jie Gan @ 2026-03-09  9:47 UTC (permalink / raw)
  To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
	Bjorn Andersson, Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree, Jie Gan, Konrad Dybcio, Mike Leach,
	Krzysztof Kozlowski

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 0x10000 > /sys/bus/coresight/devices/ctcu0/irq_threshold0
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_threshold0

---
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/

To: Suzuki K Poulose <suzuki.poulose@arm.com>
To: Mike Leach <mike.leach@arm.com>
To: James Clark <james.clark@linaro.org>
To: Alexander Shishkin <alexander.shishkin@linux.intel.com>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Tingwei Zhang <tingwei.zhang@oss.qualcomm.com>
To: Bjorn Andersson <andersson@kernel.org>
To: Konrad Dybcio <konradybcio@kernel.org>
Cc: coresight@lists.linaro.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-msm@vger.kernel.org
Cc: devicetree@vger.kernel.org
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>

---
Jie Gan (7):
      coresight: core: refactor ctcu_get_active_port and make it generic
      coresight: tmc: add create/clean functions for etr_buf_list
      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   |   8 +
 .../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       |  24 ++
 .../hwtracing/coresight/coresight-ctcu-byte-cntr.c | 351 +++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-ctcu-core.c  | 122 +++++--
 drivers/hwtracing/coresight/coresight-ctcu.h       |  76 ++++-
 drivers/hwtracing/coresight/coresight-priv.h       |   2 +
 drivers/hwtracing/coresight/coresight-tmc-core.c   |  58 ++--
 drivers/hwtracing/coresight/coresight-tmc-etr.c    | 136 ++++++++
 drivers/hwtracing/coresight/coresight-tmc.h        |  37 +++
 12 files changed, 768 insertions(+), 61 deletions(-)
---
base-commit: a0ae2a256046c0c5d3778d1a194ff2e171f16e5f
change-id: 20260309-enable-byte-cntr-for-ctcu-ff86e6198b7f

Best regards,
-- 
Jie Gan <jie.gan@oss.qualcomm.com>


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v14 1/7] coresight: core: refactor ctcu_get_active_port and make it generic
  2026-03-09  9:47 [PATCH v14 0/7] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
@ 2026-03-09  9:47 ` Jie Gan
  2026-03-09  9:47 ` [PATCH v14 2/7] coresight: tmc: add create/clean functions for etr_buf_list Jie Gan
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 15+ messages in thread
From: Jie Gan @ 2026-03-09  9:47 UTC (permalink / raw)
  To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
	Bjorn Andersson, Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree, Jie Gan, Mike Leach

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@linaro.org>
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
 drivers/hwtracing/coresight/coresight-core.c      | 24 +++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-ctcu-core.c | 19 +-----------------
 drivers/hwtracing/coresight/coresight-priv.h      |  2 ++
 3 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 46f247f73cf6..221de57ca57b 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -588,6 +588,30 @@ 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]->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..e8720026c9e3 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,7 +128,7 @@ static int ctcu_set_etr_traceid(struct coresight_device *csdev, struct coresight
 		return -EINVAL;
 	}
 
-	port_num = ctcu_get_active_port(sink, csdev);
+	port_num = coresight_get_in_port(sink, csdev);
 	if (port_num < 0)
 		return -EINVAL;
 
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 1ea882dffd70..5532ec82e82c 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);
 void 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 related	[flat|nested] 15+ messages in thread

* [PATCH v14 2/7] coresight: tmc: add create/clean functions for etr_buf_list
  2026-03-09  9:47 [PATCH v14 0/7] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
  2026-03-09  9:47 ` [PATCH v14 1/7] coresight: core: refactor ctcu_get_active_port and make it generic Jie Gan
@ 2026-03-09  9:47 ` Jie Gan
  2026-03-09 10:02   ` Suzuki K Poulose
  2026-03-09  9:47 ` [PATCH v14 3/7] coresight: tmc: introduce tmc_sysfs_ops to wrap sysfs read operations Jie Gan
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 15+ messages in thread
From: Jie Gan @ 2026-03-09  9:47 UTC (permalink / raw)
  To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
	Bjorn Andersson, Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree, Jie Gan, Mike Leach

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@linaro.org>
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  | 94 ++++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-tmc.h      | 17 +++++
 3 files changed, 112 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
index c89fe996af23..bac3278ef4dd 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 4dc1defe27a5..15c0874ff641 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1918,6 +1918,100 @@ 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 the allocated node from the list and free the extra buffer.
+ */
+void tmc_clean_etr_buf_list(struct tmc_drvdata *drvdata)
+{
+	struct etr_buf_node *nd, *next;
+
+	list_for_each_entry_safe(nd, next, &drvdata->etr_buf_list, node) {
+		if (nd->sysfs_buf == drvdata->sysfs_buf) {
+			if (coresight_get_mode(drvdata->csdev) == CS_MODE_DISABLED) {
+				drvdata->sysfs_buf = NULL;
+				tmc_free_etr_buf(nd->sysfs_buf);
+				nd->sysfs_buf = NULL;
+			}
+			list_del(&nd->node);
+			kfree(nd);
+		} else {
+			/* Free allocated buffers which are not utilized by ETR */
+			list_del(&nd->node);
+			tmc_free_etr_buf(nd->sysfs_buf);
+			nd->sysfs_buf = NULL;
+			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.
+ *
+ * Return 0 upon success and return the error number if fail.
+ */
+int tmc_create_etr_buf_list(struct tmc_drvdata *drvdata, int num_nodes)
+{
+	struct etr_buf_node *new_node;
+	struct etr_buf *sysfs_buf;
+	int i = 0, ret = 0;
+
+	/* We dont need a list if there is only one node */
+	if (num_nodes < 2)
+		return -EINVAL;
+
+	/* We expect that sysfs_buf in drvdata has already been allocated. */
+	if (drvdata->sysfs_buf) {
+		/* Directly insert the allocated sysfs_buf into the list first */
+		new_node = kzalloc_obj(*new_node, GFP_KERNEL);
+		if (IS_ERR(new_node))
+			return PTR_ERR(new_node);
+
+		new_node->sysfs_buf = drvdata->sysfs_buf;
+		new_node->is_free = false;
+		list_add(&new_node->node, &drvdata->etr_buf_list);
+		i++;
+	}
+
+	while (i < num_nodes) {
+		new_node = kzalloc_obj(*new_node, GFP_KERNEL);
+		if (IS_ERR(new_node)) {
+			ret = PTR_ERR(new_node);
+			break;
+		}
+
+		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;
+		}
+
+		/* We dont have a available sysfs_buf in drvdata, setup one */
+		if (!drvdata->sysfs_buf) {
+			drvdata->sysfs_buf = sysfs_buf;
+			new_node->is_free = false;
+		} else
+			new_node->is_free = true;
+
+		new_node->sysfs_buf = sysfs_buf;
+		list_add(&new_node->node, &drvdata->etr_buf_list);
+		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;
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 319a354ede9f..5ac07e8dd5ff 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -208,6 +208,19 @@ struct tmc_resrv_buf {
 	s64		len;
 };
 
+/**
+ * @sysfs_buf:	Allocated sysfs_buf.
+ * @is_free:	Indicates whether the buffer is free to choose.
+ * @pos:	Position of the buffer.
+ * @node:	Node in etr_buf_list.
+ */
+struct etr_buf_node {
+	struct etr_buf		*sysfs_buf;
+	bool			is_free;
+	loff_t			pos;
+	struct list_head	node;
+};
+
 /**
  * struct tmc_drvdata - specifics associated to an TMC component
  * @atclk:	optional clock for the core parts of the TMC.
@@ -245,6 +258,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 +289,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 +462,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 related	[flat|nested] 15+ messages in thread

* [PATCH v14 3/7] coresight: tmc: introduce tmc_sysfs_ops to wrap sysfs read operations
  2026-03-09  9:47 [PATCH v14 0/7] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
  2026-03-09  9:47 ` [PATCH v14 1/7] coresight: core: refactor ctcu_get_active_port and make it generic Jie Gan
  2026-03-09  9:47 ` [PATCH v14 2/7] coresight: tmc: add create/clean functions for etr_buf_list Jie Gan
@ 2026-03-09  9:47 ` Jie Gan
  2026-03-09  9:47 ` [PATCH v14 4/7] coresight: etr: add a new function to retrieve the CTCU device Jie Gan
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 15+ messages in thread
From: Jie Gan @ 2026-03-09  9:47 UTC (permalink / raw)
  To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
	Bjorn Andersson, Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree, Jie Gan, Mike Leach

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@linaro.org>
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 bac3278ef4dd..110eedde077f 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 5ac07e8dd5ff..69d55ee3895f 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -259,6 +259,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;
@@ -290,6 +291,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 related	[flat|nested] 15+ messages in thread

* [PATCH v14 4/7] coresight: etr: add a new function to retrieve the CTCU device
  2026-03-09  9:47 [PATCH v14 0/7] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
                   ` (2 preceding siblings ...)
  2026-03-09  9:47 ` [PATCH v14 3/7] coresight: tmc: introduce tmc_sysfs_ops to wrap sysfs read operations Jie Gan
@ 2026-03-09  9:47 ` Jie Gan
  2026-03-09  9:47 ` [PATCH v14 5/7] dt-bindings: arm: add an interrupt property for Coresight CTCU Jie Gan
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 15+ messages in thread
From: Jie Gan @ 2026-03-09  9:47 UTC (permalink / raw)
  To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
	Bjorn Andersson, Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree, Jie Gan

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 15c0874ff641..f4223215ef8d 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 69d55ee3895f..aaa443abe8e9 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -470,6 +470,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 related	[flat|nested] 15+ messages in thread

* [PATCH v14 5/7] dt-bindings: arm: add an interrupt property for Coresight CTCU
  2026-03-09  9:47 [PATCH v14 0/7] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
                   ` (3 preceding siblings ...)
  2026-03-09  9:47 ` [PATCH v14 4/7] coresight: etr: add a new function to retrieve the CTCU device Jie Gan
@ 2026-03-09  9:47 ` Jie Gan
  2026-03-09  9:47 ` [PATCH v14 6/7] coresight: ctcu: enable byte-cntr for TMC ETR devices Jie Gan
  2026-03-09  9:47 ` [PATCH v14 7/7] arm64: dts: qcom: lemans: add interrupts to CTCU device Jie Gan
  6 siblings, 0 replies; 15+ messages in thread
From: Jie Gan @ 2026-03-09  9:47 UTC (permalink / raw)
  To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
	Bjorn Andersson, Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree, Jie Gan, Krzysztof Kozlowski, Mike Leach

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@linaro.org>
Reviewed-by: Mike Leach <mike.leach@linaro.org>
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 e002f87361ad..2981001a7d7f 100644
--- a/Documentation/devicetree/bindings/arm/qcom,coresight-ctcu.yaml
+++ b/Documentation/devicetree/bindings/arm/qcom,coresight-ctcu.yaml
@@ -44,6 +44,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.
@@ -65,6 +70,8 @@ additionalProperties: false
 
 examples:
   - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
     ctcu@1001000 {
         compatible = "qcom,sa8775p-ctcu";
         reg = <0x1001000 0x1000>;
@@ -72,6 +79,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 related	[flat|nested] 15+ messages in thread

* [PATCH v14 6/7] coresight: ctcu: enable byte-cntr for TMC ETR devices
  2026-03-09  9:47 [PATCH v14 0/7] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
                   ` (4 preceding siblings ...)
  2026-03-09  9:47 ` [PATCH v14 5/7] dt-bindings: arm: add an interrupt property for Coresight CTCU Jie Gan
@ 2026-03-09  9:47 ` Jie Gan
  2026-03-09 12:43   ` Suzuki K Poulose
  2026-03-09  9:47 ` [PATCH v14 7/7] arm64: dts: qcom: lemans: add interrupts to CTCU device Jie Gan
  6 siblings, 1 reply; 15+ messages in thread
From: Jie Gan @ 2026-03-09  9:47 UTC (permalink / raw)
  To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
	Bjorn Andersson, Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree, Jie Gan

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.

Switching the sysfs_buf when current buffer is full or the timeout is
triggered and resets rrp and rwp registers after switched the buffer.
The synced buffer will become available for reading after the switch.

Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
 .../ABI/testing/sysfs-bus-coresight-devices-ctcu   |   8 +
 drivers/hwtracing/coresight/Makefile               |   2 +-
 .../hwtracing/coresight/coresight-ctcu-byte-cntr.c | 351 +++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-ctcu-core.c  | 103 +++++-
 drivers/hwtracing/coresight/coresight-ctcu.h       |  76 ++++-
 drivers/hwtracing/coresight/coresight-tmc-core.c   |   8 +-
 drivers/hwtracing/coresight/coresight-tmc-etr.c    |  18 ++
 drivers/hwtracing/coresight/coresight-tmc.h        |   4 +
 8 files changed, 555 insertions(+), 15 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..6ff1708fb944
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu
@@ -0,0 +1,8 @@
+What:           /sys/bus/coresight/devices/<ctcu-name>/irq_threshold[0:1]
+Date:           March 2026
+KernelVersion:  7.1
+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 byte-cntr IRQ register for the specified ETR device
+		based on its port number. An interrupt is generated when the data size
+		exceeds the value set in the IRQ register.
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..0bf738d6c283
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c
@@ -0,0 +1,351 @@
+// 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/of_irq.h>
+#include <linux/uaccess.h>
+
+#include "coresight-ctcu.h"
+#include "coresight-priv.h"
+#include "coresight-tmc.h"
+
+static irqreturn_t byte_cntr_handler(int irq, void *data)
+{
+	struct ctcu_byte_cntr *byte_cntr_data = (struct ctcu_byte_cntr *)data;
+
+	atomic_inc(&byte_cntr_data->irq_cnt);
+	wake_up(&byte_cntr_data->wq);
+
+	return IRQ_HANDLED;
+}
+
+static void ctcu_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);
+}
+
+static void ctcu_cfg_byte_cntr_reg(struct tmc_drvdata *drvdata, u32 val, u32 offset)
+{
+	struct ctcu_drvdata *ctcu_drvdata;
+	struct coresight_device *helper;
+
+	helper = tmc_etr_get_ctcu_device(drvdata);
+	if (!helper)
+		return;
+
+	ctcu_drvdata = dev_get_drvdata(helper->dev.parent);
+	/* A one value for IRQCTRL register represents 8 bytes */
+	ctcu_program_register(ctcu_drvdata, val / 8, offset);
+}
+
+static struct ctcu_byte_cntr *ctcu_get_byte_cntr_data(struct tmc_drvdata *drvdata)
+{
+	struct ctcu_byte_cntr *byte_cntr_data;
+	struct ctcu_drvdata *ctcu_drvdata;
+	struct coresight_device *helper;
+	int port;
+
+	helper = tmc_etr_get_ctcu_device(drvdata);
+	if (!helper)
+		return NULL;
+
+	port = coresight_get_in_port(drvdata->csdev, helper);
+	if (port < 0)
+		return NULL;
+
+	ctcu_drvdata = dev_get_drvdata(helper->dev.parent);
+	byte_cntr_data = &ctcu_drvdata->byte_cntr_data[port];
+	return byte_cntr_data;
+}
+
+static bool ctcu_byte_cntr_switch_buffer(struct tmc_drvdata *drvdata,
+					 struct ctcu_byte_cntr *byte_cntr_data)
+{
+	struct etr_buf_node *nd, *next, *curr_node, *picked_node;
+	struct etr_buf *curr_buf = drvdata->sysfs_buf;
+	bool found_free_buf = false;
+
+	if (WARN_ON(!drvdata || !byte_cntr_data))
+		return found_free_buf;
+
+	/* Stop the ETR before initiating the switch */
+	if (coresight_get_mode(drvdata->csdev) != CS_MODE_DISABLED)
+		tmc_etr_enable_disable_hw(drvdata, false);
+
+	list_for_each_entry_safe(nd, next, &drvdata->etr_buf_list, node) {
+		/* curr_buf is free for next round */
+		if (nd->sysfs_buf == curr_buf) {
+			nd->is_free = true;
+			curr_node = nd;
+		}
+
+		if (!found_free_buf && nd->is_free && nd->sysfs_buf != curr_buf) {
+			picked_node = nd;
+			found_free_buf = true;
+		}
+	}
+
+	if (found_free_buf) {
+		curr_node->pos = 0;
+		drvdata->buf_node = curr_node;
+		drvdata->sysfs_buf = picked_node->sysfs_buf;
+		drvdata->etr_buf = picked_node->sysfs_buf;
+		picked_node->is_free = false;
+		/* Reset irq_cnt for next etr_buf */
+		atomic_set(&byte_cntr_data->irq_cnt, 0);
+		/* Reset rrp and rwp when the system has switched the buffer*/
+		ctcu_reset_sysfs_buf(drvdata);
+		/* Restart the ETR once a free buffer is available */
+		if (coresight_get_mode(drvdata->csdev) != CS_MODE_DISABLED)
+			tmc_etr_enable_disable_hw(drvdata, true);
+	}
+
+	return found_free_buf;
+}
+
+/*
+ * 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 ctcu_byte_cntr_get_data(struct tmc_drvdata *drvdata, loff_t pos,
+				       size_t len, char **bufpp)
+{
+	struct etr_buf *sysfs_buf = drvdata->sysfs_buf;
+	struct device *dev = &drvdata->csdev->dev;
+	ssize_t actual, size = sysfs_buf->size;
+	struct ctcu_byte_cntr *byte_cntr_data;
+	size_t thresh_val;
+	atomic_t *irq_cnt;
+	int ret;
+
+	byte_cntr_data = ctcu_get_byte_cntr_data(drvdata);
+	if (!byte_cntr_data)
+		return -EINVAL;
+
+	thresh_val = byte_cntr_data->thresh_val;
+	irq_cnt = &byte_cntr_data->irq_cnt;
+
+wait_buffer:
+	if (!byte_cntr_data->reading_data) {
+		ret = wait_event_interruptible_timeout(byte_cntr_data->wq,
+				((atomic_read(irq_cnt) + 1) * thresh_val >= size) ||
+				!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) {
+			if (!ctcu_byte_cntr_switch_buffer(drvdata, byte_cntr_data)) {
+				dev_err(dev, "Switch buffer failed for the byte-cntr\n");
+				return -EINVAL;
+			}
+
+			byte_cntr_data->reading_data = true;
+		} else {
+			/*
+			 * TMC-ETR has been disabled, so directly reads data from
+			 * the drvdata->sysfs_buf.
+			 */
+			actual = etr_sysfs_ops.get_trace_data(drvdata, pos, len, bufpp);
+			if (actual > 0) {
+				byte_cntr_data->total_size += actual;
+				return actual;
+			}
+
+			/* Exit byte-cntr reading */
+			return 0;
+		}
+	}
+
+	/* Check the status of current etr_buf*/
+	if (atomic_read(irq_cnt) * thresh_val >= size)
+		dev_warn(dev, "Data overwrite happened\n");
+
+	pos = drvdata->buf_node->pos;
+	actual = etr_sysfs_ops.get_trace_data(drvdata, pos, len, bufpp);
+	if (actual <= 0) {
+		/* Reset flags upon reading is finished or failed */
+		byte_cntr_data->reading_data = false;
+		drvdata->buf_node = NULL;
+
+		/*
+		 * Nothing in the buffer, waiting for the next buffer
+		 * to be filled.
+		 */
+		if (actual == 0)
+			goto wait_buffer;
+	} else
+		byte_cntr_data->total_size += actual;
+
+	return actual;
+}
+
+static int ctcu_read_prepare_byte_cntr(struct tmc_drvdata *drvdata)
+{
+	struct ctcu_byte_cntr *byte_cntr_data;
+	unsigned long flags;
+	int ret = 0;
+
+	byte_cntr_data = ctcu_get_byte_cntr_data(drvdata);
+	if (!byte_cntr_data)
+		return -EINVAL;
+
+	/*
+	 * The threshold value must not exceed the buffer size.
+	 * A margin should be maintained between the two values to account
+	 * for the time gap between the interrupt and buffer switching.
+	 */
+	if (byte_cntr_data->thresh_val + SZ_16K >= drvdata->size) {
+		dev_err(&drvdata->csdev->dev, "The threshold value is too large\n");
+		return -EINVAL;
+	}
+
+	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (byte_cntr_data->reading) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
+	byte_cntr_data->reading = true;
+	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	/* Setup an available etr_buf_list for byte-cntr */
+	ret = tmc_create_etr_buf_list(drvdata, 2);
+	if (ret)
+		goto out;
+
+	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
+	atomic_set(&byte_cntr_data->irq_cnt, 0);
+	/* Configure the byte-cntr register to enable IRQ */
+	ctcu_cfg_byte_cntr_reg(drvdata, byte_cntr_data->thresh_val,
+			       byte_cntr_data->irq_ctrl_offset);
+	enable_irq_wake(byte_cntr_data->irq);
+	byte_cntr_data->total_size = 0;
+
+out_unlock:
+	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+out:
+	return ret;
+}
+
+static int ctcu_read_unprepare_byte_cntr(struct tmc_drvdata *drvdata)
+{
+	struct device *dev = &drvdata->csdev->dev;
+	struct ctcu_byte_cntr *byte_cntr_data;
+	unsigned long flags;
+
+	byte_cntr_data = ctcu_get_byte_cntr_data(drvdata);
+	if (!byte_cntr_data)
+		return -EINVAL;
+
+	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
+	/* Configure the byte-cntr register to disable IRQ */
+	ctcu_cfg_byte_cntr_reg(drvdata, 0, byte_cntr_data->irq_ctrl_offset);
+	disable_irq_wake(byte_cntr_data->irq);
+	byte_cntr_data->reading = false;
+	byte_cntr_data->reading_data = false;
+	drvdata->buf_node = NULL;
+	/* Restore the original sysfs_ops */
+	drvdata->sysfs_ops = &etr_sysfs_ops;
+	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	dev_dbg(dev, "send data total size:%llu bytes\n", byte_cntr_data->total_size);
+	tmc_clean_etr_buf_list(drvdata);
+
+	return 0;
+}
+
+const struct tmc_sysfs_ops byte_cntr_sysfs_ops = {
+	.read_prepare	= ctcu_read_prepare_byte_cntr,
+	.read_unprepare	= ctcu_read_unprepare_byte_cntr,
+	.get_trace_data	= ctcu_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 ctcu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+	struct coresight_device *sink = coresight_get_sink(path);
+	struct tmc_drvdata *tmc_drvdata = dev_get_drvdata(sink->dev.parent);
+	struct ctcu_byte_cntr *byte_cntr_data;
+	int port_num;
+
+	port_num = coresight_get_in_port(sink, csdev);
+	if (port_num < 0)
+		return;
+
+	byte_cntr_data = &drvdata->byte_cntr_data[port_num];
+	/* Don't start byte-cntr function when threshold is not set. */
+	if (!byte_cntr_data->thresh_val || byte_cntr_data->enable)
+		return;
+
+	guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
+	byte_cntr_data->enable = true;
+	byte_cntr_data->reading_data = false;
+	/* Replace with byte-cntr's sysfs_ops */
+	tmc_drvdata->sysfs_ops = &byte_cntr_sysfs_ops;
+}
+
+/* Stop the byte-cntr function when the path is disabled. */
+void ctcu_byte_cntr_stop(struct coresight_device *csdev, struct coresight_path *path)
+{
+	struct ctcu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+	struct coresight_device *sink = coresight_get_sink(path);
+	struct ctcu_byte_cntr *byte_cntr_data;
+	int port_num;
+
+	if (coresight_get_mode(sink) == CS_MODE_SYSFS)
+		return;
+
+	port_num = coresight_get_in_port(sink, csdev);
+	if (port_num < 0)
+		return;
+
+	byte_cntr_data = &drvdata->byte_cntr_data[port_num];
+	guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
+	byte_cntr_data->enable = false;
+}
+
+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;
+
+	for (i = 0; i < etr_num; i++) {
+		byte_cntr_data = &drvdata->byte_cntr_data[i];
+		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 | IRQF_SHARED,
+				       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;
+		init_waitqueue_head(&byte_cntr_data->wq);
+		raw_spin_lock_init(&byte_cntr_data->spin_lock);
+	}
+}
diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c
index e8720026c9e3..60f1db3ab70d 100644
--- a/drivers/hwtracing/coresight/coresight-ctcu-core.c
+++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c
@@ -15,6 +15,7 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
+#include <linux/sizes.h>
 
 #include "coresight-ctcu.h"
 #include "coresight-priv.h"
@@ -43,17 +44,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 +67,88 @@ static const struct ctcu_config sa8775p_cfgs = {
 	.num_etr_config	= ARRAY_SIZE(sa8775p_etr_cfgs),
 };
 
+void ctcu_program_register(struct ctcu_drvdata *drvdata, u32 val, u32 offset)
+{
+	CS_UNLOCK(drvdata->base);
+	ctcu_writel(drvdata, val, offset);
+	CS_LOCK(drvdata->base);
+}
+
+static ssize_t irq_threshold_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].thresh_val);
+}
+
+static ssize_t irq_threshold_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;
+
+	/* Threshold 0 disables the interruption. */
+	guard(raw_spinlock_irqsave)(&drvdata->spin_lock);
+	/* A small threshold will result in a large number of interruptions */
+	if (val && val < SZ_4K)
+		return -EINVAL;
+
+	if (drvdata->byte_cntr_data[port].irq_ctrl_offset)
+		drvdata->byte_cntr_data[port].thresh_val = val;
+
+	return size;
+}
+
+static umode_t irq_threshold_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;
+
+	if (drvdata && drvdata->byte_cntr_data[port].irq_ctrl_offset)
+		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_threshold_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 +227,15 @@ 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)
 {
+	ctcu_byte_cntr_start(csdev, path);
+
 	return ctcu_set_etr_traceid(csdev, path, true);
 }
 
 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 +286,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 +303,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);
diff --git a/drivers/hwtracing/coresight/coresight-ctcu.h b/drivers/hwtracing/coresight/coresight-ctcu.h
index e9594c38dd91..c52cf6a33d2e 100644
--- a/drivers/hwtracing/coresight/coresight-ctcu.h
+++ b/drivers/hwtracing/coresight/coresight-ctcu.h
@@ -5,19 +5,26 @@
 
 #ifndef _CORESIGHT_CTCU_H
 #define _CORESIGHT_CTCU_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)
+
 /**
  * struct ctcu_etr_config
  * @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.
+ * @irq_name:           IRQ name in dt node.
  */
 struct ctcu_etr_config {
 	const u32 atid_offset;
 	const u32 port_num;
+	const u32 irq_ctrl_offset;
 };
 
 struct ctcu_config {
@@ -25,15 +32,68 @@ 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
+ * @enable:		indicates that byte_cntr function is enabled or not.
+ * @reading:		indicates that byte-cntr reading is started.
+ * @reading_data:	indicates that byte-cntr is reading data from the buffer.
+ * @thresh_val:		threshold to trigger a interruption.
+ * @total_size:		total size of the transferred data.
+ * @irq:		allocated number of the IRQ.
+ * @irq_cnt:		IRQ count number for triggered interruptions.
+ * @wq:			waitqueue for reading data from ETR buffer.
+ * @spin_lock:		spinlock of byte_cntr_data.
+ * @irq_ctrl_offset:	offset to the BYTECNTVAL Register.
+ */
+struct ctcu_byte_cntr {
+	bool			enable;
+	bool                    reading;
+	bool			reading_data;
+	u32			thresh_val;
+	u64			total_size;
+	int			irq;
+	atomic_t		irq_cnt;
+	wait_queue_head_t	wq;
 	raw_spinlock_t		spin_lock;
-	u32			atid_offset[ETR_MAX_NUM];
+	u32			irq_ctrl_offset;
+};
+
+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];
+	u8				traceid_refcnt[ETR_MAX_NUM][CORESIGHT_TRACE_ID_RES_TOP];
 };
 
+/**
+ * struct ctcu_irq_thresh_attribute
+ * @attr:	The device attribute.
+ * @idx:	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_threshold##port, 0644, irq_threshold_show,	\
+		irq_threshold_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 110eedde077f..948ea864f2a1 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-core.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
@@ -293,7 +293,10 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
 		return -EFAULT;
 	}
 
-	*ppos += actual;
+	if (drvdata->buf_node)
+		drvdata->buf_node->pos += actual;
+	else
+		*ppos += actual;
 	dev_dbg(&drvdata->csdev->dev, "%zu bytes copied\n", actual);
 
 	return actual;
@@ -748,11 +751,12 @@ static const struct tmc_sysfs_ops etb_sysfs_ops = {
 	.get_trace_data	= tmc_etb_get_sysfs_trace,
 };
 
-static const struct tmc_sysfs_ops etr_sysfs_ops = {
+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,
 };
+EXPORT_SYMBOL_GPL(etr_sysfs_ops);
 
 static int __tmc_probe(struct device *dev, struct resource *res)
 {
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index f4223215ef8d..8896fb7a9ed3 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1185,6 +1185,10 @@ ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
 	ssize_t actual = len;
 	struct etr_buf *etr_buf = drvdata->sysfs_buf;
 
+	/* Reading the buffer from the buf_node if it exists*/
+	if (drvdata->buf_node)
+		etr_buf = drvdata->buf_node->sysfs_buf;
+
 	if (pos + actual > etr_buf->len)
 		actual = etr_buf->len - pos;
 	if (actual <= 0)
@@ -1248,6 +1252,20 @@ static void __tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 
 }
 
+/**
+ * tmc_etr_enable_disable_hw - enable/disable the ETR hw.
+ * @drvdata:	drvdata of the TMC device.
+ * @enable:	indicates enable/disable.
+ */
+void tmc_etr_enable_disable_hw(struct tmc_drvdata *drvdata, bool enable)
+{
+	if (enable)
+		__tmc_etr_enable_hw(drvdata);
+	else
+		__tmc_etr_disable_hw(drvdata);
+}
+EXPORT_SYMBOL_GPL(tmc_etr_enable_disable_hw);
+
 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 aaa443abe8e9..183c649b982c 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -260,6 +260,7 @@ struct etr_buf_node {
  *		 Used by ETR/ETF.
  * @etr_buf_list: List that is used to manage allocated etr_buf.
  * @sysfs_ops:	Read operations for the sysfs mode.
+ * @buf_node:	Available buffer_node for byte-cntr reading.
  */
 struct tmc_drvdata {
 	struct clk		*atclk;
@@ -292,6 +293,7 @@ struct tmc_drvdata {
 	struct tmc_resrv_buf	crash_mdata;
 	struct list_head        etr_buf_list;
 	const struct tmc_sysfs_ops	*sysfs_ops;
+	struct etr_buf_node	*buf_node;
 };
 
 /**
@@ -371,6 +373,7 @@ void tmc_etr_disable_hw(struct tmc_drvdata *drvdata);
 extern const struct coresight_ops tmc_etr_cs_ops;
 ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
 				loff_t pos, size_t len, char **bufpp);
+extern const struct tmc_sysfs_ops etr_sysfs_ops;
 
 
 #define TMC_REG_PAIR(name, lo_off, hi_off)				\
@@ -480,5 +483,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);
+void tmc_etr_enable_disable_hw(struct tmc_drvdata *drvdata, bool enable);
 
 #endif

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v14 7/7] arm64: dts: qcom: lemans: add interrupts to CTCU device
  2026-03-09  9:47 [PATCH v14 0/7] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
                   ` (5 preceding siblings ...)
  2026-03-09  9:47 ` [PATCH v14 6/7] coresight: ctcu: enable byte-cntr for TMC ETR devices Jie Gan
@ 2026-03-09  9:47 ` Jie Gan
  6 siblings, 0 replies; 15+ messages in thread
From: Jie Gan @ 2026-03-09  9:47 UTC (permalink / raw)
  To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
	Bjorn Andersson, Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree, Jie Gan, 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 808827b83553..e8a20789cd58 100644
--- a/arch/arm64/boot/dts/qcom/lemans.dtsi
+++ b/arch/arm64/boot/dts/qcom/lemans.dtsi
@@ -2800,6 +2800,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 related	[flat|nested] 15+ messages in thread

* Re: [PATCH v14 2/7] coresight: tmc: add create/clean functions for etr_buf_list
  2026-03-09  9:47 ` [PATCH v14 2/7] coresight: tmc: add create/clean functions for etr_buf_list Jie Gan
@ 2026-03-09 10:02   ` Suzuki K Poulose
  2026-03-09 10:27     ` Jie Gan
  0 siblings, 1 reply; 15+ messages in thread
From: Suzuki K Poulose @ 2026-03-09 10:02 UTC (permalink / raw)
  To: Jie Gan, Mike Leach, James Clark, Alexander Shishkin, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang, Bjorn Andersson,
	Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree

On 09/03/2026 09:47, Jie Gan wrote:
> 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@linaro.org>
> 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  | 94 ++++++++++++++++++++++++
>   drivers/hwtracing/coresight/coresight-tmc.h      | 17 +++++
>   3 files changed, 112 insertions(+)
> 
> diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
> index c89fe996af23..bac3278ef4dd 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 4dc1defe27a5..15c0874ff641 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
> +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
> @@ -1918,6 +1918,100 @@ 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 the allocated node from the list and free the extra buffer.
> + */
> +void tmc_clean_etr_buf_list(struct tmc_drvdata *drvdata)
> +{
...

> +}
> +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.
> + *
> + * Return 0 upon success and return the error number if fail.
> + */
> +int tmc_create_etr_buf_list(struct tmc_drvdata *drvdata, int num_nodes)
> +{

...


> +EXPORT_SYMBOL_GPL(tmc_create_etr_buf_list);

Given the above functions are "EXPORTED" please could you make sure that
the locking requirements are documented and asserted (lockdep_assert)
in the functions ?


Suzuki


> +
>   int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
>   {
>   	int ret = 0;
> diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
> index 319a354ede9f..5ac07e8dd5ff 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc.h
> +++ b/drivers/hwtracing/coresight/coresight-tmc.h
> @@ -208,6 +208,19 @@ struct tmc_resrv_buf {
>   	s64		len;
>   };
>   
> +/**
> + * @sysfs_buf:	Allocated sysfs_buf.
> + * @is_free:	Indicates whether the buffer is free to choose.
> + * @pos:	Position of the buffer.
> + * @node:	Node in etr_buf_list.
> + */
> +struct etr_buf_node {
> +	struct etr_buf		*sysfs_buf;
> +	bool			is_free;
> +	loff_t			pos;
> +	struct list_head	node;
> +};
> +
>   /**
>    * struct tmc_drvdata - specifics associated to an TMC component
>    * @atclk:	optional clock for the core parts of the TMC.
> @@ -245,6 +258,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 +289,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 +462,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
> 


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v14 2/7] coresight: tmc: add create/clean functions for etr_buf_list
  2026-03-09 10:02   ` Suzuki K Poulose
@ 2026-03-09 10:27     ` Jie Gan
  0 siblings, 0 replies; 15+ messages in thread
From: Jie Gan @ 2026-03-09 10:27 UTC (permalink / raw)
  To: Suzuki K Poulose, Jie Gan, Mike Leach, James Clark,
	Alexander Shishkin, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Tingwei Zhang, Bjorn Andersson, Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree



On 3/9/2026 6:02 PM, Suzuki K Poulose wrote:
> On 09/03/2026 09:47, Jie Gan wrote:
>> 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@linaro.org>
>> 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  | 94 ++++++++++++++ 
>> ++++++++++
>>   drivers/hwtracing/coresight/coresight-tmc.h      | 17 +++++
>>   3 files changed, 112 insertions(+)
>>
>> diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/ 
>> drivers/hwtracing/coresight/coresight-tmc-core.c
>> index c89fe996af23..bac3278ef4dd 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 4dc1defe27a5..15c0874ff641 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> @@ -1918,6 +1918,100 @@ 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 the allocated node from the list and free the extra buffer.
>> + */
>> +void tmc_clean_etr_buf_list(struct tmc_drvdata *drvdata)
>> +{
> ...
> 
>> +}
>> +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.
>> + *
>> + * Return 0 upon success and return the error number if fail.
>> + */
>> +int tmc_create_etr_buf_list(struct tmc_drvdata *drvdata, int num_nodes)
>> +{
> 
> ...
> 
> 
>> +EXPORT_SYMBOL_GPL(tmc_create_etr_buf_list);
> 
> Given the above functions are "EXPORTED" please could you make sure that
> the locking requirements are documented and asserted (lockdep_assert)
> in the functions ?
> 

Will checking the locking scenario here and asserted with 
lockdep_assert_held.

These two functions should be protected with tmc_drvdata->spinlock.

Thanks,
Jie

> 
> Suzuki
> 
> 
>> +
>>   int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
>>   {
>>       int ret = 0;
>> diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/ 
>> hwtracing/coresight/coresight-tmc.h
>> index 319a354ede9f..5ac07e8dd5ff 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc.h
>> +++ b/drivers/hwtracing/coresight/coresight-tmc.h
>> @@ -208,6 +208,19 @@ struct tmc_resrv_buf {
>>       s64        len;
>>   };
>> +/**
>> + * @sysfs_buf:    Allocated sysfs_buf.
>> + * @is_free:    Indicates whether the buffer is free to choose.
>> + * @pos:    Position of the buffer.
>> + * @node:    Node in etr_buf_list.
>> + */
>> +struct etr_buf_node {
>> +    struct etr_buf        *sysfs_buf;
>> +    bool            is_free;
>> +    loff_t            pos;
>> +    struct list_head    node;
>> +};
>> +
>>   /**
>>    * struct tmc_drvdata - specifics associated to an TMC component
>>    * @atclk:    optional clock for the core parts of the TMC.
>> @@ -245,6 +258,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 +289,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 +462,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
>>
> 
> 


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v14 6/7] coresight: ctcu: enable byte-cntr for TMC ETR devices
  2026-03-09  9:47 ` [PATCH v14 6/7] coresight: ctcu: enable byte-cntr for TMC ETR devices Jie Gan
@ 2026-03-09 12:43   ` Suzuki K Poulose
  2026-03-10  3:01     ` Jie Gan
  0 siblings, 1 reply; 15+ messages in thread
From: Suzuki K Poulose @ 2026-03-09 12:43 UTC (permalink / raw)
  To: Jie Gan, Mike Leach, James Clark, Alexander Shishkin, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang, Bjorn Andersson,
	Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree

On 09/03/2026 09:47, Jie Gan wrote:
> 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.
> 
> Switching the sysfs_buf when current buffer is full or the timeout is
> triggered and resets rrp and rwp registers after switched the buffer.
> The synced buffer will become available for reading after the switch.
> 
> Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
> ---
>   .../ABI/testing/sysfs-bus-coresight-devices-ctcu   |   8 +
>   drivers/hwtracing/coresight/Makefile               |   2 +-
>   .../hwtracing/coresight/coresight-ctcu-byte-cntr.c | 351 +++++++++++++++++++++
>   drivers/hwtracing/coresight/coresight-ctcu-core.c  | 103 +++++-
>   drivers/hwtracing/coresight/coresight-ctcu.h       |  76 ++++-
>   drivers/hwtracing/coresight/coresight-tmc-core.c   |   8 +-
>   drivers/hwtracing/coresight/coresight-tmc-etr.c    |  18 ++
>   drivers/hwtracing/coresight/coresight-tmc.h        |   4 +
>   8 files changed, 555 insertions(+), 15 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..6ff1708fb944
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu
> @@ -0,0 +1,8 @@
> +What:           /sys/bus/coresight/devices/<ctcu-name>/irq_threshold[0:1]
> +Date:           March 2026
> +KernelVersion:  7.1
> +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 byte-cntr IRQ register for the specified ETR device
> +		based on its port number. An interrupt is generated when the data size
> +		exceeds the value set in the IRQ register.
> 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..0bf738d6c283
> --- /dev/null
> +++ b/drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c
> @@ -0,0 +1,351 @@
> +// 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/of_irq.h>
> +#include <linux/uaccess.h>
> +
> +#include "coresight-ctcu.h"
> +#include "coresight-priv.h"
> +#include "coresight-tmc.h"
> +
> +static irqreturn_t byte_cntr_handler(int irq, void *data)
> +{
> +	struct ctcu_byte_cntr *byte_cntr_data = (struct ctcu_byte_cntr *)data;
> +
> +	atomic_inc(&byte_cntr_data->irq_cnt);
> +	wake_up(&byte_cntr_data->wq);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void ctcu_reset_sysfs_buf(struct tmc_drvdata *drvdata)

minor nit: This has nothing to do with the CTCU. For what it is worth,
it must be called, tmc_etr_reset_sysf_buf(). But more on this below,
and even do we need it, further below.

> +{
> +	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);

Could we not keep this function in the tmc-etr.c and invoke from here ?

> +}
> +
> +static void ctcu_cfg_byte_cntr_reg(struct tmc_drvdata *drvdata, u32 val, u32 offset)
> +{
> +	struct ctcu_drvdata *ctcu_drvdata;
> +	struct coresight_device *helper;
> +
> +	helper = tmc_etr_get_ctcu_device(drvdata);
> +	if (!helper)
> +		return;
> +
> +	ctcu_drvdata = dev_get_drvdata(helper->dev.parent);
> +	/* A one value for IRQCTRL register represents 8 bytes */
> +	ctcu_program_register(ctcu_drvdata, val / 8, offset);
> +}
> +
> +static struct ctcu_byte_cntr *ctcu_get_byte_cntr_data(struct tmc_drvdata *drvdata)
> +{
> +	struct ctcu_byte_cntr *byte_cntr_data;
> +	struct ctcu_drvdata *ctcu_drvdata;
> +	struct coresight_device *helper;
> +	int port;
> +
> +	helper = tmc_etr_get_ctcu_device(drvdata);
> +	if (!helper)
> +		return NULL;
> +



> +	port = coresight_get_in_port(drvdata->csdev, helper);
> +	if (port < 0)
> +		return NULL;
> +

Please validate that the port_num you get is valid for the CTCU ? That 
applies to all uses of this construct.

> +	ctcu_drvdata = dev_get_drvdata(helper->dev.parent);
> +	byte_cntr_data = &ctcu_drvdata->byte_cntr_data[port];
> +	return byte_cntr_data;



nit:
	return  &ctcu_drvdata->byte_cntr_data[port]; ?

Also, why not make this into a helper, as we seem to use this other 
places too ?


> +}
> +
> +static bool ctcu_byte_cntr_switch_buffer(struct tmc_drvdata *drvdata,
> +					 struct ctcu_byte_cntr *byte_cntr_data)
> +{
> +	struct etr_buf_node *nd, *next, *curr_node, *picked_node;
> +	struct etr_buf *curr_buf = drvdata->sysfs_buf;
> +	bool found_free_buf = false;
> +
> +	if (WARN_ON(!drvdata || !byte_cntr_data))
> +		return found_free_buf;
> +
> +	/* Stop the ETR before initiating the switch */
> +	if (coresight_get_mode(drvdata->csdev) != CS_MODE_DISABLED)
> +		tmc_etr_enable_disable_hw(drvdata, false);
> +
> +	list_for_each_entry_safe(nd, next, &drvdata->etr_buf_list, node) {
> +		/* curr_buf is free for next round */
> +		if (nd->sysfs_buf == curr_buf) {
> +			nd->is_free = true;
> +			curr_node = nd;

  ---8>--
> +		}
> +
> +		if (!found_free_buf && nd->is_free && nd->sysfs_buf != curr_buf) {

--<8---
		} else if (!found_free_buf && nd->is_free) {
		
> +			picked_node = nd;
> +			found_free_buf = true;
> +		}
> +	}
> +
> +	if (found_free_buf) {
> +		curr_node->pos = 0;
> +		drvdata->buf_node = curr_node;

Why are we adding something new into the drvdata ? Could we not extend
the sysfs_buf struct and add the "link" there ? That would make it much
more simpler ?

> +		drvdata->sysfs_buf = picked_node->sysfs_buf;
> +		drvdata->etr_buf = picked_node->sysfs_buf;
> +		picked_node->is_free = false;
> +		/* Reset irq_cnt for next etr_buf */
> +		atomic_set(&byte_cntr_data->irq_cnt, 0);
> +		/* Reset rrp and rwp when the system has switched the buffer*/
> +		ctcu_reset_sysfs_buf(drvdata);

Why do we do this ? Could we not leave this to the __tmc_etr_enable() 
below ?

> +		/* Restart the ETR once a free buffer is available */
> +		if (coresight_get_mode(drvdata->csdev) != CS_MODE_DISABLED)
> +			tmc_etr_enable_disable_hw(drvdata, true);
> +	}
> +
> +	return found_free_buf;
> +}
> +
> +/*
> + * 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 ctcu_byte_cntr_get_data(struct tmc_drvdata *drvdata, loff_t pos,
> +				       size_t len, char **bufpp)
> +{
> +	struct etr_buf *sysfs_buf = drvdata->sysfs_buf;
> +	struct device *dev = &drvdata->csdev->dev;
> +	ssize_t actual, size = sysfs_buf->size;
> +	struct ctcu_byte_cntr *byte_cntr_data;
> +	size_t thresh_val;
> +	atomic_t *irq_cnt;
> +	int ret;
> +
> +	byte_cntr_data = ctcu_get_byte_cntr_data(drvdata);
> +	if (!byte_cntr_data)
> +		return -EINVAL;
> +
> +	thresh_val = byte_cntr_data->thresh_val;
> +	irq_cnt = &byte_cntr_data->irq_cnt;
> +
> +wait_buffer:
> +	if (!byte_cntr_data->reading_data) {
> +		ret = wait_event_interruptible_timeout(byte_cntr_data->wq,
> +				((atomic_read(irq_cnt) + 1) * thresh_val >= size) ||
> +				!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) {
> +			if (!ctcu_byte_cntr_switch_buffer(drvdata, byte_cntr_data)) {
> +				dev_err(dev, "Switch buffer failed for the byte-cntr\n");
> +				return -EINVAL;
> +			}
> +
> +			byte_cntr_data->reading_data = true;
> +		} else {
> +			/*
> +			 * TMC-ETR has been disabled, so directly reads data from
> +			 * the drvdata->sysfs_buf.
> +			 */
> +			actual = etr_sysfs_ops.get_trace_data(drvdata, pos, len, bufpp);
> +			if (actual > 0) {
> +				byte_cntr_data->total_size += actual;
> +				return actual;
> +			}
> +
> +			/* Exit byte-cntr reading */
> +			return 0;
> +		}
> +	}
> +
> +	/* Check the status of current etr_buf*/
> +	if (atomic_read(irq_cnt) * thresh_val >= size)
> +		dev_warn(dev, "Data overwrite happened\n");
> +
> +	pos = drvdata->buf_node->pos;
> +	actual = etr_sysfs_ops.get_trace_data(drvdata, pos, len, bufpp);
> +	if (actual <= 0) {
> +		/* Reset flags upon reading is finished or failed */
> +		byte_cntr_data->reading_data = false;
> +		drvdata->buf_node = NULL;
> +
> +		/*
> +		 * Nothing in the buffer, waiting for the next buffer
> +		 * to be filled.
> +		 */
> +		if (actual == 0)
> +			goto wait_buffer;
> +	} else
> +		byte_cntr_data->total_size += actual;
> +
> +	return actual;
> +}
> +
> +static int ctcu_read_prepare_byte_cntr(struct tmc_drvdata *drvdata)

Please use "tmc_data" or even "etr_drvdata" to make it easier to read
the code below

> +{
> +	struct ctcu_byte_cntr *byte_cntr_data;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	byte_cntr_data = ctcu_get_byte_cntr_data(drvdata);
> +	if (!byte_cntr_data)
> +		return -EINVAL;
> +
> +	/*
> +	 * The threshold value must not exceed the buffer size.
> +	 * A margin should be maintained between the two values to account
> +	 * for the time gap between the interrupt and buffer switching.
> +	 */
> +	if (byte_cntr_data->thresh_val + SZ_16K >= drvdata->size) {

Whats the magic number SZ_16K ? Comment could explain it.

> +		dev_err(&drvdata->csdev->dev, "The threshold value is too large\n");

Could the threshold be a percentage of the size used by the ETR (You 
could always align it to the nearest PAGE_SIZE)? That would make it
convenient for people to change the ETR size and not  having to bother
about moving the threshold value with the ETR size.

> +		return -EINVAL;
> +	}
> +
> +	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
> +	if (byte_cntr_data->reading) {
> +		ret = -EBUSY;
> +		goto out_unlock;
> +	}
> +
> +	byte_cntr_data->reading = true;
> +	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);

All of this can be avoided by retaining the etr_sysf_ops in TMC-> and
calling into the CTCU after basic checks.

> +	/* Setup an available etr_buf_list for byte-cntr */
> +	ret = tmc_create_etr_buf_list(drvdata, 2);
> +	if (ret)
> +		goto out;
> +
> +	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
> +	atomic_set(&byte_cntr_data->irq_cnt, 0);
> +	/* Configure the byte-cntr register to enable IRQ */
> +	ctcu_cfg_byte_cntr_reg(drvdata, byte_cntr_data->thresh_val,
> +			       byte_cntr_data->irq_ctrl_offset);

Is there no way of going back to the ctcu_drvdata from byte_cntr_data ?
For a starter, we could easily store the ctcu_drvdata in byte_cntr_data
and always write straight to the device, rather than always going to the
ETR drvdata in ctcu_cfg_byte_cntr_reg() ?

> +	enable_irq_wake(byte_cntr_data->irq);
> +	byte_cntr_data->total_size = 0;
> +
> +out_unlock:
> +	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +
> +out:
> +	return ret;
> +}
> +
> +static int ctcu_read_unprepare_byte_cntr(struct tmc_drvdata *drvdata)

Same as above. name

> +{
> +	struct device *dev = &drvdata->csdev->dev;
> +	struct ctcu_byte_cntr *byte_cntr_data;
> +	unsigned long flags;
> +
> +	byte_cntr_data = ctcu_get_byte_cntr_data(drvdata);
> +	if (!byte_cntr_data)
> +		return -EINVAL;
> +
> +	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
> +	/* Configure the byte-cntr register to disable IRQ */
> +	ctcu_cfg_byte_cntr_reg(drvdata, 0, byte_cntr_data->irq_ctrl_offset);


> +	disable_irq_wake(byte_cntr_data->irq);
> +	byte_cntr_data->reading = false;
> +	byte_cntr_data->reading_data = false;
> +	drvdata->buf_node = NULL;


> +	/* Restore the original sysfs_ops */
> +	drvdata->sysfs_ops = &etr_sysfs_ops;

Please see if you can avoid it.

> +	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +	dev_dbg(dev, "send data total size:%llu bytes\n", byte_cntr_data->total_size);
> +	tmc_clean_etr_buf_list(drvdata);
> +
> +	return 0;
> +}
> +
> +const struct tmc_sysfs_ops byte_cntr_sysfs_ops = {
> +	.read_prepare	= ctcu_read_prepare_byte_cntr,
> +	.read_unprepare	= ctcu_read_unprepare_byte_cntr,
> +	.get_trace_data	= ctcu_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 ctcu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> +	struct coresight_device *sink = coresight_get_sink(path);
> +	struct tmc_drvdata *tmc_drvdata = dev_get_drvdata(sink->dev.parent);
> +	struct ctcu_byte_cntr *byte_cntr_data;
> +	int port_num;
> +
> +	port_num = coresight_get_in_port(sink, csdev);
> +	if (port_num < 0)
> +		return;
> +
> +	byte_cntr_data = &drvdata->byte_cntr_data[port_num];

instance 2

> +	/* Don't start byte-cntr function when threshold is not set. */
> +	if (!byte_cntr_data->thresh_val || byte_cntr_data->enable)
> +		return;
> +
> +	guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
> +	byte_cntr_data->enable = true;
> +	byte_cntr_data->reading_data = false;
> +	/* Replace with byte-cntr's sysfs_ops */
> +	tmc_drvdata->sysfs_ops = &byte_cntr_sysfs_ops;

Why are we hacking this ? This looks *very ugly* and we are doing this 
without any locks at the tmc-etr driver !!! ?? Could we not call out
from the etr_sysf_ops into the CTCU ops based some checks ?
Anyways, please don't change anything in the tmc_drvdata from this
driver, especially not the "operations".

Also the locking seems inconsistent. Here we use byte_cntr_data->lock.
But in ctcu_read_unprepare_byte_cntr(), we use the TMC-ETR spinlock 
:facepalm:



> +}
> +
> +/* Stop the byte-cntr function when the path is disabled. */
> +void ctcu_byte_cntr_stop(struct coresight_device *csdev, struct coresight_path *path)
> +{
> +	struct ctcu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> +	struct coresight_device *sink = coresight_get_sink(path);
> +	struct ctcu_byte_cntr *byte_cntr_data;
> +	int port_num;
> +
> +	if (coresight_get_mode(sink) == CS_MODE_SYSFS)
> +		return;
> + 
> +	port_num = coresight_get_in_port(sink, csdev);
> +	if (port_num < 0)
> +		return;
> +
> +	byte_cntr_data = &drvdata->byte_cntr_data[port_num];

instance 3




> +	guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
> +	byte_cntr_data->enable = false;
> +}
> +
> +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;
> +
> +	for (i = 0; i < etr_num; i++) {
> +		byte_cntr_data = &drvdata->byte_cntr_data[i];
> +		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 | IRQF_SHARED,
> +				       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;
> +		init_waitqueue_head(&byte_cntr_data->wq);
> +		raw_spin_lock_init(&byte_cntr_data->spin_lock);
> +	}
> +}
> diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c
> index e8720026c9e3..60f1db3ab70d 100644
> --- a/drivers/hwtracing/coresight/coresight-ctcu-core.c
> +++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c
> @@ -15,6 +15,7 @@
>   #include <linux/platform_device.h>
>   #include <linux/pm_runtime.h>
>   #include <linux/slab.h>
> +#include <linux/sizes.h>
>   
>   #include "coresight-ctcu.h"
>   #include "coresight-priv.h"
> @@ -43,17 +44,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 +67,88 @@ static const struct ctcu_config sa8775p_cfgs = {
>   	.num_etr_config	= ARRAY_SIZE(sa8775p_etr_cfgs),
>   };
>   
> +void ctcu_program_register(struct ctcu_drvdata *drvdata, u32 val, u32 offset)
> +{
> +	CS_UNLOCK(drvdata->base);
> +	ctcu_writel(drvdata, val, offset);
> +	CS_LOCK(drvdata->base);
> +}
> +
> +static ssize_t irq_threshold_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].thresh_val);
> +}
> +
> +static ssize_t irq_threshold_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;
> +
> +	/* Threshold 0 disables the interruption. */
> +	guard(raw_spinlock_irqsave)(&drvdata->spin_lock);
> +	/* A small threshold will result in a large number of interruptions */
> +	if (val && val < SZ_4K)
> +		return -EINVAL;
> +
> +	if (drvdata->byte_cntr_data[port].irq_ctrl_offset)
> +		drvdata->byte_cntr_data[port].thresh_val = val;
> +
> +	return size;
> +}
> +
> +static umode_t irq_threshold_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;
> +
> +	if (drvdata && drvdata->byte_cntr_data[port].irq_ctrl_offset)
> +		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_threshold_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 +227,15 @@ 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)
>   {
> +	ctcu_byte_cntr_start(csdev, path);
> +
>   	return ctcu_set_etr_traceid(csdev, path, true);
>   }
>   
>   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 +286,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 +303,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);
> diff --git a/drivers/hwtracing/coresight/coresight-ctcu.h b/drivers/hwtracing/coresight/coresight-ctcu.h
> index e9594c38dd91..c52cf6a33d2e 100644
> --- a/drivers/hwtracing/coresight/coresight-ctcu.h
> +++ b/drivers/hwtracing/coresight/coresight-ctcu.h
> @@ -5,19 +5,26 @@
>   
>   #ifndef _CORESIGHT_CTCU_H
>   #define _CORESIGHT_CTCU_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)
> +
>   /**
>    * struct ctcu_etr_config
>    * @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.
> + * @irq_name:           IRQ name in dt node.
>    */
>   struct ctcu_etr_config {
>   	const u32 atid_offset;
>   	const u32 port_num;
> +	const u32 irq_ctrl_offset;
>   };
>   
>   struct ctcu_config {
> @@ -25,15 +32,68 @@ 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
> + * @enable:		indicates that byte_cntr function is enabled or not.
> + * @reading:		indicates that byte-cntr reading is started.
> + * @reading_data:	indicates that byte-cntr is reading data from the buffer.
> + * @thresh_val:		threshold to trigger a interruption.
> + * @total_size:		total size of the transferred data.
> + * @irq:		allocated number of the IRQ.
> + * @irq_cnt:		IRQ count number for triggered interruptions.
> + * @wq:			waitqueue for reading data from ETR buffer.
> + * @spin_lock:		spinlock of byte_cntr_data.
> + * @irq_ctrl_offset:	offset to the BYTECNTVAL Register.
> + */
> +struct ctcu_byte_cntr {
> +	bool			enable;
> +	bool                    reading;
> +	bool			reading_data;
> +	u32			thresh_val;
> +	u64			total_size;
> +	int			irq;
> +	atomic_t		irq_cnt;
> +	wait_queue_head_t	wq;
>   	raw_spinlock_t		spin_lock;
> -	u32			atid_offset[ETR_MAX_NUM];
> +	u32			irq_ctrl_offset;
> +};
> +
> +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];
> +	u8				traceid_refcnt[ETR_MAX_NUM][CORESIGHT_TRACE_ID_RES_TOP];
>   };
>   
> +/**
> + * struct ctcu_irq_thresh_attribute
> + * @attr:	The device attribute.
> + * @idx:	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_threshold##port, 0644, irq_threshold_show,	\
> +		irq_threshold_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 110eedde077f..948ea864f2a1 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc-core.c
> +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
> @@ -293,7 +293,10 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
>   		return -EFAULT;
>   	}
>   
> -	*ppos += actual;
> +	if (drvdata->buf_node)
> +		drvdata->buf_node->pos += actual;
> +	else
> +		*ppos += actual;
>   	dev_dbg(&drvdata->csdev->dev, "%zu bytes copied\n", actual);
>   
>   	return actual;
> @@ -748,11 +751,12 @@ static const struct tmc_sysfs_ops etb_sysfs_ops = {
>   	.get_trace_data	= tmc_etb_get_sysfs_trace,
>   };
>   
> -static const struct tmc_sysfs_ops etr_sysfs_ops = {
> +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,
>   };
> +EXPORT_SYMBOL_GPL(etr_sysfs_ops);
>   
>   static int __tmc_probe(struct device *dev, struct resource *res)
>   {
> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
> index f4223215ef8d..8896fb7a9ed3 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
> +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
> @@ -1185,6 +1185,10 @@ ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
>   	ssize_t actual = len;
>   	struct etr_buf *etr_buf = drvdata->sysfs_buf;
>   
> +	/* Reading the buffer from the buf_node if it exists*/
> +	if (drvdata->buf_node)
> +		etr_buf = drvdata->buf_node->sysfs_buf;
> +
>   	if (pos + actual > etr_buf->len)
>   		actual = etr_buf->len - pos;
>   	if (actual <= 0)
> @@ -1248,6 +1252,20 @@ static void __tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
>   
>   }
>   
> +/**
> + * tmc_etr_enable_disable_hw - enable/disable the ETR hw.
> + * @drvdata:	drvdata of the TMC device.
> + * @enable:	indicates enable/disable.
> + */
> +void tmc_etr_enable_disable_hw(struct tmc_drvdata *drvdata, bool enable)
> +{
> +	if (enable)
> +		__tmc_etr_enable_hw(drvdata);
> +	else
> +		__tmc_etr_disable_hw(drvdata);
> +}
> +EXPORT_SYMBOL_GPL(tmc_etr_enable_disable_hw);
> +
>   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 aaa443abe8e9..183c649b982c 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc.h
> +++ b/drivers/hwtracing/coresight/coresight-tmc.h
> @@ -260,6 +260,7 @@ struct etr_buf_node {
>    *		 Used by ETR/ETF.
>    * @etr_buf_list: List that is used to manage allocated etr_buf.
>    * @sysfs_ops:	Read operations for the sysfs mode.
> + * @buf_node:	Available buffer_node for byte-cntr reading.
>    */
>   struct tmc_drvdata {
>   	struct clk		*atclk;
> @@ -292,6 +293,7 @@ struct tmc_drvdata {
>   	struct tmc_resrv_buf	crash_mdata;
>   	struct list_head        etr_buf_list;
>   	const struct tmc_sysfs_ops	*sysfs_ops;
> +	struct etr_buf_node	*buf_node;
>   };
>   
>   /**
> @@ -371,6 +373,7 @@ void tmc_etr_disable_hw(struct tmc_drvdata *drvdata);
>   extern const struct coresight_ops tmc_etr_cs_ops;
>   ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
>   				loff_t pos, size_t len, char **bufpp);
> +extern const struct tmc_sysfs_ops etr_sysfs_ops;
>   
>   
>   #define TMC_REG_PAIR(name, lo_off, hi_off)				\
> @@ -480,5 +483,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);
> +void tmc_etr_enable_disable_hw(struct tmc_drvdata *drvdata, bool enable);
>   
>   #endif
> 


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v14 6/7] coresight: ctcu: enable byte-cntr for TMC ETR devices
  2026-03-09 12:43   ` Suzuki K Poulose
@ 2026-03-10  3:01     ` Jie Gan
  2026-03-10  9:15       ` Suzuki K Poulose
  2026-03-11  8:02       ` Jie Gan
  0 siblings, 2 replies; 15+ messages in thread
From: Jie Gan @ 2026-03-10  3:01 UTC (permalink / raw)
  To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
	Bjorn Andersson, Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree



On 3/9/2026 8:43 PM, Suzuki K Poulose wrote:
> On 09/03/2026 09:47, Jie Gan wrote:
>> 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.
>>
>> Switching the sysfs_buf when current buffer is full or the timeout is
>> triggered and resets rrp and rwp registers after switched the buffer.
>> The synced buffer will become available for reading after the switch.
>>
>> Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
>> ---
>>   .../ABI/testing/sysfs-bus-coresight-devices-ctcu   |   8 +
>>   drivers/hwtracing/coresight/Makefile               |   2 +-
>>   .../hwtracing/coresight/coresight-ctcu-byte-cntr.c | 351 +++++++++++ 
>> ++++++++++
>>   drivers/hwtracing/coresight/coresight-ctcu-core.c  | 103 +++++-
>>   drivers/hwtracing/coresight/coresight-ctcu.h       |  76 ++++-
>>   drivers/hwtracing/coresight/coresight-tmc-core.c   |   8 +-
>>   drivers/hwtracing/coresight/coresight-tmc-etr.c    |  18 ++
>>   drivers/hwtracing/coresight/coresight-tmc.h        |   4 +
>>   8 files changed, 555 insertions(+), 15 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..6ff1708fb944
>> --- /dev/null
>> +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu
>> @@ -0,0 +1,8 @@
>> +What:           /sys/bus/coresight/devices/<ctcu-name>/ 
>> irq_threshold[0:1]
>> +Date:           March 2026
>> +KernelVersion:  7.1
>> +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 byte-cntr IRQ register for the specified 
>> ETR device
>> +        based on its port number. An interrupt is generated when the 
>> data size
>> +        exceeds the value set in the IRQ register.
>> 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..0bf738d6c283
>> --- /dev/null
>> +++ b/drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c
>> @@ -0,0 +1,351 @@
>> +// 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/of_irq.h>
>> +#include <linux/uaccess.h>
>> +
>> +#include "coresight-ctcu.h"
>> +#include "coresight-priv.h"
>> +#include "coresight-tmc.h"
>> +
>> +static irqreturn_t byte_cntr_handler(int irq, void *data)
>> +{
>> +    struct ctcu_byte_cntr *byte_cntr_data = (struct ctcu_byte_cntr 
>> *)data;
>> +
>> +    atomic_inc(&byte_cntr_data->irq_cnt);
>> +    wake_up(&byte_cntr_data->wq);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>> +static void ctcu_reset_sysfs_buf(struct tmc_drvdata *drvdata)
> 
> minor nit: This has nothing to do with the CTCU. For what it is worth,
> it must be called, tmc_etr_reset_sysf_buf(). But more on this below,
> and even do we need it, further below.
> 
>> +{
>> +    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);
> 
> Could we not keep this function in the tmc-etr.c and invoke from here ?
> 

Sure, will move the function tmc-etr.c

>> +}
>> +
>> +static void ctcu_cfg_byte_cntr_reg(struct tmc_drvdata *drvdata, u32 
>> val, u32 offset)
>> +{
>> +    struct ctcu_drvdata *ctcu_drvdata;
>> +    struct coresight_device *helper;
>> +
>> +    helper = tmc_etr_get_ctcu_device(drvdata);
>> +    if (!helper)
>> +        return;
>> +
>> +    ctcu_drvdata = dev_get_drvdata(helper->dev.parent);
>> +    /* A one value for IRQCTRL register represents 8 bytes */
>> +    ctcu_program_register(ctcu_drvdata, val / 8, offset);
>> +}
>> +
>> +static struct ctcu_byte_cntr *ctcu_get_byte_cntr_data(struct 
>> tmc_drvdata *drvdata)
>> +{
>> +    struct ctcu_byte_cntr *byte_cntr_data;
>> +    struct ctcu_drvdata *ctcu_drvdata;
>> +    struct coresight_device *helper;
>> +    int port;
>> +
>> +    helper = tmc_etr_get_ctcu_device(drvdata);
>> +    if (!helper)
>> +        return NULL;
>> +
> 
> 
> 
>> +    port = coresight_get_in_port(drvdata->csdev, helper);
>> +    if (port < 0)
>> +        return NULL;
>> +
> 
> Please validate that the port_num you get is valid for the CTCU ? That 
> applies to all uses of this construct.
> 

Will validate it before using.

>> +    ctcu_drvdata = dev_get_drvdata(helper->dev.parent);
>> +    byte_cntr_data = &ctcu_drvdata->byte_cntr_data[port];
>> +    return byte_cntr_data;
> 
> 
> 
> nit:
>      return  &ctcu_drvdata->byte_cntr_data[port]; ?
> 
> Also, why not make this into a helper, as we seem to use this other 
> places too ?
> 

Didnt get the point here. We may run more than one ETR devices 
concurrently. So we should get the proper byte_cntr_data according to 
the port number at runtime.

> 
>> +}
>> +
>> +static bool ctcu_byte_cntr_switch_buffer(struct tmc_drvdata *drvdata,
>> +                     struct ctcu_byte_cntr *byte_cntr_data)
>> +{
>> +    struct etr_buf_node *nd, *next, *curr_node, *picked_node;
>> +    struct etr_buf *curr_buf = drvdata->sysfs_buf;
>> +    bool found_free_buf = false;
>> +
>> +    if (WARN_ON(!drvdata || !byte_cntr_data))
>> +        return found_free_buf;
>> +
>> +    /* Stop the ETR before initiating the switch */
>> +    if (coresight_get_mode(drvdata->csdev) != CS_MODE_DISABLED)
>> +        tmc_etr_enable_disable_hw(drvdata, false);
>> +
>> +    list_for_each_entry_safe(nd, next, &drvdata->etr_buf_list, node) {
>> +        /* curr_buf is free for next round */
>> +        if (nd->sysfs_buf == curr_buf) {
>> +            nd->is_free = true;
>> +            curr_node = nd;
> 
>   ---8>--
>> +        }
>> +
>> +        if (!found_free_buf && nd->is_free && nd->sysfs_buf != 
>> curr_buf) {
> 
> --<8---
>          } else if (!found_free_buf && nd->is_free) {
> 

will fix it.

>> +            picked_node = nd;
>> +            found_free_buf = true;
>> +        }
>> +    }
>> +
>> +    if (found_free_buf) {
>> +        curr_node->pos = 0;
>> +        drvdata->buf_node = curr_node;
> 
> Why are we adding something new into the drvdata ? Could we not extend
> the sysfs_buf struct and add the "link" there ? That would make it much
> more simpler ?
> 
>> +        drvdata->sysfs_buf = picked_node->sysfs_buf;
>> +        drvdata->etr_buf = picked_node->sysfs_buf;
>> +        picked_node->is_free = false;
>> +        /* Reset irq_cnt for next etr_buf */
>> +        atomic_set(&byte_cntr_data->irq_cnt, 0);
>> +        /* Reset rrp and rwp when the system has switched the buffer*/
>> +        ctcu_reset_sysfs_buf(drvdata);
> 
> Why do we do this ? Could we not leave this to the __tmc_etr_enable() 
> below ?

Will fix it.

> 
>> +        /* Restart the ETR once a free buffer is available */
>> +        if (coresight_get_mode(drvdata->csdev) != CS_MODE_DISABLED)
>> +            tmc_etr_enable_disable_hw(drvdata, true);
>> +    }
>> +
>> +    return found_free_buf;
>> +}
>> +
>> +/*
>> + * 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 ctcu_byte_cntr_get_data(struct tmc_drvdata *drvdata, 
>> loff_t pos,
>> +                       size_t len, char **bufpp)
>> +{
>> +    struct etr_buf *sysfs_buf = drvdata->sysfs_buf;
>> +    struct device *dev = &drvdata->csdev->dev;
>> +    ssize_t actual, size = sysfs_buf->size;
>> +    struct ctcu_byte_cntr *byte_cntr_data;
>> +    size_t thresh_val;
>> +    atomic_t *irq_cnt;
>> +    int ret;
>> +
>> +    byte_cntr_data = ctcu_get_byte_cntr_data(drvdata);
>> +    if (!byte_cntr_data)
>> +        return -EINVAL;
>> +
>> +    thresh_val = byte_cntr_data->thresh_val;
>> +    irq_cnt = &byte_cntr_data->irq_cnt;
>> +
>> +wait_buffer:
>> +    if (!byte_cntr_data->reading_data) {
>> +        ret = wait_event_interruptible_timeout(byte_cntr_data->wq,
>> +                ((atomic_read(irq_cnt) + 1) * thresh_val >= size) ||
>> +                !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) {
>> +            if (!ctcu_byte_cntr_switch_buffer(drvdata, 
>> byte_cntr_data)) {
>> +                dev_err(dev, "Switch buffer failed for the byte- 
>> cntr\n");
>> +                return -EINVAL;
>> +            }
>> +
>> +            byte_cntr_data->reading_data = true;
>> +        } else {
>> +            /*
>> +             * TMC-ETR has been disabled, so directly reads data from
>> +             * the drvdata->sysfs_buf.
>> +             */
>> +            actual = etr_sysfs_ops.get_trace_data(drvdata, pos, len, 
>> bufpp);
>> +            if (actual > 0) {
>> +                byte_cntr_data->total_size += actual;
>> +                return actual;
>> +            }
>> +
>> +            /* Exit byte-cntr reading */
>> +            return 0;
>> +        }
>> +    }
>> +
>> +    /* Check the status of current etr_buf*/
>> +    if (atomic_read(irq_cnt) * thresh_val >= size)
>> +        dev_warn(dev, "Data overwrite happened\n");
>> +
>> +    pos = drvdata->buf_node->pos;
>> +    actual = etr_sysfs_ops.get_trace_data(drvdata, pos, len, bufpp);
>> +    if (actual <= 0) {
>> +        /* Reset flags upon reading is finished or failed */
>> +        byte_cntr_data->reading_data = false;
>> +        drvdata->buf_node = NULL;
>> +
>> +        /*
>> +         * Nothing in the buffer, waiting for the next buffer
>> +         * to be filled.
>> +         */
>> +        if (actual == 0)
>> +            goto wait_buffer;
>> +    } else
>> +        byte_cntr_data->total_size += actual;
>> +
>> +    return actual;
>> +}
>> +
>> +static int ctcu_read_prepare_byte_cntr(struct tmc_drvdata *drvdata)
> 
> Please use "tmc_data" or even "etr_drvdata" to make it easier to read
> the code below

Will fix it.

> 
>> +{
>> +    struct ctcu_byte_cntr *byte_cntr_data;
>> +    unsigned long flags;
>> +    int ret = 0;
>> +
>> +    byte_cntr_data = ctcu_get_byte_cntr_data(drvdata);
>> +    if (!byte_cntr_data)
>> +        return -EINVAL;
>> +
>> +    /*
>> +     * The threshold value must not exceed the buffer size.
>> +     * A margin should be maintained between the two values to account
>> +     * for the time gap between the interrupt and buffer switching.
>> +     */
>> +    if (byte_cntr_data->thresh_val + SZ_16K >= drvdata->size) {
> 
> Whats the magic number SZ_16K ? Comment could explain it.

will add comment if still need it in next version.

> 
>> +        dev_err(&drvdata->csdev->dev, "The threshold value is too 
>> large\n");
> 
> Could the threshold be a percentage of the size used by the ETR (You 
> could always align it to the nearest PAGE_SIZE)? That would make it
> convenient for people to change the ETR size and not  having to bother
> about moving the threshold value with the ETR size.

So we just enable/disable the byte-cntr function via sysfs node. Let the 
ETR buffer size to determine the threshoild value?

> 
>> +        return -EINVAL;
>> +    }
>> +
>> +    raw_spin_lock_irqsave(&drvdata->spinlock, flags);
>> +    if (byte_cntr_data->reading) {
>> +        ret = -EBUSY;
>> +        goto out_unlock;
>> +    }
>> +
>> +    byte_cntr_data->reading = true;
>> +    raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
> 
> All of this can be avoided by retaining the etr_sysf_ops in TMC-> and
> calling into the CTCU after basic checks.

Understood. we can rely on the tmc->reading check, jump to CTCU ops 
after proper check.

> 
>> +    /* Setup an available etr_buf_list for byte-cntr */
>> +    ret = tmc_create_etr_buf_list(drvdata, 2);
>> +    if (ret)
>> +        goto out;
>> +
>> +    raw_spin_lock_irqsave(&drvdata->spinlock, flags);
>> +    atomic_set(&byte_cntr_data->irq_cnt, 0);
>> +    /* Configure the byte-cntr register to enable IRQ */
>> +    ctcu_cfg_byte_cntr_reg(drvdata, byte_cntr_data->thresh_val,
>> +                   byte_cntr_data->irq_ctrl_offset);
> 
> Is there no way of going back to the ctcu_drvdata from byte_cntr_data ?
> For a starter, we could easily store the ctcu_drvdata in byte_cntr_data
> and always write straight to the device, rather than always going to the
> ETR drvdata in ctcu_cfg_byte_cntr_reg() ?

Understood, will fix it.

> 
>> +    enable_irq_wake(byte_cntr_data->irq);
>> +    byte_cntr_data->total_size = 0;
>> +
>> +out_unlock:
>> +    raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +
>> +out:
>> +    return ret;
>> +}
>> +
>> +static int ctcu_read_unprepare_byte_cntr(struct tmc_drvdata *drvdata)
> 
> Same as above. name

ditto.

> 
>> +{
>> +    struct device *dev = &drvdata->csdev->dev;
>> +    struct ctcu_byte_cntr *byte_cntr_data;
>> +    unsigned long flags;
>> +
>> +    byte_cntr_data = ctcu_get_byte_cntr_data(drvdata);
>> +    if (!byte_cntr_data)
>> +        return -EINVAL;
>> +
>> +    raw_spin_lock_irqsave(&drvdata->spinlock, flags);
>> +    /* Configure the byte-cntr register to disable IRQ */
>> +    ctcu_cfg_byte_cntr_reg(drvdata, 0, byte_cntr_data->irq_ctrl_offset);
> 
> 
>> +    disable_irq_wake(byte_cntr_data->irq);
>> +    byte_cntr_data->reading = false;
>> +    byte_cntr_data->reading_data = false;
>> +    drvdata->buf_node = NULL;
> 
> 
>> +    /* Restore the original sysfs_ops */
>> +    drvdata->sysfs_ops = &etr_sysfs_ops;
> 
> Please see if you can avoid it.

Sure, will check other solution.

> 
>> +    raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +    dev_dbg(dev, "send data total size:%llu bytes\n", byte_cntr_data- 
>> >total_size);
>> +    tmc_clean_etr_buf_list(drvdata);
>> +
>> +    return 0;
>> +}
>> +
>> +const struct tmc_sysfs_ops byte_cntr_sysfs_ops = {
>> +    .read_prepare    = ctcu_read_prepare_byte_cntr,
>> +    .read_unprepare    = ctcu_read_unprepare_byte_cntr,
>> +    .get_trace_data    = ctcu_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 ctcu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
>> +    struct coresight_device *sink = coresight_get_sink(path);
>> +    struct tmc_drvdata *tmc_drvdata = dev_get_drvdata(sink->dev.parent);
>> +    struct ctcu_byte_cntr *byte_cntr_data;
>> +    int port_num;
>> +
>> +    port_num = coresight_get_in_port(sink, csdev);
>> +    if (port_num < 0)
>> +        return;
>> +
>> +    byte_cntr_data = &drvdata->byte_cntr_data[port_num];
> 
> instance 2
> 
>> +    /* Don't start byte-cntr function when threshold is not set. */
>> +    if (!byte_cntr_data->thresh_val || byte_cntr_data->enable)
>> +        return;
>> +
>> +    guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
>> +    byte_cntr_data->enable = true;
>> +    byte_cntr_data->reading_data = false;
>> +    /* Replace with byte-cntr's sysfs_ops */
>> +    tmc_drvdata->sysfs_ops = &byte_cntr_sysfs_ops;
> 
> Why are we hacking this ? This looks *very ugly* and we are doing this 
> without any locks at the tmc-etr driver !!! ?? Could we not call out
> from the etr_sysf_ops into the CTCU ops based some checks ?
> Anyways, please don't change anything in the tmc_drvdata from this
> driver, especially not the "operations".
> 

Understood. Will take care of it in next version.

> Also the locking seems inconsistent. Here we use byte_cntr_data->lock.
> But in ctcu_read_unprepare_byte_cntr(), we use the TMC-ETR 
> spinlock :facepalm:

Sorry for the hack solution here. Will fix it.

Thanks,
Jie

> 
> 
> 
>> +}
>> +
>> +/* Stop the byte-cntr function when the path is disabled. */
>> +void ctcu_byte_cntr_stop(struct coresight_device *csdev, struct 
>> coresight_path *path)
>> +{
>> +    struct ctcu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
>> +    struct coresight_device *sink = coresight_get_sink(path);
>> +    struct ctcu_byte_cntr *byte_cntr_data;
>> +    int port_num;
>> +
>> +    if (coresight_get_mode(sink) == CS_MODE_SYSFS)
>> +        return;
>> + +    port_num = coresight_get_in_port(sink, csdev);
>> +    if (port_num < 0)
>> +        return;
>> +
>> +    byte_cntr_data = &drvdata->byte_cntr_data[port_num];
> 
> instance 3
> 
> 
> 
> 
>> +    guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
>> +    byte_cntr_data->enable = false;
>> +}
>> +
>> +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;
>> +
>> +    for (i = 0; i < etr_num; i++) {
>> +        byte_cntr_data = &drvdata->byte_cntr_data[i];
>> +        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 | IRQF_SHARED,
>> +                       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;
>> +        init_waitqueue_head(&byte_cntr_data->wq);
>> +        raw_spin_lock_init(&byte_cntr_data->spin_lock);
>> +    }
>> +}
>> diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/ 
>> drivers/hwtracing/coresight/coresight-ctcu-core.c
>> index e8720026c9e3..60f1db3ab70d 100644
>> --- a/drivers/hwtracing/coresight/coresight-ctcu-core.c
>> +++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c
>> @@ -15,6 +15,7 @@
>>   #include <linux/platform_device.h>
>>   #include <linux/pm_runtime.h>
>>   #include <linux/slab.h>
>> +#include <linux/sizes.h>
>>   #include "coresight-ctcu.h"
>>   #include "coresight-priv.h"
>> @@ -43,17 +44,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 +67,88 @@ static const struct ctcu_config sa8775p_cfgs = {
>>       .num_etr_config    = ARRAY_SIZE(sa8775p_etr_cfgs),
>>   };
>> +void ctcu_program_register(struct ctcu_drvdata *drvdata, u32 val, u32 
>> offset)
>> +{
>> +    CS_UNLOCK(drvdata->base);
>> +    ctcu_writel(drvdata, val, offset);
>> +    CS_LOCK(drvdata->base);
>> +}
>> +
>> +static ssize_t irq_threshold_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].thresh_val);
>> +}
>> +
>> +static ssize_t irq_threshold_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;
>> +
>> +    /* Threshold 0 disables the interruption. */
>> +    guard(raw_spinlock_irqsave)(&drvdata->spin_lock);
>> +    /* A small threshold will result in a large number of 
>> interruptions */
>> +    if (val && val < SZ_4K)
>> +        return -EINVAL;
>> +
>> +    if (drvdata->byte_cntr_data[port].irq_ctrl_offset)
>> +        drvdata->byte_cntr_data[port].thresh_val = val;
>> +
>> +    return size;
>> +}
>> +
>> +static umode_t irq_threshold_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;
>> +
>> +    if (drvdata && drvdata->byte_cntr_data[port].irq_ctrl_offset)
>> +        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_threshold_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 +227,15 @@ 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)
>>   {
>> +    ctcu_byte_cntr_start(csdev, path);
>> +
>>       return ctcu_set_etr_traceid(csdev, path, true);
>>   }
>>   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 +286,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 +303,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);
>> diff --git a/drivers/hwtracing/coresight/coresight-ctcu.h b/drivers/ 
>> hwtracing/coresight/coresight-ctcu.h
>> index e9594c38dd91..c52cf6a33d2e 100644
>> --- a/drivers/hwtracing/coresight/coresight-ctcu.h
>> +++ b/drivers/hwtracing/coresight/coresight-ctcu.h
>> @@ -5,19 +5,26 @@
>>   #ifndef _CORESIGHT_CTCU_H
>>   #define _CORESIGHT_CTCU_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)
>> +
>>   /**
>>    * struct ctcu_etr_config
>>    * @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.
>> + * @irq_name:           IRQ name in dt node.
>>    */
>>   struct ctcu_etr_config {
>>       const u32 atid_offset;
>>       const u32 port_num;
>> +    const u32 irq_ctrl_offset;
>>   };
>>   struct ctcu_config {
>> @@ -25,15 +32,68 @@ 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
>> + * @enable:        indicates that byte_cntr function is enabled or not.
>> + * @reading:        indicates that byte-cntr reading is started.
>> + * @reading_data:    indicates that byte-cntr is reading data from 
>> the buffer.
>> + * @thresh_val:        threshold to trigger a interruption.
>> + * @total_size:        total size of the transferred data.
>> + * @irq:        allocated number of the IRQ.
>> + * @irq_cnt:        IRQ count number for triggered interruptions.
>> + * @wq:            waitqueue for reading data from ETR buffer.
>> + * @spin_lock:        spinlock of byte_cntr_data.
>> + * @irq_ctrl_offset:    offset to the BYTECNTVAL Register.
>> + */
>> +struct ctcu_byte_cntr {
>> +    bool            enable;
>> +    bool                    reading;
>> +    bool            reading_data;
>> +    u32            thresh_val;
>> +    u64            total_size;
>> +    int            irq;
>> +    atomic_t        irq_cnt;
>> +    wait_queue_head_t    wq;
>>       raw_spinlock_t        spin_lock;
>> -    u32            atid_offset[ETR_MAX_NUM];
>> +    u32            irq_ctrl_offset;
>> +};
>> +
>> +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];
>> +    u8                traceid_refcnt[ETR_MAX_NUM] 
>> [CORESIGHT_TRACE_ID_RES_TOP];
>>   };
>> +/**
>> + * struct ctcu_irq_thresh_attribute
>> + * @attr:    The device attribute.
>> + * @idx:    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_threshold##port, 0644, irq_threshold_show,    \
>> +        irq_threshold_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 110eedde077f..948ea864f2a1 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc-core.c
>> +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
>> @@ -293,7 +293,10 @@ static ssize_t tmc_read(struct file *file, char 
>> __user *data, size_t len,
>>           return -EFAULT;
>>       }
>> -    *ppos += actual;
>> +    if (drvdata->buf_node)
>> +        drvdata->buf_node->pos += actual;
>> +    else
>> +        *ppos += actual;
>>       dev_dbg(&drvdata->csdev->dev, "%zu bytes copied\n", actual);
>>       return actual;
>> @@ -748,11 +751,12 @@ static const struct tmc_sysfs_ops etb_sysfs_ops = {
>>       .get_trace_data    = tmc_etb_get_sysfs_trace,
>>   };
>> -static const struct tmc_sysfs_ops etr_sysfs_ops = {
>> +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,
>>   };
>> +EXPORT_SYMBOL_GPL(etr_sysfs_ops);
>>   static int __tmc_probe(struct device *dev, struct resource *res)
>>   {
>> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/ 
>> drivers/hwtracing/coresight/coresight-tmc-etr.c
>> index f4223215ef8d..8896fb7a9ed3 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> @@ -1185,6 +1185,10 @@ ssize_t tmc_etr_get_sysfs_trace(struct 
>> tmc_drvdata *drvdata,
>>       ssize_t actual = len;
>>       struct etr_buf *etr_buf = drvdata->sysfs_buf;
>> +    /* Reading the buffer from the buf_node if it exists*/
>> +    if (drvdata->buf_node)
>> +        etr_buf = drvdata->buf_node->sysfs_buf;
>> +
>>       if (pos + actual > etr_buf->len)
>>           actual = etr_buf->len - pos;
>>       if (actual <= 0)
>> @@ -1248,6 +1252,20 @@ static void __tmc_etr_disable_hw(struct 
>> tmc_drvdata *drvdata)
>>   }
>> +/**
>> + * tmc_etr_enable_disable_hw - enable/disable the ETR hw.
>> + * @drvdata:    drvdata of the TMC device.
>> + * @enable:    indicates enable/disable.
>> + */
>> +void tmc_etr_enable_disable_hw(struct tmc_drvdata *drvdata, bool enable)
>> +{
>> +    if (enable)
>> +        __tmc_etr_enable_hw(drvdata);
>> +    else
>> +        __tmc_etr_disable_hw(drvdata);
>> +}
>> +EXPORT_SYMBOL_GPL(tmc_etr_enable_disable_hw);
>> +
>>   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 aaa443abe8e9..183c649b982c 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc.h
>> +++ b/drivers/hwtracing/coresight/coresight-tmc.h
>> @@ -260,6 +260,7 @@ struct etr_buf_node {
>>    *         Used by ETR/ETF.
>>    * @etr_buf_list: List that is used to manage allocated etr_buf.
>>    * @sysfs_ops:    Read operations for the sysfs mode.
>> + * @buf_node:    Available buffer_node for byte-cntr reading.
>>    */
>>   struct tmc_drvdata {
>>       struct clk        *atclk;
>> @@ -292,6 +293,7 @@ struct tmc_drvdata {
>>       struct tmc_resrv_buf    crash_mdata;
>>       struct list_head        etr_buf_list;
>>       const struct tmc_sysfs_ops    *sysfs_ops;
>> +    struct etr_buf_node    *buf_node;
>>   };
>>   /**
>> @@ -371,6 +373,7 @@ void tmc_etr_disable_hw(struct tmc_drvdata *drvdata);
>>   extern const struct coresight_ops tmc_etr_cs_ops;
>>   ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
>>                   loff_t pos, size_t len, char **bufpp);
>> +extern const struct tmc_sysfs_ops etr_sysfs_ops;
>>   #define TMC_REG_PAIR(name, lo_off, hi_off)                \
>> @@ -480,5 +483,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);
>> +void tmc_etr_enable_disable_hw(struct tmc_drvdata *drvdata, bool 
>> enable);
>>   #endif
>>
> 


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v14 6/7] coresight: ctcu: enable byte-cntr for TMC ETR devices
  2026-03-10  3:01     ` Jie Gan
@ 2026-03-10  9:15       ` Suzuki K Poulose
  2026-03-10 10:58         ` Jie Gan
  2026-03-11  8:02       ` Jie Gan
  1 sibling, 1 reply; 15+ messages in thread
From: Suzuki K Poulose @ 2026-03-10  9:15 UTC (permalink / raw)
  To: Jie Gan, Mike Leach, James Clark, Alexander Shishkin, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang, Bjorn Andersson,
	Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree

On 10/03/2026 03:01, Jie Gan wrote:
> 
> 
> On 3/9/2026 8:43 PM, Suzuki K Poulose wrote:
>> On 09/03/2026 09:47, Jie Gan wrote:
>>> 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.
>>>
>>> Switching the sysfs_buf when current buffer is full or the timeout is
>>> triggered and resets rrp and rwp registers after switched the buffer.
>>> The synced buffer will become available for reading after the switch.
>>>
>>> Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
>>> ---
>>>   .../ABI/testing/sysfs-bus-coresight-devices-ctcu   |   8 +
>>>   drivers/hwtracing/coresight/Makefile               |   2 +-
>>>   .../hwtracing/coresight/coresight-ctcu-byte-cntr.c | 351 ++++++++++ 
>>> + ++++++++++
>>>   drivers/hwtracing/coresight/coresight-ctcu-core.c  | 103 +++++-
>>>   drivers/hwtracing/coresight/coresight-ctcu.h       |  76 ++++-
>>>   drivers/hwtracing/coresight/coresight-tmc-core.c   |   8 +-
>>>   drivers/hwtracing/coresight/coresight-tmc-etr.c    |  18 ++
>>>   drivers/hwtracing/coresight/coresight-tmc.h        |   4 +
>>>   8 files changed, 555 insertions(+), 15 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..6ff1708fb944
>>> --- /dev/null
>>> +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu
>>> @@ -0,0 +1,8 @@
>>> +What:           /sys/bus/coresight/devices/<ctcu-name>/ 
>>> irq_threshold[0:1]
>>> +Date:           March 2026
>>> +KernelVersion:  7.1
>>> +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 byte-cntr IRQ register for the specified 
>>> ETR device
>>> +        based on its port number. An interrupt is generated when the 
>>> data size
>>> +        exceeds the value set in the IRQ register.
>>> 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..0bf738d6c283
>>> --- /dev/null
>>> +++ b/drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c
>>> @@ -0,0 +1,351 @@
>>> +// 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/of_irq.h>
>>> +#include <linux/uaccess.h>
>>> +
>>> +#include "coresight-ctcu.h"
>>> +#include "coresight-priv.h"
>>> +#include "coresight-tmc.h"
>>> +
>>> +static irqreturn_t byte_cntr_handler(int irq, void *data)
>>> +{
>>> +    struct ctcu_byte_cntr *byte_cntr_data = (struct ctcu_byte_cntr 
>>> *)data;
>>> +
>>> +    atomic_inc(&byte_cntr_data->irq_cnt);
>>> +    wake_up(&byte_cntr_data->wq);
>>> +
>>> +    return IRQ_HANDLED;
>>> +}
>>> +
>>> +static void ctcu_reset_sysfs_buf(struct tmc_drvdata *drvdata)
>>
>> minor nit: This has nothing to do with the CTCU. For what it is worth,
>> it must be called, tmc_etr_reset_sysf_buf(). But more on this below,
>> and even do we need it, further below.
>>
>>> +{
>>> +    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);
>>
>> Could we not keep this function in the tmc-etr.c and invoke from here ?
>>
> 
> Sure, will move the function tmc-etr.c
> 
>>> +}
>>> +
>>> +static void ctcu_cfg_byte_cntr_reg(struct tmc_drvdata *drvdata, u32 
>>> val, u32 offset)
>>> +{
>>> +    struct ctcu_drvdata *ctcu_drvdata;
>>> +    struct coresight_device *helper;
>>> +
>>> +    helper = tmc_etr_get_ctcu_device(drvdata);
>>> +    if (!helper)
>>> +        return;
>>> +
>>> +    ctcu_drvdata = dev_get_drvdata(helper->dev.parent);
>>> +    /* A one value for IRQCTRL register represents 8 bytes */
>>> +    ctcu_program_register(ctcu_drvdata, val / 8, offset);
>>> +}
>>> +
>>> +static struct ctcu_byte_cntr *ctcu_get_byte_cntr_data(struct 
>>> tmc_drvdata *drvdata)
>>> +{
>>> +    struct ctcu_byte_cntr *byte_cntr_data;
>>> +    struct ctcu_drvdata *ctcu_drvdata;
>>> +    struct coresight_device *helper;
>>> +    int port;
>>> +
>>> +    helper = tmc_etr_get_ctcu_device(drvdata);
>>> +    if (!helper)
>>> +        return NULL;
>>> +
>>
>>
>>
>>> +    port = coresight_get_in_port(drvdata->csdev, helper);
>>> +    if (port < 0)
>>> +        return NULL;
>>> +
>>
>> Please validate that the port_num you get is valid for the CTCU ? That 
>> applies to all uses of this construct.
>>
> 
> Will validate it before using.
> 
>>> +    ctcu_drvdata = dev_get_drvdata(helper->dev.parent);
>>> +    byte_cntr_data = &ctcu_drvdata->byte_cntr_data[port];
>>> +    return byte_cntr_data;
>>
>>
>>
>> nit:
>>      return  &ctcu_drvdata->byte_cntr_data[port]; ?
>>
>> Also, why not make this into a helper, as we seem to use this other 
>> places too ?
>>
> 
> Didnt get the point here. We may run more than one ETR devices 
> concurrently. So we should get the proper byte_cntr_data according to 
> the port number at runtime.
> 


static struct ctcu_byte_cntr *ctcu_byte_cntr(struct coresight_device 
*cctcu_dev, struct coresight_device *tmc_etr, ) {

	port = coresight_get_in_port()..
	// Verify the port in this helper and everyone uses this.
	if (//!validate_port//)
		return NULL
	return ...
}


Suzuki


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v14 6/7] coresight: ctcu: enable byte-cntr for TMC ETR devices
  2026-03-10  9:15       ` Suzuki K Poulose
@ 2026-03-10 10:58         ` Jie Gan
  0 siblings, 0 replies; 15+ messages in thread
From: Jie Gan @ 2026-03-10 10:58 UTC (permalink / raw)
  To: Suzuki K Poulose, Jie Gan, Mike Leach, James Clark,
	Alexander Shishkin, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Tingwei Zhang, Bjorn Andersson, Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree



On 3/10/2026 5:15 PM, Suzuki K Poulose wrote:
> On 10/03/2026 03:01, Jie Gan wrote:
>>
>>
>> On 3/9/2026 8:43 PM, Suzuki K Poulose wrote:
>>> On 09/03/2026 09:47, Jie Gan wrote:
>>>> 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.
>>>>
>>>> Switching the sysfs_buf when current buffer is full or the timeout is
>>>> triggered and resets rrp and rwp registers after switched the buffer.
>>>> The synced buffer will become available for reading after the switch.
>>>>
>>>> Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
>>>> ---
>>>>   .../ABI/testing/sysfs-bus-coresight-devices-ctcu   |   8 +
>>>>   drivers/hwtracing/coresight/Makefile               |   2 +-
>>>>   .../hwtracing/coresight/coresight-ctcu-byte-cntr.c | 351 +++++++++ 
>>>> + + ++++++++++
>>>>   drivers/hwtracing/coresight/coresight-ctcu-core.c  | 103 +++++-
>>>>   drivers/hwtracing/coresight/coresight-ctcu.h       |  76 ++++-
>>>>   drivers/hwtracing/coresight/coresight-tmc-core.c   |   8 +-
>>>>   drivers/hwtracing/coresight/coresight-tmc-etr.c    |  18 ++
>>>>   drivers/hwtracing/coresight/coresight-tmc.h        |   4 +
>>>>   8 files changed, 555 insertions(+), 15 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..6ff1708fb944
>>>> --- /dev/null
>>>> +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu
>>>> @@ -0,0 +1,8 @@
>>>> +What:           /sys/bus/coresight/devices/<ctcu-name>/ 
>>>> irq_threshold[0:1]
>>>> +Date:           March 2026
>>>> +KernelVersion:  7.1
>>>> +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 byte-cntr IRQ register for the specified 
>>>> ETR device
>>>> +        based on its port number. An interrupt is generated when 
>>>> the data size
>>>> +        exceeds the value set in the IRQ register.
>>>> 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..0bf738d6c283
>>>> --- /dev/null
>>>> +++ b/drivers/hwtracing/coresight/coresight-ctcu-byte-cntr.c
>>>> @@ -0,0 +1,351 @@
>>>> +// 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/of_irq.h>
>>>> +#include <linux/uaccess.h>
>>>> +
>>>> +#include "coresight-ctcu.h"
>>>> +#include "coresight-priv.h"
>>>> +#include "coresight-tmc.h"
>>>> +
>>>> +static irqreturn_t byte_cntr_handler(int irq, void *data)
>>>> +{
>>>> +    struct ctcu_byte_cntr *byte_cntr_data = (struct ctcu_byte_cntr 
>>>> *)data;
>>>> +
>>>> +    atomic_inc(&byte_cntr_data->irq_cnt);
>>>> +    wake_up(&byte_cntr_data->wq);
>>>> +
>>>> +    return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +static void ctcu_reset_sysfs_buf(struct tmc_drvdata *drvdata)
>>>
>>> minor nit: This has nothing to do with the CTCU. For what it is worth,
>>> it must be called, tmc_etr_reset_sysf_buf(). But more on this below,
>>> and even do we need it, further below.
>>>
>>>> +{
>>>> +    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);
>>>
>>> Could we not keep this function in the tmc-etr.c and invoke from here ?
>>>
>>
>> Sure, will move the function tmc-etr.c
>>
>>>> +}
>>>> +
>>>> +static void ctcu_cfg_byte_cntr_reg(struct tmc_drvdata *drvdata, u32 
>>>> val, u32 offset)
>>>> +{
>>>> +    struct ctcu_drvdata *ctcu_drvdata;
>>>> +    struct coresight_device *helper;
>>>> +
>>>> +    helper = tmc_etr_get_ctcu_device(drvdata);
>>>> +    if (!helper)
>>>> +        return;
>>>> +
>>>> +    ctcu_drvdata = dev_get_drvdata(helper->dev.parent);
>>>> +    /* A one value for IRQCTRL register represents 8 bytes */
>>>> +    ctcu_program_register(ctcu_drvdata, val / 8, offset);
>>>> +}
>>>> +
>>>> +static struct ctcu_byte_cntr *ctcu_get_byte_cntr_data(struct 
>>>> tmc_drvdata *drvdata)
>>>> +{
>>>> +    struct ctcu_byte_cntr *byte_cntr_data;
>>>> +    struct ctcu_drvdata *ctcu_drvdata;
>>>> +    struct coresight_device *helper;
>>>> +    int port;
>>>> +
>>>> +    helper = tmc_etr_get_ctcu_device(drvdata);
>>>> +    if (!helper)
>>>> +        return NULL;
>>>> +
>>>
>>>
>>>
>>>> +    port = coresight_get_in_port(drvdata->csdev, helper);
>>>> +    if (port < 0)
>>>> +        return NULL;
>>>> +
>>>
>>> Please validate that the port_num you get is valid for the CTCU ? 
>>> That applies to all uses of this construct.
>>>
>>
>> Will validate it before using.
>>
>>>> +    ctcu_drvdata = dev_get_drvdata(helper->dev.parent);
>>>> +    byte_cntr_data = &ctcu_drvdata->byte_cntr_data[port];
>>>> +    return byte_cntr_data;
>>>
>>>
>>>
>>> nit:
>>>      return  &ctcu_drvdata->byte_cntr_data[port]; ?
>>>
>>> Also, why not make this into a helper, as we seem to use this other 
>>> places too ?
>>>
>>
>> Didnt get the point here. We may run more than one ETR devices 
>> concurrently. So we should get the proper byte_cntr_data according to 
>> the port number at runtime.
>>
> 
> 
> static struct ctcu_byte_cntr *ctcu_byte_cntr(struct coresight_device 
> *cctcu_dev, struct coresight_device *tmc_etr, ) {
> 
>      port = coresight_get_in_port()..
>      // Verify the port in this helper and everyone uses this.
>      if (//!validate_port//)
>          return NULL
>      return ...
> }

Got the point now. I have missed the instance 2/instance 3 you have 
mentioned in previous message.

Thanks,
Jie

> 
> 
> Suzuki
> 
> 


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v14 6/7] coresight: ctcu: enable byte-cntr for TMC ETR devices
  2026-03-10  3:01     ` Jie Gan
  2026-03-10  9:15       ` Suzuki K Poulose
@ 2026-03-11  8:02       ` Jie Gan
  1 sibling, 0 replies; 15+ messages in thread
From: Jie Gan @ 2026-03-11  8:02 UTC (permalink / raw)
  To: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang,
	Bjorn Andersson, Konrad Dybcio
  Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
	devicetree



On 3/10/2026 11:01 AM, Jie Gan wrote:
> 
> 
> On 3/9/2026 8:43 PM, Suzuki K Poulose wrote:
>> On 09/03/2026 09:47, Jie Gan wrote:
>>> 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.
>>>
>>> Switching the sysfs_buf when current buffer is full or the timeout is
>>> triggered and resets rrp and rwp registers after switched the buffer.
>>> The synced buffer will become available for reading after the switch.
>>>
>>> Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
>>> ---
>>>   .../ABI/testing/sysfs-bus-coresight-devices-ctcu   |   8 +
>>>   drivers/hwtracing/coresight/Makefile               |   2 +-
>>>   .../hwtracing/coresight/coresight-ctcu-byte-cntr.c | 351 ++++++++++ 
>>> + ++++++++++
>>>   drivers/hwtracing/coresight/coresight-ctcu-core.c  | 103 +++++-
>>>   drivers/hwtracing/coresight/coresight-ctcu.h       |  76 ++++-
>>>   drivers/hwtracing/coresight/coresight-tmc-core.c   |   8 +-
>>>   drivers/hwtracing/coresight/coresight-tmc-etr.c    |  18 ++
>>>   drivers/hwtracing/coresight/coresight-tmc.h        |   4 +
>>>   8 files changed, 555 insertions(+), 15 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..6ff1708fb944
>>> --- /dev/null
>>> +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-ctcu
>>> @@ -0,0 +1,8 @@
>>> +What:           /sys/bus/coresight/devices/<ctcu-name>/ 
>>> irq_threshold[0:1]
>>> +Date:           March 2026
>>> +KernelVersion:  7.1
>>> +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 byte-cntr IRQ register for the specified 
>>> ETR device
>>> +        based on its port number. An interrupt is generated when the 
>>> data size
>>> +        exceeds the value set in the IRQ register.
>>> 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

[...]

>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    raw_spin_lock_irqsave(&drvdata->spinlock, flags);
>>> +    if (byte_cntr_data->reading) {
>>> +        ret = -EBUSY;
>>> +        goto out_unlock;
>>> +    }
>>> +
>>> +    byte_cntr_data->reading = true;
>>> +    raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
>>
>> All of this can be avoided by retaining the etr_sysf_ops in TMC-> and
>> calling into the CTCU after basic checks.
> 
> Understood. we can rely on the tmc->reading check, jump to CTCU ops 
> after proper check.

Hi Suzuki,

I think we need a new flag to control the byte-cntr read work. Here is 
the reason:

For normal tmc ETR read flow:
tmc_read_prepare -> tmc_read -> tmc_read_unprepare -> tmc_etr_disable_hw

We dont allow disable the ETR device while reading (drvdata->reading).

For byte-cntr continously read flow:

byte-cntr-start -> tmc_read_prepare -> tmc_read (continously read data 
until ETR is disabled or is stopped by userspace) -> tmc_etr_disable_hw 
->tmc_read_unprepare.

With drvdata->reading = true. We cant disable the ETR device. so the 
issue happened.

We can jump to the 
tmc_read_prepare_byte_cntr/tmc_read_unprepare_byte_cntr from 
tmc_read_prepare_etr/tmc_read_unprepare_etr, but we must skip to 
set/reset drvdata->reading.

Thanks,
Jie

> 
>>
>>> +    /* Setup an available etr_buf_list for byte-cntr */
>>> +    ret = tmc_create_etr_buf_list(drvdata, 2);
>>> +    if (ret)
>>> +        goto out;
>>> +
>>> +    raw_spin_lock_irqsave(&drvdata->spinlock, flags);
>>> +    atomic_set(&byte_cntr_data->irq_cnt, 0);
>>> +    /* Configure the byte-cntr register to enable IRQ */
>>> +    ctcu_cfg_byte_cntr_reg(drvdata, byte_cntr_data->thresh_val,
>>> +                   byte_cntr_data->irq_ctrl_offset);
>>
>> Is there no way of going back to the ctcu_drvdata from byte_cntr_data ?
>> For a starter, we could easily store the ctcu_drvdata in byte_cntr_data
>> and always write straight to the device, rather than always going to the
>> ETR drvdata in ctcu_cfg_byte_cntr_reg() ?
> 
> Understood, will fix it.
> 
>>
>>> +    enable_irq_wake(byte_cntr_data->irq);
>>> +    byte_cntr_data->total_size = 0;
>>> +
>>> +out_unlock:
>>> +    raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
>>> +
>>> +out:
>>> +    return ret;
>>> +}
>>> +
>>> +static int ctcu_read_unprepare_byte_cntr(struct tmc_drvdata *drvdata)
>>
>> Same as above. name
> 
> ditto.
> 
>>
>>> +{
>>> +    struct device *dev = &drvdata->csdev->dev;
>>> +    struct ctcu_byte_cntr *byte_cntr_data;
>>> +    unsigned long flags;
>>> +
>>> +    byte_cntr_data = ctcu_get_byte_cntr_data(drvdata);
>>> +    if (!byte_cntr_data)
>>> +        return -EINVAL;
>>> +
>>> +    raw_spin_lock_irqsave(&drvdata->spinlock, flags);
>>> +    /* Configure the byte-cntr register to disable IRQ */
>>> +    ctcu_cfg_byte_cntr_reg(drvdata, 0, byte_cntr_data- 
>>> >irq_ctrl_offset);
>>
>>
>>> +    disable_irq_wake(byte_cntr_data->irq);
>>> +    byte_cntr_data->reading = false;
>>> +    byte_cntr_data->reading_data = false;
>>> +    drvdata->buf_node = NULL;
>>
>>
>>> +    /* Restore the original sysfs_ops */
>>> +    drvdata->sysfs_ops = &etr_sysfs_ops;
>>
>> Please see if you can avoid it.
> 
> Sure, will check other solution.
> 
>>
>>> +    raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
>>> +    dev_dbg(dev, "send data total size:%llu bytes\n", 
>>> byte_cntr_data- >total_size);
>>> +    tmc_clean_etr_buf_list(drvdata);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +const struct tmc_sysfs_ops byte_cntr_sysfs_ops = {
>>> +    .read_prepare    = ctcu_read_prepare_byte_cntr,
>>> +    .read_unprepare    = ctcu_read_unprepare_byte_cntr,
>>> +    .get_trace_data    = ctcu_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 ctcu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
>>> +    struct coresight_device *sink = coresight_get_sink(path);
>>> +    struct tmc_drvdata *tmc_drvdata = dev_get_drvdata(sink- 
>>> >dev.parent);
>>> +    struct ctcu_byte_cntr *byte_cntr_data;
>>> +    int port_num;
>>> +
>>> +    port_num = coresight_get_in_port(sink, csdev);
>>> +    if (port_num < 0)
>>> +        return;
>>> +
>>> +    byte_cntr_data = &drvdata->byte_cntr_data[port_num];
>>
>> instance 2
>>
>>> +    /* Don't start byte-cntr function when threshold is not set. */
>>> +    if (!byte_cntr_data->thresh_val || byte_cntr_data->enable)
>>> +        return;
>>> +
>>> +    guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
>>> +    byte_cntr_data->enable = true;
>>> +    byte_cntr_data->reading_data = false;
>>> +    /* Replace with byte-cntr's sysfs_ops */
>>> +    tmc_drvdata->sysfs_ops = &byte_cntr_sysfs_ops;
>>
>> Why are we hacking this ? This looks *very ugly* and we are doing this 
>> without any locks at the tmc-etr driver !!! ?? Could we not call out
>> from the etr_sysf_ops into the CTCU ops based some checks ?
>> Anyways, please don't change anything in the tmc_drvdata from this
>> driver, especially not the "operations".
>>
> 
> Understood. Will take care of it in next version.
> 
>> Also the locking seems inconsistent. Here we use byte_cntr_data->lock.
>> But in ctcu_read_unprepare_byte_cntr(), we use the TMC-ETR 
>> spinlock :facepalm:
> 
> Sorry for the hack solution here. Will fix it.
> 
> Thanks,
> Jie
> 
>>
>>
>>
>>> +}
>>> +
>>> +/* Stop the byte-cntr function when the path is disabled. */
>>> +void ctcu_byte_cntr_stop(struct coresight_device *csdev, struct 
>>> coresight_path *path)
>>> +{
>>> +    struct ctcu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
>>> +    struct coresight_device *sink = coresight_get_sink(path);
>>> +    struct ctcu_byte_cntr *byte_cntr_data;
>>> +    int port_num;
>>> +
>>> +    if (coresight_get_mode(sink) == CS_MODE_SYSFS)
>>> +        return;
>>> + +    port_num = coresight_get_in_port(sink, csdev);
>>> +    if (port_num < 0)
>>> +        return;
>>> +
>>> +    byte_cntr_data = &drvdata->byte_cntr_data[port_num];
>>
>> instance 3
>>
>>
>>
>>
>>> +    guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
>>> +    byte_cntr_data->enable = false;
>>> +}
>>> +
>>> +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;
>>> +
>>> +    for (i = 0; i < etr_num; i++) {
>>> +        byte_cntr_data = &drvdata->byte_cntr_data[i];
>>> +        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 | IRQF_SHARED,
>>> +                       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;
>>> +        init_waitqueue_head(&byte_cntr_data->wq);
>>> +        raw_spin_lock_init(&byte_cntr_data->spin_lock);
>>> +    }
>>> +}
>>> diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/ 
>>> drivers/hwtracing/coresight/coresight-ctcu-core.c
>>> index e8720026c9e3..60f1db3ab70d 100644
>>> --- a/drivers/hwtracing/coresight/coresight-ctcu-core.c
>>> +++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c
>>> @@ -15,6 +15,7 @@
>>>   #include <linux/platform_device.h>
>>>   #include <linux/pm_runtime.h>
>>>   #include <linux/slab.h>
>>> +#include <linux/sizes.h>
>>>   #include "coresight-ctcu.h"
>>>   #include "coresight-priv.h"
>>> @@ -43,17 +44,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 +67,88 @@ static const struct ctcu_config sa8775p_cfgs = {
>>>       .num_etr_config    = ARRAY_SIZE(sa8775p_etr_cfgs),
>>>   };
>>> +void ctcu_program_register(struct ctcu_drvdata *drvdata, u32 val, 
>>> u32 offset)
>>> +{
>>> +    CS_UNLOCK(drvdata->base);
>>> +    ctcu_writel(drvdata, val, offset);
>>> +    CS_LOCK(drvdata->base);
>>> +}
>>> +
>>> +static ssize_t irq_threshold_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].thresh_val);
>>> +}
>>> +
>>> +static ssize_t irq_threshold_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;
>>> +
>>> +    /* Threshold 0 disables the interruption. */
>>> +    guard(raw_spinlock_irqsave)(&drvdata->spin_lock);
>>> +    /* A small threshold will result in a large number of 
>>> interruptions */
>>> +    if (val && val < SZ_4K)
>>> +        return -EINVAL;
>>> +
>>> +    if (drvdata->byte_cntr_data[port].irq_ctrl_offset)
>>> +        drvdata->byte_cntr_data[port].thresh_val = val;
>>> +
>>> +    return size;
>>> +}
>>> +
>>> +static umode_t irq_threshold_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;
>>> +
>>> +    if (drvdata && drvdata->byte_cntr_data[port].irq_ctrl_offset)
>>> +        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_threshold_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 +227,15 @@ 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)
>>>   {
>>> +    ctcu_byte_cntr_start(csdev, path);
>>> +
>>>       return ctcu_set_etr_traceid(csdev, path, true);
>>>   }
>>>   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 +286,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 +303,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);
>>> diff --git a/drivers/hwtracing/coresight/coresight-ctcu.h b/drivers/ 
>>> hwtracing/coresight/coresight-ctcu.h
>>> index e9594c38dd91..c52cf6a33d2e 100644
>>> --- a/drivers/hwtracing/coresight/coresight-ctcu.h
>>> +++ b/drivers/hwtracing/coresight/coresight-ctcu.h
>>> @@ -5,19 +5,26 @@
>>>   #ifndef _CORESIGHT_CTCU_H
>>>   #define _CORESIGHT_CTCU_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)
>>> +
>>>   /**
>>>    * struct ctcu_etr_config
>>>    * @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.
>>> + * @irq_name:           IRQ name in dt node.
>>>    */
>>>   struct ctcu_etr_config {
>>>       const u32 atid_offset;
>>>       const u32 port_num;
>>> +    const u32 irq_ctrl_offset;
>>>   };
>>>   struct ctcu_config {
>>> @@ -25,15 +32,68 @@ 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
>>> + * @enable:        indicates that byte_cntr function is enabled or not.
>>> + * @reading:        indicates that byte-cntr reading is started.
>>> + * @reading_data:    indicates that byte-cntr is reading data from 
>>> the buffer.
>>> + * @thresh_val:        threshold to trigger a interruption.
>>> + * @total_size:        total size of the transferred data.
>>> + * @irq:        allocated number of the IRQ.
>>> + * @irq_cnt:        IRQ count number for triggered interruptions.
>>> + * @wq:            waitqueue for reading data from ETR buffer.
>>> + * @spin_lock:        spinlock of byte_cntr_data.
>>> + * @irq_ctrl_offset:    offset to the BYTECNTVAL Register.
>>> + */
>>> +struct ctcu_byte_cntr {
>>> +    bool            enable;
>>> +    bool                    reading;
>>> +    bool            reading_data;
>>> +    u32            thresh_val;
>>> +    u64            total_size;
>>> +    int            irq;
>>> +    atomic_t        irq_cnt;
>>> +    wait_queue_head_t    wq;
>>>       raw_spinlock_t        spin_lock;
>>> -    u32            atid_offset[ETR_MAX_NUM];
>>> +    u32            irq_ctrl_offset;
>>> +};
>>> +
>>> +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];
>>> +    u8                traceid_refcnt[ETR_MAX_NUM] 
>>> [CORESIGHT_TRACE_ID_RES_TOP];
>>>   };
>>> +/**
>>> + * struct ctcu_irq_thresh_attribute
>>> + * @attr:    The device attribute.
>>> + * @idx:    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_threshold##port, 0644, irq_threshold_show,    \
>>> +        irq_threshold_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 110eedde077f..948ea864f2a1 100644
>>> --- a/drivers/hwtracing/coresight/coresight-tmc-core.c
>>> +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
>>> @@ -293,7 +293,10 @@ static ssize_t tmc_read(struct file *file, char 
>>> __user *data, size_t len,
>>>           return -EFAULT;
>>>       }
>>> -    *ppos += actual;
>>> +    if (drvdata->buf_node)
>>> +        drvdata->buf_node->pos += actual;
>>> +    else
>>> +        *ppos += actual;
>>>       dev_dbg(&drvdata->csdev->dev, "%zu bytes copied\n", actual);
>>>       return actual;
>>> @@ -748,11 +751,12 @@ static const struct tmc_sysfs_ops etb_sysfs_ops 
>>> = {
>>>       .get_trace_data    = tmc_etb_get_sysfs_trace,
>>>   };
>>> -static const struct tmc_sysfs_ops etr_sysfs_ops = {
>>> +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,
>>>   };
>>> +EXPORT_SYMBOL_GPL(etr_sysfs_ops);
>>>   static int __tmc_probe(struct device *dev, struct resource *res)
>>>   {
>>> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/ 
>>> drivers/hwtracing/coresight/coresight-tmc-etr.c
>>> index f4223215ef8d..8896fb7a9ed3 100644
>>> --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
>>> +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
>>> @@ -1185,6 +1185,10 @@ ssize_t tmc_etr_get_sysfs_trace(struct 
>>> tmc_drvdata *drvdata,
>>>       ssize_t actual = len;
>>>       struct etr_buf *etr_buf = drvdata->sysfs_buf;
>>> +    /* Reading the buffer from the buf_node if it exists*/
>>> +    if (drvdata->buf_node)
>>> +        etr_buf = drvdata->buf_node->sysfs_buf;
>>> +
>>>       if (pos + actual > etr_buf->len)
>>>           actual = etr_buf->len - pos;
>>>       if (actual <= 0)
>>> @@ -1248,6 +1252,20 @@ static void __tmc_etr_disable_hw(struct 
>>> tmc_drvdata *drvdata)
>>>   }
>>> +/**
>>> + * tmc_etr_enable_disable_hw - enable/disable the ETR hw.
>>> + * @drvdata:    drvdata of the TMC device.
>>> + * @enable:    indicates enable/disable.
>>> + */
>>> +void tmc_etr_enable_disable_hw(struct tmc_drvdata *drvdata, bool 
>>> enable)
>>> +{
>>> +    if (enable)
>>> +        __tmc_etr_enable_hw(drvdata);
>>> +    else
>>> +        __tmc_etr_disable_hw(drvdata);
>>> +}
>>> +EXPORT_SYMBOL_GPL(tmc_etr_enable_disable_hw);
>>> +
>>>   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 aaa443abe8e9..183c649b982c 100644
>>> --- a/drivers/hwtracing/coresight/coresight-tmc.h
>>> +++ b/drivers/hwtracing/coresight/coresight-tmc.h
>>> @@ -260,6 +260,7 @@ struct etr_buf_node {
>>>    *         Used by ETR/ETF.
>>>    * @etr_buf_list: List that is used to manage allocated etr_buf.
>>>    * @sysfs_ops:    Read operations for the sysfs mode.
>>> + * @buf_node:    Available buffer_node for byte-cntr reading.
>>>    */
>>>   struct tmc_drvdata {
>>>       struct clk        *atclk;
>>> @@ -292,6 +293,7 @@ struct tmc_drvdata {
>>>       struct tmc_resrv_buf    crash_mdata;
>>>       struct list_head        etr_buf_list;
>>>       const struct tmc_sysfs_ops    *sysfs_ops;
>>> +    struct etr_buf_node    *buf_node;
>>>   };
>>>   /**
>>> @@ -371,6 +373,7 @@ void tmc_etr_disable_hw(struct tmc_drvdata 
>>> *drvdata);
>>>   extern const struct coresight_ops tmc_etr_cs_ops;
>>>   ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
>>>                   loff_t pos, size_t len, char **bufpp);
>>> +extern const struct tmc_sysfs_ops etr_sysfs_ops;
>>>   #define TMC_REG_PAIR(name, lo_off, hi_off)                \
>>> @@ -480,5 +483,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);
>>> +void tmc_etr_enable_disable_hw(struct tmc_drvdata *drvdata, bool 
>>> enable);
>>>   #endif
>>>
>>
> 
> 


^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-03-11  8:02 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-09  9:47 [PATCH v14 0/7] coresight: ctcu: Enable byte-cntr function for TMC ETR Jie Gan
2026-03-09  9:47 ` [PATCH v14 1/7] coresight: core: refactor ctcu_get_active_port and make it generic Jie Gan
2026-03-09  9:47 ` [PATCH v14 2/7] coresight: tmc: add create/clean functions for etr_buf_list Jie Gan
2026-03-09 10:02   ` Suzuki K Poulose
2026-03-09 10:27     ` Jie Gan
2026-03-09  9:47 ` [PATCH v14 3/7] coresight: tmc: introduce tmc_sysfs_ops to wrap sysfs read operations Jie Gan
2026-03-09  9:47 ` [PATCH v14 4/7] coresight: etr: add a new function to retrieve the CTCU device Jie Gan
2026-03-09  9:47 ` [PATCH v14 5/7] dt-bindings: arm: add an interrupt property for Coresight CTCU Jie Gan
2026-03-09  9:47 ` [PATCH v14 6/7] coresight: ctcu: enable byte-cntr for TMC ETR devices Jie Gan
2026-03-09 12:43   ` Suzuki K Poulose
2026-03-10  3:01     ` Jie Gan
2026-03-10  9:15       ` Suzuki K Poulose
2026-03-10 10:58         ` Jie Gan
2026-03-11  8:02       ` Jie Gan
2026-03-09  9:47 ` [PATCH v14 7/7] arm64: dts: qcom: lemans: add interrupts to CTCU device Jie Gan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox