If clk_get fail, assume the etb does not need a separate clock.
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/kernel/etm.c | 14 +++++++-------
1 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 11db628..308b733 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -362,12 +362,10 @@ static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
goto out_unmap;
t->emu_clk = clk_get(&dev->dev, "emu_src_ck");
- if (IS_ERR(t->emu_clk)) {
+ if (IS_ERR(t->emu_clk))
dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n");
- return -EFAULT;
- }
-
- clk_enable(t->emu_clk);
+ else
+ clk_enable(t->emu_clk);
etb_unlock(t);
t->etb_bufsz = etb_readl(t, ETBR_DEPTH);
@@ -402,8 +400,10 @@ static int etb_remove(struct amba_device *dev)
iounmap(t->etb_regs);
t->etb_regs = NULL;
- clk_disable(t->emu_clk);
- clk_put(t->emu_clk);
+ if (!IS_ERR(t->emu_clk)) {
+ clk_disable(t->emu_clk);
+ clk_put(t->emu_clk);
+ }
amba_release_regions(dev);
--
1.7.3.1
On some systems kernel code is considered secure, and this code
already limits tracing to the kernel text segment which results
in no trace data.
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/kernel/etm.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 308b733..2ea0b7c 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -55,7 +55,7 @@ static inline bool trace_isrunning(struct tracectx *t)
static int etm_setup_address_range(struct tracectx *t, int n,
unsigned long start, unsigned long end, int exclude, int data)
{
- u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \
+ u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY |
ETMAAT_NOVALCMP;
if (n < 1 || n > t->ncmppairs)
--
1.7.3.1
If the write address was at the end of the buffer, toggling the trace
capture bit would set the RAM-full status instead of clearing it.
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/kernel/etm.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 2ea0b7c..7652e9f 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -235,6 +235,7 @@ static void etm_dump(void)
printk(KERN_INFO "\n--- ETB buffer end ---\n");
/* deassert the overflow bit */
+ etb_writel(t, 0, ETBR_WRITEADDR);
etb_writel(t, 1, ETBR_CTRL);
etb_writel(t, 0, ETBR_CTRL);
@@ -300,6 +301,7 @@ static ssize_t etb_read(struct file *file, char __user *data,
buf[i] = etb_readl(t, ETBR_READMEM);
/* the only way to deassert overflow bit in ETB status is this */
+ etb_writel(t, 0, ETBR_WRITEADDR);
etb_writel(t, 1, ETBR_CTRL);
etb_writel(t, 0, ETBR_CTRL);
--
1.7.3.1
Trace kernel text segment by default as before, allow tracing of other
ranges by writing a range to /sys/devices/etm/trace_range, or to trace
everything by writing 0 0.
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/kernel/etm.c | 48 +++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 45 insertions(+), 3 deletions(-)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 7652e9f..6ff1004 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -40,12 +40,17 @@ struct tracectx {
unsigned long flags;
int ncmppairs;
int etm_portsz;
+ unsigned long range_start;
+ unsigned long range_end;
struct device *dev;
struct clk *emu_clk;
struct mutex mutex;
};
-static struct tracectx tracer;
+static struct tracectx tracer = {
+ .range_start = (unsigned long)_stext,
+ .range_end = (unsigned long)_etext,
+};
static inline bool trace_isrunning(struct tracectx *t)
{
@@ -114,8 +119,12 @@ static int trace_start(struct tracectx *t)
return -EFAULT;
}
- etm_setup_address_range(t, 1, (unsigned long)_stext,
- (unsigned long)_etext, 0, 0);
+ if (t->range_start || t->range_end)
+ etm_setup_address_range(t, 1,
+ t->range_start, t->range_end, 0, 0);
+ else
+ etm_writel(t, ETMTE_INCLEXCL, ETMR_TRACEENCTRL);
+
etm_writel(t, 0, ETMR_TRACEENCTRL2);
etm_writel(t, 0, ETMR_TRACESSCTRL);
etm_writel(t, 0x6f, ETMR_TRACEENEVT);
@@ -532,6 +541,35 @@ static ssize_t trace_mode_store(struct kobject *kobj,
static struct kobj_attribute trace_mode_attr =
__ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store);
+static ssize_t trace_range_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%08lx %08lx\n",
+ tracer.range_start, tracer.range_end);
+}
+
+static ssize_t trace_range_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned long range_start, range_end;
+
+ if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2)
+ return -EINVAL;
+
+ mutex_lock(&tracer.mutex);
+ tracer.range_start = range_start;
+ tracer.range_end = range_end;
+ mutex_unlock(&tracer.mutex);
+
+ return n;
+}
+
+
+static struct kobj_attribute trace_range_attr =
+ __ATTR(trace_range, 0644, trace_range_show, trace_range_store);
+
static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
{
struct tracectx *t = &tracer;
@@ -583,6 +621,10 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
if (ret)
dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n");
+ ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr);
+ if (ret)
+ dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n");
+
dev_dbg(t->dev, "ETM AMBA driver initialized.\n");
out:
--
1.7.3.1
The old code enabled data tracing, but did not configure the
range. We now configure it to trace all data addresses by default,
and add a trace_data_range attribute to change the range or disable
data tracing.
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/include/asm/hardware/coresight.h | 10 +++-
arch/arm/kernel/etm.c | 76 +++++++++++++++++++++++++++-
2 files changed, 82 insertions(+), 4 deletions(-)
diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h
index 7ecd793..b95a6f0 100644
--- a/arch/arm/include/asm/hardware/coresight.h
+++ b/arch/arm/include/asm/hardware/coresight.h
@@ -17,9 +17,11 @@
#define TRACER_ACCESSED_BIT 0
#define TRACER_RUNNING_BIT 1
#define TRACER_CYCLE_ACC_BIT 2
+#define TRACER_TRACE_DATA_BIT 3
#define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT)
#define TRACER_RUNNING BIT(TRACER_RUNNING_BIT)
#define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT)
+#define TRACER_TRACE_DATA BIT(TRACER_TRACE_DATA_BIT)
#define TRACER_TIMEOUT 10000
@@ -113,8 +115,14 @@
#define ETMR_TRACEENCTRL 0x24
#define ETMTE_INCLEXCL BIT(24)
#define ETMR_TRACEENEVT 0x20
+
+#define ETMR_VIEWDATAEVT 0x30
+#define ETMR_VIEWDATACTRL1 0x34
+#define ETMR_VIEWDATACTRL2 0x38
+#define ETMR_VIEWDATACTRL3 0x3c
+#define ETMVDC3_EXCLONLY (1 << 16)
+
#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT | \
- ETMCTRL_DATA_DO_ADDR | \
ETMCTRL_BRANCH_OUTPUT | \
ETMCTRL_DO_CONTEXTID)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 6ff1004..ccd6d12 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -42,6 +42,8 @@ struct tracectx {
int etm_portsz;
unsigned long range_start;
unsigned long range_end;
+ unsigned long data_range_start;
+ unsigned long data_range_end;
struct device *dev;
struct clk *emu_clk;
struct mutex mutex;
@@ -83,8 +85,15 @@ static int etm_setup_address_range(struct tracectx *t, int n,
etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1));
etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1));
- flags = exclude ? ETMTE_INCLEXCL : 0;
- etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL);
+ if (data) {
+ flags = exclude ? ETMVDC3_EXCLONLY : 0;
+ if (exclude)
+ n += 8;
+ etm_writel(t, flags | BIT(n), ETMR_VIEWDATACTRL3);
+ } else {
+ flags = exclude ? ETMTE_INCLEXCL : 0;
+ etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL);
+ }
return 0;
}
@@ -107,6 +116,9 @@ static int trace_start(struct tracectx *t)
if (t->flags & TRACER_CYCLE_ACC)
v |= ETMCTRL_CYCLEACCURATE;
+ if (t->flags & TRACER_TRACE_DATA)
+ v |= ETMCTRL_DATA_DO_ADDR;
+
etm_unlock(t);
etm_writel(t, v, ETMR_CTRL);
@@ -129,6 +141,17 @@ static int trace_start(struct tracectx *t)
etm_writel(t, 0, ETMR_TRACESSCTRL);
etm_writel(t, 0x6f, ETMR_TRACEENEVT);
+ etm_writel(t, 0, ETMR_VIEWDATACTRL1);
+ etm_writel(t, 0, ETMR_VIEWDATACTRL2);
+
+ if (t->data_range_start || t->data_range_end)
+ etm_setup_address_range(t, 2, t->data_range_start,
+ t->data_range_end, 0, 1);
+ else
+ etm_writel(t, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3);
+
+ etm_writel(t, 0x6f, ETMR_VIEWDATAEVT);
+
v &= ~ETMCTRL_PROGRAM;
v |= ETMCTRL_PORTSEL;
@@ -570,6 +593,48 @@ static ssize_t trace_range_store(struct kobject *kobj,
static struct kobj_attribute trace_range_attr =
__ATTR(trace_range, 0644, trace_range_show, trace_range_store);
+static ssize_t trace_data_range_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ unsigned long range_start;
+ u64 range_end;
+ mutex_lock(&tracer.mutex);
+ range_start = tracer.data_range_start;
+ range_end = tracer.data_range_end;
+ if (!range_end && (tracer.flags & TRACER_TRACE_DATA))
+ range_end = 0x100000000ULL;
+ mutex_unlock(&tracer.mutex);
+ return sprintf(buf, "%08lx %08llx\n", range_start, range_end);
+}
+
+static ssize_t trace_data_range_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned long range_start;
+ u64 range_end;
+
+ if (sscanf(buf, "%lx %llx", &range_start, &range_end) != 2)
+ return -EINVAL;
+
+ mutex_lock(&tracer.mutex);
+ tracer.data_range_start = range_start;
+ tracer.data_range_end = (unsigned long)range_end;
+ if (range_end)
+ tracer.flags |= TRACER_TRACE_DATA;
+ else
+ tracer.flags &= ~TRACER_TRACE_DATA;
+ mutex_unlock(&tracer.mutex);
+
+ return n;
+}
+
+
+static struct kobj_attribute trace_data_range_attr =
+ __ATTR(trace_data_range, 0644,
+ trace_data_range_show, trace_data_range_store);
+
static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
{
struct tracectx *t = &tracer;
@@ -595,7 +660,7 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
mutex_init(&t->mutex);
t->dev = &dev->dev;
- t->flags = TRACER_CYCLE_ACC;
+ t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA;
t->etm_portsz = 1;
etm_unlock(t);
@@ -625,6 +690,11 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
if (ret)
dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n");
+ ret = sysfs_create_file(&dev->dev.kobj, &trace_data_range_attr.attr);
+ if (ret)
+ dev_dbg(&dev->dev,
+ "Failed to create trace_data_range in sysfs\n");
+
dev_dbg(t->dev, "ETM AMBA driver initialized.\n");
out:
--
1.7.3.1
On Wed, Feb 02, 2011 at 06:54:19PM -0800, Arve Hj??nnev??g wrote:
> If clk_get fail, assume the etb does not need a separate clock.
>
> Signed-off-by: Arve Hj??nnev??g <[email protected]>
Would it not be cleaner for the affected platforms to ensure that
clk_get() does the right thing here, for example by returning a dummy
clock? Otherwise we'll just silently carry on if we can't get a clock
we were supposed to which doesn't seem ideal.
On Thu, Feb 3, 2011 at 4:45 AM, Mark Brown
<[email protected]> wrote:
> On Wed, Feb 02, 2011 at 06:54:19PM -0800, Arve Hj??nnev??g wrote:
>> If clk_get fail, assume the etb does not need a separate clock.
>>
>> Signed-off-by: Arve Hj??nnev??g <[email protected]>
>
> Would it not be cleaner for the affected platforms to ensure that
> clk_get() does the right thing here, for example by returning a dummy
> clock? ?Otherwise we'll just silently carry on if we can't get a clock
> we were supposed to which doesn't seem ideal.
>
This clock seem to be an omap specific virtual clock that switches the
clock source of the etb. It is not used to enable the clock when the
etb is in use, and it does not seem to have a failure case other than
not existing. So, I don't know that requiring this clock would cause
fewer problems than making it optional.
--
Arve Hj?nnev?g
On Thu, Feb 03, 2011 at 04:30:46PM -0800, Arve Hj?nnev?g wrote:
> On Thu, Feb 3, 2011 at 4:45 AM, Mark Brown
> > Would it not be cleaner for the affected platforms to ensure that
> > clk_get() does the right thing here, for example by returning a dummy
> > clock? ?Otherwise we'll just silently carry on if we can't get a clock
> > we were supposed to which doesn't seem ideal.
> This clock seem to be an omap specific virtual clock that switches the
> clock source of the etb. It is not used to enable the clock when the
> etb is in use, and it does not seem to have a failure case other than
> not existing. So, I don't know that requiring this clock would cause
> fewer problems than making it optional.
That doesn't seem unreasonable, but in that case it'd be good to explain
why things are this way in the code (and the changelog) so someone
doesn't change the code back.
If clk_get fail, assume the etb does not need a separate clock.
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/kernel/etm.c | 15 ++++++++-------
1 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 11db628..d4ec368 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -361,13 +361,12 @@ static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
if (ret)
goto out_unmap;
+ /* Get optional clock. Currently used to select clock source on omap3 */
t->emu_clk = clk_get(&dev->dev, "emu_src_ck");
- if (IS_ERR(t->emu_clk)) {
+ if (IS_ERR(t->emu_clk))
dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n");
- return -EFAULT;
- }
-
- clk_enable(t->emu_clk);
+ else
+ clk_enable(t->emu_clk);
etb_unlock(t);
t->etb_bufsz = etb_readl(t, ETBR_DEPTH);
@@ -402,8 +401,10 @@ static int etb_remove(struct amba_device *dev)
iounmap(t->etb_regs);
t->etb_regs = NULL;
- clk_disable(t->emu_clk);
- clk_put(t->emu_clk);
+ if (!IS_ERR(t->emu_clk)) {
+ clk_disable(t->emu_clk);
+ clk_put(t->emu_clk);
+ }
amba_release_regions(dev);
--
1.7.3.1
On some systems kernel code is considered secure, and this code
already limits tracing to the kernel text segment which results
in no trace data.
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/kernel/etm.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index d4ec368..6e15c48 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -55,7 +55,7 @@ static inline bool trace_isrunning(struct tracectx *t)
static int etm_setup_address_range(struct tracectx *t, int n,
unsigned long start, unsigned long end, int exclude, int data)
{
- u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \
+ u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY |
ETMAAT_NOVALCMP;
if (n < 1 || n > t->ncmppairs)
--
1.7.3.1
Trace kernel text segment by default as before, allow tracing of other
ranges by writing a range to /sys/devices/etm/trace_range, or to trace
everything by writing 0 0.
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/kernel/etm.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 46 insertions(+), 3 deletions(-)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index bc7d8f2..8a1c422 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -40,12 +40,17 @@ struct tracectx {
unsigned long flags;
int ncmppairs;
int etm_portsz;
+ unsigned long range_start;
+ unsigned long range_end;
struct device *dev;
struct clk *emu_clk;
struct mutex mutex;
};
-static struct tracectx tracer;
+static struct tracectx tracer = {
+ .range_start = (unsigned long)_stext,
+ .range_end = (unsigned long)_etext,
+};
static inline bool trace_isrunning(struct tracectx *t)
{
@@ -115,8 +120,12 @@ static int trace_start(struct tracectx *t)
return -EFAULT;
}
- etm_setup_address_range(t, 1, (unsigned long)_stext,
- (unsigned long)_etext, 0, 0);
+ if (t->range_start || t->range_end)
+ etm_setup_address_range(t, 1,
+ t->range_start, t->range_end, 0, 0);
+ else
+ etm_writel(t, ETMTE_INCLEXCL, ETMR_TRACEENCTRL);
+
etm_writel(t, 0, ETMR_TRACEENCTRL2);
etm_writel(t, 0, ETMR_TRACESSCTRL);
etm_writel(t, 0x6f, ETMR_TRACEENEVT);
@@ -525,6 +534,35 @@ static ssize_t trace_mode_store(struct kobject *kobj,
static struct kobj_attribute trace_mode_attr =
__ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store);
+static ssize_t trace_range_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%08lx %08lx\n",
+ tracer.range_start, tracer.range_end);
+}
+
+static ssize_t trace_range_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned long range_start, range_end;
+
+ if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2)
+ return -EINVAL;
+
+ mutex_lock(&tracer.mutex);
+ tracer.range_start = range_start;
+ tracer.range_end = range_end;
+ mutex_unlock(&tracer.mutex);
+
+ return n;
+}
+
+
+static struct kobj_attribute trace_range_attr =
+ __ATTR(trace_range, 0644, trace_range_show, trace_range_store);
+
static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
{
struct tracectx *t = &tracer;
@@ -576,6 +614,10 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
if (ret)
dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n");
+ ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr);
+ if (ret)
+ dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n");
+
dev_dbg(t->dev, "ETM AMBA driver initialized.\n");
out:
@@ -605,6 +647,7 @@ static int etm_remove(struct amba_device *dev)
sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr);
sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr);
sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr);
+ sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr);
return 0;
}
--
1.7.3.1
On some SOCs the read and write pointer are reset when the chip
resets, but the trace buffer content is preserved. If the status
bits indicates that the buffer is empty and we have never started
tracing, assume the buffer is full instead. This can be useful
if the system rebooted from a watchdog reset.
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/kernel/etm.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 8126beb..4ad1257 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -44,6 +44,7 @@ struct tracectx {
unsigned long range_end;
unsigned long data_range_start;
unsigned long data_range_end;
+ bool dump_initial_etb;
struct device *dev;
struct clk *emu_clk;
struct mutex mutex;
@@ -105,6 +106,7 @@ static int trace_start(struct tracectx *t)
etb_unlock(t);
+ t->dump_initial_etb = false;
etb_writel(t, 0, ETBR_WRITEADDR);
etb_writel(t, 0, ETBR_FORMATTERCTRL);
etb_writel(t, 1, ETBR_CTRL);
@@ -311,6 +313,8 @@ static ssize_t etb_read(struct file *file, char __user *data,
etb_unlock(t);
total = etb_getdatalen(t);
+ if (total == 0 && t->dump_initial_etb)
+ total = t->etb_bufsz;
if (total == t->etb_bufsz)
first = etb_readl(t, ETBR_WRITEADDR);
@@ -385,6 +389,7 @@ static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
goto out_release;
}
+ t->dump_initial_etb = true;
amba_set_drvdata(dev, t);
etb_unlock(t);
--
1.7.3.1
If more than one ETM or PTM are present, configure all of them
and enable the formatter in the ETB. This allows tracing on dual
core systems (e.g. omap4).
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/include/asm/hardware/coresight.h | 16 ++-
arch/arm/kernel/etm.c | 234 +++++++++++++++++++----------
2 files changed, 166 insertions(+), 84 deletions(-)
diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h
index 6ea507f..6643d6c 100644
--- a/arch/arm/include/asm/hardware/coresight.h
+++ b/arch/arm/include/asm/hardware/coresight.h
@@ -25,9 +25,9 @@
#define TRACER_TIMEOUT 10000
-#define etm_writel(t, v, x) \
- (__raw_writel((v), (t)->etm_regs + (x)))
-#define etm_readl(t, x) (__raw_readl((t)->etm_regs + (x)))
+#define etm_writel(t, id, v, x) \
+ (__raw_writel((v), (t)->etm_regs[(id)] + (x)))
+#define etm_readl(t, id, x) (__raw_readl((t)->etm_regs[(id)] + (x)))
/* CoreSight Management Registers */
#define CSMR_LOCKACCESS 0xfb0
@@ -126,6 +126,8 @@
ETMCTRL_BRANCH_OUTPUT | \
ETMCTRL_DO_CONTEXTID)
+#define ETMR_TRACEIDR 0x200
+
/* ETM management registers, "ETM Architecture", 3.5.24 */
#define ETMMR_OSLAR 0x300
#define ETMMR_OSLSR 0x304
@@ -148,14 +150,16 @@
#define ETBFF_TRIGIN BIT(8)
#define ETBFF_TRIGEVT BIT(9)
#define ETBFF_TRIGFL BIT(10)
+#define ETBFF_STOPFL BIT(12)
#define etb_writel(t, v, x) \
(__raw_writel((v), (t)->etb_regs + (x)))
#define etb_readl(t, x) (__raw_readl((t)->etb_regs + (x)))
-#define etm_lock(t) do { etm_writel((t), 0, CSMR_LOCKACCESS); } while (0)
-#define etm_unlock(t) \
- do { etm_writel((t), UNLOCK_MAGIC, CSMR_LOCKACCESS); } while (0)
+#define etm_lock(t, id) \
+ do { etm_writel((t), (id), 0, CSMR_LOCKACCESS); } while (0)
+#define etm_unlock(t, id) \
+ do { etm_writel((t), (id), UNLOCK_MAGIC, CSMR_LOCKACCESS); } while (0)
#define etb_lock(t) do { etb_writel((t), 0, CSMR_LOCKACCESS); } while (0)
#define etb_unlock(t) \
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 4ad1257..48de5a4 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/io.h>
+#include <linux/slab.h>
#include <linux/sysrq.h>
#include <linux/device.h>
#include <linux/clk.h>
@@ -36,10 +37,12 @@ MODULE_AUTHOR("Alexander Shishkin");
struct tracectx {
unsigned int etb_bufsz;
void __iomem *etb_regs;
- void __iomem *etm_regs;
+ void __iomem **etm_regs;
+ int etm_regs_count;
unsigned long flags;
int ncmppairs;
int etm_portsz;
+ u32 etb_fc;
unsigned long range_start;
unsigned long range_end;
unsigned long data_range_start;
@@ -60,7 +63,7 @@ static inline bool trace_isrunning(struct tracectx *t)
return !!(t->flags & TRACER_RUNNING);
}
-static int etm_setup_address_range(struct tracectx *t, int n,
+static int etm_setup_address_range(struct tracectx *t, int id, int n,
unsigned long start, unsigned long end, int exclude, int data)
{
u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY |
@@ -79,41 +82,31 @@ static int etm_setup_address_range(struct tracectx *t, int n,
flags |= ETMAAT_IEXEC;
/* first comparator for the range */
- etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2));
- etm_writel(t, start, ETMR_COMP_VAL(n * 2));
+ etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2));
+ etm_writel(t, id, start, ETMR_COMP_VAL(n * 2));
/* second comparator is right next to it */
- etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1));
- etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1));
+ etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1));
+ etm_writel(t, id, end, ETMR_COMP_VAL(n * 2 + 1));
if (data) {
flags = exclude ? ETMVDC3_EXCLONLY : 0;
if (exclude)
n += 8;
- etm_writel(t, flags | BIT(n), ETMR_VIEWDATACTRL3);
+ etm_writel(t, id, flags | BIT(n), ETMR_VIEWDATACTRL3);
} else {
flags = exclude ? ETMTE_INCLEXCL : 0;
- etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL);
+ etm_writel(t, id, flags | (1 << n), ETMR_TRACEENCTRL);
}
return 0;
}
-static int trace_start(struct tracectx *t)
+static int trace_start_etm(struct tracectx *t, int id)
{
u32 v;
unsigned long timeout = TRACER_TIMEOUT;
- etb_unlock(t);
-
- t->dump_initial_etb = false;
- etb_writel(t, 0, ETBR_WRITEADDR);
- etb_writel(t, 0, ETBR_FORMATTERCTRL);
- etb_writel(t, 1, ETBR_CTRL);
-
- etb_lock(t);
-
- /* configure etm */
v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz);
if (t->flags & TRACER_CYCLE_ACC)
@@ -122,79 +115,122 @@ static int trace_start(struct tracectx *t)
if (t->flags & TRACER_TRACE_DATA)
v |= ETMCTRL_DATA_DO_ADDR;
- etm_unlock(t);
+ etm_unlock(t, id);
- etm_writel(t, v, ETMR_CTRL);
+ etm_writel(t, id, v, ETMR_CTRL);
- while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
+ while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
;
if (!timeout) {
dev_dbg(t->dev, "Waiting for progbit to assert timed out\n");
- etm_lock(t);
+ etm_lock(t, id);
return -EFAULT;
}
if (t->range_start || t->range_end)
- etm_setup_address_range(t, 1,
+ etm_setup_address_range(t, id, 1,
t->range_start, t->range_end, 0, 0);
else
- etm_writel(t, ETMTE_INCLEXCL, ETMR_TRACEENCTRL);
+ etm_writel(t, id, ETMTE_INCLEXCL, ETMR_TRACEENCTRL);
- etm_writel(t, 0, ETMR_TRACEENCTRL2);
- etm_writel(t, 0, ETMR_TRACESSCTRL);
- etm_writel(t, 0x6f, ETMR_TRACEENEVT);
+ etm_writel(t, id, 0, ETMR_TRACEENCTRL2);
+ etm_writel(t, id, 0, ETMR_TRACESSCTRL);
+ etm_writel(t, id, 0x6f, ETMR_TRACEENEVT);
- etm_writel(t, 0, ETMR_VIEWDATACTRL1);
- etm_writel(t, 0, ETMR_VIEWDATACTRL2);
+ etm_writel(t, id, 0, ETMR_VIEWDATACTRL1);
+ etm_writel(t, id, 0, ETMR_VIEWDATACTRL2);
if (t->data_range_start || t->data_range_end)
- etm_setup_address_range(t, 2, t->data_range_start,
+ etm_setup_address_range(t, id, 2, t->data_range_start,
t->data_range_end, 0, 1);
else
- etm_writel(t, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3);
+ etm_writel(t, id, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3);
- etm_writel(t, 0x6f, ETMR_VIEWDATAEVT);
+ etm_writel(t, id, 0x6f, ETMR_VIEWDATAEVT);
v &= ~ETMCTRL_PROGRAM;
v |= ETMCTRL_PORTSEL;
- etm_writel(t, v, ETMR_CTRL);
+ etm_writel(t, id, v, ETMR_CTRL);
timeout = TRACER_TIMEOUT;
- while (etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout)
+ while (etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout)
;
if (!timeout) {
dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n");
- etm_lock(t);
+ etm_lock(t, id);
return -EFAULT;
}
- etm_lock(t);
+ etm_lock(t, id);
+ return 0;
+}
+
+static int trace_start(struct tracectx *t)
+{
+ int ret;
+ int id;
+ u32 etb_fc = t->etb_fc;
+
+ etb_unlock(t);
+
+ t->dump_initial_etb = false;
+ etb_writel(t, 0, ETBR_WRITEADDR);
+ etb_writel(t, etb_fc, ETBR_FORMATTERCTRL);
+ etb_writel(t, 1, ETBR_CTRL);
+
+ etb_lock(t);
+
+ /* configure etm(s) */
+ for (id = 0; id < t->etm_regs_count; id++) {
+ ret = trace_start_etm(t, id);
+ if (ret)
+ return ret;
+ }
t->flags |= TRACER_RUNNING;
return 0;
}
-static int trace_stop(struct tracectx *t)
+static int trace_stop_etm(struct tracectx *t, int id)
{
unsigned long timeout = TRACER_TIMEOUT;
- etm_unlock(t);
+ etm_unlock(t, id);
- etm_writel(t, 0x440, ETMR_CTRL);
- while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
+ etm_writel(t, id, 0x440, ETMR_CTRL);
+ while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
;
if (!timeout) {
dev_dbg(t->dev, "Waiting for progbit to assert timed out\n");
- etm_lock(t);
+ etm_lock(t, id);
return -EFAULT;
}
- etm_lock(t);
+ etm_lock(t, id);
+ return 0;
+}
+
+static int trace_stop(struct tracectx *t)
+{
+ int id;
+ int ret;
+ unsigned long timeout = TRACER_TIMEOUT;
+ u32 etb_fc = t->etb_fc;
+
+ for (id = 0; id < t->etm_regs_count; id++) {
+ ret = trace_stop_etm(t, id);
+ if (ret)
+ return ret;
+ }
etb_unlock(t);
- etb_writel(t, ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL);
+ if (etb_fc) {
+ etb_fc |= ETBFF_STOPFL;
+ etb_writel(t, t->etb_fc, ETBR_FORMATTERCTRL);
+ }
+ etb_writel(t, etb_fc | ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL);
timeout = TRACER_TIMEOUT;
while (etb_readl(t, ETBR_FORMATTERCTRL) &
@@ -389,6 +425,7 @@ static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
goto out_release;
}
+ t->dev = &dev->dev;
t->dump_initial_etb = true;
amba_set_drvdata(dev, t);
@@ -507,6 +544,8 @@ static ssize_t trace_info_show(struct kobject *kobj,
{
u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st;
int datalen;
+ int id;
+ int ret;
mutex_lock(&tracer.mutex);
if (tracer.etb_regs) {
@@ -522,28 +561,33 @@ static ssize_t trace_info_show(struct kobject *kobj,
datalen = -1;
}
- etm_unlock(&tracer);
- etm_ctrl = etm_readl(&tracer, ETMR_CTRL);
- etm_st = etm_readl(&tracer, ETMR_STATUS);
- etm_lock(&tracer);
- mutex_unlock(&tracer.mutex);
-
- return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n"
+ ret = sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n"
"ETBR_WRITEADDR:\t%08x\n"
"ETBR_READADDR:\t%08x\n"
"ETBR_STATUS:\t%08x\n"
- "ETBR_FORMATTERCTRL:\t%08x\n"
- "ETMR_CTRL:\t%08x\n"
- "ETMR_STATUS:\t%08x\n",
+ "ETBR_FORMATTERCTRL:\t%08x\n",
datalen,
tracer.ncmppairs,
etb_wa,
etb_ra,
etb_st,
- etb_fc,
+ etb_fc
+ );
+
+ for (id = 0; id < tracer.etm_regs_count; id++) {
+ etm_unlock(&tracer, id);
+ etm_ctrl = etm_readl(&tracer, id, ETMR_CTRL);
+ etm_st = etm_readl(&tracer, id, ETMR_STATUS);
+ etm_lock(&tracer, id);
+ ret += sprintf(buf + ret, "ETMR_CTRL:\t%08x\n"
+ "ETMR_STATUS:\t%08x\n",
etm_ctrl,
etm_st
);
+ }
+ mutex_unlock(&tracer.mutex);
+
+ return ret;
}
static struct kobj_attribute trace_info_attr =
@@ -657,37 +701,46 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
{
struct tracectx *t = &tracer;
int ret = 0;
+ void __iomem **new_regs;
+ int new_count;
- if (t->etm_regs) {
- dev_dbg(&dev->dev, "ETM already initialized\n");
- ret = -EBUSY;
+ mutex_lock(&t->mutex);
+ new_count = t->etm_regs_count + 1;
+ new_regs = krealloc(t->etm_regs,
+ sizeof(t->etm_regs[0]) * new_count, GFP_KERNEL);
+
+ if (!new_regs) {
+ dev_dbg(&dev->dev, "Failed to allocate ETM register array\n");
+ ret = -ENOMEM;
goto out;
}
+ t->etm_regs = new_regs;
ret = amba_request_regions(dev, NULL);
if (ret)
goto out;
- t->etm_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res));
- if (!t->etm_regs) {
+ t->etm_regs[t->etm_regs_count] =
+ ioremap_nocache(dev->res.start, resource_size(&dev->res));
+ if (!t->etm_regs[t->etm_regs_count]) {
ret = -ENOMEM;
goto out_release;
}
- amba_set_drvdata(dev, t);
+ amba_set_drvdata(dev, t->etm_regs[t->etm_regs_count]);
- t->dev = &dev->dev;
t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA;
t->etm_portsz = 1;
- etm_unlock(t);
- (void)etm_readl(t, ETMMR_PDSR);
+ etm_unlock(t, t->etm_regs_count);
+ (void)etm_readl(t, t->etm_regs_count, ETMMR_PDSR);
/* dummy first read */
- (void)etm_readl(&tracer, ETMMR_OSSRR);
+ (void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR);
- t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf;
- etm_writel(t, 0x440, ETMR_CTRL);
- etm_lock(t);
+ t->ncmppairs = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE) & 0xf;
+ etm_writel(t, t->etm_regs_count, 0x440, ETMR_CTRL);
+ etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR);
+ etm_lock(t, t->etm_regs_count);
ret = sysfs_create_file(&dev->dev.kobj,
&trace_running_attr.attr);
@@ -712,31 +765,34 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
dev_dbg(&dev->dev,
"Failed to create trace_data_range in sysfs\n");
- dev_dbg(t->dev, "ETM AMBA driver initialized.\n");
+ dev_dbg(&dev->dev, "ETM AMBA driver initialized.\n");
+
+ /* Enable formatter if there are multiple trace sources */
+ if (new_count > 1)
+ t->etb_fc = ETBFF_ENFCONT | ETBFF_ENFTC;
+
+ t->etm_regs_count = new_count;
out:
+ mutex_unlock(&t->mutex);
return ret;
out_unmap:
amba_set_drvdata(dev, NULL);
- iounmap(t->etm_regs);
+ iounmap(t->etm_regs[t->etm_regs_count]);
out_release:
amba_release_regions(dev);
+ mutex_unlock(&t->mutex);
return ret;
}
static int etm_remove(struct amba_device *dev)
{
- struct tracectx *t = amba_get_drvdata(dev);
-
- amba_set_drvdata(dev, NULL);
-
- iounmap(t->etm_regs);
- t->etm_regs = NULL;
-
- amba_release_regions(dev);
+ int i;
+ struct tracectx *t = &tracer;
+ void __iomem *etm_regs = amba_get_drvdata(dev);
sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr);
sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr);
@@ -744,6 +800,24 @@ static int etm_remove(struct amba_device *dev)
sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr);
sysfs_remove_file(&dev->dev.kobj, &trace_data_range_attr.attr);
+ amba_set_drvdata(dev, NULL);
+
+ mutex_lock(&t->mutex);
+ for (i = 0; i < t->etm_regs_count; i++)
+ if (t->etm_regs[i] == etm_regs)
+ break;
+ for (; i < t->etm_regs_count - 1; i++)
+ t->etm_regs[i] = t->etm_regs[i + 1];
+ t->etm_regs_count--;
+ if (!t->etm_regs_count) {
+ kfree(t->etm_regs);
+ t->etm_regs = NULL;
+ }
+ mutex_unlock(&t->mutex);
+
+ iounmap(etm_regs);
+ amba_release_regions(dev);
+
return 0;
}
@@ -752,6 +826,10 @@ static struct amba_id etm_ids[] = {
.id = 0x0003b921,
.mask = 0x0007ffff,
},
+ {
+ .id = 0x0003b950,
+ .mask = 0x0007ffff,
+ },
{ 0, 0 },
};
--
1.7.3.1
If the write address was at the end of the buffer, toggling the trace
capture bit would set the RAM-full status instead of clearing it, and
if any of the stop bits in the formatter is set toggling the trace
capture bit may not do anything.
Instead use the read position to find out if the data has already
been returned.
This also fixes the read function so it works when the trace buffer is
larger than the buffer passed in from user space. The old version
would reset the trace buffer pointers after every read, so the second
call to read would always return 0.
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/kernel/etm.c | 58 ++++++++++++++++++++++---------------------------
1 files changed, 26 insertions(+), 32 deletions(-)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 6e15c48..bc7d8f2 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -91,6 +91,7 @@ static int trace_start(struct tracectx *t)
etb_unlock(t);
+ etb_writel(t, 0, ETBR_WRITEADDR);
etb_writel(t, 0, ETBR_FORMATTERCTRL);
etb_writel(t, 1, ETBR_CTRL);
@@ -184,24 +185,15 @@ static int trace_stop(struct tracectx *t)
static int etb_getdatalen(struct tracectx *t)
{
u32 v;
- int rp, wp;
+ int wp;
v = etb_readl(t, ETBR_STATUS);
if (v & 1)
return t->etb_bufsz;
- rp = etb_readl(t, ETBR_READADDR);
wp = etb_readl(t, ETBR_WRITEADDR);
-
- if (rp > wp) {
- etb_writel(t, 0, ETBR_READADDR);
- etb_writel(t, 0, ETBR_WRITEADDR);
-
- return 0;
- }
-
- return wp - rp;
+ return wp;
}
/* sysrq+v will always stop the running trace and leave it at that */
@@ -234,14 +226,6 @@ static void etm_dump(void)
printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM)));
printk(KERN_INFO "\n--- ETB buffer end ---\n");
- /* deassert the overflow bit */
- etb_writel(t, 1, ETBR_CTRL);
- etb_writel(t, 0, ETBR_CTRL);
-
- etb_writel(t, 0, ETBR_TRIGGERCOUNT);
- etb_writel(t, 0, ETBR_READADDR);
- etb_writel(t, 0, ETBR_WRITEADDR);
-
etb_lock(t);
}
@@ -275,6 +259,10 @@ static ssize_t etb_read(struct file *file, char __user *data,
struct tracectx *t = file->private_data;
u32 first = 0;
u32 *buf;
+ int wpos;
+ int skip;
+ long wlength;
+ loff_t pos = *ppos;
mutex_lock(&t->mutex);
@@ -289,28 +277,34 @@ static ssize_t etb_read(struct file *file, char __user *data,
if (total == t->etb_bufsz)
first = etb_readl(t, ETBR_WRITEADDR);
+ if (pos > total * 4) {
+ skip = 0;
+ wpos = total;
+ } else {
+ skip = (int)pos % 4;
+ wpos = (int)pos / 4;
+ }
+ total -= wpos;
+ first = (first + wpos) % t->etb_bufsz;
+
etb_writel(t, first, ETBR_READADDR);
- length = min(total * 4, (int)len);
- buf = vmalloc(length);
+ wlength = min(total, DIV_ROUND_UP(skip + (int)len, 4));
+ length = min(total * 4 - skip, (int)len);
+ buf = vmalloc(wlength * 4);
- dev_dbg(t->dev, "ETB buffer length: %d\n", total);
+ dev_dbg(t->dev, "ETB read %ld bytes to %lld from %ld words at %d\n",
+ length, pos, wlength, first);
+ dev_dbg(t->dev, "ETB buffer length: %d\n", total + wpos);
dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS));
- for (i = 0; i < length / 4; i++)
+ for (i = 0; i < wlength; i++)
buf[i] = etb_readl(t, ETBR_READMEM);
- /* the only way to deassert overflow bit in ETB status is this */
- etb_writel(t, 1, ETBR_CTRL);
- etb_writel(t, 0, ETBR_CTRL);
-
- etb_writel(t, 0, ETBR_WRITEADDR);
- etb_writel(t, 0, ETBR_READADDR);
- etb_writel(t, 0, ETBR_TRIGGERCOUNT);
-
etb_lock(t);
- length -= copy_to_user(data, buf, length);
+ length -= copy_to_user(data, (u8 *)buf + skip, length);
vfree(buf);
+ *ppos = pos + length;
out:
mutex_unlock(&t->mutex);
--
1.7.3.1
It is not safe to call etm_lock or etb_lock without holding the
mutex since another thread may also have unlocked the registers.
Also add some missing checks for valid etb_regs in the etm sysfs
entries.
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/kernel/etm.c | 57 +++++++++++++++++++++++++++++++++---------------
1 files changed, 39 insertions(+), 18 deletions(-)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 96b1abb..8126beb 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -263,8 +263,13 @@ static void etm_dump(void)
static void sysrq_etm_dump(int key)
{
+ if (!mutex_trylock(&tracer.mutex)) {
+ printk(KERN_INFO "Tracing hardware busy\n");
+ return;
+ }
dev_dbg(tracer.dev, "Dumping ETB buffer\n");
etm_dump();
+ mutex_unlock(&tracer.mutex);
}
static struct sysrq_key_op sysrq_etm_op = {
@@ -373,6 +378,7 @@ static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
if (ret)
goto out;
+ mutex_lock(&t->mutex);
t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res));
if (!t->etb_regs) {
ret = -ENOMEM;
@@ -381,6 +387,16 @@ static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
amba_set_drvdata(dev, t);
+ etb_unlock(t);
+ t->etb_bufsz = etb_readl(t, ETBR_DEPTH);
+ dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz);
+
+ /* make sure trace capture is disabled */
+ etb_writel(t, 0, ETBR_CTRL);
+ etb_writel(t, 0x1000, ETBR_FORMATTERCTRL);
+ etb_lock(t);
+ mutex_unlock(&t->mutex);
+
etb_miscdev.parent = &dev->dev;
ret = misc_register(&etb_miscdev);
@@ -394,25 +410,19 @@ static int __init etb_probe(struct amba_device *dev, struct amba_id *id)
else
clk_enable(t->emu_clk);
- etb_unlock(t);
- t->etb_bufsz = etb_readl(t, ETBR_DEPTH);
- dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz);
-
- /* make sure trace capture is disabled */
- etb_writel(t, 0, ETBR_CTRL);
- etb_writel(t, 0x1000, ETBR_FORMATTERCTRL);
- etb_lock(t);
-
dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n");
out:
return ret;
out_unmap:
+ mutex_lock(&t->mutex);
amba_set_drvdata(dev, NULL);
iounmap(t->etb_regs);
+ t->etb_regs = NULL;
out_release:
+ mutex_unlock(&t->mutex);
amba_release_regions(dev);
return ret;
@@ -474,7 +484,10 @@ static ssize_t trace_running_store(struct kobject *kobj,
return -EINVAL;
mutex_lock(&tracer.mutex);
- ret = value ? trace_start(&tracer) : trace_stop(&tracer);
+ if (!tracer.etb_regs)
+ ret = -ENODEV;
+ else
+ ret = value ? trace_start(&tracer) : trace_stop(&tracer);
mutex_unlock(&tracer.mutex);
return ret ? : n;
@@ -490,18 +503,25 @@ static ssize_t trace_info_show(struct kobject *kobj,
u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st;
int datalen;
- etb_unlock(&tracer);
- datalen = etb_getdatalen(&tracer);
- etb_wa = etb_readl(&tracer, ETBR_WRITEADDR);
- etb_ra = etb_readl(&tracer, ETBR_READADDR);
- etb_st = etb_readl(&tracer, ETBR_STATUS);
- etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL);
- etb_lock(&tracer);
+ mutex_lock(&tracer.mutex);
+ if (tracer.etb_regs) {
+ etb_unlock(&tracer);
+ datalen = etb_getdatalen(&tracer);
+ etb_wa = etb_readl(&tracer, ETBR_WRITEADDR);
+ etb_ra = etb_readl(&tracer, ETBR_READADDR);
+ etb_st = etb_readl(&tracer, ETBR_STATUS);
+ etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL);
+ etb_lock(&tracer);
+ } else {
+ etb_wa = etb_ra = etb_st = etb_fc = ~0;
+ datalen = -1;
+ }
etm_unlock(&tracer);
etm_ctrl = etm_readl(&tracer, ETMR_CTRL);
etm_st = etm_readl(&tracer, ETMR_STATUS);
etm_lock(&tracer);
+ mutex_unlock(&tracer.mutex);
return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n"
"ETBR_WRITEADDR:\t%08x\n"
@@ -651,7 +671,6 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
amba_set_drvdata(dev, t);
- mutex_init(&t->mutex);
t->dev = &dev->dev;
t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA;
t->etm_portsz = 1;
@@ -745,6 +764,8 @@ static int __init etm_init(void)
{
int retval;
+ mutex_init(&tracer.mutex);
+
retval = amba_driver_register(&etb_driver);
if (retval) {
printk(KERN_ERR "Failed to register etb\n");
--
1.7.3.1
The old code enabled data tracing, but did not configure the
range. We now configure it to trace all data addresses by default,
and add a trace_data_range attribute to change the range or disable
data tracing.
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/include/asm/hardware/coresight.h | 10 +++-
arch/arm/kernel/etm.c | 77 +++++++++++++++++++++++++++-
2 files changed, 83 insertions(+), 4 deletions(-)
diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h
index 7ecd793..6ea507f 100644
--- a/arch/arm/include/asm/hardware/coresight.h
+++ b/arch/arm/include/asm/hardware/coresight.h
@@ -17,9 +17,11 @@
#define TRACER_ACCESSED_BIT 0
#define TRACER_RUNNING_BIT 1
#define TRACER_CYCLE_ACC_BIT 2
+#define TRACER_TRACE_DATA_BIT 3
#define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT)
#define TRACER_RUNNING BIT(TRACER_RUNNING_BIT)
#define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT)
+#define TRACER_TRACE_DATA BIT(TRACER_TRACE_DATA_BIT)
#define TRACER_TIMEOUT 10000
@@ -113,8 +115,14 @@
#define ETMR_TRACEENCTRL 0x24
#define ETMTE_INCLEXCL BIT(24)
#define ETMR_TRACEENEVT 0x20
+
+#define ETMR_VIEWDATAEVT 0x30
+#define ETMR_VIEWDATACTRL1 0x34
+#define ETMR_VIEWDATACTRL2 0x38
+#define ETMR_VIEWDATACTRL3 0x3c
+#define ETMVDC3_EXCLONLY BIT(16)
+
#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT | \
- ETMCTRL_DATA_DO_ADDR | \
ETMCTRL_BRANCH_OUTPUT | \
ETMCTRL_DO_CONTEXTID)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 8a1c422..96b1abb 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -42,6 +42,8 @@ struct tracectx {
int etm_portsz;
unsigned long range_start;
unsigned long range_end;
+ unsigned long data_range_start;
+ unsigned long data_range_end;
struct device *dev;
struct clk *emu_clk;
struct mutex mutex;
@@ -83,8 +85,15 @@ static int etm_setup_address_range(struct tracectx *t, int n,
etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1));
etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1));
- flags = exclude ? ETMTE_INCLEXCL : 0;
- etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL);
+ if (data) {
+ flags = exclude ? ETMVDC3_EXCLONLY : 0;
+ if (exclude)
+ n += 8;
+ etm_writel(t, flags | BIT(n), ETMR_VIEWDATACTRL3);
+ } else {
+ flags = exclude ? ETMTE_INCLEXCL : 0;
+ etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL);
+ }
return 0;
}
@@ -108,6 +117,9 @@ static int trace_start(struct tracectx *t)
if (t->flags & TRACER_CYCLE_ACC)
v |= ETMCTRL_CYCLEACCURATE;
+ if (t->flags & TRACER_TRACE_DATA)
+ v |= ETMCTRL_DATA_DO_ADDR;
+
etm_unlock(t);
etm_writel(t, v, ETMR_CTRL);
@@ -130,6 +142,17 @@ static int trace_start(struct tracectx *t)
etm_writel(t, 0, ETMR_TRACESSCTRL);
etm_writel(t, 0x6f, ETMR_TRACEENEVT);
+ etm_writel(t, 0, ETMR_VIEWDATACTRL1);
+ etm_writel(t, 0, ETMR_VIEWDATACTRL2);
+
+ if (t->data_range_start || t->data_range_end)
+ etm_setup_address_range(t, 2, t->data_range_start,
+ t->data_range_end, 0, 1);
+ else
+ etm_writel(t, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3);
+
+ etm_writel(t, 0x6f, ETMR_VIEWDATAEVT);
+
v &= ~ETMCTRL_PROGRAM;
v |= ETMCTRL_PORTSEL;
@@ -563,6 +586,48 @@ static ssize_t trace_range_store(struct kobject *kobj,
static struct kobj_attribute trace_range_attr =
__ATTR(trace_range, 0644, trace_range_show, trace_range_store);
+static ssize_t trace_data_range_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ unsigned long range_start;
+ u64 range_end;
+ mutex_lock(&tracer.mutex);
+ range_start = tracer.data_range_start;
+ range_end = tracer.data_range_end;
+ if (!range_end && (tracer.flags & TRACER_TRACE_DATA))
+ range_end = 0x100000000ULL;
+ mutex_unlock(&tracer.mutex);
+ return sprintf(buf, "%08lx %08llx\n", range_start, range_end);
+}
+
+static ssize_t trace_data_range_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned long range_start;
+ u64 range_end;
+
+ if (sscanf(buf, "%lx %llx", &range_start, &range_end) != 2)
+ return -EINVAL;
+
+ mutex_lock(&tracer.mutex);
+ tracer.data_range_start = range_start;
+ tracer.data_range_end = (unsigned long)range_end;
+ if (range_end)
+ tracer.flags |= TRACER_TRACE_DATA;
+ else
+ tracer.flags &= ~TRACER_TRACE_DATA;
+ mutex_unlock(&tracer.mutex);
+
+ return n;
+}
+
+
+static struct kobj_attribute trace_data_range_attr =
+ __ATTR(trace_data_range, 0644,
+ trace_data_range_show, trace_data_range_store);
+
static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
{
struct tracectx *t = &tracer;
@@ -588,7 +653,7 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
mutex_init(&t->mutex);
t->dev = &dev->dev;
- t->flags = TRACER_CYCLE_ACC;
+ t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA;
t->etm_portsz = 1;
etm_unlock(t);
@@ -618,6 +683,11 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
if (ret)
dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n");
+ ret = sysfs_create_file(&dev->dev.kobj, &trace_data_range_attr.attr);
+ if (ret)
+ dev_dbg(&dev->dev,
+ "Failed to create trace_data_range in sysfs\n");
+
dev_dbg(t->dev, "ETM AMBA driver initialized.\n");
out:
@@ -648,6 +718,7 @@ static int etm_remove(struct amba_device *dev)
sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr);
sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr);
sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr);
+ sysfs_remove_file(&dev->dev.kobj, &trace_data_range_attr.attr);
return 0;
}
--
1.7.3.1
On Mon, Feb 14, 2011 at 10:11:12PM -0800, Arve Hjønnevåg wrote:
> Trace kernel text segment by default as before, allow tracing of other
> ranges by writing a range to /sys/devices/etm/trace_range, or to trace
> everything by writing 0 0.
Since you're adding this, I think it might make sense to add a 3rd optional
ctx id field, so that userspace tracing is also possible, especially when
on-chip ETB sizes increase to something more sensible than what they are
these days.
Thanks!
>
> Signed-off-by: Arve Hjønnevåg <[email protected]>
> ---
> arch/arm/kernel/etm.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++---
> 1 files changed, 46 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
> index bc7d8f2..8a1c422 100644
> --- a/arch/arm/kernel/etm.c
> +++ b/arch/arm/kernel/etm.c
> @@ -40,12 +40,17 @@ struct tracectx {
> unsigned long flags;
> int ncmppairs;
> int etm_portsz;
> + unsigned long range_start;
> + unsigned long range_end;
> struct device *dev;
> struct clk *emu_clk;
> struct mutex mutex;
> };
>
> -static struct tracectx tracer;
> +static struct tracectx tracer = {
> + .range_start = (unsigned long)_stext,
> + .range_end = (unsigned long)_etext,
> +};
>
> static inline bool trace_isrunning(struct tracectx *t)
> {
> @@ -115,8 +120,12 @@ static int trace_start(struct tracectx *t)
> return -EFAULT;
> }
>
> - etm_setup_address_range(t, 1, (unsigned long)_stext,
> - (unsigned long)_etext, 0, 0);
> + if (t->range_start || t->range_end)
> + etm_setup_address_range(t, 1,
> + t->range_start, t->range_end, 0, 0);
> + else
> + etm_writel(t, ETMTE_INCLEXCL, ETMR_TRACEENCTRL);
> +
> etm_writel(t, 0, ETMR_TRACEENCTRL2);
> etm_writel(t, 0, ETMR_TRACESSCTRL);
> etm_writel(t, 0x6f, ETMR_TRACEENEVT);
> @@ -525,6 +534,35 @@ static ssize_t trace_mode_store(struct kobject *kobj,
> static struct kobj_attribute trace_mode_attr =
> __ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store);
>
> +static ssize_t trace_range_show(struct kobject *kobj,
> + struct kobj_attribute *attr,
> + char *buf)
> +{
> + return sprintf(buf, "%08lx %08lx\n",
> + tracer.range_start, tracer.range_end);
> +}
> +
> +static ssize_t trace_range_store(struct kobject *kobj,
> + struct kobj_attribute *attr,
> + const char *buf, size_t n)
> +{
> + unsigned long range_start, range_end;
> +
> + if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2)
> + return -EINVAL;
> +
> + mutex_lock(&tracer.mutex);
> + tracer.range_start = range_start;
> + tracer.range_end = range_end;
> + mutex_unlock(&tracer.mutex);
> +
> + return n;
> +}
> +
> +
> +static struct kobj_attribute trace_range_attr =
> + __ATTR(trace_range, 0644, trace_range_show, trace_range_store);
> +
> static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
> {
> struct tracectx *t = &tracer;
> @@ -576,6 +614,10 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
> if (ret)
> dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n");
>
> + ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr);
> + if (ret)
> + dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n");
> +
> dev_dbg(t->dev, "ETM AMBA driver initialized.\n");
>
> out:
> @@ -605,6 +647,7 @@ static int etm_remove(struct amba_device *dev)
> sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr);
> sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr);
> sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr);
> + sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr);
>
> return 0;
> }
> --
> 1.7.3.1
>
On Tue, Feb 15, 2011 at 5:50 AM, Alexander Shishkin <[email protected]> wrote:
> On Mon, Feb 14, 2011 at 10:11:12PM -0800, Arve Hj?nnev?g wrote:
>> Trace kernel text segment by default as before, allow tracing of other
>> ranges by writing a range to /sys/devices/etm/trace_range, or to trace
>> everything by writing 0 0.
>
> Since you're adding this, I think it might make sense to add a 3rd optional
> ctx id field, so that userspace tracing is also possible, especially when
> on-chip ETB sizes increase to something more sensible than what they are
> these days.
>
That might be useful, but I think it can be added later. My main
motivation for adding this is so we can turn off the filtering. We
want to use tracing in combination with a hardware watchdog to debug
hard lockups and we don't want any filtering in this case.
--
Arve Hj?nnev?g
Without this change a saw an 18% increase in idle power consumption
on one deivce when trace support is compiled into the kernel. Now
I see the same increase only when tracing.
Signed-off-by: Arve Hjønnevåg <[email protected]>
---
arch/arm/kernel/etm.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
index 48de5a4..f2bc95e 100644
--- a/arch/arm/kernel/etm.c
+++ b/arch/arm/kernel/etm.c
@@ -199,7 +199,7 @@ static int trace_stop_etm(struct tracectx *t, int id)
etm_unlock(t, id);
- etm_writel(t, id, 0x440, ETMR_CTRL);
+ etm_writel(t, id, 0x441, ETMR_CTRL);
while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
;
if (!timeout) {
@@ -738,7 +738,7 @@ static int __init etm_probe(struct amba_device *dev, struct amba_id *id)
(void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR);
t->ncmppairs = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE) & 0xf;
- etm_writel(t, t->etm_regs_count, 0x440, ETMR_CTRL);
+ etm_writel(t, t->etm_regs_count, 0x441, ETMR_CTRL);
etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR);
etm_lock(t, t->etm_regs_count);
--
1.7.3.1