2023-08-13 15:48:25

by Mao Jinlong

[permalink] [raw]
Subject: [PATCH v2 0/3] Add support for a streaming interface for TMC ETR

This patch series is to add support for a streaming interface for
TMC ETR to allow for continuous log collection to secondary storage.
An interrupt based mechanism is used to stream out the data from the device.

QDSS_CS_QDSSCSR_ETRIRQCTRL register is used to set the IRQ byte counter
value. The value of this registers defines the number of bytes that when moved by
the ETR AXI interface. It will casues an interrupt which can be used by an
userspace program to know how much data is present in memory requiring copy to some
other location. A zero setting disables the interrupt.A one setting
means 8 bytes, two 16 bytes, etc. In other words, the value in this
register is the interrupt threshold times 8 bytes. ETR must be enabled
when use this interrupt function.

Sample:
echo 4096 > /sys/bus/coresight/devices/csr0/etr_byte_cntr_val
echo 1 > /sys/bus/coresight/devices/tmc_etr0/enable_sink
echo 1 > /sys/bus/coresight/devices/stm0/enabl_source

cat /dev/byte-cntr > /data/qdss.bin &

The log collection will stop after disabling the ETR.

codelinaro link:
https://git.codelinaro.org/clo/linux-kernel/coresight/-/commits/coresight-byte-cntr-v2?ref_type=heads

Change in V2:
Make CSR device as a helper device of ETR device. When enable ETR, it
will enable CSR byte-cntr function.

Mao Jinlong (3):
Coresight: Add driver to support for CSR
dt-bindings: arm: Adds CoreSight CSR hardware definitions
coresight-csr: Add support for streaming interface for ETR

.../testing/sysfs-bus-coresight-devices-csr | 6 +
.../bindings/arm/qcom,coresight-csr.yaml | 130 ++++++
MAINTAINERS | 2 +-
drivers/hwtracing/coresight/Kconfig | 12 +
drivers/hwtracing/coresight/Makefile | 2 +
drivers/hwtracing/coresight/coresight-core.c | 31 +-
.../coresight/coresight-csr-bytecntr.c | 275 ++++++++++++
.../hwtracing/coresight/coresight-csr-core.c | 393 ++++++++++++++++++
drivers/hwtracing/coresight/coresight-csr.h | 112 +++++
drivers/hwtracing/coresight/coresight-priv.h | 8 +
.../hwtracing/coresight/coresight-tmc-etr.c | 3 +-
drivers/hwtracing/coresight/coresight-tmc.h | 2 +
include/dt-bindings/arm/coresight-csr-dt.h | 12 +
include/linux/coresight.h | 3 +-
14 files changed, 984 insertions(+), 7 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-csr
create mode 100644 Documentation/devicetree/bindings/arm/qcom,coresight-csr.yaml
create mode 100644 drivers/hwtracing/coresight/coresight-csr-bytecntr.c
create mode 100644 drivers/hwtracing/coresight/coresight-csr-core.c
create mode 100644 drivers/hwtracing/coresight/coresight-csr.h
create mode 100644 include/dt-bindings/arm/coresight-csr-dt.h

--
2.17.1



2023-08-13 16:00:07

by Mao Jinlong

[permalink] [raw]
Subject: [PATCH v2 1/3] Coresight: Add driver to support for CSR

This driver provides support for CoreSight Slave Register block
that hosts miscellaneous configuration registers. Those
configuration registers can be used to control, various coresight
configurations.

Signed-off-by: Hao Zhang <[email protected]>
Signed-off-by: Mao Jinlong <[email protected]>
---
drivers/hwtracing/coresight/Kconfig | 12 ++
drivers/hwtracing/coresight/Makefile | 2 +
.../hwtracing/coresight/coresight-csr-core.c | 104 ++++++++++++++++++
drivers/hwtracing/coresight/coresight-csr.h | 32 ++++++
4 files changed, 150 insertions(+)
create mode 100644 drivers/hwtracing/coresight/coresight-csr-core.c
create mode 100644 drivers/hwtracing/coresight/coresight-csr.h

diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index 06f0a7594169..ddabab1e2516 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -247,4 +247,16 @@ config CORESIGHT_DUMMY

To compile this driver as a module, choose M here: the module will be
called coresight-dummy.
+
+config CORESIGHT_CSR
+ tristate "CoreSight Slave Register driver"
+ help
+ This driver provides support for CoreSight Slave Register block
+ that hosts miscellaneous configuration registers.
+ Those configuration registers can be used to control, various
+ coresight configurations.
+
+ To compile this driver as a module, choose M here: the module will be
+ called coresight-csr.
+
endif
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 995d3b2c76df..6f8d17003ff7 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -31,3 +31,5 @@ coresight-cti-y := coresight-cti-core.o coresight-cti-platform.o \
coresight-cti-sysfs.o
obj-$(CONFIG_ULTRASOC_SMB) += ultrasoc-smb.o
obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
+obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o
+coresight-csr-y := coresight-csr-core.o
diff --git a/drivers/hwtracing/coresight/coresight-csr-core.c b/drivers/hwtracing/coresight/coresight-csr-core.c
new file mode 100644
index 000000000000..e07070b650d3
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-csr-core.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include "coresight-priv.h"
+#include "coresight-csr.h"
+
+DEFINE_CORESIGHT_DEVLIST(csr_devs, "csr");
+
+static int csr_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct coresight_platform_data *pdata;
+ struct csr_drvdata *drvdata;
+ struct resource *res;
+ struct coresight_desc desc = { 0 };
+
+ desc.name = coresight_alloc_device_name(&csr_devs, dev);
+ if (!desc.name)
+ return -ENOMEM;
+ pdata = coresight_get_platform_data(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ pdev->dev.platform_data = pdata;
+
+ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+ drvdata->dev = &pdev->dev;
+ platform_set_drvdata(pdev, drvdata);
+
+ drvdata->clk = devm_clk_get(dev, "apb_pclk");
+ if (IS_ERR(drvdata->clk))
+ dev_dbg(dev, "csr not config clk\n");
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr-base");
+ if (!res)
+ return -ENODEV;
+ drvdata->pbase = res->start;
+
+ drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!drvdata->base)
+ return -ENOMEM;
+
+ desc.type = CORESIGHT_DEV_TYPE_HELPER;
+ desc.pdata = pdev->dev.platform_data;
+ desc.dev = &pdev->dev;
+
+ drvdata->csdev = coresight_register(&desc);
+ if (IS_ERR(drvdata->csdev))
+ return PTR_ERR(drvdata->csdev);
+
+ spin_lock_init(&drvdata->spin_lock);
+
+ dev_dbg(dev, "CSR initialized: %s\n", dev_name(dev));
+ return 0;
+}
+
+static int csr_remove(struct platform_device *pdev)
+{
+ struct csr_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ coresight_unregister(drvdata->csdev);
+ return 0;
+}
+
+static const struct of_device_id csr_match[] = {
+ {.compatible = "qcom,coresight-csr"},
+ {}
+};
+
+static struct platform_driver csr_driver = {
+ .probe = csr_probe,
+ .remove = csr_remove,
+ .driver = {
+ .name = "coresight-csr",
+ .of_match_table = csr_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+static int __init csr_init(void)
+{
+ return platform_driver_register(&csr_driver);
+}
+module_init(csr_init);
+
+static void __exit csr_exit(void)
+{
+ platform_driver_unregister(&csr_driver);
+}
+module_exit(csr_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CoreSight CSR driver");
diff --git a/drivers/hwtracing/coresight/coresight-csr.h b/drivers/hwtracing/coresight/coresight-csr.h
new file mode 100644
index 000000000000..b4fb947fe23b
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-csr.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _CORESIGHT_CSR_H
+#define _CORESIGHT_CSR_H
+#include <linux/clk.h>
+#include <linux/coresight.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+
+/**
+ * struct csr_drvdata - specifics for the CSR device.
+ * @base: Memory mapped base address for this component.
+ * @pbase: Physical address base.
+ * @dev: The device entity associated to this component.
+ * @csdev: Data struct for coresight device.
+ * @clk: Clock of this component.
+ * @spin_lock: Spin lock for the data.
+ */
+struct csr_drvdata {
+ void __iomem *base;
+ phys_addr_t pbase;
+ struct device *dev;
+ struct coresight_device *csdev;
+ struct clk *clk;
+ spinlock_t spin_lock;
+};
+
+#endif
+
--
2.17.1


2023-08-13 17:27:49

by Mao Jinlong

[permalink] [raw]
Subject: [PATCH v2 3/3] coresight-csr: Add support for streaming interface for ETR

Add support for a streaming interface for TMC ETR to allow for continuous
log collection to secondary storage. An interrupt based mechanism is used
to stream out the data from the device.

Signed-off-by: Mao Jinlong <[email protected]>
---
.../testing/sysfs-bus-coresight-devices-csr | 6 +
drivers/hwtracing/coresight/Makefile | 2 +-
drivers/hwtracing/coresight/coresight-core.c | 31 +-
.../coresight/coresight-csr-bytecntr.c | 275 ++++++++++++++++
.../hwtracing/coresight/coresight-csr-core.c | 293 +++++++++++++++++-
drivers/hwtracing/coresight/coresight-csr.h | 80 +++++
drivers/hwtracing/coresight/coresight-priv.h | 8 +
.../hwtracing/coresight/coresight-tmc-etr.c | 3 +-
drivers/hwtracing/coresight/coresight-tmc.h | 2 +
include/linux/coresight.h | 3 +-
10 files changed, 694 insertions(+), 9 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-csr
create mode 100644 drivers/hwtracing/coresight/coresight-csr-bytecntr.c

diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-csr b/Documentation/ABI/testing/sysfs-bus-coresight-devices-csr
new file mode 100644
index 000000000000..2694ed527a50
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-csr
@@ -0,0 +1,6 @@
+What: /sys/bus/coresight/devices/<memory_map>.csr/etr_byte_cntr_val
+Date: August 2023
+KernelVersion: 6.5
+Contact: Mao Jinlong <[email protected]>
+Description: (RW) Size of the ETR irq byte counter value.
+
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 6f8d17003ff7..3e20030b6242 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -32,4 +32,4 @@ coresight-cti-y := coresight-cti-core.o coresight-cti-platform.o \
obj-$(CONFIG_ULTRASOC_SMB) += ultrasoc-smb.o
obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o
-coresight-csr-y := coresight-csr-core.o
+coresight-csr-y := coresight-csr-core.o coresight-csr-bytecntr.o
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 9fabe00a40d6..72d54092d839 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -60,6 +60,7 @@ const u32 coresight_barrier_pkt[4] = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fff
EXPORT_SYMBOL_GPL(coresight_barrier_pkt);

static const struct cti_assoc_op *cti_assoc_ops;
+static const struct csr_assoc_op *csr_assoc_ops;

ssize_t coresight_simple_show_pair(struct device *_dev,
struct device_attribute *attr, char *buf)
@@ -89,6 +90,18 @@ ssize_t coresight_simple_show32(struct device *_dev,
}
EXPORT_SYMBOL_GPL(coresight_simple_show32);

+void coresight_set_csr_ops(const struct csr_assoc_op *csr_op)
+{
+ csr_assoc_ops = csr_op;
+}
+EXPORT_SYMBOL_GPL(coresight_set_csr_ops);
+
+void coresight_remove_csr_ops(void)
+{
+ csr_assoc_ops = NULL;
+}
+EXPORT_SYMBOL_GPL(coresight_remove_csr_ops);
+
void coresight_set_cti_ops(const struct cti_assoc_op *cti_op)
{
cti_assoc_ops = cti_op;
@@ -416,14 +429,14 @@ static int coresight_enable_helper(struct coresight_device *csdev,
return 0;
}

-static void coresight_disable_helper(struct coresight_device *csdev)
+static void coresight_disable_helper(struct coresight_device *csdev, void *data)
{
int ret;

if (!helper_ops(csdev)->disable)
return;

- ret = helper_ops(csdev)->disable(csdev, NULL);
+ ret = helper_ops(csdev)->disable(csdev, data);
if (ret)
return;
csdev->enable = false;
@@ -437,7 +450,7 @@ static void coresight_disable_helpers(struct coresight_device *csdev)
for (i = 0; i < csdev->pdata->nr_outconns; ++i) {
helper = csdev->pdata->out_conns[i]->dest_dev;
if (helper && coresight_is_helper(helper))
- coresight_disable_helper(helper);
+ coresight_disable_helper(helper, csdev);
}
}

@@ -535,7 +548,11 @@ static int coresight_enable_helpers(struct coresight_device *csdev,
if (!helper || !coresight_is_helper(helper))
continue;

- ret = coresight_enable_helper(helper, mode, data);
+ if (helper->subtype.helper_subtype == CORESIGHT_DEV_SUBTYPE_HELPER_CSR)
+ ret = coresight_enable_helper(helper, mode, csdev);
+ else
+ ret = coresight_enable_helper(helper, mode, data);
+
if (ret)
return ret;
}
@@ -1639,6 +1656,9 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
if (!ret) {
if (cti_assoc_ops && cti_assoc_ops->add)
cti_assoc_ops->add(csdev);
+
+ if (csr_assoc_ops && csr_assoc_ops->add)
+ csr_assoc_ops->add(csdev);
return csdev;
}

@@ -1661,6 +1681,9 @@ void coresight_unregister(struct coresight_device *csdev)
/* Remove references of that device in the topology */
if (cti_assoc_ops && cti_assoc_ops->remove)
cti_assoc_ops->remove(csdev);
+
+ if (csr_assoc_ops && csr_assoc_ops->remove)
+ csr_assoc_ops->remove(csdev);
coresight_remove_conns(csdev);
coresight_clear_default_sink(csdev);
coresight_release_platform_data(csdev, csdev->dev.parent, csdev->pdata);
diff --git a/drivers/hwtracing/coresight/coresight-csr-bytecntr.c b/drivers/hwtracing/coresight/coresight-csr-bytecntr.c
new file mode 100644
index 000000000000..1a94498897e0
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-csr-bytecntr.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "coresight-csr.h"
+
+/* Read the data from ETR's DDR buffer */
+static void tmc_etr_read_bytes(struct byte_cntr *byte_cntr_data, long offset,
+ size_t bytes, size_t *len, char **bufp)
+{
+ struct csr_assoc_data *assoc_data =
+ container_of(byte_cntr_data, struct csr_assoc_data, byte_cntr_data);
+ struct tmc_drvdata *tmcdrvdata = dev_get_drvdata(assoc_data->assoc_csdev->dev.parent);
+ struct etr_buf *etr_buf = tmcdrvdata->sysfs_buf;
+ size_t actual;
+
+ if (*len >= bytes)
+ *len = bytes;
+ else if (((uint32_t)offset % bytes) + *len > bytes)
+ *len = bytes - ((uint32_t)offset % bytes);
+
+ actual = tmc_etr_buf_get_data(etr_buf, offset, *len, bufp);
+ *len = actual;
+ if (actual == bytes || (actual + (uint32_t)offset) % bytes == 0)
+ atomic_dec(&byte_cntr_data->irq_cnt);
+}
+
+static irqreturn_t etr_handler(int irq, void *data)
+{
+ struct byte_cntr *byte_cntr_data = data;
+
+ atomic_inc(&byte_cntr_data->irq_cnt);
+ wake_up(&byte_cntr_data->wq);
+
+ return IRQ_HANDLED;
+}
+
+/* Read function for /dev/byte-cntr */
+static ssize_t tmc_etr_byte_cntr_read(struct file *fp, char __user *data,
+ size_t len, loff_t *ppos)
+{
+ struct byte_cntr *byte_cntr_data = fp->private_data;
+ struct csr_assoc_data *assoc_data =
+ container_of(byte_cntr_data, struct csr_assoc_data, byte_cntr_data);
+ struct tmc_drvdata *tmcdrvdata = dev_get_drvdata(assoc_data->assoc_csdev->dev.parent);
+ char *bufp = NULL;
+ int ret = 0;
+
+ if (!data)
+ return -EINVAL;
+
+ mutex_lock(&byte_cntr_data->byte_cntr_lock);
+
+ if (byte_cntr_data->enable) {
+ if (!atomic_read(&byte_cntr_data->irq_cnt)) {
+ mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+ if (wait_event_interruptible(byte_cntr_data->wq,
+ atomic_read(&byte_cntr_data->irq_cnt) > 0
+ || !byte_cntr_data->enable))
+ return -ERESTARTSYS;
+ mutex_lock(&byte_cntr_data->byte_cntr_lock);
+ }
+
+ tmc_etr_read_bytes(byte_cntr_data, byte_cntr_data->offset,
+ byte_cntr_data->block_size, &len, &bufp);
+ } else {
+ ret = -EINVAL;
+ goto err0;
+ }
+
+ if (copy_to_user(data, bufp, len)) {
+ mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+ return -EFAULT;
+ }
+
+ if (byte_cntr_data->offset + len >= tmcdrvdata->size)
+ byte_cntr_data->offset = 0;
+ else
+ byte_cntr_data->offset += len;
+
+ goto out;
+
+err0:
+ mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+ return ret;
+out:
+ mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+ return len;
+}
+
+/* Start byte-cntr function. */
+void csr_byte_cntr_start(struct byte_cntr *byte_cntr_data)
+{
+ if (!byte_cntr_data)
+ return;
+
+ mutex_lock(&byte_cntr_data->byte_cntr_lock);
+
+ /*
+ * When block_size is not set or /dev/byte-cntr
+ * is being read, don't start byte-cntr function.
+ */
+ if (byte_cntr_data->block_size <= 0 || byte_cntr_data->read_active) {
+ mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+ return;
+ }
+
+ atomic_set(&byte_cntr_data->irq_cnt, 0);
+ byte_cntr_data->enable = true;
+ mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+}
+
+/* Stop byte-cntr function */
+void csr_byte_cntr_stop(struct byte_cntr *byte_cntr_data)
+{
+ mutex_lock(&byte_cntr_data->byte_cntr_lock);
+ byte_cntr_data->enable = false;
+ byte_cntr_data->read_active = false;
+ atomic_set(&byte_cntr_data->irq_cnt, 0);
+ wake_up(&byte_cntr_data->wq);
+ mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+}
+
+static int tmc_etr_byte_cntr_release(struct inode *in, struct file *fp)
+{
+ struct byte_cntr *byte_cntr_data = fp->private_data;
+
+ mutex_lock(&byte_cntr_data->byte_cntr_lock);
+ byte_cntr_data->read_active = false;
+ atomic_set(&byte_cntr_data->irq_cnt, 0);
+ disable_irq_wake(byte_cntr_data->byte_cntr_irq);
+ mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+
+ return 0;
+}
+
+static int tmc_etr_byte_cntr_open(struct inode *in, struct file *fp)
+{
+ struct byte_cntr *byte_cntr_data =
+ container_of(in->i_cdev, struct byte_cntr, dev);
+ struct csr_assoc_data *assoc_data =
+ container_of(byte_cntr_data, struct csr_assoc_data, byte_cntr_data);
+ struct tmc_drvdata *tmcdrvdata = dev_get_drvdata(assoc_data->assoc_csdev->dev.parent);
+
+ mutex_lock(&byte_cntr_data->byte_cntr_lock);
+
+ if (byte_cntr_data->read_active) {
+ mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+ return -EBUSY;
+ }
+
+ if (tmcdrvdata->mode != CS_MODE_SYSFS ||
+ !byte_cntr_data->block_size) {
+ mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+ return -EINVAL;
+ }
+
+ enable_irq_wake(byte_cntr_data->byte_cntr_irq);
+
+ fp->private_data = byte_cntr_data;
+ nonseekable_open(in, fp);
+ byte_cntr_data->enable = true;
+ byte_cntr_data->read_active = true;
+ mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+ return 0;
+}
+
+static const struct file_operations byte_cntr_fops = {
+ .owner = THIS_MODULE,
+ .open = tmc_etr_byte_cntr_open,
+ .read = tmc_etr_byte_cntr_read,
+ .release = tmc_etr_byte_cntr_release,
+ .llseek = no_llseek,
+};
+
+static int byte_cntr_register_chardev(struct byte_cntr *byte_cntr_data)
+{
+ int ret;
+ unsigned int baseminor = 0;
+ unsigned int count = 1;
+ struct device *device;
+ dev_t dev;
+
+ ret = alloc_chrdev_region(&dev, baseminor, count, byte_cntr_data->name);
+ if (ret < 0) {
+ pr_err("alloc_chrdev_region failed %d\n", ret);
+ return ret;
+ }
+ cdev_init(&byte_cntr_data->dev, &byte_cntr_fops);
+
+ byte_cntr_data->dev.owner = THIS_MODULE;
+ byte_cntr_data->dev.ops = &byte_cntr_fops;
+
+ ret = cdev_add(&byte_cntr_data->dev, dev, 1);
+ if (ret)
+ goto exit_unreg_chrdev_region;
+
+ byte_cntr_data->driver_class = class_create(byte_cntr_data->class_name);
+ if (IS_ERR(byte_cntr_data->driver_class)) {
+ ret = -ENOMEM;
+ pr_err("class_create failed %d\n", ret);
+ goto exit_unreg_chrdev_region;
+ }
+
+ device = device_create(byte_cntr_data->driver_class, NULL,
+ byte_cntr_data->dev.dev, byte_cntr_data,
+ byte_cntr_data->name);
+
+ if (IS_ERR(device)) {
+ pr_err("class_device_create failed %d\n", ret);
+ ret = -ENOMEM;
+ goto exit_destroy_class;
+ }
+
+ return 0;
+
+exit_destroy_class:
+ class_destroy(byte_cntr_data->driver_class);
+exit_unreg_chrdev_region:
+ unregister_chrdev_region(byte_cntr_data->dev.dev, 1);
+ return ret;
+}
+
+int byte_cntr_init(struct csr_drvdata *drvdata, struct csr_assoc_data *assoc_data)
+{
+ int byte_cntr_irq;
+ int ret = 0;
+ struct device *dev = drvdata->dev;
+
+ byte_cntr_irq = fwnode_irq_get_byname(assoc_data->fnode, CSR_DT_BYTECNTR_IRQ);
+ if (byte_cntr_irq < 0)
+ return byte_cntr_irq;
+
+ ret = fwnode_property_read_string(assoc_data->fnode, CSR_DT_BYTECNTR_NAME,
+ &assoc_data->byte_cntr_data.name);
+ if (ret)
+ assoc_data->byte_cntr_data.name = "byte-cntr";
+
+ ret = fwnode_property_read_string(assoc_data->fnode, CSR_DT_BYTECNTR_CLASS_NAME,
+ &assoc_data->byte_cntr_data.class_name);
+ if (ret)
+ assoc_data->byte_cntr_data.class_name = "coresight-tmc-etr-stream";
+
+ ret = fwnode_property_read_u32(assoc_data->fnode, CSR_DT_BYTECNTVAL_OFFSET,
+ &assoc_data->byte_cntr_data.byte_cntr_offset);
+ if (ret)
+ assoc_data->byte_cntr_data.byte_cntr_offset = CSR_ETR_BYTECNTVAL;
+
+ ret = devm_request_irq(dev, byte_cntr_irq, etr_handler,
+ IRQF_TRIGGER_RISING | IRQF_SHARED,
+ assoc_data->assoc_dev_name, &assoc_data->byte_cntr_data);
+ if (ret)
+ return ret;
+
+ ret = byte_cntr_register_chardev(&assoc_data->byte_cntr_data);
+ if (ret)
+ return ret;
+
+ assoc_data->byte_cntr_data.byte_cntr_irq = byte_cntr_irq;
+ atomic_set(&assoc_data->byte_cntr_data.irq_cnt, 0);
+ init_waitqueue_head(&assoc_data->byte_cntr_data.wq);
+ mutex_init(&assoc_data->byte_cntr_data.byte_cntr_lock);
+
+ return ret;
+}
+
+void byte_cntr_remove(struct byte_cntr *byte_cntr_data)
+{
+ device_destroy(byte_cntr_data->driver_class,
+ byte_cntr_data->dev.dev);
+ class_destroy(byte_cntr_data->driver_class);
+ unregister_chrdev_region(byte_cntr_data->dev.dev, 1);
+}
+
diff --git a/drivers/hwtracing/coresight/coresight-csr-core.c b/drivers/hwtracing/coresight/coresight-csr-core.c
index e07070b650d3..6195c9c3b3e5 100644
--- a/drivers/hwtracing/coresight/coresight-csr-core.c
+++ b/drivers/hwtracing/coresight/coresight-csr-core.c
@@ -13,9 +13,274 @@

#include "coresight-priv.h"
#include "coresight-csr.h"
+#include <dt-bindings/arm/coresight-csr-dt.h>
+
+static LIST_HEAD(csr_list);

DEFINE_CORESIGHT_DEVLIST(csr_devs, "csr");

+#define csdev_to_csr_drvdata(csdev) \
+ dev_get_drvdata(csdev->dev.parent)
+
+static ssize_t etr_byte_cntr_val_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+ return scnprintf(buf, PAGE_SIZE, "%#x\n", drvdata->etr_byte_cntr_value);
+}
+
+static ssize_t etr_byte_cntr_val_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ unsigned long val, flags;
+ int ret;
+ struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&drvdata->spin_lock, flags);
+
+ if (kstrtoul(buf, 16, &val)) {
+ spin_unlock_irqrestore(&drvdata->spin_lock, flags);
+ return -EINVAL;
+ }
+
+ drvdata->etr_byte_cntr_value = val;
+ CS_UNLOCK(drvdata->base);
+ /*
+ * A zero setting disables the interrupt. A one
+ * setting means 8 bytes, two 16 bytes, etc. In
+ * other words, the value in this register is the
+ * interrupt threshold times 8 bytes.
+ */
+ writel_relaxed(val / 8, drvdata->base + CSR_ETR_BYTECNTVAL);
+ CS_LOCK(drvdata->base);
+
+ spin_unlock_irqrestore(&drvdata->spin_lock, flags);
+
+ clk_disable_unprepare(drvdata->clk);
+ return size;
+}
+static DEVICE_ATTR_RW(etr_byte_cntr_val);
+
+static struct attribute *csr_attrs[] = {
+ &dev_attr_etr_byte_cntr_val.attr,
+ NULL,
+};
+
+static struct attribute_group csr_attr_grp = {
+ .attrs = csr_attrs,
+};
+
+static const struct attribute_group *csr_attr_grps[] = {
+ &csr_attr_grp,
+ NULL,
+};
+
+static int csr_read_bytecntr_value(struct csr_drvdata *drvdata, void __iomem *address)
+{
+ int ret;
+ int byte_cntr_value;
+
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret)
+ return ret;
+
+ byte_cntr_value = readl_relaxed(address);
+ clk_disable_unprepare(drvdata->clk);
+
+ return byte_cntr_value;
+}
+
+static int csr_enable(struct coresight_device *csdev, enum cs_mode mode,
+ void *data)
+{
+ struct csr_drvdata *drvdata;
+ struct coresight_device *assoc_csdev = (struct coresight_device *)data;
+ struct csr_assoc_data *assoc_data = NULL;
+ u32 byte_cntr_value;
+ u32 offset;
+
+ drvdata = csdev_to_csr_drvdata(csdev);
+ list_for_each_entry(assoc_data, &drvdata->csr_assoc, node) {
+ /* Get the assocated data of the device by the device name */
+ if (!strcmp(dev_name(&assoc_csdev->dev),
+ dev_name(&assoc_data->assoc_csdev->dev))) {
+ /* If it is ETR device, start byte-cntr function. */
+ if (assoc_data->assoc_csdev->type == CORESIGHT_DEV_TYPE_SINK) {
+ offset = assoc_data->byte_cntr_data.byte_cntr_offset;
+ byte_cntr_value = csr_read_bytecntr_value(drvdata,
+ drvdata->base + offset);
+ if (byte_cntr_value > 0) {
+ assoc_data->byte_cntr_data.block_size = byte_cntr_value * 8;
+ csr_byte_cntr_start(&assoc_data->byte_cntr_data);
+ }
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int csr_disable(struct coresight_device *csdev, void *data)
+{
+ struct csr_drvdata *drvdata;
+ struct csr_assoc_data *assoc_data = NULL;
+ struct coresight_device *assoc_csdev = (struct coresight_device *)data;
+ u32 byte_cntr_value;
+ u32 offset;
+
+ drvdata = csdev_to_csr_drvdata(csdev);
+
+ list_for_each_entry(assoc_data, &drvdata->csr_assoc, node) {
+ if (!strcmp(dev_name(&assoc_csdev->dev),
+ dev_name(&assoc_data->assoc_csdev->dev))) {
+ if (assoc_data->assoc_csdev->type == CORESIGHT_DEV_TYPE_SINK) {
+ offset = assoc_data->byte_cntr_data.byte_cntr_offset;
+ byte_cntr_value = csr_read_bytecntr_value(drvdata,
+ drvdata->base + offset);
+ if (byte_cntr_value > 0) {
+ assoc_data->byte_cntr_data.block_size = 0;
+ csr_byte_cntr_stop(&assoc_data->byte_cntr_data);
+ }
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const struct coresight_ops_helper csr_helper_ops = {
+ .enable = csr_enable,
+ .disable = csr_disable,
+};
+
+static const struct coresight_ops csr_ops = {
+ .helper_ops = &csr_helper_ops,
+};
+
+const char *csr_plat_get_node_name(struct fwnode_handle *fwnode)
+{
+ if (is_of_node(fwnode))
+ return of_node_full_name(to_of_node(fwnode));
+ return "unknown";
+}
+
+static bool csr_plat_node_name_eq(struct fwnode_handle *fwnode,
+ const char *name)
+{
+ if (is_of_node(fwnode))
+ return of_node_name_eq(to_of_node(fwnode), name);
+ return false;
+}
+
+static void csr_add_assoc_to_csdev(struct coresight_device *csdev)
+{
+ struct csr_drvdata *drvdata;
+ struct csr_assoc_data *assoc_data = NULL;
+ const char *name = NULL;
+
+ name = csr_plat_get_node_name(dev_fwnode(csdev->dev.parent));
+ list_for_each_entry(drvdata, &csr_list, node) {
+ list_for_each_entry(assoc_data, &drvdata->csr_assoc, node) {
+ if (!strcmp(assoc_data->assoc_dev_name, name)) {
+ assoc_data->assoc_dev_name = dev_name(&csdev->dev);
+ assoc_data->assoc_csdev = csdev;
+ coresight_add_helper(csdev, drvdata->csdev);
+ break;
+ }
+ }
+ }
+}
+
+static void csr_remove_assoc_from_csdev(struct coresight_device *csdev)
+{
+ struct csr_drvdata *drvdata;
+ struct csr_assoc_data *assoc_data = NULL;
+ const char *name = NULL;
+
+ name = csr_plat_get_node_name(dev_fwnode(csdev->dev.parent));
+ list_for_each_entry(drvdata, &csr_list, node) {
+ list_for_each_entry(assoc_data, &drvdata->csr_assoc, node) {
+ if (!strcmp(assoc_data->assoc_dev_name, name)) {
+ assoc_data->assoc_csdev = NULL;
+ break;
+ }
+ }
+ }
+}
+
+static struct csr_assoc_op csr_assoc_ops = {
+ .add = csr_add_assoc_to_csdev,
+ .remove = csr_remove_assoc_from_csdev
+};
+
+/**
+ * Get the data of the assocated device to CSR.
+ * @dev: Device of CSR.
+ * @drvdata: Driver data of CSR.
+ */
+static int csr_get_assoc_data(struct device *dev, struct csr_drvdata *drvdata)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+ struct fwnode_handle *child = NULL;
+ struct fwnode_handle *cs_fwnode = NULL;
+ struct csr_assoc_data *assoc_data = NULL;
+ struct coresight_device *csdev = NULL;
+ u32 dev_type;
+ int ret;
+
+ if (IS_ERR_OR_NULL(fwnode))
+ return -EINVAL;
+
+ /* Get the data from each child node of the CSR node. */
+ fwnode_for_each_child_node(fwnode, child) {
+ if (csr_plat_node_name_eq(child, CSR_DT_ASSOC_DEVICE)) {
+ assoc_data = devm_kzalloc(dev, sizeof(struct csr_assoc_data), GFP_KERNEL);
+ if (!assoc_data)
+ return -ENOMEM;
+
+ /* Assocated DT node */
+ cs_fwnode = fwnode_find_reference(child, CSR_DT_CSDEV_ASSOC, 0);
+ if (IS_ERR(cs_fwnode))
+ return -EINVAL;
+
+ /* Find the assocated csdev by fwnode. */
+ csdev = coresight_find_csdev_by_fwnode(cs_fwnode);
+ if (csdev) {
+ assoc_data->assoc_dev_name = dev_name(&csdev->dev);
+ assoc_data->assoc_csdev = csdev;
+ } else {
+ assoc_data->assoc_dev_name = csr_plat_get_node_name(cs_fwnode);
+ assoc_data->assoc_csdev = NULL;
+ }
+ assoc_data->fnode = child;
+ ret = fwnode_property_read_u32(child, CSR_DT_CSDEV_ASSOC_DEV_TYPE,
+ &dev_type);
+ if (ret)
+ return ret;
+ /* If assocated device is ETR device. Init all the byte counter data*/
+ if (dev_type == CSR_ASSOC_DEV_ETR) {
+ assoc_data->dev_type = CSR_ASSOC_DEV_ETR;
+ byte_cntr_init(drvdata, assoc_data);
+ }
+ /* Add assocate data to the list */
+ list_add_tail(&assoc_data->node, &drvdata->csr_assoc);
+ }
+ }
+
+ fwnode_handle_put(child);
+ return 0;
+}
+
static int csr_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -23,6 +288,7 @@ static int csr_probe(struct platform_device *pdev)
struct csr_drvdata *drvdata;
struct resource *res;
struct coresight_desc desc = { 0 };
+ int ret = 0;

desc.name = coresight_alloc_device_name(&csr_devs, dev);
if (!desc.name)
@@ -51,23 +317,40 @@ static int csr_probe(struct platform_device *pdev)
if (!drvdata->base)
return -ENOMEM;

+ INIT_LIST_HEAD(&drvdata->csr_assoc);
+ ret = csr_get_assoc_data(dev, drvdata);
+ if (ret)
+ return ret;
+
desc.type = CORESIGHT_DEV_TYPE_HELPER;
+ desc.subtype.helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_CSR;
desc.pdata = pdev->dev.platform_data;
desc.dev = &pdev->dev;
+ desc.groups = csr_attr_grps;
+ desc.ops = &csr_ops;

drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);

+ list_add(&drvdata->node, &csr_list);
spin_lock_init(&drvdata->spin_lock);

dev_dbg(dev, "CSR initialized: %s\n", dev_name(dev));
- return 0;
+ return ret;
}

static int csr_remove(struct platform_device *pdev)
{
struct csr_drvdata *drvdata = platform_get_drvdata(pdev);
+ struct csr_assoc_data *assoc_data = NULL;
+
+ list_for_each_entry(drvdata, &csr_list, node) {
+ list_for_each_entry(assoc_data, &drvdata->csr_assoc, node) {
+ if (assoc_data->assoc_csdev->type == CORESIGHT_DEV_TYPE_SINK)
+ byte_cntr_remove(&assoc_data->byte_cntr_data);
+ }
+ }

coresight_unregister(drvdata->csdev);
return 0;
@@ -90,12 +373,18 @@ static struct platform_driver csr_driver = {

static int __init csr_init(void)
{
- return platform_driver_register(&csr_driver);
+ int ret;
+
+ ret = platform_driver_register(&csr_driver);
+ coresight_set_csr_ops(&csr_assoc_ops);
+
+ return ret;
}
module_init(csr_init);

static void __exit csr_exit(void)
{
+ coresight_remove_csr_ops();
platform_driver_unregister(&csr_driver);
}
module_exit(csr_exit);
diff --git a/drivers/hwtracing/coresight/coresight-csr.h b/drivers/hwtracing/coresight/coresight-csr.h
index b4fb947fe23b..d253d06a7f4e 100644
--- a/drivers/hwtracing/coresight/coresight-csr.h
+++ b/drivers/hwtracing/coresight/coresight-csr.h
@@ -5,10 +5,80 @@

#ifndef _CORESIGHT_CSR_H
#define _CORESIGHT_CSR_H
+
+#include <linux/cdev.h>
#include <linux/clk.h>
#include <linux/coresight.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/moduleparam.h>
#include <linux/of.h>
+#include <linux/property.h>
+
+#include "coresight-tmc.h"
+
+#define CSR_DT_ASSOC_DEVICE "assoc_device"
+#define CSR_DT_CSDEV_ASSOC "qcom,cs-dev-assoc"
+#define CSR_DT_CSDEV_ASSOC_DEV_TYPE "qcom,cs-dev-type"
+#define CSR_DT_BYTECNTVAL_OFFSET "qcom,csr-bytecntr-offset"
+#define CSR_DT_BYTECNTR_NAME "qcom,csr-bytecntr-name"
+#define CSR_DT_BYTECNTR_CLASS_NAME "qcom,csr-bytecntr-class-name"
+#define CSR_DT_BYTECNTR_IRQ "byte-cntr-irq"
+
+#define CSR_ETR_BYTECNTVAL 0x06C
+
+/**
+ * struct byte_cntr
+ * @dev: cdev for byte_cntr.
+ * @driver_class: device classes.
+ * @enable: Indicates that byte_cntr function is enabled or not.
+ * @read_active: Indicates that byte-cntr node is opened or not.
+ * @block_size: Byte counter value of IRQ.
+ * @byte_cntr_irq: IRQ number.
+ * @irq_cnt: The irq counter number for the ETR data.
+ * @wq: Workqueue of reading ETR data.
+ * @read_work: Work of reading ETR data.
+ * @byte_cntr_lock: lock of byte_cntr data.
+ * @offset: Offset of reading pointer.
+ * @byte_cntr_offset: BYTECNTR IRQ Register offset to the CSR base.
+ * @name: The name of byte cntr device node.
+ * @class_name: Device class name configured in DT.
+ */
+struct byte_cntr {
+ struct cdev dev;
+ struct class *driver_class;
+ bool enable;
+ bool read_active;
+ u32 block_size;
+ int byte_cntr_irq;
+ atomic_t irq_cnt;
+ wait_queue_head_t wq;
+ struct work_struct read_work;
+ struct mutex byte_cntr_lock;
+ unsigned long offset;
+ u32 byte_cntr_offset;
+ const char *name;
+ const char *class_name;
+};
+
+/**
+ * struct csr_assoc_data - Data of assocated device to CSR device.
+ * @assoc_dev_name: Assocated device's name.
+ * @assoc_csdev: Assocated csdev.
+ * @node: List node for CSR assocated data list.
+ * @dev_type: Type of the assocated device.
+ * @fnode: fwnode of the child node.
+ * @byte_cntr_data: byte_cntr_data of the assocated device.
+ */
+struct csr_assoc_data {
+ const char *assoc_dev_name;
+ struct coresight_device *assoc_csdev;
+ struct list_head node;
+ u32 dev_type;
+ struct fwnode_handle *fnode;
+ struct byte_cntr byte_cntr_data;
+};

/**
* struct csr_drvdata - specifics for the CSR device.
@@ -16,17 +86,27 @@
* @pbase: Physical address base.
* @dev: The device entity associated to this component.
* @csdev: Data struct for coresight device.
+ * @node: List node of the CSR data list.
+ * @csr_assoc: Assocated device data list.
* @clk: Clock of this component.
* @spin_lock: Spin lock for the data.
+ * @etr_byte_cntr_value: Byte cntr value of the ETR.
*/
struct csr_drvdata {
void __iomem *base;
phys_addr_t pbase;
struct device *dev;
struct coresight_device *csdev;
+ struct list_head node;
+ struct list_head csr_assoc;
struct clk *clk;
spinlock_t spin_lock;
+ u32 etr_byte_cntr_value;
};

+void csr_byte_cntr_start(struct byte_cntr *byte_cntr_data);
+void csr_byte_cntr_stop(struct byte_cntr *byte_cntr_data);
+int byte_cntr_init(struct csr_drvdata *drvdata, struct csr_assoc_data *assoc_data);
+void byte_cntr_remove(struct byte_cntr *byte_cntr_data);
#endif

diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 767076e07970..b95478d52ed7 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -164,6 +164,14 @@ struct cti_assoc_op {
extern void coresight_set_cti_ops(const struct cti_assoc_op *cti_op);
extern void coresight_remove_cti_ops(void);

+struct csr_assoc_op {
+ void (*add)(struct coresight_device *csdev);
+ void (*remove)(struct coresight_device *csdev);
+};
+
+extern void coresight_set_csr_ops(const struct csr_assoc_op *csr_op);
+extern void coresight_remove_csr_ops(void);
+
/*
* Macros and inline functions to handle CoreSight UCI data and driver
* private data in AMBA ID table entries, and extract data values.
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 66dc5f97a009..faa2928c239f 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -905,7 +905,7 @@ static void tmc_free_etr_buf(struct etr_buf *etr_buf)
* Returns: The size of the linear data available @pos, with *bufpp
* updated to point to the buffer.
*/
-static ssize_t tmc_etr_buf_get_data(struct etr_buf *etr_buf,
+ssize_t tmc_etr_buf_get_data(struct etr_buf *etr_buf,
u64 offset, size_t len, char **bufpp)
{
/* Adjust the length to limit this transaction to end of buffer */
@@ -913,6 +913,7 @@ static ssize_t tmc_etr_buf_get_data(struct etr_buf *etr_buf,

return etr_buf->ops->get_data(etr_buf, (u64)offset, len, bufpp);
}
+EXPORT_SYMBOL_GPL(tmc_etr_buf_get_data);

static inline s64
tmc_etr_buf_insert_barrier_packet(struct etr_buf *etr_buf, u64 offset)
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 0ee48c5ba764..ea5aec00c2be 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -322,6 +322,8 @@ void tmc_sg_table_sync_data_range(struct tmc_sg_table *table,
u64 offset, u64 size);
ssize_t tmc_sg_table_get_data(struct tmc_sg_table *sg_table,
u64 offset, size_t len, char **bufpp);
+ssize_t tmc_etr_buf_get_data(struct etr_buf *etr_buf,
+ u64 offset, size_t len, char **bufpp);
static inline unsigned long
tmc_sg_table_buf_size(struct tmc_sg_table *sg_table)
{
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index a269fffaf991..15994f6b0873 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -69,7 +69,8 @@ enum coresight_dev_subtype_source {

enum coresight_dev_subtype_helper {
CORESIGHT_DEV_SUBTYPE_HELPER_CATU,
- CORESIGHT_DEV_SUBTYPE_HELPER_ECT_CTI
+ CORESIGHT_DEV_SUBTYPE_HELPER_ECT_CTI,
+ CORESIGHT_DEV_SUBTYPE_HELPER_CSR,
};

/**
--
2.17.1