+
+/* Start the byte-cntr function when the path is enabled. */
+void ctcu_byte_cntr_start(struct coresight_device *csdev, struct
coresight_path *path)
+{
+ struct ctcu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+ struct coresight_device *sink = coresight_get_sink(path);
+ struct ctcu_byte_cntr *byte_cntr_data;
+ int port_num;
+
+ if (!sink)
+ return;
+
+ port_num = ctcu_get_active_port(sink, csdev);
+ if (port_num < 0)
+ return;
+
+ byte_cntr_data = &drvdata->byte_cntr_data[port_num];
+ /* Don't start byte-cntr function when threshold is not set. */
+ if (!byte_cntr_data->thresh_val)
+ return;
+
+ guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
+ atomic_set(&byte_cntr_data->irq_cnt, 0);
+ byte_cntr_data->sink = sink;
+ byte_cntr_data->enable = true;
+}
+
+/* Stop the byte-cntr function when the path is disabled. */
+void ctcu_byte_cntr_stop(struct coresight_device *csdev, struct
coresight_path *path)
+{
+ struct ctcu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+ struct coresight_device *sink = coresight_get_sink(path);
+ struct ctcu_byte_cntr *byte_cntr_data;
+ struct tmc_drvdata *tmcdrvdata;
+ int port_num;
+
+ if (!sink)
+ return;
+
+ port_num = ctcu_get_active_port(sink, csdev);
+ if (port_num < 0)
+ return;
+
+ byte_cntr_data = &drvdata->byte_cntr_data[port_num];
+ tmcdrvdata = dev_get_drvdata(byte_cntr_data->sink->dev.parent);
+
+ guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
+ /* Store the w_offset of the ETR buffer when stopping. */
+ byte_cntr_data->w_offset = tmc_get_rwp_offset(tmcdrvdata);
+ atomic_set(&byte_cntr_data->irq_cnt, 0);
+ byte_cntr_data->read_active = false;
+ byte_cntr_data->enable = false;
+ /*
+ * Wakeup once to force the read function to read the remaining
+ * data of the ETR buffer.
+ */
+ wake_up(&byte_cntr_data->wq);
+}
+
+static int ctcu_byte_cntr_release(struct inode *in, struct file *fp)
+{
+ struct ctcu_byte_cntr *byte_cntr_data = fp->private_data;
+ struct device *dev = &byte_cntr_data->sink->dev;
+
+ guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
+ atomic_set(&byte_cntr_data->irq_cnt, 0);
+ byte_cntr_data->read_active = false;
+ disable_irq_wake(byte_cntr_data->byte_cntr_irq);
+ dev_dbg(dev, "send data total size: %llu bytes, r_offset: %ld
w_offset: %ld\n",
+ byte_cntr_data->total_size, byte_cntr_data->r_offset,
+ byte_cntr_data->w_offset);
+
+ return 0;
+}
+
+static int ctcu_byte_cntr_open(struct inode *in, struct file *fp)
+{
+ struct ctcu_byte_cntr *byte_cntr_data = container_of(in->i_cdev,
+ struct ctcu_byte_cntr, c_dev);
+ struct tmc_drvdata *tmcdrvdata;
+
+ if (byte_cntr_data->read_active)
+ return -EBUSY;
+
+ if (!byte_cntr_data->thresh_val || !byte_cntr_data->sink ||
+ (coresight_get_mode(byte_cntr_data->sink) != CS_MODE_SYSFS))
+ return -EINVAL;
+
+ guard(raw_spinlock_irqsave)(&byte_cntr_data->spin_lock);
+ enable_irq_wake(byte_cntr_data->byte_cntr_irq);
+ fp->private_data = byte_cntr_data;
+ nonseekable_open(in, fp);
+ tmcdrvdata = dev_get_drvdata(byte_cntr_data->sink->dev.parent);
+ /*
+ * The original r_offset is the w_offset of the ETR buffer at the
+ * start of the byte-cntr.
+ */
+ byte_cntr_data->r_offset = tmc_get_rwp_offset(tmcdrvdata);
+ byte_cntr_data->total_size = 0;
+ byte_cntr_data->read_active = true;
+ byte_cntr_data->enable = true;
+
+ return 0;
+}
+
+static const struct file_operations byte_cntr_fops = {
+ .owner = THIS_MODULE,
+ .open = ctcu_byte_cntr_open,
+ .read = ctcu_byte_cntr_read_etr_bytes,
+ .release = ctcu_byte_cntr_release,
+};
+
+static int ctcu_byte_cntr_register_chardev(struct ctcu_byte_cntr
*byte_cntr_data,
+ int port_num)
+{
+ struct device *device;
+ dev_t devt;
+ int ret;
+
+ cdev_init(&byte_cntr_data->c_dev, &byte_cntr_fops);
+ devt = MKDEV(MAJOR(byte_cntr_base), MINOR(byte_cntr_base) +
port_num);
+ ret = cdev_add(&byte_cntr_data->c_dev, devt, 1);
+ if (ret < 0)
+ return -ENOMEM;
+
+ device = device_create(byte_cntr_class, NULL, devt, byte_cntr_data,
+ byte_cntr_data->name);
+
+ if (IS_ERR(device))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void ctcu_byte_cntr_unregister_chardev(struct ctcu_drvdata
*drvdata)
+{
+ struct ctcu_byte_cntr *byte_cntr_data;
+ int i;
+
+ for (i = 0; i < ETR_MAX_NUM; i++) {
+ byte_cntr_data = &drvdata->byte_cntr_data[i];
+ device_destroy(byte_cntr_class, byte_cntr_data->c_dev.dev);
+ }
+}
+
+int ctcu_byte_cntr_init(struct device *dev, struct ctcu_drvdata
*drvdata, int etr_num)
+{
+ struct ctcu_byte_cntr *byte_cntr_data;
+ struct device_node *nd = dev->of_node;
+ int byte_cntr_irq, ret, i;
+
+ ret = alloc_chrdev_region(&byte_cntr_base, 0, ETR_MAX_NUM,
BYTE_CNTR_CLASS_STR);
+ if (ret < 0)
+ return -ENOMEM;
+
+ byte_cntr_class = class_create(BYTE_CNTR_CLASS_STR);
+ if (IS_ERR(byte_cntr_class)) {
+ unregister_chrdev_region(byte_cntr_base, ETR_MAX_NUM);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < etr_num; i++) {
+ byte_cntr_data = &drvdata->byte_cntr_data[i];
+ byte_cntr_irq = of_irq_get_byname(nd, byte_cntr_data-
>irq_name);
+ if (byte_cntr_irq < 0) {
+ ret = byte_cntr_irq;
+ goto err_exit;
+ }
+
+ ret = devm_request_irq(dev, byte_cntr_irq, byte_cntr_handler,
+ IRQF_TRIGGER_RISING | IRQF_SHARED,
+ dev_name(dev), byte_cntr_data);
+ if (ret) {
+ dev_err(dev, "Failed to register IRQ for %s\n",
+ byte_cntr_data->name);
+ goto err_exit;
+ }
+
+ ret = ctcu_byte_cntr_register_chardev(byte_cntr_data, i);
+ if (ret) {
+ dev_err(dev, "Failed to register chardev for %s\n",
+ byte_cntr_data->name);
+ goto err_exit;
+ }
+
+ byte_cntr_data->byte_cntr_irq = byte_cntr_irq;
+ atomic_set(&byte_cntr_data->irq_cnt, 0);
+ init_waitqueue_head(&byte_cntr_data->wq);
+ }
+
+ return 0;
+
+err_exit:
+ ctcu_byte_cntr_unregister_chardev(drvdata);
+ class_destroy(byte_cntr_class);
+ unregister_chrdev_region(byte_cntr_base, ETR_MAX_NUM);
+ return ret;
+}
+
+void ctcu_byte_cntr_remove(struct ctcu_drvdata *drvdata)
+{
+ ctcu_byte_cntr_unregister_chardev(drvdata);
+ class_destroy(byte_cntr_class);
+ unregister_chrdev_region(byte_cntr_base, ETR_MAX_NUM);
+}
diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/
drivers/hwtracing/coresight/coresight-ctcu-core.c
index da35d8b4d579..5782655a5f39 100644
--- a/drivers/hwtracing/coresight/coresight-ctcu-core.c
+++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c
@@ -46,16 +46,24 @@ DEFINE_CORESIGHT_DEVLIST(ctcu_devs, "ctcu");
#define CTCU_ATID_REG_BIT(traceid) (traceid % 32)
#define CTCU_ATID_REG_SIZE 0x10
#define CTCU_ETR0_ATID0 0xf8
+#define CTCU_ETR0_IRQCTRL 0x6c
#define CTCU_ETR1_ATID0 0x108
+#define CTCU_ETR1_IRQCTRL 0x70
static const struct ctcu_etr_config sa8775p_etr_cfgs[] = {
{
- .atid_offset = CTCU_ETR0_ATID0,
- .port_num = 0,
+ .atid_offset = CTCU_ETR0_ATID0,
+ .irq_ctrl_offset = CTCU_ETR0_IRQCTRL,
+ .irq_name = "etr0",
+ .byte_cntr_name = "byte-cntr0",
+ .port_num = 0,
},
{
- .atid_offset = CTCU_ETR1_ATID0,
- .port_num = 1,
+ .atid_offset = CTCU_ETR1_ATID0,
+ .irq_ctrl_offset = CTCU_ETR1_IRQCTRL,
+ .irq_name = "etr1",
+ .byte_cntr_name = "byte-cntr1",
+ .port_num = 1,
},
};
@@ -64,6 +72,69 @@ static const struct ctcu_config sa8775p_cfgs = {
.num_etr_config = ARRAY_SIZE(sa8775p_etr_cfgs),
};
+static ssize_t byte_cntr_val_show(struct device *dev, struct
device_attribute *attr,
+ char *buf)
+{
+ struct ctcu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ int i, len = 0;
+
+ for (i = 0; i < ETR_MAX_NUM; i++) {
+ if (drvdata->byte_cntr_data[i].irq_ctrl_offset)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%u ",
+ drvdata->byte_cntr_data[i].thresh_val);
+ }
+
+ len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
+
+ return len;
+}
+
+static ssize_t byte_cntr_val_store(struct device *dev, struct
device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct ctcu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ u32 thresh_vals[ETR_MAX_NUM] = { 0 };
+ u32 irq_ctrl_offset;
+ int num, i;
+
+ num = sscanf(buf, "%i %i", &thresh_vals[0], &thresh_vals[1]);
+ if (num <= 0 || num > ETR_MAX_NUM)
+ return -EINVAL;
+
+ /* Threshold 0 disables the interruption. */
+ guard(raw_spinlock_irqsave)(&drvdata->spin_lock);
+ for (i = 0; i < num; i++) {
+ /* A small threshold will result in a large number of
interruptions */
+ if (thresh_vals[i] && thresh_vals[i] < 4096)
+ return -EINVAL;
+
+ if (drvdata->byte_cntr_data[i].irq_ctrl_offset) {
+ drvdata->byte_cntr_data[i].thresh_val = thresh_vals[i];
+ irq_ctrl_offset = drvdata-
>byte_cntr_data[i].irq_ctrl_offset;
+ CS_UNLOCK(drvdata->base);
+ writel_relaxed(thresh_vals[i], drvdata->base +
irq_ctrl_offset);
+ CS_LOCK(drvdata->base);
+ }
+ }
+
+ return size;
+}
+static DEVICE_ATTR_RW(byte_cntr_val);
+
+static struct attribute *ctcu_attrs[] = {
+ &dev_attr_byte_cntr_val.attr,
+ NULL,
+};
+
+static struct attribute_group ctcu_attr_grp = {
+ .attrs = ctcu_attrs,
+};
+
+static const struct attribute_group *ctcu_attr_grps[] = {
+ &ctcu_attr_grp,
+ NULL,
+};
+
static void ctcu_program_atid_register(struct ctcu_drvdata
*drvdata, u32 reg_offset,
u8 bit, bool enable)
{
@@ -122,7 +193,7 @@ static int __ctcu_set_etr_traceid(struct
coresight_device *csdev, u8 traceid, in
* Searching the sink device from helper's view in case there are
multiple helper devices
* connected to the sink device.
*/
-static int ctcu_get_active_port(struct coresight_device *sink,
struct coresight_device *helper)
+int ctcu_get_active_port(struct coresight_device *sink, struct
coresight_device *helper)
{
struct coresight_platform_data *pdata = helper->pdata;
int i;
@@ -160,6 +231,8 @@ static int ctcu_enable(struct coresight_device
*csdev, enum cs_mode mode, void *
{
struct coresight_path *path = (struct coresight_path *)data;
+ ctcu_byte_cntr_start(csdev, path);
+
return ctcu_set_etr_traceid(csdev, path, true);
}
@@ -167,6 +240,8 @@ static int ctcu_disable(struct coresight_device
*csdev, void *data)
{
struct coresight_path *path = (struct coresight_path *)data;
+ ctcu_byte_cntr_stop(csdev, path);
+
return ctcu_set_etr_traceid(csdev, path, false);
}
@@ -188,7 +263,7 @@ static int ctcu_probe(struct platform_device *pdev)
const struct ctcu_config *cfgs;
struct ctcu_drvdata *drvdata;
void __iomem *base;
- int i;
+ int ret, i;
desc.name = coresight_alloc_device_name(&ctcu_devs, dev);
if (!desc.name)
@@ -217,7 +292,14 @@ static int ctcu_probe(struct platform_device *pdev)
for (i = 0; i < cfgs->num_etr_config; i++) {
etr_cfg = &cfgs->etr_cfgs[i];
drvdata->atid_offset[i] = etr_cfg->atid_offset;
+ drvdata->byte_cntr_data[i].irq_name = etr_cfg-
>irq_name;
+ drvdata->byte_cntr_data[i].name = etr_cfg-
>byte_cntr_name;
+ drvdata->byte_cntr_data[i].irq_ctrl_offset =
+ etr_cfg->irq_ctrl_offset;
}
+ ret = ctcu_byte_cntr_init(dev, drvdata, cfgs-
>num_etr_config);
+ if (ret < 0)
+ dev_warn(dev, "Byte cntr init failed\n");
}
}
@@ -229,6 +311,7 @@ static int ctcu_probe(struct platform_device *pdev)
desc.subtype.helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_CTCU;
desc.pdata = pdata;
desc.dev = dev;
+ desc.groups = ctcu_attr_grps;
desc.ops = &ctcu_ops;
desc.access = CSDEV_ACCESS_IOMEM(base);
@@ -247,6 +330,7 @@ static void ctcu_remove(struct platform_device
*pdev)
{
struct ctcu_drvdata *drvdata = platform_get_drvdata(pdev);
+ ctcu_byte_cntr_remove(drvdata);
coresight_unregister(drvdata->csdev);
}
diff --git a/drivers/hwtracing/coresight/coresight-ctcu.h b/drivers/
hwtracing/coresight/coresight-ctcu.h
index e9594c38dd91..e38535c91090 100644
--- a/drivers/hwtracing/coresight/coresight-ctcu.h
+++ b/drivers/hwtracing/coresight/coresight-ctcu.h
@@ -5,6 +5,8 @@
#ifndef _CORESIGHT_CTCU_H
#define _CORESIGHT_CTCU_H
+#include <linux/cdev.h>
+
#include "coresight-trace-id.h"
/* Maximum number of supported ETR devices for a single CTCU. */
@@ -13,10 +15,16 @@
/**
* struct ctcu_etr_config
* @atid_offset: offset to the ATID0 Register.
- * @port_num: in-port number of CTCU device that connected to
ETR.
+ * @irq_ctrl_offset: offset to the BYTECNTRVAL register.
+ * @irq_name: IRQ name in dt node.
+ * @byte_cntr_name: name of the byte cntr device node.
+ * @port_num: in-port number of the CTCU device that
connected to ETR.
*/
struct ctcu_etr_config {
const u32 atid_offset;
+ const u32 irq_ctrl_offset;
+ const char *irq_name;
+ const char *byte_cntr_name;
const u32 port_num;
};
@@ -25,15 +33,64 @@ struct ctcu_config {
int num_etr_config;
};
+/**
+ * struct ctcu_byte_cntr
+ * @c_dev: cdev for byte_cntr.
+ * @sink csdev of sink device.
+ * @enable: indicates that byte_cntr function is enabled or not.
+ * @read_active: indicates that byte-cntr node is opened or not.
+ * @thresh_val: threshold to trigger a interruption.
+ * @total_size total size of transferred data.
+ * @byte_cntr_irq: IRQ number.
+ * @irq_cnt: IRQ count.
+ * @wq: workqueue of reading ETR data.
+ * @read_work: work of reading ETR data.
+ * @spin_lock: spinlock of byte cntr data.
+ * @r_offset: offset of the pointer where reading begins.
+ * @w_offset: offset of the write pointer in the ETR buffer when
+ * the byte cntr is stopped.
+ * @irq_ctrl_offset: offset to the BYTECNTRVAL Register.
+ * @name: the name of byte cntr device node.
+ * @irq_name: IRQ name in DT.
+ */
+struct ctcu_byte_cntr {
+ struct cdev c_dev;
+ struct coresight_device *sink;
+ bool enable;
+ bool read_active;
+ u32 thresh_val;
+ u64 total_size;
+ int byte_cntr_irq;
+ atomic_t irq_cnt;
+ wait_queue_head_t wq;
+ struct work_struct read_work;
+ raw_spinlock_t spin_lock;
+ long r_offset;
+ long w_offset;
+ u32 irq_ctrl_offset;
+ const char *name;
+ const char *irq_name;
+};
+
struct ctcu_drvdata {
void __iomem *base;
struct clk *apb_clk;
struct device *dev;
struct coresight_device *csdev;
+ struct ctcu_byte_cntr byte_cntr_data[ETR_MAX_NUM];
raw_spinlock_t spin_lock;
u32 atid_offset[ETR_MAX_NUM];
/* refcnt for each traceid of each sink */
u8 traceid_refcnt[ETR_MAX_NUM]
[CORESIGHT_TRACE_ID_RES_TOP];
};
+/* Generic functions */
+int ctcu_get_active_port(struct coresight_device *sink, struct
coresight_device *helper);
+
+/* Byte-cntr functions */
+void ctcu_byte_cntr_start(struct coresight_device *csdev, struct
coresight_path *path);
+void ctcu_byte_cntr_stop(struct coresight_device *csdev, struct
coresight_path *path);
+int ctcu_byte_cntr_init(struct device *dev, struct ctcu_drvdata
*drvdata, int port_num);
+void ctcu_byte_cntr_remove(struct ctcu_drvdata *drvdata);
+
#endif
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/
drivers/hwtracing/coresight/coresight-tmc-etr.c
index ec636ab1fd75..5dc94e890927 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1040,14 +1040,15 @@ 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,
- 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)
{
/* Adjust the length to limit this transaction to end of buffer */
len = (len < (etr_buf->size - offset)) ? len : etr_buf->size -
offset;
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 baedb4dcfc3f..2fc77fd4ea25 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -443,5 +443,7 @@ struct etr_buf *tmc_etr_get_buffer(struct
coresight_device *csdev,
enum cs_mode mode, void *data);
extern const struct attribute_group coresight_etr_group;
long tmc_get_rwp_offset(struct tmc_drvdata *drvdata);
+ssize_t tmc_etr_buf_get_data(struct etr_buf *etr_buf, u64 offset,
size_t len,
+ char **bufpp);
#endif