Re: [PATCH 1/2] arm/omap3: a driver for on-chip ETM and ETB

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



* virtuoso@xxxxxxxxx <virtuoso@xxxxxxxxx> [091007 15:38]:
> From: Alexander Shishkin <virtuoso@xxxxxxxxx>
> 
> This driver implements /dev/tracebuf and some control files for ETM
> and ETB in sysfs.

Cool. This should go in via the linux-arm-kernel list, can you please
resend? Please also Cc linux-omap list too.

Regards,

Tony
 
> Signed-off-by: Alexander Shishkin <virtuoso@xxxxxxxxx>
> ---
>  arch/arm/Kconfig.debug                    |    8 +
>  arch/arm/include/asm/hardware/coresight.h |  164 ++++++++
>  arch/arm/kernel/Makefile                  |    2 +
>  arch/arm/kernel/etm.c                     |  584 +++++++++++++++++++++++++++++
>  arch/arm/mach-omap2/Kconfig               |    7 +
>  arch/arm/mach-omap2/Makefile              |    3 +
>  arch/arm/mach-omap2/emu.c                 |   70 ++++
>  7 files changed, 838 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm/include/asm/hardware/coresight.h
>  create mode 100644 arch/arm/kernel/etm.c
>  create mode 100644 arch/arm/mach-omap2/emu.c
> 
> diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
> index 1a6f70e..ac83c03 100644
> --- a/arch/arm/Kconfig.debug
> +++ b/arch/arm/Kconfig.debug
> @@ -83,6 +83,14 @@ config DEBUG_ICEDCC
>  	  It does include a timeout to ensure that the system does not
>  	  totally freeze when there is nothing connected to read.
>  
> +config OC_ETM
> +	tristate "On-chip ETM and ETB"
> +	depends on ARCH_OMAP3
> +	help
> +	  Enables the on-chip embedded trace macrocell and embedded trace
> +	  buffer driver that will allow you to collect traces of the
> +	  kernel code.
> +
>  config DEBUG_DC21285_PORT
>  	bool "Kernel low-level debugging messages via footbridge serial port"
>  	depends on DEBUG_LL && FOOTBRIDGE
> diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h
> new file mode 100644
> index 0000000..ba22df9
> --- /dev/null
> +++ b/arch/arm/include/asm/hardware/coresight.h
> @@ -0,0 +1,164 @@
> +/*
> + * linux/arch/arm/include/asm/hardware/coresight.h
> + *
> + * CoreSight components' registers
> + *
> + * Copyright (C) 2009 Nokia Corporation.
> + * Alexander Shishkin
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __ASM_HARDWARE_CORESIGHT_H
> +#define __ASM_HARDWARE_CORESIGHT_H
> +
> +#define TRACER_ACCESSED_BIT	0
> +#define TRACER_RUNNING_BIT	1
> +#define TRACER_CYCLE_ACC_BIT	2
> +#define TRACER_ACCESSED		BIT(TRACER_ACCESSED_BIT)
> +#define TRACER_RUNNING		BIT(TRACER_RUNNING_BIT)
> +#define TRACER_CYCLE_ACC	BIT(TRACER_CYCLE_ACC_BIT)
> +
> +struct tracectx {
> +	unsigned int etb_bufsz;
> +	void __iomem *etb_regs;
> +	void __iomem *etm_regs;
> +	unsigned long flags;
> +	int ncmppairs;
> +	int etm_portsz;
> +	struct device *dev;
> +	struct mutex mutex;
> +};
> +
> +#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)))
> +
> +/* CoreSight Management Registers */
> +#define CSMR_LOCKACCESS 0xfb0
> +#define CSMR_LOCKSTATUS 0xfb4
> +#define CSMR_AUTHSTATUS 0xfb8
> +#define CSMR_DEVID	0xfc8
> +#define CSMR_DEVTYPE	0xfcc
> +/* CoreSight Component Registers */
> +#define CSCR_CLASS	0xff4
> +
> +#define CSCR_PRSR	0x314
> +
> +#define UNLOCK_MAGIC	0xc5acce55
> +
> +/* ETM control register, "ETM Architecture", 3.3.1 */
> +#define ETMR_CTRL		0
> +#define ETMCTRL_POWERDOWN	1
> +#define ETMCTRL_PROGRAM		(1 << 10)
> +#define ETMCTRL_PORTSEL		(1 << 11)
> +#define ETMCTRL_DO_CONTEXTID	(3 << 14)
> +#define ETMCTRL_PORTMASK1	(7 << 4)
> +#define ETMCTRL_PORTMASK2	(1 << 21)
> +#define ETMCTRL_PORTMASK	(ETMCTRL_PORTMASK1 | ETMCTRL_PORTMASK2)
> +#define ETMCTRL_PORTSIZE(x) ((((x) & 7) << 4) | (!!((x) & 8)) << 21)
> +#define ETMCTRL_DO_CPRT		(1 << 1)
> +#define ETMCTRL_DATAMASK	(3 << 2)
> +#define ETMCTRL_DATA_DO_DATA	(1 << 2)
> +#define ETMCTRL_DATA_DO_ADDR	(1 << 3)
> +#define ETMCTRL_DATA_DO_BOTH	(ETMCTRL_DATA_DO_DATA | ETMCTRL_DATA_DO_ADDR)
> +#define ETMCTRL_BRANCH_OUTPUT	(1 << 8)
> +#define ETMCTRL_CYCLEACCURATE	(1 << 12)
> +
> +/* ETM configuration code register */
> +#define ETMR_CONFCODE		(0x04)
> +
> +/* ETM trace start/stop resource control register */
> +#define ETMR_TRACESSCTRL	(0x18)
> +
> +/* ETM trigger event register */
> +#define ETMR_TRIGEVT		(0x08)
> +
> +/* address access type register bits, "ETM architecture",
> + * table 3-27 */
> +/* - access type */
> +#define ETMAAT_IFETCH		0
> +#define ETMAAT_IEXEC		1
> +#define ETMAAT_IEXECPASS	2
> +#define ETMAAT_IEXECFAIL	3
> +#define ETMAAT_DLOADSTORE	4
> +#define ETMAAT_DLOAD		5
> +#define ETMAAT_DSTORE		6
> +/* - comparison access size */
> +#define ETMAAT_JAVA		(0 << 3)
> +#define ETMAAT_THUMB		(1 << 3)
> +#define ETMAAT_ARM		(3 << 3)
> +/* - data value comparison control */
> +#define ETMAAT_NOVALCMP		(0 << 5)
> +#define ETMAAT_VALMATCH		(1 << 5)
> +#define ETMAAT_VALNOMATCH	(3 << 5)
> +/* - exact match */
> +#define ETMAAT_EXACTMATCH	(1 << 7)
> +/* - context id comparator control */
> +#define ETMAAT_IGNCONTEXTID	(0 << 8)
> +#define ETMAAT_VALUE1		(1 << 8)
> +#define ETMAAT_VALUE2		(2 << 8)
> +#define ETMAAT_VALUE3		(3 << 8)
> +/* - security level control */
> +#define ETMAAT_IGNSECURITY	(0 << 10)
> +#define ETMAAT_NSONLY		(1 << 10)
> +#define ETMAAT_SONLY		(2 << 10)
> +
> +#define ETMR_COMP_VAL(x)	(0x40 + (x) * 4)
> +#define ETMR_COMP_ACC_TYPE(x)	(0x80 + (x) * 4)
> +
> +/* ETM status register, "ETM Architecture", 3.3.2 */
> +#define ETMR_STATUS		(0x10)
> +#define ETMST_OVERFLOW		(1 << 0)
> +#define ETMST_PROGBIT		(1 << 1)
> +#define ETMST_STARTSTOP		(1 << 2)
> +#define ETMST_TRIGGER		(1 << 3)
> +
> +#define etm_progbit(t)		(etm_readl((t), ETMR_STATUS) & ETMST_PROGBIT)
> +#define etm_started(t)		(etm_readl((t), ETMR_STATUS) & ETMST_STARTSTOP)
> +#define etm_triggered(t)	(etm_readl((t), ETMR_STATUS) & ETMST_TRIGGER)
> +
> +#define ETMR_TRACEENCTRL2	0x1c
> +#define ETMR_TRACEENCTRL	0x24
> +#define ETMTE_INCLEXCL		(1 << 24)
> +#define ETMR_TRACEENEVT		0x20
> +#define ETMCTRL_OPTS		(ETMCTRL_DO_CPRT | \
> +				ETMCTRL_DATA_DO_ADDR | \
> +				ETMCTRL_BRANCH_OUTPUT | \
> +				ETMCTRL_DO_CONTEXTID)
> +
> +/* ETB registers, "CoreSight Components TRM", 9.3 */
> +#define ETBR_DEPTH		0x04
> +#define ETBR_STATUS		0x0c
> +#define ETBR_READMEM		0x10
> +#define ETBR_READADDR		0x14
> +#define ETBR_WRITEADDR		0x18
> +#define ETBR_TRIGGERCOUNT	0x1c
> +#define ETBR_CTRL		0x20
> +#define ETBR_FORMATTERCTRL	0x304
> +#define ETBFF_ENFTC		1
> +#define ETBFF_ENFCONT		(1 << 1)
> +#define ETBFF_FONFLIN		(1 << 4)
> +#define ETBFF_MANUAL_FLUSH	(1 << 6)
> +#define ETBFF_TRIGIN		(1 << 8)
> +#define ETBFF_TRIGEVT		(1 << 9)
> +#define ETBFF_TRIGFL		(1 << 10)
> +
> +#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 etb_lock(t) do { etb_writel((t), 0, CSMR_LOCKACCESS); } while (0)
> +#define etb_unlock(t) \
> +	do { etb_writel((t), UNLOCK_MAGIC, CSMR_LOCKACCESS); } while (0)
> +
> +#endif /* __ASM_HARDWARE_CORESIGHT_H */
> +
> diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
> index 79087dd..e7ccf7e 100644
> --- a/arch/arm/kernel/Makefile
> +++ b/arch/arm/kernel/Makefile
> @@ -17,6 +17,8 @@ obj-y		:= compat.o elf.o entry-armv.o entry-common.o irq.o \
>  		   process.o ptrace.o return_address.o setup.o signal.o \
>  		   sys_arm.o stacktrace.o time.o traps.o
>  
> +obj-$(CONFIG_OC_ETM)		+= etm.o
> +
>  obj-$(CONFIG_ISA_DMA_API)	+= dma.o
>  obj-$(CONFIG_ARCH_ACORN)	+= ecard.o 
>  obj-$(CONFIG_FIQ)		+= fiq.o
> diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c
> new file mode 100644
> index 0000000..fe233b0
> --- /dev/null
> +++ b/arch/arm/kernel/etm.c
> @@ -0,0 +1,584 @@
> +/*
> + * linux/arch/arm/kernel/etm.c
> + *
> + * Driver for ARM's Embedded Trace Macrocell and Embedded Trace Buffer.
> + *
> + * Copyright (C) 2009 Nokia Corporation.
> + * Alexander Shishkin
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +#include <linux/sysrq.h>
> +#include <linux/platform_device.h>
> +#include <linux/fs.h>
> +#include <linux/uaccess.h>
> +#include <linux/miscdevice.h>
> +#include <linux/vmalloc.h>
> +#include <linux/mutex.h>
> +#include <asm/hardware/coresight.h>
> +#include <asm/sections.h>
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Alexander Shishkin");
> +
> +static struct tracectx tracer;
> +
> +static inline bool trace_isrunning(struct tracectx *t)
> +{
> +	return !!(t->flags & TRACER_RUNNING);
> +}
> +
> +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 | \
> +		    ETMAAT_NOVALCMP;
> +
> +	if (n < 1 || n > t->ncmppairs)
> +		return -EINVAL;
> +
> +	/* comparators and ranges are numbered starting with 1 as opposed
> +	 * to bits in a word */
> +	n--;
> +
> +	if (data)
> +		flags |= ETMAAT_DLOADSTORE;
> +	else
> +		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));
> +
> +	/* 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));
> +
> +	flags = exclude ? ETMTE_INCLEXCL : 0;
> +	etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL);
> +
> +	return 0;
> +}
> +
> +static int trace_start(struct tracectx *t)
> +{
> +	u32 v;
> +	unsigned long timeout = TRACER_TIMEOUT;
> +
> +	etb_unlock(t);
> +
> +	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)
> +		v |= ETMCTRL_CYCLEACCURATE;
> +
> +	etm_unlock(t);
> +
> +	etm_writel(t, v, ETMR_CTRL);
> +
> +	while (--timeout && !(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM))
> +		;
> +	if (!timeout) {
> +		dev_dbg(t->dev, "Waiting for progbit to assert timed out\n");
> +		return -EFAULT;
> +	}
> +
> +	etm_setup_address_range(t, 1, (unsigned long)_stext,
> +			(unsigned long)_etext, 0, 0);
> +	etm_writel(t, 0, ETMR_TRACEENCTRL2);
> +	etm_writel(t, 0, ETMR_TRACESSCTRL);
> +	etm_writel(t, 0x6f, ETMR_TRACEENEVT);
> +
> +	v &= ~ETMCTRL_PROGRAM;
> +	v |= ETMCTRL_PORTSEL;
> +
> +	etm_writel(t, v, ETMR_CTRL);
> +
> +	timeout = TRACER_TIMEOUT;
> +	while (--timeout && etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM)
> +		;
> +	if (!timeout) {
> +		dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n");
> +		return -EFAULT;
> +	}
> +
> +	etm_lock(t);
> +
> +	t->flags |= TRACER_RUNNING;
> +
> +	return 0;
> +}
> +
> +static int trace_stop(struct tracectx *t)
> +{
> +	unsigned long timeout = TRACER_TIMEOUT;
> +
> +	etm_unlock(t);
> +
> +	etm_writel(t, 0x440, ETMR_CTRL);
> +	while (--timeout && !(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM))
> +		;
> +	if (!timeout) {
> +		dev_dbg(t->dev, "Waiting for progbit to assert timed out\n");
> +		return -EFAULT;
> +	}
> +
> +	etm_lock(t);
> +
> +	etb_unlock(t);
> +	etb_writel(t, ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL);
> +
> +	timeout = TRACER_TIMEOUT;
> +	while (--timeout && etb_readl(t, ETBR_FORMATTERCTRL) &
> +			ETBFF_MANUAL_FLUSH)
> +		;
> +	if (!timeout) {
> +		dev_dbg(t->dev, "Waiting for formatter flush to commence "
> +				"timed out\n");
> +		return -EFAULT;
> +	}
> +
> +	etb_writel(t, 0, ETBR_CTRL);
> +
> +	etb_lock(t);
> +
> +	t->flags &= ~TRACER_RUNNING;
> +
> +	return 0;
> +}
> +
> +static int etb_getdatalen(struct tracectx *t)
> +{
> +	u32 v;
> +	int rp, 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;
> +}
> +
> +/* sysrq+v will always stop the running trace and leave it at that */
> +static void etm_dump(void)
> +{
> +	struct tracectx *t = &tracer;
> +	u32 first = 0;
> +	int length;
> +
> +	if (!t->etb_regs) {
> +		printk(KERN_INFO "No tracing hardware found\n");
> +		return;
> +	}
> +
> +	if (!mutex_trylock(&t->mutex))
> +		return;
> +
> +	if (trace_isrunning(t))
> +		trace_stop(t);
> +
> +	etb_unlock(t);
> +
> +	length = etb_getdatalen(t);
> +
> +	if (length == t->etb_bufsz)
> +		first = etb_readl(t, ETBR_WRITEADDR);
> +
> +	etb_writel(t, first, ETBR_READADDR);
> +
> +	printk(KERN_INFO "Trace buffer contents length: %d\n", length);
> +	printk(KERN_INFO "--- ETB buffer begin ---\n");
> +	for (; length; length--)
> +		printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM)));
> +	printk(KERN_INFO "\n--- ETB buffer end ---\n");
> +
> +	etb_writel(t, 0, ETBR_TRIGGERCOUNT);
> +	etb_writel(t, 0, ETBR_READADDR);
> +	etb_writel(t, 0, ETBR_WRITEADDR);
> +
> +	etb_lock(t);
> +
> +	mutex_unlock(&t->mutex);
> +}
> +
> +static void sysrq_etm_dump(int key, struct tty_struct *tty)
> +{
> +	dev_dbg(tracer.dev, "Dumping ETB buffer\n");
> +	etm_dump();
> +}
> +
> +static struct sysrq_key_op sysrq_etm_op = {
> +	.handler = sysrq_etm_dump,
> +	.help_msg = "ETM buffer dump",
> +	.action_msg = "etm",
> +};
> +
> +static int etb_open(struct inode *inode, struct file *file)
> +{
> +	if (!tracer.etb_regs)
> +		return -ENODEV;
> +
> +	file->private_data = &tracer;
> +
> +	return nonseekable_open(inode, file);
> +}
> +
> +static ssize_t etb_read(struct file *file, char __user *data,
> +		size_t len, loff_t *ppos)
> +{
> +	int total, i;
> +	long length;
> +	struct tracectx *t = file->private_data;
> +	u32 first = 0;
> +	u32 *buf;
> +
> +	mutex_lock(&t->mutex);
> +
> +	if (trace_isrunning(t)) {
> +		length = 0;
> +		goto out;
> +	}
> +
> +	etb_unlock(t);
> +
> +	total = etb_getdatalen(t);
> +	if (total == t->etb_bufsz)
> +		first = etb_readl(t, ETBR_WRITEADDR);
> +
> +	etb_writel(t, first, ETBR_READADDR);
> +
> +	length = min(total * 4, (int)len);
> +	buf = vmalloc(length);
> +
> +	dev_dbg(t->dev, "ETB buffer length: %d\n", total);
> +	dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS));
> +	for (i = 0; i < length / 4; 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);
> +	vfree(buf);
> +
> +out:
> +	mutex_unlock(&t->mutex);
> +
> +	return length;
> +}
> +
> +static int etb_release(struct inode *inode, struct file *file)
> +{
> +	/* there's nothing to do here, actually */
> +	return 0;
> +}
> +
> +static struct file_operations etb_fops = {
> +	.owner = THIS_MODULE,
> +	.read = etb_read,
> +	.open = etb_open,
> +	.release = etb_release,
> +};
> +
> +static struct miscdevice etb_miscdev = {
> +	.name = "tracebuf",
> +	.minor = 0,
> +	.fops = &etb_fops,
> +};
> +
> +static int __devinit etb_drv_probe(struct platform_device *pdev)
> +{
> +	struct tracectx *t = platform_get_drvdata(pdev);
> +	struct resource *res;
> +	struct clk *clk;
> +	int ret = 0;
> +
> +	if (t && t->etb_regs) {
> +		dev_dbg(&pdev->dev, "ETB already initialized\n");
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		ret = -ENODEV;
> +		goto out;
> +	}
> +
> +	if (!request_mem_region(res->start, SZ_4K, "etb")) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
> +	if (!t) {
> +		t = &tracer;
> +		platform_set_drvdata(pdev, t);
> +	}
> +
> +	t->etb_regs = ioremap_nocache(res->start, SZ_4K);
> +	if (!t->etb_regs) {
> +		ret = -ENOMEM;
> +		platform_set_drvdata(pdev, NULL);
> +		release_mem_region(res->start, SZ_4K);
> +		goto out;
> +	}
> +
> +	etb_miscdev.parent = &pdev->dev;
> +
> +	ret = misc_register(&etb_miscdev);
> +	if (ret) {
> +		platform_set_drvdata(pdev, NULL);
> +		iounmap(t->etb_regs);
> +		release_mem_region(res->start, SZ_4K);
> +		goto out;
> +	}
> +
> +	clk = clk_get(&pdev->dev, "emu_core_alwon_ck");
> +	clk_enable(clk);
> +
> +	clk = clk_get(&pdev->dev, "emu_per_alwon_ck");
> +	clk_enable(clk);
> +
> +	clk = clk_get(&pdev->dev, "emu_mpu_alwon_ck");
> +	clk_enable(clk);
> +
> +	clk = clk_get(&pdev->dev, "emu_src_ck");
> +	clk_enable(clk);
> +
> +	etb_unlock(t);
> +	t->etb_bufsz = etb_readl(t, ETBR_DEPTH);
> +	dev_dbg(&pdev->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(&pdev->dev, "ETB platform driver initialized.\n");
> +
> +	ret = 0;
> +out:
> +	return ret;
> +}
> +
> +static struct platform_driver etb_driver = {
> +	.probe		 = etb_drv_probe,
> +	.driver	 = {
> +		.name	 = "etb",
> +		.owner	= THIS_MODULE,
> +	},
> +};
> +
> +/* use a sysfs file "trace_running" to start/stop tracing */
> +static ssize_t trace_running_show(struct kobject *kobj,
> +				  struct kobj_attribute *attr,
> +				  char *buf)
> +{
> +	return sprintf(buf, "%x\n", trace_isrunning(&tracer));
> +}
> +
> +static ssize_t trace_running_store(struct kobject *kobj,
> +				   struct kobj_attribute *attr,
> +				   const char *buf, size_t n)
> +{
> +	unsigned int value;
> +	int ret;
> +
> +	if (sscanf(buf, "%u", &value) != 1)
> +		return -EINVAL;
> +
> +	mutex_lock(&tracer.mutex);
> +	ret = value ? trace_start(&tracer) : trace_stop(&tracer);
> +	mutex_unlock(&tracer.mutex);
> +
> +	return ret ? : n;
> +}
> +
> +static struct kobj_attribute trace_running_attr =
> +	__ATTR(trace_running, 0644, trace_running_show, trace_running_store);
> +
> +static ssize_t trace_info_show(struct kobject *kobj,
> +				  struct kobj_attribute *attr,
> +				  char *buf)
> +{
> +	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);
> +
> +	etm_unlock(&tracer);
> +	etm_ctrl = etm_readl(&tracer, ETMR_CTRL);
> +	etm_st = etm_readl(&tracer, ETMR_STATUS);
> +	etm_lock(&tracer);
> +
> +	return 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",
> +			datalen,
> +			tracer.ncmppairs,
> +			etb_wa,
> +			etb_ra,
> +			etb_st,
> +			etb_fc,
> +			etm_ctrl,
> +			etm_st
> +			);
> +}
> +
> +static struct kobj_attribute trace_info_attr =
> +	__ATTR(trace_info, 0444, trace_info_show, NULL);
> +
> +static ssize_t trace_mode_show(struct kobject *kobj,
> +				  struct kobj_attribute *attr,
> +				  char *buf)
> +{
> +	return sprintf(buf, "%d %d\n",
> +			!!(tracer.flags & TRACER_CYCLE_ACC),
> +			tracer.etm_portsz);
> +}
> +
> +static ssize_t trace_mode_store(struct kobject *kobj,
> +				   struct kobj_attribute *attr,
> +				   const char *buf, size_t n)
> +{
> +	unsigned int cycacc, portsz;
> +
> +	if (sscanf(buf, "%u %u", &cycacc, &portsz) != 2)
> +		return -EINVAL;
> +
> +	mutex_lock(&tracer.mutex);
> +	if (cycacc)
> +		tracer.flags |= TRACER_CYCLE_ACC;
> +	else
> +		tracer.flags &= ~TRACER_CYCLE_ACC;
> +
> +	tracer.etm_portsz = portsz & 0x0f;
> +	mutex_unlock(&tracer.mutex);
> +
> +	return n;
> +}
> +
> +static struct kobj_attribute trace_mode_attr =
> +	__ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store);
> +
> +static int __devinit etm_drv_probe(struct platform_device *pdev)
> +{
> +	struct tracectx *t = platform_get_drvdata(pdev);
> +	struct resource *res;
> +	int ret = 0;
> +
> +	if (t && t->etm_regs) {
> +		dev_dbg(&pdev->dev, "ETM already initialized\n");
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		ret = -ENODEV;
> +		goto out;
> +	}
> +
> +	if (!request_mem_region(res->start, SZ_4K, "etm")) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
> +	if (!t) {
> +		t = &tracer;
> +		platform_set_drvdata(pdev, t);
> +	}
> +
> +	t->etm_regs = ioremap_nocache(res->start, SZ_4K);
> +	if (!t->etm_regs) {
> +		ret = -ENOMEM;
> +		release_mem_region(res->start, SZ_4K);
> +		platform_set_drvdata(pdev, NULL);
> +		goto out;
> +	}
> +
> +	mutex_init(&t->mutex);
> +	t->dev = &pdev->dev;
> +	t->flags = TRACER_CYCLE_ACC;
> +	t->etm_portsz = 1;
> +
> +	etm_unlock(t);
> +	ret = etm_readl(t, CSCR_PRSR);
> +
> +	t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf;
> +	etm_writel(t, 0x440, ETMR_CTRL);
> +	etm_lock(t);
> +
> +	ret = sysfs_create_file(&pdev->dev.kobj,
> +			&trace_running_attr.attr);
> +	ret = sysfs_create_file(&pdev->dev.kobj,
> +			&trace_info_attr.attr);
> +	ret = sysfs_create_file(&pdev->dev.kobj,
> +			&trace_mode_attr.attr);
> +	dev_dbg(t->dev, "ETM platform driver initialized.\n");
> +
> +out:
> +	return ret;
> +}
> +
> +static struct platform_driver etm_driver = {
> +	.probe           = etm_drv_probe,
> +	.driver  = {
> +		.name    = "etm",
> +		.owner   = THIS_MODULE,
> +	},
> +};
> +
> +static int __init etm_init(void)
> +{
> +	platform_driver_register(&etb_driver);
> +	platform_driver_register(&etm_driver);
> +	register_sysrq_key('v', &sysrq_etm_op);
> +
> +	return 0;
> +}
> +
> +module_init(etm_init);
> +
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> index 75b1c7e..87bcc2a 100644
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -88,3 +88,10 @@ config MACH_OMAP_ZOOM2
>  config MACH_OMAP_4430SDP
>  	bool "OMAP 4430 SDP board"
>  	depends on ARCH_OMAP4
> +
> +config OMAP3_EMU
> +	tristate "OMAP3 debugging peripherals"
> +	depends on ARCH_OMAP3 && OC_ETM
> +	help
> +	  Say Y here to enable debugging hardware of omap3
> +
> diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
> index 6b7702f..572dd27 100644
> --- a/arch/arm/mach-omap2/Makefile
> +++ b/arch/arm/mach-omap2/Makefile
> @@ -44,6 +44,9 @@ obj-$(CONFIG_ARCH_OMAP4)		+= cm4xxx.o
>  obj-$(CONFIG_ARCH_OMAP2)		+= clock24xx.o
>  obj-$(CONFIG_ARCH_OMAP3)		+= clock34xx.o
>  
> +# EMU periferals
> +obj-$(CONFIG_OMAP3_EMU)		+= emu.o
> +
>  iommu-y					+= iommu2.o
>  iommu-$(CONFIG_ARCH_OMAP3)		+= omap3-iommu.o
>  
> diff --git a/arch/arm/mach-omap2/emu.c b/arch/arm/mach-omap2/emu.c
> new file mode 100644
> index 0000000..f98874e
> --- /dev/null
> +++ b/arch/arm/mach-omap2/emu.c
> @@ -0,0 +1,70 @@
> +/*
> + * linux/arch/arm/mach-omap2/emu.c
> + *
> + * ETM and ETB CoreSight components' resources as found in OMAP3xxx.
> + *
> + * Copyright (C) 2009 Nokia Corporation.
> + * Alexander Shishkin
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Alexander Shishkin");
> +
> +/* Cortex CoreSight components within omap3xxx EMU */
> +#define ETM_BASE	(L4_EMU_34XX_PHYS + 0x10000)
> +#define DBG_BASE	(L4_EMU_34XX_PHYS + 0x11000)
> +#define ETB_BASE	(L4_EMU_34XX_PHYS + 0x1b000)
> +#define DAPCTL		(L4_EMU_34XX_PHYS + 0x1d000)
> +
> +static struct resource rx51_etb_resource = {
> +	.start = ETB_BASE,
> +	.end   = ETB_BASE + SZ_4K,
> +	.flags = IORESOURCE_MEM,
> +};
> +
> +static struct platform_device rx51_etb_device = {
> +	.name = "etb",
> +	.id   = -1,
> +	.num_resources = 1,
> +	.resource = &rx51_etb_resource,
> +};
> +
> +static struct resource rx51_etm_resource = {
> +	.start = ETM_BASE,
> +	.end   = ETM_BASE + SZ_4K,
> +	.flags = IORESOURCE_MEM,
> +};
> +
> +static struct platform_device rx51_etm_device = {
> +	.name = "etm",
> +	.id   = -1,
> +	.num_resources = 1,
> +	.resource = &rx51_etm_resource,
> +};
> +
> +static struct platform_device *rx51_trace_devices[] = {
> +	&rx51_etm_device,
> +	&rx51_etb_device,
> +};
> +
> +static int __init emu_init(void)
> +{
> +	platform_add_devices(rx51_trace_devices,
> +			ARRAY_SIZE(rx51_trace_devices));
> +
> +	return 0;
> +}
> +
> +module_init(emu_init);
> +
> -- 
> 1.6.3.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux