linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/10] Add support for TPDM and TPDA
@ 2021-10-21  7:38 Tao Zhang
  2021-10-21  7:38 ` [PATCH 01/10] coresight: add support to enable more coresight paths Tao Zhang
                   ` (10 more replies)
  0 siblings, 11 replies; 37+ messages in thread
From: Tao Zhang @ 2021-10-21  7:38 UTC (permalink / raw)
  To: Mathieu Poirier, Suzuki K Poulose, Alexander Shishkin
  Cc: Tao Zhang, Mike Leach, Leo Yan, Greg Kroah-Hartman, coresight,
	linux-arm-kernel, linux-kernel, Tingwei Zhang, Mao Jinlong,
	Yuanfang Zhang, Trilok Soni

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="UTF-8", Size: 5070 bytes --]

This series adds support for the trace performance monitoring and
diagnostics hardware (TPDM and TPDA). It is composed of two major
elements.
a) Changes for original coresight framework to support for TPDM and TPDA.
b) Add driver code for TPDM and TPDA.

Introduction of changes for original coresight framework
a) Support TPDM as new coresight source.
Since only STM and ETM are supported as coresight source originally.
TPDM is a newly added coresight source. We need to change
the original way of saving coresight path to support more types source
for coresight driver.
The following patch is to add support more coresight sources.
coresight: add support to enable more coresight paths

b) To support multi-port input and multi-port output for funnels
In some cases, different TPDM hardware will be connected to the same
funnel, but eventually they need to be linked to different TPDAs or
funnels. This requires funnel to support multi-port input and multi-port
output, and can specify which input port corresponds to which output port.
Use property “label” in the funnel’s configuration to point out from
which input port to which output port.
The following patch is to support multi-port input and multi-port output
for funnels.
coresight: funnel: add support for multiple output ports

Introduction of TPDM and TPDA
TPDM - The trace performance monitoring and diagnostics monitor or TPDM in
short serves as data collection component for various dataset types
specified in the QPMDA(Qualcomm performance monitoring and diagnostics
architecture) spec. The primary use case of the TPDM is to collect data
from different data sources and send it to a TPDA for packetization,
timestamping and funneling.
The following patch is to add driver for TPDM.
Coresight: Add driver to support Coresight device TPDM
Coresight: Enable BC and GPR for TPDM driver
Coresight: Add interface for TPDM BC subunit
Coresight: Enable and add interface for TPDM TC subunit
Coresight: Enable DSB subunit for TPDM
Coresight: Enable CMB subunit for TPDM

TPDA - The trace performance monitoring and diagnostics aggregator or
TPDA in short serves as an arbitration and packetization engine for the
performance monitoring and diagnostics network as specified in the QPMDA
(Qualcomm performance monitoring and diagnostics architecture)
specification. The primary use case of the TPDA is to provide
packetization, funneling and timestamping of Monitor data as specified
in the QPMDA specification.
The following patch is to add driver for TPDA.
coresight: Add driver to support Coresight device TPDA

The last patch of this series is a device tree modification, which add
the TPDM and TPDA configuration to device tree for validating.
ARM: dts: msm: Add TPDA and TPDM configuration to device

Once this series patches are applied properly, the tpdm and tpda nodes
should be observed at the coresight path /sys/bus/coresight/devices
e.g.
/sys/bus/coresight/devices # ls -l | grep tpd
tpda0 -> ../../../devices/platform/soc@0/6004000.tpda/tpda0
tpdm0 -> ../../../devices/platform/soc@0/6844000.lpass.tpdm/tpdm0

We can use the commands are similar to the below to validate TPDMs.
Enable coresight sink first.
echo 1 > /sys/bus/coresight/devices/tpdm0/enable_source
echo 1 > /sys/bus/coresight/devices/tpdm0/integration_test
echo 2 > /sys/bus/coresight/devices/tpdm0/integration_test
The test data will be collected in the coresight sink which is enabled.

This series applies to coresight/next
https://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux.git?h=next

Tao Zhang (10):
  coresight: add support to enable more coresight paths
  coresight: funnel: add support for multiple output ports
  Coresight: Add driver to support Coresight device TPDM
  Coresight: Enable BC and GPR for TPDM driver
  Coresight: Add interface for TPDM BC subunit
  Coresight: Enable and add interface for TPDM TC subunit
  Coresight: Enable DSB subunit for TPDM
  Coresight: Enable CMB subunit for TPDM
  coresight: Add driver to support Coresight device TPDA
  ARM: dts: msm: Add TPDA and TPDM support to DTS for RB5

 .../bindings/arm/coresight-tpda.yaml          |  169 +
 .../bindings/arm/coresight-tpdm.yaml          |   86 +
 MAINTAINERS                                   |    6 +
 arch/arm64/boot/dts/qcom/qrb5165-rb5.dts      |  439 ++
 drivers/hwtracing/coresight/Kconfig           |   18 +
 drivers/hwtracing/coresight/Makefile          |    2 +
 drivers/hwtracing/coresight/coresight-core.c  |  175 +-
 .../hwtracing/coresight/coresight-platform.c  |    8 +
 drivers/hwtracing/coresight/coresight-tpda.c  |  828 ++++
 drivers/hwtracing/coresight/coresight-tpdm.c  | 4253 +++++++++++++++++
 include/linux/coresight.h                     |    2 +
 11 files changed, 5928 insertions(+), 58 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/coresight-tpda.yaml
 create mode 100644 Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
 create mode 100644 drivers/hwtracing/coresight/coresight-tpda.c
 create mode 100644 drivers/hwtracing/coresight/coresight-tpdm.c

-- 
2.17.1


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

* [PATCH 01/10] coresight: add support to enable more coresight paths
  2021-10-21  7:38 [PATCH 00/10] Add support for TPDM and TPDA Tao Zhang
@ 2021-10-21  7:38 ` Tao Zhang
  2021-10-28 18:06   ` Mathieu Poirier
  2021-10-21  7:38 ` [PATCH 02/10] coresight: funnel: add support for multiple output ports Tao Zhang
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 37+ messages in thread
From: Tao Zhang @ 2021-10-21  7:38 UTC (permalink / raw)
  To: Mathieu Poirier, Suzuki K Poulose, Alexander Shishkin
  Cc: Tao Zhang, Mike Leach, Leo Yan, Greg Kroah-Hartman, coresight,
	linux-arm-kernel, linux-kernel, Tingwei Zhang, Mao Jinlong,
	Yuanfang Zhang, Trilok Soni, Tingwei Zhang

Current coresight implementation only supports enabling source
ETMs or STM. This patch adds support to enable more kinds of
coresight source to sink paths. We build a path from source to
sink when any source is enabled and store it in a list. When the
source is disabled, we fetch the corresponding path from the list
and decrement the refcount on each device in the path. The device
is disabled if the refcount reaches zero. Don't store path to
coresight data structure of source to avoid unnecessary change to
ABI.
Since some targets may have coresight sources other than STM and
ETMs, we need to add this change to support these coresight
devices.

Signed-off-by: Tingwei Zhang <tingwei@codeaurora.org>
Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
---
 drivers/hwtracing/coresight/coresight-core.c | 100 +++++++++++--------
 1 file changed, 56 insertions(+), 44 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 8a18c71df37a..1e621d61307a 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -37,18 +37,16 @@ struct coresight_node {
 };
 
 /*
- * When operating Coresight drivers from the sysFS interface, only a single
- * path can exist from a tracer (associated to a CPU) to a sink.
+ * struct coresight_path - path from source to sink
+ * @path:	Address of path list.
+ * @link:	hook to the list.
  */
-static DEFINE_PER_CPU(struct list_head *, tracer_path);
+struct coresight_path {
+	struct list_head *path;
+	struct list_head link;
+};
 
-/*
- * As of this writing only a single STM can be found in CS topologies.  Since
- * there is no way to know if we'll ever see more and what kind of
- * configuration they will enact, for the time being only define a single path
- * for STM.
- */
-static struct list_head *stm_path;
+static LIST_HEAD(cs_active_paths);
 
 /*
  * When losing synchronisation a new barrier packet needs to be inserted at the
@@ -354,6 +352,7 @@ static void coresight_disable_sink(struct coresight_device *csdev)
 	if (ret)
 		return;
 	coresight_control_assoc_ectdev(csdev, false);
+	csdev->activated = false;
 	csdev->enable = false;
 }
 
@@ -590,6 +589,20 @@ int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data)
 	goto out;
 }
 
+static struct coresight_device *coresight_get_source(struct list_head *path)
+{
+	struct coresight_device *csdev;
+
+	if (!path)
+		return NULL;
+
+	csdev = list_first_entry(path, struct coresight_node, link)->csdev;
+	if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE)
+		return NULL;
+
+	return csdev;
+}
+
 struct coresight_device *coresight_get_sink(struct list_head *path)
 {
 	struct coresight_device *csdev;
@@ -1086,9 +1099,23 @@ static int coresight_validate_source(struct coresight_device *csdev,
 	return 0;
 }
 
+static int coresight_store_path(struct list_head *path)
+{
+	struct coresight_path *node;
+
+	node = kzalloc(sizeof(struct coresight_path), GFP_KERNEL);
+	if (!node)
+		return -ENOMEM;
+
+	node->path = path;
+	list_add(&node->link, &cs_active_paths);
+
+	return 0;
+}
+
 int coresight_enable(struct coresight_device *csdev)
 {
-	int cpu, ret = 0;
+	int ret = 0;
 	struct coresight_device *sink;
 	struct list_head *path;
 	enum coresight_dev_subtype_source subtype;
@@ -1133,25 +1160,9 @@ int coresight_enable(struct coresight_device *csdev)
 	if (ret)
 		goto err_source;
 
-	switch (subtype) {
-	case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
-		/*
-		 * When working from sysFS it is important to keep track
-		 * of the paths that were created so that they can be
-		 * undone in 'coresight_disable()'.  Since there can only
-		 * be a single session per tracer (when working from sysFS)
-		 * a per-cpu variable will do just fine.
-		 */
-		cpu = source_ops(csdev)->cpu_id(csdev);
-		per_cpu(tracer_path, cpu) = path;
-		break;
-	case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
-		stm_path = path;
-		break;
-	default:
-		/* We can't be here */
-		break;
-	}
+	ret = coresight_store_path(path);
+	if (ret)
+		goto err_source;
 
 out:
 	mutex_unlock(&coresight_mutex);
@@ -1168,8 +1179,11 @@ EXPORT_SYMBOL_GPL(coresight_enable);
 
 void coresight_disable(struct coresight_device *csdev)
 {
-	int cpu, ret;
+	int  ret;
 	struct list_head *path = NULL;
+	struct coresight_path *cspath = NULL;
+	struct coresight_path *cspath_next = NULL;
+	struct coresight_device *src_csdev = NULL;
 
 	mutex_lock(&coresight_mutex);
 
@@ -1180,20 +1194,18 @@ void coresight_disable(struct coresight_device *csdev)
 	if (!csdev->enable || !coresight_disable_source(csdev))
 		goto out;
 
-	switch (csdev->subtype.source_subtype) {
-	case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
-		cpu = source_ops(csdev)->cpu_id(csdev);
-		path = per_cpu(tracer_path, cpu);
-		per_cpu(tracer_path, cpu) = NULL;
-		break;
-	case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
-		path = stm_path;
-		stm_path = NULL;
-		break;
-	default:
-		/* We can't be here */
-		break;
+	list_for_each_entry_safe(cspath, cspath_next, &cs_active_paths, link) {
+		src_csdev = coresight_get_source(cspath->path);
+		if (!src_csdev)
+			continue;
+		if (src_csdev == csdev) {
+			path = cspath->path;
+			list_del(&cspath->link);
+			kfree(cspath);
+		}
 	}
+	if (path == NULL)
+		goto out;
 
 	coresight_disable_path(path);
 	coresight_release_path(path);
-- 
2.17.1


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

* [PATCH 02/10] coresight: funnel: add support for multiple output ports
  2021-10-21  7:38 [PATCH 00/10] Add support for TPDM and TPDA Tao Zhang
  2021-10-21  7:38 ` [PATCH 01/10] coresight: add support to enable more coresight paths Tao Zhang
@ 2021-10-21  7:38 ` Tao Zhang
  2021-10-29 17:48   ` Mathieu Poirier
  2021-10-21  7:38 ` [PATCH 03/10] Coresight: Add driver to support Coresight device TPDM Tao Zhang
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 37+ messages in thread
From: Tao Zhang @ 2021-10-21  7:38 UTC (permalink / raw)
  To: Mathieu Poirier, Suzuki K Poulose, Alexander Shishkin
  Cc: Tao Zhang, Mike Leach, Leo Yan, Greg Kroah-Hartman, coresight,
	linux-arm-kernel, linux-kernel, Tingwei Zhang, Mao Jinlong,
	Yuanfang Zhang, Trilok Soni, Tingwei Zhang

Funnel devices are now capable of supporting multiple-inputs and
multiple-outputs configuration with in built hardware filtering
for TPDM devices. Add software support to this function. Output
port is selected according to the source of the trace path.

The source of the input port on funnels will be marked in the
device tree.
e.g.
tpdm_XXX: tpdm@xxxxxxx {
    ... ... ... ...
};

funnel_XXX: funnel@xxxxxxx {
    ... ... ... ...
    out-ports {
        ... ... ... ...
        port@x {
            ... ... ... ...
            label = <&tpdm_XXX>; <-- To point out tpdm_XXX should
        };                           be outputed from port@x. And
    ... ... ... ...                  this is a hardware static
    };                               connections. Here needs
    ... ... ... ...                  to refer to hardware design.
};

Then driver will parse the source name marked in the device tree, and
save it to the coresight path. When the function needs to know the
source name, it could get the source name from coresight path parameter.
Finally, the output port knows which source it corresponds to, and it
also knows which input port it corresponds to.

Signed-off-by: Tingwei Zhang <tingwei@codeaurora.org>
Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
---
 drivers/hwtracing/coresight/coresight-core.c  | 75 +++++++++++++++----
 .../hwtracing/coresight/coresight-platform.c  |  8 ++
 include/linux/coresight.h                     |  2 +
 3 files changed, 71 insertions(+), 14 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 1e621d61307a..490c9d8d43f9 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -56,6 +56,8 @@ static LIST_HEAD(cs_active_paths);
 const u32 coresight_barrier_pkt[4] = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff};
 EXPORT_SYMBOL_GPL(coresight_barrier_pkt);
 
+static struct coresight_device *coresight_get_source(struct list_head *path);
+
 static const struct cti_assoc_op *cti_assoc_ops;
 
 void coresight_set_cti_ops(const struct cti_assoc_op *cti_op)
@@ -121,14 +123,46 @@ static int coresight_source_is_unique(struct coresight_device *csdev)
 				 csdev, coresight_id_match);
 }
 
+/**
+ * coresight_source_filter - checks whether the connection matches the source
+ * of path if connection is binded to specific source.
+ * @path:	The list of devices
+ * @conn:	The connection of one outport
+ *
+ * Return zero if the connection doesn't have a source binded or source of the
+ * path matches the source binds to connection.
+ */
+static int coresight_source_filter(struct list_head *path,
+			struct coresight_connection *conn)
+{
+	int ret = 0;
+	struct coresight_device *source = NULL;
+
+	if (conn->source_name == NULL)
+		return ret;
+
+	source = coresight_get_source(path);
+	if (source == NULL)
+		return ret;
+
+	if (is_of_node(source->dev.fwnode))
+		return strcmp(conn->source_name,
+			of_node_full_name(to_of_node(source->dev.fwnode)));
+
+	return ret;
+}
+
 static int coresight_find_link_inport(struct coresight_device *csdev,
-				      struct coresight_device *parent)
+				      struct coresight_device *parent,
+				      struct list_head *path)
 {
 	int i;
 	struct coresight_connection *conn;
 
 	for (i = 0; i < parent->pdata->nr_outport; i++) {
 		conn = &parent->pdata->conns[i];
+		if (coresight_source_filter(path, conn))
+			continue;
 		if (conn->child_dev == csdev)
 			return conn->child_port;
 	}
@@ -140,13 +174,16 @@ static int coresight_find_link_inport(struct coresight_device *csdev,
 }
 
 static int coresight_find_link_outport(struct coresight_device *csdev,
-				       struct coresight_device *child)
+				       struct coresight_device *child,
+				       struct list_head *path)
 {
 	int i;
 	struct coresight_connection *conn;
 
 	for (i = 0; i < csdev->pdata->nr_outport; i++) {
 		conn = &csdev->pdata->conns[i];
+		if (coresight_source_filter(path, conn))
+			continue;
 		if (conn->child_dev == child)
 			return conn->outport;
 	}
@@ -358,7 +395,8 @@ static void coresight_disable_sink(struct coresight_device *csdev)
 
 static int coresight_enable_link(struct coresight_device *csdev,
 				 struct coresight_device *parent,
-				 struct coresight_device *child)
+				 struct coresight_device *child,
+				 struct list_head *path)
 {
 	int ret = 0;
 	int link_subtype;
@@ -367,8 +405,8 @@ static int coresight_enable_link(struct coresight_device *csdev,
 	if (!parent || !child)
 		return -EINVAL;
 
-	inport = coresight_find_link_inport(csdev, parent);
-	outport = coresight_find_link_outport(csdev, child);
+	inport = coresight_find_link_inport(csdev, parent, path);
+	outport = coresight_find_link_outport(csdev, child, path);
 	link_subtype = csdev->subtype.link_subtype;
 
 	if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG && inport < 0)
@@ -393,7 +431,8 @@ static int coresight_enable_link(struct coresight_device *csdev,
 
 static void coresight_disable_link(struct coresight_device *csdev,
 				   struct coresight_device *parent,
-				   struct coresight_device *child)
+				   struct coresight_device *child,
+				   struct list_head *path)
 {
 	int i, nr_conns;
 	int link_subtype;
@@ -402,8 +441,8 @@ static void coresight_disable_link(struct coresight_device *csdev,
 	if (!parent || !child)
 		return;
 
-	inport = coresight_find_link_inport(csdev, parent);
-	outport = coresight_find_link_outport(csdev, child);
+	inport = coresight_find_link_inport(csdev, parent, path);
+	outport = coresight_find_link_outport(csdev, child, path);
 	link_subtype = csdev->subtype.link_subtype;
 
 	if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) {
@@ -518,7 +557,7 @@ static void coresight_disable_path_from(struct list_head *path,
 		case CORESIGHT_DEV_TYPE_LINK:
 			parent = list_prev_entry(nd, link)->csdev;
 			child = list_next_entry(nd, link)->csdev;
-			coresight_disable_link(csdev, parent, child);
+			coresight_disable_link(csdev, parent, child, path);
 			break;
 		default:
 			break;
@@ -573,7 +612,7 @@ int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data)
 		case CORESIGHT_DEV_TYPE_LINK:
 			parent = list_prev_entry(nd, link)->csdev;
 			child = list_next_entry(nd, link)->csdev;
-			ret = coresight_enable_link(csdev, parent, child);
+			ret = coresight_enable_link(csdev, parent, child, path);
 			if (ret)
 				goto err;
 			break;
@@ -801,7 +840,8 @@ static void coresight_drop_device(struct coresight_device *csdev)
  */
 static int _coresight_build_path(struct coresight_device *csdev,
 				 struct coresight_device *sink,
-				 struct list_head *path)
+				 struct list_head *path,
+				 struct coresight_device *source)
 {
 	int i, ret;
 	bool found = false;
@@ -813,7 +853,7 @@ static int _coresight_build_path(struct coresight_device *csdev,
 
 	if (coresight_is_percpu_source(csdev) && coresight_is_percpu_sink(sink) &&
 	    sink == per_cpu(csdev_sink, source_ops(csdev)->cpu_id(csdev))) {
-		if (_coresight_build_path(sink, sink, path) == 0) {
+		if (_coresight_build_path(sink, sink, path, source) == 0) {
 			found = true;
 			goto out;
 		}
@@ -824,8 +864,15 @@ static int _coresight_build_path(struct coresight_device *csdev,
 		struct coresight_device *child_dev;
 
 		child_dev = csdev->pdata->conns[i].child_dev;
+
+		if (csdev->pdata->conns[i].source_name &&
+			is_of_node(source->dev.fwnode) &&
+		    strcmp(csdev->pdata->conns[i].source_name,
+				of_node_full_name(to_of_node(source->dev.fwnode))))
+			continue;
+
 		if (child_dev &&
-		    _coresight_build_path(child_dev, sink, path) == 0) {
+		    _coresight_build_path(child_dev, sink, path, source) == 0) {
 			found = true;
 			break;
 		}
@@ -870,7 +917,7 @@ struct list_head *coresight_build_path(struct coresight_device *source,
 
 	INIT_LIST_HEAD(path);
 
-	rc = _coresight_build_path(source, sink, path);
+	rc = _coresight_build_path(source, sink, path, source);
 	if (rc) {
 		kfree(path);
 		return ERR_PTR(rc);
diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c
index c594f45319fc..60c43ab149a5 100644
--- a/drivers/hwtracing/coresight/coresight-platform.c
+++ b/drivers/hwtracing/coresight/coresight-platform.c
@@ -222,6 +222,7 @@ static int of_coresight_parse_endpoint(struct device *dev,
 	struct of_endpoint endpoint, rendpoint;
 	struct device_node *rparent = NULL;
 	struct device_node *rep = NULL;
+	struct device_node *sn = NULL;
 	struct device *rdev = NULL;
 	struct fwnode_handle *rdev_fwnode;
 	struct coresight_connection *conn;
@@ -269,6 +270,13 @@ static int of_coresight_parse_endpoint(struct device *dev,
 		 */
 		conn->child_fwnode = fwnode_handle_get(rdev_fwnode);
 		conn->child_port = rendpoint.port;
+		conn->source_name = NULL;
+		sn = of_parse_phandle(ep, "label", 0);
+		if (sn) {
+			conn->source_name = sn->full_name;
+			of_node_put(sn);
+		}
+
 		/* Connection record updated */
 	} while (0);
 
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 93a2922b7653..7065b60a767b 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -170,6 +170,7 @@ struct coresight_desc {
  * struct coresight_connection - representation of a single connection
  * @outport:	a connection's output port number.
  * @child_port:	remote component's port number @output is connected to.
+ * @source_name:source component's name.
  * @chid_fwnode: remote component's fwnode handle.
  * @child_dev:	a @coresight_device representation of the component
 		connected to @outport.
@@ -178,6 +179,7 @@ struct coresight_desc {
 struct coresight_connection {
 	int outport;
 	int child_port;
+	const char *source_name;
 	struct fwnode_handle *child_fwnode;
 	struct coresight_device *child_dev;
 	struct coresight_sysfs_link *link;
-- 
2.17.1


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

* [PATCH 03/10] Coresight: Add driver to support Coresight device TPDM
  2021-10-21  7:38 [PATCH 00/10] Add support for TPDM and TPDA Tao Zhang
  2021-10-21  7:38 ` [PATCH 01/10] coresight: add support to enable more coresight paths Tao Zhang
  2021-10-21  7:38 ` [PATCH 02/10] coresight: funnel: add support for multiple output ports Tao Zhang
@ 2021-10-21  7:38 ` Tao Zhang
  2021-11-02 17:59   ` Mathieu Poirier
  2021-10-21  7:38 ` [PATCH 04/10] Coresight: Enable BC and GPR for TPDM driver Tao Zhang
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 37+ messages in thread
From: Tao Zhang @ 2021-10-21  7:38 UTC (permalink / raw)
  To: Mathieu Poirier, Suzuki K Poulose, Alexander Shishkin
  Cc: Tao Zhang, Mike Leach, Leo Yan, Greg Kroah-Hartman, coresight,
	linux-arm-kernel, linux-kernel, Tingwei Zhang, Mao Jinlong,
	Yuanfang Zhang, Trilok Soni

Add driver to support Coresight device TPDM. This driver provides
support for configuring monitor. Monitors are primarily responsible
for data set collection and support the ability to collect any
permutation of data set types. Monitors are also responsible for
interaction with system cross triggering.

Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
---
 .../bindings/arm/coresight-tpdm.yaml          |  86 +++
 MAINTAINERS                                   |   5 +
 drivers/hwtracing/coresight/Kconfig           |   9 +
 drivers/hwtracing/coresight/Makefile          |   1 +
 drivers/hwtracing/coresight/coresight-tpdm.c  | 583 ++++++++++++++++++
 5 files changed, 684 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
 create mode 100644 drivers/hwtracing/coresight/coresight-tpdm.c

diff --git a/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml b/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
new file mode 100644
index 000000000000..44541075d77f
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/coresight-tpdm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Trace, Profiling and Diagnostics Monitor - TPDM
+
+description: |
+  The TPDM or Monitor serves as data collection component for various dataset
+  types specified in the QPMDA spec. It covers Basic Counts (BC), Tenure
+  Counts (TC), Continuous Multi-Bit (CMB), and Discrete Single Bit (DSB). It
+  performs data collection in the data producing clock domain and transfers it
+  to the data collection time domain, generally ATB clock domain.
+
+  The primary use case of the TPDM is to collect data from different data
+  sources and send it to a TPDA for packetization, timestamping, and funneling.
+
+maintainers:
+  - Tao Zhang <quic_taozha@quicinc.com>
+
+properties:
+  $nodename:
+    pattern: "^tpdm(@[0-9a-f]+)$"
+  compatible:
+    items:
+      - const: arm,primecell
+
+  reg:
+    maxItems: 1
+
+  reg-names:
+    items:
+      - const: tpdm-base
+
+  atid:
+    maxItems: 1
+    description: |
+      The QPMDA specification repurposed the ATID field of the AMBA ATB
+      specification to use it to convey packetization information to the
+      Aggregator.
+
+  out-ports:
+    description: |
+      Output connections from the TPDM to legacy CoreSight trace bus.
+    $ref: /schemas/graph.yaml#/properties/ports
+    properties:
+      port:
+        description: Output connection from the TPDM to legacy CoreSight
+          Trace bus.
+        $ref: /schemas/graph.yaml#/properties/port
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - atid
+  - clocks
+  - clock-names
+
+additionalProperties: false
+
+examples:
+  # minimum TPDM definition.
+  - |
+    tpdm@6980000 {
+      compatible = "arm,primecell";
+      reg = <0x6980000 0x1000>;
+      reg-names = "tpdm-base";
+
+      clocks = <&aoss_qmp>;
+      clock-names = "apb_pclk";
+
+      atid = <78>;
+      out-ports {
+        port {
+          tpdm_turing_out_funnel_turing: endpoint {
+            remote-endpoint =
+              <&funnel_turing_in_tpdm_turing>;
+          };
+        };
+      };
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index eeb4c70b3d5b..cabecf760488 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15303,6 +15303,11 @@ L:	netdev@vger.kernel.org
 S:	Supported
 F:	drivers/net/ipa/
 
+QCOM CORESIGHT TPDM DRIVER
+M:	Tao Zhang <quic_taozha@quicinc.com>
+S:	Maintained
+F:	drivers/hwtracing/coresight/coresight-tpdm.c
+
 QEMU MACHINE EMULATOR AND VIRTUALIZER SUPPORT
 M:	Gabriel Somlo <somlo@cmu.edu>
 M:	"Michael S. Tsirkin" <mst@redhat.com>
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index f026e5c0e777..abe244a968f6 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -188,4 +188,13 @@ config CORESIGHT_TRBE
 
 	  To compile this driver as a module, choose M here: the module will be
 	  called coresight-trbe.
+
+config CORESIGHT_TPDM
+	tristate "CoreSight Trace, Profiling & Diagnostics Monitor driver"
+	select CORESIGHT_LINKS_AND_SINKS
+	help
+	  This driver provides support for configuring monitor. Monitors are
+	  primarily responsible for data set collection and support the
+	  ability to collect any permutation of data set types. Monitors are
+	  also responsible for interaction with system cross triggering.
 endif
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index b6c4a48140ec..e7392a0dddeb 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -25,5 +25,6 @@ obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o
 obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o
 obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
 obj-$(CONFIG_CORESIGHT_TRBE) += coresight-trbe.o
+obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o
 coresight-cti-y := coresight-cti-core.o	coresight-cti-platform.o \
 		   coresight-cti-sysfs.o
diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
new file mode 100644
index 000000000000..906776c859d6
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-tpdm.c
@@ -0,0 +1,583 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/amba/bus.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/bitmap.h>
+#include <linux/of.h>
+#include <linux/coresight.h>
+#include <linux/regulator/consumer.h>
+
+#include "coresight-priv.h"
+
+#define tpdm_writel(drvdata, val, off)	__raw_writel((val), drvdata->base + off)
+#define tpdm_readl(drvdata, off)		__raw_readl(drvdata->base + off)
+
+#define TPDM_LOCK(drvdata)						\
+do {									\
+	mb(); /* ensure configuration take effect before we lock it */	\
+	tpdm_writel(drvdata, 0x0, CORESIGHT_LAR);			\
+} while (0)
+#define TPDM_UNLOCK(drvdata)						\
+do {									\
+	tpdm_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR);		\
+	mb(); /* ensure unlock take effect before we configure */	\
+} while (0)
+
+/* GPR Registers */
+#define TPDM_GPR_CR(n)		(0x0 + (n * 4))
+
+/* BC Subunit Registers */
+#define TPDM_BC_CR		(0x280)
+#define TPDM_BC_SATROLL		(0x284)
+#define TPDM_BC_CNTENSET	(0x288)
+#define TPDM_BC_CNTENCLR	(0x28C)
+#define TPDM_BC_INTENSET	(0x290)
+#define TPDM_BC_INTENCLR	(0x294)
+#define TPDM_BC_TRIG_LO(n)	(0x298 + (n * 4))
+#define TPDM_BC_TRIG_HI(n)	(0x318 + (n * 4))
+#define TPDM_BC_GANG		(0x398)
+#define TPDM_BC_OVERFLOW(n)	(0x39C + (n * 4))
+#define TPDM_BC_OVSR		(0x3C0)
+#define TPDM_BC_SELR		(0x3C4)
+#define TPDM_BC_CNTR_LO		(0x3C8)
+#define TPDM_BC_CNTR_HI		(0x3CC)
+#define TPDM_BC_SHADOW_LO(n)	(0x3D0 + (n * 4))
+#define TPDM_BC_SHADOW_HI(n)	(0x450 + (n * 4))
+#define TPDM_BC_SWINC		(0x4D0)
+#define TPDM_BC_MSR(n)		(0x4F0 + (n * 4))
+
+/* TC Subunit Registers */
+#define TPDM_TC_CR		(0x500)
+#define TPDM_TC_CNTENSET	(0x504)
+#define TPDM_TC_CNTENCLR	(0x508)
+#define TPDM_TC_INTENSET	(0x50C)
+#define TPDM_TC_INTENCLR	(0x510)
+#define TPDM_TC_TRIG_SEL(n)	(0x514 + (n * 4))
+#define TPDM_TC_TRIG_LO(n)	(0x534 + (n * 4))
+#define TPDM_TC_TRIG_HI(n)	(0x554 + (n * 4))
+#define TPDM_TC_OVSR_GP		(0x580)
+#define TPDM_TC_OVSR_IMPL	(0x584)
+#define TPDM_TC_SELR		(0x588)
+#define TPDM_TC_CNTR_LO		(0x58C)
+#define TPDM_TC_CNTR_HI		(0x590)
+#define TPDM_TC_SHADOW_LO(n)	(0x594 + (n * 4))
+#define TPDM_TC_SHADOW_HI(n)	(0x644 + (n * 4))
+#define TPDM_TC_SWINC		(0x700)
+#define TPDM_TC_MSR(n)		(0x768 + (n * 4))
+
+/* DSB Subunit Registers */
+#define TPDM_DSB_CR		(0x780)
+#define TPDM_DSB_TIER		(0x784)
+#define TPDM_DSB_TPR(n)		(0x788 + (n * 4))
+#define TPDM_DSB_TPMR(n)	(0x7A8 + (n * 4))
+#define TPDM_DSB_XPR(n)		(0x7C8 + (n * 4))
+#define TPDM_DSB_XPMR(n)	(0x7E8 + (n * 4))
+#define TPDM_DSB_EDCR(n)	(0x808 + (n * 4))
+#define TPDM_DSB_EDCMR(n)	(0x848 + (n * 4))
+#define TPDM_DSB_CA_SELECT(n)	(0x86c + (n * 4))
+#define TPDM_DSB_MSR(n)		(0x980 + (n * 4))
+
+/* CMB/MCMB Subunit Registers */
+#define TPDM_CMB_CR		(0xA00)
+#define TPDM_CMB_TIER		(0xA04)
+#define TPDM_CMB_TPR(n)		(0xA08 + (n * 4))
+#define TPDM_CMB_TPMR(n)	(0xA10 + (n * 4))
+#define TPDM_CMB_XPR(n)		(0xA18 + (n * 4))
+#define TPDM_CMB_XPMR(n)	(0xA20 + (n * 4))
+#define TPDM_CMB_MARKR		(0xA28)
+#define TPDM_CMB_READCTL	(0xA70)
+#define TPDM_CMB_READVAL	(0xA74)
+#define TPDM_CMB_MSR(n)		(0xA80 + (n * 4))
+
+/* TPDM Specific Registers */
+#define TPDM_ITATBCNTRL		(0xEF0)
+#define TPDM_CLK_CTRL		(0x220)
+#define TPDM_ITCNTRL		(0xF00)
+
+
+#define TPDM_DATASETS		32
+#define TPDM_BC_MAX_COUNTERS	32
+#define TPDM_BC_MAX_OVERFLOW	6
+#define TPDM_BC_MAX_MSR		4
+#define TPDM_TC_MAX_COUNTERS	44
+#define TPDM_TC_MAX_TRIG	8
+#define TPDM_TC_MAX_MSR		6
+#define TPDM_DSB_MAX_PATT	8
+#define TPDM_DSB_MAX_SELECT	8
+#define TPDM_DSB_MAX_MSR	32
+#define TPDM_DSB_MAX_EDCR	16
+#define TPDM_DSB_MAX_LINES	256
+#define TPDM_CMB_PATT_CMP	2
+#define TPDM_CMB_MAX_MSR	128
+#define TPDM_MCMB_MAX_LANES	8
+
+/* DSB programming modes */
+#define TPDM_DSB_MODE_CYCACC(val)	BMVAL(val, 0, 2)
+#define TPDM_DSB_MODE_PERF		BIT(3)
+#define TPDM_DSB_MODE_HPBYTESEL(val)	BMVAL(val, 4, 8)
+#define TPDM_MODE_ALL			(0xFFFFFFF)
+
+#define NUM_OF_BITS		32
+#define TPDM_GPR_REGS_MAX	160
+
+#define TPDM_TRACE_ID_START	128
+
+#define TPDM_REVISION_A		0
+#define TPDM_REVISION_B		1
+
+#define HW_ENABLE_CHECK_VALUE   0x10
+
+
+#define ATBCNTRL_VAL_32		0xC00F1409
+#define ATBCNTRL_VAL_64		0xC01F1409
+
+
+enum tpdm_dataset {
+	TPDM_DS_IMPLDEF,
+	TPDM_DS_DSB,
+	TPDM_DS_CMB,
+	TPDM_DS_TC,
+	TPDM_DS_BC,
+	TPDM_DS_GPR,
+	TPDM_DS_MCMB,
+};
+
+enum tpdm_mode {
+	TPDM_MODE_ATB,
+	TPDM_MODE_APB,
+};
+
+enum tpdm_support_type {
+	TPDM_SUPPORT_TYPE_FULL,
+	TPDM_SUPPORT_TYPE_PARTIAL,
+	TPDM_SUPPORT_TYPE_NO,
+};
+
+enum tpdm_cmb_patt_bits {
+	TPDM_CMB_LSB,
+	TPDM_CMB_MSB,
+};
+
+#ifdef CONFIG_CORESIGHT_TPDM_DEFAULT_ENABLE
+static int boot_enable = 1;
+#else
+static int boot_enable;
+#endif
+
+struct gpr_dataset {
+	DECLARE_BITMAP(gpr_dirty, TPDM_GPR_REGS_MAX);
+	uint32_t		gp_regs[TPDM_GPR_REGS_MAX];
+};
+
+struct bc_dataset {
+	enum tpdm_mode		capture_mode;
+	enum tpdm_mode		retrieval_mode;
+	uint32_t		sat_mode;
+	uint32_t		enable_counters;
+	uint32_t		clear_counters;
+	uint32_t		enable_irq;
+	uint32_t		clear_irq;
+	uint32_t		trig_val_lo[TPDM_BC_MAX_COUNTERS];
+	uint32_t		trig_val_hi[TPDM_BC_MAX_COUNTERS];
+	uint32_t		enable_ganging;
+	uint32_t		overflow_val[TPDM_BC_MAX_OVERFLOW];
+	uint32_t		msr[TPDM_BC_MAX_MSR];
+};
+
+struct tc_dataset {
+	enum tpdm_mode		capture_mode;
+	enum tpdm_mode		retrieval_mode;
+	bool			sat_mode;
+	uint32_t		enable_counters;
+	uint32_t		clear_counters;
+	uint32_t		enable_irq;
+	uint32_t		clear_irq;
+	uint32_t		trig_sel[TPDM_TC_MAX_TRIG];
+	uint32_t		trig_val_lo[TPDM_TC_MAX_TRIG];
+	uint32_t		trig_val_hi[TPDM_TC_MAX_TRIG];
+	uint32_t		msr[TPDM_TC_MAX_MSR];
+};
+
+struct dsb_dataset {
+	uint32_t		mode;
+	uint32_t		edge_ctrl[TPDM_DSB_MAX_EDCR];
+	uint32_t		edge_ctrl_mask[TPDM_DSB_MAX_EDCR / 2];
+	uint32_t		patt_val[TPDM_DSB_MAX_PATT];
+	uint32_t		patt_mask[TPDM_DSB_MAX_PATT];
+	bool			patt_ts;
+	bool			patt_type;
+	uint32_t		trig_patt_val[TPDM_DSB_MAX_PATT];
+	uint32_t		trig_patt_mask[TPDM_DSB_MAX_PATT];
+	bool			trig_ts;
+	bool			trig_type;
+	uint32_t		select_val[TPDM_DSB_MAX_SELECT];
+	uint32_t		msr[TPDM_DSB_MAX_MSR];
+};
+
+struct mcmb_dataset {
+	uint8_t		mcmb_trig_lane;
+	uint8_t		mcmb_lane_select;
+};
+
+struct cmb_dataset {
+	bool			trace_mode;
+	uint32_t		cycle_acc;
+	uint32_t		patt_val[TPDM_CMB_PATT_CMP];
+	uint32_t		patt_mask[TPDM_CMB_PATT_CMP];
+	bool			patt_ts;
+	uint32_t		trig_patt_val[TPDM_CMB_PATT_CMP];
+	uint32_t		trig_patt_mask[TPDM_CMB_PATT_CMP];
+	bool			trig_ts;
+	bool			ts_all;
+	uint32_t		msr[TPDM_CMB_MAX_MSR];
+	uint8_t			read_ctl_reg;
+	struct mcmb_dataset	*mcmb;
+};
+
+DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm");
+
+struct tpdm_drvdata {
+	void __iomem		*base;
+	struct device		*dev;
+	struct coresight_device	*csdev;
+	int			nr_tclk;
+	struct clk		**tclk;
+	int			nr_treg;
+	struct regulator	**treg;
+	struct mutex		lock;
+	bool			enable;
+	bool			clk_enable;
+	DECLARE_BITMAP(datasets, TPDM_DATASETS);
+	DECLARE_BITMAP(enable_ds, TPDM_DATASETS);
+	enum tpdm_support_type	tc_trig_type;
+	enum tpdm_support_type	bc_trig_type;
+	enum tpdm_support_type	bc_gang_type;
+	uint32_t		bc_counters_avail;
+	uint32_t		tc_counters_avail;
+	struct gpr_dataset	*gpr;
+	struct bc_dataset	*bc;
+	struct tc_dataset	*tc;
+	struct dsb_dataset	*dsb;
+	struct cmb_dataset	*cmb;
+	int			traceid;
+	uint32_t		version;
+	bool			msr_support;
+	bool			msr_fix_req;
+	bool			cmb_msr_skip;
+};
+
+static void tpdm_init_default_data(struct tpdm_drvdata *drvdata);
+
+static void __tpdm_enable(struct tpdm_drvdata *drvdata)
+{
+	TPDM_UNLOCK(drvdata);
+
+	if (drvdata->clk_enable)
+		tpdm_writel(drvdata, 0x1, TPDM_CLK_CTRL);
+
+	TPDM_LOCK(drvdata);
+}
+
+static int tpdm_enable(struct coresight_device *csdev,
+		       struct perf_event *event, u32 mode)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+	int ret = 0;
+
+	if (drvdata->enable) {
+		dev_err(drvdata->dev,
+			"TPDM setup already enabled,Skipping enablei\n");
+		return ret;
+	}
+
+	mutex_lock(&drvdata->lock);
+	__tpdm_enable(drvdata);
+	drvdata->enable = true;
+	mutex_unlock(&drvdata->lock);
+
+	dev_info(drvdata->dev, "TPDM tracing enabled\n");
+	return 0;
+}
+
+static void __tpdm_disable(struct tpdm_drvdata *drvdata)
+{
+	TPDM_UNLOCK(drvdata);
+
+	if (drvdata->clk_enable)
+		tpdm_writel(drvdata, 0x0, TPDM_CLK_CTRL);
+
+	TPDM_LOCK(drvdata);
+}
+
+static void tpdm_disable(struct coresight_device *csdev,
+			 struct perf_event *event)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (!drvdata->enable) {
+		dev_err(drvdata->dev,
+			"TPDM setup already disabled, Skipping disable\n");
+		return;
+	}
+	mutex_lock(&drvdata->lock);
+	__tpdm_disable(drvdata);
+	drvdata->enable = false;
+	mutex_unlock(&drvdata->lock);
+
+	dev_info(drvdata->dev, "TPDM tracing disabled\n");
+}
+
+static int tpdm_trace_id(struct coresight_device *csdev)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	return drvdata->traceid;
+}
+
+static const struct coresight_ops_source tpdm_source_ops = {
+	.trace_id	= tpdm_trace_id,
+	.enable		= tpdm_enable,
+	.disable	= tpdm_disable,
+};
+
+static const struct coresight_ops tpdm_cs_ops = {
+	.source_ops	= &tpdm_source_ops,
+};
+
+static int tpdm_datasets_alloc(struct tpdm_drvdata *drvdata)
+{
+	if (test_bit(TPDM_DS_GPR, drvdata->datasets)) {
+		drvdata->gpr = devm_kzalloc(drvdata->dev, sizeof(*drvdata->gpr),
+					    GFP_KERNEL);
+		if (!drvdata->gpr)
+			return -ENOMEM;
+	}
+	if (test_bit(TPDM_DS_BC, drvdata->datasets)) {
+		drvdata->bc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->bc),
+					   GFP_KERNEL);
+		if (!drvdata->bc)
+			return -ENOMEM;
+	}
+	if (test_bit(TPDM_DS_TC, drvdata->datasets)) {
+		drvdata->tc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->tc),
+					   GFP_KERNEL);
+		if (!drvdata->tc)
+			return -ENOMEM;
+	}
+	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
+		drvdata->dsb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->dsb),
+					    GFP_KERNEL);
+		if (!drvdata->dsb)
+			return -ENOMEM;
+	}
+	if (test_bit(TPDM_DS_CMB, drvdata->datasets)) {
+		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
+					    GFP_KERNEL);
+		if (!drvdata->cmb)
+			return -ENOMEM;
+	} else if (test_bit(TPDM_DS_MCMB, drvdata->datasets)) {
+		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
+					    GFP_KERNEL);
+		if (!drvdata->cmb)
+			return -ENOMEM;
+		drvdata->cmb->mcmb = devm_kzalloc(drvdata->dev,
+						  sizeof(*drvdata->cmb->mcmb),
+						  GFP_KERNEL);
+		if (!drvdata->cmb->mcmb)
+			return -ENOMEM;
+	}
+	return 0;
+}
+
+static void tpdm_init_default_data(struct tpdm_drvdata *drvdata)
+{
+	if (test_bit(TPDM_DS_BC, drvdata->datasets))
+		drvdata->bc->retrieval_mode = TPDM_MODE_ATB;
+
+	if (test_bit(TPDM_DS_TC, drvdata->datasets))
+		drvdata->tc->retrieval_mode = TPDM_MODE_ATB;
+
+	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
+		drvdata->dsb->trig_ts = true;
+		drvdata->dsb->trig_type = false;
+	}
+
+	if (test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	    test_bit(TPDM_DS_MCMB, drvdata->datasets))
+		drvdata->cmb->trig_ts = true;
+}
+
+static int tpdm_parse_of_data(struct tpdm_drvdata *drvdata)
+{
+	int i, ret;
+	const char *tclk_name, *treg_name;
+	struct device_node *node = drvdata->dev->of_node;
+
+	drvdata->clk_enable = of_property_read_bool(node, "qcom,clk-enable");
+	drvdata->msr_fix_req = of_property_read_bool(node, "qcom,msr-fix-req");
+	drvdata->cmb_msr_skip = of_property_read_bool(node,
+					"qcom,cmb-msr-skip");
+
+	drvdata->nr_tclk = of_property_count_strings(node, "qcom,tpdm-clks");
+	if (drvdata->nr_tclk > 0) {
+		drvdata->tclk = devm_kzalloc(drvdata->dev, drvdata->nr_tclk *
+					     sizeof(*drvdata->tclk),
+					     GFP_KERNEL);
+		if (!drvdata->tclk)
+			return -ENOMEM;
+
+		for (i = 0; i < drvdata->nr_tclk; i++) {
+			ret = of_property_read_string_index(node,
+					    "qcom,tpdm-clks", i, &tclk_name);
+			if (ret)
+				return ret;
+
+			drvdata->tclk[i] = devm_clk_get(drvdata->dev,
+							tclk_name);
+			if (IS_ERR(drvdata->tclk[i]))
+				return PTR_ERR(drvdata->tclk[i]);
+		}
+	}
+
+	drvdata->nr_treg = of_property_count_strings(node, "qcom,tpdm-regs");
+	if (drvdata->nr_treg > 0) {
+		drvdata->treg = devm_kzalloc(drvdata->dev, drvdata->nr_treg *
+					     sizeof(*drvdata->treg),
+					     GFP_KERNEL);
+		if (!drvdata->treg)
+			return -ENOMEM;
+
+		for (i = 0; i < drvdata->nr_treg; i++) {
+			ret = of_property_read_string_index(node,
+					    "qcom,tpdm-regs", i, &treg_name);
+			if (ret)
+				return ret;
+
+			drvdata->treg[i] = devm_regulator_get(drvdata->dev,
+							treg_name);
+			if (IS_ERR(drvdata->treg[i]))
+				return PTR_ERR(drvdata->treg[i]);
+		}
+	}
+
+	return 0;
+}
+
+static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
+{
+	int ret, i;
+	uint32_t pidr, devid;
+	struct device *dev = &adev->dev;
+	struct coresight_platform_data *pdata;
+	struct tpdm_drvdata *drvdata;
+	struct coresight_desc desc = { 0 };
+	static int traceid = TPDM_TRACE_ID_START;
+	uint32_t version;
+
+	desc.name = coresight_alloc_device_name(&tpdm_devs, dev);
+	if (!desc.name)
+		return -ENOMEM;
+	pdata = coresight_get_platform_data(dev);
+	if (IS_ERR(pdata))
+		return PTR_ERR(pdata);
+	adev->dev.platform_data = pdata;
+
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+	drvdata->dev = &adev->dev;
+	dev_set_drvdata(dev, drvdata);
+
+	drvdata->base = devm_ioremap_resource(dev, &adev->res);
+	if (!drvdata->base)
+		return -ENOMEM;
+
+	mutex_init(&drvdata->lock);
+
+	ret = tpdm_parse_of_data(drvdata);
+	if (ret) {
+		dev_err(drvdata->dev, "TPDM parse of data fail\n");
+		return -EINVAL;
+	}
+
+	desc.type = CORESIGHT_DEV_TYPE_SOURCE;
+	desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
+	desc.ops = &tpdm_cs_ops;
+	desc.pdata = adev->dev.platform_data;
+	desc.dev = &adev->dev;
+	drvdata->csdev = coresight_register(&desc);
+	if (IS_ERR(drvdata->csdev))
+		return PTR_ERR(drvdata->csdev);
+
+	version = tpdm_readl(drvdata, CORESIGHT_PERIPHIDR2);
+	drvdata->version = BMVAL(version, 4, 7);
+
+	if (drvdata->version)
+		drvdata->msr_support = true;
+
+	pidr = tpdm_readl(drvdata, CORESIGHT_PERIPHIDR0);
+	for (i = 0; i < TPDM_DATASETS; i++) {
+		if (pidr & BIT(i)) {
+			__set_bit(i, drvdata->datasets);
+			__set_bit(i, drvdata->enable_ds);
+		}
+	}
+
+	ret = tpdm_datasets_alloc(drvdata);
+	if (ret) {
+		coresight_unregister(drvdata->csdev);
+		return ret;
+	}
+
+	tpdm_init_default_data(drvdata);
+
+	devid = tpdm_readl(drvdata, CORESIGHT_DEVID);
+	drvdata->tc_trig_type = BMVAL(devid, 27, 28);
+	drvdata->bc_trig_type = BMVAL(devid, 25, 26);
+	drvdata->bc_gang_type = BMVAL(devid, 23, 24);
+	drvdata->bc_counters_avail = BMVAL(devid, 6, 10) + 1;
+	drvdata->tc_counters_avail = BMVAL(devid, 4, 5) + 1;
+
+	drvdata->traceid = traceid++;
+
+	dev_dbg(drvdata->dev, "TPDM initialized\n");
+
+	if (boot_enable)
+		coresight_enable(drvdata->csdev);
+
+	pm_runtime_put(&adev->dev);
+
+	return 0;
+}
+
+static struct amba_id tpdm_ids[] = {
+	{
+		.id     = 0x001f0e00,
+		.mask   = 0x00ffff00,
+		.data	= "TPDM",
+	},
+	{ 0, 0},
+};
+
+static struct amba_driver tpdm_driver = {
+	.drv = {
+		.name   = "coresight-tpdm",
+		.owner	= THIS_MODULE,
+		.suppress_bind_attrs = true,
+	},
+	.probe          = tpdm_probe,
+	.id_table	= tpdm_ids,
+};
+
+module_amba_driver(tpdm_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Monitor driver");
-- 
2.17.1


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

* [PATCH 04/10] Coresight: Enable BC and GPR for TPDM driver
  2021-10-21  7:38 [PATCH 00/10] Add support for TPDM and TPDA Tao Zhang
                   ` (2 preceding siblings ...)
  2021-10-21  7:38 ` [PATCH 03/10] Coresight: Add driver to support Coresight device TPDM Tao Zhang
@ 2021-10-21  7:38 ` Tao Zhang
  2021-11-03 19:43   ` Mathieu Poirier
  2021-10-21  7:38 ` [PATCH 05/10] Coresight: Add interface for TPDM BC subunit Tao Zhang
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 37+ messages in thread
From: Tao Zhang @ 2021-10-21  7:38 UTC (permalink / raw)
  To: Mathieu Poirier, Suzuki K Poulose, Alexander Shishkin
  Cc: Tao Zhang, Mike Leach, Leo Yan, Greg Kroah-Hartman, coresight,
	linux-arm-kernel, linux-kernel, Tingwei Zhang, Mao Jinlong,
	Yuanfang Zhang, Trilok Soni

Enable GPR and Basic Counts(BC) for TPDM. Add GPR interface and
basic control sysFS interface for TPDM. The GPR interface has RW
and RO fields for controlling external logic and mapping core
signals to an APB accessible address in the TPDM address map.

Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
---
 drivers/hwtracing/coresight/coresight-tpdm.c | 334 +++++++++++++++++++
 1 file changed, 334 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
index 906776c859d6..c0a01979e42f 100644
--- a/drivers/hwtracing/coresight/coresight-tpdm.c
+++ b/drivers/hwtracing/coresight/coresight-tpdm.c
@@ -276,6 +276,93 @@ struct tpdm_drvdata {
 
 static void tpdm_init_default_data(struct tpdm_drvdata *drvdata);
 
+static void __tpdm_enable_gpr(struct tpdm_drvdata *drvdata)
+{
+	int i;
+
+	for (i = 0; i < TPDM_GPR_REGS_MAX; i++) {
+		if (!test_bit(i, drvdata->gpr->gpr_dirty))
+			continue;
+		tpdm_writel(drvdata, drvdata->gpr->gp_regs[i], TPDM_GPR_CR(i));
+	}
+}
+
+static void __tpdm_config_bc_msr(struct tpdm_drvdata *drvdata)
+{
+	int i;
+
+	if (!drvdata->msr_support)
+		return;
+
+	for (i = 0; i < TPDM_BC_MAX_MSR; i++)
+		tpdm_writel(drvdata, drvdata->bc->msr[i], TPDM_BC_MSR(i));
+}
+
+static void __tpdm_enable_bc(struct tpdm_drvdata *drvdata)
+{
+	int i;
+	uint32_t val;
+
+	if (drvdata->bc->sat_mode)
+		tpdm_writel(drvdata, drvdata->bc->sat_mode,
+			    TPDM_BC_SATROLL);
+	else
+		tpdm_writel(drvdata, 0x0, TPDM_BC_SATROLL);
+
+	if (drvdata->bc->enable_counters) {
+		tpdm_writel(drvdata, 0xFFFFFFFF, TPDM_BC_CNTENCLR);
+		tpdm_writel(drvdata, drvdata->bc->enable_counters,
+			    TPDM_BC_CNTENSET);
+	}
+	if (drvdata->bc->clear_counters)
+		tpdm_writel(drvdata, drvdata->bc->clear_counters,
+			    TPDM_BC_CNTENCLR);
+
+	if (drvdata->bc->enable_irq) {
+		tpdm_writel(drvdata, 0xFFFFFFFF, TPDM_BC_INTENCLR);
+		tpdm_writel(drvdata, drvdata->bc->enable_irq,
+			    TPDM_BC_INTENSET);
+	}
+	if (drvdata->bc->clear_irq)
+		tpdm_writel(drvdata, drvdata->bc->clear_irq,
+			    TPDM_BC_INTENCLR);
+
+	if (drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_FULL) {
+		for (i = 0; i < drvdata->bc_counters_avail; i++) {
+			tpdm_writel(drvdata, drvdata->bc->trig_val_lo[i],
+				    TPDM_BC_TRIG_LO(i));
+			tpdm_writel(drvdata, drvdata->bc->trig_val_hi[i],
+				    TPDM_BC_TRIG_HI(i));
+		}
+	} else if (drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_PARTIAL) {
+		tpdm_writel(drvdata, drvdata->bc->trig_val_lo[0],
+			    TPDM_BC_TRIG_LO(0));
+		tpdm_writel(drvdata, drvdata->bc->trig_val_hi[0],
+			    TPDM_BC_TRIG_HI(0));
+	}
+
+	if (drvdata->bc->enable_ganging)
+		tpdm_writel(drvdata, drvdata->bc->enable_ganging, TPDM_BC_GANG);
+
+	for (i = 0; i < TPDM_BC_MAX_OVERFLOW; i++)
+		tpdm_writel(drvdata, drvdata->bc->overflow_val[i],
+			    TPDM_BC_OVERFLOW(i));
+
+	__tpdm_config_bc_msr(drvdata);
+
+	val = tpdm_readl(drvdata, TPDM_BC_CR);
+	if (drvdata->bc->retrieval_mode == TPDM_MODE_APB)
+		val = val | BIT(2);
+	else
+		val = val & ~BIT(2);
+	tpdm_writel(drvdata, val, TPDM_BC_CR);
+
+	val = tpdm_readl(drvdata, TPDM_BC_CR);
+	/* Set the enable bit */
+	val = val | BIT(0);
+	tpdm_writel(drvdata, val, TPDM_BC_CR);
+}
+
 static void __tpdm_enable(struct tpdm_drvdata *drvdata)
 {
 	TPDM_UNLOCK(drvdata);
@@ -283,6 +370,12 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata)
 	if (drvdata->clk_enable)
 		tpdm_writel(drvdata, 0x1, TPDM_CLK_CTRL);
 
+	if (test_bit(TPDM_DS_GPR, drvdata->enable_ds))
+		__tpdm_enable_gpr(drvdata);
+
+	if (test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		__tpdm_enable_bc(drvdata);
+
 	TPDM_LOCK(drvdata);
 }
 
@@ -307,10 +400,22 @@ static int tpdm_enable(struct coresight_device *csdev,
 	return 0;
 }
 
+static void __tpdm_disable_bc(struct tpdm_drvdata *drvdata)
+{
+	uint32_t config;
+
+	config = tpdm_readl(drvdata, TPDM_BC_CR);
+	config = config & ~BIT(0);
+	tpdm_writel(drvdata, config, TPDM_BC_CR);
+}
+
 static void __tpdm_disable(struct tpdm_drvdata *drvdata)
 {
 	TPDM_UNLOCK(drvdata);
 
+	if (test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		__tpdm_disable_bc(drvdata);
+
 	if (drvdata->clk_enable)
 		tpdm_writel(drvdata, 0x0, TPDM_CLK_CTRL);
 
@@ -352,6 +457,234 @@ static const struct coresight_ops tpdm_cs_ops = {
 	.source_ops	= &tpdm_source_ops,
 };
 
+static ssize_t available_datasets_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+
+	if (test_bit(TPDM_DS_IMPLDEF, drvdata->datasets))
+		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s",
+				  "IMPLDEF");
+
+	if (test_bit(TPDM_DS_DSB, drvdata->datasets))
+		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "DSB");
+
+	if (test_bit(TPDM_DS_CMB, drvdata->datasets))
+		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "CMB");
+
+	if (test_bit(TPDM_DS_TC, drvdata->datasets))
+		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "TC");
+
+	if (test_bit(TPDM_DS_BC, drvdata->datasets))
+		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "BC");
+
+	if (test_bit(TPDM_DS_GPR, drvdata->datasets))
+		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "GPR");
+
+	if (test_bit(TPDM_DS_MCMB, drvdata->datasets))
+		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "MCMB");
+
+	size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
+	return size;
+}
+static DEVICE_ATTR_RO(available_datasets);
+
+static ssize_t enable_datasets_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size;
+
+	size = scnprintf(buf, PAGE_SIZE, "%*pb\n", TPDM_DATASETS,
+			 drvdata->enable_ds);
+
+	if (PAGE_SIZE - size < 2)
+		size = -EINVAL;
+	else
+		size += scnprintf(buf + size, 2, "\n");
+	return size;
+}
+
+static ssize_t enable_datasets_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf,
+					  size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+	int i;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+	if (drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	for (i = 0; i < TPDM_DATASETS; i++) {
+		if (test_bit(i, drvdata->datasets) && (val & BIT(i)))
+			__set_bit(i, drvdata->enable_ds);
+		else
+			__clear_bit(i, drvdata->enable_ds);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(enable_datasets);
+
+static ssize_t reset_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf,
+					  size_t size)
+{
+	int ret = 0;
+	unsigned long val;
+	struct mcmb_dataset *mcmb_temp = NULL;
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&drvdata->lock);
+	/* Reset all datasets to ZERO */
+	if (drvdata->gpr != NULL)
+		memset(drvdata->gpr, 0, sizeof(struct gpr_dataset));
+
+	if (drvdata->bc != NULL)
+		memset(drvdata->bc, 0, sizeof(struct bc_dataset));
+
+	if (drvdata->tc != NULL)
+		memset(drvdata->tc, 0, sizeof(struct tc_dataset));
+
+	if (drvdata->dsb != NULL)
+		memset(drvdata->dsb, 0, sizeof(struct dsb_dataset));
+
+	if (drvdata->cmb != NULL) {
+		if (drvdata->cmb->mcmb != NULL) {
+			mcmb_temp = drvdata->cmb->mcmb;
+			memset(drvdata->cmb->mcmb, 0,
+				sizeof(struct mcmb_dataset));
+			}
+
+		memset(drvdata->cmb, 0, sizeof(struct cmb_dataset));
+		drvdata->cmb->mcmb = mcmb_temp;
+	}
+	/* Init the default data */
+	tpdm_init_default_data(drvdata);
+
+	mutex_unlock(&drvdata->lock);
+
+	/* Disable tpdm if enabled */
+	if (drvdata->enable)
+		coresight_disable(drvdata->csdev);
+
+	return size;
+}
+static DEVICE_ATTR_WO(reset);
+
+static ssize_t integration_test_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf,
+					  size_t size)
+{
+	int i, ret = 0;
+	unsigned long val;
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	if (val != 1 && val != 2)
+		return -EINVAL;
+
+	if (!drvdata->enable)
+		return -EINVAL;
+
+	if (val == 1)
+		val = ATBCNTRL_VAL_64;
+	else
+		val = ATBCNTRL_VAL_32;
+	TPDM_UNLOCK(drvdata);
+	tpdm_writel(drvdata, 0x1, TPDM_ITCNTRL);
+
+	for (i = 1; i < 5; i++)
+		tpdm_writel(drvdata, val, TPDM_ITATBCNTRL);
+
+	tpdm_writel(drvdata, 0, TPDM_ITCNTRL);
+	TPDM_LOCK(drvdata);
+	return size;
+}
+static DEVICE_ATTR_WO(integration_test);
+
+static ssize_t gp_regs_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_GPR, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_GPR_REGS_MAX; i++) {
+		if (!test_bit(i, drvdata->gpr->gpr_dirty))
+			continue;
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  drvdata->gpr->gp_regs[i]);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t gp_regs_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long index, val;
+
+	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_GPR, drvdata->datasets) ||
+	    index >= TPDM_GPR_REGS_MAX)
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->gpr->gp_regs[index] = val;
+	__set_bit(index, drvdata->gpr->gpr_dirty);
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(gp_regs);
+
+static struct attribute *tpdm_attrs[] = {
+	&dev_attr_available_datasets.attr,
+	&dev_attr_enable_datasets.attr,
+	&dev_attr_reset.attr,
+	&dev_attr_integration_test.attr,
+	&dev_attr_gp_regs.attr,
+	NULL,
+};
+
+static struct attribute_group tpdm_attr_grp = {
+	.attrs = tpdm_attrs,
+};
+static const struct attribute_group *tpdm_attr_grps[] = {
+	&tpdm_attr_grp,
+	NULL,
+};
+
 static int tpdm_datasets_alloc(struct tpdm_drvdata *drvdata)
 {
 	if (test_bit(TPDM_DS_GPR, drvdata->datasets)) {
@@ -513,6 +846,7 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
 	desc.ops = &tpdm_cs_ops;
 	desc.pdata = adev->dev.platform_data;
 	desc.dev = &adev->dev;
+	desc.groups = tpdm_attr_grps;
 	drvdata->csdev = coresight_register(&desc);
 	if (IS_ERR(drvdata->csdev))
 		return PTR_ERR(drvdata->csdev);
-- 
2.17.1


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

* [PATCH 05/10] Coresight: Add interface for TPDM BC subunit
  2021-10-21  7:38 [PATCH 00/10] Add support for TPDM and TPDA Tao Zhang
                   ` (3 preceding siblings ...)
  2021-10-21  7:38 ` [PATCH 04/10] Coresight: Enable BC and GPR for TPDM driver Tao Zhang
@ 2021-10-21  7:38 ` Tao Zhang
  2021-11-04 18:01   ` Mathieu Poirier
  2021-10-21  7:38 ` [PATCH 06/10] Coresight: Enable and add interface for TPDM TC subunit Tao Zhang
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 37+ messages in thread
From: Tao Zhang @ 2021-10-21  7:38 UTC (permalink / raw)
  To: Mathieu Poirier, Suzuki K Poulose, Alexander Shishkin
  Cc: Tao Zhang, Mike Leach, Leo Yan, Greg Kroah-Hartman, coresight,
	linux-arm-kernel, linux-kernel, Tingwei Zhang, Mao Jinlong,
	Yuanfang Zhang, Trilok Soni

The BC(Basic Counters) interface has RW, WO and RO fields for
controlling BC dataset elements transmitted on ATB flush.
The BC data set subunit supports from 1-32 counter instances
allowing for collection of BC data sets.

Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
---
 drivers/hwtracing/coresight/coresight-tpdm.c | 873 +++++++++++++++++++
 1 file changed, 873 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
index c0a01979e42f..0970c69ac8e2 100644
--- a/drivers/hwtracing/coresight/coresight-tpdm.c
+++ b/drivers/hwtracing/coresight/coresight-tpdm.c
@@ -668,6 +668,878 @@ static ssize_t gp_regs_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(gp_regs);
 
+static ssize_t bc_capture_mode_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n",
+			 drvdata->bc->capture_mode == TPDM_MODE_ATB ?
+			 "ATB" : "APB");
+}
+
+static ssize_t bc_capture_mode_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf,
+					  size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	char str[20] = "";
+	uint32_t val;
+
+	if (size >= 20)
+		return -EINVAL;
+	if (sscanf(buf, "%s", str) != 1)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (!strcmp(str, "ATB")) {
+		drvdata->bc->capture_mode = TPDM_MODE_ATB;
+	} else if (!strcmp(str, "APB") &&
+		   drvdata->bc->retrieval_mode == TPDM_MODE_APB) {
+
+		TPDM_UNLOCK(drvdata);
+		val = tpdm_readl(drvdata, TPDM_BC_CR);
+		val = val | BIT(3);
+		tpdm_writel(drvdata, val, TPDM_BC_CR);
+		TPDM_LOCK(drvdata);
+
+		drvdata->bc->capture_mode = TPDM_MODE_APB;
+	} else {
+		mutex_unlock(&drvdata->lock);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_capture_mode);
+
+static ssize_t bc_retrieval_mode_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n",
+			 drvdata->bc->retrieval_mode == TPDM_MODE_ATB ?
+			 "ATB" : "APB");
+}
+
+static ssize_t bc_retrieval_mode_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf,
+					    size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	char str[20] = "";
+
+	if (size >= 20)
+		return -EINVAL;
+	if (sscanf(buf, "%s", str) != 1)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (!strcmp(str, "ATB")) {
+		drvdata->bc->retrieval_mode = TPDM_MODE_ATB;
+	} else if (!strcmp(str, "APB")) {
+		drvdata->bc->retrieval_mode = TPDM_MODE_APB;
+	} else {
+		mutex_unlock(&drvdata->lock);
+		return -EINVAL;
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_retrieval_mode);
+
+static ssize_t bc_reset_counters_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf,
+					    size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (val) {
+		TPDM_UNLOCK(drvdata);
+		val = tpdm_readl(drvdata, TPDM_BC_CR);
+		val = val | BIT(1);
+		tpdm_writel(drvdata, val, TPDM_BC_CR);
+		TPDM_LOCK(drvdata);
+	}
+
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_WO(bc_reset_counters);
+
+static ssize_t bc_sat_mode_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%lx\n",
+			 (unsigned long)drvdata->bc->sat_mode);
+}
+
+static ssize_t bc_sat_mode_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->bc->sat_mode = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_sat_mode);
+
+static ssize_t bc_enable_counters_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%lx\n",
+			 (unsigned long)drvdata->bc->enable_counters);
+}
+
+static ssize_t bc_enable_counters_store(struct device *dev,
+					     struct device_attribute *attr,
+					     const char *buf,
+					     size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->bc->enable_counters = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_enable_counters);
+
+static ssize_t bc_clear_counters_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%lx\n",
+			 (unsigned long)drvdata->bc->clear_counters);
+}
+
+static ssize_t bc_clear_counters_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf,
+					    size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->bc->clear_counters = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_clear_counters);
+
+static ssize_t bc_enable_irq_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%lx\n",
+			 (unsigned long)drvdata->bc->enable_irq);
+}
+
+static ssize_t bc_enable_irq_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->bc->enable_irq = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_enable_irq);
+
+static ssize_t bc_clear_irq_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%lx\n",
+			 (unsigned long)drvdata->bc->clear_irq);
+}
+
+static ssize_t bc_clear_irq_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf,
+				       size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->bc->clear_irq = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_clear_irq);
+
+static ssize_t bc_trig_val_lo_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_BC_MAX_COUNTERS; i++)
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  drvdata->bc->trig_val_lo[i]);
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t bc_trig_val_lo_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long index, val;
+
+	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets) ||
+	    index >= drvdata->bc_counters_avail ||
+	    drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_NO ||
+	    (drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_PARTIAL && index > 0))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->bc->trig_val_lo[index] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_trig_val_lo);
+
+static ssize_t bc_trig_val_hi_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_BC_MAX_COUNTERS; i++)
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  drvdata->bc->trig_val_hi[i]);
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t bc_trig_val_hi_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long index, val;
+
+	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets) ||
+	    index >= drvdata->bc_counters_avail ||
+	    drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_NO ||
+	    (drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_PARTIAL && index > 0))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->bc->trig_val_hi[index] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_trig_val_hi);
+
+static ssize_t bc_enable_ganging_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%lx\n",
+			 (unsigned long)drvdata->bc->enable_ganging);
+}
+
+static ssize_t bc_enable_ganging_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf,
+					    size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->bc->enable_ganging = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_enable_ganging);
+
+static ssize_t bc_overflow_val_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_BC_MAX_OVERFLOW; i++)
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  drvdata->bc->overflow_val[i]);
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t bc_overflow_val_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf,
+					  size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long index, val;
+
+	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets) ||
+	    index >= TPDM_BC_MAX_OVERFLOW)
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->bc->overflow_val[index] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_overflow_val);
+
+static ssize_t bc_ovsr_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	val = tpdm_readl(drvdata, TPDM_BC_OVSR);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+
+static ssize_t bc_ovsr_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (val) {
+		TPDM_UNLOCK(drvdata);
+		tpdm_writel(drvdata, val, TPDM_BC_OVSR);
+		TPDM_LOCK(drvdata);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_ovsr);
+
+static ssize_t bc_counter_sel_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	val = tpdm_readl(drvdata, TPDM_BC_SELR);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+
+static ssize_t bc_counter_sel_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable || val >= drvdata->bc_counters_avail) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	tpdm_writel(drvdata, val, TPDM_BC_SELR);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_counter_sel);
+
+static ssize_t bc_count_val_lo_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	val = tpdm_readl(drvdata, TPDM_BC_CNTR_LO);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+
+static ssize_t bc_count_val_lo_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf,
+					  size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val, select;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (val) {
+		TPDM_UNLOCK(drvdata);
+		select = tpdm_readl(drvdata, TPDM_BC_SELR);
+
+		/* Check if selected counter is disabled */
+		if (BMVAL(tpdm_readl(drvdata, TPDM_BC_CNTENSET), select, select)) {
+			mutex_unlock(&drvdata->lock);
+			return -EPERM;
+		}
+
+		tpdm_writel(drvdata, val, TPDM_BC_CNTR_LO);
+		TPDM_LOCK(drvdata);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_count_val_lo);
+
+static ssize_t bc_count_val_hi_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	val = tpdm_readl(drvdata, TPDM_BC_CNTR_HI);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+
+static ssize_t bc_count_val_hi_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf,
+					  size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val, select;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (val) {
+		TPDM_UNLOCK(drvdata);
+		select = tpdm_readl(drvdata, TPDM_BC_SELR);
+
+		/* Check if selected counter is disabled */
+		if (BMVAL(tpdm_readl(drvdata, TPDM_BC_CNTENSET), select, select)) {
+			mutex_unlock(&drvdata->lock);
+			return -EPERM;
+		}
+
+		tpdm_writel(drvdata, val, TPDM_BC_CNTR_HI);
+		TPDM_LOCK(drvdata);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_count_val_hi);
+
+static ssize_t bc_shadow_val_lo_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	for (i = 0; i < drvdata->bc_counters_avail; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  tpdm_readl(drvdata, TPDM_BC_SHADOW_LO(i)));
+	}
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RO(bc_shadow_val_lo);
+
+static ssize_t bc_shadow_val_hi_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	for (i = 0; i < drvdata->bc_counters_avail; i++)
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  tpdm_readl(drvdata, TPDM_BC_SHADOW_HI(i)));
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RO(bc_shadow_val_hi);
+
+static ssize_t bc_sw_inc_show(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	val = tpdm_readl(drvdata, TPDM_BC_SWINC);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+
+static ssize_t bc_sw_inc_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf,
+				    size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (val) {
+		TPDM_UNLOCK(drvdata);
+		tpdm_writel(drvdata, val, TPDM_BC_SWINC);
+		TPDM_LOCK(drvdata);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_sw_inc);
+
+static ssize_t bc_msr_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned int i;
+	ssize_t len = 0;
+
+	if (!drvdata->msr_support)
+		return -EINVAL;
+
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	for (i = 0; i < TPDM_BC_MAX_MSR; i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%u 0x%x\n",
+				 i, drvdata->bc->msr[i]);
+
+	return len;
+}
+
+static ssize_t bc_msr_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned int num, val;
+	int nval;
+
+	if (!drvdata->msr_support)
+		return -EINVAL;
+
+	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
+		return -EPERM;
+
+	nval = sscanf(buf, "%u %x", &num, &val);
+	if (nval != 2)
+		return -EINVAL;
+
+	if (num >= TPDM_BC_MAX_MSR)
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->bc->msr[num] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(bc_msr);
+
+static struct attribute *tpdm_bc_attrs[] = {
+	&dev_attr_bc_capture_mode.attr,
+	&dev_attr_bc_retrieval_mode.attr,
+	&dev_attr_bc_reset_counters.attr,
+	&dev_attr_bc_sat_mode.attr,
+	&dev_attr_bc_enable_counters.attr,
+	&dev_attr_bc_clear_counters.attr,
+	&dev_attr_bc_enable_irq.attr,
+	&dev_attr_bc_clear_irq.attr,
+	&dev_attr_bc_trig_val_lo.attr,
+	&dev_attr_bc_trig_val_hi.attr,
+	&dev_attr_bc_enable_ganging.attr,
+	&dev_attr_bc_overflow_val.attr,
+	&dev_attr_bc_ovsr.attr,
+	&dev_attr_bc_counter_sel.attr,
+	&dev_attr_bc_count_val_lo.attr,
+	&dev_attr_bc_count_val_hi.attr,
+	&dev_attr_bc_shadow_val_lo.attr,
+	&dev_attr_bc_shadow_val_hi.attr,
+	&dev_attr_bc_sw_inc.attr,
+	&dev_attr_bc_msr.attr,
+	NULL,
+};
+
+static struct attribute_group tpdm_bc_attr_grp = {
+	.attrs = tpdm_bc_attrs,
+};
+
 static struct attribute *tpdm_attrs[] = {
 	&dev_attr_available_datasets.attr,
 	&dev_attr_enable_datasets.attr,
@@ -682,6 +1554,7 @@ static struct attribute_group tpdm_attr_grp = {
 };
 static const struct attribute_group *tpdm_attr_grps[] = {
 	&tpdm_attr_grp,
+	&tpdm_bc_attr_grp,
 	NULL,
 };
 
-- 
2.17.1


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

* [PATCH 06/10] Coresight: Enable and add interface for TPDM TC subunit
  2021-10-21  7:38 [PATCH 00/10] Add support for TPDM and TPDA Tao Zhang
                   ` (4 preceding siblings ...)
  2021-10-21  7:38 ` [PATCH 05/10] Coresight: Add interface for TPDM BC subunit Tao Zhang
@ 2021-10-21  7:38 ` Tao Zhang
  2021-10-21  7:38 ` [PATCH 07/10] Coresight: Enable DSB subunit for TPDM Tao Zhang
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 37+ messages in thread
From: Tao Zhang @ 2021-10-21  7:38 UTC (permalink / raw)
  To: Mathieu Poirier, Suzuki K Poulose, Alexander Shishkin
  Cc: Tao Zhang, Mike Leach, Leo Yan, Greg Kroah-Hartman, coresight,
	linux-arm-kernel, linux-kernel, Tingwei Zhang, Mao Jinlong,
	Yuanfang Zhang, Trilok Soni

Enable dataset type Tenure Counts(TC) for TPDM. The TC interface
has RW, WO and RO fields for controlling BC dataset elements
transmitted on ATB flush.
The TC data set subunit supports from 1-4 tenure counter instances
where each tenure counter instance is charged with collecting or
computing elements of an STVn data set(MIN, MAX, TAT and GP
Counter 0-7). Computation of data set elements MIN, MAX, and TAT
is performed by the TC subunit by monitoring a pool of 1 to 1024
tenure scratchpad counters. Computation/decoding of increment
enables for GP Counters 0-7 is accomplished via monitoring the
state (TC subunit outputs) of the tenure scratchpad registers.
This computation/decoding must be performed by the core
instancing the TPDM to which the TC subunit belongs.

Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
---
 drivers/hwtracing/coresight/coresight-tpdm.c | 1066 +++++++++++++++++-
 1 file changed, 1029 insertions(+), 37 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
index 0970c69ac8e2..635382be5de6 100644
--- a/drivers/hwtracing/coresight/coresight-tpdm.c
+++ b/drivers/hwtracing/coresight/coresight-tpdm.c
@@ -298,6 +298,17 @@ static void __tpdm_config_bc_msr(struct tpdm_drvdata *drvdata)
 		tpdm_writel(drvdata, drvdata->bc->msr[i], TPDM_BC_MSR(i));
 }
 
+static void __tpdm_config_tc_msr(struct tpdm_drvdata *drvdata)
+{
+	int i;
+
+	if (!drvdata->msr_support)
+		return;
+
+	for (i = 0; i < TPDM_TC_MAX_MSR; i++)
+		tpdm_writel(drvdata, drvdata->tc->msr[i], TPDM_TC_MSR(i));
+}
+
 static void __tpdm_enable_bc(struct tpdm_drvdata *drvdata)
 {
 	int i;
@@ -363,6 +374,66 @@ static void __tpdm_enable_bc(struct tpdm_drvdata *drvdata)
 	tpdm_writel(drvdata, val, TPDM_BC_CR);
 }
 
+static void __tpdm_enable_tc(struct tpdm_drvdata *drvdata)
+{
+	int i;
+	uint32_t val;
+
+	if (drvdata->tc->enable_counters) {
+		tpdm_writel(drvdata, 0xF, TPDM_TC_CNTENCLR);
+		tpdm_writel(drvdata, drvdata->tc->enable_counters,
+			    TPDM_TC_CNTENSET);
+	}
+	if (drvdata->tc->clear_counters)
+		tpdm_writel(drvdata, drvdata->tc->clear_counters,
+			    TPDM_TC_CNTENCLR);
+
+	if (drvdata->tc->enable_irq) {
+		tpdm_writel(drvdata, 0xF, TPDM_TC_INTENCLR);
+		tpdm_writel(drvdata, drvdata->tc->enable_irq,
+			    TPDM_TC_INTENSET);
+	}
+	if (drvdata->tc->clear_irq)
+		tpdm_writel(drvdata, drvdata->tc->clear_irq,
+			    TPDM_TC_INTENCLR);
+
+	if (drvdata->tc_trig_type == TPDM_SUPPORT_TYPE_FULL) {
+		for (i = 0; i < TPDM_TC_MAX_TRIG; i++) {
+			tpdm_writel(drvdata, drvdata->tc->trig_sel[i],
+				    TPDM_TC_TRIG_SEL(i));
+			tpdm_writel(drvdata, drvdata->tc->trig_val_lo[i],
+				    TPDM_TC_TRIG_LO(i));
+			tpdm_writel(drvdata, drvdata->tc->trig_val_hi[i],
+				    TPDM_TC_TRIG_HI(i));
+		}
+	} else if (drvdata->tc_trig_type == TPDM_SUPPORT_TYPE_PARTIAL) {
+		tpdm_writel(drvdata, drvdata->tc->trig_sel[0],
+			    TPDM_TC_TRIG_SEL(0));
+		tpdm_writel(drvdata, drvdata->tc->trig_val_lo[0],
+			    TPDM_TC_TRIG_LO(0));
+		tpdm_writel(drvdata, drvdata->tc->trig_val_hi[0],
+			    TPDM_TC_TRIG_HI(0));
+	}
+
+	__tpdm_config_tc_msr(drvdata);
+
+	val = tpdm_readl(drvdata, TPDM_TC_CR);
+	if (drvdata->tc->sat_mode)
+		val = val | BIT(4);
+	else
+		val = val & ~BIT(4);
+	if (drvdata->tc->retrieval_mode == TPDM_MODE_APB)
+		val = val | BIT(2);
+	else
+		val = val & ~BIT(2);
+	tpdm_writel(drvdata, val, TPDM_TC_CR);
+
+	val = tpdm_readl(drvdata, TPDM_TC_CR);
+	/* Set the enable bit */
+	val = val | BIT(0);
+	tpdm_writel(drvdata, val, TPDM_TC_CR);
+}
+
 static void __tpdm_enable(struct tpdm_drvdata *drvdata)
 {
 	TPDM_UNLOCK(drvdata);
@@ -376,6 +447,9 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata)
 	if (test_bit(TPDM_DS_BC, drvdata->enable_ds))
 		__tpdm_enable_bc(drvdata);
 
+	if (test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		__tpdm_enable_tc(drvdata);
+
 	TPDM_LOCK(drvdata);
 }
 
@@ -409,6 +483,15 @@ static void __tpdm_disable_bc(struct tpdm_drvdata *drvdata)
 	tpdm_writel(drvdata, config, TPDM_BC_CR);
 }
 
+static void __tpdm_disable_tc(struct tpdm_drvdata *drvdata)
+{
+	uint32_t config;
+
+	config = tpdm_readl(drvdata, TPDM_TC_CR);
+	config = config & ~BIT(0);
+	tpdm_writel(drvdata, config, TPDM_TC_CR);
+}
+
 static void __tpdm_disable(struct tpdm_drvdata *drvdata)
 {
 	TPDM_UNLOCK(drvdata);
@@ -416,6 +499,9 @@ static void __tpdm_disable(struct tpdm_drvdata *drvdata)
 	if (test_bit(TPDM_DS_BC, drvdata->enable_ds))
 		__tpdm_disable_bc(drvdata);
 
+	if (test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		__tpdm_disable_tc(drvdata);
+
 	if (drvdata->clk_enable)
 		tpdm_writel(drvdata, 0x0, TPDM_CLK_CTRL);
 
@@ -1512,49 +1598,955 @@ static ssize_t bc_msr_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(bc_msr);
 
-static struct attribute *tpdm_bc_attrs[] = {
-	&dev_attr_bc_capture_mode.attr,
-	&dev_attr_bc_retrieval_mode.attr,
-	&dev_attr_bc_reset_counters.attr,
-	&dev_attr_bc_sat_mode.attr,
-	&dev_attr_bc_enable_counters.attr,
-	&dev_attr_bc_clear_counters.attr,
-	&dev_attr_bc_enable_irq.attr,
-	&dev_attr_bc_clear_irq.attr,
-	&dev_attr_bc_trig_val_lo.attr,
-	&dev_attr_bc_trig_val_hi.attr,
-	&dev_attr_bc_enable_ganging.attr,
-	&dev_attr_bc_overflow_val.attr,
-	&dev_attr_bc_ovsr.attr,
-	&dev_attr_bc_counter_sel.attr,
-	&dev_attr_bc_count_val_lo.attr,
-	&dev_attr_bc_count_val_hi.attr,
-	&dev_attr_bc_shadow_val_lo.attr,
-	&dev_attr_bc_shadow_val_hi.attr,
-	&dev_attr_bc_sw_inc.attr,
-	&dev_attr_bc_msr.attr,
-	NULL,
-};
+static ssize_t tc_capture_mode_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
 
-static struct attribute_group tpdm_bc_attr_grp = {
-	.attrs = tpdm_bc_attrs,
-};
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
 
-static struct attribute *tpdm_attrs[] = {
-	&dev_attr_available_datasets.attr,
-	&dev_attr_enable_datasets.attr,
-	&dev_attr_reset.attr,
-	&dev_attr_integration_test.attr,
-	&dev_attr_gp_regs.attr,
-	NULL,
-};
+	return scnprintf(buf, PAGE_SIZE, "%s\n",
+			 drvdata->tc->capture_mode == TPDM_MODE_ATB ?
+			 "ATB" : "APB");
+}
+
+static ssize_t tc_capture_mode_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf,
+					  size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	char str[20] = "";
+	uint32_t val;
+
+	if (size >= 20)
+		return -EINVAL;
+	if (sscanf(buf, "%s", str) != 1)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (!strcmp(str, "ATB")) {
+		drvdata->tc->capture_mode = TPDM_MODE_ATB;
+	} else if (!strcmp(str, "APB") &&
+		   drvdata->tc->retrieval_mode == TPDM_MODE_APB) {
+
+		TPDM_UNLOCK(drvdata);
+		val = tpdm_readl(drvdata, TPDM_TC_CR);
+		val = val | BIT(3);
+		tpdm_writel(drvdata, val, TPDM_TC_CR);
+		TPDM_LOCK(drvdata);
+
+		drvdata->tc->capture_mode = TPDM_MODE_APB;
+	} else {
+		mutex_unlock(&drvdata->lock);
+		return -EINVAL;
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_capture_mode);
+
+static ssize_t tc_retrieval_mode_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n",
+			 drvdata->tc->retrieval_mode == TPDM_MODE_ATB ?
+			 "ATB" : "APB");
+}
+
+static ssize_t tc_retrieval_mode_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf,
+					    size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	char str[20] = "";
+
+	if (size >= 20)
+		return -EINVAL;
+	if (sscanf(buf, "%s", str) != 1)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (!strcmp(str, "ATB")) {
+		drvdata->tc->retrieval_mode = TPDM_MODE_ATB;
+	} else if (!strcmp(str, "APB")) {
+		drvdata->tc->retrieval_mode = TPDM_MODE_APB;
+	} else {
+		mutex_unlock(&drvdata->lock);
+		return -EINVAL;
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_retrieval_mode);
+
+static ssize_t tc_reset_counters_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf,
+					    size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (val) {
+		TPDM_UNLOCK(drvdata);
+		val = tpdm_readl(drvdata, TPDM_TC_CR);
+		val = val | BIT(1);
+		tpdm_writel(drvdata, val, TPDM_TC_CR);
+		TPDM_LOCK(drvdata);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_WO(tc_reset_counters);
+
+static ssize_t tc_sat_mode_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->tc->sat_mode);
+}
+
+static ssize_t tc_sat_mode_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->tc->sat_mode = true;
+	else
+		drvdata->tc->sat_mode = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_sat_mode);
+
+static ssize_t tc_enable_counters_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%lx\n",
+			 (unsigned long)drvdata->tc->enable_counters);
+}
+
+static ssize_t tc_enable_counters_store(struct device *dev,
+					     struct device_attribute *attr,
+					     const char *buf,
+					     size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+	if (val >> drvdata->tc_counters_avail)
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->tc->enable_counters = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_enable_counters);
+
+static ssize_t tc_clear_counters_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%lx\n",
+			 (unsigned long)drvdata->tc->clear_counters);
+}
+
+static ssize_t tc_clear_counters_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf,
+					    size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+	if (val >> drvdata->tc_counters_avail)
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->tc->clear_counters = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_clear_counters);
+
+static ssize_t tc_enable_irq_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%lx\n",
+			 (unsigned long)drvdata->tc->enable_irq);
+}
+
+static ssize_t tc_enable_irq_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->tc->enable_irq = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_enable_irq);
+
+static ssize_t tc_clear_irq_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%lx\n",
+			 (unsigned long)drvdata->tc->clear_irq);
+}
+
+static ssize_t tc_clear_irq_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf,
+				       size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->tc->clear_irq = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_clear_irq);
+
+static ssize_t tc_trig_sel_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_TC_MAX_TRIG; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  drvdata->tc->trig_sel[i]);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t tc_trig_sel_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long index, val;
+
+	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets) ||
+	    index >= TPDM_TC_MAX_TRIG ||
+	    drvdata->tc_trig_type == TPDM_SUPPORT_TYPE_NO ||
+	    (drvdata->tc_trig_type == TPDM_SUPPORT_TYPE_PARTIAL && index > 0))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->tc->trig_sel[index] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_trig_sel);
+
+static ssize_t tc_trig_val_lo_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_TC_MAX_TRIG; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  drvdata->tc->trig_val_lo[i]);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t tc_trig_val_lo_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long index, val;
+
+	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets) ||
+	    index >= TPDM_TC_MAX_TRIG ||
+	    drvdata->tc_trig_type == TPDM_SUPPORT_TYPE_NO ||
+	    (drvdata->tc_trig_type == TPDM_SUPPORT_TYPE_PARTIAL && index > 0))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->tc->trig_val_lo[index] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_trig_val_lo);
+
+static ssize_t tc_trig_val_hi_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_TC_MAX_TRIG; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  drvdata->tc->trig_val_hi[i]);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t tc_trig_val_hi_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long index, val;
+
+	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets) ||
+	    index >= TPDM_TC_MAX_TRIG ||
+	    drvdata->tc_trig_type == TPDM_SUPPORT_TYPE_NO ||
+	    (drvdata->tc_trig_type == TPDM_SUPPORT_TYPE_PARTIAL && index > 0))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->tc->trig_val_hi[index] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_trig_val_hi);
+
+static ssize_t tc_ovsr_gp_show(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	val = tpdm_readl(drvdata, TPDM_TC_OVSR_GP);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+
+static ssize_t tc_ovsr_gp_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf,
+				     size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (val) {
+		TPDM_UNLOCK(drvdata);
+		tpdm_writel(drvdata, val, TPDM_TC_OVSR_GP);
+		TPDM_LOCK(drvdata);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_ovsr_gp);
+
+static ssize_t tc_ovsr_impl_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	val = tpdm_readl(drvdata, TPDM_TC_OVSR_IMPL);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+
+static ssize_t tc_ovsr_impl_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf,
+				       size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (val) {
+		TPDM_UNLOCK(drvdata);
+		tpdm_writel(drvdata, val, TPDM_TC_OVSR_IMPL);
+		TPDM_LOCK(drvdata);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_ovsr_impl);
+
+static ssize_t tc_counter_sel_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	val = tpdm_readl(drvdata, TPDM_TC_SELR);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+
+static ssize_t tc_counter_sel_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	tpdm_writel(drvdata, val, TPDM_TC_SELR);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_counter_sel);
+
+static ssize_t tc_count_val_lo_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	val = tpdm_readl(drvdata, TPDM_TC_CNTR_LO);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+
+static ssize_t tc_count_val_lo_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf,
+					  size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val, select;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (val) {
+		TPDM_UNLOCK(drvdata);
+		select = tpdm_readl(drvdata, TPDM_TC_SELR);
+		select = (select >> 11) & 0x3;
+
+		/* Check if selected counter is disabled */
+		if (BMVAL(tpdm_readl(drvdata, TPDM_TC_CNTENSET), select, select)) {
+			mutex_unlock(&drvdata->lock);
+			return -EPERM;
+		}
+
+		tpdm_writel(drvdata, val, TPDM_TC_CNTR_LO);
+		TPDM_LOCK(drvdata);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_count_val_lo);
+
+static ssize_t tc_count_val_hi_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	val = tpdm_readl(drvdata, TPDM_TC_CNTR_HI);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+
+static ssize_t tc_count_val_hi_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf,
+					  size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val, select;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (val) {
+		TPDM_UNLOCK(drvdata);
+		select = tpdm_readl(drvdata, TPDM_TC_SELR);
+		select = (select >> 11) & 0x3;
+
+		/* Check if selected counter is disabled */
+		if (BMVAL(tpdm_readl(drvdata, TPDM_TC_CNTENSET), select, select)) {
+			mutex_unlock(&drvdata->lock);
+			return -EPERM;
+		}
+
+		tpdm_writel(drvdata, val, TPDM_TC_CNTR_HI);
+		TPDM_LOCK(drvdata);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_count_val_hi);
+
+static ssize_t tc_shadow_val_lo_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	for (i = 0; i < TPDM_TC_MAX_COUNTERS; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  tpdm_readl(drvdata, TPDM_TC_SHADOW_LO(i)));
+	}
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RO(tc_shadow_val_lo);
+
+static ssize_t tc_shadow_val_hi_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	for (i = 0; i < TPDM_TC_MAX_COUNTERS; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  tpdm_readl(drvdata, TPDM_TC_SHADOW_HI(i)));
+	}
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RO(tc_shadow_val_hi);
+
+static ssize_t tc_sw_inc_show(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDM_UNLOCK(drvdata);
+	val = tpdm_readl(drvdata, TPDM_TC_SWINC);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+
+static ssize_t tc_sw_inc_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf,
+				    size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_TC, drvdata->enable_ds))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (val) {
+		TPDM_UNLOCK(drvdata);
+		tpdm_writel(drvdata, val, TPDM_TC_SWINC);
+		TPDM_LOCK(drvdata);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_sw_inc);
+
+static ssize_t tc_msr_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned int i;
+	ssize_t len = 0;
+
+	if (!drvdata->msr_support)
+		return -EINVAL;
+
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	for (i = 0; i < TPDM_TC_MAX_MSR; i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%u 0x%x\n",
+				 i, drvdata->tc->msr[i]);
+
+	return len;
+}
+
+static ssize_t tc_msr_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf,
+				 size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned int num, val;
+	int nval;
+
+	if (!drvdata->msr_support)
+		return -EINVAL;
+
+	if (!test_bit(TPDM_DS_TC, drvdata->datasets))
+		return -EPERM;
+
+	nval = sscanf(buf, "%u %x", &num, &val);
+	if (nval != 2)
+		return -EINVAL;
+
+	if (num >= TPDM_TC_MAX_MSR)
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->tc->msr[num] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(tc_msr);
+
+static struct attribute *tpdm_bc_attrs[] = {
+	&dev_attr_bc_capture_mode.attr,
+	&dev_attr_bc_retrieval_mode.attr,
+	&dev_attr_bc_reset_counters.attr,
+	&dev_attr_bc_sat_mode.attr,
+	&dev_attr_bc_enable_counters.attr,
+	&dev_attr_bc_clear_counters.attr,
+	&dev_attr_bc_enable_irq.attr,
+	&dev_attr_bc_clear_irq.attr,
+	&dev_attr_bc_trig_val_lo.attr,
+	&dev_attr_bc_trig_val_hi.attr,
+	&dev_attr_bc_enable_ganging.attr,
+	&dev_attr_bc_overflow_val.attr,
+	&dev_attr_bc_ovsr.attr,
+	&dev_attr_bc_counter_sel.attr,
+	&dev_attr_bc_count_val_lo.attr,
+	&dev_attr_bc_count_val_hi.attr,
+	&dev_attr_bc_shadow_val_lo.attr,
+	&dev_attr_bc_shadow_val_hi.attr,
+	&dev_attr_bc_sw_inc.attr,
+	&dev_attr_bc_msr.attr,
+	NULL,
+};
+
+static struct attribute *tpdm_tc_attrs[] = {
+	&dev_attr_tc_capture_mode.attr,
+	&dev_attr_tc_retrieval_mode.attr,
+	&dev_attr_tc_reset_counters.attr,
+	&dev_attr_tc_sat_mode.attr,
+	&dev_attr_tc_enable_counters.attr,
+	&dev_attr_tc_clear_counters.attr,
+	&dev_attr_tc_enable_irq.attr,
+	&dev_attr_tc_clear_irq.attr,
+	&dev_attr_tc_trig_sel.attr,
+	&dev_attr_tc_trig_val_lo.attr,
+	&dev_attr_tc_trig_val_hi.attr,
+	&dev_attr_tc_ovsr_gp.attr,
+	&dev_attr_tc_ovsr_impl.attr,
+	&dev_attr_tc_counter_sel.attr,
+	&dev_attr_tc_count_val_lo.attr,
+	&dev_attr_tc_count_val_hi.attr,
+	&dev_attr_tc_shadow_val_lo.attr,
+	&dev_attr_tc_shadow_val_hi.attr,
+	&dev_attr_tc_sw_inc.attr,
+	&dev_attr_tc_msr.attr,
+	NULL,
+};
+
+static struct attribute_group tpdm_bc_attr_grp = {
+	.attrs = tpdm_bc_attrs,
+};
+
+static struct attribute_group tpdm_tc_attr_grp = {
+	.attrs = tpdm_tc_attrs,
+};
+
+static struct attribute *tpdm_attrs[] = {
+	&dev_attr_available_datasets.attr,
+	&dev_attr_enable_datasets.attr,
+	&dev_attr_reset.attr,
+	&dev_attr_integration_test.attr,
+	&dev_attr_gp_regs.attr,
+	NULL,
+};
+
+static struct attribute_group tpdm_attr_grp = {
+	.attrs = tpdm_attrs,
+};
 
-static struct attribute_group tpdm_attr_grp = {
-	.attrs = tpdm_attrs,
-};
 static const struct attribute_group *tpdm_attr_grps[] = {
 	&tpdm_attr_grp,
 	&tpdm_bc_attr_grp,
+	&tpdm_tc_attr_grp,
 	NULL,
 };
 
-- 
2.17.1


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

* [PATCH 07/10] Coresight: Enable DSB subunit for TPDM
  2021-10-21  7:38 [PATCH 00/10] Add support for TPDM and TPDA Tao Zhang
                   ` (5 preceding siblings ...)
  2021-10-21  7:38 ` [PATCH 06/10] Coresight: Enable and add interface for TPDM TC subunit Tao Zhang
@ 2021-10-21  7:38 ` Tao Zhang
  2021-10-21  7:38 ` [PATCH 08/10] Coresight: Enable CMB " Tao Zhang
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 37+ messages in thread
From: Tao Zhang @ 2021-10-21  7:38 UTC (permalink / raw)
  To: Mathieu Poirier, Suzuki K Poulose, Alexander Shishkin
  Cc: Tao Zhang, Mike Leach, Leo Yan, Greg Kroah-Hartman, coresight,
	linux-arm-kernel, linux-kernel, Tingwei Zhang, Mao Jinlong,
	Yuanfang Zhang, Trilok Soni

Enable Discrete Single Bit(DSB) subunit for TPDM. The DSB dataset
elements flow out ATB while the BC/TC dataset elements are sent
only on ATB flush requests from the TPDA.
The DSB data set subunit is responsible for collection of DSB data
sets. The width of the DSB subunit interface must be between 8 and
256 bits. A monitor may support either a 32 or 64 bit DSB data set
element size (e.g. via a hardware parameter).

Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
---
 drivers/hwtracing/coresight/coresight-tpdm.c | 680 +++++++++++++++++++
 1 file changed, 680 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
index 635382be5de6..5f07363e4650 100644
--- a/drivers/hwtracing/coresight/coresight-tpdm.c
+++ b/drivers/hwtracing/coresight/coresight-tpdm.c
@@ -309,6 +309,17 @@ static void __tpdm_config_tc_msr(struct tpdm_drvdata *drvdata)
 		tpdm_writel(drvdata, drvdata->tc->msr[i], TPDM_TC_MSR(i));
 }
 
+static void __tpdm_config_dsb_msr(struct tpdm_drvdata *drvdata)
+{
+	int i;
+
+	if (!drvdata->msr_support)
+		return;
+
+	for (i = 0; i < TPDM_DSB_MAX_MSR; i++)
+		tpdm_writel(drvdata, drvdata->dsb->msr[i], TPDM_DSB_MSR(i));
+}
+
 static void __tpdm_enable_bc(struct tpdm_drvdata *drvdata)
 {
 	int i;
@@ -434,6 +445,86 @@ static void __tpdm_enable_tc(struct tpdm_drvdata *drvdata)
 	tpdm_writel(drvdata, val, TPDM_TC_CR);
 }
 
+static void __tpdm_enable_dsb(struct tpdm_drvdata *drvdata)
+{
+	uint32_t val, mode, i;
+
+	for (i = 0; i < TPDM_DSB_MAX_EDCR; i++)
+		tpdm_writel(drvdata, drvdata->dsb->edge_ctrl[i],
+			    TPDM_DSB_EDCR(i));
+	for (i = 0; i < TPDM_DSB_MAX_EDCR / 2; i++)
+		tpdm_writel(drvdata, drvdata->dsb->edge_ctrl_mask[i],
+			    TPDM_DSB_EDCMR(i));
+
+	for (i = 0; i < TPDM_DSB_MAX_PATT; i++) {
+		tpdm_writel(drvdata, drvdata->dsb->patt_val[i],
+			    TPDM_DSB_TPR(i));
+		tpdm_writel(drvdata, drvdata->dsb->patt_mask[i],
+			    TPDM_DSB_TPMR(i));
+	}
+
+	for (i = 0; i < TPDM_DSB_MAX_PATT; i++) {
+		tpdm_writel(drvdata, drvdata->dsb->trig_patt_val[i],
+			    TPDM_DSB_XPR(i));
+		tpdm_writel(drvdata, drvdata->dsb->trig_patt_mask[i],
+			    TPDM_DSB_XPMR(i));
+	}
+
+	for (i = 0; i < TPDM_DSB_MAX_SELECT; i++)
+		tpdm_writel(drvdata, drvdata->dsb->select_val[i],
+			    TPDM_DSB_CA_SELECT(i));
+
+	val = tpdm_readl(drvdata, TPDM_DSB_TIER);
+	if (drvdata->dsb->patt_ts) {
+		val = val | BIT(0);
+		if (drvdata->dsb->patt_type)
+			val = val | BIT(2);
+		else
+			val = val & ~BIT(2);
+	} else {
+		val = val & ~BIT(0);
+	}
+	if (drvdata->dsb->trig_ts)
+		val = val | BIT(1);
+	else
+		val = val & ~BIT(1);
+	tpdm_writel(drvdata, val, TPDM_DSB_TIER);
+
+	if (!drvdata->msr_fix_req)
+		__tpdm_config_dsb_msr(drvdata);
+
+	val = tpdm_readl(drvdata, TPDM_DSB_CR);
+	/* Set the cycle accurate mode */
+	mode = TPDM_DSB_MODE_CYCACC(drvdata->dsb->mode);
+	val = val & ~(0x7 << 9);
+	val = val | (mode << 9);
+	/* Set the byte lane for high-performance mode */
+	mode = TPDM_DSB_MODE_HPBYTESEL(drvdata->dsb->mode);
+	val = val & ~(0x1F << 2);
+	val = val | (mode << 2);
+	/* Set the performance mode */
+	if (drvdata->dsb->mode & TPDM_DSB_MODE_PERF)
+		val = val | BIT(1);
+	else
+		val = val & ~BIT(1);
+
+	/* Set trigger type */
+	if (drvdata->dsb->trig_type)
+		val = val | BIT(12);
+	else
+		val = val & ~BIT(12);
+
+	tpdm_writel(drvdata, val, TPDM_DSB_CR);
+
+	val = tpdm_readl(drvdata, TPDM_DSB_CR);
+	/* Set the enable bit */
+	val = val | BIT(0);
+	tpdm_writel(drvdata, val, TPDM_DSB_CR);
+
+	if (drvdata->msr_fix_req)
+		__tpdm_config_dsb_msr(drvdata);
+}
+
 static void __tpdm_enable(struct tpdm_drvdata *drvdata)
 {
 	TPDM_UNLOCK(drvdata);
@@ -450,6 +541,9 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata)
 	if (test_bit(TPDM_DS_TC, drvdata->enable_ds))
 		__tpdm_enable_tc(drvdata);
 
+	if (test_bit(TPDM_DS_DSB, drvdata->enable_ds))
+		__tpdm_enable_dsb(drvdata);
+
 	TPDM_LOCK(drvdata);
 }
 
@@ -492,6 +586,15 @@ static void __tpdm_disable_tc(struct tpdm_drvdata *drvdata)
 	tpdm_writel(drvdata, config, TPDM_TC_CR);
 }
 
+static void __tpdm_disable_dsb(struct tpdm_drvdata *drvdata)
+{
+	uint32_t config;
+
+	config = tpdm_readl(drvdata, TPDM_DSB_CR);
+	config = config & ~BIT(0);
+	tpdm_writel(drvdata, config, TPDM_DSB_CR);
+}
+
 static void __tpdm_disable(struct tpdm_drvdata *drvdata)
 {
 	TPDM_UNLOCK(drvdata);
@@ -502,6 +605,9 @@ static void __tpdm_disable(struct tpdm_drvdata *drvdata)
 	if (test_bit(TPDM_DS_TC, drvdata->enable_ds))
 		__tpdm_disable_tc(drvdata);
 
+	if (test_bit(TPDM_DS_DSB, drvdata->enable_ds))
+		__tpdm_disable_dsb(drvdata);
+
 	if (drvdata->clk_enable)
 		tpdm_writel(drvdata, 0x0, TPDM_CLK_CTRL);
 
@@ -2474,6 +2580,558 @@ static ssize_t tc_msr_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(tc_msr);
 
+static ssize_t dsb_mode_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%lx\n",
+			 (unsigned long)drvdata->dsb->mode);
+}
+
+static ssize_t dsb_mode_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf,
+				   size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->dsb->mode = val & TPDM_MODE_ALL;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(dsb_mode);
+
+static ssize_t dsb_edge_ctrl_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i;
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_DSB_MAX_EDCR; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index:0x%x Val:0x%x\n", i,
+				  drvdata->dsb->edge_ctrl[i]);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t dsb_edge_ctrl_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long start, end, edge_ctrl;
+	uint32_t val;
+	int i, bit, reg;
+
+	if (sscanf(buf, "%lx %lx %lx", &start, &end, &edge_ctrl) != 3)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets) ||
+	    (start >= TPDM_DSB_MAX_LINES) || (end >= TPDM_DSB_MAX_LINES) ||
+	    edge_ctrl > 0x2)
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = start; i <= end; i++) {
+		reg = i / (NUM_OF_BITS / 2);
+		bit = i % (NUM_OF_BITS / 2);
+		bit = bit * 2;
+
+		val = drvdata->dsb->edge_ctrl[reg];
+		val = val & ~GENMASK((bit + 1), bit);
+		val = val | (edge_ctrl << bit);
+		drvdata->dsb->edge_ctrl[reg] = val;
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(dsb_edge_ctrl);
+
+static ssize_t dsb_edge_ctrl_mask_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i;
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_DSB_MAX_EDCR / 2; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index:0x%x Val:0x%x\n", i,
+				  drvdata->dsb->edge_ctrl_mask[i]);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t dsb_edge_ctrl_mask_store(struct device *dev,
+					     struct device_attribute *attr,
+					     const char *buf,
+					     size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long start, end, val;
+	uint32_t set;
+	int i, bit, reg;
+
+	if (sscanf(buf, "%lx %lx %lx", &start, &end, &val) != 3)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets) ||
+	    (start >= TPDM_DSB_MAX_LINES) || (end >= TPDM_DSB_MAX_LINES))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = start; i <= end; i++) {
+		reg = i / NUM_OF_BITS;
+		bit = (i % NUM_OF_BITS);
+
+		set = drvdata->dsb->edge_ctrl_mask[reg];
+		if (val)
+			set = set | BIT(bit);
+		else
+			set = set & ~BIT(bit);
+		drvdata->dsb->edge_ctrl_mask[reg] = set;
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(dsb_edge_ctrl_mask);
+
+static ssize_t dsb_patt_val_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_DSB_MAX_PATT; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  drvdata->dsb->patt_val[i]);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t dsb_patt_val_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf,
+				       size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long index, val;
+
+	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets) ||
+	    index >= TPDM_DSB_MAX_PATT)
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->dsb->patt_val[index] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(dsb_patt_val);
+
+static ssize_t dsb_patt_mask_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_DSB_MAX_PATT; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  drvdata->dsb->patt_mask[i]);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t dsb_patt_mask_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long index, val;
+
+	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets) ||
+	    index >= TPDM_DSB_MAX_PATT)
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->dsb->patt_mask[index] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(dsb_patt_mask);
+
+static ssize_t dsb_patt_ts_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->dsb->patt_ts);
+}
+
+static ssize_t dsb_patt_ts_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->dsb->patt_ts = true;
+	else
+		drvdata->dsb->patt_ts = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(dsb_patt_ts);
+
+static ssize_t dsb_patt_type_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->dsb->patt_type);
+}
+
+static ssize_t dsb_patt_type_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->dsb->patt_type = true;
+	else
+		drvdata->dsb->patt_type = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(dsb_patt_type);
+
+static ssize_t dsb_trig_patt_val_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_DSB_MAX_PATT; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  drvdata->dsb->trig_patt_val[i]);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t dsb_trig_patt_val_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf,
+					    size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long index, val;
+
+	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets) ||
+	    index >= TPDM_DSB_MAX_PATT)
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->dsb->trig_patt_val[index] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(dsb_trig_patt_val);
+
+static ssize_t dsb_trig_patt_mask_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i = 0;
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_DSB_MAX_PATT; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  drvdata->dsb->trig_patt_mask[i]);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t dsb_trig_patt_mask_store(struct device *dev,
+					     struct device_attribute *attr,
+					     const char *buf,
+					     size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long index, val;
+
+	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets) ||
+	    index >= TPDM_DSB_MAX_PATT)
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->dsb->trig_patt_mask[index] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(dsb_trig_patt_mask);
+
+static ssize_t dsb_trig_type_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->dsb->trig_type);
+}
+
+static ssize_t dsb_trig_type_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->dsb->trig_type = true;
+	else
+		drvdata->dsb->trig_type = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(dsb_trig_type);
+
+static ssize_t dsb_trig_ts_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->dsb->trig_ts);
+}
+
+static ssize_t dsb_trig_ts_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->dsb->trig_ts = true;
+	else
+		drvdata->dsb->trig_ts = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(dsb_trig_ts);
+
+static ssize_t dsb_select_val_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i;
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_DSB_MAX_SELECT; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index:0x%x Val:0x%x\n", i,
+				  drvdata->dsb->select_val[i]);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t dsb_select_val_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long start, end;
+	uint32_t val;
+	int i, bit, reg;
+
+	if (sscanf(buf, "%lx %lx", &start, &end) != 2)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets) ||
+	    (start >= TPDM_DSB_MAX_LINES) || (end >= TPDM_DSB_MAX_LINES))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = start; i <= end; i++) {
+		reg = i / NUM_OF_BITS;
+		bit = (i % NUM_OF_BITS);
+
+		val = drvdata->dsb->select_val[reg];
+		val = val | BIT(bit);
+		drvdata->dsb->select_val[reg] = val;
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(dsb_select_val);
+
+static ssize_t dsb_msr_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned int i;
+	ssize_t len = 0;
+
+	if (!drvdata->msr_support)
+		return -EINVAL;
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	for (i = 0; i < TPDM_DSB_MAX_MSR; i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%u 0x%x\n",
+				 i, drvdata->dsb->msr[i]);
+
+	return len;
+}
+
+static ssize_t dsb_msr_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned int num, val;
+	int nval;
+
+	if (!drvdata->msr_support)
+		return -EINVAL;
+
+	if (!test_bit(TPDM_DS_DSB, drvdata->datasets))
+		return -EPERM;
+
+	nval = sscanf(buf, "%u %x", &num, &val);
+	if (nval != 2)
+		return -EINVAL;
+
+	if (num >= TPDM_DSB_MAX_MSR)
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->dsb->msr[num] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(dsb_msr);
+
 static struct attribute *tpdm_bc_attrs[] = {
 	&dev_attr_bc_capture_mode.attr,
 	&dev_attr_bc_retrieval_mode.attr,
@@ -2522,6 +3180,23 @@ static struct attribute *tpdm_tc_attrs[] = {
 	NULL,
 };
 
+static struct attribute *tpdm_dsb_attrs[] = {
+	&dev_attr_dsb_mode.attr,
+	&dev_attr_dsb_edge_ctrl.attr,
+	&dev_attr_dsb_edge_ctrl_mask.attr,
+	&dev_attr_dsb_patt_val.attr,
+	&dev_attr_dsb_patt_mask.attr,
+	&dev_attr_dsb_patt_ts.attr,
+	&dev_attr_dsb_patt_type.attr,
+	&dev_attr_dsb_trig_patt_val.attr,
+	&dev_attr_dsb_trig_patt_mask.attr,
+	&dev_attr_dsb_trig_ts.attr,
+	&dev_attr_dsb_trig_type.attr,
+	&dev_attr_dsb_select_val.attr,
+	&dev_attr_dsb_msr.attr,
+	NULL,
+};
+
 static struct attribute_group tpdm_bc_attr_grp = {
 	.attrs = tpdm_bc_attrs,
 };
@@ -2530,6 +3205,10 @@ static struct attribute_group tpdm_tc_attr_grp = {
 	.attrs = tpdm_tc_attrs,
 };
 
+static struct attribute_group tpdm_dsb_attr_grp = {
+	.attrs = tpdm_dsb_attrs,
+};
+
 static struct attribute *tpdm_attrs[] = {
 	&dev_attr_available_datasets.attr,
 	&dev_attr_enable_datasets.attr,
@@ -2547,6 +3226,7 @@ static const struct attribute_group *tpdm_attr_grps[] = {
 	&tpdm_attr_grp,
 	&tpdm_bc_attr_grp,
 	&tpdm_tc_attr_grp,
+	&tpdm_dsb_attr_grp,
 	NULL,
 };
 
-- 
2.17.1


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

* [PATCH 08/10] Coresight: Enable CMB subunit for TPDM
  2021-10-21  7:38 [PATCH 00/10] Add support for TPDM and TPDA Tao Zhang
                   ` (6 preceding siblings ...)
  2021-10-21  7:38 ` [PATCH 07/10] Coresight: Enable DSB subunit for TPDM Tao Zhang
@ 2021-10-21  7:38 ` Tao Zhang
  2021-10-21  7:38 ` [PATCH 09/10] coresight: Add driver to support Coresight device TPDA Tao Zhang
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 37+ messages in thread
From: Tao Zhang @ 2021-10-21  7:38 UTC (permalink / raw)
  To: Mathieu Poirier, Suzuki K Poulose, Alexander Shishkin
  Cc: Tao Zhang, Mike Leach, Leo Yan, Greg Kroah-Hartman, coresight,
	linux-arm-kernel, linux-kernel, Tingwei Zhang, Mao Jinlong,
	Yuanfang Zhang, Trilok Soni

Enable CMB subunit for TPDM. The CMB dataset elements flow out ATB
while the BC/TC dataset elements are sent only on ATB flush
requests from the TPDA.
The CMB data set subunit is responsible for collection of CMB data
sets. The CMB subunit data set interface must be a legal ATB width
less than or equal to 64 bits.

Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
---
 drivers/hwtracing/coresight/coresight-tpdm.c | 931 +++++++++++++++++--
 1 file changed, 861 insertions(+), 70 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
index 5f07363e4650..3e7f5b48150b 100644
--- a/drivers/hwtracing/coresight/coresight-tpdm.c
+++ b/drivers/hwtracing/coresight/coresight-tpdm.c
@@ -320,6 +320,17 @@ static void __tpdm_config_dsb_msr(struct tpdm_drvdata *drvdata)
 		tpdm_writel(drvdata, drvdata->dsb->msr[i], TPDM_DSB_MSR(i));
 }
 
+static void __tpdm_config_cmb_msr(struct tpdm_drvdata *drvdata)
+{
+	int i;
+
+	if (!drvdata->msr_support)
+		return;
+
+	for (i = 0; i < TPDM_CMB_MAX_MSR; i++)
+		tpdm_writel(drvdata, drvdata->cmb->msr[i], TPDM_CMB_MSR(i));
+}
+
 static void __tpdm_enable_bc(struct tpdm_drvdata *drvdata)
 {
 	int i;
@@ -525,6 +536,112 @@ static void __tpdm_enable_dsb(struct tpdm_drvdata *drvdata)
 		__tpdm_config_dsb_msr(drvdata);
 }
 
+static void __tpdm_enable_cmb(struct tpdm_drvdata *drvdata)
+{
+	uint32_t val;
+	int i;
+
+	for (i = 0; i < TPDM_CMB_PATT_CMP; i++) {
+		tpdm_writel(drvdata, drvdata->cmb->patt_val[i],
+			    TPDM_CMB_TPR(i));
+		tpdm_writel(drvdata, drvdata->cmb->patt_mask[i],
+			    TPDM_CMB_TPMR(i));
+		tpdm_writel(drvdata, drvdata->cmb->trig_patt_val[i],
+			    TPDM_CMB_XPR(i));
+		tpdm_writel(drvdata, drvdata->cmb->trig_patt_mask[i],
+			    TPDM_CMB_XPMR(i));
+	}
+
+	val = tpdm_readl(drvdata, TPDM_CMB_TIER);
+	if (drvdata->cmb->patt_ts)
+		val = val | BIT(0);
+	else
+		val = val & ~BIT(0);
+	if (drvdata->cmb->trig_ts)
+		val = val | BIT(1);
+	else
+		val = val & ~BIT(1);
+	if (drvdata->cmb->ts_all)
+		val = val | BIT(2);
+	else
+		val = val & ~BIT(2);
+
+	tpdm_writel(drvdata, val, TPDM_CMB_TIER);
+
+	if (!drvdata->cmb_msr_skip)
+		__tpdm_config_cmb_msr(drvdata);
+
+	val = tpdm_readl(drvdata, TPDM_CMB_CR);
+	/* Set the flow control bit */
+	val = val & ~BIT(2);
+	if (drvdata->cmb->trace_mode)
+		val = val | BIT(1);
+	else
+		val = val & ~BIT(1);
+
+	val = val & ~GENMASK(9, 8);
+	val = val | BMVAL(drvdata->cmb->cycle_acc, 0, 1) << 8;
+	tpdm_writel(drvdata, val, TPDM_CMB_CR);
+	/* Set the enable bit */
+	val = val | BIT(0);
+	tpdm_writel(drvdata, val, TPDM_CMB_CR);
+}
+
+static void __tpdm_enable_mcmb(struct tpdm_drvdata *drvdata)
+{
+	uint32_t val;
+	struct mcmb_dataset *mcmb = drvdata->cmb->mcmb;
+	int i;
+
+	for (i = 0; i < TPDM_CMB_PATT_CMP; i++) {
+		tpdm_writel(drvdata, drvdata->cmb->patt_val[i],
+			    TPDM_CMB_TPR(i));
+		tpdm_writel(drvdata, drvdata->cmb->patt_mask[i],
+			    TPDM_CMB_TPMR(i));
+		tpdm_writel(drvdata, drvdata->cmb->trig_patt_val[i],
+			    TPDM_CMB_XPR(i));
+		tpdm_writel(drvdata, drvdata->cmb->trig_patt_mask[i],
+			    TPDM_CMB_XPMR(i));
+	}
+
+	val = tpdm_readl(drvdata, TPDM_CMB_TIER);
+	if (drvdata->cmb->patt_ts)
+		val = val | BIT(0);
+	else
+		val = val & ~BIT(0);
+	if (drvdata->cmb->trig_ts)
+		val = val | BIT(1);
+	else
+		val = val & ~BIT(1);
+	if (drvdata->cmb->ts_all)
+		val = val | BIT(2);
+	else
+		val = val & ~BIT(2);
+	tpdm_writel(drvdata, val, TPDM_CMB_TIER);
+
+	__tpdm_config_cmb_msr(drvdata);
+
+	val = tpdm_readl(drvdata, TPDM_CMB_CR);
+	/* Set the flow control bit */
+	val = val & ~BIT(2);
+	if (drvdata->cmb->trace_mode)
+		val = val | BIT(1);
+	else
+		val = val & ~BIT(1);
+
+	val = val & ~GENMASK(9, 8);
+	val = val | BMVAL(drvdata->cmb->cycle_acc, 0, 1) << 8;
+	val = val & ~GENMASK(20, 18);
+	val = val | (BMVAL(mcmb->mcmb_trig_lane, 0, 2) << 18);
+	val = val & ~GENMASK(17, 10);
+	val = val | (BMVAL(mcmb->mcmb_lane_select, 0, 7) << 10);
+
+	tpdm_writel(drvdata, val, TPDM_CMB_CR);
+	/* Set the enable bit */
+	val = val | BIT(0);
+	tpdm_writel(drvdata, val, TPDM_CMB_CR);
+}
+
 static void __tpdm_enable(struct tpdm_drvdata *drvdata)
 {
 	TPDM_UNLOCK(drvdata);
@@ -544,6 +661,11 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata)
 	if (test_bit(TPDM_DS_DSB, drvdata->enable_ds))
 		__tpdm_enable_dsb(drvdata);
 
+	if (test_bit(TPDM_DS_CMB, drvdata->enable_ds))
+		__tpdm_enable_cmb(drvdata);
+	else if (test_bit(TPDM_DS_MCMB, drvdata->enable_ds))
+		__tpdm_enable_mcmb(drvdata);
+
 	TPDM_LOCK(drvdata);
 }
 
@@ -595,6 +717,15 @@ static void __tpdm_disable_dsb(struct tpdm_drvdata *drvdata)
 	tpdm_writel(drvdata, config, TPDM_DSB_CR);
 }
 
+static void __tpdm_disable_cmb(struct tpdm_drvdata *drvdata)
+{
+	uint32_t config;
+
+	config = tpdm_readl(drvdata, TPDM_CMB_CR);
+	config = config & ~BIT(0);
+	tpdm_writel(drvdata, config, TPDM_CMB_CR);
+}
+
 static void __tpdm_disable(struct tpdm_drvdata *drvdata)
 {
 	TPDM_UNLOCK(drvdata);
@@ -608,6 +739,10 @@ static void __tpdm_disable(struct tpdm_drvdata *drvdata)
 	if (test_bit(TPDM_DS_DSB, drvdata->enable_ds))
 		__tpdm_disable_dsb(drvdata);
 
+	if (test_bit(TPDM_DS_CMB, drvdata->enable_ds) ||
+		test_bit(TPDM_DS_MCMB, drvdata->enable_ds))
+		__tpdm_disable_cmb(drvdata);
+
 	if (drvdata->clk_enable)
 		tpdm_writel(drvdata, 0x0, TPDM_CLK_CTRL);
 
@@ -3132,81 +3267,736 @@ static ssize_t dsb_msr_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(dsb_msr);
 
-static struct attribute *tpdm_bc_attrs[] = {
-	&dev_attr_bc_capture_mode.attr,
-	&dev_attr_bc_retrieval_mode.attr,
-	&dev_attr_bc_reset_counters.attr,
-	&dev_attr_bc_sat_mode.attr,
-	&dev_attr_bc_enable_counters.attr,
-	&dev_attr_bc_clear_counters.attr,
-	&dev_attr_bc_enable_irq.attr,
-	&dev_attr_bc_clear_irq.attr,
-	&dev_attr_bc_trig_val_lo.attr,
-	&dev_attr_bc_trig_val_hi.attr,
-	&dev_attr_bc_enable_ganging.attr,
-	&dev_attr_bc_overflow_val.attr,
-	&dev_attr_bc_ovsr.attr,
-	&dev_attr_bc_counter_sel.attr,
-	&dev_attr_bc_count_val_lo.attr,
-	&dev_attr_bc_count_val_hi.attr,
-	&dev_attr_bc_shadow_val_lo.attr,
-	&dev_attr_bc_shadow_val_hi.attr,
-	&dev_attr_bc_sw_inc.attr,
-	&dev_attr_bc_msr.attr,
-	NULL,
-};
+static ssize_t cmb_available_modes_show(struct device *dev,
+					     struct device_attribute *attr,
+					     char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%s\n", "continuous trace_on_change");
+}
+static DEVICE_ATTR_RO(cmb_available_modes);
 
-static struct attribute *tpdm_tc_attrs[] = {
-	&dev_attr_tc_capture_mode.attr,
-	&dev_attr_tc_retrieval_mode.attr,
-	&dev_attr_tc_reset_counters.attr,
-	&dev_attr_tc_sat_mode.attr,
-	&dev_attr_tc_enable_counters.attr,
-	&dev_attr_tc_clear_counters.attr,
-	&dev_attr_tc_enable_irq.attr,
-	&dev_attr_tc_clear_irq.attr,
-	&dev_attr_tc_trig_sel.attr,
-	&dev_attr_tc_trig_val_lo.attr,
-	&dev_attr_tc_trig_val_hi.attr,
-	&dev_attr_tc_ovsr_gp.attr,
-	&dev_attr_tc_ovsr_impl.attr,
-	&dev_attr_tc_counter_sel.attr,
-	&dev_attr_tc_count_val_lo.attr,
-	&dev_attr_tc_count_val_hi.attr,
-	&dev_attr_tc_shadow_val_lo.attr,
-	&dev_attr_tc_shadow_val_hi.attr,
-	&dev_attr_tc_sw_inc.attr,
-	&dev_attr_tc_msr.attr,
-	NULL,
-};
+static ssize_t cmb_mode_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
 
-static struct attribute *tpdm_dsb_attrs[] = {
-	&dev_attr_dsb_mode.attr,
-	&dev_attr_dsb_edge_ctrl.attr,
-	&dev_attr_dsb_edge_ctrl_mask.attr,
-	&dev_attr_dsb_patt_val.attr,
-	&dev_attr_dsb_patt_mask.attr,
-	&dev_attr_dsb_patt_ts.attr,
-	&dev_attr_dsb_patt_type.attr,
-	&dev_attr_dsb_trig_patt_val.attr,
-	&dev_attr_dsb_trig_patt_mask.attr,
-	&dev_attr_dsb_trig_ts.attr,
-	&dev_attr_dsb_trig_type.attr,
-	&dev_attr_dsb_select_val.attr,
-	&dev_attr_dsb_msr.attr,
-	NULL,
-};
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
 
-static struct attribute_group tpdm_bc_attr_grp = {
-	.attrs = tpdm_bc_attrs,
-};
+	return scnprintf(buf, PAGE_SIZE, "trace_mode: %s cycle_acc: %d\n",
+			 drvdata->cmb->trace_mode ?
+			 "trace_on_change" : "continuous",
+			 drvdata->cmb->cycle_acc);
+}
 
-static struct attribute_group tpdm_tc_attr_grp = {
-	.attrs = tpdm_tc_attrs,
-};
+static ssize_t cmb_mode_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf,
+				   size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned int trace_mode, cycle_acc;
+	int nval;
 
-static struct attribute_group tpdm_dsb_attr_grp = {
-	.attrs = tpdm_dsb_attrs,
+	nval = sscanf(buf, "%u %u", &trace_mode, &cycle_acc);
+	if (nval != 2)
+		return -EINVAL;
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->cmb->trace_mode = trace_mode;
+	drvdata->cmb->cycle_acc = cycle_acc;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(cmb_mode);
+
+static ssize_t cmb_patt_val_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i;
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_CMB_PATT_CMP; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  drvdata->cmb->patt_val[i]);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+
+static ssize_t cmb_patt_val_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long index, val;
+
+	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
+		return -EINVAL;
+	if (index >= TPDM_CMB_PATT_CMP)
+		return -EINVAL;
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->cmb->patt_val[index] = val;
+	mutex_unlock(&drvdata->lock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cmb_patt_val);
+
+static ssize_t cmb_patt_mask_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	ssize_t size = 0;
+	int i;
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	for (i = 0; i < TPDM_CMB_PATT_CMP; i++) {
+		size += scnprintf(buf + size, PAGE_SIZE - size,
+				  "Index: 0x%x Value: 0x%x\n", i,
+				  drvdata->cmb->patt_mask[i]);
+	}
+	mutex_unlock(&drvdata->lock);
+	return size;
+
+}
+
+static ssize_t cmb_patt_mask_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf, size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long index, val;
+
+	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
+		return -EINVAL;
+	if (index >= TPDM_CMB_PATT_CMP)
+		return -EINVAL;
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->cmb->patt_mask[index] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(cmb_patt_mask);
+
+static ssize_t cmb_patt_ts_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->cmb->patt_ts);
+}
+
+static ssize_t cmb_patt_ts_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->cmb->patt_ts = true;
+	else
+		drvdata->cmb->patt_ts = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(cmb_patt_ts);
+
+static ssize_t cmb_ts_all_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->cmb->ts_all);
+}
+
+static ssize_t cmb_ts_all_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->cmb->ts_all = true;
+	else
+		drvdata->cmb->ts_all = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(cmb_ts_all);
+
+static ssize_t cmb_trig_patt_val_lsb_show(struct device *dev,
+					       struct device_attribute *attr,
+					       char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	val = drvdata->cmb->trig_patt_val[TPDM_CMB_LSB];
+
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+
+static ssize_t cmb_trig_patt_val_lsb_store(struct device *dev,
+						struct device_attribute *attr,
+						const char *buf, size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->cmb->trig_patt_val[TPDM_CMB_LSB] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(cmb_trig_patt_val_lsb);
+
+static ssize_t cmb_trig_patt_mask_lsb_show(struct device *dev,
+						struct device_attribute *attr,
+						char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	val = drvdata->cmb->trig_patt_mask[TPDM_CMB_LSB];
+
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+
+static ssize_t cmb_trig_patt_mask_lsb_store(struct device *dev,
+						 struct device_attribute *attr,
+						 const char *buf, size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->cmb->trig_patt_mask[TPDM_CMB_LSB] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(cmb_trig_patt_mask_lsb);
+
+static ssize_t cmb_trig_patt_val_msb_show(struct device *dev,
+					       struct device_attribute *attr,
+					       char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	val = drvdata->cmb->trig_patt_val[TPDM_CMB_MSB];
+
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+
+static ssize_t cmb_trig_patt_val_msb_store(struct device *dev,
+						struct device_attribute *attr,
+						const char *buf, size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->cmb->trig_patt_val[TPDM_CMB_MSB] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(cmb_trig_patt_val_msb);
+
+static ssize_t cmb_trig_patt_mask_msb_show(struct device *dev,
+						struct device_attribute *attr,
+						char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	val = drvdata->cmb->trig_patt_mask[TPDM_CMB_MSB];
+
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+
+static ssize_t cmb_trig_patt_mask_msb_store(struct device *dev,
+						 struct device_attribute *attr,
+						 const char *buf, size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->cmb->trig_patt_mask[TPDM_CMB_MSB] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(cmb_trig_patt_mask_msb);
+
+static ssize_t cmb_trig_ts_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->cmb->trig_ts);
+}
+
+static ssize_t cmb_trig_ts_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->cmb->trig_ts = true;
+	else
+		drvdata->cmb->trig_ts = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(cmb_trig_ts);
+
+static ssize_t cmb_msr_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned int i;
+	ssize_t len = 0;
+
+	if (!drvdata->msr_support)
+		return -EINVAL;
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	for (i = 0; i < TPDM_CMB_MAX_MSR; i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%u 0x%x\n",
+				 i, drvdata->cmb->msr[i]);
+
+	return len;
+}
+
+static ssize_t cmb_msr_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned int num, val;
+	int nval;
+
+	if (!drvdata->msr_support)
+		return -EINVAL;
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	nval = sscanf(buf, "%u %x", &num, &val);
+	if (nval != 2)
+		return -EINVAL;
+
+	if (num >= TPDM_CMB_MAX_MSR)
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->cmb->msr[num] = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(cmb_msr);
+
+static ssize_t cmb_read_interface_state_show(struct device *dev,
+						  struct device_attribute *attr,
+						  char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+	TPDM_UNLOCK(drvdata);
+	val = tpdm_readl(drvdata, TPDM_CMB_READVAL);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+static DEVICE_ATTR_RO(cmb_read_interface_state);
+
+static ssize_t cmb_read_ctl_reg_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+	TPDM_UNLOCK(drvdata);
+	val = tpdm_readl(drvdata, TPDM_CMB_READCTL);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+
+	if (test_bit(TPDM_DS_CMB, drvdata->datasets))
+		return scnprintf(buf, PAGE_SIZE, "SEL: %lx\n", val);
+	else
+		return scnprintf(buf, PAGE_SIZE, "Lane %u SEL: %lx\n",
+				 (unsigned int)BMVAL(val, 1, 3), val & 0x1);
+}
+
+static ssize_t cmb_read_ctl_reg_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf,
+					   size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+	TPDM_UNLOCK(drvdata);
+	tpdm_writel(drvdata, val, TPDM_CMB_READCTL);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cmb_read_ctl_reg);
+
+static ssize_t mcmb_trig_lane_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_MCMB, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->cmb->mcmb->mcmb_trig_lane);
+}
+
+static ssize_t mcmb_trig_lane_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 10, &val))
+		return -EINVAL;
+	if (val >= TPDM_MCMB_MAX_LANES)
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_MCMB, drvdata->datasets))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->cmb->mcmb->mcmb_trig_lane = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(mcmb_trig_lane);
+
+static ssize_t mcmb_lanes_select_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!test_bit(TPDM_DS_MCMB, drvdata->datasets))
+		return -EPERM;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->cmb->mcmb->mcmb_lane_select);
+}
+
+static ssize_t mcmb_lanes_select_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf,
+					    size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+	if (!test_bit(TPDM_DS_MCMB, drvdata->datasets))
+		return -EPERM;
+
+	val = BMVAL(val, 0, TPDM_MCMB_MAX_LANES - 1);
+
+	mutex_lock(&drvdata->lock);
+	drvdata->cmb->mcmb->mcmb_lane_select = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(mcmb_lanes_select);
+
+static ssize_t cmb_markr_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf,
+				    size_t size)
+{
+	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	if (!(test_bit(TPDM_DS_CMB, drvdata->datasets) ||
+	      test_bit(TPDM_DS_MCMB, drvdata->datasets)))
+		return -EPERM;
+
+	mutex_lock(&drvdata->lock);
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+	TPDM_UNLOCK(drvdata);
+	tpdm_writel(drvdata, val, TPDM_CMB_MARKR);
+	TPDM_LOCK(drvdata);
+	mutex_unlock(&drvdata->lock);
+
+	return size;
+}
+static DEVICE_ATTR_WO(cmb_markr);
+
+static struct attribute *tpdm_bc_attrs[] = {
+	&dev_attr_bc_capture_mode.attr,
+	&dev_attr_bc_retrieval_mode.attr,
+	&dev_attr_bc_reset_counters.attr,
+	&dev_attr_bc_sat_mode.attr,
+	&dev_attr_bc_enable_counters.attr,
+	&dev_attr_bc_clear_counters.attr,
+	&dev_attr_bc_enable_irq.attr,
+	&dev_attr_bc_clear_irq.attr,
+	&dev_attr_bc_trig_val_lo.attr,
+	&dev_attr_bc_trig_val_hi.attr,
+	&dev_attr_bc_enable_ganging.attr,
+	&dev_attr_bc_overflow_val.attr,
+	&dev_attr_bc_ovsr.attr,
+	&dev_attr_bc_counter_sel.attr,
+	&dev_attr_bc_count_val_lo.attr,
+	&dev_attr_bc_count_val_hi.attr,
+	&dev_attr_bc_shadow_val_lo.attr,
+	&dev_attr_bc_shadow_val_hi.attr,
+	&dev_attr_bc_sw_inc.attr,
+	&dev_attr_bc_msr.attr,
+	NULL,
+};
+
+static struct attribute *tpdm_tc_attrs[] = {
+	&dev_attr_tc_capture_mode.attr,
+	&dev_attr_tc_retrieval_mode.attr,
+	&dev_attr_tc_reset_counters.attr,
+	&dev_attr_tc_sat_mode.attr,
+	&dev_attr_tc_enable_counters.attr,
+	&dev_attr_tc_clear_counters.attr,
+	&dev_attr_tc_enable_irq.attr,
+	&dev_attr_tc_clear_irq.attr,
+	&dev_attr_tc_trig_sel.attr,
+	&dev_attr_tc_trig_val_lo.attr,
+	&dev_attr_tc_trig_val_hi.attr,
+	&dev_attr_tc_ovsr_gp.attr,
+	&dev_attr_tc_ovsr_impl.attr,
+	&dev_attr_tc_counter_sel.attr,
+	&dev_attr_tc_count_val_lo.attr,
+	&dev_attr_tc_count_val_hi.attr,
+	&dev_attr_tc_shadow_val_lo.attr,
+	&dev_attr_tc_shadow_val_hi.attr,
+	&dev_attr_tc_sw_inc.attr,
+	&dev_attr_tc_msr.attr,
+	NULL,
+};
+
+static struct attribute *tpdm_dsb_attrs[] = {
+	&dev_attr_dsb_mode.attr,
+	&dev_attr_dsb_edge_ctrl.attr,
+	&dev_attr_dsb_edge_ctrl_mask.attr,
+	&dev_attr_dsb_patt_val.attr,
+	&dev_attr_dsb_patt_mask.attr,
+	&dev_attr_dsb_patt_ts.attr,
+	&dev_attr_dsb_patt_type.attr,
+	&dev_attr_dsb_trig_patt_val.attr,
+	&dev_attr_dsb_trig_patt_mask.attr,
+	&dev_attr_dsb_trig_ts.attr,
+	&dev_attr_dsb_trig_type.attr,
+	&dev_attr_dsb_select_val.attr,
+	&dev_attr_dsb_msr.attr,
+	NULL,
+};
+
+static struct attribute *tpdm_cmb_attrs[] = {
+	&dev_attr_cmb_available_modes.attr,
+	&dev_attr_cmb_mode.attr,
+	&dev_attr_cmb_patt_val.attr,
+	&dev_attr_cmb_patt_mask.attr,
+	&dev_attr_cmb_patt_ts.attr,
+	&dev_attr_cmb_ts_all.attr,
+	&dev_attr_cmb_trig_patt_val_lsb.attr,
+	&dev_attr_cmb_trig_patt_mask_lsb.attr,
+	&dev_attr_cmb_trig_patt_val_msb.attr,
+	&dev_attr_cmb_trig_patt_mask_msb.attr,
+	&dev_attr_cmb_trig_ts.attr,
+	&dev_attr_cmb_msr.attr,
+	&dev_attr_cmb_read_interface_state.attr,
+	&dev_attr_cmb_read_ctl_reg.attr,
+	&dev_attr_cmb_markr.attr,
+	&dev_attr_mcmb_trig_lane.attr,
+	&dev_attr_mcmb_lanes_select.attr,
+	NULL,
+};
+
+static struct attribute_group tpdm_bc_attr_grp = {
+	.attrs = tpdm_bc_attrs,
+};
+
+static struct attribute_group tpdm_tc_attr_grp = {
+	.attrs = tpdm_tc_attrs,
+};
+
+static struct attribute_group tpdm_dsb_attr_grp = {
+	.attrs = tpdm_dsb_attrs,
+};
+
+static struct attribute_group tpdm_cmb_attr_grp = {
+	.attrs = tpdm_cmb_attrs,
 };
 
 static struct attribute *tpdm_attrs[] = {
@@ -3227,6 +4017,7 @@ static const struct attribute_group *tpdm_attr_grps[] = {
 	&tpdm_bc_attr_grp,
 	&tpdm_tc_attr_grp,
 	&tpdm_dsb_attr_grp,
+	&tpdm_cmb_attr_grp,
 	NULL,
 };
 
-- 
2.17.1


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

* [PATCH 09/10] coresight: Add driver to support Coresight device TPDA
  2021-10-21  7:38 [PATCH 00/10] Add support for TPDM and TPDA Tao Zhang
                   ` (7 preceding siblings ...)
  2021-10-21  7:38 ` [PATCH 08/10] Coresight: Enable CMB " Tao Zhang
@ 2021-10-21  7:38 ` Tao Zhang
  2021-10-21  7:38 ` [PATCH 10/10] ARM: dts: msm: Add TPDA and TPDM support to DTS for RB5 Tao Zhang
  2021-10-28 17:16 ` [PATCH 00/10] Add support for TPDM and TPDA Mathieu Poirier
  10 siblings, 0 replies; 37+ messages in thread
From: Tao Zhang @ 2021-10-21  7:38 UTC (permalink / raw)
  To: Mathieu Poirier, Suzuki K Poulose, Alexander Shishkin
  Cc: Tao Zhang, Mike Leach, Leo Yan, Greg Kroah-Hartman, coresight,
	linux-arm-kernel, linux-kernel, Tingwei Zhang, Mao Jinlong,
	Yuanfang Zhang, Trilok Soni

Add driver to support Coresight device TPDA. This driver provides
support for configuring aggregator. This is primarily useful for
pulling the data sets from one or more attached monitors and
pushing the resultant data out. Multiple monitors are connected on
different input ports of TPDA.

Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
---
 .../bindings/arm/coresight-tpda.yaml          | 169 ++++
 MAINTAINERS                                   |   3 +-
 drivers/hwtracing/coresight/Kconfig           |   9 +
 drivers/hwtracing/coresight/Makefile          |   1 +
 drivers/hwtracing/coresight/coresight-tpda.c  | 828 ++++++++++++++++++
 5 files changed, 1009 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/arm/coresight-tpda.yaml
 create mode 100644 drivers/hwtracing/coresight/coresight-tpda.c

diff --git a/Documentation/devicetree/bindings/arm/coresight-tpda.yaml b/Documentation/devicetree/bindings/arm/coresight-tpda.yaml
new file mode 100644
index 000000000000..860e86d460b5
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/coresight-tpda.yaml
@@ -0,0 +1,169 @@
+# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
+# Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/arm/coresigh-tpda.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: "Trace, Profiling and Diagnostics Aggregator - TPDA"
+
+maintainers:
+  - Tao Zhang <quic_taozha@quicinc.com>
+
+description:
+  TPDAs are responsible for:
+    Packetization and timestamping of data sets utilizing the MIPI STPv2 packet protocol
+    Pulling data sets from one or more attached TPDM and pushing the resultant (packetized) data out a master ATB interface
+    Performing an arbitrated ATB interleaving (funneling) task for free-flowing data from TPDM (i.e. CMB and DSB data set flows)
+
+properties:
+  $nodename:
+    pattern: "^tpda(@[0-9a-f]+)$"
+
+  compatible:
+    items:
+      const: arm,primecell
+
+  reg:
+    maxItems: 1
+
+  reg-names:
+    items:
+      const: tpda-base
+
+  qcom,tpda-atid:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    maxItems: 1
+    description:
+      Use the ATID field for trace source identification. This allows multiple TPDMs to be interleaved
+      and formatted via the Coresight trace formatter framing protocol and de-formatted/parsed on a host or debugger.
+
+  qcom,bc-elem-size:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    minItems: 1
+    description:
+      Specifies the BC element size supported by each monitor connected to the aggregator on each port.
+
+  qcom,tc-elem-size:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    minItems: 1
+    description:
+      Specifies the TC element size supported by each monitor connected to the aggregator on each port.
+
+  qcom,dsb-elem-size:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    minItems: 1
+    description:
+      Specifies the DSB element size supported by each monitor connected to the aggregator on each port.
+
+  qcom,cmb-elem-size:
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    minItems: 1
+    description:
+      Specifies the CMB element size supported by each monitor connected to the aggregator on each port.
+
+  clocks:
+    description:
+      The clock node that QDSS components need to use.
+
+  clock-names:
+    items:
+      const: apb_pclk
+
+  in-ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    description:
+      Input connections from TPDM to TPDA
+  
+    properties:
+      "#size-cells":
+        const: 0
+
+      "#address-cells":
+        const: 1
+
+    patternProperties:
+      '^port@([0-9]+)$':
+      type: object
+      description:
+        Input connections from TPDM to TPDA
+
+      properties:
+        reg:
+          maxItems: 1
+
+      required:
+        reg
+
+
+  out-ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    description:
+      Output connections from the TPDA to legacy CoreSight trace bus.
+   
+    properties:
+     $ref: /schemas/graph.yaml#/properties/port
+     port:
+       description: 
+         Output connection from the TPDA to legacy CoreSight Trace bus.
+       
+
+required:
+    - compatible
+    - reg
+    - reg-names
+    - qcom,tpda-atid:
+    - clocks
+    - clock-names
+    - in-ports
+    - out-ports
+
+additionalProperties: false
+  
+examples:
+  # minimum tpda definition.
+  - |
+    tpda@10004000 {
+       compatible = "arm,primecell";
+    
+       reg = <0x10004000 0x1000>;
+       reg-names = "tpda-base";
+    
+       qcom,tpda-atid = <65>;
+    
+       clocks = <&aoss_qmp>;
+       clock-names = "apb_pclk";
+    
+       in-ports {
+    
+         #address-cells = <1>;
+         #size-cells = <0>;
+    
+         port@1 {
+           reg = <1>;
+           tpda_qdss_1_in_tpdm_spdm: endpoint {
+                 remote-endpoint =
+                    <&tpdm_spdm_out_tpda_qdss_1>;
+            };
+         };
+    
+         port@0 {
+                 reg = <0>;
+                 tpda_qdss_0_in_tpdm_dcc: endpoint {
+                    remote-endpoint =
+                    <&tpdm_dcc_out_tpda_qdss_0>;
+                 };
+              };
+        };
+    
+       out-ports {
+         port {
+                 tpda_qdss_out_funnel_in0: endpoint {
+                    remote-endpoint =
+                    <&funnel_in0_in_tpda_qdss>;
+                  };
+          };
+       };
+    };
+
+...
\ No newline at end of file
diff --git a/MAINTAINERS b/MAINTAINERS
index cabecf760488..71dd4178d4f8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15303,9 +15303,10 @@ L:	netdev@vger.kernel.org
 S:	Supported
 F:	drivers/net/ipa/
 
-QCOM CORESIGHT TPDM DRIVER
+QCOM CORESIGHT COMPONENTS DRIVER
 M:	Tao Zhang <quic_taozha@quicinc.com>
 S:	Maintained
+F:	drivers/hwtracing/coresight/coresight-tpda.c
 F:	drivers/hwtracing/coresight/coresight-tpdm.c
 
 QEMU MACHINE EMULATOR AND VIRTUALIZER SUPPORT
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index abe244a968f6..206a27325bd3 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -197,4 +197,13 @@ config CORESIGHT_TPDM
 	  primarily responsible for data set collection and support the
 	  ability to collect any permutation of data set types. Monitors are
 	  also responsible for interaction with system cross triggering.
+
+config CORESIGHT_TPDA
+	tristate "CoreSight Trace, Profiling & Diagnostics Aggregator driver"
+	select CORESIGHT_LINKS_AND_SINKS
+	help
+	  This driver provides support for configuring aggregator. This is
+	  primarily useful for pulling the data sets from one or more
+	  attached monitors and pushing the resultant data out. Multiple
+	  monitors are connected on different input ports of TPDA.
 endif
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index e7392a0dddeb..cd8079ec276d 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -26,5 +26,6 @@ obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o
 obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
 obj-$(CONFIG_CORESIGHT_TRBE) += coresight-trbe.o
 obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o
+obj-$(CONFIG_CORESIGHT_TPDA) += coresight-tpda.o
 coresight-cti-y := coresight-cti-core.o	coresight-cti-platform.o \
 		   coresight-cti-sysfs.o
diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c
new file mode 100644
index 000000000000..3dc46e173be4
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-tpda.c
@@ -0,0 +1,828 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/amba/bus.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/bitmap.h>
+#include <linux/of.h>
+#include <linux/coresight.h>
+
+#include "coresight-priv.h"
+
+#define tpda_writel(drvdata, val, off)	__raw_writel((val), drvdata->base + off)
+#define tpda_readl(drvdata, off)	__raw_readl(drvdata->base + off)
+
+#define TPDA_LOCK(drvdata)						\
+do {									\
+	mb(); /* ensure configuration take effect before we lock it */	\
+	tpda_writel(drvdata, 0x0, CORESIGHT_LAR);			\
+} while (0)
+#define TPDA_UNLOCK(drvdata)						\
+do {									\
+	tpda_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR);		\
+	mb(); /* ensure unlock take effect before we configure */	\
+} while (0)
+
+#define TPDA_CR			(0x000)
+#define TPDA_Pn_CR(n)		(0x004 + (n * 4))
+#define TPDA_FPID_CR		(0x084)
+#define TPDA_FREQREQ_VAL	(0x088)
+#define TPDA_SYNCR		(0x08C)
+#define TPDA_FLUSH_CR		(0x090)
+#define TPDA_FLUSH_SR		(0x094)
+#define TPDA_FLUSH_ERR		(0x098)
+
+#define TPDA_MAX_INPORTS	32
+
+DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda");
+
+struct tpda_drvdata {
+	void __iomem		*base;
+	struct device		*dev;
+	struct coresight_device	*csdev;
+	struct mutex		lock;
+	bool			enable;
+	uint32_t		atid;
+	uint32_t		bc_esize[TPDA_MAX_INPORTS];
+	uint32_t		tc_esize[TPDA_MAX_INPORTS];
+	uint32_t		dsb_esize[TPDA_MAX_INPORTS];
+	uint32_t		cmb_esize[TPDA_MAX_INPORTS];
+	bool			trig_async;
+	bool			trig_flag_ts;
+	bool			trig_freq;
+	bool			freq_ts;
+	uint32_t		freq_req_val;
+	bool			freq_req;
+	bool			cmbchan_mode;
+};
+
+static void __tpda_enable_pre_port(struct tpda_drvdata *drvdata)
+{
+	uint32_t val;
+
+	val = tpda_readl(drvdata, TPDA_CR);
+	/* Set the master id */
+	val = val & ~(0x7F << 13);
+	val = val & ~(0x7F << 6);
+	val |= (drvdata->atid << 6);
+	if (drvdata->trig_async)
+		val = val | BIT(5);
+	else
+		val = val & ~BIT(5);
+	if (drvdata->trig_flag_ts)
+		val = val | BIT(4);
+	else
+		val = val & ~BIT(4);
+	if (drvdata->trig_freq)
+		val = val | BIT(3);
+	else
+		val = val & ~BIT(3);
+	if (drvdata->freq_ts)
+		val = val | BIT(2);
+	else
+		val = val & ~BIT(2);
+	if (drvdata->cmbchan_mode)
+		val = val | BIT(20);
+	else
+		val = val & ~BIT(20);
+	tpda_writel(drvdata, val, TPDA_CR);
+
+	/*
+	 * If FLRIE bit is set, set the master and channel
+	 * id as zero
+	 */
+	if (BMVAL(tpda_readl(drvdata, TPDA_CR), 4, 4))
+		tpda_writel(drvdata, 0x0, TPDA_FPID_CR);
+}
+
+static void __tpda_enable_port(struct tpda_drvdata *drvdata, int port)
+{
+	uint32_t val;
+
+	val = tpda_readl(drvdata, TPDA_Pn_CR(port));
+	if (drvdata->bc_esize[port] == 32)
+		val = val & ~BIT(4);
+	else if (drvdata->bc_esize[port] == 64)
+		val = val | BIT(4);
+
+	if (drvdata->tc_esize[port] == 32)
+		val = val & ~BIT(5);
+	else if (drvdata->tc_esize[port] == 64)
+		val = val | BIT(5);
+
+	if (drvdata->dsb_esize[port] == 32)
+		val = val & ~BIT(8);
+	else if (drvdata->dsb_esize[port] == 64)
+		val = val | BIT(8);
+
+	val = val & ~(0x3 << 6);
+	if (drvdata->cmb_esize[port] == 8)
+		val &= ~(0x3 << 6);
+	else if (drvdata->cmb_esize[port] == 32)
+		val |= (0x1 << 6);
+	else if (drvdata->cmb_esize[port] == 64)
+		val |= (0x2 << 6);
+
+	/* Set the hold time */
+	val = val & ~(0x7 << 1);
+	val |= (0x5 << 1);
+	tpda_writel(drvdata, val, TPDA_Pn_CR(port));
+	/* Enable the port */
+	val = val | BIT(0);
+	tpda_writel(drvdata, val, TPDA_Pn_CR(port));
+}
+
+static void __tpda_enable_post_port(struct tpda_drvdata *drvdata)
+{
+	uint32_t val;
+
+	val = tpda_readl(drvdata, TPDA_SYNCR);
+	/* Clear the mode */
+	val = val & ~BIT(12);
+	/* Program the counter value */
+	val = val | 0xFFF;
+	tpda_writel(drvdata, val, TPDA_SYNCR);
+
+	if (drvdata->freq_req_val)
+		tpda_writel(drvdata, drvdata->freq_req_val, TPDA_FREQREQ_VAL);
+	else
+		tpda_writel(drvdata, 0x0, TPDA_FREQREQ_VAL);
+
+	val = tpda_readl(drvdata, TPDA_CR);
+	if (drvdata->freq_req)
+		val = val | BIT(1);
+	else
+		val = val & ~BIT(1);
+	tpda_writel(drvdata, val, TPDA_CR);
+}
+
+static void __tpda_enable(struct tpda_drvdata *drvdata, int port)
+{
+	TPDA_UNLOCK(drvdata);
+
+	if (!drvdata->enable)
+		__tpda_enable_pre_port(drvdata);
+
+	__tpda_enable_port(drvdata, port);
+
+	if (!drvdata->enable)
+		__tpda_enable_post_port(drvdata);
+
+	TPDA_LOCK(drvdata);
+}
+
+static int tpda_enable(struct coresight_device *csdev, int inport, int outport)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	mutex_lock(&drvdata->lock);
+	__tpda_enable(drvdata, inport);
+	drvdata->enable = true;
+	mutex_unlock(&drvdata->lock);
+
+	dev_info(drvdata->dev, "TPDA inport %d enabled\n", inport);
+	return 0;
+}
+
+static void __tpda_disable(struct tpda_drvdata *drvdata, int port)
+{
+	uint32_t val;
+
+	TPDA_UNLOCK(drvdata);
+
+	val = tpda_readl(drvdata, TPDA_Pn_CR(port));
+	val = val & ~BIT(0);
+	tpda_writel(drvdata, val, TPDA_Pn_CR(port));
+
+	TPDA_LOCK(drvdata);
+}
+
+static void tpda_disable(struct coresight_device *csdev, int inport,
+			   int outport)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	mutex_lock(&drvdata->lock);
+	__tpda_disable(drvdata, inport);
+	drvdata->enable = false;
+	mutex_unlock(&drvdata->lock);
+
+	dev_info(drvdata->dev, "TPDA inport %d disabled\n", inport);
+}
+
+static const struct coresight_ops_link tpda_link_ops = {
+	.enable		= tpda_enable,
+	.disable	= tpda_disable,
+};
+
+static const struct coresight_ops tpda_cs_ops = {
+	.link_ops	= &tpda_link_ops,
+};
+
+static ssize_t trig_async_enable_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->trig_async);
+}
+
+static ssize_t trig_async_enable_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf,
+					    size_t size)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->trig_async = true;
+	else
+		drvdata->trig_async = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(trig_async_enable);
+
+static ssize_t trig_flag_ts_enable_show(struct device *dev,
+					     struct device_attribute *attr,
+					     char *buf)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->trig_flag_ts);
+}
+
+static ssize_t trig_flag_ts_enable_store(struct device *dev,
+					      struct device_attribute *attr,
+					      const char *buf,
+					      size_t size)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->trig_flag_ts = true;
+	else
+		drvdata->trig_flag_ts = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(trig_flag_ts_enable);
+
+static ssize_t trig_freq_enable_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->trig_freq);
+}
+
+static ssize_t trig_freq_enable_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf,
+					   size_t size)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->trig_freq = true;
+	else
+		drvdata->trig_freq = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(trig_freq_enable);
+
+static ssize_t freq_ts_enable_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->freq_ts);
+}
+
+static ssize_t freq_ts_enable_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t size)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->freq_ts = true;
+	else
+		drvdata->freq_ts = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(freq_ts_enable);
+
+static ssize_t freq_req_val_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val = drvdata->freq_req_val;
+
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+
+static ssize_t freq_req_val_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf,
+				       size_t size)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+	drvdata->freq_req_val = val;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(freq_req_val);
+
+static ssize_t freq_req_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->freq_req);
+}
+
+static ssize_t freq_req_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf,
+				   size_t size)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->freq_req = true;
+	else
+		drvdata->freq_req = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(freq_req);
+
+static ssize_t global_flush_req_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	mutex_lock(&drvdata->lock);
+
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDA_UNLOCK(drvdata);
+	val = tpda_readl(drvdata, TPDA_CR);
+	TPDA_LOCK(drvdata);
+
+	mutex_unlock(&drvdata->lock);
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+
+static ssize_t global_flush_req_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf,
+					   size_t size)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (val) {
+		TPDA_UNLOCK(drvdata);
+		val = tpda_readl(drvdata, TPDA_CR);
+		val = val | BIT(0);
+		tpda_writel(drvdata, val, TPDA_CR);
+		TPDA_LOCK(drvdata);
+	}
+
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(global_flush_req);
+
+static ssize_t port_flush_req_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	mutex_lock(&drvdata->lock);
+
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	TPDA_UNLOCK(drvdata);
+	val = tpda_readl(drvdata, TPDA_FLUSH_CR);
+	TPDA_LOCK(drvdata);
+
+	mutex_unlock(&drvdata->lock);
+	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
+}
+
+static ssize_t port_flush_req_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t size)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 16, &val))
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+
+	if (!drvdata->enable) {
+		mutex_unlock(&drvdata->lock);
+		return -EPERM;
+	}
+
+	if (val) {
+		TPDA_UNLOCK(drvdata);
+		tpda_writel(drvdata, val, TPDA_FLUSH_CR);
+		TPDA_LOCK(drvdata);
+	}
+
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(port_flush_req);
+
+static ssize_t cmbchan_mode_show(struct device *dev,
+					     struct device_attribute *attr,
+					     char *buf)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n",
+			 (unsigned int)drvdata->cmbchan_mode);
+}
+
+static ssize_t cmbchan_mode_store(struct device *dev,
+					      struct device_attribute *attr,
+					      const char *buf,
+					      size_t size)
+{
+	struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	bool val;
+
+	if (kstrtobool(buf, &val))
+		return -EINVAL;
+
+	mutex_lock(&drvdata->lock);
+	if (val)
+		drvdata->cmbchan_mode = true;
+	else
+		drvdata->cmbchan_mode = false;
+	mutex_unlock(&drvdata->lock);
+	return size;
+}
+static DEVICE_ATTR_RW(cmbchan_mode);
+
+static struct attribute *tpda_attrs[] = {
+	&dev_attr_trig_async_enable.attr,
+	&dev_attr_trig_flag_ts_enable.attr,
+	&dev_attr_trig_freq_enable.attr,
+	&dev_attr_freq_ts_enable.attr,
+	&dev_attr_freq_req_val.attr,
+	&dev_attr_freq_req.attr,
+	&dev_attr_global_flush_req.attr,
+	&dev_attr_port_flush_req.attr,
+	&dev_attr_cmbchan_mode.attr,
+	NULL,
+};
+
+static struct attribute_group tpda_attr_grp = {
+	.attrs = tpda_attrs,
+};
+
+static const struct attribute_group *tpda_attr_grps[] = {
+	&tpda_attr_grp,
+	NULL,
+};
+
+static int tpda_parse_tc(struct tpda_drvdata *drvdata)
+{
+	int len, port, i;
+	const __be32 *prop;
+	struct device_node *node = drvdata->dev->of_node;
+
+	prop = of_get_property(node, "qcom,tc-elem-size", &len);
+	if (prop) {
+		len /= sizeof(__be32);
+		if (len < 2 || len > 63 || len % 2 != 0) {
+			dev_err(drvdata->dev,
+				"Dataset TC width entries are wrong\n");
+			return -EINVAL;
+		}
+
+		for (i = 0; i < len; i++) {
+			port = be32_to_cpu(prop[i++]);
+			if (port >= TPDA_MAX_INPORTS) {
+				dev_err(drvdata->dev,
+					"Wrong port specified for TC\n");
+				return -EINVAL;
+			}
+			drvdata->tc_esize[port] = be32_to_cpu(prop[i]);
+		}
+	}
+
+	return 0;
+}
+
+static int tpda_parse_bc(struct tpda_drvdata *drvdata)
+{
+	int len, port, i;
+	const __be32 *prop;
+	struct device_node *node = drvdata->dev->of_node;
+
+	prop = of_get_property(node, "qcom,bc-elem-size", &len);
+	if (prop) {
+		len /= sizeof(__be32);
+		if (len < 2 || len > 63 || len % 2 != 0) {
+			dev_err(drvdata->dev,
+				"Dataset BC width entries are wrong\n");
+			return -EINVAL;
+		}
+
+		for (i = 0; i < len; i++) {
+			port = be32_to_cpu(prop[i++]);
+			if (port >= TPDA_MAX_INPORTS) {
+				dev_err(drvdata->dev,
+					"Wrong port specified for BC\n");
+				return -EINVAL;
+			}
+			drvdata->bc_esize[port] = be32_to_cpu(prop[i]);
+		}
+	}
+
+	return 0;
+}
+
+static int tpda_parse_dsb(struct tpda_drvdata *drvdata)
+{
+	int len, port, i;
+	const __be32 *prop;
+	struct device_node *node = drvdata->dev->of_node;
+
+	prop = of_get_property(node, "qcom,dsb-elem-size", &len);
+	if (prop) {
+		len /= sizeof(__be32);
+		if (len < 2 || len > 63 || len % 2 != 0) {
+			dev_err(drvdata->dev,
+				"Dataset DSB width entries are wrong\n");
+			return -EINVAL;
+		}
+
+		for (i = 0; i < len; i++) {
+			port = be32_to_cpu(prop[i++]);
+			if (port >= TPDA_MAX_INPORTS) {
+				dev_err(drvdata->dev,
+					"Wrong port specified for DSB\n");
+				return -EINVAL;
+			}
+			drvdata->dsb_esize[port] = be32_to_cpu(prop[i]);
+		}
+	}
+
+	return 0;
+}
+
+static int tpda_parse_cmb(struct tpda_drvdata *drvdata)
+{
+	int len, port, i;
+	const __be32 *prop;
+	struct device_node *node = drvdata->dev->of_node;
+
+	prop = of_get_property(node, "qcom,cmb-elem-size", &len);
+	if (prop) {
+		len /= sizeof(__be32);
+		if (len < 2 || len > 63 || len % 2 != 0) {
+			dev_err(drvdata->dev,
+				"Dataset CMB width entries are wrong\n");
+			return -EINVAL;
+		}
+
+		for (i = 0; i < len; i++) {
+			port = be32_to_cpu(prop[i++]);
+			if (port >= TPDA_MAX_INPORTS) {
+				dev_err(drvdata->dev,
+					"Wrong port specified for CMB\n");
+				return -EINVAL;
+			}
+			drvdata->cmb_esize[port] = be32_to_cpu(prop[i]);
+		}
+	}
+
+	return 0;
+}
+
+static int tpda_parse_of_data(struct tpda_drvdata *drvdata)
+{
+	int ret;
+	struct device_node *node = drvdata->dev->of_node;
+
+	ret = of_property_read_u32(node, "qcom,tpda-atid", &drvdata->atid);
+	if (ret) {
+		dev_err(drvdata->dev, "TPDA ATID is not specified\n");
+		return -EINVAL;
+	}
+
+	ret = tpda_parse_tc(drvdata);
+	if (ret) {
+		dev_err(drvdata->dev, "Dataset TC width entries are wrong\n");
+		return -EINVAL;
+	}
+
+	ret = tpda_parse_bc(drvdata);
+	if (ret) {
+		dev_err(drvdata->dev, "Dataset BC width entries are wrong\n");
+		return -EINVAL;
+	}
+
+	ret = tpda_parse_dsb(drvdata);
+	if (ret) {
+		dev_err(drvdata->dev, "Dataset DSB width entries are wrong\n");
+		return -EINVAL;
+	}
+
+	ret = tpda_parse_cmb(drvdata);
+	if (ret) {
+		dev_err(drvdata->dev, "Dataset CMB width entries are wrong\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void tpda_init_default_data(struct tpda_drvdata *drvdata)
+{
+	drvdata->freq_ts = true;
+}
+
+static bool coresight_authstatus_enabled(void __iomem *addr)
+{
+	int ret;
+	unsigned int auth_val;
+
+	if (!addr)
+		return false;
+
+	auth_val = readl_relaxed(addr + CORESIGHT_AUTHSTATUS);
+
+	if ((BMVAL(auth_val, 0, 1) == 0x2) ||
+		(BMVAL(auth_val, 2, 3) == 0x2) ||
+		(BMVAL(auth_val, 4, 5) == 0x2) ||
+		(BMVAL(auth_val, 6, 7) == 0x2))
+		ret = false;
+	else
+		ret = true;
+
+	return ret;
+}
+
+static int tpda_probe(struct amba_device *adev, const struct amba_id *id)
+{
+	int ret;
+	struct device *dev = &adev->dev;
+	struct coresight_platform_data *pdata;
+	struct tpda_drvdata *drvdata;
+	struct coresight_desc desc = { 0 };
+
+	desc.name = coresight_alloc_device_name(&tpda_devs, dev);
+	if (!desc.name)
+		return -ENOMEM;
+	pdata = coresight_get_platform_data(dev);
+	if (IS_ERR(pdata))
+		return PTR_ERR(pdata);
+	adev->dev.platform_data = pdata;
+
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+
+	drvdata->dev = &adev->dev;
+	dev_set_drvdata(dev, drvdata);
+
+	drvdata->base = devm_ioremap_resource(dev, &adev->res);
+	if (!drvdata->base)
+		return -ENOMEM;
+
+	mutex_init(&drvdata->lock);
+
+	ret = tpda_parse_of_data(drvdata);
+	if (ret)
+		return ret;
+
+	if (!coresight_authstatus_enabled(drvdata->base))
+		goto err;
+
+	tpda_init_default_data(drvdata);
+
+	desc.type = CORESIGHT_DEV_TYPE_LINK;
+	desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
+	desc.ops = &tpda_cs_ops;
+	desc.pdata = adev->dev.platform_data;
+	desc.dev = &adev->dev;
+	desc.groups = tpda_attr_grps;
+	drvdata->csdev = coresight_register(&desc);
+	if (IS_ERR(drvdata->csdev))
+		return PTR_ERR(drvdata->csdev);
+
+	pm_runtime_put(&adev->dev);
+
+	dev_dbg(drvdata->dev, "TPDA initialized\n");
+	return 0;
+err:
+	return -EPERM;
+}
+
+static struct amba_id tpda_ids[] = {
+	{
+		.id     = 0x000f0f00,
+		.mask   = 0x000fff00,
+		.data	= "TPDA",
+	},
+	{ 0, 0},
+};
+
+static struct amba_driver tpda_driver = {
+	.drv = {
+		.name   = "coresight-tpda",
+		.owner	= THIS_MODULE,
+		.suppress_bind_attrs = true,
+	},
+	.probe          = tpda_probe,
+	.id_table	= tpda_ids,
+};
+
+module_amba_driver(tpda_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Aggregator driver");
-- 
2.17.1


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

* [PATCH 10/10] ARM: dts: msm: Add TPDA and TPDM support to DTS for RB5
  2021-10-21  7:38 [PATCH 00/10] Add support for TPDM and TPDA Tao Zhang
                   ` (8 preceding siblings ...)
  2021-10-21  7:38 ` [PATCH 09/10] coresight: Add driver to support Coresight device TPDA Tao Zhang
@ 2021-10-21  7:38 ` Tao Zhang
  2021-11-02 18:02   ` Mathieu Poirier
  2021-11-04  9:45   ` Suzuki K Poulose
  2021-10-28 17:16 ` [PATCH 00/10] Add support for TPDM and TPDA Mathieu Poirier
  10 siblings, 2 replies; 37+ messages in thread
From: Tao Zhang @ 2021-10-21  7:38 UTC (permalink / raw)
  To: Mathieu Poirier, Suzuki K Poulose, Alexander Shishkin
  Cc: Tao Zhang, Mike Leach, Leo Yan, Greg Kroah-Hartman, coresight,
	linux-arm-kernel, linux-kernel, Tingwei Zhang, Mao Jinlong,
	Yuanfang Zhang, Trilok Soni

Add TPDA and TPDM support to DTS for RB5 board. This change is a
sample for validating. After applying this patch, the new TPDM and
TPDA nodes can be observed at the coresight devices path. TPDM and
TPDA hardware can be operated by commands.

List the commands for validating this series patches as below.
echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
echo 1 > /sys/bus/coresight/devices/tpdm0/enable_source
echo 1 > /sys/bus/coresight/devices/tpdm0/integration_test
echo 2 > /sys/bus/coresight/devices/tpdm0/integration_test
cat /dev/tmc_etf0 > /data/etf-tpdm0.bin
echo 0 > /sys/bus/coresight/devices/tpdm0/enable_source
echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
echo 1 > /sys/bus/coresight/devices/tpdm1/enable_source
echo 1 > /sys/bus/coresight/devices/tpdm1/integration_test
echo 2 > /sys/bus/coresight/devices/tpdm1/integration_test
cat /dev/tmc_etf0 > /data/etf-tpdm1.bin
echo 0 > /sys/bus/coresight/devices/tpdm1/enable_source
echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
echo 1 > /sys/bus/coresight/devices/tpdm2/enable_source
echo 1 > /sys/bus/coresight/devices/tpdm2/integration_test
echo 2 > /sys/bus/coresight/devices/tpdm2/integration_test
cat /dev/tmc_etf0 > /data/etf-tpdm2.bin
echo 0 > /sys/bus/coresight/devices/tpdm2/enable_source
echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink

If the data from TPDMs can be obtained from the ETF, it means
that the TPDMs verification is successful. At the same time,
since TPDM0, TPDM1 and TPDM2 are all connected to the same
funnel "funnel@6c2d000" and output via different output ports,
it also means that the following patches verification is
successful.
coresight: add support to enable more coresight paths
coresight: funnel: add support for multiple output ports

Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
---
 arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 439 +++++++++++++++++++++++
 1 file changed, 439 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
index 8ac96f8e79d4..bcec8b181e11 100644
--- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
+++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
@@ -222,6 +222,445 @@
 		regulator-max-microvolt = <1800000>;
 		regulator-always-on;
 	};
+
+	stm@6002000 {
+		compatible = "arm,coresight-stm", "arm,primecell";
+		reg = <0 0x06002000 0 0x1000>,
+		      <0 0x16280000 0 0x180000>;
+		reg-names = "stm-base", "stm-stimulus-base";
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+
+		out-ports {
+			port {
+				stm_out: endpoint {
+					remote-endpoint =
+					  <&funnel0_in7>;
+				};
+			};
+		};
+	};
+
+	funnel@6041000 {
+		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
+		reg = <0 0x06041000 0 0x1000>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+
+		out-ports {
+			port {
+				funnel0_out: endpoint {
+					remote-endpoint =
+					  <&merge_funnel_in0>;
+				};
+			};
+		};
+
+		in-ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@7 {
+				reg = <7>;
+				funnel0_in7: endpoint {
+					remote-endpoint = <&stm_out>;
+				};
+			};
+		};
+	};
+
+	funnel@6042000 {
+		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
+		reg = <0 0x06042000 0 0x1000>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+
+		out-ports {
+			port {
+				funnel2_out: endpoint {
+					remote-endpoint =
+					  <&merge_funnel_in2>;
+				};
+			};
+		};
+
+		in-ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@4 {
+				reg = <4>;
+				funnel2_in5: endpoint {
+					remote-endpoint =
+					  <&apss_merge_funnel_out>;
+				};
+			};
+		};
+	};
+
+	funnel@6b04000 {
+		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
+		arm,primecell-periphid = <0x000bb908>;
+
+		reg = <0 0x6b04000 0 0x1000>;
+		reg-names = "funnel-base";
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+
+		out-ports {
+			port {
+				merge_funnel_out: endpoint {
+					remote-endpoint =
+						<&etf_in>;
+				};
+			};
+		};
+
+		in-ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@7 {
+				reg = <7>;
+				swao_funnel_in7: endpoint {
+					slave-mode;
+					remote-endpoint=
+						<&merg_funnel_out>;
+				};
+			};
+		};
+
+	};
+
+	funnel@6045000 {
+		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
+		reg = <0 0x06045000 0 0x1000>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+
+		out-ports {
+			port {
+				merg_funnel_out: endpoint {
+					remote-endpoint = <&swao_funnel_in7>;
+				};
+			};
+		};
+
+		in-ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				merge_funnel_in0: endpoint {
+					remote-endpoint =
+					  <&funnel0_out>;
+				};
+			};
+
+			port@1 {
+				reg = <1>;
+				merge_funnel_in2: endpoint {
+					remote-endpoint =
+					  <&funnel2_out>;
+				};
+			};
+		};
+	};
+
+	etf@6b05000 {
+		compatible = "arm,coresight-tmc", "arm,primecell";
+		reg = <0 0x06b05000 0 0x1000>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+
+		in-ports {
+			port {
+				etf_in: endpoint {
+					remote-endpoint =
+					  <&merge_funnel_out>;
+				};
+			};
+		};
+	};
+
+	etm@7040000 {
+		compatible = "arm,coresight-etm4x", "arm,primecell";
+		reg = <0 0x07040000 0 0x1000>;
+
+		cpu = <&CPU0>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+		arm,coresight-loses-context-with-cpu;
+
+		out-ports {
+			port {
+				etm0_out: endpoint {
+					remote-endpoint =
+					  <&apss_funnel_in0>;
+				};
+			};
+		};
+	};
+
+	etm@7140000 {
+		compatible = "arm,coresight-etm4x", "arm,primecell";
+		reg = <0 0x07140000 0 0x1000>;
+
+		cpu = <&CPU1>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+		arm,coresight-loses-context-with-cpu;
+
+		out-ports {
+			port {
+				etm1_out: endpoint {
+					remote-endpoint =
+					  <&apss_funnel_in1>;
+				};
+			};
+		};
+	};
+
+	etm@7240000 {
+		compatible = "arm,coresight-etm4x", "arm,primecell";
+		reg = <0 0x07240000 0 0x1000>;
+
+		cpu = <&CPU2>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+		arm,coresight-loses-context-with-cpu;
+
+		out-ports {
+			port {
+				etm2_out: endpoint {
+					remote-endpoint =
+					  <&apss_funnel_in2>;
+				};
+			};
+		};
+	};
+
+	etm@7340000 {
+		compatible = "arm,coresight-etm4x", "arm,primecell";
+		reg = <0 0x07340000 0 0x1000>;
+
+		cpu = <&CPU3>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+		arm,coresight-loses-context-with-cpu;
+
+		out-ports {
+			port {
+				etm3_out: endpoint {
+					remote-endpoint =
+					  <&apss_funnel_in3>;
+				};
+			};
+		};
+	};
+
+	etm@7440000 {
+		compatible = "arm,coresight-etm4x", "arm,primecell";
+		reg = <0 0x07440000 0 0x1000>;
+
+		cpu = <&CPU4>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+		arm,coresight-loses-context-with-cpu;
+
+		out-ports {
+			port {
+				etm4_out: endpoint {
+					remote-endpoint =
+					  <&apss_funnel_in4>;
+				};
+			};
+		};
+	};
+
+	etm@7540000 {
+		compatible = "arm,coresight-etm4x", "arm,primecell";
+		reg = <0 0x07540000 0 0x1000>;
+
+		cpu = <&CPU5>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+		arm,coresight-loses-context-with-cpu;
+
+		out-ports {
+			port {
+				etm5_out: endpoint {
+					remote-endpoint =
+					  <&apss_funnel_in5>;
+				};
+			};
+		};
+	};
+
+	etm@7640000 {
+		compatible = "arm,coresight-etm4x", "arm,primecell";
+		reg = <0 0x07640000 0 0x1000>;
+
+		cpu = <&CPU6>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+		arm,coresight-loses-context-with-cpu;
+
+		out-ports {
+			port {
+				etm6_out: endpoint {
+					remote-endpoint =
+					  <&apss_funnel_in6>;
+				};
+			};
+		};
+	};
+
+	etm@7740000 {
+		compatible = "arm,coresight-etm4x", "arm,primecell";
+		reg = <0 0x07740000 0 0x1000>;
+
+		cpu = <&CPU7>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+		arm,coresight-loses-context-with-cpu;
+
+		out-ports {
+			port {
+				etm7_out: endpoint {
+					remote-endpoint =
+					  <&apss_funnel_in7>;
+				};
+			};
+		};
+	};
+
+	funnel@7800000 {
+		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
+		reg = <0 0x07800000 0 0x1000>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+
+		out-ports {
+			port {
+				apss_funnel_out: endpoint {
+					remote-endpoint =
+					  <&apss_merge_funnel_in>;
+				};
+			};
+		};
+
+		in-ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				apss_funnel_in0: endpoint {
+					remote-endpoint =
+					  <&etm0_out>;
+				};
+			};
+
+			port@1 {
+				reg = <1>;
+				apss_funnel_in1: endpoint {
+					remote-endpoint =
+					  <&etm1_out>;
+				};
+			};
+
+			port@2 {
+				reg = <2>;
+				apss_funnel_in2: endpoint {
+					remote-endpoint =
+					  <&etm2_out>;
+				};
+			};
+
+			port@3 {
+				reg = <3>;
+				apss_funnel_in3: endpoint {
+					remote-endpoint =
+					  <&etm3_out>;
+				};
+			};
+
+			port@4 {
+				reg = <4>;
+				apss_funnel_in4: endpoint {
+					remote-endpoint =
+					  <&etm4_out>;
+				};
+			};
+
+			port@5 {
+				reg = <5>;
+				apss_funnel_in5: endpoint {
+					remote-endpoint =
+					  <&etm5_out>;
+				};
+			};
+
+			port@6 {
+				reg = <6>;
+				apss_funnel_in6: endpoint {
+					remote-endpoint =
+					  <&etm6_out>;
+				};
+			};
+
+			port@7 {
+				reg = <7>;
+				apss_funnel_in7: endpoint {
+					remote-endpoint =
+					  <&etm7_out>;
+				};
+			};
+		};
+	};
+
+	funnel@7810000 {
+		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
+		reg = <0 0x07810000 0 0x1000>;
+
+		clocks = <&aoss_qmp>;
+		clock-names = "apb_pclk";
+
+		out-ports {
+			port {
+				apss_merge_funnel_out: endpoint {
+					remote-endpoint =
+					  <&funnel2_in5>;
+				};
+			};
+		};
+
+		in-ports {
+			port {
+				apss_merge_funnel_in: endpoint {
+					remote-endpoint =
+					  <&apss_funnel_out>;
+				};
+			};
+		};
+	};
 };
 
 &adsp {
-- 
2.17.1


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

* Re: [PATCH 00/10] Add support for TPDM and TPDA
  2021-10-21  7:38 [PATCH 00/10] Add support for TPDM and TPDA Tao Zhang
                   ` (9 preceding siblings ...)
  2021-10-21  7:38 ` [PATCH 10/10] ARM: dts: msm: Add TPDA and TPDM support to DTS for RB5 Tao Zhang
@ 2021-10-28 17:16 ` Mathieu Poirier
  2021-10-29 15:11   ` Tao Zhang
  10 siblings, 1 reply; 37+ messages in thread
From: Mathieu Poirier @ 2021-10-28 17:16 UTC (permalink / raw)
  To: Tao Zhang
  Cc: Suzuki K Poulose, Alexander Shishkin, Mike Leach, Leo Yan,
	Greg Kroah-Hartman, coresight, linux-arm-kernel, linux-kernel,
	Tingwei Zhang, Mao Jinlong, Yuanfang Zhang, Trilok Soni

I have started to review this set.  It is substantial in size and as such will
take me several days, most likely weeks.  

Did you run checkpatch before sending this set out?  If so, any reason why there
are still errors?

Thanks,
Mathieu

On Thu, Oct 21, 2021 at 03:38:46PM +0800, Tao Zhang wrote:
> This series adds support for the trace performance monitoring and
> diagnostics hardware (TPDM and TPDA). It is composed of two major
> elements.
> a) Changes for original coresight framework to support for TPDM and TPDA.
> b) Add driver code for TPDM and TPDA.
> 
> Introduction of changes for original coresight framework
> a) Support TPDM as new coresight source.
> Since only STM and ETM are supported as coresight source originally.
> TPDM is a newly added coresight source. We need to change
> the original way of saving coresight path to support more types source
> for coresight driver.
> The following patch is to add support more coresight sources.
> coresight: add support to enable more coresight paths
> 
> b) To support multi-port input and multi-port output for funnels
> In some cases, different TPDM hardware will be connected to the same
> funnel, but eventually they need to be linked to different TPDAs or
> funnels. This requires funnel to support multi-port input and multi-port
> output, and can specify which input port corresponds to which output port.
> Use property ?label? in the funnel?s configuration to point out from
> which input port to which output port.
> The following patch is to support multi-port input and multi-port output
> for funnels.
> coresight: funnel: add support for multiple output ports
> 
> Introduction of TPDM and TPDA
> TPDM - The trace performance monitoring and diagnostics monitor or TPDM in
> short serves as data collection component for various dataset types
> specified in the QPMDA(Qualcomm performance monitoring and diagnostics
> architecture) spec. The primary use case of the TPDM is to collect data
> from different data sources and send it to a TPDA for packetization,
> timestamping and funneling.
> The following patch is to add driver for TPDM.
> Coresight: Add driver to support Coresight device TPDM
> Coresight: Enable BC and GPR for TPDM driver
> Coresight: Add interface for TPDM BC subunit
> Coresight: Enable and add interface for TPDM TC subunit
> Coresight: Enable DSB subunit for TPDM
> Coresight: Enable CMB subunit for TPDM
> 
> TPDA - The trace performance monitoring and diagnostics aggregator or
> TPDA in short serves as an arbitration and packetization engine for the
> performance monitoring and diagnostics network as specified in the QPMDA
> (Qualcomm performance monitoring and diagnostics architecture)
> specification. The primary use case of the TPDA is to provide
> packetization, funneling and timestamping of Monitor data as specified
> in the QPMDA specification.
> The following patch is to add driver for TPDA.
> coresight: Add driver to support Coresight device TPDA
> 
> The last patch of this series is a device tree modification, which add
> the TPDM and TPDA configuration to device tree for validating.
> ARM: dts: msm: Add TPDA and TPDM configuration to device
> 
> Once this series patches are applied properly, the tpdm and tpda nodes
> should be observed at the coresight path /sys/bus/coresight/devices
> e.g.
> /sys/bus/coresight/devices # ls -l | grep tpd
> tpda0 -> ../../../devices/platform/soc@0/6004000.tpda/tpda0
> tpdm0 -> ../../../devices/platform/soc@0/6844000.lpass.tpdm/tpdm0
> 
> We can use the commands are similar to the below to validate TPDMs.
> Enable coresight sink first.
> echo 1 > /sys/bus/coresight/devices/tpdm0/enable_source
> echo 1 > /sys/bus/coresight/devices/tpdm0/integration_test
> echo 2 > /sys/bus/coresight/devices/tpdm0/integration_test
> The test data will be collected in the coresight sink which is enabled.
> 
> This series applies to coresight/next
> https://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux.git?h=next
> 
> Tao Zhang (10):
>   coresight: add support to enable more coresight paths
>   coresight: funnel: add support for multiple output ports
>   Coresight: Add driver to support Coresight device TPDM
>   Coresight: Enable BC and GPR for TPDM driver
>   Coresight: Add interface for TPDM BC subunit
>   Coresight: Enable and add interface for TPDM TC subunit
>   Coresight: Enable DSB subunit for TPDM
>   Coresight: Enable CMB subunit for TPDM
>   coresight: Add driver to support Coresight device TPDA
>   ARM: dts: msm: Add TPDA and TPDM support to DTS for RB5
> 
>  .../bindings/arm/coresight-tpda.yaml          |  169 +
>  .../bindings/arm/coresight-tpdm.yaml          |   86 +
>  MAINTAINERS                                   |    6 +
>  arch/arm64/boot/dts/qcom/qrb5165-rb5.dts      |  439 ++
>  drivers/hwtracing/coresight/Kconfig           |   18 +
>  drivers/hwtracing/coresight/Makefile          |    2 +
>  drivers/hwtracing/coresight/coresight-core.c  |  175 +-
>  .../hwtracing/coresight/coresight-platform.c  |    8 +
>  drivers/hwtracing/coresight/coresight-tpda.c  |  828 ++++
>  drivers/hwtracing/coresight/coresight-tpdm.c  | 4253 +++++++++++++++++
>  include/linux/coresight.h                     |    2 +
>  11 files changed, 5928 insertions(+), 58 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/arm/coresight-tpda.yaml
>  create mode 100644 Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
>  create mode 100644 drivers/hwtracing/coresight/coresight-tpda.c
>  create mode 100644 drivers/hwtracing/coresight/coresight-tpdm.c
> 
> -- 
> 2.17.1
> 

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

* Re: [PATCH 01/10] coresight: add support to enable more coresight paths
  2021-10-21  7:38 ` [PATCH 01/10] coresight: add support to enable more coresight paths Tao Zhang
@ 2021-10-28 18:06   ` Mathieu Poirier
  2021-11-22 15:12     ` Jinlong Mao
  0 siblings, 1 reply; 37+ messages in thread
From: Mathieu Poirier @ 2021-10-28 18:06 UTC (permalink / raw)
  To: Tao Zhang
  Cc: Suzuki K Poulose, Alexander Shishkin, Mike Leach, Leo Yan,
	Greg Kroah-Hartman, coresight, linux-arm-kernel, linux-kernel,
	Tingwei Zhang, Mao Jinlong, Yuanfang Zhang, Trilok Soni,
	Tingwei Zhang

On Thu, Oct 21, 2021 at 03:38:47PM +0800, Tao Zhang wrote:
> Current coresight implementation only supports enabling source
> ETMs or STM. This patch adds support to enable more kinds of
> coresight source to sink paths. We build a path from source to
> sink when any source is enabled and store it in a list. When the
> source is disabled, we fetch the corresponding path from the list
> and decrement the refcount on each device in the path. The device
> is disabled if the refcount reaches zero. Don't store path to
> coresight data structure of source to avoid unnecessary change to
> ABI.
> Since some targets may have coresight sources other than STM and
> ETMs, we need to add this change to support these coresight
> devices.
> 
> Signed-off-by: Tingwei Zhang <tingwei@codeaurora.org>
> Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> ---
>  drivers/hwtracing/coresight/coresight-core.c | 100 +++++++++++--------
>  1 file changed, 56 insertions(+), 44 deletions(-)
> 
> diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
> index 8a18c71df37a..1e621d61307a 100644
> --- a/drivers/hwtracing/coresight/coresight-core.c
> +++ b/drivers/hwtracing/coresight/coresight-core.c
> @@ -37,18 +37,16 @@ struct coresight_node {
>  };
>  
>  /*
> - * When operating Coresight drivers from the sysFS interface, only a single
> - * path can exist from a tracer (associated to a CPU) to a sink.
> + * struct coresight_path - path from source to sink
> + * @path:	Address of path list.
> + * @link:	hook to the list.
>   */
> -static DEFINE_PER_CPU(struct list_head *, tracer_path);
> +struct coresight_path {
> +	struct list_head *path;
> +	struct list_head link;
> +};

For sources associated with a CPU, like ETMs, having a per-cpu way of storing
paths is a definite advantage and should be kept that way.  

>  
> -/*
> - * As of this writing only a single STM can be found in CS topologies.  Since
> - * there is no way to know if we'll ever see more and what kind of
> - * configuration they will enact, for the time being only define a single path
> - * for STM.
> - */
> -static struct list_head *stm_path;
> +static LIST_HEAD(cs_active_paths);

Then there are sources that aren't associated with a CPU like STMs and TPDMs.
Perhaps using an IDR or the hash of the device name as a key to a hashing
vector would be better than doing a sequential search, especially as the
list of devices is bound to increase over time.

>  
>  /*
>   * When losing synchronisation a new barrier packet needs to be inserted at the
> @@ -354,6 +352,7 @@ static void coresight_disable_sink(struct coresight_device *csdev)
>  	if (ret)
>  		return;
>  	coresight_control_assoc_ectdev(csdev, false);
> +	csdev->activated = false;

I don't see why this is needed and without proper documentation there is no way
for me to guess the logic behind the change.  The ->activated flag should be
manipulated from the command line interface only.

>  	csdev->enable = false;
>  }
>  
> @@ -590,6 +589,20 @@ int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data)
>  	goto out;
>  }
>  
> +static struct coresight_device *coresight_get_source(struct list_head *path)
> +{
> +	struct coresight_device *csdev;
> +
> +	if (!path)
> +		return NULL;
> +
> +	csdev = list_first_entry(path, struct coresight_node, link)->csdev;
> +	if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE)
> +		return NULL;
> +
> +	return csdev;
> +}
> +
>  struct coresight_device *coresight_get_sink(struct list_head *path)
>  {
>  	struct coresight_device *csdev;
> @@ -1086,9 +1099,23 @@ static int coresight_validate_source(struct coresight_device *csdev,
>  	return 0;
>  }
>  
> +static int coresight_store_path(struct list_head *path)
> +{
> +	struct coresight_path *node;
> +
> +	node = kzalloc(sizeof(struct coresight_path), GFP_KERNEL);
> +	if (!node)
> +		return -ENOMEM;
> +
> +	node->path = path;
> +	list_add(&node->link, &cs_active_paths);
> +
> +	return 0;
> +}
> +
>  int coresight_enable(struct coresight_device *csdev)
>  {
> -	int cpu, ret = 0;
> +	int ret = 0;
>  	struct coresight_device *sink;
>  	struct list_head *path;
>  	enum coresight_dev_subtype_source subtype;
> @@ -1133,25 +1160,9 @@ int coresight_enable(struct coresight_device *csdev)
>  	if (ret)
>  		goto err_source;
>  
> -	switch (subtype) {
> -	case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
> -		/*
> -		 * When working from sysFS it is important to keep track
> -		 * of the paths that were created so that they can be
> -		 * undone in 'coresight_disable()'.  Since there can only
> -		 * be a single session per tracer (when working from sysFS)
> -		 * a per-cpu variable will do just fine.
> -		 */
> -		cpu = source_ops(csdev)->cpu_id(csdev);
> -		per_cpu(tracer_path, cpu) = path;
> -		break;
> -	case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
> -		stm_path = path;
> -		break;
> -	default:
> -		/* We can't be here */
> -		break;
> -	}
> +	ret = coresight_store_path(path);
> +	if (ret)
> +		goto err_source;
>  
>  out:
>  	mutex_unlock(&coresight_mutex);
> @@ -1168,8 +1179,11 @@ EXPORT_SYMBOL_GPL(coresight_enable);
>  
>  void coresight_disable(struct coresight_device *csdev)
>  {
> -	int cpu, ret;
> +	int  ret;
>  	struct list_head *path = NULL;
> +	struct coresight_path *cspath = NULL;
> +	struct coresight_path *cspath_next = NULL;
> +	struct coresight_device *src_csdev = NULL;
>  
>  	mutex_lock(&coresight_mutex);
>  
> @@ -1180,20 +1194,18 @@ void coresight_disable(struct coresight_device *csdev)
>  	if (!csdev->enable || !coresight_disable_source(csdev))
>  		goto out;
>  
> -	switch (csdev->subtype.source_subtype) {
> -	case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
> -		cpu = source_ops(csdev)->cpu_id(csdev);
> -		path = per_cpu(tracer_path, cpu);
> -		per_cpu(tracer_path, cpu) = NULL;
> -		break;
> -	case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
> -		path = stm_path;
> -		stm_path = NULL;
> -		break;
> -	default:
> -		/* We can't be here */
> -		break;
> +	list_for_each_entry_safe(cspath, cspath_next, &cs_active_paths, link) {
> +		src_csdev = coresight_get_source(cspath->path);
> +		if (!src_csdev)
> +			continue;
> +		if (src_csdev == csdev) {
> +			path = cspath->path;
> +			list_del(&cspath->link);
> +			kfree(cspath);

See my comment above - I agree that sources _not_ associated with a CPU should
be handled differently.  CPU bound sources should be kept untouched.

That is all the time I had for today, I will continue tomorrow.

Thanks,
Mathieu

> +		}
>  	}
> +	if (path == NULL)
> +		goto out;
>  
>  	coresight_disable_path(path);
>  	coresight_release_path(path);
> -- 
> 2.17.1
> 

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

* Re: [PATCH 00/10] Add support for TPDM and TPDA
  2021-10-28 17:16 ` [PATCH 00/10] Add support for TPDM and TPDA Mathieu Poirier
@ 2021-10-29 15:11   ` Tao Zhang
  0 siblings, 0 replies; 37+ messages in thread
From: Tao Zhang @ 2021-10-29 15:11 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: Suzuki K Poulose, Alexander Shishkin, Mike Leach, Leo Yan,
	Greg Kroah-Hartman, coresight, linux-arm-kernel, linux-kernel,
	Tingwei Zhang, Mao Jinlong, Yuanfang Zhang, Trilok Soni

On Thu, Oct 28, 2021 at 11:16:15AM -0600, Mathieu Poirier wrote:
> I have started to review this set.  It is substantial in size and as such will
> take me several days, most likely weeks.  
> 
> Did you run checkpatch before sending this set out?  If so, any reason why there
> are still errors?
> 
> Thanks,
> Mathieu
> 
> On Thu, Oct 21, 2021 at 03:38:46PM +0800, Tao Zhang wrote:
Hi Mathieu,

Sorry, Mathieu, I didn't run checkpatch script after adding DT binding docs to the
patches. We will check the errors and fix them in the next version.

Thanks,
Tao
> > This series adds support for the trace performance monitoring and
> > diagnostics hardware (TPDM and TPDA). It is composed of two major
> > elements.
> > a) Changes for original coresight framework to support for TPDM and TPDA.
> > b) Add driver code for TPDM and TPDA.
> > 
> > Introduction of changes for original coresight framework
> > a) Support TPDM as new coresight source.
> > Since only STM and ETM are supported as coresight source originally.
> > TPDM is a newly added coresight source. We need to change
> > the original way of saving coresight path to support more types source
> > for coresight driver.
> > The following patch is to add support more coresight sources.
> > coresight: add support to enable more coresight paths
> > 
> > b) To support multi-port input and multi-port output for funnels
> > In some cases, different TPDM hardware will be connected to the same
> > funnel, but eventually they need to be linked to different TPDAs or
> > funnels. This requires funnel to support multi-port input and multi-port
> > output, and can specify which input port corresponds to which output port.
> > Use property ?label? in the funnel?s configuration to point out from
> > which input port to which output port.
> > The following patch is to support multi-port input and multi-port output
> > for funnels.
> > coresight: funnel: add support for multiple output ports
> > 
> > Introduction of TPDM and TPDA
> > TPDM - The trace performance monitoring and diagnostics monitor or TPDM in
> > short serves as data collection component for various dataset types
> > specified in the QPMDA(Qualcomm performance monitoring and diagnostics
> > architecture) spec. The primary use case of the TPDM is to collect data
> > from different data sources and send it to a TPDA for packetization,
> > timestamping and funneling.
> > The following patch is to add driver for TPDM.
> > Coresight: Add driver to support Coresight device TPDM
> > Coresight: Enable BC and GPR for TPDM driver
> > Coresight: Add interface for TPDM BC subunit
> > Coresight: Enable and add interface for TPDM TC subunit
> > Coresight: Enable DSB subunit for TPDM
> > Coresight: Enable CMB subunit for TPDM
> > 
> > TPDA - The trace performance monitoring and diagnostics aggregator or
> > TPDA in short serves as an arbitration and packetization engine for the
> > performance monitoring and diagnostics network as specified in the QPMDA
> > (Qualcomm performance monitoring and diagnostics architecture)
> > specification. The primary use case of the TPDA is to provide
> > packetization, funneling and timestamping of Monitor data as specified
> > in the QPMDA specification.
> > The following patch is to add driver for TPDA.
> > coresight: Add driver to support Coresight device TPDA
> > 
> > The last patch of this series is a device tree modification, which add
> > the TPDM and TPDA configuration to device tree for validating.
> > ARM: dts: msm: Add TPDA and TPDM configuration to device
> > 
> > Once this series patches are applied properly, the tpdm and tpda nodes
> > should be observed at the coresight path /sys/bus/coresight/devices
> > e.g.
> > /sys/bus/coresight/devices # ls -l | grep tpd
> > tpda0 -> ../../../devices/platform/soc@0/6004000.tpda/tpda0
> > tpdm0 -> ../../../devices/platform/soc@0/6844000.lpass.tpdm/tpdm0
> > 
> > We can use the commands are similar to the below to validate TPDMs.
> > Enable coresight sink first.
> > echo 1 > /sys/bus/coresight/devices/tpdm0/enable_source
> > echo 1 > /sys/bus/coresight/devices/tpdm0/integration_test
> > echo 2 > /sys/bus/coresight/devices/tpdm0/integration_test
> > The test data will be collected in the coresight sink which is enabled.
> > 
> > This series applies to coresight/next
> > https://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux.git?h=next
> > 
> > Tao Zhang (10):
> >   coresight: add support to enable more coresight paths
> >   coresight: funnel: add support for multiple output ports
> >   Coresight: Add driver to support Coresight device TPDM
> >   Coresight: Enable BC and GPR for TPDM driver
> >   Coresight: Add interface for TPDM BC subunit
> >   Coresight: Enable and add interface for TPDM TC subunit
> >   Coresight: Enable DSB subunit for TPDM
> >   Coresight: Enable CMB subunit for TPDM
> >   coresight: Add driver to support Coresight device TPDA
> >   ARM: dts: msm: Add TPDA and TPDM support to DTS for RB5
> > 
> >  .../bindings/arm/coresight-tpda.yaml          |  169 +
> >  .../bindings/arm/coresight-tpdm.yaml          |   86 +
> >  MAINTAINERS                                   |    6 +
> >  arch/arm64/boot/dts/qcom/qrb5165-rb5.dts      |  439 ++
> >  drivers/hwtracing/coresight/Kconfig           |   18 +
> >  drivers/hwtracing/coresight/Makefile          |    2 +
> >  drivers/hwtracing/coresight/coresight-core.c  |  175 +-
> >  .../hwtracing/coresight/coresight-platform.c  |    8 +
> >  drivers/hwtracing/coresight/coresight-tpda.c  |  828 ++++
> >  drivers/hwtracing/coresight/coresight-tpdm.c  | 4253 +++++++++++++++++
> >  include/linux/coresight.h                     |    2 +
> >  11 files changed, 5928 insertions(+), 58 deletions(-)
> >  create mode 100644 Documentation/devicetree/bindings/arm/coresight-tpda.yaml
> >  create mode 100644 Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
> >  create mode 100644 drivers/hwtracing/coresight/coresight-tpda.c
> >  create mode 100644 drivers/hwtracing/coresight/coresight-tpdm.c
> > 
> > -- 
> > 2.17.1
> > 

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

* Re: [PATCH 02/10] coresight: funnel: add support for multiple output ports
  2021-10-21  7:38 ` [PATCH 02/10] coresight: funnel: add support for multiple output ports Tao Zhang
@ 2021-10-29 17:48   ` Mathieu Poirier
  0 siblings, 0 replies; 37+ messages in thread
From: Mathieu Poirier @ 2021-10-29 17:48 UTC (permalink / raw)
  To: Tao Zhang
  Cc: Suzuki K Poulose, Alexander Shishkin, Mike Leach, Leo Yan,
	Greg Kroah-Hartman, coresight, linux-arm-kernel, linux-kernel,
	Tingwei Zhang, Mao Jinlong, Yuanfang Zhang, Trilok Soni,
	Tingwei Zhang

Good morning,

On Thu, Oct 21, 2021 at 03:38:48PM +0800, Tao Zhang wrote:
> Funnel devices are now capable of supporting multiple-inputs and
> multiple-outputs configuration with in built hardware filtering
> for TPDM devices. Add software support to this function. Output
> port is selected according to the source of the trace path.

There isn't any modification in this patch that relates to funnel input ports.

> 
> The source of the input port on funnels will be marked in the
> device tree.
> e.g.
> tpdm_XXX: tpdm@xxxxxxx {
>     ... ... ... ...
> };
> 
> funnel_XXX: funnel@xxxxxxx {
>     ... ... ... ...
>     out-ports {
>         ... ... ... ...
>         port@x {
>             ... ... ... ...
>             label = <&tpdm_XXX>; <-- To point out tpdm_XXX should
>         };                           be outputed from port@x. And
>     ... ... ... ...                  this is a hardware static
>     };                               connections. Here needs
>     ... ... ... ...                  to refer to hardware design.
> };

There is no indication as to why the output port of a funnel needs to be
labelled.  On that topic, twice in the past we have been clear on the fact
that labels should be introduced once the initial functionality has been merged.
And yet this patch is centered entirely on that concept...

> 
> Then driver will parse the source name marked in the device tree, and
> save it to the coresight path. When the function needs to know the
> source name, it could get the source name from coresight path parameter.
> Finally, the output port knows which source it corresponds to, and it
> also knows which input port it corresponds to.
> 
> Signed-off-by: Tingwei Zhang <tingwei@codeaurora.org>
> Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> ---
>  drivers/hwtracing/coresight/coresight-core.c  | 75 +++++++++++++++----
>  .../hwtracing/coresight/coresight-platform.c  |  8 ++
>  include/linux/coresight.h                     |  2 +
>  3 files changed, 71 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
> index 1e621d61307a..490c9d8d43f9 100644
> --- a/drivers/hwtracing/coresight/coresight-core.c
> +++ b/drivers/hwtracing/coresight/coresight-core.c
> @@ -56,6 +56,8 @@ static LIST_HEAD(cs_active_paths);
>  const u32 coresight_barrier_pkt[4] = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff};
>  EXPORT_SYMBOL_GPL(coresight_barrier_pkt);
>  
> +static struct coresight_device *coresight_get_source(struct list_head *path);
> +
>  static const struct cti_assoc_op *cti_assoc_ops;
>  
>  void coresight_set_cti_ops(const struct cti_assoc_op *cti_op)
> @@ -121,14 +123,46 @@ static int coresight_source_is_unique(struct coresight_device *csdev)
>  				 csdev, coresight_id_match);
>  }
>  
> +/**
> + * coresight_source_filter - checks whether the connection matches the source
> + * of path if connection is binded to specific source.
> + * @path:	The list of devices
> + * @conn:	The connection of one outport
> + *
> + * Return zero if the connection doesn't have a source binded or source of the
> + * path matches the source binds to connection.
> + */
> +static int coresight_source_filter(struct list_head *path,
> +			struct coresight_connection *conn)
> +{
> +	int ret = 0;
> +	struct coresight_device *source = NULL;
> +
> +	if (conn->source_name == NULL)
> +		return ret;
> +
> +	source = coresight_get_source(path);
> +	if (source == NULL)
> +		return ret;
> +
> +	if (is_of_node(source->dev.fwnode))
> +		return strcmp(conn->source_name,
> +			of_node_full_name(to_of_node(source->dev.fwnode)));
> +
> +	return ret;
> +}
> +
>  static int coresight_find_link_inport(struct coresight_device *csdev,
> -				      struct coresight_device *parent)
> +				      struct coresight_device *parent,
> +				      struct list_head *path)
>  {
>  	int i;
>  	struct coresight_connection *conn;
>  
>  	for (i = 0; i < parent->pdata->nr_outport; i++) {
>  		conn = &parent->pdata->conns[i];
> +		if (coresight_source_filter(path, conn))
> +			continue;
>  		if (conn->child_dev == csdev)
>  			return conn->child_port;
>  	}
> @@ -140,13 +174,16 @@ static int coresight_find_link_inport(struct coresight_device *csdev,
>  }
>  
>  static int coresight_find_link_outport(struct coresight_device *csdev,
> -				       struct coresight_device *child)
> +				       struct coresight_device *child,
> +				       struct list_head *path)
>  {
>  	int i;
>  	struct coresight_connection *conn;
>  
>  	for (i = 0; i < csdev->pdata->nr_outport; i++) {
>  		conn = &csdev->pdata->conns[i];
> +		if (coresight_source_filter(path, conn))
> +			continue;
>  		if (conn->child_dev == child)
>  			return conn->outport;
>  	}
> @@ -358,7 +395,8 @@ static void coresight_disable_sink(struct coresight_device *csdev)
>  
>  static int coresight_enable_link(struct coresight_device *csdev,
>  				 struct coresight_device *parent,
> -				 struct coresight_device *child)
> +				 struct coresight_device *child,
> +				 struct list_head *path)
>  {
>  	int ret = 0;
>  	int link_subtype;
> @@ -367,8 +405,8 @@ static int coresight_enable_link(struct coresight_device *csdev,
>  	if (!parent || !child)
>  		return -EINVAL;
>  
> -	inport = coresight_find_link_inport(csdev, parent);
> -	outport = coresight_find_link_outport(csdev, child);
> +	inport = coresight_find_link_inport(csdev, parent, path);
> +	outport = coresight_find_link_outport(csdev, child, path);
>  	link_subtype = csdev->subtype.link_subtype;
>  
>  	if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG && inport < 0)
> @@ -393,7 +431,8 @@ static int coresight_enable_link(struct coresight_device *csdev,
>  
>  static void coresight_disable_link(struct coresight_device *csdev,
>  				   struct coresight_device *parent,
> -				   struct coresight_device *child)
> +				   struct coresight_device *child,
> +				   struct list_head *path)
>  {
>  	int i, nr_conns;
>  	int link_subtype;
> @@ -402,8 +441,8 @@ static void coresight_disable_link(struct coresight_device *csdev,
>  	if (!parent || !child)
>  		return;
>  
> -	inport = coresight_find_link_inport(csdev, parent);
> -	outport = coresight_find_link_outport(csdev, child);
> +	inport = coresight_find_link_inport(csdev, parent, path);
> +	outport = coresight_find_link_outport(csdev, child, path);
>  	link_subtype = csdev->subtype.link_subtype;
>  
>  	if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) {
> @@ -518,7 +557,7 @@ static void coresight_disable_path_from(struct list_head *path,
>  		case CORESIGHT_DEV_TYPE_LINK:
>  			parent = list_prev_entry(nd, link)->csdev;
>  			child = list_next_entry(nd, link)->csdev;
> -			coresight_disable_link(csdev, parent, child);
> +			coresight_disable_link(csdev, parent, child, path);
>  			break;
>  		default:
>  			break;
> @@ -573,7 +612,7 @@ int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data)
>  		case CORESIGHT_DEV_TYPE_LINK:
>  			parent = list_prev_entry(nd, link)->csdev;
>  			child = list_next_entry(nd, link)->csdev;
> -			ret = coresight_enable_link(csdev, parent, child);
> +			ret = coresight_enable_link(csdev, parent, child, path);
>  			if (ret)
>  				goto err;
>  			break;
> @@ -801,7 +840,8 @@ static void coresight_drop_device(struct coresight_device *csdev)
>   */
>  static int _coresight_build_path(struct coresight_device *csdev,
>  				 struct coresight_device *sink,
> -				 struct list_head *path)
> +				 struct list_head *path,
> +				 struct coresight_device *source)
>  {
>  	int i, ret;
>  	bool found = false;
> @@ -813,7 +853,7 @@ static int _coresight_build_path(struct coresight_device *csdev,
>  
>  	if (coresight_is_percpu_source(csdev) && coresight_is_percpu_sink(sink) &&
>  	    sink == per_cpu(csdev_sink, source_ops(csdev)->cpu_id(csdev))) {
> -		if (_coresight_build_path(sink, sink, path) == 0) {
> +		if (_coresight_build_path(sink, sink, path, source) == 0) {
>  			found = true;
>  			goto out;
>  		}
> @@ -824,8 +864,15 @@ static int _coresight_build_path(struct coresight_device *csdev,
>  		struct coresight_device *child_dev;
>  
>  		child_dev = csdev->pdata->conns[i].child_dev;
> +
> +		if (csdev->pdata->conns[i].source_name &&
> +			is_of_node(source->dev.fwnode) &&
> +		    strcmp(csdev->pdata->conns[i].source_name,
> +				of_node_full_name(to_of_node(source->dev.fwnode))))
> +			continue;
> +

All this seems to be doing is optimize the search graph by not taking the paths
that are not labelled.  As far as I can tell the sink would be found regardless
of this enhancement.  We can think about optimizations for building paths when
it is demonstrated that bottlenecks originate from that part of the code.

>  		if (child_dev &&
> -		    _coresight_build_path(child_dev, sink, path) == 0) {
> +		    _coresight_build_path(child_dev, sink, path, source) == 0) {
>  			found = true;
>  			break;
>  		}
> @@ -870,7 +917,7 @@ struct list_head *coresight_build_path(struct coresight_device *source,
>  
>  	INIT_LIST_HEAD(path);
>  
> -	rc = _coresight_build_path(source, sink, path);
> +	rc = _coresight_build_path(source, sink, path, source);
>  	if (rc) {
>  		kfree(path);
>  		return ERR_PTR(rc);
> diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c
> index c594f45319fc..60c43ab149a5 100644
> --- a/drivers/hwtracing/coresight/coresight-platform.c
> +++ b/drivers/hwtracing/coresight/coresight-platform.c
> @@ -222,6 +222,7 @@ static int of_coresight_parse_endpoint(struct device *dev,
>  	struct of_endpoint endpoint, rendpoint;
>  	struct device_node *rparent = NULL;
>  	struct device_node *rep = NULL;
> +	struct device_node *sn = NULL;
>  	struct device *rdev = NULL;
>  	struct fwnode_handle *rdev_fwnode;
>  	struct coresight_connection *conn;
> @@ -269,6 +270,13 @@ static int of_coresight_parse_endpoint(struct device *dev,
>  		 */
>  		conn->child_fwnode = fwnode_handle_get(rdev_fwnode);
>  		conn->child_port = rendpoint.port;
> +		conn->source_name = NULL;
> +		sn = of_parse_phandle(ep, "label", 0);
> +		if (sn) {
> +			conn->source_name = sn->full_name;
> +			of_node_put(sn);
> +		}

See my comments above on this.  Moreover neither of the yaml files for both
TPDM and TPDA nor the DTS submitted with this patchset account for a "label",
making me very confused about the relevance of this patch.

More comments to come on Monday.

Thanks,
Mathieu

> +
>  		/* Connection record updated */
>  	} while (0);
>  
> diff --git a/include/linux/coresight.h b/include/linux/coresight.h
> index 93a2922b7653..7065b60a767b 100644
> --- a/include/linux/coresight.h
> +++ b/include/linux/coresight.h
> @@ -170,6 +170,7 @@ struct coresight_desc {
>   * struct coresight_connection - representation of a single connection
>   * @outport:	a connection's output port number.
>   * @child_port:	remote component's port number @output is connected to.
> + * @source_name:source component's name.
>   * @chid_fwnode: remote component's fwnode handle.
>   * @child_dev:	a @coresight_device representation of the component
>  		connected to @outport.
> @@ -178,6 +179,7 @@ struct coresight_desc {
>  struct coresight_connection {
>  	int outport;
>  	int child_port;
> +	const char *source_name;
>  	struct fwnode_handle *child_fwnode;
>  	struct coresight_device *child_dev;
>  	struct coresight_sysfs_link *link;
> -- 
> 2.17.1
> 

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

* Re: [PATCH 03/10] Coresight: Add driver to support Coresight device TPDM
  2021-10-21  7:38 ` [PATCH 03/10] Coresight: Add driver to support Coresight device TPDM Tao Zhang
@ 2021-11-02 17:59   ` Mathieu Poirier
  2021-11-04  8:56     ` Jinlong
  2021-11-04  9:37     ` Suzuki K Poulose
  0 siblings, 2 replies; 37+ messages in thread
From: Mathieu Poirier @ 2021-11-02 17:59 UTC (permalink / raw)
  To: Tao Zhang
  Cc: Suzuki K Poulose, Alexander Shishkin, Mike Leach, Leo Yan,
	Greg Kroah-Hartman, coresight, linux-arm-kernel, linux-kernel,
	Tingwei Zhang, Mao Jinlong, Yuanfang Zhang, Trilok Soni

Good morning,


On Thu, Oct 21, 2021 at 03:38:49PM +0800, Tao Zhang wrote:
> Add driver to support Coresight device TPDM. This driver provides
> support for configuring monitor. Monitors are primarily responsible
> for data set collection and support the ability to collect any
> permutation of data set types. Monitors are also responsible for
> interaction with system cross triggering.

As far as I can tell there is nothing related to CTIs in this patch.  And if
there is, it is not documented.

> 
> Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> ---
>  .../bindings/arm/coresight-tpdm.yaml          |  86 +++

As checkpatch says, this should be in a separate file.

>  MAINTAINERS                                   |   5 +

Since this is a coresight device Suzuki and I will continue the maintenance.
The get_maintainer script will make sure you care CC'ed on patches related to
the TPDM/TPDA drivers, and we would typically requried a "Reviewed-by" tag from
you before merging.

>  drivers/hwtracing/coresight/Kconfig           |   9 +
>  drivers/hwtracing/coresight/Makefile          |   1 +
>  drivers/hwtracing/coresight/coresight-tpdm.c  | 583 ++++++++++++++++++
>  5 files changed, 684 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
>  create mode 100644 drivers/hwtracing/coresight/coresight-tpdm.c
> 
> diff --git a/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml b/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
> new file mode 100644
> index 000000000000..44541075d77f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
> @@ -0,0 +1,86 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/arm/coresight-tpdm.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Trace, Profiling and Diagnostics Monitor - TPDM
> +
> +description: |
> +  The TPDM or Monitor serves as data collection component for various dataset
> +  types specified in the QPMDA spec. It covers Basic Counts (BC), Tenure
> +  Counts (TC), Continuous Multi-Bit (CMB), and Discrete Single Bit (DSB). It
> +  performs data collection in the data producing clock domain and transfers it
> +  to the data collection time domain, generally ATB clock domain.
> +
> +  The primary use case of the TPDM is to collect data from different data
> +  sources and send it to a TPDA for packetization, timestamping, and funneling.
> +
> +maintainers:
> +  - Tao Zhang <quic_taozha@quicinc.com>
> +
> +properties:
> +  $nodename:
> +    pattern: "^tpdm(@[0-9a-f]+)$"
> +  compatible:
> +    items:
> +      - const: arm,primecell
> +
> +  reg:
> +    maxItems: 1
> +
> +  reg-names:
> +    items:
> +      - const: tpdm-base
> +
> +  atid:
> +    maxItems: 1
> +    description: |
> +      The QPMDA specification repurposed the ATID field of the AMBA ATB
> +      specification to use it to convey packetization information to the
> +      Aggregator.
> +
> +  out-ports:
> +    description: |
> +      Output connections from the TPDM to legacy CoreSight trace bus.
> +    $ref: /schemas/graph.yaml#/properties/ports
> +    properties:
> +      port:
> +        description: Output connection from the TPDM to legacy CoreSight
> +          Trace bus.
> +        $ref: /schemas/graph.yaml#/properties/port
> +
> +required:
> +  - compatible
> +  - reg
> +  - reg-names
> +  - atid
> +  - clocks
> +  - clock-names
> +
> +additionalProperties: false
> +
> +examples:
> +  # minimum TPDM definition.
> +  - |
> +    tpdm@6980000 {
> +      compatible = "arm,primecell";
> +      reg = <0x6980000 0x1000>;
> +      reg-names = "tpdm-base";
> +
> +      clocks = <&aoss_qmp>;
> +      clock-names = "apb_pclk";
> +
> +      atid = <78>;
> +      out-ports {
> +        port {
> +          tpdm_turing_out_funnel_turing: endpoint {
> +            remote-endpoint =
> +              <&funnel_turing_in_tpdm_turing>;
> +          };
> +        };
> +      };
> +    };
> +
> +...
> diff --git a/MAINTAINERS b/MAINTAINERS
> index eeb4c70b3d5b..cabecf760488 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15303,6 +15303,11 @@ L:	netdev@vger.kernel.org
>  S:	Supported
>  F:	drivers/net/ipa/
>  
> +QCOM CORESIGHT TPDM DRIVER
> +M:	Tao Zhang <quic_taozha@quicinc.com>
> +S:	Maintained
> +F:	drivers/hwtracing/coresight/coresight-tpdm.c
> +
>  QEMU MACHINE EMULATOR AND VIRTUALIZER SUPPORT
>  M:	Gabriel Somlo <somlo@cmu.edu>
>  M:	"Michael S. Tsirkin" <mst@redhat.com>
> diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
> index f026e5c0e777..abe244a968f6 100644
> --- a/drivers/hwtracing/coresight/Kconfig
> +++ b/drivers/hwtracing/coresight/Kconfig
> @@ -188,4 +188,13 @@ config CORESIGHT_TRBE
>  
>  	  To compile this driver as a module, choose M here: the module will be
>  	  called coresight-trbe.
> +
> +config CORESIGHT_TPDM
> +	tristate "CoreSight Trace, Profiling & Diagnostics Monitor driver"
> +	select CORESIGHT_LINKS_AND_SINKS
> +	help
> +	  This driver provides support for configuring monitor. Monitors are
> +	  primarily responsible for data set collection and support the
> +	  ability to collect any permutation of data set types. Monitors are
> +	  also responsible for interaction with system cross triggering.

You also need to indicate what the name of the module would be.  Please look at
other extries in the Kconfig file for examples.

>  endif
> diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
> index b6c4a48140ec..e7392a0dddeb 100644
> --- a/drivers/hwtracing/coresight/Makefile
> +++ b/drivers/hwtracing/coresight/Makefile
> @@ -25,5 +25,6 @@ obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o
>  obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o
>  obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
>  obj-$(CONFIG_CORESIGHT_TRBE) += coresight-trbe.o
> +obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o
>  coresight-cti-y := coresight-cti-core.o	coresight-cti-platform.o \
>  		   coresight-cti-sysfs.o
> diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
> new file mode 100644
> index 000000000000..906776c859d6
> --- /dev/null
> +++ b/drivers/hwtracing/coresight/coresight-tpdm.c
> @@ -0,0 +1,583 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/amba/bus.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/fs.h>
> +#include <linux/bitmap.h>
> +#include <linux/of.h>
> +#include <linux/coresight.h>
> +#include <linux/regulator/consumer.h>

These should be in alphabetical order.

> +
> +#include "coresight-priv.h"
> +
> +#define tpdm_writel(drvdata, val, off)	__raw_writel((val), drvdata->base + off)
> +#define tpdm_readl(drvdata, off)		__raw_readl(drvdata->base + off)

writel/readl_relaxed() are defines for __raw_writel/readl and as such the above
should not be needed.

> +
> +#define TPDM_LOCK(drvdata)						\
> +do {									\
> +	mb(); /* ensure configuration take effect before we lock it */	\
> +	tpdm_writel(drvdata, 0x0, CORESIGHT_LAR);			\
> +} while (0)
> +#define TPDM_UNLOCK(drvdata)						\
> +do {									\
> +	tpdm_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR);		\
> +	mb(); /* ensure unlock take effect before we configure */	\
> +} while (0)

That too isn't needed.  Simply use CS_LOCK/UNLOCK().

> +
> +/* GPR Registers */
> +#define TPDM_GPR_CR(n)		(0x0 + (n * 4))
> +
> +/* BC Subunit Registers */
> +#define TPDM_BC_CR		(0x280)
> +#define TPDM_BC_SATROLL		(0x284)
> +#define TPDM_BC_CNTENSET	(0x288)
> +#define TPDM_BC_CNTENCLR	(0x28C)
> +#define TPDM_BC_INTENSET	(0x290)
> +#define TPDM_BC_INTENCLR	(0x294)
> +#define TPDM_BC_TRIG_LO(n)	(0x298 + (n * 4))
> +#define TPDM_BC_TRIG_HI(n)	(0x318 + (n * 4))
> +#define TPDM_BC_GANG		(0x398)
> +#define TPDM_BC_OVERFLOW(n)	(0x39C + (n * 4))
> +#define TPDM_BC_OVSR		(0x3C0)
> +#define TPDM_BC_SELR		(0x3C4)
> +#define TPDM_BC_CNTR_LO		(0x3C8)
> +#define TPDM_BC_CNTR_HI		(0x3CC)
> +#define TPDM_BC_SHADOW_LO(n)	(0x3D0 + (n * 4))
> +#define TPDM_BC_SHADOW_HI(n)	(0x450 + (n * 4))
> +#define TPDM_BC_SWINC		(0x4D0)
> +#define TPDM_BC_MSR(n)		(0x4F0 + (n * 4))
> +
> +/* TC Subunit Registers */
> +#define TPDM_TC_CR		(0x500)
> +#define TPDM_TC_CNTENSET	(0x504)
> +#define TPDM_TC_CNTENCLR	(0x508)
> +#define TPDM_TC_INTENSET	(0x50C)
> +#define TPDM_TC_INTENCLR	(0x510)
> +#define TPDM_TC_TRIG_SEL(n)	(0x514 + (n * 4))
> +#define TPDM_TC_TRIG_LO(n)	(0x534 + (n * 4))
> +#define TPDM_TC_TRIG_HI(n)	(0x554 + (n * 4))
> +#define TPDM_TC_OVSR_GP		(0x580)
> +#define TPDM_TC_OVSR_IMPL	(0x584)
> +#define TPDM_TC_SELR		(0x588)
> +#define TPDM_TC_CNTR_LO		(0x58C)
> +#define TPDM_TC_CNTR_HI		(0x590)
> +#define TPDM_TC_SHADOW_LO(n)	(0x594 + (n * 4))
> +#define TPDM_TC_SHADOW_HI(n)	(0x644 + (n * 4))
> +#define TPDM_TC_SWINC		(0x700)
> +#define TPDM_TC_MSR(n)		(0x768 + (n * 4))
> +
> +/* DSB Subunit Registers */
> +#define TPDM_DSB_CR		(0x780)
> +#define TPDM_DSB_TIER		(0x784)
> +#define TPDM_DSB_TPR(n)		(0x788 + (n * 4))
> +#define TPDM_DSB_TPMR(n)	(0x7A8 + (n * 4))
> +#define TPDM_DSB_XPR(n)		(0x7C8 + (n * 4))
> +#define TPDM_DSB_XPMR(n)	(0x7E8 + (n * 4))
> +#define TPDM_DSB_EDCR(n)	(0x808 + (n * 4))
> +#define TPDM_DSB_EDCMR(n)	(0x848 + (n * 4))
> +#define TPDM_DSB_CA_SELECT(n)	(0x86c + (n * 4))
> +#define TPDM_DSB_MSR(n)		(0x980 + (n * 4))
> +
> +/* CMB/MCMB Subunit Registers */
> +#define TPDM_CMB_CR		(0xA00)
> +#define TPDM_CMB_TIER		(0xA04)
> +#define TPDM_CMB_TPR(n)		(0xA08 + (n * 4))
> +#define TPDM_CMB_TPMR(n)	(0xA10 + (n * 4))
> +#define TPDM_CMB_XPR(n)		(0xA18 + (n * 4))
> +#define TPDM_CMB_XPMR(n)	(0xA20 + (n * 4))
> +#define TPDM_CMB_MARKR		(0xA28)
> +#define TPDM_CMB_READCTL	(0xA70)
> +#define TPDM_CMB_READVAL	(0xA74)
> +#define TPDM_CMB_MSR(n)		(0xA80 + (n * 4))
> +
> +/* TPDM Specific Registers */
> +#define TPDM_ITATBCNTRL		(0xEF0)
> +#define TPDM_CLK_CTRL		(0x220)
> +#define TPDM_ITCNTRL		(0xF00)
> +
> +
> +#define TPDM_DATASETS		32
> +#define TPDM_BC_MAX_COUNTERS	32
> +#define TPDM_BC_MAX_OVERFLOW	6
> +#define TPDM_BC_MAX_MSR		4
> +#define TPDM_TC_MAX_COUNTERS	44
> +#define TPDM_TC_MAX_TRIG	8
> +#define TPDM_TC_MAX_MSR		6
> +#define TPDM_DSB_MAX_PATT	8
> +#define TPDM_DSB_MAX_SELECT	8
> +#define TPDM_DSB_MAX_MSR	32
> +#define TPDM_DSB_MAX_EDCR	16
> +#define TPDM_DSB_MAX_LINES	256
> +#define TPDM_CMB_PATT_CMP	2
> +#define TPDM_CMB_MAX_MSR	128
> +#define TPDM_MCMB_MAX_LANES	8
> +
> +/* DSB programming modes */
> +#define TPDM_DSB_MODE_CYCACC(val)	BMVAL(val, 0, 2)
> +#define TPDM_DSB_MODE_PERF		BIT(3)
> +#define TPDM_DSB_MODE_HPBYTESEL(val)	BMVAL(val, 4, 8)
> +#define TPDM_MODE_ALL			(0xFFFFFFF)
> +
> +#define NUM_OF_BITS		32
> +#define TPDM_GPR_REGS_MAX	160
> +
> +#define TPDM_TRACE_ID_START	128
> +
> +#define TPDM_REVISION_A		0
> +#define TPDM_REVISION_B		1
> +
> +#define HW_ENABLE_CHECK_VALUE   0x10
> +
> +

Extra newline

> +#define ATBCNTRL_VAL_32		0xC00F1409
> +#define ATBCNTRL_VAL_64		0xC01F1409
> +

All of the above should be in a header file.

> +
> +enum tpdm_dataset {
> +	TPDM_DS_IMPLDEF,
> +	TPDM_DS_DSB,
> +	TPDM_DS_CMB,
> +	TPDM_DS_TC,
> +	TPDM_DS_BC,
> +	TPDM_DS_GPR,
> +	TPDM_DS_MCMB,
> +};
> +
> +enum tpdm_mode {
> +	TPDM_MODE_ATB,
> +	TPDM_MODE_APB,
> +};
> +
> +enum tpdm_support_type {
> +	TPDM_SUPPORT_TYPE_FULL,
> +	TPDM_SUPPORT_TYPE_PARTIAL,
> +	TPDM_SUPPORT_TYPE_NO,
> +};
> +
> +enum tpdm_cmb_patt_bits {
> +	TPDM_CMB_LSB,
> +	TPDM_CMB_MSB,
> +};

These too.

> +
> +#ifdef CONFIG_CORESIGHT_TPDM_DEFAULT_ENABLE
> +static int boot_enable = 1;
> +#else
> +static int boot_enable;
> +#endif

That isn't the proper way to do this.  Look at how it is done in
coresight-etm4x.c

> +
> +struct gpr_dataset {
> +	DECLARE_BITMAP(gpr_dirty, TPDM_GPR_REGS_MAX);
> +	uint32_t		gp_regs[TPDM_GPR_REGS_MAX];

Shouldn't this be u32?

> +};
> +
> +struct bc_dataset {
> +	enum tpdm_mode		capture_mode;
> +	enum tpdm_mode		retrieval_mode;
> +	uint32_t		sat_mode;
> +	uint32_t		enable_counters;
> +	uint32_t		clear_counters;
> +	uint32_t		enable_irq;
> +	uint32_t		clear_irq;
> +	uint32_t		trig_val_lo[TPDM_BC_MAX_COUNTERS];
> +	uint32_t		trig_val_hi[TPDM_BC_MAX_COUNTERS];
> +	uint32_t		enable_ganging;
> +	uint32_t		overflow_val[TPDM_BC_MAX_OVERFLOW];
> +	uint32_t		msr[TPDM_BC_MAX_MSR];
> +};
> +
> +struct tc_dataset {
> +	enum tpdm_mode		capture_mode;
> +	enum tpdm_mode		retrieval_mode;
> +	bool			sat_mode;
> +	uint32_t		enable_counters;
> +	uint32_t		clear_counters;
> +	uint32_t		enable_irq;
> +	uint32_t		clear_irq;
> +	uint32_t		trig_sel[TPDM_TC_MAX_TRIG];
> +	uint32_t		trig_val_lo[TPDM_TC_MAX_TRIG];
> +	uint32_t		trig_val_hi[TPDM_TC_MAX_TRIG];
> +	uint32_t		msr[TPDM_TC_MAX_MSR];
> +};
> +
> +struct dsb_dataset {
> +	uint32_t		mode;
> +	uint32_t		edge_ctrl[TPDM_DSB_MAX_EDCR];
> +	uint32_t		edge_ctrl_mask[TPDM_DSB_MAX_EDCR / 2];
> +	uint32_t		patt_val[TPDM_DSB_MAX_PATT];
> +	uint32_t		patt_mask[TPDM_DSB_MAX_PATT];
> +	bool			patt_ts;
> +	bool			patt_type;
> +	uint32_t		trig_patt_val[TPDM_DSB_MAX_PATT];
> +	uint32_t		trig_patt_mask[TPDM_DSB_MAX_PATT];
> +	bool			trig_ts;
> +	bool			trig_type;
> +	uint32_t		select_val[TPDM_DSB_MAX_SELECT];
> +	uint32_t		msr[TPDM_DSB_MAX_MSR];
> +};
> +
> +struct mcmb_dataset {
> +	uint8_t		mcmb_trig_lane;
> +	uint8_t		mcmb_lane_select;
> +};
> +
> +struct cmb_dataset {
> +	bool			trace_mode;
> +	uint32_t		cycle_acc;
> +	uint32_t		patt_val[TPDM_CMB_PATT_CMP];
> +	uint32_t		patt_mask[TPDM_CMB_PATT_CMP];
> +	bool			patt_ts;
> +	uint32_t		trig_patt_val[TPDM_CMB_PATT_CMP];
> +	uint32_t		trig_patt_mask[TPDM_CMB_PATT_CMP];
> +	bool			trig_ts;
> +	bool			ts_all;
> +	uint32_t		msr[TPDM_CMB_MAX_MSR];
> +	uint8_t			read_ctl_reg;
> +	struct mcmb_dataset	*mcmb;
> +};
> +
> +DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm");
> +
> +struct tpdm_drvdata {
> +	void __iomem		*base;
> +	struct device		*dev;
> +	struct coresight_device	*csdev;
> +	int			nr_tclk;
> +	struct clk		**tclk;
> +	int			nr_treg;
> +	struct regulator	**treg;
> +	struct mutex		lock;
> +	bool			enable;
> +	bool			clk_enable;
> +	DECLARE_BITMAP(datasets, TPDM_DATASETS);
> +	DECLARE_BITMAP(enable_ds, TPDM_DATASETS);
> +	enum tpdm_support_type	tc_trig_type;
> +	enum tpdm_support_type	bc_trig_type;
> +	enum tpdm_support_type	bc_gang_type;
> +	uint32_t		bc_counters_avail;
> +	uint32_t		tc_counters_avail;
> +	struct gpr_dataset	*gpr;
> +	struct bc_dataset	*bc;
> +	struct tc_dataset	*tc;
> +	struct dsb_dataset	*dsb;
> +	struct cmb_dataset	*cmb;
> +	int			traceid;
> +	uint32_t		version;
> +	bool			msr_support;
> +	bool			msr_fix_req;
> +	bool			cmb_msr_skip;
> +};

All of these should also be in a header file and properly documented.

> +
> +static void tpdm_init_default_data(struct tpdm_drvdata *drvdata);

This isn't needed.

> +
> +static void __tpdm_enable(struct tpdm_drvdata *drvdata)
> +{
> +	TPDM_UNLOCK(drvdata);
> +
> +	if (drvdata->clk_enable)
> +		tpdm_writel(drvdata, 0x1, TPDM_CLK_CTRL);
> +
> +	TPDM_LOCK(drvdata);
> +}
> +
> +static int tpdm_enable(struct coresight_device *csdev,
> +		       struct perf_event *event, u32 mode)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> +	int ret = 0;
> +
> +	if (drvdata->enable) {

This a race condition.

> +		dev_err(drvdata->dev,
> +			"TPDM setup already enabled,Skipping enablei\n");

Please remove this.

> +		return ret;

Shouldn't this be return -EBUSY? 

> +	}
> +
> +	mutex_lock(&drvdata->lock);
> +	__tpdm_enable(drvdata);
> +	drvdata->enable = true;
> +	mutex_unlock(&drvdata->lock);
> +
> +	dev_info(drvdata->dev, "TPDM tracing enabled\n");
> +	return 0;
> +}
> +
> +static void __tpdm_disable(struct tpdm_drvdata *drvdata)
> +{
> +	TPDM_UNLOCK(drvdata);
> +
> +	if (drvdata->clk_enable)
> +		tpdm_writel(drvdata, 0x0, TPDM_CLK_CTRL);
> +
> +	TPDM_LOCK(drvdata);
> +}
> +
> +static void tpdm_disable(struct coresight_device *csdev,
> +			 struct perf_event *event)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> +
> +	if (!drvdata->enable) {
> +		dev_err(drvdata->dev,
> +			"TPDM setup already disabled, Skipping disable\n");
> +		return;
> +	}
> +	mutex_lock(&drvdata->lock);
> +	__tpdm_disable(drvdata);
> +	drvdata->enable = false;
> +	mutex_unlock(&drvdata->lock);

Same comments as above.

> +
> +	dev_info(drvdata->dev, "TPDM tracing disabled\n");
> +}
> +
> +static int tpdm_trace_id(struct coresight_device *csdev)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> +
> +	return drvdata->traceid;
> +}
> +
> +static const struct coresight_ops_source tpdm_source_ops = {
> +	.trace_id	= tpdm_trace_id,
> +	.enable		= tpdm_enable,
> +	.disable	= tpdm_disable,
> +};
> +
> +static const struct coresight_ops tpdm_cs_ops = {
> +	.source_ops	= &tpdm_source_ops,
> +};
> +
> +static int tpdm_datasets_alloc(struct tpdm_drvdata *drvdata)
> +{
> +	if (test_bit(TPDM_DS_GPR, drvdata->datasets)) {
> +		drvdata->gpr = devm_kzalloc(drvdata->dev, sizeof(*drvdata->gpr),
> +					    GFP_KERNEL);
> +		if (!drvdata->gpr)
> +			return -ENOMEM;
> +	}
> +	if (test_bit(TPDM_DS_BC, drvdata->datasets)) {
> +		drvdata->bc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->bc),
> +					   GFP_KERNEL);
> +		if (!drvdata->bc)
> +			return -ENOMEM;
> +	}
> +	if (test_bit(TPDM_DS_TC, drvdata->datasets)) {
> +		drvdata->tc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->tc),
> +					   GFP_KERNEL);
> +		if (!drvdata->tc)
> +			return -ENOMEM;
> +	}
> +	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
> +		drvdata->dsb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->dsb),
> +					    GFP_KERNEL);
> +		if (!drvdata->dsb)
> +			return -ENOMEM;
> +	}
> +	if (test_bit(TPDM_DS_CMB, drvdata->datasets)) {
> +		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
> +					    GFP_KERNEL);
> +		if (!drvdata->cmb)
> +			return -ENOMEM;
> +	} else if (test_bit(TPDM_DS_MCMB, drvdata->datasets)) {
> +		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
> +					    GFP_KERNEL);
> +		if (!drvdata->cmb)
> +			return -ENOMEM;
> +		drvdata->cmb->mcmb = devm_kzalloc(drvdata->dev,
> +						  sizeof(*drvdata->cmb->mcmb),
> +						  GFP_KERNEL);
> +		if (!drvdata->cmb->mcmb)
> +			return -ENOMEM;

How can I understand what the above does when:

1) There isn't a single line of comments.
2) I don't know the HW.
3) I don't have access to the documentation.

> +	}
> +	return 0;
> +}
> +
> +static void tpdm_init_default_data(struct tpdm_drvdata *drvdata)
> +{
> +	if (test_bit(TPDM_DS_BC, drvdata->datasets))
> +		drvdata->bc->retrieval_mode = TPDM_MODE_ATB;
> +
> +	if (test_bit(TPDM_DS_TC, drvdata->datasets))
> +		drvdata->tc->retrieval_mode = TPDM_MODE_ATB;
> +
> +	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
> +		drvdata->dsb->trig_ts = true;
> +		drvdata->dsb->trig_type = false;
> +	}
> +
> +	if (test_bit(TPDM_DS_CMB, drvdata->datasets) ||
> +	    test_bit(TPDM_DS_MCMB, drvdata->datasets))
> +		drvdata->cmb->trig_ts = true;
> +}
> +
> +static int tpdm_parse_of_data(struct tpdm_drvdata *drvdata)
> +{
> +	int i, ret;
> +	const char *tclk_name, *treg_name;
> +	struct device_node *node = drvdata->dev->of_node;
> +
> +	drvdata->clk_enable = of_property_read_bool(node, "qcom,clk-enable");
> +	drvdata->msr_fix_req = of_property_read_bool(node, "qcom,msr-fix-req");
> +	drvdata->cmb_msr_skip = of_property_read_bool(node,
> +					"qcom,cmb-msr-skip");
> +
> +	drvdata->nr_tclk = of_property_count_strings(node, "qcom,tpdm-clks");
> +	if (drvdata->nr_tclk > 0) {
> +		drvdata->tclk = devm_kzalloc(drvdata->dev, drvdata->nr_tclk *
> +					     sizeof(*drvdata->tclk),
> +					     GFP_KERNEL);
> +		if (!drvdata->tclk)
> +			return -ENOMEM;
> +
> +		for (i = 0; i < drvdata->nr_tclk; i++) {
> +			ret = of_property_read_string_index(node,
> +					    "qcom,tpdm-clks", i, &tclk_name);
> +			if (ret)
> +				return ret;
> +
> +			drvdata->tclk[i] = devm_clk_get(drvdata->dev,
> +							tclk_name);
> +			if (IS_ERR(drvdata->tclk[i]))
> +				return PTR_ERR(drvdata->tclk[i]);
> +		}
> +	}
> +
> +	drvdata->nr_treg = of_property_count_strings(node, "qcom,tpdm-regs");
> +	if (drvdata->nr_treg > 0) {
> +		drvdata->treg = devm_kzalloc(drvdata->dev, drvdata->nr_treg *
> +					     sizeof(*drvdata->treg),
> +					     GFP_KERNEL);
> +		if (!drvdata->treg)
> +			return -ENOMEM;
> +
> +		for (i = 0; i < drvdata->nr_treg; i++) {
> +			ret = of_property_read_string_index(node,
> +					    "qcom,tpdm-regs", i, &treg_name);
> +			if (ret)
> +				return ret;
> +
> +			drvdata->treg[i] = devm_regulator_get(drvdata->dev,
> +							treg_name);
> +			if (IS_ERR(drvdata->treg[i]))
> +				return PTR_ERR(drvdata->treg[i]);
> +		}
> +	}

_None_ of the above are defined in the yaml file and/or part of the example that
is shown there.  Moreover they don't appear in patch 10/10 where TPDM and TPDA are
supposed to be introduced.  I will comment on patch 10/10 when I'm done with
this one.

> +
> +	return 0;
> +}
> +
> +static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
> +{
> +	int ret, i;
> +	uint32_t pidr, devid;
> +	struct device *dev = &adev->dev;
> +	struct coresight_platform_data *pdata;
> +	struct tpdm_drvdata *drvdata;
> +	struct coresight_desc desc = { 0 };
> +	static int traceid = TPDM_TRACE_ID_START;
> +	uint32_t version;
> +
> +	desc.name = coresight_alloc_device_name(&tpdm_devs, dev);
> +	if (!desc.name)
> +		return -ENOMEM;
> +	pdata = coresight_get_platform_data(dev);
> +	if (IS_ERR(pdata))
> +		return PTR_ERR(pdata);
> +	adev->dev.platform_data = pdata;
> +
> +	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> +	if (!drvdata)
> +		return -ENOMEM;
> +	drvdata->dev = &adev->dev;
> +	dev_set_drvdata(dev, drvdata);
> +
> +	drvdata->base = devm_ioremap_resource(dev, &adev->res);
> +	if (!drvdata->base)
> +		return -ENOMEM;
> +
> +	mutex_init(&drvdata->lock);
> +
> +	ret = tpdm_parse_of_data(drvdata);
> +	if (ret) {
> +		dev_err(drvdata->dev, "TPDM parse of data fail\n");
> +		return -EINVAL;
> +	}
> +
> +	desc.type = CORESIGHT_DEV_TYPE_SOURCE;
> +	desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;

Why is this of subtype CORESIGHT_DEV_SUBTYPE_SOURCE_PROC when TPDMs are not
associated with a CPU?  Here we should probably introduce something like
CORESIGHT_DEV_SUBTYPE_SOURCE_SYS

> +	desc.ops = &tpdm_cs_ops;
> +	desc.pdata = adev->dev.platform_data;
> +	desc.dev = &adev->dev;
> +	drvdata->csdev = coresight_register(&desc);
> +	if (IS_ERR(drvdata->csdev))
> +		return PTR_ERR(drvdata->csdev);
> +
> +	version = tpdm_readl(drvdata, CORESIGHT_PERIPHIDR2);
> +	drvdata->version = BMVAL(version, 4, 7);
> +

What is MSR support?  This should be documented.  Looking more closely at this
patch, not a single line of documentation is provided.  If you look at other
drivers, you will find things to be quite different.  The coresight subsystem
has become very complex and documentation helps us, and anyone trying to
contribute, understand what the code does.

> +	if (drvdata->version)
> +		drvdata->msr_support = true;
> +
> +	pidr = tpdm_readl(drvdata, CORESIGHT_PERIPHIDR0);
> +	for (i = 0; i < TPDM_DATASETS; i++) {
> +		if (pidr & BIT(i)) {
> +			__set_bit(i, drvdata->datasets);
> +			__set_bit(i, drvdata->enable_ds);
> +		}
> +	}
> +
> +	ret = tpdm_datasets_alloc(drvdata);
> +	if (ret) {
> +		coresight_unregister(drvdata->csdev);
> +		return ret;
> +	}
> +
> +	tpdm_init_default_data(drvdata);
> +
> +	devid = tpdm_readl(drvdata, CORESIGHT_DEVID);
> +	drvdata->tc_trig_type = BMVAL(devid, 27, 28);
> +	drvdata->bc_trig_type = BMVAL(devid, 25, 26);
> +	drvdata->bc_gang_type = BMVAL(devid, 23, 24);
> +	drvdata->bc_counters_avail = BMVAL(devid, 6, 10) + 1;
> +	drvdata->tc_counters_avail = BMVAL(devid, 4, 5) + 1;

Shouldn't this be part of tpdm_init_default_data()?

> +
> +	drvdata->traceid = traceid++;

For this to work a new function needs to be introduced in coresight-pmu.h.
Something like:

static inline int coresight_get_system_trace_id(int id)
{
        /* Start system IDs above the highest per CPU trace ID. */
        return coresigth_get_trace_id(cpumask_last(cpu_possible_mask) + 1);
}

> +
> +	dev_dbg(drvdata->dev, "TPDM initialized\n");

Please remove this.

> +
> +	if (boot_enable)
> +		coresight_enable(drvdata->csdev);
> +
> +	pm_runtime_put(&adev->dev);
> +
> +	return 0;
> +}
> +
> +static struct amba_id tpdm_ids[] = {
> +	{
> +		.id     = 0x001f0e00,
> +		.mask   = 0x00ffff00,

Any way we can use CS_AMBA_ID() here?

> +		.data	= "TPDM",

What is .data used for?

> +	},
> +	{ 0, 0},
> +};
> +
> +static struct amba_driver tpdm_driver = {
> +	.drv = {
> +		.name   = "coresight-tpdm",
> +		.owner	= THIS_MODULE,
> +		.suppress_bind_attrs = true,
> +	},
> +	.probe          = tpdm_probe,
> +	.id_table	= tpdm_ids,
> +};
> +
> +module_amba_driver(tpdm_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Monitor driver");
> -- 
> 2.17.1
> 

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

* Re: [PATCH 10/10] ARM: dts: msm: Add TPDA and TPDM support to DTS for RB5
  2021-10-21  7:38 ` [PATCH 10/10] ARM: dts: msm: Add TPDA and TPDM support to DTS for RB5 Tao Zhang
@ 2021-11-02 18:02   ` Mathieu Poirier
  2021-11-03  8:14     ` Tao Zhang
  2021-11-04  9:45   ` Suzuki K Poulose
  1 sibling, 1 reply; 37+ messages in thread
From: Mathieu Poirier @ 2021-11-02 18:02 UTC (permalink / raw)
  To: Tao Zhang
  Cc: Suzuki K Poulose, Alexander Shishkin, Mike Leach, Leo Yan,
	Greg Kroah-Hartman, coresight, linux-arm-kernel, linux-kernel,
	Tingwei Zhang, Mao Jinlong, Yuanfang Zhang, Trilok Soni

On Thu, Oct 21, 2021 at 03:38:56PM +0800, Tao Zhang wrote:
> Add TPDA and TPDM support to DTS for RB5 board. This change is a
> sample for validating. After applying this patch, the new TPDM and
> TPDA nodes can be observed at the coresight devices path. TPDM and
> TPDA hardware can be operated by commands.
> 
> List the commands for validating this series patches as below.
> echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> echo 1 > /sys/bus/coresight/devices/tpdm0/enable_source
> echo 1 > /sys/bus/coresight/devices/tpdm0/integration_test
> echo 2 > /sys/bus/coresight/devices/tpdm0/integration_test
> cat /dev/tmc_etf0 > /data/etf-tpdm0.bin
> echo 0 > /sys/bus/coresight/devices/tpdm0/enable_source
> echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> echo 1 > /sys/bus/coresight/devices/tpdm1/enable_source
> echo 1 > /sys/bus/coresight/devices/tpdm1/integration_test
> echo 2 > /sys/bus/coresight/devices/tpdm1/integration_test
> cat /dev/tmc_etf0 > /data/etf-tpdm1.bin
> echo 0 > /sys/bus/coresight/devices/tpdm1/enable_source
> echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> echo 1 > /sys/bus/coresight/devices/tpdm2/enable_source
> echo 1 > /sys/bus/coresight/devices/tpdm2/integration_test
> echo 2 > /sys/bus/coresight/devices/tpdm2/integration_test
> cat /dev/tmc_etf0 > /data/etf-tpdm2.bin
> echo 0 > /sys/bus/coresight/devices/tpdm2/enable_source
> echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> 
> If the data from TPDMs can be obtained from the ETF, it means
> that the TPDMs verification is successful. At the same time,
> since TPDM0, TPDM1 and TPDM2 are all connected to the same
> funnel "funnel@6c2d000" and output via different output ports,

Where is "funnel@6c2d000" in this patch?

> it also means that the following patches verification is
> successful.
> coresight: add support to enable more coresight paths
> coresight: funnel: add support for multiple output ports
> 
> Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> ---
>  arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 439 +++++++++++++++++++++++
>  1 file changed, 439 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
> index 8ac96f8e79d4..bcec8b181e11 100644
> --- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
> +++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
> @@ -222,6 +222,445 @@
>  		regulator-max-microvolt = <1800000>;
>  		regulator-always-on;
>  	};
> +
> +	stm@6002000 {
> +		compatible = "arm,coresight-stm", "arm,primecell";
> +		reg = <0 0x06002000 0 0x1000>,
> +		      <0 0x16280000 0 0x180000>;
> +		reg-names = "stm-base", "stm-stimulus-base";
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +
> +		out-ports {
> +			port {
> +				stm_out: endpoint {
> +					remote-endpoint =
> +					  <&funnel0_in7>;
> +				};
> +			};
> +		};
> +	};
> +
> +	funnel@6041000 {
> +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> +		reg = <0 0x06041000 0 0x1000>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +
> +		out-ports {
> +			port {
> +				funnel0_out: endpoint {
> +					remote-endpoint =
> +					  <&merge_funnel_in0>;
> +				};
> +			};
> +		};
> +
> +		in-ports {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			port@7 {
> +				reg = <7>;
> +				funnel0_in7: endpoint {
> +					remote-endpoint = <&stm_out>;
> +				};
> +			};
> +		};
> +	};
> +
> +	funnel@6042000 {
> +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> +		reg = <0 0x06042000 0 0x1000>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +
> +		out-ports {
> +			port {
> +				funnel2_out: endpoint {
> +					remote-endpoint =
> +					  <&merge_funnel_in2>;
> +				};
> +			};
> +		};
> +
> +		in-ports {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			port@4 {
> +				reg = <4>;
> +				funnel2_in5: endpoint {
> +					remote-endpoint =
> +					  <&apss_merge_funnel_out>;
> +				};
> +			};
> +		};
> +	};
> +
> +	funnel@6b04000 {
> +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> +		arm,primecell-periphid = <0x000bb908>;
> +
> +		reg = <0 0x6b04000 0 0x1000>;
> +		reg-names = "funnel-base";
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +
> +		out-ports {
> +			port {
> +				merge_funnel_out: endpoint {
> +					remote-endpoint =
> +						<&etf_in>;
> +				};
> +			};
> +		};
> +
> +		in-ports {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			port@7 {
> +				reg = <7>;
> +				swao_funnel_in7: endpoint {
> +					slave-mode;
> +					remote-endpoint=
> +						<&merg_funnel_out>;
> +				};
> +			};
> +		};
> +
> +	};
> +
> +	funnel@6045000 {
> +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> +		reg = <0 0x06045000 0 0x1000>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +
> +		out-ports {
> +			port {
> +				merg_funnel_out: endpoint {
> +					remote-endpoint = <&swao_funnel_in7>;
> +				};
> +			};
> +		};
> +
> +		in-ports {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			port@0 {
> +				reg = <0>;
> +				merge_funnel_in0: endpoint {
> +					remote-endpoint =
> +					  <&funnel0_out>;
> +				};
> +			};
> +
> +			port@1 {
> +				reg = <1>;
> +				merge_funnel_in2: endpoint {
> +					remote-endpoint =
> +					  <&funnel2_out>;
> +				};
> +			};
> +		};
> +	};
> +
> +	etf@6b05000 {
> +		compatible = "arm,coresight-tmc", "arm,primecell";
> +		reg = <0 0x06b05000 0 0x1000>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +
> +		in-ports {
> +			port {
> +				etf_in: endpoint {
> +					remote-endpoint =
> +					  <&merge_funnel_out>;
> +				};
> +			};
> +		};
> +	};
> +
> +	etm@7040000 {
> +		compatible = "arm,coresight-etm4x", "arm,primecell";
> +		reg = <0 0x07040000 0 0x1000>;
> +
> +		cpu = <&CPU0>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +		arm,coresight-loses-context-with-cpu;
> +
> +		out-ports {
> +			port {
> +				etm0_out: endpoint {
> +					remote-endpoint =
> +					  <&apss_funnel_in0>;
> +				};
> +			};
> +		};
> +	};
> +
> +	etm@7140000 {
> +		compatible = "arm,coresight-etm4x", "arm,primecell";
> +		reg = <0 0x07140000 0 0x1000>;
> +
> +		cpu = <&CPU1>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +		arm,coresight-loses-context-with-cpu;
> +
> +		out-ports {
> +			port {
> +				etm1_out: endpoint {
> +					remote-endpoint =
> +					  <&apss_funnel_in1>;
> +				};
> +			};
> +		};
> +	};
> +
> +	etm@7240000 {
> +		compatible = "arm,coresight-etm4x", "arm,primecell";
> +		reg = <0 0x07240000 0 0x1000>;
> +
> +		cpu = <&CPU2>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +		arm,coresight-loses-context-with-cpu;
> +
> +		out-ports {
> +			port {
> +				etm2_out: endpoint {
> +					remote-endpoint =
> +					  <&apss_funnel_in2>;
> +				};
> +			};
> +		};
> +	};
> +
> +	etm@7340000 {
> +		compatible = "arm,coresight-etm4x", "arm,primecell";
> +		reg = <0 0x07340000 0 0x1000>;
> +
> +		cpu = <&CPU3>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +		arm,coresight-loses-context-with-cpu;
> +
> +		out-ports {
> +			port {
> +				etm3_out: endpoint {
> +					remote-endpoint =
> +					  <&apss_funnel_in3>;
> +				};
> +			};
> +		};
> +	};
> +
> +	etm@7440000 {
> +		compatible = "arm,coresight-etm4x", "arm,primecell";
> +		reg = <0 0x07440000 0 0x1000>;
> +
> +		cpu = <&CPU4>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +		arm,coresight-loses-context-with-cpu;
> +
> +		out-ports {
> +			port {
> +				etm4_out: endpoint {
> +					remote-endpoint =
> +					  <&apss_funnel_in4>;
> +				};
> +			};
> +		};
> +	};
> +
> +	etm@7540000 {
> +		compatible = "arm,coresight-etm4x", "arm,primecell";
> +		reg = <0 0x07540000 0 0x1000>;
> +
> +		cpu = <&CPU5>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +		arm,coresight-loses-context-with-cpu;
> +
> +		out-ports {
> +			port {
> +				etm5_out: endpoint {
> +					remote-endpoint =
> +					  <&apss_funnel_in5>;
> +				};
> +			};
> +		};
> +	};
> +
> +	etm@7640000 {
> +		compatible = "arm,coresight-etm4x", "arm,primecell";
> +		reg = <0 0x07640000 0 0x1000>;
> +
> +		cpu = <&CPU6>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +		arm,coresight-loses-context-with-cpu;
> +
> +		out-ports {
> +			port {
> +				etm6_out: endpoint {
> +					remote-endpoint =
> +					  <&apss_funnel_in6>;
> +				};
> +			};
> +		};
> +	};
> +
> +	etm@7740000 {
> +		compatible = "arm,coresight-etm4x", "arm,primecell";
> +		reg = <0 0x07740000 0 0x1000>;
> +
> +		cpu = <&CPU7>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +		arm,coresight-loses-context-with-cpu;
> +
> +		out-ports {
> +			port {
> +				etm7_out: endpoint {
> +					remote-endpoint =
> +					  <&apss_funnel_in7>;
> +				};
> +			};
> +		};
> +	};
> +
> +	funnel@7800000 {
> +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> +		reg = <0 0x07800000 0 0x1000>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +
> +		out-ports {
> +			port {
> +				apss_funnel_out: endpoint {
> +					remote-endpoint =
> +					  <&apss_merge_funnel_in>;
> +				};
> +			};
> +		};
> +
> +		in-ports {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			port@0 {
> +				reg = <0>;
> +				apss_funnel_in0: endpoint {
> +					remote-endpoint =
> +					  <&etm0_out>;
> +				};
> +			};
> +
> +			port@1 {
> +				reg = <1>;
> +				apss_funnel_in1: endpoint {
> +					remote-endpoint =
> +					  <&etm1_out>;
> +				};
> +			};
> +
> +			port@2 {
> +				reg = <2>;
> +				apss_funnel_in2: endpoint {
> +					remote-endpoint =
> +					  <&etm2_out>;
> +				};
> +			};
> +
> +			port@3 {
> +				reg = <3>;
> +				apss_funnel_in3: endpoint {
> +					remote-endpoint =
> +					  <&etm3_out>;
> +				};
> +			};
> +
> +			port@4 {
> +				reg = <4>;
> +				apss_funnel_in4: endpoint {
> +					remote-endpoint =
> +					  <&etm4_out>;
> +				};
> +			};
> +
> +			port@5 {
> +				reg = <5>;
> +				apss_funnel_in5: endpoint {
> +					remote-endpoint =
> +					  <&etm5_out>;
> +				};
> +			};
> +
> +			port@6 {
> +				reg = <6>;
> +				apss_funnel_in6: endpoint {
> +					remote-endpoint =
> +					  <&etm6_out>;
> +				};
> +			};
> +
> +			port@7 {
> +				reg = <7>;
> +				apss_funnel_in7: endpoint {
> +					remote-endpoint =
> +					  <&etm7_out>;
> +				};
> +			};
> +		};
> +	};
> +
> +	funnel@7810000 {
> +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> +		reg = <0 0x07810000 0 0x1000>;
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +
> +		out-ports {
> +			port {
> +				apss_merge_funnel_out: endpoint {
> +					remote-endpoint =
> +					  <&funnel2_in5>;
> +				};
> +			};
> +		};
> +
> +		in-ports {
> +			port {
> +				apss_merge_funnel_in: endpoint {
> +					remote-endpoint =
> +					  <&apss_funnel_out>;
> +				};
> +			};
> +		};
> +	};

Which of the above introduces TPDM and TPDA devices?

More comments to come tomorrow.

Thanks,
Mathieu

>  };
>  
>  &adsp {
> -- 
> 2.17.1
> 

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

* Re: [PATCH 10/10] ARM: dts: msm: Add TPDA and TPDM support to DTS for RB5
  2021-11-02 18:02   ` Mathieu Poirier
@ 2021-11-03  8:14     ` Tao Zhang
  0 siblings, 0 replies; 37+ messages in thread
From: Tao Zhang @ 2021-11-03  8:14 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: Suzuki K Poulose, Alexander Shishkin, Mike Leach, Leo Yan,
	Greg Kroah-Hartman, coresight, linux-arm-kernel, linux-kernel,
	Tingwei Zhang, Mao Jinlong, Yuanfang Zhang, Trilok Soni

On Tue, Nov 02, 2021 at 12:02:39PM -0600, Mathieu Poirier wrote:
> On Thu, Oct 21, 2021 at 03:38:56PM +0800, Tao Zhang wrote:
> > Add TPDA and TPDM support to DTS for RB5 board. This change is a
> > sample for validating. After applying this patch, the new TPDM and
> > TPDA nodes can be observed at the coresight devices path. TPDM and
> > TPDA hardware can be operated by commands.
> > 
> > List the commands for validating this series patches as below.
> > echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> > echo 1 > /sys/bus/coresight/devices/tpdm0/enable_source
> > echo 1 > /sys/bus/coresight/devices/tpdm0/integration_test
> > echo 2 > /sys/bus/coresight/devices/tpdm0/integration_test
> > cat /dev/tmc_etf0 > /data/etf-tpdm0.bin
> > echo 0 > /sys/bus/coresight/devices/tpdm0/enable_source
> > echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> > echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> > echo 1 > /sys/bus/coresight/devices/tpdm1/enable_source
> > echo 1 > /sys/bus/coresight/devices/tpdm1/integration_test
> > echo 2 > /sys/bus/coresight/devices/tpdm1/integration_test
> > cat /dev/tmc_etf0 > /data/etf-tpdm1.bin
> > echo 0 > /sys/bus/coresight/devices/tpdm1/enable_source
> > echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> > echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> > echo 1 > /sys/bus/coresight/devices/tpdm2/enable_source
> > echo 1 > /sys/bus/coresight/devices/tpdm2/integration_test
> > echo 2 > /sys/bus/coresight/devices/tpdm2/integration_test
> > cat /dev/tmc_etf0 > /data/etf-tpdm2.bin
> > echo 0 > /sys/bus/coresight/devices/tpdm2/enable_source
> > echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> > 
> > If the data from TPDMs can be obtained from the ETF, it means
> > that the TPDMs verification is successful. At the same time,
> > since TPDM0, TPDM1 and TPDM2 are all connected to the same
> > funnel "funnel@6c2d000" and output via different output ports,
> 
> Where is "funnel@6c2d000" in this patch?
>
Hi Mathieu,

Sorry, the TPDM/TPDA related code in the device tree was omitted in this submission.
We will add them to device tree in the next version.
Paste this funnel configuration below for you review first. 
funnel@6c2d000 {
    compatible = "arm,coresight-dynamic-funnel", "arm,primecell";

    reg = <0 0x6c2d000 0 0x1000>;
    reg-names = "funnel-base";

    clocks = <&aoss_qmp>;
    clock-names = "apb_pclk";

    out-ports {
        #address-cells = <1>;
        #size-cells = <0>;
        port@2 {
            reg = <2>;
            tpdm_mm_out_tpda9: endpoint {
                remote-endpoint =
                    <&tpda_9_in_tpdm_mm>;
                label = <&tpdm_mm>;
            };
        };

        port@3 {
            reg = <3>;
            funnel_dl_center_out_tpda_10: endpoint {
                remote-endpoint =
                    <&tpda_10_in_funnel_dl_center>;
                label = <&tpdm_lpass>;
            };
        };

        port@7 {
            reg = <7>;
            tpdm_turing_out_tpda14: endpoint {
                remote-endpoint =
                    <&tpda_14_in_tpdm_turing>;
                label = <&tpdm_turing>;
            };
        };
    };

    in-ports {
        #address-cells = <1>;
        #size-cells = <0>;

        port@15 {
            reg = <2>;
            funnel_dl_center_in_funnel_dl_mm: endpoint {
                slave-mode;
                remote-endpoint =
                <&funnel_dl_mm_out_funnel_dl_center>;
            };
        };

        port@16 {
            reg = <3>;
            funnel_dl_center_in_funnel_lpass: endpoint {
                slave-mode;
                remote-endpoint =
                <&funnel_lpass_out_funnel_dl_center>;
            };
        };

        port@18 {
            reg = <5>;
            funnel_dl_center_in_funnel_compute: endpoint {
                slave-mode;
                remote-endpoint =
                <&funnel_compute_out_funnel_dl_center>;
            };
        };
    };
};
> > it also means that the following patches verification is
> > successful.
> > coresight: add support to enable more coresight paths
> > coresight: funnel: add support for multiple output ports
> > 
> > Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> > ---
> >  arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 439 +++++++++++++++++++++++
> >  1 file changed, 439 insertions(+)
> > 
> > diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
> > index 8ac96f8e79d4..bcec8b181e11 100644
> > --- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
> > +++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
> > @@ -222,6 +222,445 @@
> >  		regulator-max-microvolt = <1800000>;
> >  		regulator-always-on;
> >  	};
> > +
> > +	stm@6002000 {
> > +		compatible = "arm,coresight-stm", "arm,primecell";
> > +		reg = <0 0x06002000 0 0x1000>,
> > +		      <0 0x16280000 0 0x180000>;
> > +		reg-names = "stm-base", "stm-stimulus-base";
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +
> > +		out-ports {
> > +			port {
> > +				stm_out: endpoint {
> > +					remote-endpoint =
> > +					  <&funnel0_in7>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	funnel@6041000 {
> > +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> > +		reg = <0 0x06041000 0 0x1000>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +
> > +		out-ports {
> > +			port {
> > +				funnel0_out: endpoint {
> > +					remote-endpoint =
> > +					  <&merge_funnel_in0>;
> > +				};
> > +			};
> > +		};
> > +
> > +		in-ports {
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +
> > +			port@7 {
> > +				reg = <7>;
> > +				funnel0_in7: endpoint {
> > +					remote-endpoint = <&stm_out>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	funnel@6042000 {
> > +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> > +		reg = <0 0x06042000 0 0x1000>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +
> > +		out-ports {
> > +			port {
> > +				funnel2_out: endpoint {
> > +					remote-endpoint =
> > +					  <&merge_funnel_in2>;
> > +				};
> > +			};
> > +		};
> > +
> > +		in-ports {
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +
> > +			port@4 {
> > +				reg = <4>;
> > +				funnel2_in5: endpoint {
> > +					remote-endpoint =
> > +					  <&apss_merge_funnel_out>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	funnel@6b04000 {
> > +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> > +		arm,primecell-periphid = <0x000bb908>;
> > +
> > +		reg = <0 0x6b04000 0 0x1000>;
> > +		reg-names = "funnel-base";
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +
> > +		out-ports {
> > +			port {
> > +				merge_funnel_out: endpoint {
> > +					remote-endpoint =
> > +						<&etf_in>;
> > +				};
> > +			};
> > +		};
> > +
> > +		in-ports {
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +
> > +			port@7 {
> > +				reg = <7>;
> > +				swao_funnel_in7: endpoint {
> > +					slave-mode;
> > +					remote-endpoint=
> > +						<&merg_funnel_out>;
> > +				};
> > +			};
> > +		};
> > +
> > +	};
> > +
> > +	funnel@6045000 {
> > +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> > +		reg = <0 0x06045000 0 0x1000>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +
> > +		out-ports {
> > +			port {
> > +				merg_funnel_out: endpoint {
> > +					remote-endpoint = <&swao_funnel_in7>;
> > +				};
> > +			};
> > +		};
> > +
> > +		in-ports {
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +
> > +			port@0 {
> > +				reg = <0>;
> > +				merge_funnel_in0: endpoint {
> > +					remote-endpoint =
> > +					  <&funnel0_out>;
> > +				};
> > +			};
> > +
> > +			port@1 {
> > +				reg = <1>;
> > +				merge_funnel_in2: endpoint {
> > +					remote-endpoint =
> > +					  <&funnel2_out>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	etf@6b05000 {
> > +		compatible = "arm,coresight-tmc", "arm,primecell";
> > +		reg = <0 0x06b05000 0 0x1000>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +
> > +		in-ports {
> > +			port {
> > +				etf_in: endpoint {
> > +					remote-endpoint =
> > +					  <&merge_funnel_out>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	etm@7040000 {
> > +		compatible = "arm,coresight-etm4x", "arm,primecell";
> > +		reg = <0 0x07040000 0 0x1000>;
> > +
> > +		cpu = <&CPU0>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +		arm,coresight-loses-context-with-cpu;
> > +
> > +		out-ports {
> > +			port {
> > +				etm0_out: endpoint {
> > +					remote-endpoint =
> > +					  <&apss_funnel_in0>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	etm@7140000 {
> > +		compatible = "arm,coresight-etm4x", "arm,primecell";
> > +		reg = <0 0x07140000 0 0x1000>;
> > +
> > +		cpu = <&CPU1>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +		arm,coresight-loses-context-with-cpu;
> > +
> > +		out-ports {
> > +			port {
> > +				etm1_out: endpoint {
> > +					remote-endpoint =
> > +					  <&apss_funnel_in1>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	etm@7240000 {
> > +		compatible = "arm,coresight-etm4x", "arm,primecell";
> > +		reg = <0 0x07240000 0 0x1000>;
> > +
> > +		cpu = <&CPU2>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +		arm,coresight-loses-context-with-cpu;
> > +
> > +		out-ports {
> > +			port {
> > +				etm2_out: endpoint {
> > +					remote-endpoint =
> > +					  <&apss_funnel_in2>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	etm@7340000 {
> > +		compatible = "arm,coresight-etm4x", "arm,primecell";
> > +		reg = <0 0x07340000 0 0x1000>;
> > +
> > +		cpu = <&CPU3>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +		arm,coresight-loses-context-with-cpu;
> > +
> > +		out-ports {
> > +			port {
> > +				etm3_out: endpoint {
> > +					remote-endpoint =
> > +					  <&apss_funnel_in3>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	etm@7440000 {
> > +		compatible = "arm,coresight-etm4x", "arm,primecell";
> > +		reg = <0 0x07440000 0 0x1000>;
> > +
> > +		cpu = <&CPU4>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +		arm,coresight-loses-context-with-cpu;
> > +
> > +		out-ports {
> > +			port {
> > +				etm4_out: endpoint {
> > +					remote-endpoint =
> > +					  <&apss_funnel_in4>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	etm@7540000 {
> > +		compatible = "arm,coresight-etm4x", "arm,primecell";
> > +		reg = <0 0x07540000 0 0x1000>;
> > +
> > +		cpu = <&CPU5>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +		arm,coresight-loses-context-with-cpu;
> > +
> > +		out-ports {
> > +			port {
> > +				etm5_out: endpoint {
> > +					remote-endpoint =
> > +					  <&apss_funnel_in5>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	etm@7640000 {
> > +		compatible = "arm,coresight-etm4x", "arm,primecell";
> > +		reg = <0 0x07640000 0 0x1000>;
> > +
> > +		cpu = <&CPU6>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +		arm,coresight-loses-context-with-cpu;
> > +
> > +		out-ports {
> > +			port {
> > +				etm6_out: endpoint {
> > +					remote-endpoint =
> > +					  <&apss_funnel_in6>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	etm@7740000 {
> > +		compatible = "arm,coresight-etm4x", "arm,primecell";
> > +		reg = <0 0x07740000 0 0x1000>;
> > +
> > +		cpu = <&CPU7>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +		arm,coresight-loses-context-with-cpu;
> > +
> > +		out-ports {
> > +			port {
> > +				etm7_out: endpoint {
> > +					remote-endpoint =
> > +					  <&apss_funnel_in7>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	funnel@7800000 {
> > +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> > +		reg = <0 0x07800000 0 0x1000>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +
> > +		out-ports {
> > +			port {
> > +				apss_funnel_out: endpoint {
> > +					remote-endpoint =
> > +					  <&apss_merge_funnel_in>;
> > +				};
> > +			};
> > +		};
> > +
> > +		in-ports {
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +
> > +			port@0 {
> > +				reg = <0>;
> > +				apss_funnel_in0: endpoint {
> > +					remote-endpoint =
> > +					  <&etm0_out>;
> > +				};
> > +			};
> > +
> > +			port@1 {
> > +				reg = <1>;
> > +				apss_funnel_in1: endpoint {
> > +					remote-endpoint =
> > +					  <&etm1_out>;
> > +				};
> > +			};
> > +
> > +			port@2 {
> > +				reg = <2>;
> > +				apss_funnel_in2: endpoint {
> > +					remote-endpoint =
> > +					  <&etm2_out>;
> > +				};
> > +			};
> > +
> > +			port@3 {
> > +				reg = <3>;
> > +				apss_funnel_in3: endpoint {
> > +					remote-endpoint =
> > +					  <&etm3_out>;
> > +				};
> > +			};
> > +
> > +			port@4 {
> > +				reg = <4>;
> > +				apss_funnel_in4: endpoint {
> > +					remote-endpoint =
> > +					  <&etm4_out>;
> > +				};
> > +			};
> > +
> > +			port@5 {
> > +				reg = <5>;
> > +				apss_funnel_in5: endpoint {
> > +					remote-endpoint =
> > +					  <&etm5_out>;
> > +				};
> > +			};
> > +
> > +			port@6 {
> > +				reg = <6>;
> > +				apss_funnel_in6: endpoint {
> > +					remote-endpoint =
> > +					  <&etm6_out>;
> > +				};
> > +			};
> > +
> > +			port@7 {
> > +				reg = <7>;
> > +				apss_funnel_in7: endpoint {
> > +					remote-endpoint =
> > +					  <&etm7_out>;
> > +				};
> > +			};
> > +		};
> > +	};
> > +
> > +	funnel@7810000 {
> > +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> > +		reg = <0 0x07810000 0 0x1000>;
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +
> > +		out-ports {
> > +			port {
> > +				apss_merge_funnel_out: endpoint {
> > +					remote-endpoint =
> > +					  <&funnel2_in5>;
> > +				};
> > +			};
> > +		};
> > +
> > +		in-ports {
> > +			port {
> > +				apss_merge_funnel_in: endpoint {
> > +					remote-endpoint =
> > +					  <&apss_funnel_out>;
> > +				};
> > +			};
> > +		};
> > +	};
> 
> Which of the above introduces TPDM and TPDA devices?
> 
> More comments to come tomorrow.
> 
> Thanks,
> Mathieu
> 
> >  };
Hi Mathieu,

We will add TPDM/TPDA related code to the device tree in the next verison.

Best,
Tao
> >  
> >  &adsp {
> > -- 
> > 2.17.1
> > 

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

* Re: [PATCH 04/10] Coresight: Enable BC and GPR for TPDM driver
  2021-10-21  7:38 ` [PATCH 04/10] Coresight: Enable BC and GPR for TPDM driver Tao Zhang
@ 2021-11-03 19:43   ` Mathieu Poirier
  2021-11-04 11:13     ` Jinlong
  0 siblings, 1 reply; 37+ messages in thread
From: Mathieu Poirier @ 2021-11-03 19:43 UTC (permalink / raw)
  To: Tao Zhang
  Cc: Suzuki K Poulose, Alexander Shishkin, Mike Leach, Leo Yan,
	Greg Kroah-Hartman, coresight, linux-arm-kernel, linux-kernel,
	Tingwei Zhang, Mao Jinlong, Yuanfang Zhang, Trilok Soni

On Thu, Oct 21, 2021 at 03:38:50PM +0800, Tao Zhang wrote:
> Enable GPR and Basic Counts(BC) for TPDM. Add GPR interface and

But what is GPR?  What does it stand for?  Where is this documented?

> basic control sysFS interface for TPDM. The GPR interface has RW
> and RO fields for controlling external logic and mapping core
> signals to an APB accessible address in the TPDM address map.
> 
> Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> ---
>  drivers/hwtracing/coresight/coresight-tpdm.c | 334 +++++++++++++++++++
>  1 file changed, 334 insertions(+)
> 
> diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
> index 906776c859d6..c0a01979e42f 100644
> --- a/drivers/hwtracing/coresight/coresight-tpdm.c
> +++ b/drivers/hwtracing/coresight/coresight-tpdm.c
> @@ -276,6 +276,93 @@ struct tpdm_drvdata {
>  
>  static void tpdm_init_default_data(struct tpdm_drvdata *drvdata);
>  
> +static void __tpdm_enable_gpr(struct tpdm_drvdata *drvdata)

There is no need for the double underscore at the beginning of the function
name, please remove.  The same goes for __tpdm_config_bc_msr() and
__tpdm_enable_bc().

> +{
> +	int i;
> +
> +	for (i = 0; i < TPDM_GPR_REGS_MAX; i++) {
> +		if (!test_bit(i, drvdata->gpr->gpr_dirty))
> +			continue;
> +		tpdm_writel(drvdata, drvdata->gpr->gp_regs[i], TPDM_GPR_CR(i));
> +	}
> +}
> +
> +static void __tpdm_config_bc_msr(struct tpdm_drvdata *drvdata)
> +{
> +	int i;
> +
> +	if (!drvdata->msr_support)

Shouldn't msr_support be part of bc_dataset?  And why do we have a function for
this when an if() condition would work just as well, as it is done in
bc_trig_type below?

> +		return;
> +
> +	for (i = 0; i < TPDM_BC_MAX_MSR; i++)
> +		tpdm_writel(drvdata, drvdata->bc->msr[i], TPDM_BC_MSR(i));
> +}
> +
> +static void __tpdm_enable_bc(struct tpdm_drvdata *drvdata)
> +{
> +	int i;
> +	uint32_t val;
> +
> +	if (drvdata->bc->sat_mode)
> +		tpdm_writel(drvdata, drvdata->bc->sat_mode,
> +			    TPDM_BC_SATROLL);
> +	else
> +		tpdm_writel(drvdata, 0x0, TPDM_BC_SATROLL);
> +
> +	if (drvdata->bc->enable_counters) {
> +		tpdm_writel(drvdata, 0xFFFFFFFF, TPDM_BC_CNTENCLR);
> +		tpdm_writel(drvdata, drvdata->bc->enable_counters,
> +			    TPDM_BC_CNTENSET);
> +	}
> +	if (drvdata->bc->clear_counters)
> +		tpdm_writel(drvdata, drvdata->bc->clear_counters,
> +			    TPDM_BC_CNTENCLR);
> +
> +	if (drvdata->bc->enable_irq) {
> +		tpdm_writel(drvdata, 0xFFFFFFFF, TPDM_BC_INTENCLR);
> +		tpdm_writel(drvdata, drvdata->bc->enable_irq,
> +			    TPDM_BC_INTENSET);
> +	}
> +	if (drvdata->bc->clear_irq)
> +		tpdm_writel(drvdata, drvdata->bc->clear_irq,
> +			    TPDM_BC_INTENCLR);
> +
> +	if (drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_FULL) {
> +		for (i = 0; i < drvdata->bc_counters_avail; i++) {

Same here - shouldn't bc_trig_type and bc_counters_avail be part of bc_dataset?

> +			tpdm_writel(drvdata, drvdata->bc->trig_val_lo[i],
> +				    TPDM_BC_TRIG_LO(i));
> +			tpdm_writel(drvdata, drvdata->bc->trig_val_hi[i],
> +				    TPDM_BC_TRIG_HI(i));
> +		}
> +	} else if (drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_PARTIAL) {
> +		tpdm_writel(drvdata, drvdata->bc->trig_val_lo[0],
> +			    TPDM_BC_TRIG_LO(0));
> +		tpdm_writel(drvdata, drvdata->bc->trig_val_hi[0],
> +			    TPDM_BC_TRIG_HI(0));
> +	}
> +
> +	if (drvdata->bc->enable_ganging)
> +		tpdm_writel(drvdata, drvdata->bc->enable_ganging, TPDM_BC_GANG);
> +
> +	for (i = 0; i < TPDM_BC_MAX_OVERFLOW; i++)
> +		tpdm_writel(drvdata, drvdata->bc->overflow_val[i],
> +			    TPDM_BC_OVERFLOW(i));
> +
> +	__tpdm_config_bc_msr(drvdata);
> +
> +	val = tpdm_readl(drvdata, TPDM_BC_CR);
> +	if (drvdata->bc->retrieval_mode == TPDM_MODE_APB)
> +		val = val | BIT(2);
> +	else
> +		val = val & ~BIT(2);
> +	tpdm_writel(drvdata, val, TPDM_BC_CR);
> +
> +	val = tpdm_readl(drvdata, TPDM_BC_CR);
> +	/* Set the enable bit */
> +	val = val | BIT(0);
> +	tpdm_writel(drvdata, val, TPDM_BC_CR);

As a whole, this function is very hard to read and understand due to the lack of
comments.

> +}
> +
>  static void __tpdm_enable(struct tpdm_drvdata *drvdata)
>  {
>  	TPDM_UNLOCK(drvdata);
> @@ -283,6 +370,12 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata)
>  	if (drvdata->clk_enable)
>  		tpdm_writel(drvdata, 0x1, TPDM_CLK_CTRL);
>  
> +	if (test_bit(TPDM_DS_GPR, drvdata->enable_ds))
> +		__tpdm_enable_gpr(drvdata);
> +
> +	if (test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		__tpdm_enable_bc(drvdata);
> +
>  	TPDM_LOCK(drvdata);
>  }
>  
> @@ -307,10 +400,22 @@ static int tpdm_enable(struct coresight_device *csdev,
>  	return 0;
>  }
>  
> +static void __tpdm_disable_bc(struct tpdm_drvdata *drvdata)
> +{
> +	uint32_t config;
> +
> +	config = tpdm_readl(drvdata, TPDM_BC_CR);
> +	config = config & ~BIT(0);
> +	tpdm_writel(drvdata, config, TPDM_BC_CR);
> +}
> +
>  static void __tpdm_disable(struct tpdm_drvdata *drvdata)
>  {
>  	TPDM_UNLOCK(drvdata);
>  
> +	if (test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		__tpdm_disable_bc(drvdata);
> +

Shouldn't GPRs be disabled as well?  If not why is it the case?  A comment
should be explaining what is going on.

>  	if (drvdata->clk_enable)
>  		tpdm_writel(drvdata, 0x0, TPDM_CLK_CTRL);
>  
> @@ -352,6 +457,234 @@ static const struct coresight_ops tpdm_cs_ops = {
>  	.source_ops	= &tpdm_source_ops,
>  };

Everything related to sysfs should be in a patch on its own.

>  
> +static ssize_t available_datasets_show(struct device *dev,
> +					    struct device_attribute *attr,
> +					    char *buf)

Indentation problem here and for the rest of the patch.

> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	ssize_t size = 0;
> +
> +	if (test_bit(TPDM_DS_IMPLDEF, drvdata->datasets))
> +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s",
> +				  "IMPLDEF");
> +
> +	if (test_bit(TPDM_DS_DSB, drvdata->datasets))
> +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "DSB");
> +
> +	if (test_bit(TPDM_DS_CMB, drvdata->datasets))
> +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "CMB");
> +
> +	if (test_bit(TPDM_DS_TC, drvdata->datasets))
> +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "TC");
> +
> +	if (test_bit(TPDM_DS_BC, drvdata->datasets))
> +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "BC");
> +
> +	if (test_bit(TPDM_DS_GPR, drvdata->datasets))
> +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "GPR");
> +
> +	if (test_bit(TPDM_DS_MCMB, drvdata->datasets))
> +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "MCMB");
> +
> +	size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
> +	return size;
> +}
> +static DEVICE_ATTR_RO(available_datasets);
> +
> +static ssize_t enable_datasets_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	ssize_t size;
> +
> +	size = scnprintf(buf, PAGE_SIZE, "%*pb\n", TPDM_DATASETS,
> +			 drvdata->enable_ds);
> +
> +	if (PAGE_SIZE - size < 2)
> +		size = -EINVAL;

TPDM_DATASETS is set to 32 - is this realistic? 

> +	else
> +		size += scnprintf(buf + size, 2, "\n");

...and what is going on here?

> +	return size;
> +}
> +
> +static ssize_t enable_datasets_store(struct device *dev,
> +					  struct device_attribute *attr,
> +					  const char *buf,
> +					  size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +	int i;
> +
> +	if (kstrtoul(buf, 16, &val))
> +		return -EINVAL;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	for (i = 0; i < TPDM_DATASETS; i++) {
> +		if (test_bit(i, drvdata->datasets) && (val & BIT(i)))
> +			__set_bit(i, drvdata->enable_ds);
> +		else
> +			__clear_bit(i, drvdata->enable_ds);
> +	}
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(enable_datasets);
> +
> +static ssize_t reset_store(struct device *dev,
> +					  struct device_attribute *attr,
> +					  const char *buf,
> +					  size_t size)
> +{
> +	int ret = 0;
> +	unsigned long val;
> +	struct mcmb_dataset *mcmb_temp = NULL;
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> +	ret = kstrtoul(buf, 10, &val);

The coresight subsystem normally uses the hexadecimal base.

> +	if (ret)
> +		return ret;

Shouldn't this be "if (!ret)" ? 

> +
> +	mutex_lock(&drvdata->lock);
> +	/* Reset all datasets to ZERO */
> +	if (drvdata->gpr != NULL)
> +		memset(drvdata->gpr, 0, sizeof(struct gpr_dataset));
> +
> +	if (drvdata->bc != NULL)
> +		memset(drvdata->bc, 0, sizeof(struct bc_dataset));
> +
> +	if (drvdata->tc != NULL)
> +		memset(drvdata->tc, 0, sizeof(struct tc_dataset));
> +
> +	if (drvdata->dsb != NULL)
> +		memset(drvdata->dsb, 0, sizeof(struct dsb_dataset));
> +
> +	if (drvdata->cmb != NULL) {
> +		if (drvdata->cmb->mcmb != NULL) {
> +			mcmb_temp = drvdata->cmb->mcmb;
> +			memset(drvdata->cmb->mcmb, 0,
> +				sizeof(struct mcmb_dataset));
> +			}
> +
> +		memset(drvdata->cmb, 0, sizeof(struct cmb_dataset));
> +		drvdata->cmb->mcmb = mcmb_temp;
> +	}
> +	/* Init the default data */
> +	tpdm_init_default_data(drvdata);
> +
> +	mutex_unlock(&drvdata->lock);
> +
> +	/* Disable tpdm if enabled */
> +	if (drvdata->enable)
> +		coresight_disable(drvdata->csdev);

Why is this done out of the lock?

> +
> +	return size;
> +}
> +static DEVICE_ATTR_WO(reset);
> +
> +static ssize_t integration_test_store(struct device *dev,
> +					  struct device_attribute *attr,
> +					  const char *buf,
> +					  size_t size)
> +{
> +	int i, ret = 0;
> +	unsigned long val;
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> +	ret = kstrtoul(buf, 10, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val != 1 && val != 2)
> +		return -EINVAL;
> +
> +	if (!drvdata->enable)
> +		return -EINVAL;
> +
> +	if (val == 1)
> +		val = ATBCNTRL_VAL_64;
> +	else
> +		val = ATBCNTRL_VAL_32;
> +	TPDM_UNLOCK(drvdata);
> +	tpdm_writel(drvdata, 0x1, TPDM_ITCNTRL);
> +
> +	for (i = 1; i < 5; i++)
> +		tpdm_writel(drvdata, val, TPDM_ITATBCNTRL);
> +
> +	tpdm_writel(drvdata, 0, TPDM_ITCNTRL);
> +	TPDM_LOCK(drvdata);
> +	return size;
> +}
> +static DEVICE_ATTR_WO(integration_test);

Integration test interface should be conditional to a compile time option.  Have
a look at what was done for CTIs.

> +
> +static ssize_t gp_regs_show(struct device *dev,
> +				 struct device_attribute *attr,
> +				 char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	ssize_t size = 0;
> +	int i = 0;
> +
> +	if (!test_bit(TPDM_DS_GPR, drvdata->datasets))
> +		return -EPERM;

                return -EINVAL;

> +
> +	mutex_lock(&drvdata->lock);
> +	for (i = 0; i < TPDM_GPR_REGS_MAX; i++) {
> +		if (!test_bit(i, drvdata->gpr->gpr_dirty))
> +			continue;
> +		size += scnprintf(buf + size, PAGE_SIZE - size,
> +				  "Index: 0x%x Value: 0x%x\n", i,
> +				  drvdata->gpr->gp_regs[i]);

This should not be - the sysfs interface requires outputs of a single line.

> +	}
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +
> +static ssize_t gp_regs_store(struct device *dev,
> +				  struct device_attribute *attr,
> +				  const char *buf,
> +				  size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long index, val;
> +
> +	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_GPR, drvdata->datasets) ||
> +	    index >= TPDM_GPR_REGS_MAX)
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	drvdata->gpr->gp_regs[index] = val;
> +	__set_bit(index, drvdata->gpr->gpr_dirty);
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(gp_regs);
> +
> +static struct attribute *tpdm_attrs[] = {
> +	&dev_attr_available_datasets.attr,
> +	&dev_attr_enable_datasets.attr,
> +	&dev_attr_reset.attr,
> +	&dev_attr_integration_test.attr,
> +	&dev_attr_gp_regs.attr,
> +	NULL,
> +};

All new sysfs interface need to be documented.  See here:

Documentation/ABI/testing/sysfs-bus-coresight-devices-xyz

More comments to come...

Thanks,
Mathieu

> +
> +static struct attribute_group tpdm_attr_grp = {
> +	.attrs = tpdm_attrs,
> +};
> +static const struct attribute_group *tpdm_attr_grps[] = {
> +	&tpdm_attr_grp,
> +	NULL,
> +};
> +
>  static int tpdm_datasets_alloc(struct tpdm_drvdata *drvdata)
>  {
>  	if (test_bit(TPDM_DS_GPR, drvdata->datasets)) {
> @@ -513,6 +846,7 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
>  	desc.ops = &tpdm_cs_ops;
>  	desc.pdata = adev->dev.platform_data;
>  	desc.dev = &adev->dev;
> +	desc.groups = tpdm_attr_grps;
>  	drvdata->csdev = coresight_register(&desc);
>  	if (IS_ERR(drvdata->csdev))
>  		return PTR_ERR(drvdata->csdev);
> -- 
> 2.17.1
> 

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

* Re: [PATCH 03/10] Coresight: Add driver to support Coresight device TPDM
  2021-11-02 17:59   ` Mathieu Poirier
@ 2021-11-04  8:56     ` Jinlong
  2021-11-04 16:55       ` Mathieu Poirier
  2021-11-04  9:37     ` Suzuki K Poulose
  1 sibling, 1 reply; 37+ messages in thread
From: Jinlong @ 2021-11-04  8:56 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: Tao Zhang, Suzuki K Poulose, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni

Thanks Mathieu for the quick review.

On Tue, Nov 02, 2021 at 11:59:20AM -0600, Mathieu Poirier wrote:
> Good morning,
> 
> 
> On Thu, Oct 21, 2021 at 03:38:49PM +0800, Tao Zhang wrote:
> > Add driver to support Coresight device TPDM. This driver provides
> > support for configuring monitor. Monitors are primarily responsible
> > for data set collection and support the ability to collect any
> > permutation of data set types. Monitors are also responsible for
> > interaction with system cross triggering.
> 
> As far as I can tell there is nothing related to CTIs in this patch.  And if
> there is, it is not documented.
>

TPDM can create trigger outputs and monitor the trigger outputs by setting dsb_trig_type and dsb_trig_patt_mask.
we will documented the details in next version.
 
> > 
> > Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> > ---
> >  .../bindings/arm/coresight-tpdm.yaml          |  86 +++
> 
> As checkpatch says, this should be in a separate file.

We will create a separate patch for the dt binding changes.

> 
> >  MAINTAINERS                                   |   5 +
> 
> Since this is a coresight device Suzuki and I will continue the maintenance.
> The get_maintainer script will make sure you care CC'ed on patches related to
> the TPDM/TPDA drivers, and we would typically requried a "Reviewed-by" tag from
> you before merging.


We will update the maintainers.

> 
> >  drivers/hwtracing/coresight/Kconfig           |   9 +
> >  drivers/hwtracing/coresight/Makefile          |   1 +
> >  drivers/hwtracing/coresight/coresight-tpdm.c  | 583 ++++++++++++++++++
> >  5 files changed, 684 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
> >  create mode 100644 drivers/hwtracing/coresight/coresight-tpdm.c
> > 
> > diff --git a/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml b/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
> > new file mode 100644
> > index 000000000000..44541075d77f
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
> > @@ -0,0 +1,86 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +# Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/arm/coresight-tpdm.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Trace, Profiling and Diagnostics Monitor - TPDM
> > +
> > +description: |
> > +  The TPDM or Monitor serves as data collection component for various dataset
> > +  types specified in the QPMDA spec. It covers Basic Counts (BC), Tenure
> > +  Counts (TC), Continuous Multi-Bit (CMB), and Discrete Single Bit (DSB). It
> > +  performs data collection in the data producing clock domain and transfers it
> > +  to the data collection time domain, generally ATB clock domain.
> > +
> > +  The primary use case of the TPDM is to collect data from different data
> > +  sources and send it to a TPDA for packetization, timestamping, and funneling.
> > +
> > +maintainers:
> > +  - Tao Zhang <quic_taozha@quicinc.com>
> > +
> > +properties:
> > +  $nodename:
> > +    pattern: "^tpdm(@[0-9a-f]+)$"
> > +  compatible:
> > +    items:
> > +      - const: arm,primecell
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  reg-names:
> > +    items:
> > +      - const: tpdm-base
> > +
> > +  atid:
> > +    maxItems: 1
> > +    description: |
> > +      The QPMDA specification repurposed the ATID field of the AMBA ATB
> > +      specification to use it to convey packetization information to the
> > +      Aggregator.
> > +
> > +  out-ports:
> > +    description: |
> > +      Output connections from the TPDM to legacy CoreSight trace bus.
> > +    $ref: /schemas/graph.yaml#/properties/ports
> > +    properties:
> > +      port:
> > +        description: Output connection from the TPDM to legacy CoreSight
> > +          Trace bus.
> > +        $ref: /schemas/graph.yaml#/properties/port
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - reg-names
> > +  - atid
> > +  - clocks
> > +  - clock-names
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > +  # minimum TPDM definition.
> > +  - |
> > +    tpdm@6980000 {
> > +      compatible = "arm,primecell";
> > +      reg = <0x6980000 0x1000>;
> > +      reg-names = "tpdm-base";
> > +
> > +      clocks = <&aoss_qmp>;
> > +      clock-names = "apb_pclk";
> > +
> > +      atid = <78>;
> > +      out-ports {
> > +        port {
> > +          tpdm_turing_out_funnel_turing: endpoint {
> > +            remote-endpoint =
> > +              <&funnel_turing_in_tpdm_turing>;
> > +          };
> > +        };
> > +      };
> > +    };
> > +
> > +...
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index eeb4c70b3d5b..cabecf760488 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -15303,6 +15303,11 @@ L:	netdev@vger.kernel.org
> >  S:	Supported
> >  F:	drivers/net/ipa/
> >  
> > +QCOM CORESIGHT TPDM DRIVER
> > +M:	Tao Zhang <quic_taozha@quicinc.com>
> > +S:	Maintained
> > +F:	drivers/hwtracing/coresight/coresight-tpdm.c
> > +
> >  QEMU MACHINE EMULATOR AND VIRTUALIZER SUPPORT
> >  M:	Gabriel Somlo <somlo@cmu.edu>
> >  M:	"Michael S. Tsirkin" <mst@redhat.com>
> > diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
> > index f026e5c0e777..abe244a968f6 100644
> > --- a/drivers/hwtracing/coresight/Kconfig
> > +++ b/drivers/hwtracing/coresight/Kconfig
> > @@ -188,4 +188,13 @@ config CORESIGHT_TRBE
> >  
> >  	  To compile this driver as a module, choose M here: the module will be
> >  	  called coresight-trbe.
> > +
> > +config CORESIGHT_TPDM
> > +	tristate "CoreSight Trace, Profiling & Diagnostics Monitor driver"
> > +	select CORESIGHT_LINKS_AND_SINKS
> > +	help
> > +	  This driver provides support for configuring monitor. Monitors are
> > +	  primarily responsible for data set collection and support the
> > +	  ability to collect any permutation of data set types. Monitors are
> > +	  also responsible for interaction with system cross triggering.
> 
> You also need to indicate what the name of the module would be.  Please look at
> other extries in the Kconfig file for examples.


We will add module name here.

> 
> >  endif
> > diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
> > index b6c4a48140ec..e7392a0dddeb 100644
> > --- a/drivers/hwtracing/coresight/Makefile
> > +++ b/drivers/hwtracing/coresight/Makefile
> > @@ -25,5 +25,6 @@ obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o
> >  obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o
> >  obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
> >  obj-$(CONFIG_CORESIGHT_TRBE) += coresight-trbe.o
> > +obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o
> >  coresight-cti-y := coresight-cti-core.o	coresight-cti-platform.o \
> >  		   coresight-cti-sysfs.o
> > diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
> > new file mode 100644
> > index 000000000000..906776c859d6
> > --- /dev/null
> > +++ b/drivers/hwtracing/coresight/coresight-tpdm.c
> > @@ -0,0 +1,583 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/device.h>
> > +#include <linux/amba/bus.h>
> > +#include <linux/io.h>
> > +#include <linux/err.h>
> > +#include <linux/fs.h>
> > +#include <linux/bitmap.h>
> > +#include <linux/of.h>
> > +#include <linux/coresight.h>
> > +#include <linux/regulator/consumer.h>
> 
> These should be in alphabetical order.
> 

We will update the order.

> > +
> > +#include "coresight-priv.h"
> > +
> > +#define tpdm_writel(drvdata, val, off)	__raw_writel((val), drvdata->base + off)
> > +#define tpdm_readl(drvdata, off)		__raw_readl(drvdata->base + off)
> 
> writel/readl_relaxed() are defines for __raw_writel/readl and as such the above
> should not be needed.
>

 We will address your comments.
 
> > +
> > +#define TPDM_LOCK(drvdata)						\
> > +do {									\
> > +	mb(); /* ensure configuration take effect before we lock it */	\
> > +	tpdm_writel(drvdata, 0x0, CORESIGHT_LAR);			\
> > +} while (0)
> > +#define TPDM_UNLOCK(drvdata)						\
> > +do {									\
> > +	tpdm_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR);		\
> > +	mb(); /* ensure unlock take effect before we configure */	\
> > +} while (0)
> 
> That too isn't needed.  Simply use CS_LOCK/UNLOCK().
> 

We will address your comments.

> > +
> > +/* GPR Registers */
> > +#define TPDM_GPR_CR(n)		(0x0 + (n * 4))
> > +
> > +/* BC Subunit Registers */
> > +#define TPDM_BC_CR		(0x280)
> > +#define TPDM_BC_SATROLL		(0x284)
> > +#define TPDM_BC_CNTENSET	(0x288)
> > +#define TPDM_BC_CNTENCLR	(0x28C)
> > +#define TPDM_BC_INTENSET	(0x290)
> > +#define TPDM_BC_INTENCLR	(0x294)
> > +#define TPDM_BC_TRIG_LO(n)	(0x298 + (n * 4))
> > +#define TPDM_BC_TRIG_HI(n)	(0x318 + (n * 4))
> > +#define TPDM_BC_GANG		(0x398)
> > +#define TPDM_BC_OVERFLOW(n)	(0x39C + (n * 4))
> > +#define TPDM_BC_OVSR		(0x3C0)
> > +#define TPDM_BC_SELR		(0x3C4)
> > +#define TPDM_BC_CNTR_LO		(0x3C8)
> > +#define TPDM_BC_CNTR_HI		(0x3CC)
> > +#define TPDM_BC_SHADOW_LO(n)	(0x3D0 + (n * 4))
> > +#define TPDM_BC_SHADOW_HI(n)	(0x450 + (n * 4))
> > +#define TPDM_BC_SWINC		(0x4D0)
> > +#define TPDM_BC_MSR(n)		(0x4F0 + (n * 4))
> > +
> > +/* TC Subunit Registers */
> > +#define TPDM_TC_CR		(0x500)
> > +#define TPDM_TC_CNTENSET	(0x504)
> > +#define TPDM_TC_CNTENCLR	(0x508)
> > +#define TPDM_TC_INTENSET	(0x50C)
> > +#define TPDM_TC_INTENCLR	(0x510)
> > +#define TPDM_TC_TRIG_SEL(n)	(0x514 + (n * 4))
> > +#define TPDM_TC_TRIG_LO(n)	(0x534 + (n * 4))
> > +#define TPDM_TC_TRIG_HI(n)	(0x554 + (n * 4))
> > +#define TPDM_TC_OVSR_GP		(0x580)
> > +#define TPDM_TC_OVSR_IMPL	(0x584)
> > +#define TPDM_TC_SELR		(0x588)
> > +#define TPDM_TC_CNTR_LO		(0x58C)
> > +#define TPDM_TC_CNTR_HI		(0x590)
> > +#define TPDM_TC_SHADOW_LO(n)	(0x594 + (n * 4))
> > +#define TPDM_TC_SHADOW_HI(n)	(0x644 + (n * 4))
> > +#define TPDM_TC_SWINC		(0x700)
> > +#define TPDM_TC_MSR(n)		(0x768 + (n * 4))
> > +
> > +/* DSB Subunit Registers */
> > +#define TPDM_DSB_CR		(0x780)
> > +#define TPDM_DSB_TIER		(0x784)
> > +#define TPDM_DSB_TPR(n)		(0x788 + (n * 4))
> > +#define TPDM_DSB_TPMR(n)	(0x7A8 + (n * 4))
> > +#define TPDM_DSB_XPR(n)		(0x7C8 + (n * 4))
> > +#define TPDM_DSB_XPMR(n)	(0x7E8 + (n * 4))
> > +#define TPDM_DSB_EDCR(n)	(0x808 + (n * 4))
> > +#define TPDM_DSB_EDCMR(n)	(0x848 + (n * 4))
> > +#define TPDM_DSB_CA_SELECT(n)	(0x86c + (n * 4))
> > +#define TPDM_DSB_MSR(n)		(0x980 + (n * 4))
> > +
> > +/* CMB/MCMB Subunit Registers */
> > +#define TPDM_CMB_CR		(0xA00)
> > +#define TPDM_CMB_TIER		(0xA04)
> > +#define TPDM_CMB_TPR(n)		(0xA08 + (n * 4))
> > +#define TPDM_CMB_TPMR(n)	(0xA10 + (n * 4))
> > +#define TPDM_CMB_XPR(n)		(0xA18 + (n * 4))
> > +#define TPDM_CMB_XPMR(n)	(0xA20 + (n * 4))
> > +#define TPDM_CMB_MARKR		(0xA28)
> > +#define TPDM_CMB_READCTL	(0xA70)
> > +#define TPDM_CMB_READVAL	(0xA74)
> > +#define TPDM_CMB_MSR(n)		(0xA80 + (n * 4))
> > +
> > +/* TPDM Specific Registers */
> > +#define TPDM_ITATBCNTRL		(0xEF0)
> > +#define TPDM_CLK_CTRL		(0x220)
> > +#define TPDM_ITCNTRL		(0xF00)
> > +
> > +
> > +#define TPDM_DATASETS		32
> > +#define TPDM_BC_MAX_COUNTERS	32
> > +#define TPDM_BC_MAX_OVERFLOW	6
> > +#define TPDM_BC_MAX_MSR		4
> > +#define TPDM_TC_MAX_COUNTERS	44
> > +#define TPDM_TC_MAX_TRIG	8
> > +#define TPDM_TC_MAX_MSR		6
> > +#define TPDM_DSB_MAX_PATT	8
> > +#define TPDM_DSB_MAX_SELECT	8
> > +#define TPDM_DSB_MAX_MSR	32
> > +#define TPDM_DSB_MAX_EDCR	16
> > +#define TPDM_DSB_MAX_LINES	256
> > +#define TPDM_CMB_PATT_CMP	2
> > +#define TPDM_CMB_MAX_MSR	128
> > +#define TPDM_MCMB_MAX_LANES	8
> > +
> > +/* DSB programming modes */
> > +#define TPDM_DSB_MODE_CYCACC(val)	BMVAL(val, 0, 2)
> > +#define TPDM_DSB_MODE_PERF		BIT(3)
> > +#define TPDM_DSB_MODE_HPBYTESEL(val)	BMVAL(val, 4, 8)
> > +#define TPDM_MODE_ALL			(0xFFFFFFF)
> > +
> > +#define NUM_OF_BITS		32
> > +#define TPDM_GPR_REGS_MAX	160
> > +
> > +#define TPDM_TRACE_ID_START	128
> > +
> > +#define TPDM_REVISION_A		0
> > +#define TPDM_REVISION_B		1
> > +
> > +#define HW_ENABLE_CHECK_VALUE   0x10
> > +
> > +
> 
> Extra newline

We will address your comments.

> 
> > +#define ATBCNTRL_VAL_32		0xC00F1409
> > +#define ATBCNTRL_VAL_64		0xC01F1409
> > +
> 
> All of the above should be in a header file.
> 

We will move these to header file. 

> > +
> > +enum tpdm_dataset {
> > +	TPDM_DS_IMPLDEF,
> > +	TPDM_DS_DSB,
> > +	TPDM_DS_CMB,
> > +	TPDM_DS_TC,
> > +	TPDM_DS_BC,
> > +	TPDM_DS_GPR,
> > +	TPDM_DS_MCMB,
> > +};
> > +
> > +enum tpdm_mode {
> > +	TPDM_MODE_ATB,
> > +	TPDM_MODE_APB,
> > +};
> > +
> > +enum tpdm_support_type {
> > +	TPDM_SUPPORT_TYPE_FULL,
> > +	TPDM_SUPPORT_TYPE_PARTIAL,
> > +	TPDM_SUPPORT_TYPE_NO,
> > +};
> > +
> > +enum tpdm_cmb_patt_bits {
> > +	TPDM_CMB_LSB,
> > +	TPDM_CMB_MSB,
> > +};
> 
> These too.
> 

We will move these to header file.

> > +
> > +#ifdef CONFIG_CORESIGHT_TPDM_DEFAULT_ENABLE
> > +static int boot_enable = 1;
> > +#else
> > +static int boot_enable;
> > +#endif
> 
> That isn't the proper way to do this.  Look at how it is done in
> coresight-etm4x.c
> 
> > +
> > +struct gpr_dataset {
> > +	DECLARE_BITMAP(gpr_dirty, TPDM_GPR_REGS_MAX);
> > +	uint32_t		gp_regs[TPDM_GPR_REGS_MAX];
> 
> Shouldn't this be u32?
> 

uint32_t is the same as u32.
typedef u32			uint32_t;

> > +};
> > +
> > +struct bc_dataset {
> > +	enum tpdm_mode		capture_mode;
> > +	enum tpdm_mode		retrieval_mode;
> > +	uint32_t		sat_mode;
> > +	uint32_t		enable_counters;
> > +	uint32_t		clear_counters;
> > +	uint32_t		enable_irq;
> > +	uint32_t		clear_irq;
> > +	uint32_t		trig_val_lo[TPDM_BC_MAX_COUNTERS];
> > +	uint32_t		trig_val_hi[TPDM_BC_MAX_COUNTERS];
> > +	uint32_t		enable_ganging;
> > +	uint32_t		overflow_val[TPDM_BC_MAX_OVERFLOW];
> > +	uint32_t		msr[TPDM_BC_MAX_MSR];
> > +};
> > +
> > +struct tc_dataset {
> > +	enum tpdm_mode		capture_mode;
> > +	enum tpdm_mode		retrieval_mode;
> > +	bool			sat_mode;
> > +	uint32_t		enable_counters;
> > +	uint32_t		clear_counters;
> > +	uint32_t		enable_irq;
> > +	uint32_t		clear_irq;
> > +	uint32_t		trig_sel[TPDM_TC_MAX_TRIG];
> > +	uint32_t		trig_val_lo[TPDM_TC_MAX_TRIG];
> > +	uint32_t		trig_val_hi[TPDM_TC_MAX_TRIG];
> > +	uint32_t		msr[TPDM_TC_MAX_MSR];
> > +};
> > +
> > +struct dsb_dataset {
> > +	uint32_t		mode;
> > +	uint32_t		edge_ctrl[TPDM_DSB_MAX_EDCR];
> > +	uint32_t		edge_ctrl_mask[TPDM_DSB_MAX_EDCR / 2];
> > +	uint32_t		patt_val[TPDM_DSB_MAX_PATT];
> > +	uint32_t		patt_mask[TPDM_DSB_MAX_PATT];
> > +	bool			patt_ts;
> > +	bool			patt_type;
> > +	uint32_t		trig_patt_val[TPDM_DSB_MAX_PATT];
> > +	uint32_t		trig_patt_mask[TPDM_DSB_MAX_PATT];
> > +	bool			trig_ts;
> > +	bool			trig_type;
> > +	uint32_t		select_val[TPDM_DSB_MAX_SELECT];
> > +	uint32_t		msr[TPDM_DSB_MAX_MSR];
> > +};
> > +
> > +struct mcmb_dataset {
> > +	uint8_t		mcmb_trig_lane;
> > +	uint8_t		mcmb_lane_select;
> > +};
> > +
> > +struct cmb_dataset {
> > +	bool			trace_mode;
> > +	uint32_t		cycle_acc;
> > +	uint32_t		patt_val[TPDM_CMB_PATT_CMP];
> > +	uint32_t		patt_mask[TPDM_CMB_PATT_CMP];
> > +	bool			patt_ts;
> > +	uint32_t		trig_patt_val[TPDM_CMB_PATT_CMP];
> > +	uint32_t		trig_patt_mask[TPDM_CMB_PATT_CMP];
> > +	bool			trig_ts;
> > +	bool			ts_all;
> > +	uint32_t		msr[TPDM_CMB_MAX_MSR];
> > +	uint8_t			read_ctl_reg;
> > +	struct mcmb_dataset	*mcmb;
> > +};
> > +
> > +DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm");
> > +
> > +struct tpdm_drvdata {
> > +	void __iomem		*base;
> > +	struct device		*dev;
> > +	struct coresight_device	*csdev;
> > +	int			nr_tclk;
> > +	struct clk		**tclk;
> > +	int			nr_treg;
> > +	struct regulator	**treg;
> > +	struct mutex		lock;
> > +	bool			enable;
> > +	bool			clk_enable;
> > +	DECLARE_BITMAP(datasets, TPDM_DATASETS);
> > +	DECLARE_BITMAP(enable_ds, TPDM_DATASETS);
> > +	enum tpdm_support_type	tc_trig_type;
> > +	enum tpdm_support_type	bc_trig_type;
> > +	enum tpdm_support_type	bc_gang_type;
> > +	uint32_t		bc_counters_avail;
> > +	uint32_t		tc_counters_avail;
> > +	struct gpr_dataset	*gpr;
> > +	struct bc_dataset	*bc;
> > +	struct tc_dataset	*tc;
> > +	struct dsb_dataset	*dsb;
> > +	struct cmb_dataset	*cmb;
> > +	int			traceid;
> > +	uint32_t		version;
> > +	bool			msr_support;
> > +	bool			msr_fix_req;
> > +	bool			cmb_msr_skip;
> > +};
> 
> All of these should also be in a header file and properly documented.
> 

We will move these to header file and add the documentation.

> > +
> > +static void tpdm_init_default_data(struct tpdm_drvdata *drvdata);
> 
> This isn't needed.

We will remove this.
> 
> > +
> > +static void __tpdm_enable(struct tpdm_drvdata *drvdata)
> > +{
> > +	TPDM_UNLOCK(drvdata);
> > +
> > +	if (drvdata->clk_enable)
> > +		tpdm_writel(drvdata, 0x1, TPDM_CLK_CTRL);
> > +
> > +	TPDM_LOCK(drvdata);
> > +}
> > +
> > +static int tpdm_enable(struct coresight_device *csdev,
> > +		       struct perf_event *event, u32 mode)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> > +	int ret = 0;
> > +
> > +	if (drvdata->enable) {
> 
> This a race condition.
>

Need to add it to mutex_lock. We will update.
 
> > +		dev_err(drvdata->dev,
> > +			"TPDM setup already enabled,Skipping enablei\n");
> 
> Please remove this.
> 

We will remove this.

> > +		return ret;
> 
> Shouldn't this be return -EBUSY? 

We will address your comments.

> 
> > +	}
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	__tpdm_enable(drvdata);
> > +	drvdata->enable = true;
> > +	mutex_unlock(&drvdata->lock);
> > +
> > +	dev_info(drvdata->dev, "TPDM tracing enabled\n");
> > +	return 0;
> > +}
> > +
> > +static void __tpdm_disable(struct tpdm_drvdata *drvdata)
> > +{
> > +	TPDM_UNLOCK(drvdata);
> > +
> > +	if (drvdata->clk_enable)
> > +		tpdm_writel(drvdata, 0x0, TPDM_CLK_CTRL);
> > +
> > +	TPDM_LOCK(drvdata);
> > +}
> > +
> > +static void tpdm_disable(struct coresight_device *csdev,
> > +			 struct perf_event *event)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> > +
> > +	if (!drvdata->enable) {
> > +		dev_err(drvdata->dev,
> > +			"TPDM setup already disabled, Skipping disable\n");
> > +		return;
> > +	}
> > +	mutex_lock(&drvdata->lock);
> > +	__tpdm_disable(drvdata);
> > +	drvdata->enable = false;
> > +	mutex_unlock(&drvdata->lock);
> 
> Same comments as above.
> 

We will address your comments.

> > +
> > +	dev_info(drvdata->dev, "TPDM tracing disabled\n");
> > +}
> > +
> > +static int tpdm_trace_id(struct coresight_device *csdev)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> > +
> > +	return drvdata->traceid;
> > +}
> > +
> > +static const struct coresight_ops_source tpdm_source_ops = {
> > +	.trace_id	= tpdm_trace_id,
> > +	.enable		= tpdm_enable,
> > +	.disable	= tpdm_disable,
> > +};
> > +
> > +static const struct coresight_ops tpdm_cs_ops = {
> > +	.source_ops	= &tpdm_source_ops,
> > +};
> > +
> > +static int tpdm_datasets_alloc(struct tpdm_drvdata *drvdata)
> > +{
> > +	if (test_bit(TPDM_DS_GPR, drvdata->datasets)) {
> > +		drvdata->gpr = devm_kzalloc(drvdata->dev, sizeof(*drvdata->gpr),
> > +					    GFP_KERNEL);
> > +		if (!drvdata->gpr)
> > +			return -ENOMEM;
> > +	}
> > +	if (test_bit(TPDM_DS_BC, drvdata->datasets)) {
> > +		drvdata->bc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->bc),
> > +					   GFP_KERNEL);
> > +		if (!drvdata->bc)
> > +			return -ENOMEM;
> > +	}
> > +	if (test_bit(TPDM_DS_TC, drvdata->datasets)) {
> > +		drvdata->tc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->tc),
> > +					   GFP_KERNEL);
> > +		if (!drvdata->tc)
> > +			return -ENOMEM;
> > +	}
> > +	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
> > +		drvdata->dsb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->dsb),
> > +					    GFP_KERNEL);
> > +		if (!drvdata->dsb)
> > +			return -ENOMEM;
> > +	}
> > +	if (test_bit(TPDM_DS_CMB, drvdata->datasets)) {
> > +		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
> > +					    GFP_KERNEL);
> > +		if (!drvdata->cmb)
> > +			return -ENOMEM;
> > +	} else if (test_bit(TPDM_DS_MCMB, drvdata->datasets)) {
> > +		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
> > +					    GFP_KERNEL);
> > +		if (!drvdata->cmb)
> > +			return -ENOMEM;
> > +		drvdata->cmb->mcmb = devm_kzalloc(drvdata->dev,
> > +						  sizeof(*drvdata->cmb->mcmb),
> > +						  GFP_KERNEL);
> > +		if (!drvdata->cmb->mcmb)
> > +			return -ENOMEM;
> 
> How can I understand what the above does when:
> 
> 1) There isn't a single line of comments.
> 2) I don't know the HW.
> 3) I don't have access to the documentation.
> 

We will add comments here.

> > +	}
> > +	return 0;
> > +}
> > +
> > +static void tpdm_init_default_data(struct tpdm_drvdata *drvdata)
> > +{
> > +	if (test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		drvdata->bc->retrieval_mode = TPDM_MODE_ATB;
> > +
> > +	if (test_bit(TPDM_DS_TC, drvdata->datasets))
> > +		drvdata->tc->retrieval_mode = TPDM_MODE_ATB;
> > +
> > +	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
> > +		drvdata->dsb->trig_ts = true;
> > +		drvdata->dsb->trig_type = false;
> > +	}
> > +
> > +	if (test_bit(TPDM_DS_CMB, drvdata->datasets) ||
> > +	    test_bit(TPDM_DS_MCMB, drvdata->datasets))
> > +		drvdata->cmb->trig_ts = true;
> > +}
> > +
> > +static int tpdm_parse_of_data(struct tpdm_drvdata *drvdata)
> > +{
> > +	int i, ret;
> > +	const char *tclk_name, *treg_name;
> > +	struct device_node *node = drvdata->dev->of_node;
> > +
> > +	drvdata->clk_enable = of_property_read_bool(node, "qcom,clk-enable");
> > +	drvdata->msr_fix_req = of_property_read_bool(node, "qcom,msr-fix-req");
> > +	drvdata->cmb_msr_skip = of_property_read_bool(node,
> > +					"qcom,cmb-msr-skip");
> > +
> > +	drvdata->nr_tclk = of_property_count_strings(node, "qcom,tpdm-clks");
> > +	if (drvdata->nr_tclk > 0) {
> > +		drvdata->tclk = devm_kzalloc(drvdata->dev, drvdata->nr_tclk *
> > +					     sizeof(*drvdata->tclk),
> > +					     GFP_KERNEL);
> > +		if (!drvdata->tclk)
> > +			return -ENOMEM;
> > +
> > +		for (i = 0; i < drvdata->nr_tclk; i++) {
> > +			ret = of_property_read_string_index(node,
> > +					    "qcom,tpdm-clks", i, &tclk_name);
> > +			if (ret)
> > +				return ret;
> > +
> > +			drvdata->tclk[i] = devm_clk_get(drvdata->dev,
> > +							tclk_name);
> > +			if (IS_ERR(drvdata->tclk[i]))
> > +				return PTR_ERR(drvdata->tclk[i]);
> > +		}
> > +	}
> > +
> > +	drvdata->nr_treg = of_property_count_strings(node, "qcom,tpdm-regs");
> > +	if (drvdata->nr_treg > 0) {
> > +		drvdata->treg = devm_kzalloc(drvdata->dev, drvdata->nr_treg *
> > +					     sizeof(*drvdata->treg),
> > +					     GFP_KERNEL);
> > +		if (!drvdata->treg)
> > +			return -ENOMEM;
> > +
> > +		for (i = 0; i < drvdata->nr_treg; i++) {
> > +			ret = of_property_read_string_index(node,
> > +					    "qcom,tpdm-regs", i, &treg_name);
> > +			if (ret)
> > +				return ret;
> > +
> > +			drvdata->treg[i] = devm_regulator_get(drvdata->dev,
> > +							treg_name);
> > +			if (IS_ERR(drvdata->treg[i]))
> > +				return PTR_ERR(drvdata->treg[i]);
> > +		}
> > +	}
> 
> _None_ of the above are defined in the yaml file and/or part of the example that
> is shown there.  Moreover they don't appear in patch 10/10 where TPDM and TPDA are
> supposed to be introduced.  I will comment on patch 10/10 when I'm done with
> this one.
> 

We will update the dtsi change in next version.

> > +
> > +	return 0;
> > +}
> > +
> > +static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
> > +{
> > +	int ret, i;
> > +	uint32_t pidr, devid;
> > +	struct device *dev = &adev->dev;
> > +	struct coresight_platform_data *pdata;
> > +	struct tpdm_drvdata *drvdata;
> > +	struct coresight_desc desc = { 0 };
> > +	static int traceid = TPDM_TRACE_ID_START;
> > +	uint32_t version;
> > +
> > +	desc.name = coresight_alloc_device_name(&tpdm_devs, dev);
> > +	if (!desc.name)
> > +		return -ENOMEM;
> > +	pdata = coresight_get_platform_data(dev);
> > +	if (IS_ERR(pdata))
> > +		return PTR_ERR(pdata);
> > +	adev->dev.platform_data = pdata;
> > +
> > +	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> > +	if (!drvdata)
> > +		return -ENOMEM;
> > +	drvdata->dev = &adev->dev;
> > +	dev_set_drvdata(dev, drvdata);
> > +
> > +	drvdata->base = devm_ioremap_resource(dev, &adev->res);
> > +	if (!drvdata->base)
> > +		return -ENOMEM;
> > +
> > +	mutex_init(&drvdata->lock);
> > +
> > +	ret = tpdm_parse_of_data(drvdata);
> > +	if (ret) {
> > +		dev_err(drvdata->dev, "TPDM parse of data fail\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	desc.type = CORESIGHT_DEV_TYPE_SOURCE;
> > +	desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
> 
> Why is this of subtype CORESIGHT_DEV_SUBTYPE_SOURCE_PROC when TPDMs are not
> associated with a CPU?  Here we should probably introduce something like
> CORESIGHT_DEV_SUBTYPE_SOURCE_SYS


We will check and update.

> 
> > +	desc.ops = &tpdm_cs_ops;
> > +	desc.pdata = adev->dev.platform_data;
> > +	desc.dev = &adev->dev;
> > +	drvdata->csdev = coresight_register(&desc);
> > +	if (IS_ERR(drvdata->csdev))
> > +		return PTR_ERR(drvdata->csdev);
> > +
> > +	version = tpdm_readl(drvdata, CORESIGHT_PERIPHIDR2);
> > +	drvdata->version = BMVAL(version, 4, 7);
> > +
> 
> What is MSR support?  This should be documented.  Looking more closely at this
> patch, not a single line of documentation is provided.  If you look at other
> drivers, you will find things to be quite different.  The coresight subsystem
> has become very complex and documentation helps us, and anyone trying to
> contribute, understand what the code does.
> 

We will add more documentation in the driver.


> > +	if (drvdata->version)
> > +		drvdata->msr_support = true;
> > +
> > +	pidr = tpdm_readl(drvdata, CORESIGHT_PERIPHIDR0);
> > +	for (i = 0; i < TPDM_DATASETS; i++) {
> > +		if (pidr & BIT(i)) {
> > +			__set_bit(i, drvdata->datasets);
> > +			__set_bit(i, drvdata->enable_ds);
> > +		}
> > +	}
> > +
> > +	ret = tpdm_datasets_alloc(drvdata);
> > +	if (ret) {
> > +		coresight_unregister(drvdata->csdev);
> > +		return ret;
> > +	}
> > +
> > +	tpdm_init_default_data(drvdata);
> > +
> > +	devid = tpdm_readl(drvdata, CORESIGHT_DEVID);
> > +	drvdata->tc_trig_type = BMVAL(devid, 27, 28);
> > +	drvdata->bc_trig_type = BMVAL(devid, 25, 26);
> > +	drvdata->bc_gang_type = BMVAL(devid, 23, 24);
> > +	drvdata->bc_counters_avail = BMVAL(devid, 6, 10) + 1;
> > +	drvdata->tc_counters_avail = BMVAL(devid, 4, 5) + 1;
> 
> Shouldn't this be part of tpdm_init_default_data()?
> 

We will address your comments.

> > +
> > +	drvdata->traceid = traceid++;
> 
> For this to work a new function needs to be introduced in coresight-pmu.h.
> Something like:
> 
> static inline int coresight_get_system_trace_id(int id)
> {
>         /* Start system IDs above the highest per CPU trace ID. */
>         return coresigth_get_trace_id(cpumask_last(cpu_possible_mask) + 1);
> }
> 

We will address your comments.

> > +
> > +	dev_dbg(drvdata->dev, "TPDM initialized\n");
> 
> Please remove this.
>

We will remove it.
 
> > +
> > +	if (boot_enable)
> > +		coresight_enable(drvdata->csdev);
> > +
> > +	pm_runtime_put(&adev->dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static struct amba_id tpdm_ids[] = {
> > +	{
> > +		.id     = 0x001f0e00,
> > +		.mask   = 0x00ffff00,
> 
> Any way we can use CS_AMBA_ID() here?
> 

We will address your comments.

> > +		.data	= "TPDM",
> 
> What is .data used for?

It is just to fill the structure. We will remove it if it is not necessary.

> 
> > +	},
> > +	{ 0, 0},
> > +};
> > +
> > +static struct amba_driver tpdm_driver = {
> > +	.drv = {
> > +		.name   = "coresight-tpdm",
> > +		.owner	= THIS_MODULE,
> > +		.suppress_bind_attrs = true,
> > +	},
> > +	.probe          = tpdm_probe,
> > +	.id_table	= tpdm_ids,
> > +};
> > +
> > +module_amba_driver(tpdm_driver);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Monitor driver");
> > -- 
> > 2.17.1
> > 

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

* Re: [PATCH 03/10] Coresight: Add driver to support Coresight device TPDM
  2021-11-02 17:59   ` Mathieu Poirier
  2021-11-04  8:56     ` Jinlong
@ 2021-11-04  9:37     ` Suzuki K Poulose
  2021-11-05  8:12       ` Jinlong
  1 sibling, 1 reply; 37+ messages in thread
From: Suzuki K Poulose @ 2021-11-04  9:37 UTC (permalink / raw)
  To: Mathieu Poirier, Tao Zhang
  Cc: Alexander Shishkin, Mike Leach, Leo Yan, Greg Kroah-Hartman,
	coresight, linux-arm-kernel, linux-kernel, Tingwei Zhang,
	Mao Jinlong, Yuanfang Zhang, Trilok Soni

Tao,

Some additional comments below.

On 02/11/2021 17:59, Mathieu Poirier wrote:
> Good morning,
> 
> 
> On Thu, Oct 21, 2021 at 03:38:49PM +0800, Tao Zhang wrote:
>> Add driver to support Coresight device TPDM. This driver provides
>> support for configuring monitor. Monitors are primarily responsible
>> for data set collection and support the ability to collect any
>> permutation of data set types. Monitors are also responsible for
>> interaction with system cross triggering.
> 
> As far as I can tell there is nothing related to CTIs in this patch.  And if
> there is, it is not documented.
> 

Please could you add a separate file documenting  the TPDM and
some of the specific details (what is used by the driver, e.g
PERPHID0/1 et) under Documentation/trace/coresight/ , in a separate
patch

>>
>> Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
>> ---
>>   .../bindings/arm/coresight-tpdm.yaml          |  86 +++
> 
> As checkpatch says, this should be in a separate file.
> 
>>   MAINTAINERS                                   |   5 +
> 
> Since this is a coresight device Suzuki and I will continue the maintenance.
> The get_maintainer script will make sure you care CC'ed on patches related to
> the TPDM/TPDA drivers, and we would typically requried a "Reviewed-by" tag from
> you before merging.
> 
>>   drivers/hwtracing/coresight/Kconfig           |   9 +
>>   drivers/hwtracing/coresight/Makefile          |   1 +
>>   drivers/hwtracing/coresight/coresight-tpdm.c  | 583 ++++++++++++++++++
>>   5 files changed, 684 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
>>   create mode 100644 drivers/hwtracing/coresight/coresight-tpdm.c
>>
>> diff --git a/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml b/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
>> new file mode 100644
>> index 000000000000..44541075d77f
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
>> @@ -0,0 +1,86 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +# Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/arm/coresight-tpdm.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Trace, Profiling and Diagnostics Monitor - TPDM
>> +
>> +description: |
>> +  The TPDM or Monitor serves as data collection component for various dataset
>> +  types specified in the QPMDA spec. It covers Basic Counts (BC), Tenure
>> +  Counts (TC), Continuous Multi-Bit (CMB), and Discrete Single Bit (DSB). It
>> +  performs data collection in the data producing clock domain and transfers it
>> +  to the data collection time domain, generally ATB clock domain.
>> +
>> +  The primary use case of the TPDM is to collect data from different data
>> +  sources and send it to a TPDA for packetization, timestamping, and funneling.
>> +
>> +maintainers:
>> +  - Tao Zhang <quic_taozha@quicinc.com>
>> +
>> +properties:
>> +  $nodename:
>> +    pattern: "^tpdm(@[0-9a-f]+)$"
>> +  compatible:
>> +    items:
>> +      - const: arm,primecell


You must have a compatible that identifies this as "tpdm", just like
the other components, even though it is not functional.

>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  reg-names:
>> +    items:
>> +      - const: tpdm-base

Is the reg-name really necessary ?

>> +
>> +  atid:
>> +    maxItems: 1
>> +    description: |
>> +      The QPMDA specification repurposed the ATID field of the AMBA ATB
>> +      specification to use it to convey packetization information to the
>> +      Aggregator.

Please could you describe how this affects the device in the doc
requested above.

>> +
>> +  out-ports:
>> +    description: |
>> +      Output connections from the TPDM to legacy CoreSight trace bus.
>> +    $ref: /schemas/graph.yaml#/properties/ports
>> +    properties:
>> +      port:
>> +        description: Output connection from the TPDM to legacy CoreSight
>> +          Trace bus.
>> +        $ref: /schemas/graph.yaml#/properties/port
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - reg-names
>> +  - atid
>> +  - clocks
>> +  - clock-names
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> +  # minimum TPDM definition.
>> +  - |
>> +    tpdm@6980000 {
>> +      compatible = "arm,primecell";

Like other components, we must have :
	  compatible = "qcom,<new-compatible>", "arm,primecell";

>> +      reg = <0x6980000 0x1000>;
>> +      reg-names = "tpdm-base";
>> +
>> +      clocks = <&aoss_qmp>;
>> +      clock-names = "apb_pclk";
>> +
>> +      atid = <78>;
>> +      out-ports {
>> +        port {
>> +          tpdm_turing_out_funnel_turing: endpoint {
>> +            remote-endpoint =
>> +              <&funnel_turing_in_tpdm_turing>;
>> +          };
>> +        };
>> +      };
>> +    };
>> +
>> +...

>> diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
>> index b6c4a48140ec..e7392a0dddeb 100644
>> --- a/drivers/hwtracing/coresight/Makefile
>> +++ b/drivers/hwtracing/coresight/Makefile
>> @@ -25,5 +25,6 @@ obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o
>>   obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o
>>   obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
>>   obj-$(CONFIG_CORESIGHT_TRBE) += coresight-trbe.o
>> +obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o
>>   coresight-cti-y := coresight-cti-core.o	coresight-cti-platform.o \
>>   		   coresight-cti-sysfs.o
>> diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
>> new file mode 100644
>> index 000000000000..906776c859d6
>> --- /dev/null
>> +++ b/drivers/hwtracing/coresight/coresight-tpdm.c

...

>> +
>> +#ifdef CONFIG_CORESIGHT_TPDM_DEFAULT_ENABLE
>> +static int boot_enable = 1;
>> +#else
>> +static int boot_enable;
>> +#endif
> 
> That isn't the proper way to do this.  Look at how it is done in
> coresight-etm4x.c
> 
>> +
>> +struct gpr_dataset {
>> +	DECLARE_BITMAP(gpr_dirty, TPDM_GPR_REGS_MAX);
>> +	uint32_t		gp_regs[TPDM_GPR_REGS_MAX];
> 
> Shouldn't this be u32?
> 
>> +};
>> +
>> +struct bc_dataset {
>> +	enum tpdm_mode		capture_mode;
>> +	enum tpdm_mode		retrieval_mode;
>> +	uint32_t		sat_mode;
>> +	uint32_t		enable_counters;
>> +	uint32_t		clear_counters;
>> +	uint32_t		enable_irq;
>> +	uint32_t		clear_irq;
>> +	uint32_t		trig_val_lo[TPDM_BC_MAX_COUNTERS];
>> +	uint32_t		trig_val_hi[TPDM_BC_MAX_COUNTERS];
>> +	uint32_t		enable_ganging;
>> +	uint32_t		overflow_val[TPDM_BC_MAX_OVERFLOW];
>> +	uint32_t		msr[TPDM_BC_MAX_MSR];
>> +};
>> +
>> +struct tc_dataset {
>> +	enum tpdm_mode		capture_mode;
>> +	enum tpdm_mode		retrieval_mode;
>> +	bool			sat_mode;
>> +	uint32_t		enable_counters;
>> +	uint32_t		clear_counters;
>> +	uint32_t		enable_irq;
>> +	uint32_t		clear_irq;
>> +	uint32_t		trig_sel[TPDM_TC_MAX_TRIG];
>> +	uint32_t		trig_val_lo[TPDM_TC_MAX_TRIG];
>> +	uint32_t		trig_val_hi[TPDM_TC_MAX_TRIG];
>> +	uint32_t		msr[TPDM_TC_MAX_MSR];
>> +};
>> +
>> +struct dsb_dataset {
>> +	uint32_t		mode;
>> +	uint32_t		edge_ctrl[TPDM_DSB_MAX_EDCR];
>> +	uint32_t		edge_ctrl_mask[TPDM_DSB_MAX_EDCR / 2];
>> +	uint32_t		patt_val[TPDM_DSB_MAX_PATT];
>> +	uint32_t		patt_mask[TPDM_DSB_MAX_PATT];
>> +	bool			patt_ts;
>> +	bool			patt_type;
>> +	uint32_t		trig_patt_val[TPDM_DSB_MAX_PATT];
>> +	uint32_t		trig_patt_mask[TPDM_DSB_MAX_PATT];
>> +	bool			trig_ts;
>> +	bool			trig_type;
>> +	uint32_t		select_val[TPDM_DSB_MAX_SELECT];
>> +	uint32_t		msr[TPDM_DSB_MAX_MSR];
>> +};
>> +
>> +struct mcmb_dataset {
>> +	uint8_t		mcmb_trig_lane;
>> +	uint8_t		mcmb_lane_select;
>> +};
>> +
>> +struct cmb_dataset {
>> +	bool			trace_mode;
>> +	uint32_t		cycle_acc;
>> +	uint32_t		patt_val[TPDM_CMB_PATT_CMP];
>> +	uint32_t		patt_mask[TPDM_CMB_PATT_CMP];
>> +	bool			patt_ts;
>> +	uint32_t		trig_patt_val[TPDM_CMB_PATT_CMP];
>> +	uint32_t		trig_patt_mask[TPDM_CMB_PATT_CMP];
>> +	bool			trig_ts;
>> +	bool			ts_all;
>> +	uint32_t		msr[TPDM_CMB_MAX_MSR];
>> +	uint8_t			read_ctl_reg;
>> +	struct mcmb_dataset	*mcmb;
>> +};
>> +
>> +DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm");
>> +
>> +struct tpdm_drvdata {
>> +	void __iomem		*base;
>> +	struct device		*dev;
>> +	struct coresight_device	*csdev;
>> +	int			nr_tclk;
>> +	struct clk		**tclk;
>> +	int			nr_treg;
>> +	struct regulator	**treg;
>> +	struct mutex		lock;
>> +	bool			enable;
>> +	bool			clk_enable;
>> +	DECLARE_BITMAP(datasets, TPDM_DATASETS);
>> +	DECLARE_BITMAP(enable_ds, TPDM_DATASETS);
>> +	enum tpdm_support_type	tc_trig_type;
>> +	enum tpdm_support_type	bc_trig_type;
>> +	enum tpdm_support_type	bc_gang_type;
>> +	uint32_t		bc_counters_avail;
>> +	uint32_t		tc_counters_avail;
>> +	struct gpr_dataset	*gpr;
>> +	struct bc_dataset	*bc;
>> +	struct tc_dataset	*tc;
>> +	struct dsb_dataset	*dsb;
>> +	struct cmb_dataset	*cmb;
>> +	int			traceid;
>> +	uint32_t		version;
>> +	bool			msr_support;
>> +	bool			msr_fix_req;
>> +	bool			cmb_msr_skip;
>> +};
> 
> All of these should also be in a header file and properly documented.
> 
>> +
>> +static void tpdm_init_default_data(struct tpdm_drvdata *drvdata);
> 
> This isn't needed.
> 
>> +
>> +static void __tpdm_enable(struct tpdm_drvdata *drvdata)
>> +{
>> +	TPDM_UNLOCK(drvdata);
>> +
>> +	if (drvdata->clk_enable)
>> +		tpdm_writel(drvdata, 0x1, TPDM_CLK_CTRL);
>> +
>> +	TPDM_LOCK(drvdata);
>> +}
>> +

>> +static const struct coresight_ops_source tpdm_source_ops = {
>> +	.trace_id	= tpdm_trace_id,
>> +	.enable		= tpdm_enable,
>> +	.disable	= tpdm_disable,
>> +};
>> +
>> +static const struct coresight_ops tpdm_cs_ops = {
>> +	.source_ops	= &tpdm_source_ops,
>> +};
>> +
>> +static int tpdm_datasets_alloc(struct tpdm_drvdata *drvdata)
>> +{
>> +	if (test_bit(TPDM_DS_GPR, drvdata->datasets)) {
>> +		drvdata->gpr = devm_kzalloc(drvdata->dev, sizeof(*drvdata->gpr),
>> +					    GFP_KERNEL);
>> +		if (!drvdata->gpr)
>> +			return -ENOMEM;
>> +	}
>> +	if (test_bit(TPDM_DS_BC, drvdata->datasets)) {
>> +		drvdata->bc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->bc),
>> +					   GFP_KERNEL);
>> +		if (!drvdata->bc)
>> +			return -ENOMEM;
>> +	}
>> +	if (test_bit(TPDM_DS_TC, drvdata->datasets)) {
>> +		drvdata->tc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->tc),
>> +					   GFP_KERNEL);
>> +		if (!drvdata->tc)
>> +			return -ENOMEM;
>> +	}
>> +	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
>> +		drvdata->dsb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->dsb),
>> +					    GFP_KERNEL);
>> +		if (!drvdata->dsb)
>> +			return -ENOMEM;
>> +	}
>> +	if (test_bit(TPDM_DS_CMB, drvdata->datasets)) {
>> +		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
>> +					    GFP_KERNEL);
>> +		if (!drvdata->cmb)
>> +			return -ENOMEM;
>> +	} else if (test_bit(TPDM_DS_MCMB, drvdata->datasets)) {
>> +		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
>> +					    GFP_KERNEL);
>> +		if (!drvdata->cmb)
>> +			return -ENOMEM;
>> +		drvdata->cmb->mcmb = devm_kzalloc(drvdata->dev,
>> +						  sizeof(*drvdata->cmb->mcmb),
>> +						  GFP_KERNEL);
>> +		if (!drvdata->cmb->mcmb)
>> +			return -ENOMEM;
> 
> How can I understand what the above does when:
> 
> 1) There isn't a single line of comments.
> 2) I don't know the HW.
> 3) I don't have access to the documentation.

+1

> 
>> +	}
>> +	return 0;
>> +}
>> +
>> +static void tpdm_init_default_data(struct tpdm_drvdata *drvdata)
>> +{
>> +	if (test_bit(TPDM_DS_BC, drvdata->datasets))
>> +		drvdata->bc->retrieval_mode = TPDM_MODE_ATB;
>> +
>> +	if (test_bit(TPDM_DS_TC, drvdata->datasets))
>> +		drvdata->tc->retrieval_mode = TPDM_MODE_ATB;
>> +
>> +	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
>> +		drvdata->dsb->trig_ts = true;
>> +		drvdata->dsb->trig_type = false;
>> +	}
>> +
>> +	if (test_bit(TPDM_DS_CMB, drvdata->datasets) ||
>> +	    test_bit(TPDM_DS_MCMB, drvdata->datasets))
>> +		drvdata->cmb->trig_ts = true;
>> +}
>> +
>> +static int tpdm_parse_of_data(struct tpdm_drvdata *drvdata)
>> +{
>> +	int i, ret;
>> +	const char *tclk_name, *treg_name;
>> +	struct device_node *node = drvdata->dev->of_node;
>> +
>> +	drvdata->clk_enable = of_property_read_bool(node, "qcom,clk-enable");
>> +	drvdata->msr_fix_req = of_property_read_bool(node, "qcom,msr-fix-req");
>> +	drvdata->cmb_msr_skip = of_property_read_bool(node,
>> +					"qcom,cmb-msr-skip");
>> +

These properties must be listed as optional/mandatory in the DT binding
with proper description.

>> +	drvdata->nr_tclk = of_property_count_strings(node, "qcom,tpdm-clks");
>> +	if (drvdata->nr_tclk > 0) {
>> +		drvdata->tclk = devm_kzalloc(drvdata->dev, drvdata->nr_tclk *
>> +					     sizeof(*drvdata->tclk),
>> +					     GFP_KERNEL);
>> +		if (!drvdata->tclk)
>> +			return -ENOMEM;
>> +
>> +		for (i = 0; i < drvdata->nr_tclk; i++) {
>> +			ret = of_property_read_string_index(node,
>> +					    "qcom,tpdm-clks", i, &tclk_name);
>> +			if (ret)
>> +				return ret;
>> +
>> +			drvdata->tclk[i] = devm_clk_get(drvdata->dev,
>> +							tclk_name);
>> +			if (IS_ERR(drvdata->tclk[i]))
>> +				return PTR_ERR(drvdata->tclk[i]);
>> +		}
>> +	}
>> +
>> +	drvdata->nr_treg = of_property_count_strings(node, "qcom,tpdm-regs");

Where is this documented in the DT ?

>> +	if (drvdata->nr_treg > 0) {
>> +		drvdata->treg = devm_kzalloc(drvdata->dev, drvdata->nr_treg *
>> +					     sizeof(*drvdata->treg),
>> +					     GFP_KERNEL);
>> +		if (!drvdata->treg)
>> +			return -ENOMEM;
>> +
>> +		for (i = 0; i < drvdata->nr_treg; i++) {
>> +			ret = of_property_read_string_index(node,
>> +					    "qcom,tpdm-regs", i, &treg_name);
>> +			if (ret)
>> +				return ret;
>> +
>> +			drvdata->treg[i] = devm_regulator_get(drvdata->dev,
>> +							treg_name);
>> +			if (IS_ERR(drvdata->treg[i]))
>> +				return PTR_ERR(drvdata->treg[i]);
>> +		}
>> +	}
> 
> _None_ of the above are defined in the yaml file and/or part of the example that
> is shown there.  Moreover they don't appear in patch 10/10 where TPDM and TPDA are
> supposed to be introduced.  I will comment on patch 10/10 when I'm done with
> this one.

+1

Suzuki

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

* Re: [PATCH 10/10] ARM: dts: msm: Add TPDA and TPDM support to DTS for RB5
  2021-10-21  7:38 ` [PATCH 10/10] ARM: dts: msm: Add TPDA and TPDM support to DTS for RB5 Tao Zhang
  2021-11-02 18:02   ` Mathieu Poirier
@ 2021-11-04  9:45   ` Suzuki K Poulose
  2021-11-05  8:07     ` Jinlong
  1 sibling, 1 reply; 37+ messages in thread
From: Suzuki K Poulose @ 2021-11-04  9:45 UTC (permalink / raw)
  To: Tao Zhang, Mathieu Poirier, Alexander Shishkin
  Cc: Mike Leach, Leo Yan, Greg Kroah-Hartman, coresight,
	linux-arm-kernel, linux-kernel, Tingwei Zhang, Mao Jinlong,
	Yuanfang Zhang, Trilok Soni

On 21/10/2021 08:38, Tao Zhang wrote:
> Add TPDA and TPDM support to DTS for RB5 board. This change is a
> sample for validating. After applying this patch, the new TPDM and
> TPDA nodes can be observed at the coresight devices path. TPDM and
> TPDA hardware can be operated by commands.
> 
> List the commands for validating this series patches as below.
> echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> echo 1 > /sys/bus/coresight/devices/tpdm0/enable_source
> echo 1 > /sys/bus/coresight/devices/tpdm0/integration_test
> echo 2 > /sys/bus/coresight/devices/tpdm0/integration_test
> cat /dev/tmc_etf0 > /data/etf-tpdm0.bin
> echo 0 > /sys/bus/coresight/devices/tpdm0/enable_source
> echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> echo 1 > /sys/bus/coresight/devices/tpdm1/enable_source
> echo 1 > /sys/bus/coresight/devices/tpdm1/integration_test
> echo 2 > /sys/bus/coresight/devices/tpdm1/integration_test
> cat /dev/tmc_etf0 > /data/etf-tpdm1.bin
> echo 0 > /sys/bus/coresight/devices/tpdm1/enable_source
> echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> echo 1 > /sys/bus/coresight/devices/tpdm2/enable_source
> echo 1 > /sys/bus/coresight/devices/tpdm2/integration_test
> echo 2 > /sys/bus/coresight/devices/tpdm2/integration_test
> cat /dev/tmc_etf0 > /data/etf-tpdm2.bin
> echo 0 > /sys/bus/coresight/devices/tpdm2/enable_source
> echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> 



> If the data from TPDMs can be obtained from the ETF, it means
> that the TPDMs verification is successful. At the same time,


How can we decode the TPDM trace ? Is there a public decoder
available ?

> since TPDM0, TPDM1 and TPDM2 are all connected to the same
> funnel "funnel@6c2d000" and output via different output ports,
> it also means that the following patches verification is
> successful.
> coresight: add support to enable more coresight paths
> coresight: funnel: add support for multiple output ports
> 
> Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> ---
>   arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 439 +++++++++++++++++++++++
>   1 file changed, 439 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
> index 8ac96f8e79d4..bcec8b181e11 100644
> --- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
> +++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
> @@ -222,6 +222,445 @@


> +
> +	funnel@6b04000 {
> +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> +		arm,primecell-periphid = <0x000bb908>;
> +
> +		reg = <0 0x6b04000 0 0x1000>;
> +		reg-names = "funnel-base";
> +
> +		clocks = <&aoss_qmp>;
> +		clock-names = "apb_pclk";
> +
> +		out-ports {
> +			port {
> +				merge_funnel_out: endpoint {
> +					remote-endpoint =
> +						<&etf_in>;
> +				};
> +			};
> +		};
> +
> +		in-ports {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			port@7 {
> +				reg = <7>;
> +				swao_funnel_in7: endpoint {

> +					slave-mode;

This is obsolete, with the new in-ports/out-ports construct.

Suzuki

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

* Re: [PATCH 04/10] Coresight: Enable BC and GPR for TPDM driver
  2021-11-03 19:43   ` Mathieu Poirier
@ 2021-11-04 11:13     ` Jinlong
  2021-11-04 17:02       ` Mathieu Poirier
  0 siblings, 1 reply; 37+ messages in thread
From: Jinlong @ 2021-11-04 11:13 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: Tao Zhang, Suzuki K Poulose, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni,
	Jinlong Mao

On Wed, Nov 03, 2021 at 01:43:00PM -0600, Mathieu Poirier wrote:
> On Thu, Oct 21, 2021 at 03:38:50PM +0800, Tao Zhang wrote:
> > Enable GPR and Basic Counts(BC) for TPDM. Add GPR interface and
> 
> But what is GPR?  What does it stand for?  Where is this documented?
>

We will add the documentation for GPR.
 
> > basic control sysFS interface for TPDM. The GPR interface has RW
> > and RO fields for controlling external logic and mapping core
> > signals to an APB accessible address in the TPDM address map.
> > 
> > Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> > ---
> >  drivers/hwtracing/coresight/coresight-tpdm.c | 334 +++++++++++++++++++
> >  1 file changed, 334 insertions(+)
> > 
> > diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
> > index 906776c859d6..c0a01979e42f 100644
> > --- a/drivers/hwtracing/coresight/coresight-tpdm.c
> > +++ b/drivers/hwtracing/coresight/coresight-tpdm.c
> > @@ -276,6 +276,93 @@ struct tpdm_drvdata {
> >  
> >  static void tpdm_init_default_data(struct tpdm_drvdata *drvdata);
> >  
> > +static void __tpdm_enable_gpr(struct tpdm_drvdata *drvdata)
> 
> There is no need for the double underscore at the beginning of the function
> name, please remove.  The same goes for __tpdm_config_bc_msr() and
> __tpdm_enable_bc().

We will remove the double underscore.

> 
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < TPDM_GPR_REGS_MAX; i++) {
> > +		if (!test_bit(i, drvdata->gpr->gpr_dirty))
> > +			continue;
> > +		tpdm_writel(drvdata, drvdata->gpr->gp_regs[i], TPDM_GPR_CR(i));
> > +	}
> > +}
> > +
> > +static void __tpdm_config_bc_msr(struct tpdm_drvdata *drvdata)
> > +{
> > +	int i;
> > +
> > +	if (!drvdata->msr_support)
> 
> Shouldn't msr_support be part of bc_dataset?  And why do we have a function for
> this when an if() condition would work just as well, as it is done in
> bc_trig_type below?

Some tpdms don't have msr registers. And not only bc has the msr register. 
There are tc, dsb, cmb registers.

> 
> > +		return;
> > +
> > +	for (i = 0; i < TPDM_BC_MAX_MSR; i++)
> > +		tpdm_writel(drvdata, drvdata->bc->msr[i], TPDM_BC_MSR(i));
> > +}
> > +
> > +static void __tpdm_enable_bc(struct tpdm_drvdata *drvdata)
> > +{
> > +	int i;
> > +	uint32_t val;
> > +
> > +	if (drvdata->bc->sat_mode)
> > +		tpdm_writel(drvdata, drvdata->bc->sat_mode,
> > +			    TPDM_BC_SATROLL);
> > +	else
> > +		tpdm_writel(drvdata, 0x0, TPDM_BC_SATROLL);
> > +
> > +	if (drvdata->bc->enable_counters) {
> > +		tpdm_writel(drvdata, 0xFFFFFFFF, TPDM_BC_CNTENCLR);
> > +		tpdm_writel(drvdata, drvdata->bc->enable_counters,
> > +			    TPDM_BC_CNTENSET);
> > +	}
> > +	if (drvdata->bc->clear_counters)
> > +		tpdm_writel(drvdata, drvdata->bc->clear_counters,
> > +			    TPDM_BC_CNTENCLR);
> > +
> > +	if (drvdata->bc->enable_irq) {
> > +		tpdm_writel(drvdata, 0xFFFFFFFF, TPDM_BC_INTENCLR);
> > +		tpdm_writel(drvdata, drvdata->bc->enable_irq,
> > +			    TPDM_BC_INTENSET);
> > +	}
> > +	if (drvdata->bc->clear_irq)
> > +		tpdm_writel(drvdata, drvdata->bc->clear_irq,
> > +			    TPDM_BC_INTENCLR);
> > +
> > +	if (drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_FULL) {
> > +		for (i = 0; i < drvdata->bc_counters_avail; i++) {
> 
> Same here - shouldn't bc_trig_type and bc_counters_avail be part of bc_dataset?

We will move them to bc_dateset.

> 
> > +			tpdm_writel(drvdata, drvdata->bc->trig_val_lo[i],
> > +				    TPDM_BC_TRIG_LO(i));
> > +			tpdm_writel(drvdata, drvdata->bc->trig_val_hi[i],
> > +				    TPDM_BC_TRIG_HI(i));
> > +		}
> > +	} else if (drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_PARTIAL) {
> > +		tpdm_writel(drvdata, drvdata->bc->trig_val_lo[0],
> > +			    TPDM_BC_TRIG_LO(0));
> > +		tpdm_writel(drvdata, drvdata->bc->trig_val_hi[0],
> > +			    TPDM_BC_TRIG_HI(0));
> > +	}
> > +
> > +	if (drvdata->bc->enable_ganging)
> > +		tpdm_writel(drvdata, drvdata->bc->enable_ganging, TPDM_BC_GANG);
> > +
> > +	for (i = 0; i < TPDM_BC_MAX_OVERFLOW; i++)
> > +		tpdm_writel(drvdata, drvdata->bc->overflow_val[i],
> > +			    TPDM_BC_OVERFLOW(i));
> > +
> > +	__tpdm_config_bc_msr(drvdata);
> > +
> > +	val = tpdm_readl(drvdata, TPDM_BC_CR);
> > +	if (drvdata->bc->retrieval_mode == TPDM_MODE_APB)
> > +		val = val | BIT(2);
> > +	else
> > +		val = val & ~BIT(2);
> > +	tpdm_writel(drvdata, val, TPDM_BC_CR);
> > +
> > +	val = tpdm_readl(drvdata, TPDM_BC_CR);
> > +	/* Set the enable bit */
> > +	val = val | BIT(0);
> > +	tpdm_writel(drvdata, val, TPDM_BC_CR);
> 
> As a whole, this function is very hard to read and understand due to the lack of
> comments.
> 

We will add more comments. 

> > +}
> > +
> >  static void __tpdm_enable(struct tpdm_drvdata *drvdata)
> >  {
> >  	TPDM_UNLOCK(drvdata);
> > @@ -283,6 +370,12 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata)
> >  	if (drvdata->clk_enable)
> >  		tpdm_writel(drvdata, 0x1, TPDM_CLK_CTRL);
> >  
> > +	if (test_bit(TPDM_DS_GPR, drvdata->enable_ds))
> > +		__tpdm_enable_gpr(drvdata);
> > +
> > +	if (test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		__tpdm_enable_bc(drvdata);
> > +
> >  	TPDM_LOCK(drvdata);
> >  }
> >  
> > @@ -307,10 +400,22 @@ static int tpdm_enable(struct coresight_device *csdev,
> >  	return 0;
> >  }
> >  
> > +static void __tpdm_disable_bc(struct tpdm_drvdata *drvdata)
> > +{
> > +	uint32_t config;
> > +
> > +	config = tpdm_readl(drvdata, TPDM_BC_CR);
> > +	config = config & ~BIT(0);
> > +	tpdm_writel(drvdata, config, TPDM_BC_CR);
> > +}
> > +
> >  static void __tpdm_disable(struct tpdm_drvdata *drvdata)
> >  {
> >  	TPDM_UNLOCK(drvdata);
> >  
> > +	if (test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		__tpdm_disable_bc(drvdata);
> > +
> 
> Shouldn't GPRs be disabled as well?  If not why is it the case?  A comment
> should be explaining what is going on.
>

We will add commets here.
 
> >  	if (drvdata->clk_enable)
> >  		tpdm_writel(drvdata, 0x0, TPDM_CLK_CTRL);
> >  
> > @@ -352,6 +457,234 @@ static const struct coresight_ops tpdm_cs_ops = {
> >  	.source_ops	= &tpdm_source_ops,
> >  };
> 
> Everything related to sysfs should be in a patch on its own.
> 
> >  

We will address your comments.

> > +static ssize_t available_datasets_show(struct device *dev,
> > +					    struct device_attribute *attr,
> > +					    char *buf)
> 
> Indentation problem here and for the rest of the patch.

We will check and update.

> 
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	ssize_t size = 0;
> > +
> > +	if (test_bit(TPDM_DS_IMPLDEF, drvdata->datasets))
> > +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s",
> > +				  "IMPLDEF");
> > +
> > +	if (test_bit(TPDM_DS_DSB, drvdata->datasets))
> > +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "DSB");
> > +
> > +	if (test_bit(TPDM_DS_CMB, drvdata->datasets))
> > +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "CMB");
> > +
> > +	if (test_bit(TPDM_DS_TC, drvdata->datasets))
> > +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "TC");
> > +
> > +	if (test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "BC");
> > +
> > +	if (test_bit(TPDM_DS_GPR, drvdata->datasets))
> > +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "GPR");
> > +
> > +	if (test_bit(TPDM_DS_MCMB, drvdata->datasets))
> > +		size += scnprintf(buf + size, PAGE_SIZE - size, "%-8s", "MCMB");
> > +
> > +	size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RO(available_datasets);
> > +
> > +static ssize_t enable_datasets_show(struct device *dev,
> > +					 struct device_attribute *attr,
> > +					 char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	ssize_t size;
> > +
> > +	size = scnprintf(buf, PAGE_SIZE, "%*pb\n", TPDM_DATASETS,
> > +			 drvdata->enable_ds);
> > +
> > +	if (PAGE_SIZE - size < 2)
> > +		size = -EINVAL;
> 
> TPDM_DATASETS is set to 32 - is this realistic? 
> 

As the register has 32bit, so we set TPDM_DATASETS as 32.
We will check and update it to a proper value.

	pidr = tpdm_readl(drvdata, CORESIGHT_PERIPHIDR0);
	for (i = 0; i < TPDM_DATASETS; i++) {
		if (pidr & BIT(i)) {
			__set_bit(i, drvdata->datasets);
			__set_bit(i, drvdata->enable_ds);
		}
	}

> > +	else
> > +		size += scnprintf(buf + size, 2, "\n");
> 
> ...and what is going on here?
> 

We will check and remove this. 

> > +	return size;
> > +}
> > +
> > +static ssize_t enable_datasets_store(struct device *dev,
> > +					  struct device_attribute *attr,
> > +					  const char *buf,
> > +					  size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +	int i;
> > +
> > +	if (kstrtoul(buf, 16, &val))
> > +		return -EINVAL;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	for (i = 0; i < TPDM_DATASETS; i++) {
> > +		if (test_bit(i, drvdata->datasets) && (val & BIT(i)))
> > +			__set_bit(i, drvdata->enable_ds);
> > +		else
> > +			__clear_bit(i, drvdata->enable_ds);
> > +	}
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(enable_datasets);
> > +
> > +static ssize_t reset_store(struct device *dev,
> > +					  struct device_attribute *attr,
> > +					  const char *buf,
> > +					  size_t size)
> > +{
> > +	int ret = 0;
> > +	unsigned long val;
> > +	struct mcmb_dataset *mcmb_temp = NULL;
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +
> > +	ret = kstrtoul(buf, 10, &val);
> 
> The coresight subsystem normally uses the hexadecimal base.
> 

We will address you comments.

> > +	if (ret)
> > +		return ret;
> 
> Shouldn't this be "if (!ret)" ? 
>

When ret is not 0, it need to return.
 
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	/* Reset all datasets to ZERO */
> > +	if (drvdata->gpr != NULL)
> > +		memset(drvdata->gpr, 0, sizeof(struct gpr_dataset));
> > +
> > +	if (drvdata->bc != NULL)
> > +		memset(drvdata->bc, 0, sizeof(struct bc_dataset));
> > +
> > +	if (drvdata->tc != NULL)
> > +		memset(drvdata->tc, 0, sizeof(struct tc_dataset));
> > +
> > +	if (drvdata->dsb != NULL)
> > +		memset(drvdata->dsb, 0, sizeof(struct dsb_dataset));
> > +
> > +	if (drvdata->cmb != NULL) {
> > +		if (drvdata->cmb->mcmb != NULL) {
> > +			mcmb_temp = drvdata->cmb->mcmb;
> > +			memset(drvdata->cmb->mcmb, 0,
> > +				sizeof(struct mcmb_dataset));
> > +			}
> > +
> > +		memset(drvdata->cmb, 0, sizeof(struct cmb_dataset));
> > +		drvdata->cmb->mcmb = mcmb_temp;
> > +	}
> > +	/* Init the default data */
> > +	tpdm_init_default_data(drvdata);
> > +
> > +	mutex_unlock(&drvdata->lock);
> > +
> > +	/* Disable tpdm if enabled */
> > +	if (drvdata->enable)
> > +		coresight_disable(drvdata->csdev);
> 
> Why is this done out of the lock?
> 

When call coresight_disable, tpdm_disable will be called. There is lock in tpdm_disable.
If add it into the lock, there will be dead lock.

> > +
> > +	return size;
> > +}
> > +static DEVICE_ATTR_WO(reset);
> > +
> > +static ssize_t integration_test_store(struct device *dev,
> > +					  struct device_attribute *attr,
> > +					  const char *buf,
> > +					  size_t size)
> > +{
> > +	int i, ret = 0;
> > +	unsigned long val;
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +
> > +	ret = kstrtoul(buf, 10, &val);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (val != 1 && val != 2)
> > +		return -EINVAL;
> > +
> > +	if (!drvdata->enable)
> > +		return -EINVAL;
> > +
> > +	if (val == 1)
> > +		val = ATBCNTRL_VAL_64;
> > +	else
> > +		val = ATBCNTRL_VAL_32;
> > +	TPDM_UNLOCK(drvdata);
> > +	tpdm_writel(drvdata, 0x1, TPDM_ITCNTRL);
> > +
> > +	for (i = 1; i < 5; i++)
> > +		tpdm_writel(drvdata, val, TPDM_ITATBCNTRL);
> > +
> > +	tpdm_writel(drvdata, 0, TPDM_ITCNTRL);
> > +	TPDM_LOCK(drvdata);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_WO(integration_test);
> 
> Integration test interface should be conditional to a compile time option.  Have
> a look at what was done for CTIs.
> 

We will check and update.

> > +
> > +static ssize_t gp_regs_show(struct device *dev,
> > +				 struct device_attribute *attr,
> > +				 char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	ssize_t size = 0;
> > +	int i = 0;
> > +
> > +	if (!test_bit(TPDM_DS_GPR, drvdata->datasets))
> > +		return -EPERM;
> 
>                 return -EINVAL;
> 
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	for (i = 0; i < TPDM_GPR_REGS_MAX; i++) {
> > +		if (!test_bit(i, drvdata->gpr->gpr_dirty))
> > +			continue;
> > +		size += scnprintf(buf + size, PAGE_SIZE - size,
> > +				  "Index: 0x%x Value: 0x%x\n", i,
> > +				  drvdata->gpr->gp_regs[i]);
> 
> This should not be - the sysfs interface requires outputs of a single line.
> 

We will check and update.

> > +	}
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +
> > +static ssize_t gp_regs_store(struct device *dev,
> > +				  struct device_attribute *attr,
> > +				  const char *buf,
> > +				  size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long index, val;
> > +
> > +	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_GPR, drvdata->datasets) ||
> > +	    index >= TPDM_GPR_REGS_MAX)
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	drvdata->gpr->gp_regs[index] = val;
> > +	__set_bit(index, drvdata->gpr->gpr_dirty);
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(gp_regs);
> > +
> > +static struct attribute *tpdm_attrs[] = {
> > +	&dev_attr_available_datasets.attr,
> > +	&dev_attr_enable_datasets.attr,
> > +	&dev_attr_reset.attr,
> > +	&dev_attr_integration_test.attr,
> > +	&dev_attr_gp_regs.attr,
> > +	NULL,
> > +};
> 
> All new sysfs interface need to be documented.  See here:
> 
> Documentation/ABI/testing/sysfs-bus-coresight-devices-xyz
> 
> More comments to come...
> 

We will add the comments. 

> Thanks,
> Mathieu
> 
> > +
> > +static struct attribute_group tpdm_attr_grp = {
> > +	.attrs = tpdm_attrs,
> > +};
> > +static const struct attribute_group *tpdm_attr_grps[] = {
> > +	&tpdm_attr_grp,
> > +	NULL,
> > +};
> > +
> >  static int tpdm_datasets_alloc(struct tpdm_drvdata *drvdata)
> >  {
> >  	if (test_bit(TPDM_DS_GPR, drvdata->datasets)) {
> > @@ -513,6 +846,7 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
> >  	desc.ops = &tpdm_cs_ops;
> >  	desc.pdata = adev->dev.platform_data;
> >  	desc.dev = &adev->dev;
> > +	desc.groups = tpdm_attr_grps;
> >  	drvdata->csdev = coresight_register(&desc);
> >  	if (IS_ERR(drvdata->csdev))
> >  		return PTR_ERR(drvdata->csdev);
> > -- 
> > 2.17.1
> > 

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

* Re: [PATCH 03/10] Coresight: Add driver to support Coresight device TPDM
  2021-11-04  8:56     ` Jinlong
@ 2021-11-04 16:55       ` Mathieu Poirier
  2021-11-05  8:15         ` Jinlong
  0 siblings, 1 reply; 37+ messages in thread
From: Mathieu Poirier @ 2021-11-04 16:55 UTC (permalink / raw)
  To: Jinlong
  Cc: Tao Zhang, Suzuki K Poulose, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni

[...]

> 
> > > +
> > > +#ifdef CONFIG_CORESIGHT_TPDM_DEFAULT_ENABLE
> > > +static int boot_enable = 1;
> > > +#else
> > > +static int boot_enable;
> > > +#endif
> > 
> > That isn't the proper way to do this.  Look at how it is done in
> > coresight-etm4x.c
> > 
> > > +
> > > +struct gpr_dataset {
> > > +	DECLARE_BITMAP(gpr_dirty, TPDM_GPR_REGS_MAX);
> > > +	uint32_t		gp_regs[TPDM_GPR_REGS_MAX];
> > 
> > Shouldn't this be u32?
> > 
> 
> uint32_t is the same as u32.
> typedef u32			uint32_t;

Right - but the common kernel convention is to use u64/32/16/8.  Please refactor
for the entire patchset.

> 
> > > +};
> > > +
> > > +struct bc_dataset {
> > > +	enum tpdm_mode		capture_mode;
> > > +	enum tpdm_mode		retrieval_mode;
> > > +	uint32_t		sat_mode;
> > > +	uint32_t		enable_counters;
> > > +	uint32_t		clear_counters;
> > > +	uint32_t		enable_irq;
> > > +	uint32_t		clear_irq;
> > > +	uint32_t		trig_val_lo[TPDM_BC_MAX_COUNTERS];
> > > +	uint32_t		trig_val_hi[TPDM_BC_MAX_COUNTERS];
> > > +	uint32_t		enable_ganging;
> > > +	uint32_t		overflow_val[TPDM_BC_MAX_OVERFLOW];
> > > +	uint32_t		msr[TPDM_BC_MAX_MSR];
> > > +};
> > > +
> > > +struct tc_dataset {
> > > +	enum tpdm_mode		capture_mode;
> > > +	enum tpdm_mode		retrieval_mode;
> > > +	bool			sat_mode;
> > > +	uint32_t		enable_counters;
> > > +	uint32_t		clear_counters;
> > > +	uint32_t		enable_irq;
> > > +	uint32_t		clear_irq;
> > > +	uint32_t		trig_sel[TPDM_TC_MAX_TRIG];
> > > +	uint32_t		trig_val_lo[TPDM_TC_MAX_TRIG];
> > > +	uint32_t		trig_val_hi[TPDM_TC_MAX_TRIG];
> > > +	uint32_t		msr[TPDM_TC_MAX_MSR];
> > > +};
> > > +
> > > +struct dsb_dataset {
> > > +	uint32_t		mode;
> > > +	uint32_t		edge_ctrl[TPDM_DSB_MAX_EDCR];
> > > +	uint32_t		edge_ctrl_mask[TPDM_DSB_MAX_EDCR / 2];
> > > +	uint32_t		patt_val[TPDM_DSB_MAX_PATT];
> > > +	uint32_t		patt_mask[TPDM_DSB_MAX_PATT];
> > > +	bool			patt_ts;
> > > +	bool			patt_type;
> > > +	uint32_t		trig_patt_val[TPDM_DSB_MAX_PATT];
> > > +	uint32_t		trig_patt_mask[TPDM_DSB_MAX_PATT];
> > > +	bool			trig_ts;
> > > +	bool			trig_type;
> > > +	uint32_t		select_val[TPDM_DSB_MAX_SELECT];
> > > +	uint32_t		msr[TPDM_DSB_MAX_MSR];
> > > +};
> > > +
> > > +struct mcmb_dataset {
> > > +	uint8_t		mcmb_trig_lane;
> > > +	uint8_t		mcmb_lane_select;
> > > +};
> > > +
> > > +struct cmb_dataset {
> > > +	bool			trace_mode;
> > > +	uint32_t		cycle_acc;
> > > +	uint32_t		patt_val[TPDM_CMB_PATT_CMP];
> > > +	uint32_t		patt_mask[TPDM_CMB_PATT_CMP];
> > > +	bool			patt_ts;
> > > +	uint32_t		trig_patt_val[TPDM_CMB_PATT_CMP];
> > > +	uint32_t		trig_patt_mask[TPDM_CMB_PATT_CMP];
> > > +	bool			trig_ts;
> > > +	bool			ts_all;
> > > +	uint32_t		msr[TPDM_CMB_MAX_MSR];
> > > +	uint8_t			read_ctl_reg;
> > > +	struct mcmb_dataset	*mcmb;
> > > +};
> > > +
> > > +DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm");
> > > +
> > > +struct tpdm_drvdata {
> > > +	void __iomem		*base;
> > > +	struct device		*dev;
> > > +	struct coresight_device	*csdev;
> > > +	int			nr_tclk;
> > > +	struct clk		**tclk;
> > > +	int			nr_treg;
> > > +	struct regulator	**treg;
> > > +	struct mutex		lock;
> > > +	bool			enable;
> > > +	bool			clk_enable;
> > > +	DECLARE_BITMAP(datasets, TPDM_DATASETS);
> > > +	DECLARE_BITMAP(enable_ds, TPDM_DATASETS);
> > > +	enum tpdm_support_type	tc_trig_type;
> > > +	enum tpdm_support_type	bc_trig_type;
> > > +	enum tpdm_support_type	bc_gang_type;
> > > +	uint32_t		bc_counters_avail;
> > > +	uint32_t		tc_counters_avail;
> > > +	struct gpr_dataset	*gpr;
> > > +	struct bc_dataset	*bc;
> > > +	struct tc_dataset	*tc;
> > > +	struct dsb_dataset	*dsb;
> > > +	struct cmb_dataset	*cmb;
> > > +	int			traceid;
> > > +	uint32_t		version;
> > > +	bool			msr_support;
> > > +	bool			msr_fix_req;
> > > +	bool			cmb_msr_skip;
> > > +};
> > 
> > All of these should also be in a header file and properly documented.
> > 
> 
> We will move these to header file and add the documentation.
> 
> > > +
> > > +static void tpdm_init_default_data(struct tpdm_drvdata *drvdata);
> > 
> > This isn't needed.
> 
> We will remove this.
> > 
> > > +
> > > +static void __tpdm_enable(struct tpdm_drvdata *drvdata)
> > > +{
> > > +	TPDM_UNLOCK(drvdata);
> > > +
> > > +	if (drvdata->clk_enable)
> > > +		tpdm_writel(drvdata, 0x1, TPDM_CLK_CTRL);
> > > +
> > > +	TPDM_LOCK(drvdata);
> > > +}
> > > +
> > > +static int tpdm_enable(struct coresight_device *csdev,
> > > +		       struct perf_event *event, u32 mode)
> > > +{
> > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> > > +	int ret = 0;
> > > +
> > > +	if (drvdata->enable) {
> > 
> > This a race condition.
> >
> 
> Need to add it to mutex_lock. We will update.
>  
> > > +		dev_err(drvdata->dev,
> > > +			"TPDM setup already enabled,Skipping enablei\n");
> > 
> > Please remove this.
> > 
> 
> We will remove this.
> 
> > > +		return ret;
> > 
> > Shouldn't this be return -EBUSY? 
> 
> We will address your comments.
> 
> > 
> > > +	}
> > > +
> > > +	mutex_lock(&drvdata->lock);
> > > +	__tpdm_enable(drvdata);
> > > +	drvdata->enable = true;
> > > +	mutex_unlock(&drvdata->lock);
> > > +
> > > +	dev_info(drvdata->dev, "TPDM tracing enabled\n");
> > > +	return 0;
> > > +}
> > > +
> > > +static void __tpdm_disable(struct tpdm_drvdata *drvdata)
> > > +{
> > > +	TPDM_UNLOCK(drvdata);
> > > +
> > > +	if (drvdata->clk_enable)
> > > +		tpdm_writel(drvdata, 0x0, TPDM_CLK_CTRL);
> > > +
> > > +	TPDM_LOCK(drvdata);
> > > +}
> > > +
> > > +static void tpdm_disable(struct coresight_device *csdev,
> > > +			 struct perf_event *event)
> > > +{
> > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> > > +
> > > +	if (!drvdata->enable) {
> > > +		dev_err(drvdata->dev,
> > > +			"TPDM setup already disabled, Skipping disable\n");
> > > +		return;
> > > +	}
> > > +	mutex_lock(&drvdata->lock);
> > > +	__tpdm_disable(drvdata);
> > > +	drvdata->enable = false;
> > > +	mutex_unlock(&drvdata->lock);
> > 
> > Same comments as above.
> > 
> 
> We will address your comments.
> 
> > > +
> > > +	dev_info(drvdata->dev, "TPDM tracing disabled\n");
> > > +}
> > > +
> > > +static int tpdm_trace_id(struct coresight_device *csdev)
> > > +{
> > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> > > +
> > > +	return drvdata->traceid;
> > > +}
> > > +
> > > +static const struct coresight_ops_source tpdm_source_ops = {
> > > +	.trace_id	= tpdm_trace_id,
> > > +	.enable		= tpdm_enable,
> > > +	.disable	= tpdm_disable,
> > > +};
> > > +
> > > +static const struct coresight_ops tpdm_cs_ops = {
> > > +	.source_ops	= &tpdm_source_ops,
> > > +};
> > > +
> > > +static int tpdm_datasets_alloc(struct tpdm_drvdata *drvdata)
> > > +{
> > > +	if (test_bit(TPDM_DS_GPR, drvdata->datasets)) {
> > > +		drvdata->gpr = devm_kzalloc(drvdata->dev, sizeof(*drvdata->gpr),
> > > +					    GFP_KERNEL);
> > > +		if (!drvdata->gpr)
> > > +			return -ENOMEM;
> > > +	}
> > > +	if (test_bit(TPDM_DS_BC, drvdata->datasets)) {
> > > +		drvdata->bc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->bc),
> > > +					   GFP_KERNEL);
> > > +		if (!drvdata->bc)
> > > +			return -ENOMEM;
> > > +	}
> > > +	if (test_bit(TPDM_DS_TC, drvdata->datasets)) {
> > > +		drvdata->tc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->tc),
> > > +					   GFP_KERNEL);
> > > +		if (!drvdata->tc)
> > > +			return -ENOMEM;
> > > +	}
> > > +	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
> > > +		drvdata->dsb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->dsb),
> > > +					    GFP_KERNEL);
> > > +		if (!drvdata->dsb)
> > > +			return -ENOMEM;
> > > +	}
> > > +	if (test_bit(TPDM_DS_CMB, drvdata->datasets)) {
> > > +		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
> > > +					    GFP_KERNEL);
> > > +		if (!drvdata->cmb)
> > > +			return -ENOMEM;
> > > +	} else if (test_bit(TPDM_DS_MCMB, drvdata->datasets)) {
> > > +		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
> > > +					    GFP_KERNEL);
> > > +		if (!drvdata->cmb)
> > > +			return -ENOMEM;
> > > +		drvdata->cmb->mcmb = devm_kzalloc(drvdata->dev,
> > > +						  sizeof(*drvdata->cmb->mcmb),
> > > +						  GFP_KERNEL);
> > > +		if (!drvdata->cmb->mcmb)
> > > +			return -ENOMEM;
> > 
> > How can I understand what the above does when:
> > 
> > 1) There isn't a single line of comments.
> > 2) I don't know the HW.
> > 3) I don't have access to the documentation.
> > 
> 
> We will add comments here.
> 
> > > +	}
> > > +	return 0;
> > > +}
> > > +
> > > +static void tpdm_init_default_data(struct tpdm_drvdata *drvdata)
> > > +{
> > > +	if (test_bit(TPDM_DS_BC, drvdata->datasets))
> > > +		drvdata->bc->retrieval_mode = TPDM_MODE_ATB;
> > > +
> > > +	if (test_bit(TPDM_DS_TC, drvdata->datasets))
> > > +		drvdata->tc->retrieval_mode = TPDM_MODE_ATB;
> > > +
> > > +	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
> > > +		drvdata->dsb->trig_ts = true;
> > > +		drvdata->dsb->trig_type = false;
> > > +	}
> > > +
> > > +	if (test_bit(TPDM_DS_CMB, drvdata->datasets) ||
> > > +	    test_bit(TPDM_DS_MCMB, drvdata->datasets))
> > > +		drvdata->cmb->trig_ts = true;
> > > +}
> > > +
> > > +static int tpdm_parse_of_data(struct tpdm_drvdata *drvdata)
> > > +{
> > > +	int i, ret;
> > > +	const char *tclk_name, *treg_name;
> > > +	struct device_node *node = drvdata->dev->of_node;
> > > +
> > > +	drvdata->clk_enable = of_property_read_bool(node, "qcom,clk-enable");
> > > +	drvdata->msr_fix_req = of_property_read_bool(node, "qcom,msr-fix-req");
> > > +	drvdata->cmb_msr_skip = of_property_read_bool(node,
> > > +					"qcom,cmb-msr-skip");
> > > +
> > > +	drvdata->nr_tclk = of_property_count_strings(node, "qcom,tpdm-clks");
> > > +	if (drvdata->nr_tclk > 0) {
> > > +		drvdata->tclk = devm_kzalloc(drvdata->dev, drvdata->nr_tclk *
> > > +					     sizeof(*drvdata->tclk),
> > > +					     GFP_KERNEL);
> > > +		if (!drvdata->tclk)
> > > +			return -ENOMEM;
> > > +
> > > +		for (i = 0; i < drvdata->nr_tclk; i++) {
> > > +			ret = of_property_read_string_index(node,
> > > +					    "qcom,tpdm-clks", i, &tclk_name);
> > > +			if (ret)
> > > +				return ret;
> > > +
> > > +			drvdata->tclk[i] = devm_clk_get(drvdata->dev,
> > > +							tclk_name);
> > > +			if (IS_ERR(drvdata->tclk[i]))
> > > +				return PTR_ERR(drvdata->tclk[i]);
> > > +		}
> > > +	}
> > > +
> > > +	drvdata->nr_treg = of_property_count_strings(node, "qcom,tpdm-regs");
> > > +	if (drvdata->nr_treg > 0) {
> > > +		drvdata->treg = devm_kzalloc(drvdata->dev, drvdata->nr_treg *
> > > +					     sizeof(*drvdata->treg),
> > > +					     GFP_KERNEL);
> > > +		if (!drvdata->treg)
> > > +			return -ENOMEM;
> > > +
> > > +		for (i = 0; i < drvdata->nr_treg; i++) {
> > > +			ret = of_property_read_string_index(node,
> > > +					    "qcom,tpdm-regs", i, &treg_name);
> > > +			if (ret)
> > > +				return ret;
> > > +
> > > +			drvdata->treg[i] = devm_regulator_get(drvdata->dev,
> > > +							treg_name);
> > > +			if (IS_ERR(drvdata->treg[i]))
> > > +				return PTR_ERR(drvdata->treg[i]);
> > > +		}
> > > +	}
> > 
> > _None_ of the above are defined in the yaml file and/or part of the example that
> > is shown there.  Moreover they don't appear in patch 10/10 where TPDM and TPDA are
> > supposed to be introduced.  I will comment on patch 10/10 when I'm done with
> > this one.
> > 
> 
> We will update the dtsi change in next version.
> 
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
> > > +{
> > > +	int ret, i;
> > > +	uint32_t pidr, devid;
> > > +	struct device *dev = &adev->dev;
> > > +	struct coresight_platform_data *pdata;
> > > +	struct tpdm_drvdata *drvdata;
> > > +	struct coresight_desc desc = { 0 };
> > > +	static int traceid = TPDM_TRACE_ID_START;
> > > +	uint32_t version;
> > > +
> > > +	desc.name = coresight_alloc_device_name(&tpdm_devs, dev);
> > > +	if (!desc.name)
> > > +		return -ENOMEM;
> > > +	pdata = coresight_get_platform_data(dev);
> > > +	if (IS_ERR(pdata))
> > > +		return PTR_ERR(pdata);
> > > +	adev->dev.platform_data = pdata;
> > > +
> > > +	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> > > +	if (!drvdata)
> > > +		return -ENOMEM;
> > > +	drvdata->dev = &adev->dev;
> > > +	dev_set_drvdata(dev, drvdata);
> > > +
> > > +	drvdata->base = devm_ioremap_resource(dev, &adev->res);
> > > +	if (!drvdata->base)
> > > +		return -ENOMEM;
> > > +
> > > +	mutex_init(&drvdata->lock);
> > > +
> > > +	ret = tpdm_parse_of_data(drvdata);
> > > +	if (ret) {
> > > +		dev_err(drvdata->dev, "TPDM parse of data fail\n");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	desc.type = CORESIGHT_DEV_TYPE_SOURCE;
> > > +	desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
> > 
> > Why is this of subtype CORESIGHT_DEV_SUBTYPE_SOURCE_PROC when TPDMs are not
> > associated with a CPU?  Here we should probably introduce something like
> > CORESIGHT_DEV_SUBTYPE_SOURCE_SYS
> 
> 
> We will check and update.
> 
> > 
> > > +	desc.ops = &tpdm_cs_ops;
> > > +	desc.pdata = adev->dev.platform_data;
> > > +	desc.dev = &adev->dev;
> > > +	drvdata->csdev = coresight_register(&desc);
> > > +	if (IS_ERR(drvdata->csdev))
> > > +		return PTR_ERR(drvdata->csdev);
> > > +
> > > +	version = tpdm_readl(drvdata, CORESIGHT_PERIPHIDR2);
> > > +	drvdata->version = BMVAL(version, 4, 7);
> > > +
> > 
> > What is MSR support?  This should be documented.  Looking more closely at this
> > patch, not a single line of documentation is provided.  If you look at other
> > drivers, you will find things to be quite different.  The coresight subsystem
> > has become very complex and documentation helps us, and anyone trying to
> > contribute, understand what the code does.
> > 
> 
> We will add more documentation in the driver.
> 
> 
> > > +	if (drvdata->version)
> > > +		drvdata->msr_support = true;
> > > +
> > > +	pidr = tpdm_readl(drvdata, CORESIGHT_PERIPHIDR0);
> > > +	for (i = 0; i < TPDM_DATASETS; i++) {
> > > +		if (pidr & BIT(i)) {
> > > +			__set_bit(i, drvdata->datasets);
> > > +			__set_bit(i, drvdata->enable_ds);
> > > +		}
> > > +	}
> > > +
> > > +	ret = tpdm_datasets_alloc(drvdata);
> > > +	if (ret) {
> > > +		coresight_unregister(drvdata->csdev);
> > > +		return ret;
> > > +	}
> > > +
> > > +	tpdm_init_default_data(drvdata);
> > > +
> > > +	devid = tpdm_readl(drvdata, CORESIGHT_DEVID);
> > > +	drvdata->tc_trig_type = BMVAL(devid, 27, 28);
> > > +	drvdata->bc_trig_type = BMVAL(devid, 25, 26);
> > > +	drvdata->bc_gang_type = BMVAL(devid, 23, 24);
> > > +	drvdata->bc_counters_avail = BMVAL(devid, 6, 10) + 1;
> > > +	drvdata->tc_counters_avail = BMVAL(devid, 4, 5) + 1;
> > 
> > Shouldn't this be part of tpdm_init_default_data()?
> > 
> 
> We will address your comments.
> 
> > > +
> > > +	drvdata->traceid = traceid++;
> > 
> > For this to work a new function needs to be introduced in coresight-pmu.h.
> > Something like:
> > 
> > static inline int coresight_get_system_trace_id(int id)
> > {
> >         /* Start system IDs above the highest per CPU trace ID. */
> >         return coresigth_get_trace_id(cpumask_last(cpu_possible_mask) + 1);
> > }
> > 
> 
> We will address your comments.
> 
> > > +
> > > +	dev_dbg(drvdata->dev, "TPDM initialized\n");
> > 
> > Please remove this.
> >
> 
> We will remove it.
>  
> > > +
> > > +	if (boot_enable)
> > > +		coresight_enable(drvdata->csdev);
> > > +
> > > +	pm_runtime_put(&adev->dev);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static struct amba_id tpdm_ids[] = {
> > > +	{
> > > +		.id     = 0x001f0e00,
> > > +		.mask   = 0x00ffff00,
> > 
> > Any way we can use CS_AMBA_ID() here?
> > 
> 
> We will address your comments.
> 
> > > +		.data	= "TPDM",
> > 
> > What is .data used for?
> 
> It is just to fill the structure. We will remove it if it is not necessary.
> 
> > 
> > > +	},
> > > +	{ 0, 0},
> > > +};
> > > +
> > > +static struct amba_driver tpdm_driver = {
> > > +	.drv = {
> > > +		.name   = "coresight-tpdm",
> > > +		.owner	= THIS_MODULE,
> > > +		.suppress_bind_attrs = true,
> > > +	},
> > > +	.probe          = tpdm_probe,
> > > +	.id_table	= tpdm_ids,
> > > +};
> > > +
> > > +module_amba_driver(tpdm_driver);
> > > +
> > > +MODULE_LICENSE("GPL v2");
> > > +MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Monitor driver");
> > > -- 
> > > 2.17.1
> > > 

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

* Re: [PATCH 04/10] Coresight: Enable BC and GPR for TPDM driver
  2021-11-04 11:13     ` Jinlong
@ 2021-11-04 17:02       ` Mathieu Poirier
  2021-11-05  8:17         ` Jinlong
  0 siblings, 1 reply; 37+ messages in thread
From: Mathieu Poirier @ 2021-11-04 17:02 UTC (permalink / raw)
  To: Jinlong
  Cc: Tao Zhang, Suzuki K Poulose, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni

[...]

> > > +
> > > +static ssize_t reset_store(struct device *dev,
> > > +					  struct device_attribute *attr,
> > > +					  const char *buf,
> > > +					  size_t size)
> > > +{
> > > +	int ret = 0;
> > > +	unsigned long val;
> > > +	struct mcmb_dataset *mcmb_temp = NULL;
> > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > > +
> > > +	ret = kstrtoul(buf, 10, &val);
> > 
> > The coresight subsystem normally uses the hexadecimal base.
> > 
> 
> We will address you comments.
> 
> > > +	if (ret)
> > > +		return ret;
> > 
> > Shouldn't this be "if (!ret)" ? 
> >
> 
> When ret is not 0, it need to return.

I would expect something like this:

$ echo 1 > /sys/path/to/tpdm/device/reset

and not

$ echo 0 > /sys/path/to/tpdm/device/reset

The latter is what the code does.

Thanks,
Mathieu

>  
> > > +
> > > +	mutex_lock(&drvdata->lock);
> > > +	/* Reset all datasets to ZERO */
> > > +	if (drvdata->gpr != NULL)
> > > +		memset(drvdata->gpr, 0, sizeof(struct gpr_dataset));
> > > +
> > > +	if (drvdata->bc != NULL)
> > > +		memset(drvdata->bc, 0, sizeof(struct bc_dataset));
> > > +
> > > +	if (drvdata->tc != NULL)
> > > +		memset(drvdata->tc, 0, sizeof(struct tc_dataset));
> > > +
> > > +	if (drvdata->dsb != NULL)
> > > +		memset(drvdata->dsb, 0, sizeof(struct dsb_dataset));
> > > +
> > > +	if (drvdata->cmb != NULL) {
> > > +		if (drvdata->cmb->mcmb != NULL) {
> > > +			mcmb_temp = drvdata->cmb->mcmb;
> > > +			memset(drvdata->cmb->mcmb, 0,
> > > +				sizeof(struct mcmb_dataset));
> > > +			}
> > > +
> > > +		memset(drvdata->cmb, 0, sizeof(struct cmb_dataset));
> > > +		drvdata->cmb->mcmb = mcmb_temp;
> > > +	}
> > > +	/* Init the default data */
> > > +	tpdm_init_default_data(drvdata);
> > > +
> > > +	mutex_unlock(&drvdata->lock);
> > > +
> > > +	/* Disable tpdm if enabled */
> > > +	if (drvdata->enable)
> > > +		coresight_disable(drvdata->csdev);
> > 
> > Why is this done out of the lock?
> > 
> 
> When call coresight_disable, tpdm_disable will be called. There is lock in tpdm_disable.
> If add it into the lock, there will be dead lock.
> 
> > > +
> > > +	return size;
> > > +}
> > > +static DEVICE_ATTR_WO(reset);
> > > +
> > > +static ssize_t integration_test_store(struct device *dev,
> > > +					  struct device_attribute *attr,
> > > +					  const char *buf,
> > > +					  size_t size)
> > > +{
> > > +	int i, ret = 0;
> > > +	unsigned long val;
> > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > > +
> > > +	ret = kstrtoul(buf, 10, &val);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	if (val != 1 && val != 2)
> > > +		return -EINVAL;
> > > +
> > > +	if (!drvdata->enable)
> > > +		return -EINVAL;
> > > +
> > > +	if (val == 1)
> > > +		val = ATBCNTRL_VAL_64;
> > > +	else
> > > +		val = ATBCNTRL_VAL_32;
> > > +	TPDM_UNLOCK(drvdata);
> > > +	tpdm_writel(drvdata, 0x1, TPDM_ITCNTRL);
> > > +
> > > +	for (i = 1; i < 5; i++)
> > > +		tpdm_writel(drvdata, val, TPDM_ITATBCNTRL);
> > > +
> > > +	tpdm_writel(drvdata, 0, TPDM_ITCNTRL);
> > > +	TPDM_LOCK(drvdata);
> > > +	return size;
> > > +}
> > > +static DEVICE_ATTR_WO(integration_test);
> > 
> > Integration test interface should be conditional to a compile time option.  Have
> > a look at what was done for CTIs.
> > 
> 
> We will check and update.
> 
> > > +
> > > +static ssize_t gp_regs_show(struct device *dev,
> > > +				 struct device_attribute *attr,
> > > +				 char *buf)
> > > +{
> > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > > +	ssize_t size = 0;
> > > +	int i = 0;
> > > +
> > > +	if (!test_bit(TPDM_DS_GPR, drvdata->datasets))
> > > +		return -EPERM;
> > 
> >                 return -EINVAL;
> > 
> > > +
> > > +	mutex_lock(&drvdata->lock);
> > > +	for (i = 0; i < TPDM_GPR_REGS_MAX; i++) {
> > > +		if (!test_bit(i, drvdata->gpr->gpr_dirty))
> > > +			continue;
> > > +		size += scnprintf(buf + size, PAGE_SIZE - size,
> > > +				  "Index: 0x%x Value: 0x%x\n", i,
> > > +				  drvdata->gpr->gp_regs[i]);
> > 
> > This should not be - the sysfs interface requires outputs of a single line.
> > 
> 
> We will check and update.
> 
> > > +	}
> > > +	mutex_unlock(&drvdata->lock);
> > > +	return size;
> > > +}
> > > +
> > > +static ssize_t gp_regs_store(struct device *dev,
> > > +				  struct device_attribute *attr,
> > > +				  const char *buf,
> > > +				  size_t size)
> > > +{
> > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > > +	unsigned long index, val;
> > > +
> > > +	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
> > > +		return -EINVAL;
> > > +	if (!test_bit(TPDM_DS_GPR, drvdata->datasets) ||
> > > +	    index >= TPDM_GPR_REGS_MAX)
> > > +		return -EPERM;
> > > +
> > > +	mutex_lock(&drvdata->lock);
> > > +	drvdata->gpr->gp_regs[index] = val;
> > > +	__set_bit(index, drvdata->gpr->gpr_dirty);
> > > +	mutex_unlock(&drvdata->lock);
> > > +	return size;
> > > +}
> > > +static DEVICE_ATTR_RW(gp_regs);
> > > +
> > > +static struct attribute *tpdm_attrs[] = {
> > > +	&dev_attr_available_datasets.attr,
> > > +	&dev_attr_enable_datasets.attr,
> > > +	&dev_attr_reset.attr,
> > > +	&dev_attr_integration_test.attr,
> > > +	&dev_attr_gp_regs.attr,
> > > +	NULL,
> > > +};
> > 
> > All new sysfs interface need to be documented.  See here:
> > 
> > Documentation/ABI/testing/sysfs-bus-coresight-devices-xyz
> > 
> > More comments to come...
> > 
> 
> We will add the comments. 
> 
> > Thanks,
> > Mathieu
> > 
> > > +
> > > +static struct attribute_group tpdm_attr_grp = {
> > > +	.attrs = tpdm_attrs,
> > > +};
> > > +static const struct attribute_group *tpdm_attr_grps[] = {
> > > +	&tpdm_attr_grp,
> > > +	NULL,
> > > +};
> > > +
> > >  static int tpdm_datasets_alloc(struct tpdm_drvdata *drvdata)
> > >  {
> > >  	if (test_bit(TPDM_DS_GPR, drvdata->datasets)) {
> > > @@ -513,6 +846,7 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
> > >  	desc.ops = &tpdm_cs_ops;
> > >  	desc.pdata = adev->dev.platform_data;
> > >  	desc.dev = &adev->dev;
> > > +	desc.groups = tpdm_attr_grps;
> > >  	drvdata->csdev = coresight_register(&desc);
> > >  	if (IS_ERR(drvdata->csdev))
> > >  		return PTR_ERR(drvdata->csdev);
> > > -- 
> > > 2.17.1
> > > 

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

* Re: [PATCH 05/10] Coresight: Add interface for TPDM BC subunit
  2021-10-21  7:38 ` [PATCH 05/10] Coresight: Add interface for TPDM BC subunit Tao Zhang
@ 2021-11-04 18:01   ` Mathieu Poirier
  2021-11-05  8:26     ` Jinlong
  0 siblings, 1 reply; 37+ messages in thread
From: Mathieu Poirier @ 2021-11-04 18:01 UTC (permalink / raw)
  To: Tao Zhang
  Cc: Suzuki K Poulose, Alexander Shishkin, Mike Leach, Leo Yan,
	Greg Kroah-Hartman, coresight, linux-arm-kernel, linux-kernel,
	Tingwei Zhang, Mao Jinlong, Yuanfang Zhang, Trilok Soni

On Thu, Oct 21, 2021 at 03:38:51PM +0800, Tao Zhang wrote:
> The BC(Basic Counters) interface has RW, WO and RO fields for
> controlling BC dataset elements transmitted on ATB flush.
> The BC data set subunit supports from 1-32 counter instances
> allowing for collection of BC data sets.
> 
> Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> ---
>  drivers/hwtracing/coresight/coresight-tpdm.c | 873 +++++++++++++++++++
>  1 file changed, 873 insertions(+)
> 
> diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
> index c0a01979e42f..0970c69ac8e2 100644
> --- a/drivers/hwtracing/coresight/coresight-tpdm.c
> +++ b/drivers/hwtracing/coresight/coresight-tpdm.c
> @@ -668,6 +668,878 @@ static ssize_t gp_regs_store(struct device *dev,
>  }
>  static DEVICE_ATTR_RW(gp_regs);
>  
> +static ssize_t bc_capture_mode_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)

Indentation.  I won't repeat this comment but please make sure it is fixed for
the entire patchset.

> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	return scnprintf(buf, PAGE_SIZE, "%s\n",
> +			 drvdata->bc->capture_mode == TPDM_MODE_ATB ?
> +			 "ATB" : "APB");
> +}
> +
> +static ssize_t bc_capture_mode_store(struct device *dev,
> +					  struct device_attribute *attr,
> +					  const char *buf,
> +					  size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	char str[20] = "";

        char str[4];

> +	uint32_t val;
> +
> +	if (size >= 20)
> +		return -EINVAL;
> +	if (sscanf(buf, "%s", str) != 1)

        if (sscanf(buf, "%3s", str) != 1)

> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;

                return -EINVAL;

Please make sure this is fixed everywhere, except when -EINVAL is really
the right error code.

> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;

Why does the device need to be enabled for this operation?  Again no comments...

> +	}
> +
> +	if (!strcmp(str, "ATB")) {
> +		drvdata->bc->capture_mode = TPDM_MODE_ATB;
> +	} else if (!strcmp(str, "APB") &&
> +		   drvdata->bc->retrieval_mode == TPDM_MODE_APB) {
> +
> +		TPDM_UNLOCK(drvdata);
> +		val = tpdm_readl(drvdata, TPDM_BC_CR);
> +		val = val | BIT(3);
> +		tpdm_writel(drvdata, val, TPDM_BC_CR);
> +		TPDM_LOCK(drvdata);
> +
> +		drvdata->bc->capture_mode = TPDM_MODE_APB;
> +	} else {
> +		mutex_unlock(&drvdata->lock);
> +		return -EINVAL;
> +	}
> +
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_capture_mode);
> +
> +static ssize_t bc_retrieval_mode_show(struct device *dev,
> +					   struct device_attribute *attr,
> +					   char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	return scnprintf(buf, PAGE_SIZE, "%s\n",
> +			 drvdata->bc->retrieval_mode == TPDM_MODE_ATB ?
> +			 "ATB" : "APB");
> +}
> +
> +static ssize_t bc_retrieval_mode_store(struct device *dev,
> +					    struct device_attribute *attr,
> +					    const char *buf,
> +					    size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	char str[20] = "";
> +
> +	if (size >= 20)
> +		return -EINVAL;
> +	if (sscanf(buf, "%s", str) != 1)
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;

Same here, I don't know why the device needs to be enabled for this to success.
Please fix everywhere.

> +	}
> +
> +	if (!strcmp(str, "ATB")) {
> +		drvdata->bc->retrieval_mode = TPDM_MODE_ATB;
> +	} else if (!strcmp(str, "APB")) {
> +		drvdata->bc->retrieval_mode = TPDM_MODE_APB;
> +	} else {
> +		mutex_unlock(&drvdata->lock);
> +		return -EINVAL;
> +	}
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_retrieval_mode);
> +
> +static ssize_t bc_reset_counters_store(struct device *dev,
> +					    struct device_attribute *attr,
> +					    const char *buf,
> +					    size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (kstrtoul(buf, 16, &val))
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	if (val) {
> +		TPDM_UNLOCK(drvdata);
> +		val = tpdm_readl(drvdata, TPDM_BC_CR);
> +		val = val | BIT(1);
> +		tpdm_writel(drvdata, val, TPDM_BC_CR);
> +		TPDM_LOCK(drvdata);
> +	}
> +
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_WO(bc_reset_counters);
> +
> +static ssize_t bc_sat_mode_show(struct device *dev,
> +				     struct device_attribute *attr,
> +				     char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	return scnprintf(buf, PAGE_SIZE, "%lx\n",
> +			 (unsigned long)drvdata->bc->sat_mode);
        
	return scnprintf(buf, PAGE_SIZE, "%#x\n", drvdata->bc->sat_mode);

And everywhere casting in used...


> +}
> +
> +static ssize_t bc_sat_mode_store(struct device *dev,
> +				      struct device_attribute *attr,
> +				      const char *buf,
> +				      size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (kstrtoul(buf, 16, &val))
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	drvdata->bc->sat_mode = val;
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_sat_mode);
> +
> +static ssize_t bc_enable_counters_show(struct device *dev,
> +					    struct device_attribute *attr,
> +					    char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	return scnprintf(buf, PAGE_SIZE, "%lx\n",
> +			 (unsigned long)drvdata->bc->enable_counters);
> +}
> +
> +static ssize_t bc_enable_counters_store(struct device *dev,
> +					     struct device_attribute *attr,
> +					     const char *buf,
> +					     size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (kstrtoul(buf, 16, &val))
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	drvdata->bc->enable_counters = val;
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_enable_counters);
> +
> +static ssize_t bc_clear_counters_show(struct device *dev,
> +					   struct device_attribute *attr,
> +					   char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	return scnprintf(buf, PAGE_SIZE, "%lx\n",
> +			 (unsigned long)drvdata->bc->clear_counters);
> +}
> +
> +static ssize_t bc_clear_counters_store(struct device *dev,
> +					    struct device_attribute *attr,
> +					    const char *buf,
> +					    size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (kstrtoul(buf, 16, &val))
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	drvdata->bc->clear_counters = val;
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_clear_counters);
> +
> +static ssize_t bc_enable_irq_show(struct device *dev,
> +				       struct device_attribute *attr,
> +				       char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	return scnprintf(buf, PAGE_SIZE, "%lx\n",
> +			 (unsigned long)drvdata->bc->enable_irq);
> +}
> +
> +static ssize_t bc_enable_irq_store(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf,
> +					size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (kstrtoul(buf, 16, &val))
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	drvdata->bc->enable_irq = val;
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_enable_irq);
> +
> +static ssize_t bc_clear_irq_show(struct device *dev,
> +				      struct device_attribute *attr,
> +				      char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	return scnprintf(buf, PAGE_SIZE, "%lx\n",
> +			 (unsigned long)drvdata->bc->clear_irq);
> +}
> +
> +static ssize_t bc_clear_irq_store(struct device *dev,
> +				       struct device_attribute *attr,
> +				       const char *buf,
> +				       size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (kstrtoul(buf, 16, &val))
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	drvdata->bc->clear_irq = val;
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_clear_irq);
> +
> +static ssize_t bc_trig_val_lo_show(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	ssize_t size = 0;
> +	int i = 0;
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	for (i = 0; i < TPDM_BC_MAX_COUNTERS; i++)
> +		size += scnprintf(buf + size, PAGE_SIZE - size,
> +				  "Index: 0x%x Value: 0x%x\n", i,
> +				  drvdata->bc->trig_val_lo[i]);

As previously stated, the sysfs interface should output single line and single
values.  I won't comment on this again, please fix everywhere.

> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +
> +static ssize_t bc_trig_val_lo_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf,
> +					 size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long index, val;
> +
> +	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets) ||
> +	    index >= drvdata->bc_counters_avail ||
> +	    drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_NO ||
> +	    (drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_PARTIAL && index > 0))
> +		return -EPERM;
> 

This is hard to read and maintain.  Please break it up in multiple if()
statements.

> +	mutex_lock(&drvdata->lock);
> +	drvdata->bc->trig_val_lo[index] = val;
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_trig_val_lo);
> +
> +static ssize_t bc_trig_val_hi_show(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	ssize_t size = 0;
> +	int i = 0;
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	for (i = 0; i < TPDM_BC_MAX_COUNTERS; i++)
> +		size += scnprintf(buf + size, PAGE_SIZE - size,
> +				  "Index: 0x%x Value: 0x%x\n", i,
> +				  drvdata->bc->trig_val_hi[i]);
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +
> +static ssize_t bc_trig_val_hi_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf,
> +					 size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long index, val;
> +
> +	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets) ||
> +	    index >= drvdata->bc_counters_avail ||
> +	    drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_NO ||
> +	    (drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_PARTIAL && index > 0))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	drvdata->bc->trig_val_hi[index] = val;
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_trig_val_hi);
> +
> +static ssize_t bc_enable_ganging_show(struct device *dev,
> +					   struct device_attribute *attr,
> +					   char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	return scnprintf(buf, PAGE_SIZE, "%lx\n",
> +			 (unsigned long)drvdata->bc->enable_ganging);
> +}
> +
> +static ssize_t bc_enable_ganging_store(struct device *dev,
> +					    struct device_attribute *attr,
> +					    const char *buf,
> +					    size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (kstrtoul(buf, 16, &val))
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	drvdata->bc->enable_ganging = val;
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_enable_ganging);
> +
> +static ssize_t bc_overflow_val_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	ssize_t size = 0;
> +	int i = 0;
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	for (i = 0; i < TPDM_BC_MAX_OVERFLOW; i++)
> +		size += scnprintf(buf + size, PAGE_SIZE - size,
> +				  "Index: 0x%x Value: 0x%x\n", i,
> +				  drvdata->bc->overflow_val[i]);
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +
> +static ssize_t bc_overflow_val_store(struct device *dev,
> +					  struct device_attribute *attr,
> +					  const char *buf,
> +					  size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long index, val;
> +
> +	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets) ||
> +	    index >= TPDM_BC_MAX_OVERFLOW)
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	drvdata->bc->overflow_val[index] = val;
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_overflow_val);
> +
> +static ssize_t bc_ovsr_show(struct device *dev,
> +				 struct device_attribute *attr,
> +				 char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	TPDM_UNLOCK(drvdata);
> +	val = tpdm_readl(drvdata, TPDM_BC_OVSR);
> +	TPDM_LOCK(drvdata);
> +	mutex_unlock(&drvdata->lock);
> +	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
> +}
> +
> +static ssize_t bc_ovsr_store(struct device *dev,
> +				  struct device_attribute *attr,
> +				  const char *buf,
> +				  size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (kstrtoul(buf, 16, &val))
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	if (val) {
> +		TPDM_UNLOCK(drvdata);
> +		tpdm_writel(drvdata, val, TPDM_BC_OVSR);
> +		TPDM_LOCK(drvdata);
> +	}
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_ovsr);
> +
> +static ssize_t bc_counter_sel_show(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	TPDM_UNLOCK(drvdata);
> +	val = tpdm_readl(drvdata, TPDM_BC_SELR);
> +	TPDM_LOCK(drvdata);
> +	mutex_unlock(&drvdata->lock);
> +	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
> +}
> +
> +static ssize_t bc_counter_sel_store(struct device *dev,
> +					 struct device_attribute *attr,
> +					 const char *buf,
> +					 size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (kstrtoul(buf, 16, &val))
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable || val >= drvdata->bc_counters_avail) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	TPDM_UNLOCK(drvdata);
> +	tpdm_writel(drvdata, val, TPDM_BC_SELR);
> +	TPDM_LOCK(drvdata);
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_counter_sel);
> +
> +static ssize_t bc_count_val_lo_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	TPDM_UNLOCK(drvdata);
> +	val = tpdm_readl(drvdata, TPDM_BC_CNTR_LO);
> +	TPDM_LOCK(drvdata);
> +	mutex_unlock(&drvdata->lock);
> +	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
> +}
> +
> +static ssize_t bc_count_val_lo_store(struct device *dev,
> +					  struct device_attribute *attr,
> +					  const char *buf,
> +					  size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val, select;
> +
> +	if (kstrtoul(buf, 16, &val))
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	if (val) {

        if (!val) {
                mutex_unlock(&drvdata->lock);
                return -EINVAL;               
        }

> +		TPDM_UNLOCK(drvdata);
> +		select = tpdm_readl(drvdata, TPDM_BC_SELR);
> +
> +		/* Check if selected counter is disabled */
> +		if (BMVAL(tpdm_readl(drvdata, TPDM_BC_CNTENSET), select, select)) {
> +			mutex_unlock(&drvdata->lock);
> +			return -EPERM;
> +		}
> +
> +		tpdm_writel(drvdata, val, TPDM_BC_CNTR_LO);
> +		TPDM_LOCK(drvdata);
> +	}
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_count_val_lo);
> +
> +static ssize_t bc_count_val_hi_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	TPDM_UNLOCK(drvdata);
> +	val = tpdm_readl(drvdata, TPDM_BC_CNTR_HI);
> +	TPDM_LOCK(drvdata);
> +	mutex_unlock(&drvdata->lock);
> +	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
> +}
> +
> +static ssize_t bc_count_val_hi_store(struct device *dev,
> +					  struct device_attribute *attr,
> +					  const char *buf,
> +					  size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val, select;
> +
> +	if (kstrtoul(buf, 16, &val))
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	if (val) {

Same

> +		TPDM_UNLOCK(drvdata);
> +		select = tpdm_readl(drvdata, TPDM_BC_SELR);
> +
> +		/* Check if selected counter is disabled */
> +		if (BMVAL(tpdm_readl(drvdata, TPDM_BC_CNTENSET), select, select)) {
> +			mutex_unlock(&drvdata->lock);
> +			return -EPERM;
> +		}
> +
> +		tpdm_writel(drvdata, val, TPDM_BC_CNTR_HI);
> +		TPDM_LOCK(drvdata);
> +	}
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_count_val_hi);
> +
> +static ssize_t bc_shadow_val_lo_show(struct device *dev,
> +					  struct device_attribute *attr,
> +					  char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	ssize_t size = 0;
> +	int i = 0;
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	TPDM_UNLOCK(drvdata);
> +	for (i = 0; i < drvdata->bc_counters_avail; i++) {
> +		size += scnprintf(buf + size, PAGE_SIZE - size,
> +				  "Index: 0x%x Value: 0x%x\n", i,
> +				  tpdm_readl(drvdata, TPDM_BC_SHADOW_LO(i)));
> +	}
> +	TPDM_LOCK(drvdata);
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RO(bc_shadow_val_lo);
> +
> +static ssize_t bc_shadow_val_hi_show(struct device *dev,
> +					  struct device_attribute *attr,
> +					  char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	ssize_t size = 0;
> +	int i = 0;
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	TPDM_UNLOCK(drvdata);
> +	for (i = 0; i < drvdata->bc_counters_avail; i++)
> +		size += scnprintf(buf + size, PAGE_SIZE - size,
> +				  "Index: 0x%x Value: 0x%x\n", i,
> +				  tpdm_readl(drvdata, TPDM_BC_SHADOW_HI(i)));
> +	TPDM_LOCK(drvdata);
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RO(bc_shadow_val_hi);
> +
> +static ssize_t bc_sw_inc_show(struct device *dev,
> +				   struct device_attribute *attr,
> +				   char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	TPDM_UNLOCK(drvdata);
> +	val = tpdm_readl(drvdata, TPDM_BC_SWINC);
> +	TPDM_LOCK(drvdata);
> +	mutex_unlock(&drvdata->lock);
> +	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
> +}
> +
> +static ssize_t bc_sw_inc_store(struct device *dev,
> +				    struct device_attribute *attr,
> +				    const char *buf,
> +				    size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned long val;
> +
> +	if (kstrtoul(buf, 16, &val))
> +		return -EINVAL;
> +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> +		return -EPERM;
> +
> +	mutex_lock(&drvdata->lock);
> +	if (!drvdata->enable) {
> +		mutex_unlock(&drvdata->lock);
> +		return -EPERM;
> +	}
> +
> +	if (val) {
> +		TPDM_UNLOCK(drvdata);
> +		tpdm_writel(drvdata, val, TPDM_BC_SWINC);
> +		TPDM_LOCK(drvdata);
> +	}
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_sw_inc);
> +
> +static ssize_t bc_msr_show(struct device *dev,
> +				struct device_attribute *attr,
> +				char *buf)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned int i;
> +	ssize_t len = 0;
> +
> +	if (!drvdata->msr_support)
> +		return -EINVAL;
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	for (i = 0; i < TPDM_BC_MAX_MSR; i++)
> +		len += scnprintf(buf + len, PAGE_SIZE - len, "%u 0x%x\n",
> +				 i, drvdata->bc->msr[i]);
> +
> +	return len;
> +}
> +
> +static ssize_t bc_msr_store(struct device *dev,
> +				 struct device_attribute *attr,
> +				 const char *buf,
> +				 size_t size)
> +{
> +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	unsigned int num, val;
> +	int nval;
> +
> +	if (!drvdata->msr_support)
> +		return -EINVAL;
> +
> +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> +		return -EPERM;
> +
> +	nval = sscanf(buf, "%u %x", &num, &val);
> +	if (nval != 2)
> +		return -EINVAL;
> +
> +	if (num >= TPDM_BC_MAX_MSR)
> +		return -EINVAL;
> +
> +	mutex_lock(&drvdata->lock);
> +	drvdata->bc->msr[num] = val;
> +	mutex_unlock(&drvdata->lock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(bc_msr);
> +
> +static struct attribute *tpdm_bc_attrs[] = {
> +	&dev_attr_bc_capture_mode.attr,
> +	&dev_attr_bc_retrieval_mode.attr,
> +	&dev_attr_bc_reset_counters.attr,
> +	&dev_attr_bc_sat_mode.attr,
> +	&dev_attr_bc_enable_counters.attr,
> +	&dev_attr_bc_clear_counters.attr,
> +	&dev_attr_bc_enable_irq.attr,
> +	&dev_attr_bc_clear_irq.attr,
> +	&dev_attr_bc_trig_val_lo.attr,
> +	&dev_attr_bc_trig_val_hi.attr,
> +	&dev_attr_bc_enable_ganging.attr,
> +	&dev_attr_bc_overflow_val.attr,
> +	&dev_attr_bc_ovsr.attr,
> +	&dev_attr_bc_counter_sel.attr,
> +	&dev_attr_bc_count_val_lo.attr,
> +	&dev_attr_bc_count_val_hi.attr,
> +	&dev_attr_bc_shadow_val_lo.attr,
> +	&dev_attr_bc_shadow_val_hi.attr,
> +	&dev_attr_bc_sw_inc.attr,
> +	&dev_attr_bc_msr.attr,
> +	NULL,

This will result in a very crowded directory.  Please move under a "bc"
subdirectory.  And as I commented before, all sysfs entries need to be
documented under Documentation/ABI/testing.

> +};
> +
> +static struct attribute_group tpdm_bc_attr_grp = {
> +	.attrs = tpdm_bc_attrs,
> +};
> +
>  static struct attribute *tpdm_attrs[] = {
>  	&dev_attr_available_datasets.attr,
>  	&dev_attr_enable_datasets.attr,
> @@ -682,6 +1554,7 @@ static struct attribute_group tpdm_attr_grp = {
>  };
>  static const struct attribute_group *tpdm_attr_grps[] = {
>  	&tpdm_attr_grp,
> +	&tpdm_bc_attr_grp,

It is quite tedious to review all these options at the same time as the core
drivers.  I suggest to concentrate on the base functionality for now.  When that
is merged we can add configuration options such as these.

I am out of time for this patchset and as such will not review the remaining
patches - those will have to wait for another iteration.

Thanks,
Mathieu

>  	NULL,
>  };
>  
> -- 
> 2.17.1
> 

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

* Re: [PATCH 10/10] ARM: dts: msm: Add TPDA and TPDM support to DTS for RB5
  2021-11-04  9:45   ` Suzuki K Poulose
@ 2021-11-05  8:07     ` Jinlong
  0 siblings, 0 replies; 37+ messages in thread
From: Jinlong @ 2021-11-05  8:07 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: Tao Zhang, Mathieu Poirier, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni

Thanks for the review, Suzuki.  

On Thu, Nov 04, 2021 at 09:45:08AM +0000, Suzuki K Poulose wrote:
> On 21/10/2021 08:38, Tao Zhang wrote:
> > Add TPDA and TPDM support to DTS for RB5 board. This change is a
> > sample for validating. After applying this patch, the new TPDM and
> > TPDA nodes can be observed at the coresight devices path. TPDM and
> > TPDA hardware can be operated by commands.
> > 
> > List the commands for validating this series patches as below.
> > echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> > echo 1 > /sys/bus/coresight/devices/tpdm0/enable_source
> > echo 1 > /sys/bus/coresight/devices/tpdm0/integration_test
> > echo 2 > /sys/bus/coresight/devices/tpdm0/integration_test
> > cat /dev/tmc_etf0 > /data/etf-tpdm0.bin
> > echo 0 > /sys/bus/coresight/devices/tpdm0/enable_source
> > echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> > echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> > echo 1 > /sys/bus/coresight/devices/tpdm1/enable_source
> > echo 1 > /sys/bus/coresight/devices/tpdm1/integration_test
> > echo 2 > /sys/bus/coresight/devices/tpdm1/integration_test
> > cat /dev/tmc_etf0 > /data/etf-tpdm1.bin
> > echo 0 > /sys/bus/coresight/devices/tpdm1/enable_source
> > echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> > echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> > echo 1 > /sys/bus/coresight/devices/tpdm2/enable_source
> > echo 1 > /sys/bus/coresight/devices/tpdm2/integration_test
> > echo 2 > /sys/bus/coresight/devices/tpdm2/integration_test
> > cat /dev/tmc_etf0 > /data/etf-tpdm2.bin
> > echo 0 > /sys/bus/coresight/devices/tpdm2/enable_source
> > echo 0 > /sys/bus/coresight/devices/tmc_etf0/enable_sink
> > 
> 
> 
> 
> > If the data from TPDMs can be obtained from the ETF, it means
> > that the TPDMs verification is successful. At the same time,
> 
> 
> How can we decode the TPDM trace ? Is there a public decoder
> available ?
> 

There is an internal tool on PC host to parse the TPDM trace. 

> > since TPDM0, TPDM1 and TPDM2 are all connected to the same
> > funnel "funnel@6c2d000" and output via different output ports,
> > it also means that the following patches verification is
> > successful.
> > coresight: add support to enable more coresight paths
> > coresight: funnel: add support for multiple output ports
> > 
> > Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> > ---
> >   arch/arm64/boot/dts/qcom/qrb5165-rb5.dts | 439 +++++++++++++++++++++++
> >   1 file changed, 439 insertions(+)
> > 
> > diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
> > index 8ac96f8e79d4..bcec8b181e11 100644
> > --- a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
> > +++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts
> > @@ -222,6 +222,445 @@
> 
> 
> > +
> > +	funnel@6b04000 {
> > +		compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
> > +		arm,primecell-periphid = <0x000bb908>;
> > +
> > +		reg = <0 0x6b04000 0 0x1000>;
> > +		reg-names = "funnel-base";
> > +
> > +		clocks = <&aoss_qmp>;
> > +		clock-names = "apb_pclk";
> > +
> > +		out-ports {
> > +			port {
> > +				merge_funnel_out: endpoint {
> > +					remote-endpoint =
> > +						<&etf_in>;
> > +				};
> > +			};
> > +		};
> > +
> > +		in-ports {
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +
> > +			port@7 {
> > +				reg = <7>;
> > +				swao_funnel_in7: endpoint {
> 
> > +					slave-mode;
> 
> This is obsolete, with the new in-ports/out-ports construct.
> 

We will check and update.

> Suzuki

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

* Re: [PATCH 03/10] Coresight: Add driver to support Coresight device TPDM
  2021-11-04  9:37     ` Suzuki K Poulose
@ 2021-11-05  8:12       ` Jinlong
  0 siblings, 0 replies; 37+ messages in thread
From: Jinlong @ 2021-11-05  8:12 UTC (permalink / raw)
  To: Suzuki K Poulose
  Cc: Mathieu Poirier, Tao Zhang, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni

Good afternoon, Suzuki.  

On Thu, Nov 04, 2021 at 09:37:05AM +0000, Suzuki K Poulose wrote:
> Tao,
> 
> Some additional comments below.
> 
> On 02/11/2021 17:59, Mathieu Poirier wrote:
> > Good morning,
> > 
> > 
> > On Thu, Oct 21, 2021 at 03:38:49PM +0800, Tao Zhang wrote:
> > > Add driver to support Coresight device TPDM. This driver provides
> > > support for configuring monitor. Monitors are primarily responsible
> > > for data set collection and support the ability to collect any
> > > permutation of data set types. Monitors are also responsible for
> > > interaction with system cross triggering.
> > 
> > As far as I can tell there is nothing related to CTIs in this patch.  And if
> > there is, it is not documented.
> > 
> 
> Please could you add a separate file documenting  the TPDM and
> some of the specific details (what is used by the driver, e.g
> PERPHID0/1 et) under Documentation/trace/coresight/ , in a separate
> patch
> 
> > > 
> > > Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> > > ---
> > >   .../bindings/arm/coresight-tpdm.yaml          |  86 +++
> > 
> > As checkpatch says, this should be in a separate file.
> > 
> > >   MAINTAINERS                                   |   5 +
> > 
> > Since this is a coresight device Suzuki and I will continue the maintenance.
> > The get_maintainer script will make sure you care CC'ed on patches related to
> > the TPDM/TPDA drivers, and we would typically requried a "Reviewed-by" tag from
> > you before merging.
> > 
> > >   drivers/hwtracing/coresight/Kconfig           |   9 +
> > >   drivers/hwtracing/coresight/Makefile          |   1 +
> > >   drivers/hwtracing/coresight/coresight-tpdm.c  | 583 ++++++++++++++++++
> > >   5 files changed, 684 insertions(+)
> > >   create mode 100644 Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
> > >   create mode 100644 drivers/hwtracing/coresight/coresight-tpdm.c
> > > 
> > > diff --git a/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml b/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
> > > new file mode 100644
> > > index 000000000000..44541075d77f
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/arm/coresight-tpdm.yaml
> > > @@ -0,0 +1,86 @@
> > > +# SPDX-License-Identifier: GPL-2.0
> > > +# Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/arm/coresight-tpdm.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Trace, Profiling and Diagnostics Monitor - TPDM
> > > +
> > > +description: |
> > > +  The TPDM or Monitor serves as data collection component for various dataset
> > > +  types specified in the QPMDA spec. It covers Basic Counts (BC), Tenure
> > > +  Counts (TC), Continuous Multi-Bit (CMB), and Discrete Single Bit (DSB). It
> > > +  performs data collection in the data producing clock domain and transfers it
> > > +  to the data collection time domain, generally ATB clock domain.
> > > +
> > > +  The primary use case of the TPDM is to collect data from different data
> > > +  sources and send it to a TPDA for packetization, timestamping, and funneling.
> > > +
> > > +maintainers:
> > > +  - Tao Zhang <quic_taozha@quicinc.com>
> > > +
> > > +properties:
> > > +  $nodename:
> > > +    pattern: "^tpdm(@[0-9a-f]+)$"
> > > +  compatible:
> > > +    items:
> > > +      - const: arm,primecell
> 
> 
> You must have a compatible that identifies this as "tpdm", just like
> the other components, even though it is not functional.
>

We will add it.
 
> > > +
> > > +  reg:
> > > +    maxItems: 1
> > > +
> > > +  reg-names:
> > > +    items:
> > > +      - const: tpdm-base
> 
> Is the reg-name really necessary ?
> 

We will check and move it out of required if it is not necessary.

> > > +
> > > +  atid:
> > > +    maxItems: 1
> > > +    description: |
> > > +      The QPMDA specification repurposed the ATID field of the AMBA ATB
> > > +      specification to use it to convey packetization information to the
> > > +      Aggregator.
> 
> Please could you describe how this affects the device in the doc
> requested above.
> 

We will add more info about it. 

> > > +
> > > +  out-ports:
> > > +    description: |
> > > +      Output connections from the TPDM to legacy CoreSight trace bus.
> > > +    $ref: /schemas/graph.yaml#/properties/ports
> > > +    properties:
> > > +      port:
> > > +        description: Output connection from the TPDM to legacy CoreSight
> > > +          Trace bus.
> > > +        $ref: /schemas/graph.yaml#/properties/port
> > > +
> > > +required:
> > > +  - compatible
> > > +  - reg
> > > +  - reg-names
> > > +  - atid
> > > +  - clocks
> > > +  - clock-names
> > > +
> > > +additionalProperties: false
> > > +
> > > +examples:
> > > +  # minimum TPDM definition.
> > > +  - |
> > > +    tpdm@6980000 {
> > > +      compatible = "arm,primecell";
> 
> Like other components, we must have :
> 	  compatible = "qcom,<new-compatible>", "arm,primecell";
> 
> > > +      reg = <0x6980000 0x1000>;
> > > +      reg-names = "tpdm-base";
> > > +
> > > +      clocks = <&aoss_qmp>;
> > > +      clock-names = "apb_pclk";
> > > +
> > > +      atid = <78>;
> > > +      out-ports {
> > > +        port {
> > > +          tpdm_turing_out_funnel_turing: endpoint {
> > > +            remote-endpoint =
> > > +              <&funnel_turing_in_tpdm_turing>;
> > > +          };
> > > +        };
> > > +      };
> > > +    };
> > > +
> > > +...
> 
> > > diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
> > > index b6c4a48140ec..e7392a0dddeb 100644
> > > --- a/drivers/hwtracing/coresight/Makefile
> > > +++ b/drivers/hwtracing/coresight/Makefile
> > > @@ -25,5 +25,6 @@ obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o
> > >   obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o
> > >   obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
> > >   obj-$(CONFIG_CORESIGHT_TRBE) += coresight-trbe.o
> > > +obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o
> > >   coresight-cti-y := coresight-cti-core.o	coresight-cti-platform.o \
> > >   		   coresight-cti-sysfs.o
> > > diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
> > > new file mode 100644
> > > index 000000000000..906776c859d6
> > > --- /dev/null
> > > +++ b/drivers/hwtracing/coresight/coresight-tpdm.c
> 
> ...
> 
> > > +
> > > +#ifdef CONFIG_CORESIGHT_TPDM_DEFAULT_ENABLE
> > > +static int boot_enable = 1;
> > > +#else
> > > +static int boot_enable;
> > > +#endif
> > 
> > That isn't the proper way to do this.  Look at how it is done in
> > coresight-etm4x.c
> > 
> > > +
> > > +struct gpr_dataset {
> > > +	DECLARE_BITMAP(gpr_dirty, TPDM_GPR_REGS_MAX);
> > > +	uint32_t		gp_regs[TPDM_GPR_REGS_MAX];
> > 
> > Shouldn't this be u32?
> > 
> > > +};
> > > +
> > > +struct bc_dataset {
> > > +	enum tpdm_mode		capture_mode;
> > > +	enum tpdm_mode		retrieval_mode;
> > > +	uint32_t		sat_mode;
> > > +	uint32_t		enable_counters;
> > > +	uint32_t		clear_counters;
> > > +	uint32_t		enable_irq;
> > > +	uint32_t		clear_irq;
> > > +	uint32_t		trig_val_lo[TPDM_BC_MAX_COUNTERS];
> > > +	uint32_t		trig_val_hi[TPDM_BC_MAX_COUNTERS];
> > > +	uint32_t		enable_ganging;
> > > +	uint32_t		overflow_val[TPDM_BC_MAX_OVERFLOW];
> > > +	uint32_t		msr[TPDM_BC_MAX_MSR];
> > > +};
> > > +
> > > +struct tc_dataset {
> > > +	enum tpdm_mode		capture_mode;
> > > +	enum tpdm_mode		retrieval_mode;
> > > +	bool			sat_mode;
> > > +	uint32_t		enable_counters;
> > > +	uint32_t		clear_counters;
> > > +	uint32_t		enable_irq;
> > > +	uint32_t		clear_irq;
> > > +	uint32_t		trig_sel[TPDM_TC_MAX_TRIG];
> > > +	uint32_t		trig_val_lo[TPDM_TC_MAX_TRIG];
> > > +	uint32_t		trig_val_hi[TPDM_TC_MAX_TRIG];
> > > +	uint32_t		msr[TPDM_TC_MAX_MSR];
> > > +};
> > > +
> > > +struct dsb_dataset {
> > > +	uint32_t		mode;
> > > +	uint32_t		edge_ctrl[TPDM_DSB_MAX_EDCR];
> > > +	uint32_t		edge_ctrl_mask[TPDM_DSB_MAX_EDCR / 2];
> > > +	uint32_t		patt_val[TPDM_DSB_MAX_PATT];
> > > +	uint32_t		patt_mask[TPDM_DSB_MAX_PATT];
> > > +	bool			patt_ts;
> > > +	bool			patt_type;
> > > +	uint32_t		trig_patt_val[TPDM_DSB_MAX_PATT];
> > > +	uint32_t		trig_patt_mask[TPDM_DSB_MAX_PATT];
> > > +	bool			trig_ts;
> > > +	bool			trig_type;
> > > +	uint32_t		select_val[TPDM_DSB_MAX_SELECT];
> > > +	uint32_t		msr[TPDM_DSB_MAX_MSR];
> > > +};
> > > +
> > > +struct mcmb_dataset {
> > > +	uint8_t		mcmb_trig_lane;
> > > +	uint8_t		mcmb_lane_select;
> > > +};
> > > +
> > > +struct cmb_dataset {
> > > +	bool			trace_mode;
> > > +	uint32_t		cycle_acc;
> > > +	uint32_t		patt_val[TPDM_CMB_PATT_CMP];
> > > +	uint32_t		patt_mask[TPDM_CMB_PATT_CMP];
> > > +	bool			patt_ts;
> > > +	uint32_t		trig_patt_val[TPDM_CMB_PATT_CMP];
> > > +	uint32_t		trig_patt_mask[TPDM_CMB_PATT_CMP];
> > > +	bool			trig_ts;
> > > +	bool			ts_all;
> > > +	uint32_t		msr[TPDM_CMB_MAX_MSR];
> > > +	uint8_t			read_ctl_reg;
> > > +	struct mcmb_dataset	*mcmb;
> > > +};
> > > +
> > > +DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm");
> > > +
> > > +struct tpdm_drvdata {
> > > +	void __iomem		*base;
> > > +	struct device		*dev;
> > > +	struct coresight_device	*csdev;
> > > +	int			nr_tclk;
> > > +	struct clk		**tclk;
> > > +	int			nr_treg;
> > > +	struct regulator	**treg;
> > > +	struct mutex		lock;
> > > +	bool			enable;
> > > +	bool			clk_enable;
> > > +	DECLARE_BITMAP(datasets, TPDM_DATASETS);
> > > +	DECLARE_BITMAP(enable_ds, TPDM_DATASETS);
> > > +	enum tpdm_support_type	tc_trig_type;
> > > +	enum tpdm_support_type	bc_trig_type;
> > > +	enum tpdm_support_type	bc_gang_type;
> > > +	uint32_t		bc_counters_avail;
> > > +	uint32_t		tc_counters_avail;
> > > +	struct gpr_dataset	*gpr;
> > > +	struct bc_dataset	*bc;
> > > +	struct tc_dataset	*tc;
> > > +	struct dsb_dataset	*dsb;
> > > +	struct cmb_dataset	*cmb;
> > > +	int			traceid;
> > > +	uint32_t		version;
> > > +	bool			msr_support;
> > > +	bool			msr_fix_req;
> > > +	bool			cmb_msr_skip;
> > > +};
> > 
> > All of these should also be in a header file and properly documented.
> > 
> > > +
> > > +static void tpdm_init_default_data(struct tpdm_drvdata *drvdata);
> > 
> > This isn't needed.
> > 
> > > +
> > > +static void __tpdm_enable(struct tpdm_drvdata *drvdata)
> > > +{
> > > +	TPDM_UNLOCK(drvdata);
> > > +
> > > +	if (drvdata->clk_enable)
> > > +		tpdm_writel(drvdata, 0x1, TPDM_CLK_CTRL);
> > > +
> > > +	TPDM_LOCK(drvdata);
> > > +}
> > > +
> 
> > > +static const struct coresight_ops_source tpdm_source_ops = {
> > > +	.trace_id	= tpdm_trace_id,
> > > +	.enable		= tpdm_enable,
> > > +	.disable	= tpdm_disable,
> > > +};
> > > +
> > > +static const struct coresight_ops tpdm_cs_ops = {
> > > +	.source_ops	= &tpdm_source_ops,
> > > +};
> > > +
> > > +static int tpdm_datasets_alloc(struct tpdm_drvdata *drvdata)
> > > +{
> > > +	if (test_bit(TPDM_DS_GPR, drvdata->datasets)) {
> > > +		drvdata->gpr = devm_kzalloc(drvdata->dev, sizeof(*drvdata->gpr),
> > > +					    GFP_KERNEL);
> > > +		if (!drvdata->gpr)
> > > +			return -ENOMEM;
> > > +	}
> > > +	if (test_bit(TPDM_DS_BC, drvdata->datasets)) {
> > > +		drvdata->bc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->bc),
> > > +					   GFP_KERNEL);
> > > +		if (!drvdata->bc)
> > > +			return -ENOMEM;
> > > +	}
> > > +	if (test_bit(TPDM_DS_TC, drvdata->datasets)) {
> > > +		drvdata->tc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->tc),
> > > +					   GFP_KERNEL);
> > > +		if (!drvdata->tc)
> > > +			return -ENOMEM;
> > > +	}
> > > +	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
> > > +		drvdata->dsb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->dsb),
> > > +					    GFP_KERNEL);
> > > +		if (!drvdata->dsb)
> > > +			return -ENOMEM;
> > > +	}
> > > +	if (test_bit(TPDM_DS_CMB, drvdata->datasets)) {
> > > +		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
> > > +					    GFP_KERNEL);
> > > +		if (!drvdata->cmb)
> > > +			return -ENOMEM;
> > > +	} else if (test_bit(TPDM_DS_MCMB, drvdata->datasets)) {
> > > +		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
> > > +					    GFP_KERNEL);
> > > +		if (!drvdata->cmb)
> > > +			return -ENOMEM;
> > > +		drvdata->cmb->mcmb = devm_kzalloc(drvdata->dev,
> > > +						  sizeof(*drvdata->cmb->mcmb),
> > > +						  GFP_KERNEL);
> > > +		if (!drvdata->cmb->mcmb)
> > > +			return -ENOMEM;
> > 
> > How can I understand what the above does when:
> > 
> > 1) There isn't a single line of comments.
> > 2) I don't know the HW.
> > 3) I don't have access to the documentation.
> 
> +1
> 
> > 
> > > +	}
> > > +	return 0;
> > > +}
> > > +
> > > +static void tpdm_init_default_data(struct tpdm_drvdata *drvdata)
> > > +{
> > > +	if (test_bit(TPDM_DS_BC, drvdata->datasets))
> > > +		drvdata->bc->retrieval_mode = TPDM_MODE_ATB;
> > > +
> > > +	if (test_bit(TPDM_DS_TC, drvdata->datasets))
> > > +		drvdata->tc->retrieval_mode = TPDM_MODE_ATB;
> > > +
> > > +	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
> > > +		drvdata->dsb->trig_ts = true;
> > > +		drvdata->dsb->trig_type = false;
> > > +	}
> > > +
> > > +	if (test_bit(TPDM_DS_CMB, drvdata->datasets) ||
> > > +	    test_bit(TPDM_DS_MCMB, drvdata->datasets))
> > > +		drvdata->cmb->trig_ts = true;
> > > +}
> > > +
> > > +static int tpdm_parse_of_data(struct tpdm_drvdata *drvdata)
> > > +{
> > > +	int i, ret;
> > > +	const char *tclk_name, *treg_name;
> > > +	struct device_node *node = drvdata->dev->of_node;
> > > +
> > > +	drvdata->clk_enable = of_property_read_bool(node, "qcom,clk-enable");
> > > +	drvdata->msr_fix_req = of_property_read_bool(node, "qcom,msr-fix-req");
> > > +	drvdata->cmb_msr_skip = of_property_read_bool(node,
> > > +					"qcom,cmb-msr-skip");
> > > +
> 
> These properties must be listed as optional/mandatory in the DT binding
> with proper description.

We will add them to dt binding doc.
> 
> > > +	drvdata->nr_tclk = of_property_count_strings(node, "qcom,tpdm-clks");
> > > +	if (drvdata->nr_tclk > 0) {
> > > +		drvdata->tclk = devm_kzalloc(drvdata->dev, drvdata->nr_tclk *
> > > +					     sizeof(*drvdata->tclk),
> > > +					     GFP_KERNEL);
> > > +		if (!drvdata->tclk)
> > > +			return -ENOMEM;
> > > +
> > > +		for (i = 0; i < drvdata->nr_tclk; i++) {
> > > +			ret = of_property_read_string_index(node,
> > > +					    "qcom,tpdm-clks", i, &tclk_name);
> > > +			if (ret)
> > > +				return ret;
> > > +
> > > +			drvdata->tclk[i] = devm_clk_get(drvdata->dev,
> > > +							tclk_name);
> > > +			if (IS_ERR(drvdata->tclk[i]))
> > > +				return PTR_ERR(drvdata->tclk[i]);
> > > +		}
> > > +	}
> > > +
> > > +	drvdata->nr_treg = of_property_count_strings(node, "qcom,tpdm-regs");
> 
> Where is this documented in the DT ?
>

We will add the doc for this.
 
> > > +	if (drvdata->nr_treg > 0) {
> > > +		drvdata->treg = devm_kzalloc(drvdata->dev, drvdata->nr_treg *
> > > +					     sizeof(*drvdata->treg),
> > > +					     GFP_KERNEL);
> > > +		if (!drvdata->treg)
> > > +			return -ENOMEM;
> > > +
> > > +		for (i = 0; i < drvdata->nr_treg; i++) {
> > > +			ret = of_property_read_string_index(node,
> > > +					    "qcom,tpdm-regs", i, &treg_name);
> > > +			if (ret)
> > > +				return ret;
> > > +
> > > +			drvdata->treg[i] = devm_regulator_get(drvdata->dev,
> > > +							treg_name);
> > > +			if (IS_ERR(drvdata->treg[i]))
> > > +				return PTR_ERR(drvdata->treg[i]);
> > > +		}
> > > +	}
> > 
> > _None_ of the above are defined in the yaml file and/or part of the example that
> > is shown there.  Moreover they don't appear in patch 10/10 where TPDM and TPDA are
> > supposed to be introduced.  I will comment on patch 10/10 when I'm done with
> > this one.
> 
> +1
> 
> Suzuki

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

* Re: [PATCH 03/10] Coresight: Add driver to support Coresight device TPDM
  2021-11-04 16:55       ` Mathieu Poirier
@ 2021-11-05  8:15         ` Jinlong
  0 siblings, 0 replies; 37+ messages in thread
From: Jinlong @ 2021-11-05  8:15 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: Tao Zhang, Suzuki K Poulose, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni

Good afternoon. 

On Thu, Nov 04, 2021 at 10:55:04AM -0600, Mathieu Poirier wrote:
> [...]
> 
> > 
> > > > +
> > > > +#ifdef CONFIG_CORESIGHT_TPDM_DEFAULT_ENABLE
> > > > +static int boot_enable = 1;
> > > > +#else
> > > > +static int boot_enable;
> > > > +#endif
> > > 
> > > That isn't the proper way to do this.  Look at how it is done in
> > > coresight-etm4x.c
> > > 
> > > > +
> > > > +struct gpr_dataset {
> > > > +	DECLARE_BITMAP(gpr_dirty, TPDM_GPR_REGS_MAX);
> > > > +	uint32_t		gp_regs[TPDM_GPR_REGS_MAX];
> > > 
> > > Shouldn't this be u32?
> > > 
> > 
> > uint32_t is the same as u32.
> > typedef u32			uint32_t;
> 
> Right - but the common kernel convention is to use u64/32/16/8.  Please refactor
> for the entire patchset.
> 

We will address your comments.

> > 
> > > > +};
> > > > +
> > > > +struct bc_dataset {
> > > > +	enum tpdm_mode		capture_mode;
> > > > +	enum tpdm_mode		retrieval_mode;
> > > > +	uint32_t		sat_mode;
> > > > +	uint32_t		enable_counters;
> > > > +	uint32_t		clear_counters;
> > > > +	uint32_t		enable_irq;
> > > > +	uint32_t		clear_irq;
> > > > +	uint32_t		trig_val_lo[TPDM_BC_MAX_COUNTERS];
> > > > +	uint32_t		trig_val_hi[TPDM_BC_MAX_COUNTERS];
> > > > +	uint32_t		enable_ganging;
> > > > +	uint32_t		overflow_val[TPDM_BC_MAX_OVERFLOW];
> > > > +	uint32_t		msr[TPDM_BC_MAX_MSR];
> > > > +};
> > > > +
> > > > +struct tc_dataset {
> > > > +	enum tpdm_mode		capture_mode;
> > > > +	enum tpdm_mode		retrieval_mode;
> > > > +	bool			sat_mode;
> > > > +	uint32_t		enable_counters;
> > > > +	uint32_t		clear_counters;
> > > > +	uint32_t		enable_irq;
> > > > +	uint32_t		clear_irq;
> > > > +	uint32_t		trig_sel[TPDM_TC_MAX_TRIG];
> > > > +	uint32_t		trig_val_lo[TPDM_TC_MAX_TRIG];
> > > > +	uint32_t		trig_val_hi[TPDM_TC_MAX_TRIG];
> > > > +	uint32_t		msr[TPDM_TC_MAX_MSR];
> > > > +};
> > > > +
> > > > +struct dsb_dataset {
> > > > +	uint32_t		mode;
> > > > +	uint32_t		edge_ctrl[TPDM_DSB_MAX_EDCR];
> > > > +	uint32_t		edge_ctrl_mask[TPDM_DSB_MAX_EDCR / 2];
> > > > +	uint32_t		patt_val[TPDM_DSB_MAX_PATT];
> > > > +	uint32_t		patt_mask[TPDM_DSB_MAX_PATT];
> > > > +	bool			patt_ts;
> > > > +	bool			patt_type;
> > > > +	uint32_t		trig_patt_val[TPDM_DSB_MAX_PATT];
> > > > +	uint32_t		trig_patt_mask[TPDM_DSB_MAX_PATT];
> > > > +	bool			trig_ts;
> > > > +	bool			trig_type;
> > > > +	uint32_t		select_val[TPDM_DSB_MAX_SELECT];
> > > > +	uint32_t		msr[TPDM_DSB_MAX_MSR];
> > > > +};
> > > > +
> > > > +struct mcmb_dataset {
> > > > +	uint8_t		mcmb_trig_lane;
> > > > +	uint8_t		mcmb_lane_select;
> > > > +};
> > > > +
> > > > +struct cmb_dataset {
> > > > +	bool			trace_mode;
> > > > +	uint32_t		cycle_acc;
> > > > +	uint32_t		patt_val[TPDM_CMB_PATT_CMP];
> > > > +	uint32_t		patt_mask[TPDM_CMB_PATT_CMP];
> > > > +	bool			patt_ts;
> > > > +	uint32_t		trig_patt_val[TPDM_CMB_PATT_CMP];
> > > > +	uint32_t		trig_patt_mask[TPDM_CMB_PATT_CMP];
> > > > +	bool			trig_ts;
> > > > +	bool			ts_all;
> > > > +	uint32_t		msr[TPDM_CMB_MAX_MSR];
> > > > +	uint8_t			read_ctl_reg;
> > > > +	struct mcmb_dataset	*mcmb;
> > > > +};
> > > > +
> > > > +DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm");
> > > > +
> > > > +struct tpdm_drvdata {
> > > > +	void __iomem		*base;
> > > > +	struct device		*dev;
> > > > +	struct coresight_device	*csdev;
> > > > +	int			nr_tclk;
> > > > +	struct clk		**tclk;
> > > > +	int			nr_treg;
> > > > +	struct regulator	**treg;
> > > > +	struct mutex		lock;
> > > > +	bool			enable;
> > > > +	bool			clk_enable;
> > > > +	DECLARE_BITMAP(datasets, TPDM_DATASETS);
> > > > +	DECLARE_BITMAP(enable_ds, TPDM_DATASETS);
> > > > +	enum tpdm_support_type	tc_trig_type;
> > > > +	enum tpdm_support_type	bc_trig_type;
> > > > +	enum tpdm_support_type	bc_gang_type;
> > > > +	uint32_t		bc_counters_avail;
> > > > +	uint32_t		tc_counters_avail;
> > > > +	struct gpr_dataset	*gpr;
> > > > +	struct bc_dataset	*bc;
> > > > +	struct tc_dataset	*tc;
> > > > +	struct dsb_dataset	*dsb;
> > > > +	struct cmb_dataset	*cmb;
> > > > +	int			traceid;
> > > > +	uint32_t		version;
> > > > +	bool			msr_support;
> > > > +	bool			msr_fix_req;
> > > > +	bool			cmb_msr_skip;
> > > > +};
> > > 
> > > All of these should also be in a header file and properly documented.
> > > 
> > 
> > We will move these to header file and add the documentation.
> > 
> > > > +
> > > > +static void tpdm_init_default_data(struct tpdm_drvdata *drvdata);
> > > 
> > > This isn't needed.
> > 
> > We will remove this.
> > > 
> > > > +
> > > > +static void __tpdm_enable(struct tpdm_drvdata *drvdata)
> > > > +{
> > > > +	TPDM_UNLOCK(drvdata);
> > > > +
> > > > +	if (drvdata->clk_enable)
> > > > +		tpdm_writel(drvdata, 0x1, TPDM_CLK_CTRL);
> > > > +
> > > > +	TPDM_LOCK(drvdata);
> > > > +}
> > > > +
> > > > +static int tpdm_enable(struct coresight_device *csdev,
> > > > +		       struct perf_event *event, u32 mode)
> > > > +{
> > > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> > > > +	int ret = 0;
> > > > +
> > > > +	if (drvdata->enable) {
> > > 
> > > This a race condition.
> > >
> > 
> > Need to add it to mutex_lock. We will update.
> >  
> > > > +		dev_err(drvdata->dev,
> > > > +			"TPDM setup already enabled,Skipping enablei\n");
> > > 
> > > Please remove this.
> > > 
> > 
> > We will remove this.
> > 
> > > > +		return ret;
> > > 
> > > Shouldn't this be return -EBUSY? 
> > 
> > We will address your comments.
> > 
> > > 
> > > > +	}
> > > > +
> > > > +	mutex_lock(&drvdata->lock);
> > > > +	__tpdm_enable(drvdata);
> > > > +	drvdata->enable = true;
> > > > +	mutex_unlock(&drvdata->lock);
> > > > +
> > > > +	dev_info(drvdata->dev, "TPDM tracing enabled\n");
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static void __tpdm_disable(struct tpdm_drvdata *drvdata)
> > > > +{
> > > > +	TPDM_UNLOCK(drvdata);
> > > > +
> > > > +	if (drvdata->clk_enable)
> > > > +		tpdm_writel(drvdata, 0x0, TPDM_CLK_CTRL);
> > > > +
> > > > +	TPDM_LOCK(drvdata);
> > > > +}
> > > > +
> > > > +static void tpdm_disable(struct coresight_device *csdev,
> > > > +			 struct perf_event *event)
> > > > +{
> > > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> > > > +
> > > > +	if (!drvdata->enable) {
> > > > +		dev_err(drvdata->dev,
> > > > +			"TPDM setup already disabled, Skipping disable\n");
> > > > +		return;
> > > > +	}
> > > > +	mutex_lock(&drvdata->lock);
> > > > +	__tpdm_disable(drvdata);
> > > > +	drvdata->enable = false;
> > > > +	mutex_unlock(&drvdata->lock);
> > > 
> > > Same comments as above.
> > > 
> > 
> > We will address your comments.
> > 
> > > > +
> > > > +	dev_info(drvdata->dev, "TPDM tracing disabled\n");
> > > > +}
> > > > +
> > > > +static int tpdm_trace_id(struct coresight_device *csdev)
> > > > +{
> > > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> > > > +
> > > > +	return drvdata->traceid;
> > > > +}
> > > > +
> > > > +static const struct coresight_ops_source tpdm_source_ops = {
> > > > +	.trace_id	= tpdm_trace_id,
> > > > +	.enable		= tpdm_enable,
> > > > +	.disable	= tpdm_disable,
> > > > +};
> > > > +
> > > > +static const struct coresight_ops tpdm_cs_ops = {
> > > > +	.source_ops	= &tpdm_source_ops,
> > > > +};
> > > > +
> > > > +static int tpdm_datasets_alloc(struct tpdm_drvdata *drvdata)
> > > > +{
> > > > +	if (test_bit(TPDM_DS_GPR, drvdata->datasets)) {
> > > > +		drvdata->gpr = devm_kzalloc(drvdata->dev, sizeof(*drvdata->gpr),
> > > > +					    GFP_KERNEL);
> > > > +		if (!drvdata->gpr)
> > > > +			return -ENOMEM;
> > > > +	}
> > > > +	if (test_bit(TPDM_DS_BC, drvdata->datasets)) {
> > > > +		drvdata->bc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->bc),
> > > > +					   GFP_KERNEL);
> > > > +		if (!drvdata->bc)
> > > > +			return -ENOMEM;
> > > > +	}
> > > > +	if (test_bit(TPDM_DS_TC, drvdata->datasets)) {
> > > > +		drvdata->tc = devm_kzalloc(drvdata->dev, sizeof(*drvdata->tc),
> > > > +					   GFP_KERNEL);
> > > > +		if (!drvdata->tc)
> > > > +			return -ENOMEM;
> > > > +	}
> > > > +	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
> > > > +		drvdata->dsb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->dsb),
> > > > +					    GFP_KERNEL);
> > > > +		if (!drvdata->dsb)
> > > > +			return -ENOMEM;
> > > > +	}
> > > > +	if (test_bit(TPDM_DS_CMB, drvdata->datasets)) {
> > > > +		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
> > > > +					    GFP_KERNEL);
> > > > +		if (!drvdata->cmb)
> > > > +			return -ENOMEM;
> > > > +	} else if (test_bit(TPDM_DS_MCMB, drvdata->datasets)) {
> > > > +		drvdata->cmb = devm_kzalloc(drvdata->dev, sizeof(*drvdata->cmb),
> > > > +					    GFP_KERNEL);
> > > > +		if (!drvdata->cmb)
> > > > +			return -ENOMEM;
> > > > +		drvdata->cmb->mcmb = devm_kzalloc(drvdata->dev,
> > > > +						  sizeof(*drvdata->cmb->mcmb),
> > > > +						  GFP_KERNEL);
> > > > +		if (!drvdata->cmb->mcmb)
> > > > +			return -ENOMEM;
> > > 
> > > How can I understand what the above does when:
> > > 
> > > 1) There isn't a single line of comments.
> > > 2) I don't know the HW.
> > > 3) I don't have access to the documentation.
> > > 
> > 
> > We will add comments here.
> > 
> > > > +	}
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static void tpdm_init_default_data(struct tpdm_drvdata *drvdata)
> > > > +{
> > > > +	if (test_bit(TPDM_DS_BC, drvdata->datasets))
> > > > +		drvdata->bc->retrieval_mode = TPDM_MODE_ATB;
> > > > +
> > > > +	if (test_bit(TPDM_DS_TC, drvdata->datasets))
> > > > +		drvdata->tc->retrieval_mode = TPDM_MODE_ATB;
> > > > +
> > > > +	if (test_bit(TPDM_DS_DSB, drvdata->datasets)) {
> > > > +		drvdata->dsb->trig_ts = true;
> > > > +		drvdata->dsb->trig_type = false;
> > > > +	}
> > > > +
> > > > +	if (test_bit(TPDM_DS_CMB, drvdata->datasets) ||
> > > > +	    test_bit(TPDM_DS_MCMB, drvdata->datasets))
> > > > +		drvdata->cmb->trig_ts = true;
> > > > +}
> > > > +
> > > > +static int tpdm_parse_of_data(struct tpdm_drvdata *drvdata)
> > > > +{
> > > > +	int i, ret;
> > > > +	const char *tclk_name, *treg_name;
> > > > +	struct device_node *node = drvdata->dev->of_node;
> > > > +
> > > > +	drvdata->clk_enable = of_property_read_bool(node, "qcom,clk-enable");
> > > > +	drvdata->msr_fix_req = of_property_read_bool(node, "qcom,msr-fix-req");
> > > > +	drvdata->cmb_msr_skip = of_property_read_bool(node,
> > > > +					"qcom,cmb-msr-skip");
> > > > +
> > > > +	drvdata->nr_tclk = of_property_count_strings(node, "qcom,tpdm-clks");
> > > > +	if (drvdata->nr_tclk > 0) {
> > > > +		drvdata->tclk = devm_kzalloc(drvdata->dev, drvdata->nr_tclk *
> > > > +					     sizeof(*drvdata->tclk),
> > > > +					     GFP_KERNEL);
> > > > +		if (!drvdata->tclk)
> > > > +			return -ENOMEM;
> > > > +
> > > > +		for (i = 0; i < drvdata->nr_tclk; i++) {
> > > > +			ret = of_property_read_string_index(node,
> > > > +					    "qcom,tpdm-clks", i, &tclk_name);
> > > > +			if (ret)
> > > > +				return ret;
> > > > +
> > > > +			drvdata->tclk[i] = devm_clk_get(drvdata->dev,
> > > > +							tclk_name);
> > > > +			if (IS_ERR(drvdata->tclk[i]))
> > > > +				return PTR_ERR(drvdata->tclk[i]);
> > > > +		}
> > > > +	}
> > > > +
> > > > +	drvdata->nr_treg = of_property_count_strings(node, "qcom,tpdm-regs");
> > > > +	if (drvdata->nr_treg > 0) {
> > > > +		drvdata->treg = devm_kzalloc(drvdata->dev, drvdata->nr_treg *
> > > > +					     sizeof(*drvdata->treg),
> > > > +					     GFP_KERNEL);
> > > > +		if (!drvdata->treg)
> > > > +			return -ENOMEM;
> > > > +
> > > > +		for (i = 0; i < drvdata->nr_treg; i++) {
> > > > +			ret = of_property_read_string_index(node,
> > > > +					    "qcom,tpdm-regs", i, &treg_name);
> > > > +			if (ret)
> > > > +				return ret;
> > > > +
> > > > +			drvdata->treg[i] = devm_regulator_get(drvdata->dev,
> > > > +							treg_name);
> > > > +			if (IS_ERR(drvdata->treg[i]))
> > > > +				return PTR_ERR(drvdata->treg[i]);
> > > > +		}
> > > > +	}
> > > 
> > > _None_ of the above are defined in the yaml file and/or part of the example that
> > > is shown there.  Moreover they don't appear in patch 10/10 where TPDM and TPDA are
> > > supposed to be introduced.  I will comment on patch 10/10 when I'm done with
> > > this one.
> > > 
> > 
> > We will update the dtsi change in next version.
> > 
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
> > > > +{
> > > > +	int ret, i;
> > > > +	uint32_t pidr, devid;
> > > > +	struct device *dev = &adev->dev;
> > > > +	struct coresight_platform_data *pdata;
> > > > +	struct tpdm_drvdata *drvdata;
> > > > +	struct coresight_desc desc = { 0 };
> > > > +	static int traceid = TPDM_TRACE_ID_START;
> > > > +	uint32_t version;
> > > > +
> > > > +	desc.name = coresight_alloc_device_name(&tpdm_devs, dev);
> > > > +	if (!desc.name)
> > > > +		return -ENOMEM;
> > > > +	pdata = coresight_get_platform_data(dev);
> > > > +	if (IS_ERR(pdata))
> > > > +		return PTR_ERR(pdata);
> > > > +	adev->dev.platform_data = pdata;
> > > > +
> > > > +	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> > > > +	if (!drvdata)
> > > > +		return -ENOMEM;
> > > > +	drvdata->dev = &adev->dev;
> > > > +	dev_set_drvdata(dev, drvdata);
> > > > +
> > > > +	drvdata->base = devm_ioremap_resource(dev, &adev->res);
> > > > +	if (!drvdata->base)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	mutex_init(&drvdata->lock);
> > > > +
> > > > +	ret = tpdm_parse_of_data(drvdata);
> > > > +	if (ret) {
> > > > +		dev_err(drvdata->dev, "TPDM parse of data fail\n");
> > > > +		return -EINVAL;
> > > > +	}
> > > > +
> > > > +	desc.type = CORESIGHT_DEV_TYPE_SOURCE;
> > > > +	desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
> > > 
> > > Why is this of subtype CORESIGHT_DEV_SUBTYPE_SOURCE_PROC when TPDMs are not
> > > associated with a CPU?  Here we should probably introduce something like
> > > CORESIGHT_DEV_SUBTYPE_SOURCE_SYS
> > 
> > 
> > We will check and update.
> > 
> > > 
> > > > +	desc.ops = &tpdm_cs_ops;
> > > > +	desc.pdata = adev->dev.platform_data;
> > > > +	desc.dev = &adev->dev;
> > > > +	drvdata->csdev = coresight_register(&desc);
> > > > +	if (IS_ERR(drvdata->csdev))
> > > > +		return PTR_ERR(drvdata->csdev);
> > > > +
> > > > +	version = tpdm_readl(drvdata, CORESIGHT_PERIPHIDR2);
> > > > +	drvdata->version = BMVAL(version, 4, 7);
> > > > +
> > > 
> > > What is MSR support?  This should be documented.  Looking more closely at this
> > > patch, not a single line of documentation is provided.  If you look at other
> > > drivers, you will find things to be quite different.  The coresight subsystem
> > > has become very complex and documentation helps us, and anyone trying to
> > > contribute, understand what the code does.
> > > 
> > 
> > We will add more documentation in the driver.
> > 
> > 
> > > > +	if (drvdata->version)
> > > > +		drvdata->msr_support = true;
> > > > +
> > > > +	pidr = tpdm_readl(drvdata, CORESIGHT_PERIPHIDR0);
> > > > +	for (i = 0; i < TPDM_DATASETS; i++) {
> > > > +		if (pidr & BIT(i)) {
> > > > +			__set_bit(i, drvdata->datasets);
> > > > +			__set_bit(i, drvdata->enable_ds);
> > > > +		}
> > > > +	}
> > > > +
> > > > +	ret = tpdm_datasets_alloc(drvdata);
> > > > +	if (ret) {
> > > > +		coresight_unregister(drvdata->csdev);
> > > > +		return ret;
> > > > +	}
> > > > +
> > > > +	tpdm_init_default_data(drvdata);
> > > > +
> > > > +	devid = tpdm_readl(drvdata, CORESIGHT_DEVID);
> > > > +	drvdata->tc_trig_type = BMVAL(devid, 27, 28);
> > > > +	drvdata->bc_trig_type = BMVAL(devid, 25, 26);
> > > > +	drvdata->bc_gang_type = BMVAL(devid, 23, 24);
> > > > +	drvdata->bc_counters_avail = BMVAL(devid, 6, 10) + 1;
> > > > +	drvdata->tc_counters_avail = BMVAL(devid, 4, 5) + 1;
> > > 
> > > Shouldn't this be part of tpdm_init_default_data()?
> > > 
> > 
> > We will address your comments.
> > 
> > > > +
> > > > +	drvdata->traceid = traceid++;
> > > 
> > > For this to work a new function needs to be introduced in coresight-pmu.h.
> > > Something like:
> > > 
> > > static inline int coresight_get_system_trace_id(int id)
> > > {
> > >         /* Start system IDs above the highest per CPU trace ID. */
> > >         return coresigth_get_trace_id(cpumask_last(cpu_possible_mask) + 1);
> > > }
> > > 
> > 
> > We will address your comments.
> > 
> > > > +
> > > > +	dev_dbg(drvdata->dev, "TPDM initialized\n");
> > > 
> > > Please remove this.
> > >
> > 
> > We will remove it.
> >  
> > > > +
> > > > +	if (boot_enable)
> > > > +		coresight_enable(drvdata->csdev);
> > > > +
> > > > +	pm_runtime_put(&adev->dev);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static struct amba_id tpdm_ids[] = {
> > > > +	{
> > > > +		.id     = 0x001f0e00,
> > > > +		.mask   = 0x00ffff00,
> > > 
> > > Any way we can use CS_AMBA_ID() here?
> > > 
> > 
> > We will address your comments.
> > 
> > > > +		.data	= "TPDM",
> > > 
> > > What is .data used for?
> > 
> > It is just to fill the structure. We will remove it if it is not necessary.
> > 
> > > 
> > > > +	},
> > > > +	{ 0, 0},
> > > > +};
> > > > +
> > > > +static struct amba_driver tpdm_driver = {
> > > > +	.drv = {
> > > > +		.name   = "coresight-tpdm",
> > > > +		.owner	= THIS_MODULE,
> > > > +		.suppress_bind_attrs = true,
> > > > +	},
> > > > +	.probe          = tpdm_probe,
> > > > +	.id_table	= tpdm_ids,
> > > > +};
> > > > +
> > > > +module_amba_driver(tpdm_driver);
> > > > +
> > > > +MODULE_LICENSE("GPL v2");
> > > > +MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Monitor driver");
> > > > -- 
> > > > 2.17.1
> > > > 

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

* Re: [PATCH 04/10] Coresight: Enable BC and GPR for TPDM driver
  2021-11-04 17:02       ` Mathieu Poirier
@ 2021-11-05  8:17         ` Jinlong
  2021-11-05 15:14           ` Mathieu Poirier
  0 siblings, 1 reply; 37+ messages in thread
From: Jinlong @ 2021-11-05  8:17 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: Tao Zhang, Suzuki K Poulose, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni

On Thu, Nov 04, 2021 at 11:02:24AM -0600, Mathieu Poirier wrote:
> [...]
> 
> > > > +
> > > > +static ssize_t reset_store(struct device *dev,
> > > > +					  struct device_attribute *attr,
> > > > +					  const char *buf,
> > > > +					  size_t size)
> > > > +{
> > > > +	int ret = 0;
> > > > +	unsigned long val;
> > > > +	struct mcmb_dataset *mcmb_temp = NULL;
> > > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > > > +
> > > > +	ret = kstrtoul(buf, 10, &val);
> > > 
> > > The coresight subsystem normally uses the hexadecimal base.
> > > 
> > 
> > We will address you comments.
> > 
> > > > +	if (ret)
> > > > +		return ret;
> > > 
> > > Shouldn't this be "if (!ret)" ? 
> > >
> > 
> > When ret is not 0, it need to return.
> 
> I would expect something like this:
> 
> $ echo 1 > /sys/path/to/tpdm/device/reset
> 
> and not
> 
> $ echo 0 > /sys/path/to/tpdm/device/reset
> 
> The latter is what the code does.
> 
> Thanks,
> Mathieu
> 

Hi Mathieu,

The ret is the result of kstrtoul not the val.

Thanks
Jinlong Mao  

> >  
> > > > +
> > > > +	mutex_lock(&drvdata->lock);
> > > > +	/* Reset all datasets to ZERO */
> > > > +	if (drvdata->gpr != NULL)
> > > > +		memset(drvdata->gpr, 0, sizeof(struct gpr_dataset));
> > > > +
> > > > +	if (drvdata->bc != NULL)
> > > > +		memset(drvdata->bc, 0, sizeof(struct bc_dataset));
> > > > +
> > > > +	if (drvdata->tc != NULL)
> > > > +		memset(drvdata->tc, 0, sizeof(struct tc_dataset));
> > > > +
> > > > +	if (drvdata->dsb != NULL)
> > > > +		memset(drvdata->dsb, 0, sizeof(struct dsb_dataset));
> > > > +
> > > > +	if (drvdata->cmb != NULL) {
> > > > +		if (drvdata->cmb->mcmb != NULL) {
> > > > +			mcmb_temp = drvdata->cmb->mcmb;
> > > > +			memset(drvdata->cmb->mcmb, 0,
> > > > +				sizeof(struct mcmb_dataset));
> > > > +			}
> > > > +
> > > > +		memset(drvdata->cmb, 0, sizeof(struct cmb_dataset));
> > > > +		drvdata->cmb->mcmb = mcmb_temp;
> > > > +	}
> > > > +	/* Init the default data */
> > > > +	tpdm_init_default_data(drvdata);
> > > > +
> > > > +	mutex_unlock(&drvdata->lock);
> > > > +
> > > > +	/* Disable tpdm if enabled */
> > > > +	if (drvdata->enable)
> > > > +		coresight_disable(drvdata->csdev);
> > > 
> > > Why is this done out of the lock?
> > > 
> > 
> > When call coresight_disable, tpdm_disable will be called. There is lock in tpdm_disable.
> > If add it into the lock, there will be dead lock.
> > 
> > > > +
> > > > +	return size;
> > > > +}
> > > > +static DEVICE_ATTR_WO(reset);
> > > > +
> > > > +static ssize_t integration_test_store(struct device *dev,
> > > > +					  struct device_attribute *attr,
> > > > +					  const char *buf,
> > > > +					  size_t size)
> > > > +{
> > > > +	int i, ret = 0;
> > > > +	unsigned long val;
> > > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > > > +
> > > > +	ret = kstrtoul(buf, 10, &val);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	if (val != 1 && val != 2)
> > > > +		return -EINVAL;
> > > > +
> > > > +	if (!drvdata->enable)
> > > > +		return -EINVAL;
> > > > +
> > > > +	if (val == 1)
> > > > +		val = ATBCNTRL_VAL_64;
> > > > +	else
> > > > +		val = ATBCNTRL_VAL_32;
> > > > +	TPDM_UNLOCK(drvdata);
> > > > +	tpdm_writel(drvdata, 0x1, TPDM_ITCNTRL);
> > > > +
> > > > +	for (i = 1; i < 5; i++)
> > > > +		tpdm_writel(drvdata, val, TPDM_ITATBCNTRL);
> > > > +
> > > > +	tpdm_writel(drvdata, 0, TPDM_ITCNTRL);
> > > > +	TPDM_LOCK(drvdata);
> > > > +	return size;
> > > > +}
> > > > +static DEVICE_ATTR_WO(integration_test);
> > > 
> > > Integration test interface should be conditional to a compile time option.  Have
> > > a look at what was done for CTIs.
> > > 
> > 
> > We will check and update.
> > 
> > > > +
> > > > +static ssize_t gp_regs_show(struct device *dev,
> > > > +				 struct device_attribute *attr,
> > > > +				 char *buf)
> > > > +{
> > > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > > > +	ssize_t size = 0;
> > > > +	int i = 0;
> > > > +
> > > > +	if (!test_bit(TPDM_DS_GPR, drvdata->datasets))
> > > > +		return -EPERM;
> > > 
> > >                 return -EINVAL;
> > > 
> > > > +
> > > > +	mutex_lock(&drvdata->lock);
> > > > +	for (i = 0; i < TPDM_GPR_REGS_MAX; i++) {
> > > > +		if (!test_bit(i, drvdata->gpr->gpr_dirty))
> > > > +			continue;
> > > > +		size += scnprintf(buf + size, PAGE_SIZE - size,
> > > > +				  "Index: 0x%x Value: 0x%x\n", i,
> > > > +				  drvdata->gpr->gp_regs[i]);
> > > 
> > > This should not be - the sysfs interface requires outputs of a single line.
> > > 
> > 
> > We will check and update.
> > 
> > > > +	}
> > > > +	mutex_unlock(&drvdata->lock);
> > > > +	return size;
> > > > +}
> > > > +
> > > > +static ssize_t gp_regs_store(struct device *dev,
> > > > +				  struct device_attribute *attr,
> > > > +				  const char *buf,
> > > > +				  size_t size)
> > > > +{
> > > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > > > +	unsigned long index, val;
> > > > +
> > > > +	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
> > > > +		return -EINVAL;
> > > > +	if (!test_bit(TPDM_DS_GPR, drvdata->datasets) ||
> > > > +	    index >= TPDM_GPR_REGS_MAX)
> > > > +		return -EPERM;
> > > > +
> > > > +	mutex_lock(&drvdata->lock);
> > > > +	drvdata->gpr->gp_regs[index] = val;
> > > > +	__set_bit(index, drvdata->gpr->gpr_dirty);
> > > > +	mutex_unlock(&drvdata->lock);
> > > > +	return size;
> > > > +}
> > > > +static DEVICE_ATTR_RW(gp_regs);
> > > > +
> > > > +static struct attribute *tpdm_attrs[] = {
> > > > +	&dev_attr_available_datasets.attr,
> > > > +	&dev_attr_enable_datasets.attr,
> > > > +	&dev_attr_reset.attr,
> > > > +	&dev_attr_integration_test.attr,
> > > > +	&dev_attr_gp_regs.attr,
> > > > +	NULL,
> > > > +};
> > > 
> > > All new sysfs interface need to be documented.  See here:
> > > 
> > > Documentation/ABI/testing/sysfs-bus-coresight-devices-xyz
> > > 
> > > More comments to come...
> > > 
> > 
> > We will add the comments. 
> > 
> > > Thanks,
> > > Mathieu
> > > 
> > > > +
> > > > +static struct attribute_group tpdm_attr_grp = {
> > > > +	.attrs = tpdm_attrs,
> > > > +};
> > > > +static const struct attribute_group *tpdm_attr_grps[] = {
> > > > +	&tpdm_attr_grp,
> > > > +	NULL,
> > > > +};
> > > > +
> > > >  static int tpdm_datasets_alloc(struct tpdm_drvdata *drvdata)
> > > >  {
> > > >  	if (test_bit(TPDM_DS_GPR, drvdata->datasets)) {
> > > > @@ -513,6 +846,7 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
> > > >  	desc.ops = &tpdm_cs_ops;
> > > >  	desc.pdata = adev->dev.platform_data;
> > > >  	desc.dev = &adev->dev;
> > > > +	desc.groups = tpdm_attr_grps;
> > > >  	drvdata->csdev = coresight_register(&desc);
> > > >  	if (IS_ERR(drvdata->csdev))
> > > >  		return PTR_ERR(drvdata->csdev);
> > > > -- 
> > > > 2.17.1
> > > > 

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

* Re: [PATCH 05/10] Coresight: Add interface for TPDM BC subunit
  2021-11-04 18:01   ` Mathieu Poirier
@ 2021-11-05  8:26     ` Jinlong
  2021-11-12  8:42       ` Jinlong
  0 siblings, 1 reply; 37+ messages in thread
From: Jinlong @ 2021-11-05  8:26 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: Tao Zhang, Suzuki K Poulose, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni

On Thu, Nov 04, 2021 at 12:01:06PM -0600, Mathieu Poirier wrote:
> On Thu, Oct 21, 2021 at 03:38:51PM +0800, Tao Zhang wrote:
> > The BC(Basic Counters) interface has RW, WO and RO fields for
> > controlling BC dataset elements transmitted on ATB flush.
> > The BC data set subunit supports from 1-32 counter instances
> > allowing for collection of BC data sets.
> > 
> > Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> > ---
> >  drivers/hwtracing/coresight/coresight-tpdm.c | 873 +++++++++++++++++++
> >  1 file changed, 873 insertions(+)
> > 
> > diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
> > index c0a01979e42f..0970c69ac8e2 100644
> > --- a/drivers/hwtracing/coresight/coresight-tpdm.c
> > +++ b/drivers/hwtracing/coresight/coresight-tpdm.c
> > @@ -668,6 +668,878 @@ static ssize_t gp_regs_store(struct device *dev,
> >  }
> >  static DEVICE_ATTR_RW(gp_regs);
> >  
> > +static ssize_t bc_capture_mode_show(struct device *dev,
> > +					 struct device_attribute *attr,
> > +					 char *buf)
> 
> Indentation.  I won't repeat this comment but please make sure it is fixed for
> the entire patchset.
>

We will fix it for entire patchset.
 
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	return scnprintf(buf, PAGE_SIZE, "%s\n",
> > +			 drvdata->bc->capture_mode == TPDM_MODE_ATB ?
> > +			 "ATB" : "APB");
> > +}
> > +
> > +static ssize_t bc_capture_mode_store(struct device *dev,
> > +					  struct device_attribute *attr,
> > +					  const char *buf,
> > +					  size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	char str[20] = "";
> 
>         char str[4];
> 
> > +	uint32_t val;
> > +
> > +	if (size >= 20)
> > +		return -EINVAL;
> > +	if (sscanf(buf, "%s", str) != 1)
> 
>         if (sscanf(buf, "%3s", str) != 1)
> 
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> 
>                 return -EINVAL;
> 
> Please make sure this is fixed everywhere, except when -EINVAL is really
> the right error code.
> 

We will check and update.

> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> 
> Why does the device need to be enabled for this operation?  Again no comments...
> 

We will add comments for this.

> > +	}
> > +
> > +	if (!strcmp(str, "ATB")) {
> > +		drvdata->bc->capture_mode = TPDM_MODE_ATB;
> > +	} else if (!strcmp(str, "APB") &&
> > +		   drvdata->bc->retrieval_mode == TPDM_MODE_APB) {
> > +
> > +		TPDM_UNLOCK(drvdata);
> > +		val = tpdm_readl(drvdata, TPDM_BC_CR);
> > +		val = val | BIT(3);
> > +		tpdm_writel(drvdata, val, TPDM_BC_CR);
> > +		TPDM_LOCK(drvdata);
> > +
> > +		drvdata->bc->capture_mode = TPDM_MODE_APB;
> > +	} else {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EINVAL;
> > +	}
> > +
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_capture_mode);
> > +
> > +static ssize_t bc_retrieval_mode_show(struct device *dev,
> > +					   struct device_attribute *attr,
> > +					   char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	return scnprintf(buf, PAGE_SIZE, "%s\n",
> > +			 drvdata->bc->retrieval_mode == TPDM_MODE_ATB ?
> > +			 "ATB" : "APB");
> > +}
> > +
> > +static ssize_t bc_retrieval_mode_store(struct device *dev,
> > +					    struct device_attribute *attr,
> > +					    const char *buf,
> > +					    size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	char str[20] = "";
> > +
> > +	if (size >= 20)
> > +		return -EINVAL;
> > +	if (sscanf(buf, "%s", str) != 1)
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> 
> Same here, I don't know why the device needs to be enabled for this to success.
> Please fix everywhere.
> 
> > +	}
> > +
> > +	if (!strcmp(str, "ATB")) {
> > +		drvdata->bc->retrieval_mode = TPDM_MODE_ATB;
> > +	} else if (!strcmp(str, "APB")) {
> > +		drvdata->bc->retrieval_mode = TPDM_MODE_APB;
> > +	} else {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EINVAL;
> > +	}
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_retrieval_mode);
> > +
> > +static ssize_t bc_reset_counters_store(struct device *dev,
> > +					    struct device_attribute *attr,
> > +					    const char *buf,
> > +					    size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (kstrtoul(buf, 16, &val))
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	if (val) {
> > +		TPDM_UNLOCK(drvdata);
> > +		val = tpdm_readl(drvdata, TPDM_BC_CR);
> > +		val = val | BIT(1);
> > +		tpdm_writel(drvdata, val, TPDM_BC_CR);
> > +		TPDM_LOCK(drvdata);
> > +	}
> > +
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_WO(bc_reset_counters);
> > +
> > +static ssize_t bc_sat_mode_show(struct device *dev,
> > +				     struct device_attribute *attr,
> > +				     char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	return scnprintf(buf, PAGE_SIZE, "%lx\n",
> > +			 (unsigned long)drvdata->bc->sat_mode);
>         
> 	return scnprintf(buf, PAGE_SIZE, "%#x\n", drvdata->bc->sat_mode);
> 
> And everywhere casting in used...
> 
> 

We will upadte it.

> > +}
> > +
> > +static ssize_t bc_sat_mode_store(struct device *dev,
> > +				      struct device_attribute *attr,
> > +				      const char *buf,
> > +				      size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (kstrtoul(buf, 16, &val))
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	drvdata->bc->sat_mode = val;
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_sat_mode);
> > +
> > +static ssize_t bc_enable_counters_show(struct device *dev,
> > +					    struct device_attribute *attr,
> > +					    char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	return scnprintf(buf, PAGE_SIZE, "%lx\n",
> > +			 (unsigned long)drvdata->bc->enable_counters);
> > +}
> > +
> > +static ssize_t bc_enable_counters_store(struct device *dev,
> > +					     struct device_attribute *attr,
> > +					     const char *buf,
> > +					     size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (kstrtoul(buf, 16, &val))
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	drvdata->bc->enable_counters = val;
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_enable_counters);
> > +
> > +static ssize_t bc_clear_counters_show(struct device *dev,
> > +					   struct device_attribute *attr,
> > +					   char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	return scnprintf(buf, PAGE_SIZE, "%lx\n",
> > +			 (unsigned long)drvdata->bc->clear_counters);
> > +}
> > +
> > +static ssize_t bc_clear_counters_store(struct device *dev,
> > +					    struct device_attribute *attr,
> > +					    const char *buf,
> > +					    size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (kstrtoul(buf, 16, &val))
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	drvdata->bc->clear_counters = val;
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_clear_counters);
> > +
> > +static ssize_t bc_enable_irq_show(struct device *dev,
> > +				       struct device_attribute *attr,
> > +				       char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	return scnprintf(buf, PAGE_SIZE, "%lx\n",
> > +			 (unsigned long)drvdata->bc->enable_irq);
> > +}
> > +
> > +static ssize_t bc_enable_irq_store(struct device *dev,
> > +					struct device_attribute *attr,
> > +					const char *buf,
> > +					size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (kstrtoul(buf, 16, &val))
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	drvdata->bc->enable_irq = val;
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_enable_irq);
> > +
> > +static ssize_t bc_clear_irq_show(struct device *dev,
> > +				      struct device_attribute *attr,
> > +				      char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	return scnprintf(buf, PAGE_SIZE, "%lx\n",
> > +			 (unsigned long)drvdata->bc->clear_irq);
> > +}
> > +
> > +static ssize_t bc_clear_irq_store(struct device *dev,
> > +				       struct device_attribute *attr,
> > +				       const char *buf,
> > +				       size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (kstrtoul(buf, 16, &val))
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	drvdata->bc->clear_irq = val;
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_clear_irq);
> > +
> > +static ssize_t bc_trig_val_lo_show(struct device *dev,
> > +					struct device_attribute *attr,
> > +					char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	ssize_t size = 0;
> > +	int i = 0;
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	for (i = 0; i < TPDM_BC_MAX_COUNTERS; i++)
> > +		size += scnprintf(buf + size, PAGE_SIZE - size,
> > +				  "Index: 0x%x Value: 0x%x\n", i,
> > +				  drvdata->bc->trig_val_lo[i]);
> 
> As previously stated, the sysfs interface should output single line and single
> values.  I won't comment on this again, please fix everywhere.
> 

We will fix it.

> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +
> > +static ssize_t bc_trig_val_lo_store(struct device *dev,
> > +					 struct device_attribute *attr,
> > +					 const char *buf,
> > +					 size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long index, val;
> > +
> > +	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets) ||
> > +	    index >= drvdata->bc_counters_avail ||
> > +	    drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_NO ||
> > +	    (drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_PARTIAL && index > 0))
> > +		return -EPERM;
> > 
> 
> This is hard to read and maintain.  Please break it up in multiple if()
> statements.
> 

We will address your comments.

> > +	mutex_lock(&drvdata->lock);
> > +	drvdata->bc->trig_val_lo[index] = val;
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_trig_val_lo);
> > +
> > +static ssize_t bc_trig_val_hi_show(struct device *dev,
> > +					struct device_attribute *attr,
> > +					char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	ssize_t size = 0;
> > +	int i = 0;
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	for (i = 0; i < TPDM_BC_MAX_COUNTERS; i++)
> > +		size += scnprintf(buf + size, PAGE_SIZE - size,
> > +				  "Index: 0x%x Value: 0x%x\n", i,
> > +				  drvdata->bc->trig_val_hi[i]);
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +
> > +static ssize_t bc_trig_val_hi_store(struct device *dev,
> > +					 struct device_attribute *attr,
> > +					 const char *buf,
> > +					 size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long index, val;
> > +
> > +	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets) ||
> > +	    index >= drvdata->bc_counters_avail ||
> > +	    drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_NO ||
> > +	    (drvdata->bc_trig_type == TPDM_SUPPORT_TYPE_PARTIAL && index > 0))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	drvdata->bc->trig_val_hi[index] = val;
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_trig_val_hi);
> > +
> > +static ssize_t bc_enable_ganging_show(struct device *dev,
> > +					   struct device_attribute *attr,
> > +					   char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	return scnprintf(buf, PAGE_SIZE, "%lx\n",
> > +			 (unsigned long)drvdata->bc->enable_ganging);
> > +}
> > +
> > +static ssize_t bc_enable_ganging_store(struct device *dev,
> > +					    struct device_attribute *attr,
> > +					    const char *buf,
> > +					    size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (kstrtoul(buf, 16, &val))
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	drvdata->bc->enable_ganging = val;
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_enable_ganging);
> > +
> > +static ssize_t bc_overflow_val_show(struct device *dev,
> > +					 struct device_attribute *attr,
> > +					 char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	ssize_t size = 0;
> > +	int i = 0;
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	for (i = 0; i < TPDM_BC_MAX_OVERFLOW; i++)
> > +		size += scnprintf(buf + size, PAGE_SIZE - size,
> > +				  "Index: 0x%x Value: 0x%x\n", i,
> > +				  drvdata->bc->overflow_val[i]);
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +
> > +static ssize_t bc_overflow_val_store(struct device *dev,
> > +					  struct device_attribute *attr,
> > +					  const char *buf,
> > +					  size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long index, val;
> > +
> > +	if (sscanf(buf, "%lx %lx", &index, &val) != 2)
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets) ||
> > +	    index >= TPDM_BC_MAX_OVERFLOW)
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	drvdata->bc->overflow_val[index] = val;
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_overflow_val);
> > +
> > +static ssize_t bc_ovsr_show(struct device *dev,
> > +				 struct device_attribute *attr,
> > +				 char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	TPDM_UNLOCK(drvdata);
> > +	val = tpdm_readl(drvdata, TPDM_BC_OVSR);
> > +	TPDM_LOCK(drvdata);
> > +	mutex_unlock(&drvdata->lock);
> > +	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
> > +}
> > +
> > +static ssize_t bc_ovsr_store(struct device *dev,
> > +				  struct device_attribute *attr,
> > +				  const char *buf,
> > +				  size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (kstrtoul(buf, 16, &val))
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	if (val) {
> > +		TPDM_UNLOCK(drvdata);
> > +		tpdm_writel(drvdata, val, TPDM_BC_OVSR);
> > +		TPDM_LOCK(drvdata);
> > +	}
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_ovsr);
> > +
> > +static ssize_t bc_counter_sel_show(struct device *dev,
> > +					struct device_attribute *attr,
> > +					char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	TPDM_UNLOCK(drvdata);
> > +	val = tpdm_readl(drvdata, TPDM_BC_SELR);
> > +	TPDM_LOCK(drvdata);
> > +	mutex_unlock(&drvdata->lock);
> > +	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
> > +}
> > +
> > +static ssize_t bc_counter_sel_store(struct device *dev,
> > +					 struct device_attribute *attr,
> > +					 const char *buf,
> > +					 size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (kstrtoul(buf, 16, &val))
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable || val >= drvdata->bc_counters_avail) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	TPDM_UNLOCK(drvdata);
> > +	tpdm_writel(drvdata, val, TPDM_BC_SELR);
> > +	TPDM_LOCK(drvdata);
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_counter_sel);
> > +
> > +static ssize_t bc_count_val_lo_show(struct device *dev,
> > +					 struct device_attribute *attr,
> > +					 char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	TPDM_UNLOCK(drvdata);
> > +	val = tpdm_readl(drvdata, TPDM_BC_CNTR_LO);
> > +	TPDM_LOCK(drvdata);
> > +	mutex_unlock(&drvdata->lock);
> > +	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
> > +}
> > +
> > +static ssize_t bc_count_val_lo_store(struct device *dev,
> > +					  struct device_attribute *attr,
> > +					  const char *buf,
> > +					  size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val, select;
> > +
> > +	if (kstrtoul(buf, 16, &val))
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	if (val) {
> 
>         if (!val) {
>                 mutex_unlock(&drvdata->lock);
>                 return -EINVAL;               
>         }
> 

We will update it.

> > +		TPDM_UNLOCK(drvdata);
> > +		select = tpdm_readl(drvdata, TPDM_BC_SELR);
> > +
> > +		/* Check if selected counter is disabled */
> > +		if (BMVAL(tpdm_readl(drvdata, TPDM_BC_CNTENSET), select, select)) {
> > +			mutex_unlock(&drvdata->lock);
> > +			return -EPERM;
> > +		}
> > +
> > +		tpdm_writel(drvdata, val, TPDM_BC_CNTR_LO);
> > +		TPDM_LOCK(drvdata);
> > +	}
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_count_val_lo);
> > +
> > +static ssize_t bc_count_val_hi_show(struct device *dev,
> > +					 struct device_attribute *attr,
> > +					 char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	TPDM_UNLOCK(drvdata);
> > +	val = tpdm_readl(drvdata, TPDM_BC_CNTR_HI);
> > +	TPDM_LOCK(drvdata);
> > +	mutex_unlock(&drvdata->lock);
> > +	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
> > +}
> > +
> > +static ssize_t bc_count_val_hi_store(struct device *dev,
> > +					  struct device_attribute *attr,
> > +					  const char *buf,
> > +					  size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val, select;
> > +
> > +	if (kstrtoul(buf, 16, &val))
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	if (val) {
> 
> Same
> 

We will update it.

> > +		TPDM_UNLOCK(drvdata);
> > +		select = tpdm_readl(drvdata, TPDM_BC_SELR);
> > +
> > +		/* Check if selected counter is disabled */
> > +		if (BMVAL(tpdm_readl(drvdata, TPDM_BC_CNTENSET), select, select)) {
> > +			mutex_unlock(&drvdata->lock);
> > +			return -EPERM;
> > +		}
> > +
> > +		tpdm_writel(drvdata, val, TPDM_BC_CNTR_HI);
> > +		TPDM_LOCK(drvdata);
> > +	}
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_count_val_hi);
> > +
> > +static ssize_t bc_shadow_val_lo_show(struct device *dev,
> > +					  struct device_attribute *attr,
> > +					  char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	ssize_t size = 0;
> > +	int i = 0;
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	TPDM_UNLOCK(drvdata);
> > +	for (i = 0; i < drvdata->bc_counters_avail; i++) {
> > +		size += scnprintf(buf + size, PAGE_SIZE - size,
> > +				  "Index: 0x%x Value: 0x%x\n", i,
> > +				  tpdm_readl(drvdata, TPDM_BC_SHADOW_LO(i)));
> > +	}
> > +	TPDM_LOCK(drvdata);
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RO(bc_shadow_val_lo);
> > +
> > +static ssize_t bc_shadow_val_hi_show(struct device *dev,
> > +					  struct device_attribute *attr,
> > +					  char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	ssize_t size = 0;
> > +	int i = 0;
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	TPDM_UNLOCK(drvdata);
> > +	for (i = 0; i < drvdata->bc_counters_avail; i++)
> > +		size += scnprintf(buf + size, PAGE_SIZE - size,
> > +				  "Index: 0x%x Value: 0x%x\n", i,
> > +				  tpdm_readl(drvdata, TPDM_BC_SHADOW_HI(i)));
> > +	TPDM_LOCK(drvdata);
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RO(bc_shadow_val_hi);
> > +
> > +static ssize_t bc_sw_inc_show(struct device *dev,
> > +				   struct device_attribute *attr,
> > +				   char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	TPDM_UNLOCK(drvdata);
> > +	val = tpdm_readl(drvdata, TPDM_BC_SWINC);
> > +	TPDM_LOCK(drvdata);
> > +	mutex_unlock(&drvdata->lock);
> > +	return scnprintf(buf, PAGE_SIZE, "%lx\n", val);
> > +}
> > +
> > +static ssize_t bc_sw_inc_store(struct device *dev,
> > +				    struct device_attribute *attr,
> > +				    const char *buf,
> > +				    size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned long val;
> > +
> > +	if (kstrtoul(buf, 16, &val))
> > +		return -EINVAL;
> > +	if (!test_bit(TPDM_DS_BC, drvdata->enable_ds))
> > +		return -EPERM;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	if (!drvdata->enable) {
> > +		mutex_unlock(&drvdata->lock);
> > +		return -EPERM;
> > +	}
> > +
> > +	if (val) {
> > +		TPDM_UNLOCK(drvdata);
> > +		tpdm_writel(drvdata, val, TPDM_BC_SWINC);
> > +		TPDM_LOCK(drvdata);
> > +	}
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_sw_inc);
> > +
> > +static ssize_t bc_msr_show(struct device *dev,
> > +				struct device_attribute *attr,
> > +				char *buf)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned int i;
> > +	ssize_t len = 0;
> > +
> > +	if (!drvdata->msr_support)
> > +		return -EINVAL;
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	for (i = 0; i < TPDM_BC_MAX_MSR; i++)
> > +		len += scnprintf(buf + len, PAGE_SIZE - len, "%u 0x%x\n",
> > +				 i, drvdata->bc->msr[i]);
> > +
> > +	return len;
> > +}
> > +
> > +static ssize_t bc_msr_store(struct device *dev,
> > +				 struct device_attribute *attr,
> > +				 const char *buf,
> > +				 size_t size)
> > +{
> > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > +	unsigned int num, val;
> > +	int nval;
> > +
> > +	if (!drvdata->msr_support)
> > +		return -EINVAL;
> > +
> > +	if (!test_bit(TPDM_DS_BC, drvdata->datasets))
> > +		return -EPERM;
> > +
> > +	nval = sscanf(buf, "%u %x", &num, &val);
> > +	if (nval != 2)
> > +		return -EINVAL;
> > +
> > +	if (num >= TPDM_BC_MAX_MSR)
> > +		return -EINVAL;
> > +
> > +	mutex_lock(&drvdata->lock);
> > +	drvdata->bc->msr[num] = val;
> > +	mutex_unlock(&drvdata->lock);
> > +	return size;
> > +}
> > +static DEVICE_ATTR_RW(bc_msr);
> > +
> > +static struct attribute *tpdm_bc_attrs[] = {
> > +	&dev_attr_bc_capture_mode.attr,
> > +	&dev_attr_bc_retrieval_mode.attr,
> > +	&dev_attr_bc_reset_counters.attr,
> > +	&dev_attr_bc_sat_mode.attr,
> > +	&dev_attr_bc_enable_counters.attr,
> > +	&dev_attr_bc_clear_counters.attr,
> > +	&dev_attr_bc_enable_irq.attr,
> > +	&dev_attr_bc_clear_irq.attr,
> > +	&dev_attr_bc_trig_val_lo.attr,
> > +	&dev_attr_bc_trig_val_hi.attr,
> > +	&dev_attr_bc_enable_ganging.attr,
> > +	&dev_attr_bc_overflow_val.attr,
> > +	&dev_attr_bc_ovsr.attr,
> > +	&dev_attr_bc_counter_sel.attr,
> > +	&dev_attr_bc_count_val_lo.attr,
> > +	&dev_attr_bc_count_val_hi.attr,
> > +	&dev_attr_bc_shadow_val_lo.attr,
> > +	&dev_attr_bc_shadow_val_hi.attr,
> > +	&dev_attr_bc_sw_inc.attr,
> > +	&dev_attr_bc_msr.attr,
> > +	NULL,
> 
> This will result in a very crowded directory.  Please move under a "bc"
> subdirectory.  And as I commented before, all sysfs entries need to be
> documented under Documentation/ABI/testing.
> 

We will check and update.

> > +};
> > +
> > +static struct attribute_group tpdm_bc_attr_grp = {
> > +	.attrs = tpdm_bc_attrs,
> > +};
> > +
> >  static struct attribute *tpdm_attrs[] = {
> >  	&dev_attr_available_datasets.attr,
> >  	&dev_attr_enable_datasets.attr,
> > @@ -682,6 +1554,7 @@ static struct attribute_group tpdm_attr_grp = {
> >  };
> >  static const struct attribute_group *tpdm_attr_grps[] = {
> >  	&tpdm_attr_grp,
> > +	&tpdm_bc_attr_grp,
> 
> It is quite tedious to review all these options at the same time as the core
> drivers.  I suggest to concentrate on the base functionality for now.  When that
> is merged we can add configuration options such as these.
> 
> I am out of time for this patchset and as such will not review the remaining
> patches - those will have to wait for another iteration.
> 
> Thanks,
> Mathieu


Thank your for all the comments.
We will update the patches.

Thanks
Jinlong Mao 

> 
> >  	NULL,
> >  };
> >  
> > -- 
> > 2.17.1
> > 

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

* Re: [PATCH 04/10] Coresight: Enable BC and GPR for TPDM driver
  2021-11-05  8:17         ` Jinlong
@ 2021-11-05 15:14           ` Mathieu Poirier
  0 siblings, 0 replies; 37+ messages in thread
From: Mathieu Poirier @ 2021-11-05 15:14 UTC (permalink / raw)
  To: Jinlong
  Cc: Tao Zhang, Suzuki K Poulose, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni

On Fri, Nov 05, 2021 at 04:17:54PM +0800, Jinlong wrote:
> On Thu, Nov 04, 2021 at 11:02:24AM -0600, Mathieu Poirier wrote:
> > [...]
> > 
> > > > > +
> > > > > +static ssize_t reset_store(struct device *dev,
> > > > > +					  struct device_attribute *attr,
> > > > > +					  const char *buf,
> > > > > +					  size_t size)
> > > > > +{
> > > > > +	int ret = 0;
> > > > > +	unsigned long val;
> > > > > +	struct mcmb_dataset *mcmb_temp = NULL;
> > > > > +	struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
> > > > > +
> > > > > +	ret = kstrtoul(buf, 10, &val);
> > > > 
> > > > The coresight subsystem normally uses the hexadecimal base.
> > > > 
> > > 
> > > We will address you comments.
> > > 
> > > > > +	if (ret)
> > > > > +		return ret;
> > > > 
> > > > Shouldn't this be "if (!ret)" ? 
> > > >
> > > 
> > > When ret is not 0, it need to return.
> > 
> > I would expect something like this:
> > 
> > $ echo 1 > /sys/path/to/tpdm/device/reset
> > 
> > and not
> > 
> > $ echo 0 > /sys/path/to/tpdm/device/reset
> > 
> > The latter is what the code does.
> > 
> > Thanks,
> > Mathieu
> > 
> 
> Hi Mathieu,
> 
> The ret is the result of kstrtoul not the val.
>

Ah yes, you are correct.


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

* Re: [PATCH 05/10] Coresight: Add interface for TPDM BC subunit
  2021-11-05  8:26     ` Jinlong
@ 2021-11-12  8:42       ` Jinlong
  2021-11-12  9:10         ` Jinlong
  0 siblings, 1 reply; 37+ messages in thread
From: Jinlong @ 2021-11-12  8:42 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: Tao Zhang, Suzuki K Poulose, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni

[....]

> >  	&tpdm_attr_grp,
> > > +	&tpdm_bc_attr_grp,
> > 
> > It is quite tedious to review all these options at the same time as the core
> > drivers.  I suggest to concentrate on the base functionality for now.  When that
> > is merged we can add configuration options such as these.
> > 
> > I am out of time for this patchset and as such will not review the remaining
> > patches - those will have to wait for another iteration.
> > 
> > Thanks,
> > Mathieu
> 
> 
> Thank your for all the comments.
> We will update the patches.
> 
> Thanks
> Jinlong Mao 
> 

Hi Mathieu,

We will reduce the code changes.
There will be code changes below for next version:

1. coresight: add support to enable more coresight paths
2. Basic function for TPDM(only enable/disable functions)
3. TPDA driver

Is that ok for your ?

> > 
> > >  	NULL,
> > >  };
> > >  
> > > -- 
> > > 2.17.1
> > > 

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

* Re: [PATCH 05/10] Coresight: Add interface for TPDM BC subunit
  2021-11-12  8:42       ` Jinlong
@ 2021-11-12  9:10         ` Jinlong
  2021-11-12 16:37           ` Mathieu Poirier
  0 siblings, 1 reply; 37+ messages in thread
From: Jinlong @ 2021-11-12  9:10 UTC (permalink / raw)
  To: Mathieu Poirier
  Cc: Tao Zhang, Suzuki K Poulose, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni

On Fri, Nov 12, 2021 at 04:42:31PM +0800, Jinlong wrote:
> [....]
> 
> > >  	&tpdm_attr_grp,
> > > > +	&tpdm_bc_attr_grp,
> > > 
> > > It is quite tedious to review all these options at the same time as the core
> > > drivers.  I suggest to concentrate on the base functionality for now.  When that
> > > is merged we can add configuration options such as these.
> > > 
> > > I am out of time for this patchset and as such will not review the remaining
> > > patches - those will have to wait for another iteration.
> > > 
> > > Thanks,
> > > Mathieu
> > 
> > 
> > Thank your for all the comments.
> > We will update the patches.
> > 
> > Thanks
> > Jinlong Mao 
> > 
>

Correct the typo.

 Hi Mathieu,
 
 We will reduce the code changes.
 There will be code changes below for next version:
 
 1. coresight: add support to enable more coresight paths
 2. Basic function for TPDM(only enable/disable functions)
 3. TPDA driver
 
 Is that ok for you ?
> 
> > > 
> > > >  	NULL,
> > > >  };
> > > >  
> > > > -- 
> > > > 2.17.1
> > > > 

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

* Re: [PATCH 05/10] Coresight: Add interface for TPDM BC subunit
  2021-11-12  9:10         ` Jinlong
@ 2021-11-12 16:37           ` Mathieu Poirier
  0 siblings, 0 replies; 37+ messages in thread
From: Mathieu Poirier @ 2021-11-12 16:37 UTC (permalink / raw)
  To: Jinlong
  Cc: Tao Zhang, Suzuki K Poulose, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni

On Fri, Nov 12, 2021 at 05:10:38PM +0800, Jinlong wrote:
> On Fri, Nov 12, 2021 at 04:42:31PM +0800, Jinlong wrote:
> > [....]
> > 
> > > >  	&tpdm_attr_grp,
> > > > > +	&tpdm_bc_attr_grp,
> > > > 
> > > > It is quite tedious to review all these options at the same time as the core
> > > > drivers.  I suggest to concentrate on the base functionality for now.  When that
> > > > is merged we can add configuration options such as these.
> > > > 
> > > > I am out of time for this patchset and as such will not review the remaining
> > > > patches - those will have to wait for another iteration.
> > > > 
> > > > Thanks,
> > > > Mathieu
> > > 
> > > 
> > > Thank your for all the comments.
> > > We will update the patches.
> > > 
> > > Thanks
> > > Jinlong Mao 
> > > 
> >
> 
> Correct the typo.
> 
>  Hi Mathieu,
>  
>  We will reduce the code changes.
>  There will be code changes below for next version:
>  
>  1. coresight: add support to enable more coresight paths
>  2. Basic function for TPDM(only enable/disable functions)
>  3. TPDA driver
>  
>  Is that ok for you ?

Yes, that sounds much better.

Regards,
Mathieu

> > 
> > > > 
> > > > >  	NULL,
> > > > >  };
> > > > >  
> > > > > -- 
> > > > > 2.17.1
> > > > > 

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

* Re: [PATCH 01/10] coresight: add support to enable more coresight paths
  2021-10-28 18:06   ` Mathieu Poirier
@ 2021-11-22 15:12     ` Jinlong Mao
  2021-11-22 16:51       ` Mathieu Poirier
  0 siblings, 1 reply; 37+ messages in thread
From: Jinlong Mao @ 2021-11-22 15:12 UTC (permalink / raw)
  To: Mathieu Poirier, Tao Zhang
  Cc: Suzuki K Poulose, Alexander Shishkin, Mike Leach, Leo Yan,
	Greg Kroah-Hartman, coresight, linux-arm-kernel, linux-kernel,
	Tingwei Zhang, Yuanfang Zhang, Trilok Soni, Tingwei Zhang

Hi Mathieu,

Thanks for the comments.

I double checked the code. Please see my comments below.


On 10/29/2021 2:06 AM, Mathieu Poirier wrote:
> On Thu, Oct 21, 2021 at 03:38:47PM +0800, Tao Zhang wrote:
>> Current coresight implementation only supports enabling source
>> ETMs or STM. This patch adds support to enable more kinds of
>> coresight source to sink paths. We build a path from source to
>> sink when any source is enabled and store it in a list. When the
>> source is disabled, we fetch the corresponding path from the list
>> and decrement the refcount on each device in the path. The device
>> is disabled if the refcount reaches zero. Don't store path to
>> coresight data structure of source to avoid unnecessary change to
>> ABI.
>> Since some targets may have coresight sources other than STM and
>> ETMs, we need to add this change to support these coresight
>> devices.
>>
>> Signed-off-by: Tingwei Zhang <tingwei@codeaurora.org>
>> Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
>> ---
>>   drivers/hwtracing/coresight/coresight-core.c | 100 +++++++++++--------
>>   1 file changed, 56 insertions(+), 44 deletions(-)
>>
>> diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
>> index 8a18c71df37a..1e621d61307a 100644
>> --- a/drivers/hwtracing/coresight/coresight-core.c
>> +++ b/drivers/hwtracing/coresight/coresight-core.c
>> @@ -37,18 +37,16 @@ struct coresight_node {
>>   };
>>   
>>   /*
>> - * When operating Coresight drivers from the sysFS interface, only a single
>> - * path can exist from a tracer (associated to a CPU) to a sink.
>> + * struct coresight_path - path from source to sink
>> + * @path:	Address of path list.
>> + * @link:	hook to the list.
>>    */
>> -static DEFINE_PER_CPU(struct list_head *, tracer_path);
>> +struct coresight_path {
>> +	struct list_head *path;
>> +	struct list_head link;
>> +};
> For sources associated with a CPU, like ETMs, having a per-cpu way of storing
> paths is a definite advantage and should be kept that way.

Hi Mathieu,

Could you please share what is the advantage to handle the sources 
associated with a CPU separatly ?

 From the code, cpu id is only used to get the path of the ETM source.

As there will be many tpdm sources, I think it will be easier to only 
maintain one list for all the sources.

>>   
>> -/*
>> - * As of this writing only a single STM can be found in CS topologies.  Since
>> - * there is no way to know if we'll ever see more and what kind of
>> - * configuration they will enact, for the time being only define a single path
>> - * for STM.
>> - */
>> -static struct list_head *stm_path;
>> +static LIST_HEAD(cs_active_paths);
> Then there are sources that aren't associated with a CPU like STMs and TPDMs.
> Perhaps using an IDR or the hash of the device name as a key to a hashing
> vector would be better than doing a sequential search, especially as the
> list of devices is bound to increase over time.

Agree with you. I will try to use IDR or  the hash of the device name as 
a key to a hashing vector.

>
>>   
>>   /*
>>    * When losing synchronisation a new barrier packet needs to be inserted at the
>> @@ -354,6 +352,7 @@ static void coresight_disable_sink(struct coresight_device *csdev)
>>   	if (ret)
>>   		return;
>>   	coresight_control_assoc_ectdev(csdev, false);
>> +	csdev->activated = false;
> I don't see why this is needed and without proper documentation there is no way
> for me to guess the logic behind the change.  The ->activated flag should be
> manipulated from the command line interface only.

When source is disabled, but sink is still actived. It will be confused 
for end users.

>
>>   	csdev->enable = false;
>>   }
>>   
>> @@ -590,6 +589,20 @@ int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data)
>>   	goto out;
>>   }
>>   
>> +static struct coresight_device *coresight_get_source(struct list_head *path)
>> +{
>> +	struct coresight_device *csdev;
>> +
>> +	if (!path)
>> +		return NULL;
>> +
>> +	csdev = list_first_entry(path, struct coresight_node, link)->csdev;
>> +	if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE)
>> +		return NULL;
>> +
>> +	return csdev;
>> +}
>> +
>>   struct coresight_device *coresight_get_sink(struct list_head *path)
>>   {
>>   	struct coresight_device *csdev;
>> @@ -1086,9 +1099,23 @@ static int coresight_validate_source(struct coresight_device *csdev,
>>   	return 0;
>>   }
>>   
>> +static int coresight_store_path(struct list_head *path)
>> +{
>> +	struct coresight_path *node;
>> +
>> +	node = kzalloc(sizeof(struct coresight_path), GFP_KERNEL);
>> +	if (!node)
>> +		return -ENOMEM;
>> +
>> +	node->path = path;
>> +	list_add(&node->link, &cs_active_paths);
>> +
>> +	return 0;
>> +}
>> +
>>   int coresight_enable(struct coresight_device *csdev)
>>   {
>> -	int cpu, ret = 0;
>> +	int ret = 0;
>>   	struct coresight_device *sink;
>>   	struct list_head *path;
>>   	enum coresight_dev_subtype_source subtype;
>> @@ -1133,25 +1160,9 @@ int coresight_enable(struct coresight_device *csdev)
>>   	if (ret)
>>   		goto err_source;
>>   
>> -	switch (subtype) {
>> -	case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
>> -		/*
>> -		 * When working from sysFS it is important to keep track
>> -		 * of the paths that were created so that they can be
>> -		 * undone in 'coresight_disable()'.  Since there can only
>> -		 * be a single session per tracer (when working from sysFS)
>> -		 * a per-cpu variable will do just fine.
>> -		 */
>> -		cpu = source_ops(csdev)->cpu_id(csdev);
>> -		per_cpu(tracer_path, cpu) = path;
>> -		break;
>> -	case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
>> -		stm_path = path;
>> -		break;
>> -	default:
>> -		/* We can't be here */
>> -		break;
>> -	}
>> +	ret = coresight_store_path(path);
>> +	if (ret)
>> +		goto err_source;
>>   
>>   out:
>>   	mutex_unlock(&coresight_mutex);
>> @@ -1168,8 +1179,11 @@ EXPORT_SYMBOL_GPL(coresight_enable);
>>   
>>   void coresight_disable(struct coresight_device *csdev)
>>   {
>> -	int cpu, ret;
>> +	int  ret;
>>   	struct list_head *path = NULL;
>> +	struct coresight_path *cspath = NULL;
>> +	struct coresight_path *cspath_next = NULL;
>> +	struct coresight_device *src_csdev = NULL;
>>   
>>   	mutex_lock(&coresight_mutex);
>>   
>> @@ -1180,20 +1194,18 @@ void coresight_disable(struct coresight_device *csdev)
>>   	if (!csdev->enable || !coresight_disable_source(csdev))
>>   		goto out;
>>   
>> -	switch (csdev->subtype.source_subtype) {
>> -	case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
>> -		cpu = source_ops(csdev)->cpu_id(csdev);
>> -		path = per_cpu(tracer_path, cpu);
>> -		per_cpu(tracer_path, cpu) = NULL;
>> -		break;
>> -	case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
>> -		path = stm_path;
>> -		stm_path = NULL;
>> -		break;
>> -	default:
>> -		/* We can't be here */
>> -		break;
>> +	list_for_each_entry_safe(cspath, cspath_next, &cs_active_paths, link) {
>> +		src_csdev = coresight_get_source(cspath->path);
>> +		if (!src_csdev)
>> +			continue;
>> +		if (src_csdev == csdev) {
>> +			path = cspath->path;
>> +			list_del(&cspath->link);
>> +			kfree(cspath);
> See my comment above - I agree that sources _not_ associated with a CPU should
> be handled differently.  CPU bound sources should be kept untouched.
>
> That is all the time I had for today, I will continue tomorrow.
>
> Thanks,
> Mathieu
>
>> +		}
>>   	}
>> +	if (path == NULL)
>> +		goto out;
>>   
>>   	coresight_disable_path(path);
>>   	coresight_release_path(path);
>> -- 
>> 2.17.1
>>

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

* Re: [PATCH 01/10] coresight: add support to enable more coresight paths
  2021-11-22 15:12     ` Jinlong Mao
@ 2021-11-22 16:51       ` Mathieu Poirier
  0 siblings, 0 replies; 37+ messages in thread
From: Mathieu Poirier @ 2021-11-22 16:51 UTC (permalink / raw)
  To: Jinlong Mao
  Cc: Tao Zhang, Suzuki K Poulose, Alexander Shishkin, Mike Leach,
	Leo Yan, Greg Kroah-Hartman, coresight, linux-arm-kernel,
	linux-kernel, Tingwei Zhang, Yuanfang Zhang, Trilok Soni,
	Tingwei Zhang

On Mon, Nov 22, 2021 at 11:12:03PM +0800, Jinlong Mao wrote:
> Hi Mathieu,
> 
> Thanks for the comments.
> 
> I double checked the code. Please see my comments below.
> 
> 
> On 10/29/2021 2:06 AM, Mathieu Poirier wrote:
> > On Thu, Oct 21, 2021 at 03:38:47PM +0800, Tao Zhang wrote:
> > > Current coresight implementation only supports enabling source
> > > ETMs or STM. This patch adds support to enable more kinds of
> > > coresight source to sink paths. We build a path from source to
> > > sink when any source is enabled and store it in a list. When the
> > > source is disabled, we fetch the corresponding path from the list
> > > and decrement the refcount on each device in the path. The device
> > > is disabled if the refcount reaches zero. Don't store path to
> > > coresight data structure of source to avoid unnecessary change to
> > > ABI.
> > > Since some targets may have coresight sources other than STM and
> > > ETMs, we need to add this change to support these coresight
> > > devices.
> > > 
> > > Signed-off-by: Tingwei Zhang <tingwei@codeaurora.org>
> > > Signed-off-by: Tao Zhang <quic_taozha@quicinc.com>
> > > ---
> > >   drivers/hwtracing/coresight/coresight-core.c | 100 +++++++++++--------
> > >   1 file changed, 56 insertions(+), 44 deletions(-)
> > > 
> > > diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
> > > index 8a18c71df37a..1e621d61307a 100644
> > > --- a/drivers/hwtracing/coresight/coresight-core.c
> > > +++ b/drivers/hwtracing/coresight/coresight-core.c
> > > @@ -37,18 +37,16 @@ struct coresight_node {
> > >   };
> > >   /*
> > > - * When operating Coresight drivers from the sysFS interface, only a single
> > > - * path can exist from a tracer (associated to a CPU) to a sink.
> > > + * struct coresight_path - path from source to sink
> > > + * @path:	Address of path list.
> > > + * @link:	hook to the list.
> > >    */
> > > -static DEFINE_PER_CPU(struct list_head *, tracer_path);
> > > +struct coresight_path {
> > > +	struct list_head *path;
> > > +	struct list_head link;
> > > +};
> > For sources associated with a CPU, like ETMs, having a per-cpu way of storing
> > paths is a definite advantage and should be kept that way.
> 
> Hi Mathieu,
> 
> Could you please share what is the advantage to handle the sources
> associated with a CPU separatly ?
>

It is a question of efficiency.  There is no point iterating through all the
sources if we don't have to.

> From the code, cpu id is only used to get the path of the ETM source.
> 
> As there will be many tpdm sources, I think it will be easier to only
> maintain one list for all the sources.
> 

So many TPDM and many ETMs...  That is definitely a reason to do better than a
sequential search.

> > > -/*
> > > - * As of this writing only a single STM can be found in CS topologies.  Since
> > > - * there is no way to know if we'll ever see more and what kind of
> > > - * configuration they will enact, for the time being only define a single path
> > > - * for STM.
> > > - */
> > > -static struct list_head *stm_path;
> > > +static LIST_HEAD(cs_active_paths);
> > Then there are sources that aren't associated with a CPU like STMs and TPDMs.
> > Perhaps using an IDR or the hash of the device name as a key to a hashing
> > vector would be better than doing a sequential search, especially as the
> > list of devices is bound to increase over time.
> 
> Agree with you. I will try to use IDR or  the hash of the device name as a
> key to a hashing vector.
>

If an IDR (or some other kind of mechanism) is used then we can use that to
store paths associated with ETMs as well.  That way everything works the same
way and access time is constant for any kind of source.

> > 
> > >   /*
> > >    * When losing synchronisation a new barrier packet needs to be inserted at the
> > > @@ -354,6 +352,7 @@ static void coresight_disable_sink(struct coresight_device *csdev)
> > >   	if (ret)
> > >   		return;
> > >   	coresight_control_assoc_ectdev(csdev, false);
> > > +	csdev->activated = false;
> > I don't see why this is needed and without proper documentation there is no way
> > for me to guess the logic behind the change.  The ->activated flag should be
> > manipulated from the command line interface only.
> 
> When source is disabled, but sink is still actived. It will be confused for
> end users.
> 

That is how it has been working for years now.  It was done this way to give as
much flexibility to users and keep kernel intelligence to a minimum.

> > 
> > >   	csdev->enable = false;
> > >   }
> > > @@ -590,6 +589,20 @@ int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data)
> > >   	goto out;
> > >   }
> > > +static struct coresight_device *coresight_get_source(struct list_head *path)
> > > +{
> > > +	struct coresight_device *csdev;
> > > +
> > > +	if (!path)
> > > +		return NULL;
> > > +
> > > +	csdev = list_first_entry(path, struct coresight_node, link)->csdev;
> > > +	if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE)
> > > +		return NULL;
> > > +
> > > +	return csdev;
> > > +}
> > > +
> > >   struct coresight_device *coresight_get_sink(struct list_head *path)
> > >   {
> > >   	struct coresight_device *csdev;
> > > @@ -1086,9 +1099,23 @@ static int coresight_validate_source(struct coresight_device *csdev,
> > >   	return 0;
> > >   }
> > > +static int coresight_store_path(struct list_head *path)
> > > +{
> > > +	struct coresight_path *node;
> > > +
> > > +	node = kzalloc(sizeof(struct coresight_path), GFP_KERNEL);
> > > +	if (!node)
> > > +		return -ENOMEM;
> > > +
> > > +	node->path = path;
> > > +	list_add(&node->link, &cs_active_paths);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > >   int coresight_enable(struct coresight_device *csdev)
> > >   {
> > > -	int cpu, ret = 0;
> > > +	int ret = 0;
> > >   	struct coresight_device *sink;
> > >   	struct list_head *path;
> > >   	enum coresight_dev_subtype_source subtype;
> > > @@ -1133,25 +1160,9 @@ int coresight_enable(struct coresight_device *csdev)
> > >   	if (ret)
> > >   		goto err_source;
> > > -	switch (subtype) {
> > > -	case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
> > > -		/*
> > > -		 * When working from sysFS it is important to keep track
> > > -		 * of the paths that were created so that they can be
> > > -		 * undone in 'coresight_disable()'.  Since there can only
> > > -		 * be a single session per tracer (when working from sysFS)
> > > -		 * a per-cpu variable will do just fine.
> > > -		 */
> > > -		cpu = source_ops(csdev)->cpu_id(csdev);
> > > -		per_cpu(tracer_path, cpu) = path;
> > > -		break;
> > > -	case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
> > > -		stm_path = path;
> > > -		break;
> > > -	default:
> > > -		/* We can't be here */
> > > -		break;
> > > -	}
> > > +	ret = coresight_store_path(path);
> > > +	if (ret)
> > > +		goto err_source;
> > >   out:
> > >   	mutex_unlock(&coresight_mutex);
> > > @@ -1168,8 +1179,11 @@ EXPORT_SYMBOL_GPL(coresight_enable);
> > >   void coresight_disable(struct coresight_device *csdev)
> > >   {
> > > -	int cpu, ret;
> > > +	int  ret;
> > >   	struct list_head *path = NULL;
> > > +	struct coresight_path *cspath = NULL;
> > > +	struct coresight_path *cspath_next = NULL;
> > > +	struct coresight_device *src_csdev = NULL;
> > >   	mutex_lock(&coresight_mutex);
> > > @@ -1180,20 +1194,18 @@ void coresight_disable(struct coresight_device *csdev)
> > >   	if (!csdev->enable || !coresight_disable_source(csdev))
> > >   		goto out;
> > > -	switch (csdev->subtype.source_subtype) {
> > > -	case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
> > > -		cpu = source_ops(csdev)->cpu_id(csdev);
> > > -		path = per_cpu(tracer_path, cpu);
> > > -		per_cpu(tracer_path, cpu) = NULL;
> > > -		break;
> > > -	case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
> > > -		path = stm_path;
> > > -		stm_path = NULL;
> > > -		break;
> > > -	default:
> > > -		/* We can't be here */
> > > -		break;
> > > +	list_for_each_entry_safe(cspath, cspath_next, &cs_active_paths, link) {
> > > +		src_csdev = coresight_get_source(cspath->path);
> > > +		if (!src_csdev)
> > > +			continue;
> > > +		if (src_csdev == csdev) {
> > > +			path = cspath->path;
> > > +			list_del(&cspath->link);
> > > +			kfree(cspath);
> > See my comment above - I agree that sources _not_ associated with a CPU should
> > be handled differently.  CPU bound sources should be kept untouched.
> > 
> > That is all the time I had for today, I will continue tomorrow.
> > 
> > Thanks,
> > Mathieu
> > 
> > > +		}
> > >   	}
> > > +	if (path == NULL)
> > > +		goto out;
> > >   	coresight_disable_path(path);
> > >   	coresight_release_path(path);
> > > -- 
> > > 2.17.1
> > > 

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

end of thread, other threads:[~2021-11-22 16:51 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-21  7:38 [PATCH 00/10] Add support for TPDM and TPDA Tao Zhang
2021-10-21  7:38 ` [PATCH 01/10] coresight: add support to enable more coresight paths Tao Zhang
2021-10-28 18:06   ` Mathieu Poirier
2021-11-22 15:12     ` Jinlong Mao
2021-11-22 16:51       ` Mathieu Poirier
2021-10-21  7:38 ` [PATCH 02/10] coresight: funnel: add support for multiple output ports Tao Zhang
2021-10-29 17:48   ` Mathieu Poirier
2021-10-21  7:38 ` [PATCH 03/10] Coresight: Add driver to support Coresight device TPDM Tao Zhang
2021-11-02 17:59   ` Mathieu Poirier
2021-11-04  8:56     ` Jinlong
2021-11-04 16:55       ` Mathieu Poirier
2021-11-05  8:15         ` Jinlong
2021-11-04  9:37     ` Suzuki K Poulose
2021-11-05  8:12       ` Jinlong
2021-10-21  7:38 ` [PATCH 04/10] Coresight: Enable BC and GPR for TPDM driver Tao Zhang
2021-11-03 19:43   ` Mathieu Poirier
2021-11-04 11:13     ` Jinlong
2021-11-04 17:02       ` Mathieu Poirier
2021-11-05  8:17         ` Jinlong
2021-11-05 15:14           ` Mathieu Poirier
2021-10-21  7:38 ` [PATCH 05/10] Coresight: Add interface for TPDM BC subunit Tao Zhang
2021-11-04 18:01   ` Mathieu Poirier
2021-11-05  8:26     ` Jinlong
2021-11-12  8:42       ` Jinlong
2021-11-12  9:10         ` Jinlong
2021-11-12 16:37           ` Mathieu Poirier
2021-10-21  7:38 ` [PATCH 06/10] Coresight: Enable and add interface for TPDM TC subunit Tao Zhang
2021-10-21  7:38 ` [PATCH 07/10] Coresight: Enable DSB subunit for TPDM Tao Zhang
2021-10-21  7:38 ` [PATCH 08/10] Coresight: Enable CMB " Tao Zhang
2021-10-21  7:38 ` [PATCH 09/10] coresight: Add driver to support Coresight device TPDA Tao Zhang
2021-10-21  7:38 ` [PATCH 10/10] ARM: dts: msm: Add TPDA and TPDM support to DTS for RB5 Tao Zhang
2021-11-02 18:02   ` Mathieu Poirier
2021-11-03  8:14     ` Tao Zhang
2021-11-04  9:45   ` Suzuki K Poulose
2021-11-05  8:07     ` Jinlong
2021-10-28 17:16 ` [PATCH 00/10] Add support for TPDM and TPDA Mathieu Poirier
2021-10-29 15:11   ` Tao Zhang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).