new file mode 100644
@@ -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 <quic_jinlmao@quicinc.com>
+Description: (RW) Size of the ETR irq byte counter value.
+
@@ -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
@@ -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);
new file mode 100644
@@ -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);
+}
+
@@ -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);
@@ -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
@@ -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.
@@ -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)
@@ -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)
{
@@ -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,
};
/**
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 <quic_jinlmao@quicinc.com> --- .../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