mirror of
https://github.com/hardkernel/linux.git
synced 2026-04-21 13:00:44 +09:00
ARM: etm: Support multiple ETMs/PTMs.
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). Change-Id: I028657d5cf2bee1b23f193d4387b607953b35888 Signed-off-by: Arve Hjønnevåg <arve@android.com>
This commit is contained in:
committed by
Colin Cross
parent
97aba2e599
commit
628c494e44
@@ -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) \
|
||||
|
||||
@@ -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 __devinit etb_probe(struct amba_device *dev, const 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 __devinit etm_probe(struct amba_device *dev, const 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 __devinit etm_probe(struct amba_device *dev, const 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 },
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user