@@ -229,6 +229,7 @@ static int tmc_prepare_crashdata(struct tmc_drvdata *drvdata)
static int tmc_read_prepare(struct tmc_drvdata *drvdata)
{
+ struct ctcu_byte_cntr *byte_cntr_data = drvdata->byte_cntr_data;
int ret = 0;
switch (drvdata->config_type) {
@@ -237,7 +238,10 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata)
ret = tmc_read_prepare_etb(drvdata);
break;
case TMC_CONFIG_TYPE_ETR:
- ret = tmc_read_prepare_etr(drvdata);
+ if (byte_cntr_data && byte_cntr_data->thresh_val)
+ ret = tmc_read_byte_cntr_prepare(drvdata);
+ else
+ ret = tmc_read_prepare_etr(drvdata);
break;
default:
ret = -EINVAL;
@@ -251,6 +255,7 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata)
static int tmc_read_unprepare(struct tmc_drvdata *drvdata)
{
+ struct ctcu_byte_cntr *byte_cntr_data = drvdata->byte_cntr_data;
int ret = 0;
switch (drvdata->config_type) {
@@ -259,7 +264,10 @@ static int tmc_read_unprepare(struct tmc_drvdata *drvdata)
ret = tmc_read_unprepare_etb(drvdata);
break;
case TMC_CONFIG_TYPE_ETR:
- ret = tmc_read_unprepare_etr(drvdata);
+ if (byte_cntr_data && byte_cntr_data->thresh_val)
+ ret = tmc_read_byte_cntr_unprepare(drvdata);
+ else
+ ret = tmc_read_unprepare_etr(drvdata);
break;
default:
ret = -EINVAL;
@@ -290,11 +298,16 @@ static int tmc_open(struct inode *inode, struct file *file)
static inline ssize_t tmc_get_sysfs_trace(struct tmc_drvdata *drvdata,
loff_t pos, size_t len, char **bufpp)
{
+ struct ctcu_byte_cntr *byte_cntr_data = drvdata->byte_cntr_data;
+
switch (drvdata->config_type) {
case TMC_CONFIG_TYPE_ETB:
case TMC_CONFIG_TYPE_ETF:
return tmc_etb_get_sysfs_trace(drvdata, pos, len, bufpp);
case TMC_CONFIG_TYPE_ETR:
+ if (byte_cntr_data && byte_cntr_data->thresh_val)
+ return tmc_byte_cntr_get_data(drvdata, &len, bufpp);
+
return tmc_etr_get_sysfs_trace(drvdata, pos, len, bufpp);
}
@@ -308,6 +321,8 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
ssize_t actual;
struct tmc_drvdata *drvdata = container_of(file->private_data,
struct tmc_drvdata, miscdev);
+ struct ctcu_byte_cntr *byte_cntr_data = drvdata->byte_cntr_data;
+
actual = tmc_get_sysfs_trace(drvdata, *ppos, len, &bufp);
if (actual <= 0)
return 0;
@@ -318,7 +333,15 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
return -EFAULT;
}
- *ppos += actual;
+ if (byte_cntr_data && byte_cntr_data->thresh_val) {
+ byte_cntr_data->total_size += actual;
+ if (byte_cntr_data->r_offset + actual >= drvdata->size)
+ byte_cntr_data->r_offset = 0;
+ else
+ byte_cntr_data->r_offset += actual;
+ } else
+ *ppos += actual;
+
dev_dbg(&drvdata->csdev->dev, "%zu bytes copied\n", actual);
return actual;
@@ -18,6 +18,7 @@
#include "coresight-etm-perf.h"
#include "coresight-priv.h"
#include "coresight-tmc.h"
+#include "coresight-ctcu.h"
struct etr_flat_buf {
struct device *dev;
@@ -1148,6 +1149,77 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
return rc;
}
+/* Read the data from ETR's DDR buffer. */
+static ssize_t __tmc_byte_cntr_get_data(struct tmc_drvdata *drvdata, size_t *len,
+ char **bufpp)
+{
+ struct ctcu_byte_cntr *byte_cntr_data = drvdata->byte_cntr_data;
+ size_t actual, bytes = byte_cntr_data->thresh_val;
+ struct etr_buf *etr_buf = drvdata->sysfs_buf;
+ long r_offset = byte_cntr_data->r_offset;
+
+ if (*len >= bytes)
+ *len = bytes;
+ else if ((r_offset % bytes) + *len > bytes)
+ *len = bytes - (r_offset % bytes);
+
+ actual = tmc_etr_buf_get_data(etr_buf, r_offset, *len, bufpp);
+ if (actual == bytes || (actual + r_offset) % bytes == 0)
+ atomic_dec(&byte_cntr_data->irq_cnt);
+
+ return actual;
+}
+
+/* Flush the remaining data in the ETR buffer after the byte-cntr has stopped. */
+static ssize_t tmc_byte_cntr_flush_buffer(struct tmc_drvdata *drvdata, size_t len,
+ char **bufpp)
+{
+ struct ctcu_byte_cntr *byte_cntr_data = drvdata->byte_cntr_data;
+ struct etr_buf *etr_buf = drvdata->sysfs_buf;
+ long r_offset = byte_cntr_data->r_offset;
+ long w_offset = byte_cntr_data->w_offset;
+ ssize_t read_len = 0, remaining_len;
+
+ if (w_offset < r_offset)
+ remaining_len = drvdata->size + w_offset - r_offset;
+ else
+ remaining_len = w_offset - r_offset;
+
+ if (remaining_len > len)
+ remaining_len = len;
+
+ if (remaining_len > 0)
+ read_len = tmc_etr_buf_get_data(etr_buf, r_offset, remaining_len, bufpp);
+
+ return read_len;
+}
+
+ssize_t tmc_byte_cntr_get_data(struct tmc_drvdata *drvdata, size_t *len, char **bufpp)
+{
+ struct ctcu_byte_cntr *byte_cntr_data = drvdata->byte_cntr_data;
+ ssize_t read_len;
+
+ /*
+ * Flush the remaining data in the ETR buffer based on the write
+ * offset of the ETR buffer when the byte cntr function has stopped.
+ */
+ if (!byte_cntr_data->read_active || !byte_cntr_data->enable) {
+ read_len = tmc_byte_cntr_flush_buffer(drvdata, *len, bufpp);
+ if (read_len > 0)
+ return read_len;
+
+ return -EINVAL;
+ }
+
+ if (!atomic_read(&byte_cntr_data->irq_cnt))
+ if (wait_event_interruptible(byte_cntr_data->wq,
+ atomic_read(&byte_cntr_data->irq_cnt) > 0 ||
+ !byte_cntr_data->enable))
+ return -ERESTARTSYS;
+
+ return __tmc_byte_cntr_get_data(drvdata, len, bufpp);
+}
+
/*
* Return the available trace data in the buffer (starts at etr_buf->offset,
* limited by etr_buf->len) from @pos, with a maximum limit of @len,
@@ -1963,6 +2035,32 @@ const struct coresight_ops tmc_etr_cs_ops = {
.panic_ops = &tmc_etr_sync_ops,
};
+int tmc_read_byte_cntr_prepare(struct tmc_drvdata *drvdata)
+{
+ struct ctcu_byte_cntr *byte_cntr_data = drvdata->byte_cntr_data;
+ long r_offset;
+
+ if (byte_cntr_data->read_active)
+ return -EBUSY;
+
+ /*
+ * The original r_offset is the w_offset of the ETR buffer at the
+ * start of the byte-cntr.
+ */
+ r_offset = tmc_etr_get_rwp_offset(drvdata);
+ if (r_offset < 0) {
+ dev_err(&drvdata->csdev->dev, "failed to get r_offset\n");
+ return r_offset;
+ }
+
+ enable_irq_wake(byte_cntr_data->byte_cntr_irq);
+ byte_cntr_data->r_offset = r_offset;
+ byte_cntr_data->total_size = 0;
+ byte_cntr_data->read_active = true;
+
+ return 0;
+}
+
int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
{
int ret = 0;
@@ -1999,6 +2097,21 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
return ret;
}
+int tmc_read_byte_cntr_unprepare(struct tmc_drvdata *drvdata)
+{
+ struct ctcu_byte_cntr *byte_cntr_data = drvdata->byte_cntr_data;
+ struct device *dev = &drvdata->csdev->dev;
+
+ disable_irq_wake(byte_cntr_data->byte_cntr_irq);
+ atomic_set(&byte_cntr_data->irq_cnt, 0);
+ byte_cntr_data->read_active = false;
+ 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;
+}
+
int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
{
unsigned long flags;
@@ -14,6 +14,8 @@
#include <linux/refcount.h>
#include <linux/crc32.h>
+#include "coresight-ctcu.h"
+
#define TMC_RSZ 0x004
#define TMC_STS 0x00c
#define TMC_RRD 0x010
@@ -334,11 +336,15 @@ ssize_t tmc_etb_get_sysfs_trace(struct tmc_drvdata *drvdata,
/* ETR functions */
int tmc_read_prepare_etr(struct tmc_drvdata *drvdata);
int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata);
+int tmc_read_prepare_etr(struct tmc_drvdata *drvdata);
+int tmc_read_byte_cntr_prepare(struct tmc_drvdata *drvdata);
+int tmc_read_byte_cntr_unprepare(struct tmc_drvdata *drvdata);
void tmc_etr_disable_hw(struct tmc_drvdata *drvdata);
extern const struct coresight_ops tmc_etr_cs_ops;
ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
loff_t pos, size_t len, char **bufpp);
-
+ssize_t tmc_byte_cntr_get_data(struct tmc_drvdata *drvdata, size_t *len,
+ char **bufpp);
#define TMC_REG_PAIR(name, lo_off, hi_off) \
static inline u64 \
The byte-cntr function only copy trace data from etr_buf based on the IRQ count number. The system will call byte-cntr realted functions when the BYTECNTRVAL register has configured. Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com> --- .../hwtracing/coresight/coresight-tmc-core.c | 29 ++++- .../hwtracing/coresight/coresight-tmc-etr.c | 113 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-tmc.h | 8 +- 3 files changed, 146 insertions(+), 4 deletions(-)