summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShashank Mittal <mittals@codeaurora.org>2016-01-15 19:03:14 -0800
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 11:15:41 -0700
commit24d0ee108b29e73be7a1d0680b4c41aa8c571c43 (patch)
treec390ce6d3280e3680b7ed4a9bde9f379094467c7
parentbabde2831dd9073cd62fec5510f4098b669dfac7 (diff)
coresight: tmc: add scatter-gather support for ETR device
Add support to configure ETR device in scatter-gather mode. In scatter-gather mode trace buffer can be configured to use bigger buffer size without need of bigger contiguous memory. Change-Id: I3ce654392d2b75d24f7982638e53c2aab27d4a0e Signed-off-by: Shashank Mittal <mittals@codeaurora.org>
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.c540
1 files changed, 517 insertions, 23 deletions
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index d4ea26a7efc3..9b83eb335a34 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -27,6 +27,7 @@
#include <linux/of.h>
#include <linux/coresight.h>
#include <linux/amba/bus.h>
+#include <asm/cacheflush.h>
#include "coresight-priv.h"
@@ -79,6 +80,12 @@
#define TMC_STS_TRIGGERED_BIT 2
#define TMC_FFCR_FLUSHMAN_BIT 6
+#define TMC_ETR_SG_ENT_TO_BLK(phys_pte) (((phys_addr_t)phys_pte >> 4) \
+ << PAGE_SHIFT)
+#define TMC_ETR_SG_ENT(phys_pte) (((phys_pte >> PAGE_SHIFT) << 4) | 0x2)
+#define TMC_ETR_SG_NXT_TBL(phys_pte) (((phys_pte >> PAGE_SHIFT) << 4) | 0x3)
+#define TMC_ETR_SG_LST_ENT(phys_pte) (((phys_pte >> PAGE_SHIFT) << 4) | 0x1)
+
enum tmc_config_type {
TMC_CONFIG_TYPE_ETB,
TMC_CONFIG_TYPE_ETR,
@@ -98,6 +105,16 @@ enum tmc_mem_intf_width {
TMC_MEM_INTF_WIDTH_256BITS = 0x5,
};
+enum tmc_etr_mem_type {
+ TMC_ETR_MEM_TYPE_CONTIG,
+ TMC_ETR_MEM_TYPE_SG,
+};
+
+static const char * const str_tmc_etr_mem_type[] = {
+ [TMC_ETR_MEM_TYPE_CONTIG] = "contig",
+ [TMC_ETR_MEM_TYPE_SG] = "sg",
+};
+
/**
* struct tmc_drvdata - specifics associated to an TMC component
* @base: memory mapped base address for this component.
@@ -131,6 +148,10 @@ struct tmc_drvdata {
bool enable;
enum tmc_config_type config_type;
u32 trigger_cntr;
+ enum tmc_etr_mem_type mem_type;
+ enum tmc_etr_mem_type memtype;
+ u32 delta_bottom;
+ int sg_blk_num;
};
static void tmc_wait_for_ready(struct tmc_drvdata *drvdata)
@@ -193,18 +214,233 @@ static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
CS_LOCK(drvdata->base);
}
+static void tmc_etr_sg_tbl_free(uint32_t *vaddr, uint32_t size, uint32_t ents)
+{
+ uint32_t i = 0, pte_n = 0, last_pte;
+ uint32_t *virt_st_tbl, *virt_pte;
+ void *virt_blk;
+ phys_addr_t phys_pte;
+ int total_ents = DIV_ROUND_UP(size, PAGE_SIZE);
+ int ents_per_blk = PAGE_SIZE/sizeof(uint32_t);
+
+ virt_st_tbl = vaddr;
+
+ while (i < total_ents) {
+ last_pte = ((i + ents_per_blk) > total_ents) ?
+ total_ents : (i + ents_per_blk);
+ while (i < last_pte) {
+ virt_pte = virt_st_tbl + pte_n;
+
+ /* Do not go beyond number of entries allocated */
+ if (i == ents) {
+ free_page((unsigned long)virt_st_tbl);
+ return;
+ }
+
+ phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte);
+ virt_blk = phys_to_virt(phys_pte);
+
+ if ((last_pte - i) > 1) {
+ free_page((unsigned long)virt_blk);
+ pte_n++;
+ } else if (last_pte == total_ents) {
+ free_page((unsigned long)virt_blk);
+ free_page((unsigned long)virt_st_tbl);
+ } else {
+ free_page((unsigned long)virt_st_tbl);
+ virt_st_tbl = (uint32_t *)virt_blk;
+ pte_n = 0;
+ break;
+ }
+ i++;
+ }
+ }
+}
+
+static void tmc_etr_sg_tbl_flush(uint32_t *vaddr, uint32_t size)
+{
+ uint32_t i = 0, pte_n = 0, last_pte;
+ uint32_t *virt_st_tbl, *virt_pte;
+ void *virt_blk;
+ phys_addr_t phys_pte;
+ int total_ents = DIV_ROUND_UP(size, PAGE_SIZE);
+ int ents_per_blk = PAGE_SIZE/sizeof(uint32_t);
+
+ virt_st_tbl = vaddr;
+ dmac_flush_range((void *)virt_st_tbl, (void *)virt_st_tbl + PAGE_SIZE);
+
+ while (i < total_ents) {
+ last_pte = ((i + ents_per_blk) > total_ents) ?
+ total_ents : (i + ents_per_blk);
+ while (i < last_pte) {
+ virt_pte = virt_st_tbl + pte_n;
+ phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte);
+ virt_blk = phys_to_virt(phys_pte);
+
+ dmac_flush_range(virt_blk, virt_blk + PAGE_SIZE);
+
+ if ((last_pte - i) > 1) {
+ pte_n++;
+ } else if (last_pte != total_ents) {
+ virt_st_tbl = (uint32_t *)virt_blk;
+ pte_n = 0;
+ break;
+ }
+ i++;
+ }
+ }
+}
+
+/*
+ * Scatter gather table layout in memory:
+ * 1. Table contains 32-bit entries
+ * 2. Each entry in the table points to 4K block of memory
+ * 3. Last entry in the table points to next table
+ * 4. (*) Based on mem_size requested, if there is no need for next level of
+ * table, last entry in the table points directly to 4K block of memory.
+ *
+ * sg_tbl_num=0
+ * |---------------|<-- drvdata->vaddr
+ * | blk_num=0 |
+ * |---------------|
+ * | blk_num=1 |
+ * |---------------|
+ * | blk_num=2 |
+ * |---------------| sg_tbl_num=1
+ * |(*)Nxt Tbl Addr|------>|---------------|
+ * |---------------| | blk_num=3 |
+ * |---------------|
+ * | blk_num=4 |
+ * |---------------|
+ * | blk_num=5 |
+ * |---------------| sg_tbl_num=2
+ * |(*)Nxt Tbl Addr|------>|---------------|
+ * |---------------| | blk_num=6 |
+ * |---------------|
+ * | blk_num=7 |
+ * |---------------|
+ * | blk_num=8 |
+ * |---------------|
+ * | |End of
+ * |---------------|-----
+ * Table
+ * For simplicity above diagram assumes following:
+ * a. mem_size = 36KB --> total_ents = 9
+ * b. ents_per_blk = 4
+ */
+
+static int tmc_etr_sg_tbl_alloc(struct tmc_drvdata *drvdata)
+{
+ int ret;
+ uint32_t i = 0, last_pte;
+ uint32_t *virt_pgdir, *virt_st_tbl;
+ void *virt_pte;
+ int total_ents = DIV_ROUND_UP(drvdata->size, PAGE_SIZE);
+ int ents_per_blk = PAGE_SIZE/sizeof(uint32_t);
+
+ virt_pgdir = (uint32_t *)get_zeroed_page(GFP_KERNEL);
+ if (!virt_pgdir)
+ return -ENOMEM;
+
+ virt_st_tbl = virt_pgdir;
+
+ while (i < total_ents) {
+ last_pte = ((i + ents_per_blk) > total_ents) ?
+ total_ents : (i + ents_per_blk);
+ while (i < last_pte) {
+ virt_pte = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!virt_pte) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if ((last_pte - i) > 1) {
+ *virt_st_tbl =
+ TMC_ETR_SG_ENT(virt_to_phys(virt_pte));
+ virt_st_tbl++;
+ } else if (last_pte == total_ents) {
+ *virt_st_tbl =
+ TMC_ETR_SG_LST_ENT(virt_to_phys(virt_pte));
+ } else {
+ *virt_st_tbl =
+ TMC_ETR_SG_NXT_TBL(virt_to_phys(virt_pte));
+ virt_st_tbl = (uint32_t *)virt_pte;
+ break;
+ }
+ i++;
+ }
+ }
+
+ drvdata->vaddr = virt_pgdir;
+ drvdata->paddr = virt_to_phys(virt_pgdir);
+
+ /* Flush the dcache before proceeding */
+ tmc_etr_sg_tbl_flush((uint32_t *)drvdata->vaddr, drvdata->size);
+
+ dev_dbg(drvdata->dev, "%s: table starts at %#lx, total entries %d\n",
+ __func__, (unsigned long)drvdata->paddr, total_ents);
+
+ return 0;
+err:
+ tmc_etr_sg_tbl_free(virt_pgdir, drvdata->size, i);
+ return ret;
+}
+
+static void tmc_etr_sg_mem_reset(uint32_t *vaddr, uint32_t size)
+{
+ uint32_t i = 0, pte_n = 0, last_pte;
+ uint32_t *virt_st_tbl, *virt_pte;
+ void *virt_blk;
+ phys_addr_t phys_pte;
+ int total_ents = DIV_ROUND_UP(size, PAGE_SIZE);
+ int ents_per_blk = PAGE_SIZE/sizeof(uint32_t);
+
+ virt_st_tbl = vaddr;
+
+ while (i < total_ents) {
+ last_pte = ((i + ents_per_blk) > total_ents) ?
+ total_ents : (i + ents_per_blk);
+ while (i < last_pte) {
+ virt_pte = virt_st_tbl + pte_n;
+ phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte);
+ virt_blk = phys_to_virt(phys_pte);
+
+ if ((last_pte - i) > 1) {
+ memset(virt_blk, 0, PAGE_SIZE);
+ pte_n++;
+ } else if (last_pte == total_ents) {
+ memset(virt_blk, 0, PAGE_SIZE);
+ } else {
+ virt_st_tbl = (uint32_t *)virt_blk;
+ pte_n = 0;
+ break;
+ }
+ i++;
+ }
+ }
+
+ /* Flush the dcache before proceeding */
+ tmc_etr_sg_tbl_flush(vaddr, size);
+}
+
static int tmc_etr_alloc_mem(struct tmc_drvdata *drvdata)
{
int ret;
if (!drvdata->vaddr) {
- drvdata->vaddr = dma_zalloc_coherent(drvdata->dev,
- drvdata->size,
- &drvdata->paddr,
- GFP_KERNEL);
- if (!drvdata->vaddr) {
- ret = -ENOMEM;
- goto err;
+ if (drvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG) {
+ drvdata->vaddr = dma_zalloc_coherent(drvdata->dev,
+ drvdata->size,
+ &drvdata->paddr,
+ GFP_KERNEL);
+ if (!drvdata->vaddr) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ } else {
+ ret = tmc_etr_sg_tbl_alloc(drvdata);
+ if (ret)
+ goto err;
}
}
/*
@@ -221,19 +457,36 @@ err:
static void tmc_etr_free_mem(struct tmc_drvdata *drvdata)
{
if (drvdata->vaddr) {
- dma_free_coherent(drvdata->dev, drvdata->size,
- drvdata->vaddr, drvdata->paddr);
+ if (drvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG)
+ dma_free_coherent(drvdata->dev, drvdata->size,
+ drvdata->vaddr, drvdata->paddr);
+ else
+ tmc_etr_sg_tbl_free((uint32_t *)drvdata->vaddr,
+ drvdata->size,
+ DIV_ROUND_UP(drvdata->size, PAGE_SIZE));
+
drvdata->vaddr = 0;
drvdata->paddr = 0;
}
}
+static void tmc_etr_mem_reset(struct tmc_drvdata *drvdata)
+{
+ if (drvdata->vaddr) {
+ if (drvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG)
+ memset(drvdata->vaddr, 0, drvdata->size);
+ else
+ tmc_etr_sg_mem_reset((uint32_t *)drvdata->vaddr,
+ drvdata->size);
+ }
+}
+
static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
{
u32 axictl;
/* Zero out the memory to help with debug */
- memset(drvdata->vaddr, 0, drvdata->size);
+ tmc_etr_mem_reset(drvdata);
CS_UNLOCK(drvdata->base);
@@ -243,7 +496,10 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
axictl = readl_relaxed(drvdata->base + TMC_AXICTL);
axictl |= TMC_AXICTL_WR_BURST_LEN;
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
- axictl &= ~TMC_AXICTL_SCT_GAT_MODE;
+ if (drvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG)
+ axictl &= ~TMC_AXICTL_SCT_GAT_MODE;
+ else
+ axictl |= TMC_AXICTL_SCT_GAT_MODE;
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
axictl = (axictl &
~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) |
@@ -292,9 +548,11 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
* enabling tmc; the new selection will be honored from
* next tmc enable session.
*/
- if (drvdata->size != drvdata->mem_size) {
+ if (drvdata->size != drvdata->mem_size ||
+ drvdata->memtype != drvdata->mem_type) {
tmc_etr_free_mem(drvdata);
drvdata->size = drvdata->mem_size;
+ drvdata->memtype = drvdata->mem_type;
}
ret = tmc_etr_alloc_mem(drvdata);
if (ret) {
@@ -385,6 +643,59 @@ static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
CS_LOCK(drvdata->base);
}
+static void tmc_etr_sg_rwp_pos(struct tmc_drvdata *drvdata, uint32_t rwp)
+{
+ uint32_t i = 0, pte_n = 0, last_pte;
+ uint32_t *virt_st_tbl, *virt_pte;
+ void *virt_blk;
+ bool found = false;
+ phys_addr_t phys_pte;
+ int total_ents = DIV_ROUND_UP(drvdata->size, PAGE_SIZE);
+ int ents_per_blk = PAGE_SIZE/sizeof(uint32_t);
+
+ virt_st_tbl = drvdata->vaddr;
+
+ while (i < total_ents) {
+ last_pte = ((i + ents_per_blk) > total_ents) ?
+ total_ents : (i + ents_per_blk);
+ while (i < last_pte) {
+ virt_pte = virt_st_tbl + pte_n;
+ phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte);
+
+ /*
+ * When the trace buffer is full; RWP could be on any
+ * 4K block from scatter gather table. Compute below -
+ * 1. Block number where RWP is currently residing
+ * 2. RWP position in that 4K block
+ * 3. Delta offset from current RWP position to end of
+ * block.
+ */
+ if (phys_pte <= rwp && rwp < (phys_pte + PAGE_SIZE)) {
+ virt_blk = phys_to_virt(phys_pte);
+ drvdata->sg_blk_num = i;
+ drvdata->buf = virt_blk + rwp - phys_pte;
+ drvdata->delta_bottom =
+ phys_pte + PAGE_SIZE - rwp;
+ found = true;
+ break;
+ }
+
+ if ((last_pte - i) > 1) {
+ pte_n++;
+ } else if (i < (total_ents - 1)) {
+ virt_blk = phys_to_virt(phys_pte);
+ virt_st_tbl = (uint32_t *)virt_blk;
+ pte_n = 0;
+ break;
+ }
+
+ i++;
+ }
+ if (found)
+ break;
+ }
+}
+
static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
{
u32 rwp, val;
@@ -392,11 +703,25 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
rwp = readl_relaxed(drvdata->base + TMC_RWP);
val = readl_relaxed(drvdata->base + TMC_STS);
- /* How much memory do we still have */
- if (val & BIT(0))
- drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
- else
- drvdata->buf = drvdata->vaddr;
+ if (drvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG) {
+ /* How much memory do we still have */
+ if (val & BIT(0))
+ drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
+ else
+ drvdata->buf = drvdata->vaddr;
+ } else {
+ /*
+ * Reset these variables before computing since we
+ * rely on their values during tmc read
+ */
+ drvdata->sg_blk_num = 0;
+ drvdata->delta_bottom = 0;
+
+ if (val & BIT(0))
+ tmc_etr_sg_rwp_pos(drvdata, rwp);
+ else
+ drvdata->buf = drvdata->vaddr;
+ }
}
static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
@@ -563,6 +888,128 @@ out:
return 0;
}
+/*
+ * TMC read logic when scatter gather feature is enabled:
+ *
+ * sg_tbl_num=0
+ * |---------------|<-- drvdata->vaddr
+ * | blk_num=0 |
+ * | blk_num_rel=5 |
+ * |---------------|
+ * | blk_num=1 |
+ * | blk_num_rel=6 |
+ * |---------------|
+ * | blk_num=2 |
+ * | blk_num_rel=7 |
+ * |---------------| sg_tbl_num=1
+ * | Next Table |------>|---------------|
+ * | Addr | | blk_num=3 |
+ * |---------------| | blk_num_rel=8 |
+ * |---------------|
+ * 4k Block Addr | blk_num=4 |
+ * |--------------| blk_num_rel=0 |
+ * | |---------------|
+ * | | blk_num=5 |
+ * | | blk_num_rel=1 |
+ * | |---------------| sg_tbl_num=2
+ * |---------------| | Next Table |------>|---------------|
+ * | | | Addr | | blk_num=6 |
+ * | | |---------------| | blk_num_rel=2 |
+ * | read_off | |---------------|
+ * | | | blk_num=7 |
+ * | | ppos | blk_num_rel=3 |
+ * |---------------|----- |---------------|
+ * | | | blk_num=8 |
+ * | delta_up | | blk_num_rel=4 |
+ * | | RWP/drvdata->buf |---------------|
+ * |---------------|----------------- | |
+ * | | | | |End of
+ * | | | |---------------|-----
+ * | | drvdata->delta_bottom Table
+ * | | |
+ * |_______________| _|_
+ * 4K Block
+ *
+ * For simplicity above diagram assumes following:
+ * a. mem_size = 36KB --> total_ents = 9
+ * b. ents_per_blk = 4
+ * c. RWP is on 5th block (blk_num = 5); so we have to start reading from RWP
+ * position
+ */
+
+static void tmc_etr_sg_compute_read(struct tmc_drvdata *drvdata, loff_t *ppos,
+ char **bufpp, size_t *len)
+{
+ uint32_t i = 0, blk_num_rel = 0, read_len = 0;
+ uint32_t blk_num, sg_tbl_num, blk_num_loc, read_off;
+ uint32_t *virt_pte, *virt_st_tbl;
+ void *virt_blk;
+ phys_addr_t phys_pte = 0;
+ int total_ents = DIV_ROUND_UP(drvdata->size, PAGE_SIZE);
+ int ents_per_blk = PAGE_SIZE/sizeof(uint32_t);
+
+ /*
+ * Find relative block number from ppos and reading offset
+ * within block and find actual block number based on relative
+ * block number
+ */
+ if (drvdata->buf == drvdata->vaddr) {
+ blk_num = *ppos / PAGE_SIZE;
+ read_off = *ppos % PAGE_SIZE;
+ } else {
+ if (*ppos < drvdata->delta_bottom) {
+ read_off = PAGE_SIZE - drvdata->delta_bottom;
+ } else {
+ blk_num_rel = (*ppos / PAGE_SIZE) + 1;
+ read_off = (*ppos - drvdata->delta_bottom) % PAGE_SIZE;
+ }
+
+ blk_num = (drvdata->sg_blk_num + blk_num_rel) % total_ents;
+ }
+
+ virt_st_tbl = (uint32_t *)drvdata->vaddr;
+
+ /* Compute table index and block entry index within that table */
+ if (blk_num && (blk_num == (total_ents - 1)) &&
+ !(blk_num % (ents_per_blk - 1))) {
+ sg_tbl_num = blk_num / ents_per_blk;
+ blk_num_loc = ents_per_blk - 1;
+ } else {
+ sg_tbl_num = blk_num / (ents_per_blk - 1);
+ blk_num_loc = blk_num % (ents_per_blk - 1);
+ }
+
+ for (i = 0; i < sg_tbl_num; i++) {
+ virt_pte = virt_st_tbl + (ents_per_blk - 1);
+ phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte);
+ virt_st_tbl = (uint32_t *)phys_to_virt(phys_pte);
+ }
+
+ virt_pte = virt_st_tbl + blk_num_loc;
+ phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte);
+ virt_blk = phys_to_virt(phys_pte);
+
+ *bufpp = virt_blk + read_off;
+
+ if (*len > (PAGE_SIZE - read_off))
+ *len = PAGE_SIZE - read_off;
+
+ /*
+ * When buffer is wrapped around and trying to read last relative
+ * block (i.e. delta_up), compute len differently
+ */
+ if (blk_num_rel && (blk_num == drvdata->sg_blk_num)) {
+ read_len = PAGE_SIZE - drvdata->delta_bottom - read_off;
+ if (*len > read_len)
+ *len = read_len;
+ }
+
+ dev_dbg_ratelimited(drvdata->dev,
+ "%s: read at %p, phys %pa len %zu blk %d, rel blk %d RWP blk %d\n",
+ __func__, *bufpp, &phys_pte, *len, blk_num, blk_num_rel,
+ drvdata->sg_blk_num);
+}
+
static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
loff_t *ppos)
{
@@ -574,12 +1021,18 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
len = drvdata->size - *ppos;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
- if (bufp == (char *)(drvdata->vaddr + drvdata->size))
- bufp = drvdata->vaddr;
- else if (bufp > (char *)(drvdata->vaddr + drvdata->size))
- bufp -= drvdata->size;
- if ((bufp + len) > (char *)(drvdata->vaddr + drvdata->size))
- len = (char *)(drvdata->vaddr + drvdata->size) - bufp;
+ if (drvdata->memtype == TMC_ETR_MEM_TYPE_CONTIG) {
+ if (bufp == (char *)(drvdata->vaddr + drvdata->size))
+ bufp = drvdata->vaddr;
+ else if (bufp >
+ (char *)(drvdata->vaddr + drvdata->size))
+ bufp -= drvdata->size;
+ if ((bufp + len) >
+ (char *)(drvdata->vaddr + drvdata->size))
+ len = (char *)(drvdata->vaddr + drvdata->size)
+ - bufp;
+ } else
+ tmc_etr_sg_compute_read(drvdata, ppos, &bufp, &len);
}
if (copy_to_user(data, bufp, len)) {
@@ -724,6 +1177,44 @@ static ssize_t mem_size_store(struct device *dev,
}
static DEVICE_ATTR_RW(mem_size);
+static ssize_t mem_type_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n",
+ str_tmc_etr_mem_type[drvdata->mem_type]);
+}
+
+static ssize_t mem_type_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ char str[10] = "";
+
+ if (strlen(buf) >= 10)
+ return -EINVAL;
+ if (sscanf(buf, "%s", str) != 1)
+ return -EINVAL;
+
+ mutex_lock(&drvdata->mem_lock);
+ if (!strcmp(str, str_tmc_etr_mem_type[TMC_ETR_MEM_TYPE_CONTIG])) {
+ drvdata->mem_type = TMC_ETR_MEM_TYPE_CONTIG;
+ } else if (!strcmp(str, str_tmc_etr_mem_type[TMC_ETR_MEM_TYPE_SG])) {
+ drvdata->mem_type = TMC_ETR_MEM_TYPE_SG;
+ } else {
+ mutex_unlock(&drvdata->mem_lock);
+ return -EINVAL;
+ }
+ mutex_unlock(&drvdata->mem_lock);
+
+ return size;
+}
+static DEVICE_ATTR_RW(mem_type);
+
static struct attribute *coresight_etb_attrs[] = {
&dev_attr_trigger_cntr.attr,
&dev_attr_status.attr,
@@ -733,6 +1224,7 @@ ATTRIBUTE_GROUPS(coresight_etb);
static struct attribute *coresight_etr_attrs[] = {
&dev_attr_mem_size.attr,
+ &dev_attr_mem_type.attr,
&dev_attr_trigger_cntr.attr,
&dev_attr_status.attr,
NULL,
@@ -794,6 +1286,8 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->size = SZ_1M;
drvdata->mem_size = drvdata->size;
+ drvdata->memtype = TMC_ETR_MEM_TYPE_CONTIG;
+ drvdata->mem_type = drvdata->memtype;
} else {
drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4;
}