Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5ecc7ecf authored by Arve Hjønnevåg's avatar Arve Hjønnevåg Committed by Ruchi Kandoi
Browse files

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: default avatarArve Hjønnevåg <arve@android.com>
parent 385e0061
Loading
Loading
Loading
Loading
+10 −6
Original line number Diff line number Diff line
@@ -25,9 +25,9 @@

#define TRACER_TIMEOUT 10000

#define etm_writel(t, v, x) \
	(writel_relaxed((v), (t)->etm_regs + (x)))
#define etm_readl(t, x) (readl_relaxed((t)->etm_regs + (x)))
#define etm_writel(t, id, v, x) \
	(writel_relaxed((v), (t)->etm_regs[(id)] + (x)))
#define etm_readl(t, id, x) (readl_relaxed((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) \
	(writel_relaxed((v), (t)->etb_regs + (x)))
#define etb_readl(t, x) (readl_relaxed((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), CS_LAR_KEY, 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), CS_LAR_KEY, CSMR_LOCKACCESS); } while (0)

#define etb_lock(t) do { etb_writel((t), 0, CSMR_LOCKACCESS); } while (0)
#define etb_unlock(t) \
+154 −76
Original line number Diff line number Diff line
@@ -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>
@@ -37,10 +38,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;
@@ -61,7 +64,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 |
@@ -80,41 +83,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)
@@ -123,79 +116,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) &
@@ -390,6 +426,7 @@ static int 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);

@@ -505,6 +542,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) {
@@ -520,29 +559,34 @@ 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 =
	__ATTR(trace_info, 0444, trace_info_show, NULL);
@@ -655,37 +699,46 @@ static int 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;

	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 (t->etm_regs) {
		dev_dbg(&dev->dev, "ETM already initialized\n");
		ret = -EBUSY;
	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);
@@ -710,28 +763,33 @@ static int 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:
	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);

	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);
@@ -739,6 +797,22 @@ 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);

	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;
}

@@ -747,6 +821,10 @@ static struct amba_id etm_ids[] = {
		.id	= 0x0003b921,
		.mask	= 0x0007ffff,
	},
	{
		.id	= 0x0003b950,
		.mask	= 0x0007ffff,
	},
	{ 0, 0 },
};