msm: qdss: coresight PFT tracing support

Initial version of the PFT trace driver.

CRs-Fixed: 305958
Signed-off-by: Pratik Patel <pratikp@codeaurora.org>
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 7e321d6..7c0405a 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -1547,11 +1547,25 @@
 		make the kernel reboot on a kernel panic - that must be
 		enabled via another mechanism.
 
+config MSM_TRACE_ACROSS_PC
+	bool "Trace support across power collapse"
+	depends on ARCH_MSM8X60 || ARCH_MSM8960
+	help
+	  Enables trace state to be saved and restored across power collapse.
+
 config MSM_ETM
 	tristate "Enable MSM ETM and ETB"
 	depends on ARCH_MSM8X60
+	select MSM_TRACE_ACROSS_PC
 	help
-		This enables embedded trace collection on Qualcomm v7 CPUs.
+	  Enables embedded trace collection on Qualcomm v7 CPUs.
+
+config MSM_QDSS
+	bool "Coresight tracing support"
+	depends on ARCH_MSM8960
+	select MSM_TRACE_ACROSS_PC
+	help
+	  Enables support for Qualcomm debug subsystem.
 
 config MSM_SLEEP_STATS
 	bool "Enable exporting of MSM sleep stats to userspace"
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 0bfaf86..e92f151 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -45,6 +45,7 @@
 
 msm-etm-objs := cp14.o etm.o
 obj-$(CONFIG_MSM_ETM) += msm-etm.o
+obj-$(CONFIG_MSM_QDSS) += qdss-etb.o qdss-tpiu.o qdss-funnel.o qdss-ptm.o
 
 quiet_cmd_mkrpcsym = MKCAP   $@
       cmd_mkrpcsym = $(PERL) $(srctree)/$(src)/mkrpcsym.pl $< $@
diff --git a/arch/arm/mach-msm/board-msm8960.c b/arch/arm/mach-msm/board-msm8960.c
index f00ba8d..9de3143 100644
--- a/arch/arm/mach-msm/board-msm8960.c
+++ b/arch/arm/mach-msm/board-msm8960.c
@@ -3236,6 +3236,12 @@
 	&ion_dev,
 #endif
 	&msm_rpm_log_device,
+#ifdef CONFIG_MSM_QDSS
+	&msm_etb_device,
+	&msm_tpiu_device,
+	&msm_funnel_device,
+	&msm_ptm_device,
+#endif
 };
 
 static struct platform_device *sim_devices[] __initdata = {
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 4ba71db..c47f056 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -2204,3 +2204,73 @@
 };
 
 #endif /* CONFIG_MSM_DSPS */
+
+#ifdef CONFIG_MSM_QDSS
+
+#define MSM_QDSS_PHYS_BASE		0x01A00000
+#define MSM_ETB_PHYS_BASE		(MSM_QDSS_PHYS_BASE + 0x1000)
+#define MSM_TPIU_PHYS_BASE		(MSM_QDSS_PHYS_BASE + 0x3000)
+#define MSM_FUNNEL_PHYS_BASE		(MSM_QDSS_PHYS_BASE + 0x4000)
+#define MSM_PTM_PHYS_BASE		(MSM_QDSS_PHYS_BASE + 0x1C000)
+
+static struct resource msm_etb_resources[] = {
+	{
+		.start = MSM_ETB_PHYS_BASE,
+		.end   = MSM_ETB_PHYS_BASE + SZ_4K - 1,
+		.flags = IORESOURCE_MEM,
+	},
+};
+
+struct platform_device msm_etb_device = {
+	.name          = "msm_etb",
+	.id            = 0,
+	.num_resources = ARRAY_SIZE(msm_etb_resources),
+	.resource      = msm_etb_resources,
+};
+
+static struct resource msm_tpiu_resources[] = {
+	{
+		.start = MSM_TPIU_PHYS_BASE,
+		.end   = MSM_TPIU_PHYS_BASE + SZ_4K - 1,
+		.flags = IORESOURCE_MEM,
+	},
+};
+
+struct platform_device msm_tpiu_device = {
+	.name          = "msm_tpiu",
+	.id            = 0,
+	.num_resources = ARRAY_SIZE(msm_tpiu_resources),
+	.resource      = msm_tpiu_resources,
+};
+
+static struct resource msm_funnel_resources[] = {
+	{
+		.start = MSM_FUNNEL_PHYS_BASE,
+		.end   = MSM_FUNNEL_PHYS_BASE + SZ_4K - 1,
+		.flags = IORESOURCE_MEM,
+	},
+};
+
+struct platform_device msm_funnel_device = {
+	.name          = "msm_funnel",
+	.id            = 0,
+	.num_resources = ARRAY_SIZE(msm_funnel_resources),
+	.resource      = msm_funnel_resources,
+};
+
+static struct resource msm_ptm_resources[] = {
+	{
+		.start = MSM_PTM_PHYS_BASE,
+		.end   = MSM_PTM_PHYS_BASE + (SZ_4K * 2) - 1,
+		.flags = IORESOURCE_MEM,
+	},
+};
+
+struct platform_device msm_ptm_device = {
+	.name          = "msm_ptm",
+	.id            = 0,
+	.num_resources = ARRAY_SIZE(msm_ptm_resources),
+	.resource      = msm_ptm_resources,
+};
+
+#endif
diff --git a/arch/arm/mach-msm/devices-msm8x60.h b/arch/arm/mach-msm/devices-msm8x60.h
index 60a1f60..33de309 100644
--- a/arch/arm/mach-msm/devices-msm8x60.h
+++ b/arch/arm/mach-msm/devices-msm8x60.h
@@ -50,6 +50,12 @@
 #ifdef CONFIG_MSM_DSPS
 extern struct platform_device msm_dsps_device;
 #endif
+#ifdef CONFIG_MSM_QDSS
+extern struct platform_device msm_etb_device;
+extern struct platform_device msm_tpiu_device;
+extern struct platform_device msm_funnel_device;
+extern struct platform_device msm_ptm_device;
+#endif
 
 #if defined(CONFIG_MSM_RPM_STATS_LOG)
 extern struct platform_device msm_rpm_stat_device;
diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S
index 5215c24..20dbd39 100644
--- a/arch/arm/mach-msm/idle-v7.S
+++ b/arch/arm/mach-msm/idle-v7.S
@@ -32,21 +32,15 @@
 #endif
 
 ENTRY(msm_arch_idle)
-	stmfd   sp!, {lr}
 #ifdef CONFIG_MSM_JTAG_V7
+	stmfd   sp!, {lr}
 	bl      msm_save_jtag_debug
 #endif
-#ifdef CONFIG_MSM_ETM
-	bl      etm_save_reg_check
-#endif
 	wfi
-#ifdef CONFIG_MSM_ETM
-	bl      etm_restore_reg_check
-#endif
 #ifdef CONFIG_MSM_JTAG_V7
 	bl      msm_restore_jtag_debug
-#endif
 	ldmfd   sp!, {lr}
+#endif
 	bx	lr
 
 ENTRY(msm_pm_collapse)
@@ -94,7 +88,7 @@
 #ifdef CONFIG_MSM_JTAG_V7
 	bl      msm_save_jtag_debug
 #endif
-#ifdef CONFIG_MSM_ETM
+#ifdef CONFIG_MSM_TRACE_ACROSS_PC
 	bl      etm_save_reg_check
 #endif
 	bl      v7_flush_dcache_all
@@ -119,7 +113,7 @@
 #if defined(CONFIG_MSM_FIQ_SUPPORT)
 	cpsie   f
 #endif
-#ifdef CONFIG_MSM_ETM
+#ifdef CONFIG_MSM_TRACE_ACROSS_PC
 	bl	etm_restore_reg_check
 #endif
 #ifdef CONFIG_MSM_JTAG_V7
@@ -207,7 +201,7 @@
 	isb
 	stmfd   sp!, {lr}
 	bl      v7_flush_kern_cache_all
-#ifdef CONFIG_MSM_ETM
+#ifdef CONFIG_MSM_TRACE_ACROSS_PC
 	bl      etm_restore_reg_check
 #endif
 #ifdef CONFIG_MSM_JTAG_V7
diff --git a/arch/arm/mach-msm/qdss-etb.c b/arch/arm/mach-msm/qdss-etb.c
new file mode 100644
index 0000000..d4f7940
--- /dev/null
+++ b/arch/arm/mach-msm/qdss-etb.c
@@ -0,0 +1,309 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include "qdss.h"
+
+#define etb_writel(etb, val, off)	__raw_writel((val), etb.base + off)
+#define etb_readl(etb, off)		__raw_readl(etb.base + off)
+
+#define ETB_RAM_DEPTH_REG	(0x004)
+#define ETB_STATUS_REG		(0x00C)
+#define ETB_RAM_READ_DATA_REG	(0x010)
+#define ETB_RAM_READ_POINTER	(0x014)
+#define ETB_RAM_WRITE_POINTER	(0x018)
+#define ETB_TRG			(0x01C)
+#define ETB_CTL_REG		(0x020)
+#define ETB_RWD_REG		(0x024)
+#define ETB_FFSR		(0x300)
+#define ETB_FFCR		(0x304)
+#define ETB_ITMISCOP0		(0xEE0)
+#define ETB_ITTRFLINACK		(0xEE4)
+#define ETB_ITTRFLIN		(0xEE8)
+#define ETB_ITATBDATA0		(0xEEC)
+#define ETB_ITATBCTR2		(0xEF0)
+#define ETB_ITATBCTR1		(0xEF4)
+#define ETB_ITATBCTR0		(0xEF8)
+
+
+#define BYTES_PER_WORD		4
+#define ETB_SIZE_WORDS		4096
+
+#define ETB_LOCK()							\
+do {									\
+	mb();								\
+	etb_writel(etb, MAGIC2, CS_LAR);				\
+} while (0)
+#define ETB_UNLOCK()							\
+do {									\
+	etb_writel(etb, MAGIC1, CS_LAR);				\
+	mb();								\
+} while (0)
+
+struct etb_ctx {
+	uint8_t		*buf;
+	void __iomem	*base;
+	bool		enabled;
+	bool		reading;
+	struct mutex	lock;
+	atomic_t	in_use;
+	struct device	*dev;
+};
+
+static struct etb_ctx etb;
+
+static void __etb_enable(void)
+{
+	int i;
+
+	ETB_UNLOCK();
+
+	etb_writel(etb, 0x0, ETB_RAM_WRITE_POINTER);
+	for (i = 0; i < ETB_SIZE_WORDS; i++)
+		etb_writel(etb, 0x0, ETB_RWD_REG);
+
+	etb_writel(etb, 0x0, ETB_RAM_WRITE_POINTER);
+	etb_writel(etb, 0x0, ETB_RAM_READ_POINTER);
+
+	etb_writel(etb, BIT(13) | BIT(0), ETB_FFCR);
+	etb_writel(etb, BIT(0), ETB_CTL_REG);
+
+	ETB_LOCK();
+}
+
+void etb_enable(void)
+{
+	mutex_lock(&etb.lock);
+	__etb_enable();
+	etb.enabled = true;
+	dev_info(etb.dev, "etb enabled\n");
+	mutex_unlock(&etb.lock);
+}
+
+static void __etb_disable(void)
+{
+	int count;
+
+	ETB_UNLOCK();
+
+	etb_writel(etb, BIT(12) | BIT(13), ETB_FFCR);
+	etb_writel(etb, 0x0, ETB_CTL_REG);
+
+	for (count = TIMEOUT_US; BVAL(etb_readl(etb, ETB_FFSR), 1) != 1
+				&& count > 0; count--)
+		udelay(1);
+	WARN(count == 0, "timeout while disabling etb\n");
+
+	ETB_LOCK();
+}
+
+void etb_disable(void)
+{
+	mutex_lock(&etb.lock);
+	__etb_disable();
+	etb.enabled = false;
+	dev_info(etb.dev, "etb disabled\n");
+	mutex_unlock(&etb.lock);
+}
+
+static void __etb_dump(void)
+{
+	int i;
+	uint8_t *buf_ptr;
+	uint32_t read_data;
+	uint32_t read_ptr;
+	uint32_t write_ptr;
+
+	ETB_UNLOCK();
+
+	read_ptr = etb_readl(etb, ETB_RAM_READ_POINTER);
+	write_ptr = etb_readl(etb, ETB_RAM_WRITE_POINTER);
+
+	if ((etb_readl(etb, ETB_STATUS_REG) & BIT(0)) == 0)
+		etb_writel(etb, 0x0, ETB_RAM_READ_POINTER);
+	else
+		etb_writel(etb, write_ptr, ETB_RAM_READ_POINTER);
+
+	buf_ptr = etb.buf;
+	for (i = 0; i < ETB_SIZE_WORDS; i++) {
+		read_data = etb_readl(etb, ETB_RAM_READ_DATA_REG);
+		*buf_ptr = read_data >> 0;
+		buf_ptr++;
+		*buf_ptr = read_data >> 8;
+		buf_ptr++;
+		*buf_ptr = read_data >> 16;
+		buf_ptr++;
+		*buf_ptr = read_data >> 24;
+		buf_ptr++;
+	}
+
+	etb_writel(etb, read_ptr, ETB_RAM_READ_POINTER);
+
+	ETB_LOCK();
+}
+
+void etb_dump(void)
+{
+	mutex_lock(&etb.lock);
+	if (etb.enabled) {
+		__etb_disable();
+		__etb_dump();
+		__etb_enable();
+
+		dev_info(etb.dev, "etb dumped\n");
+	}
+	mutex_unlock(&etb.lock);
+}
+
+static int etb_open(struct inode *inode, struct file *file)
+{
+	if (atomic_cmpxchg(&etb.in_use, 0, 1))
+		return -EBUSY;
+
+	dev_dbg(etb.dev, "%s: successfully opened\n", __func__);
+	return 0;
+}
+
+static ssize_t etb_read(struct file *file, char __user *data,
+				size_t len, loff_t *ppos)
+{
+	if (etb.reading == false) {
+		etb_dump();
+		etb.reading = true;
+	}
+
+	if (*ppos + len > ETB_SIZE_WORDS * BYTES_PER_WORD)
+		len = ETB_SIZE_WORDS * BYTES_PER_WORD - *ppos;
+
+	if (copy_to_user(data, etb.buf + *ppos, len)) {
+		dev_dbg(etb.dev, "%s: copy_to_user failed\n", __func__);
+		return -EFAULT;
+	}
+
+	*ppos += len;
+
+	dev_dbg(etb.dev, "%s: %d bytes copied, %d bytes left\n",
+		__func__, len, (int) (ETB_SIZE_WORDS * BYTES_PER_WORD - *ppos));
+
+	return len;
+}
+
+static int etb_release(struct inode *inode, struct file *file)
+{
+	etb.reading = false;
+
+	atomic_set(&etb.in_use, 0);
+
+	dev_dbg(etb.dev, "%s: released\n", __func__);
+
+	return 0;
+}
+
+static const struct file_operations etb_fops = {
+	.owner =	THIS_MODULE,
+	.open =		etb_open,
+	.read =		etb_read,
+	.release =	etb_release,
+};
+
+static struct miscdevice etb_misc = {
+	.name =		"msm_etb",
+	.minor =	MISC_DYNAMIC_MINOR,
+	.fops =		&etb_fops,
+};
+
+static int __devinit etb_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -EINVAL;
+		goto err_res;
+	}
+
+	etb.base = ioremap_nocache(res->start, resource_size(res));
+	if (!etb.base) {
+		ret = -EINVAL;
+		goto err_ioremap;
+	}
+
+	etb.dev = &pdev->dev;
+
+	ret = misc_register(&etb_misc);
+	if (ret)
+		goto err_misc;
+
+	etb.buf = kzalloc(ETB_SIZE_WORDS * BYTES_PER_WORD, GFP_KERNEL);
+	if (!etb.buf) {
+		ret = -ENOMEM;
+		goto err_alloc;
+	}
+
+	mutex_init(&etb.lock);
+
+	return 0;
+
+err_alloc:
+	misc_deregister(&etb_misc);
+err_misc:
+	iounmap(etb.base);
+err_ioremap:
+err_res:
+	return ret;
+}
+
+static int __devexit etb_remove(struct platform_device *pdev)
+{
+	if (etb.enabled)
+		etb_disable();
+	mutex_destroy(&etb.lock);
+	kfree(etb.buf);
+	misc_deregister(&etb_misc);
+	iounmap(etb.base);
+
+	return 0;
+}
+
+static struct platform_driver etb_driver = {
+	.probe          = etb_probe,
+	.remove         = __devexit_p(etb_remove),
+	.driver         = {
+		.name   = "msm_etb",
+	},
+};
+
+static int __init etb_init(void)
+{
+	return platform_driver_register(&etb_driver);
+}
+module_init(etb_init);
+
+static void __exit etb_exit(void)
+{
+	platform_driver_unregister(&etb_driver);
+}
+module_exit(etb_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Coresight Embedded Trace Buffer");
diff --git a/arch/arm/mach-msm/qdss-funnel.c b/arch/arm/mach-msm/qdss-funnel.c
new file mode 100644
index 0000000..9ad407b
--- /dev/null
+++ b/arch/arm/mach-msm/qdss-funnel.c
@@ -0,0 +1,159 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include "qdss.h"
+
+#define funnel_writel(funnel, id, val, off)	\
+			__raw_writel((val), funnel.base + (SZ_4K * id) + off)
+#define funnel_readl(funnel, id, off)		\
+			__raw_readl(funnel.base + (SZ_4K * id) + off)
+
+#define CS_TFUNNEL_FUNCTL		(0x000)
+#define CS_TFUNNEL_PRICTL		(0x004)
+#define CS_TFUNNEL_ITATBDATA0		(0xEEC)
+#define CS_TFUNNEL_ITATBCTR2		(0xEF0)
+#define CS_TFUNNEL_ITATBCTR1		(0xEF4)
+#define CS_TFUNNEL_ITATBCTR0		(0xEF8)
+
+
+#define FUNNEL_LOCK(id)							\
+do {									\
+	mb();								\
+	funnel_writel(funnel, id, MAGIC2, CS_LAR);			\
+} while (0)
+#define FUNNEL_UNLOCK(id)						\
+do {									\
+	funnel_writel(funnel, id, MAGIC1, CS_LAR);			\
+	mb();								\
+} while (0)
+
+#define DEFAULT_HOLDTIME_MASK		(0xF00)
+#define DEFAULT_HOLDTIME_SHFT		(0x8)
+#define DEFAULT_HOLDTIME		(0x7 << DEFAULT_HOLDTIME_SHFT)
+#define DEFAULT_PRIORITY		(0xFAC680)
+
+struct funnel_ctx {
+	void __iomem	*base;
+	bool		enabled;
+	struct device	*dev;
+};
+
+static struct funnel_ctx funnel;
+
+static void __funnel_enable(uint8_t id, uint32_t port_mask)
+{
+	uint32_t functl;
+
+	FUNNEL_UNLOCK(id);
+
+	functl = funnel_readl(funnel, id, CS_TFUNNEL_FUNCTL);
+	functl &= ~DEFAULT_HOLDTIME_MASK;
+	functl |= DEFAULT_HOLDTIME;
+	functl |= port_mask;
+	funnel_writel(funnel, id, functl, CS_TFUNNEL_FUNCTL);
+	funnel_writel(funnel, id, DEFAULT_PRIORITY, CS_TFUNNEL_PRICTL);
+
+	FUNNEL_LOCK(id);
+}
+
+void funnel_enable(uint8_t id, uint32_t port_mask)
+{
+	__funnel_enable(id, port_mask);
+	funnel.enabled = true;
+	dev_info(funnel.dev, "funnel port mask 0x%lx enabled\n",
+					(unsigned long) port_mask);
+}
+
+static void __funnel_disable(uint8_t id, uint32_t port_mask)
+{
+	uint32_t functl;
+
+	FUNNEL_UNLOCK(id);
+
+	functl = funnel_readl(funnel, id, CS_TFUNNEL_FUNCTL);
+	functl &= ~port_mask;
+	funnel_writel(funnel, id, functl, CS_TFUNNEL_FUNCTL);
+
+	FUNNEL_LOCK(id);
+}
+
+void funnel_disable(uint8_t id, uint32_t port_mask)
+{
+	__funnel_disable(id, port_mask);
+	funnel.enabled = false;
+	dev_info(funnel.dev, "funnel port mask 0x%lx disabled\n",
+					(unsigned long) port_mask);
+}
+
+static int __devinit funnel_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -EINVAL;
+		goto err_res;
+	}
+
+	funnel.base = ioremap_nocache(res->start, resource_size(res));
+	if (!funnel.base) {
+		ret = -EINVAL;
+		goto err_ioremap;
+	}
+
+	funnel.dev = &pdev->dev;
+
+	return 0;
+
+err_ioremap:
+err_res:
+	return ret;
+}
+
+static int __devexit funnel_remove(struct platform_device *pdev)
+{
+	if (funnel.enabled)
+		funnel_disable(0x0, 0xFF);
+	iounmap(funnel.base);
+
+	return 0;
+}
+
+static struct platform_driver funnel_driver = {
+	.probe          = funnel_probe,
+	.remove         = __devexit_p(funnel_remove),
+	.driver         = {
+		.name   = "msm_funnel",
+	},
+};
+
+static int __init funnel_init(void)
+{
+	return platform_driver_register(&funnel_driver);
+}
+module_init(funnel_init);
+
+static void __exit funnel_exit(void)
+{
+	platform_driver_unregister(&funnel_driver);
+}
+module_exit(funnel_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Coresight Funnel");
diff --git a/arch/arm/mach-msm/qdss-ptm.c b/arch/arm/mach-msm/qdss-ptm.c
new file mode 100644
index 0000000..ffd0b8d
--- /dev/null
+++ b/arch/arm/mach-msm/qdss-ptm.c
@@ -0,0 +1,1021 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/smp.h>
+#include <linux/percpu.h>
+#include <linux/wakelock.h>
+#include <linux/pm_qos_params.h>
+#include <linux/clk.h>
+#include <asm/atomic.h>
+
+#include "qdss.h"
+
+#define ptm_writel(ptm, cpu, val, off)	\
+			__raw_writel((val), ptm.base + (SZ_4K * cpu) + off)
+#define ptm_readl(ptm, cpu, off)	\
+			__raw_readl(ptm.base + (SZ_4K * cpu) + off)
+
+/*
+ * Device registers:
+ * 0x000 - 0x2FC: Trace		registers
+ * 0x300 - 0x314: Management	registers
+ * 0x318 - 0xEFC: Trace		registers
+ *
+ * Coresight registers
+ * 0xF00 - 0xF9C: Management	registers
+ * 0xFA0 - 0xFA4: Management	registers in PFTv1.0
+ *		  Trace		registers in PFTv1.1
+ * 0xFA8 - 0xFFC: Management	registers
+ */
+
+/* Trace registers (0x000-0x2FC) */
+#define ETMCR			(0x000)
+#define ETMCCR			(0x004)
+#define ETMTRIGGER		(0x008)
+#define ETMSR			(0x010)
+#define ETMSCR			(0x014)
+#define ETMTSSCR		(0x018)
+#define ETMTEEVR		(0x020)
+#define ETMTECR1		(0x024)
+#define ETMFFLR			(0x02C)
+#define ETMACVRn(n)		(0x040 + (n * 4))
+#define ETMACTRn(n)		(0x080 + (n * 4))
+#define ETMCNTRLDVRn(n)		(0x140 + (n * 4))
+#define ETMCNTENRn(n)		(0x150 + (n * 4))
+#define ETMCNTRLDEVRn(n)	(0x160 + (n * 4))
+#define ETMCNTVRn(n)		(0x170 + (n * 4))
+#define ETMSQ12EVR		(0x180)
+#define ETMSQ21EVR		(0x184)
+#define ETMSQ23EVR		(0x188)
+#define ETMSQ32EVR		(0x18C)
+#define ETMSQ13EVR		(0x190)
+#define ETMSQ31EVR		(0x194)
+#define ETMSQR			(0x19C)
+#define ETMEXTOUTEVRn(n)	(0x1A0 + (n * 4))
+#define ETMCIDCVRn(n)		(0x1B0 + (n * 4))
+#define ETMCIDCMR		(0x1BC)
+#define ETMIMPSPEC0		(0x1C0)
+#define ETMIMPSPEC1		(0x1C4)
+#define ETMIMPSPEC2		(0x1C8)
+#define ETMIMPSPEC3		(0x1CC)
+#define ETMIMPSPEC4		(0x1D0)
+#define ETMIMPSPEC5		(0x1D4)
+#define ETMIMPSPEC6		(0x1D8)
+#define ETMIMPSPEC7		(0x1DC)
+#define ETMSYNCFR		(0x1E0)
+#define ETMIDR			(0x1E4)
+#define ETMCCER			(0x1E8)
+#define ETMEXTINSELR		(0x1EC)
+#define ETMTESSEICR		(0x1F0)
+#define ETMEIBCR		(0x1F4)
+#define ETMTSEVR		(0x1F8)
+#define ETMAUXCR		(0x1FC)
+#define ETMTRACEIDR		(0x200)
+#define ETMVMIDCVR		(0x204)
+/* Management registers (0x300-0x314) */
+#define ETMOSLAR		(0x300)
+#define ETMOSLSR		(0x304)
+#define ETMOSSRR		(0x308)
+#define ETMPDCR			(0x310)
+#define ETMPDSR			(0x314)
+
+
+#define PTM_LOCK(cpu)							\
+do {									\
+	mb();								\
+	ptm_writel(ptm, cpu, MAGIC2, CS_LAR);				\
+} while (0)
+#define PTM_UNLOCK(cpu)							\
+do {									\
+	ptm_writel(ptm, cpu, MAGIC1, CS_LAR);				\
+	mb();								\
+} while (0)
+
+#define PTM_OS_LOCK(cpu)						\
+do {									\
+	ptm_writel(ptm, cpu, MAGIC1, ETMOSLAR);				\
+	mb();								\
+} while (0)
+#define PTM_OS_UNLOCK(cpu)						\
+do {									\
+	ptm_writel(ptm, cpu, MAGIC2, ETMOSLAR);				\
+	mb();								\
+} while (0)
+
+#define MAX_TRACE_REGS		(78)
+#define MAX_STATE_SIZE		(MAX_TRACE_REGS * num_possible_cpus())
+
+/* Forward declarations */
+static void ptm_cfg_rw_init(void);
+
+static int trace_on_boot;
+module_param_named(
+	trace_on_boot, trace_on_boot, int, S_IRUGO
+);
+
+struct ptm_config {
+	/* read only config registers */
+	uint32_t	config_code;
+	/* derived values */
+	uint8_t		nr_addr_comp;
+	uint8_t		nr_cntr;
+	uint8_t		nr_ext_input;
+	uint8_t		nr_ext_output;
+	uint8_t		nr_context_id_comp;
+
+	uint32_t	config_code_extn;
+	/* derived values */
+	uint8_t		nr_extnd_ext_input_sel;
+	uint8_t		nr_instr_resources;
+
+	uint32_t	system_config;
+	/* derived values */
+	uint8_t		fifofull_supported;
+	uint8_t		nr_procs_supported;
+
+	/* read-write registers */
+	uint32_t	main_control;
+	uint32_t	trigger_event;
+	uint32_t	te_start_stop_control;
+	uint32_t	te_event;
+	uint32_t	te_control;
+	uint32_t	fifofull_level;
+	uint32_t	addr_comp_value[16];
+	uint32_t	addr_comp_access_type[16];
+	uint32_t	cntr_reload_value[4];
+	uint32_t	cntr_enable_event[4];
+	uint32_t	cntr_reload_event[4];
+	uint32_t	cntr_value[4];
+	uint32_t	seq_state_12_event;
+	uint32_t	seq_state_21_event;
+	uint32_t	seq_state_23_event;
+	uint32_t	seq_state_32_event;
+	uint32_t	seq_state_13_event;
+	uint32_t	seq_state_31_event;
+	uint32_t	current_seq_state;
+	uint32_t	ext_output_event[4];
+	uint32_t	context_id_comp_value[3];
+	uint32_t	context_id_comp_mask;
+	uint32_t	sync_freq;
+	uint32_t	extnd_ext_input_sel;
+	uint32_t	ts_event;
+	uint32_t	aux_control;
+	uint32_t	coresight_trace_id;
+	uint32_t	vmid_comp_value;
+};
+
+struct ptm_ctx {
+	struct ptm_config		cfg;
+	void __iomem			*base;
+	uint32_t			*state;
+	bool				trace_enabled;
+	int				*cpu_restore;
+	struct wake_lock		wake_lock;
+	struct pm_qos_request_list	qos_req;
+	atomic_t			in_use;
+	struct device			*dev;
+	struct clk			*qdss_at_clk;
+	struct clk			*qdss_pclkdbg_clk;
+	struct clk			*qdss_pclk;
+	struct clk			*qdss_traceclkin_clk;
+	struct clk			*qdss_tsctr_clk;
+};
+
+static struct ptm_ctx ptm;
+
+/* Memory mapped writes to clear os lock don't work */
+static void ptm_os_unlock(void *unused)
+{
+	unsigned long value = 0x0;
+
+	asm("mcr p14, 1, %0, c1, c0, 4\n\t" : : "r" (value));
+	asm("isb\n\t");
+}
+
+static int ptm_clock_enable(void)
+{
+	int ret;
+
+	ret = clk_enable(ptm.qdss_at_clk);
+	if (WARN(ret, "qdss_at_clk not enabled (%d)\n", ret))
+		goto err;
+
+	ret = clk_enable(ptm.qdss_pclkdbg_clk);
+	if (WARN(ret, "qdss_pclkdbg_clk not enabled (%d)\n", ret))
+		goto err_pclkdbg;
+
+	ret = clk_enable(ptm.qdss_pclk);
+	if (WARN(ret, "qdss_pclk not enabled (%d)\n", ret))
+		goto err_pclk;
+
+	ret = clk_enable(ptm.qdss_traceclkin_clk);
+	if (WARN(ret, "qdss_traceclkin_clk not enabled (%d)\n", ret))
+		goto err_traceclkin;
+
+	ret = clk_enable(ptm.qdss_tsctr_clk);
+	if (WARN(ret, "qdss_tsctr_clk not enabled (%d)\n", ret))
+		goto err_tsctr;
+
+	return 0;
+
+err_tsctr:
+	clk_disable(ptm.qdss_traceclkin_clk);
+err_traceclkin:
+	clk_disable(ptm.qdss_pclk);
+err_pclk:
+	clk_disable(ptm.qdss_pclkdbg_clk);
+err_pclkdbg:
+	clk_disable(ptm.qdss_at_clk);
+err:
+	return ret;
+}
+
+static void ptm_clock_disable(void)
+{
+	clk_disable(ptm.qdss_tsctr_clk);
+	clk_disable(ptm.qdss_traceclkin_clk);
+	clk_disable(ptm.qdss_pclk);
+	clk_disable(ptm.qdss_pclkdbg_clk);
+	clk_disable(ptm.qdss_at_clk);
+}
+
+static void ptm_set_powerdown(int cpu)
+{
+	uint32_t etmcr;
+
+	etmcr = ptm_readl(ptm, cpu, ETMCR);
+	etmcr |= BIT(0);
+	ptm_writel(ptm, cpu, etmcr, ETMCR);
+}
+
+static void ptm_clear_powerdown(int cpu)
+{
+	uint32_t etmcr;
+
+	etmcr = ptm_readl(ptm, cpu, ETMCR);
+	etmcr &= ~BIT(0);
+	ptm_writel(ptm, cpu, etmcr, ETMCR);
+}
+
+static void ptm_set_prog(int cpu)
+{
+	uint32_t etmcr;
+	int count;
+
+	etmcr = ptm_readl(ptm, cpu, ETMCR);
+	etmcr |= BIT(10);
+	ptm_writel(ptm, cpu, etmcr, ETMCR);
+
+	for (count = TIMEOUT_US; BVAL(ptm_readl(ptm, cpu, ETMSR), 1) != 1
+				&& count > 0; count--)
+		udelay(1);
+	WARN(count == 0, "timeout while setting prog bit\n");
+}
+
+static void ptm_clear_prog(int cpu)
+{
+	uint32_t etmcr;
+	int count;
+
+	etmcr = ptm_readl(ptm, cpu, ETMCR);
+	etmcr &= ~BIT(10);
+	ptm_writel(ptm, cpu, etmcr, ETMCR);
+
+	for (count = TIMEOUT_US; BVAL(ptm_readl(ptm, cpu, ETMSR), 1) != 0
+				&& count > 0; count--)
+		udelay(1);
+	WARN(count == 0, "timeout while clearing prog bit\n");
+}
+
+static void __ptm_trace_enable(void)
+{
+	int i, cpu;
+
+	for_each_online_cpu(cpu) {
+		PTM_UNLOCK(cpu);
+		ptm_clear_powerdown(cpu);
+		ptm_set_prog(cpu);
+
+		ptm_writel(ptm, cpu, ptm.cfg.main_control | BIT(10), ETMCR);
+		ptm_writel(ptm, cpu, ptm.cfg.trigger_event, ETMTRIGGER);
+		ptm_writel(ptm, cpu, ptm.cfg.te_start_stop_control, ETMTSSCR);
+		ptm_writel(ptm, cpu, ptm.cfg.te_event, ETMTEEVR);
+		ptm_writel(ptm, cpu, ptm.cfg.te_control, ETMTECR1);
+		ptm_writel(ptm, cpu, ptm.cfg.fifofull_level, ETMFFLR);
+		for (i = 0; i < ptm.cfg.nr_addr_comp; i++) {
+			ptm_writel(ptm, cpu, ptm.cfg.addr_comp_value[i],
+							ETMACVRn(i));
+			ptm_writel(ptm, cpu, ptm.cfg.addr_comp_access_type[i],
+							ETMACTRn(i));
+		}
+		for (i = 0; i < ptm.cfg.nr_cntr; i++) {
+			ptm_writel(ptm, cpu, ptm.cfg.cntr_reload_value[i],
+							ETMCNTRLDVRn(i));
+			ptm_writel(ptm, cpu, ptm.cfg.cntr_enable_event[i],
+							ETMCNTENRn(i));
+			ptm_writel(ptm, cpu, ptm.cfg.cntr_reload_event[i],
+							ETMCNTRLDEVRn(i));
+			ptm_writel(ptm, cpu, ptm.cfg.cntr_value[i],
+							ETMCNTVRn(i));
+		}
+		ptm_writel(ptm, cpu, ptm.cfg.seq_state_12_event, ETMSQ12EVR);
+		ptm_writel(ptm, cpu, ptm.cfg.seq_state_21_event, ETMSQ21EVR);
+		ptm_writel(ptm, cpu, ptm.cfg.seq_state_23_event, ETMSQ23EVR);
+		ptm_writel(ptm, cpu, ptm.cfg.seq_state_32_event, ETMSQ32EVR);
+		ptm_writel(ptm, cpu, ptm.cfg.seq_state_13_event, ETMSQ13EVR);
+		ptm_writel(ptm, cpu, ptm.cfg.seq_state_31_event, ETMSQ31EVR);
+		ptm_writel(ptm, cpu, ptm.cfg.current_seq_state, ETMSQR);
+		for (i = 0; i < ptm.cfg.nr_ext_output; i++)
+			ptm_writel(ptm, cpu, ptm.cfg.ext_output_event[i],
+							ETMEXTOUTEVRn(i));
+		for (i = 0; i < ptm.cfg.nr_context_id_comp; i++)
+			ptm_writel(ptm, cpu, ptm.cfg.context_id_comp_value[i],
+							ETMCIDCVRn(i));
+		ptm_writel(ptm, cpu, ptm.cfg.context_id_comp_mask, ETMCIDCMR);
+		ptm_writel(ptm, cpu, ptm.cfg.sync_freq, ETMSYNCFR);
+		ptm_writel(ptm, cpu, ptm.cfg.extnd_ext_input_sel, ETMEXTINSELR);
+		ptm_writel(ptm, cpu, ptm.cfg.ts_event, ETMTSEVR);
+		ptm_writel(ptm, cpu, ptm.cfg.aux_control, ETMAUXCR);
+		ptm_writel(ptm, cpu, ptm.cfg.vmid_comp_value, ETMVMIDCVR);
+
+		ptm_clear_prog(cpu);
+		PTM_LOCK(cpu);
+	}
+}
+
+static int ptm_trace_enable(void)
+{
+	int ret;
+
+	ret = ptm_clock_enable();
+	if (ret)
+		return ret;
+
+	wake_lock(&ptm.wake_lock);
+	/* 1. causes all cpus to come out of idle PC
+	 * 2. prevents idle PC until save restore flag is enabled atomically
+	 */
+	pm_qos_update_request(&ptm.qos_req, 0);
+
+	etb_disable();
+	tpiu_disable();
+	/* enable ETB first to avoid loosing any trace data */
+	etb_enable();
+	funnel_enable(0x0, 0x3);
+	ptm_os_unlock(NULL);
+	smp_call_function(ptm_os_unlock, NULL, 1);
+	__ptm_trace_enable();
+
+	ptm.trace_enabled = true;
+
+	pm_qos_update_request(&ptm.qos_req, PM_QOS_DEFAULT_VALUE);
+	wake_unlock(&ptm.wake_lock);
+
+	return 0;
+}
+
+static void __ptm_trace_disable(void)
+{
+	int cpu;
+
+	for_each_online_cpu(cpu) {
+		PTM_UNLOCK(cpu);
+		ptm_set_prog(cpu);
+
+		/* program trace enable to low by using always false event */
+		ptm_writel(ptm, cpu, 0x6F | BIT(14), ETMTEEVR);
+
+		ptm_set_powerdown(cpu);
+		PTM_LOCK(cpu);
+	}
+}
+
+static void ptm_trace_disable(void)
+{
+	int cpu;
+
+	wake_lock(&ptm.wake_lock);
+	/* 1. causes all cpus to come out of idle PC
+	 * 2. prevents idle PC until save restore flag is disabled atomically
+	 */
+	pm_qos_update_request(&ptm.qos_req, 0);
+
+	ptm_os_unlock(NULL);
+	smp_call_function(ptm_os_unlock, NULL, 1);
+	__ptm_trace_disable();
+	etb_dump();
+	etb_disable();
+	funnel_disable(0x0, 0x3);
+
+	ptm.trace_enabled = false;
+
+	for_each_online_cpu(cpu)
+		*per_cpu_ptr(ptm.cpu_restore, cpu) = 0;
+
+	pm_qos_update_request(&ptm.qos_req, PM_QOS_DEFAULT_VALUE);
+	wake_unlock(&ptm.wake_lock);
+
+	ptm_clock_disable();
+}
+
+static int ptm_open(struct inode *inode, struct file *file)
+{
+	if (atomic_cmpxchg(&ptm.in_use, 0, 1))
+		return -EBUSY;
+
+	dev_dbg(ptm.dev, "%s: successfully opened\n", __func__);
+	return 0;
+}
+
+static void ptm_range_filter(char range, uint32_t reg1,
+				uint32_t addr1, uint32_t reg2, uint32_t addr2)
+{
+	ptm.cfg.addr_comp_value[reg1] = addr1;
+	ptm.cfg.addr_comp_value[reg2] = addr2;
+
+	ptm.cfg.te_control |= (1 << (reg1/2));
+	if (range == 'i')
+		ptm.cfg.te_control &= ~BIT(24);
+	else if (range == 'e')
+		ptm.cfg.te_control |= BIT(24);
+}
+
+static void ptm_start_stop_filter(char start_stop,
+				uint32_t reg, uint32_t addr)
+{
+	ptm.cfg.addr_comp_value[reg] = addr;
+
+	if (start_stop == 's')
+		ptm.cfg.te_start_stop_control |= (1 << reg);
+	else if (start_stop == 't')
+		ptm.cfg.te_start_stop_control |= (1 << (reg + 16));
+
+	ptm.cfg.te_control |= BIT(25);
+}
+
+#define MAX_COMMAND_STRLEN  40
+static ssize_t ptm_write(struct file *file, const char __user *data,
+				size_t len, loff_t *ppos)
+{
+	char command[MAX_COMMAND_STRLEN];
+	int str_len;
+	unsigned long reg1, reg2;
+	unsigned long addr1, addr2;
+
+	str_len = strnlen_user(data, MAX_COMMAND_STRLEN);
+	dev_dbg(ptm.dev, "string length: %d", str_len);
+	if (str_len == 0 || str_len == (MAX_COMMAND_STRLEN+1)) {
+		dev_err(ptm.dev, "error in str_len: %d", str_len);
+		return -EFAULT;
+	}
+	/* includes the null character */
+	if (copy_from_user(command, data, str_len)) {
+		dev_err(ptm.dev, "error in copy_from_user: %d", str_len);
+		return -EFAULT;
+	}
+
+	dev_dbg(ptm.dev, "input = %s", command);
+
+	switch (command[0]) {
+	case '0':
+		if (ptm.trace_enabled) {
+			ptm_trace_disable();
+			dev_info(ptm.dev, "tracing disabled\n");
+		} else
+			dev_err(ptm.dev, "trace already disabled\n");
+
+		break;
+	case '1':
+		if (!ptm.trace_enabled) {
+			if (!ptm_trace_enable())
+				dev_info(ptm.dev, "tracing enabled\n");
+			else
+				dev_err(ptm.dev, "error enabling trace\n");
+		} else
+			dev_err(ptm.dev, "trace already enabled\n");
+		break;
+	case 'f':
+		switch (command[2]) {
+		case 'i':
+			switch (command[4]) {
+			case 'i':
+				if (sscanf(&command[6], "%lx:%lx:%lx:%lx\\0",
+					&reg1, &addr1, &reg2, &addr2) != 4)
+					goto err_out;
+				if (reg1 > 7 || reg2 > 7 || (reg1 % 2))
+					goto err_out;
+				ptm_range_filter('i',
+						reg1, addr1, reg2, addr2);
+				break;
+			case 'e':
+				if (sscanf(&command[6], "%lx:%lx:%lx:%lx\\0",
+					&reg1, &addr1, &reg2, &addr2) != 4)
+					goto err_out;
+				if (reg1 > 7 || reg2 > 7 || (reg1 % 2)
+					|| command[2] == 'd')
+					goto err_out;
+				ptm_range_filter('e',
+						reg1, addr1, reg2, addr2);
+				break;
+			case 's':
+				if (sscanf(&command[6], "%lx:%lx\\0",
+					&reg1, &addr1) != 2)
+					goto err_out;
+				if (reg1 > 7)
+					goto err_out;
+				ptm_start_stop_filter('s', reg1, addr1);
+				break;
+			case 't':
+				if (sscanf(&command[6], "%lx:%lx\\0",
+						&reg1, &addr1) != 2)
+					goto err_out;
+				if (reg1 > 7)
+					goto err_out;
+				ptm_start_stop_filter('t', reg1, addr1);
+				break;
+			default:
+				goto err_out;
+			}
+			break;
+		case 'r':
+			ptm_cfg_rw_init();
+			break;
+		default:
+			goto err_out;
+		}
+		break;
+	default:
+		goto err_out;
+	}
+
+	return len;
+
+err_out:
+	return -EFAULT;
+}
+
+static int ptm_release(struct inode *inode, struct file *file)
+{
+	atomic_set(&ptm.in_use, 0);
+	dev_dbg(ptm.dev, "%s: released\n", __func__);
+	return 0;
+}
+
+static const struct file_operations ptm_fops = {
+	.owner =	THIS_MODULE,
+	.open =		ptm_open,
+	.write =	ptm_write,
+	.release =	ptm_release,
+};
+
+static struct miscdevice ptm_misc = {
+	.name =		"msm_ptm",
+	.minor =	MISC_DYNAMIC_MINOR,
+	.fops =		&ptm_fops,
+};
+
+static void ptm_save_reg(int cpu)
+{
+	uint32_t i;
+	int j;
+
+	PTM_UNLOCK(cpu);
+
+	i = cpu * MAX_TRACE_REGS;
+
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMCR);
+	ptm_set_prog(cpu);
+
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMTRIGGER);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMSR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMTSSCR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMTEEVR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMTECR1);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMFFLR);
+	for (j = 0; j < ptm.cfg.nr_addr_comp; j++) {
+		ptm.state[i++] = ptm_readl(ptm, cpu, ETMACVRn(j));
+		ptm.state[i++] = ptm_readl(ptm, cpu, ETMACTRn(j));
+	}
+	for (j = 0; j < ptm.cfg.nr_cntr; j++) {
+		ptm.state[i++] = ptm_readl(ptm, cpu, ETMCNTRLDVRn(j));
+		ptm.state[i++] = ptm_readl(ptm, cpu, ETMCNTENRn(j));
+		ptm.state[i++] = ptm_readl(ptm, cpu, ETMCNTRLDEVRn(j));
+		ptm.state[i++] = ptm_readl(ptm, cpu, ETMCNTVRn(j));
+	}
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMSQ12EVR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMSQ21EVR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMSQ23EVR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMSQ32EVR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMSQ13EVR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMSQ31EVR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMSQR);
+	for (j = 0; j < ptm.cfg.nr_ext_output; j++)
+		ptm.state[i++] = ptm_readl(ptm, cpu, ETMEXTOUTEVRn(j));
+	for (j = 0; j < ptm.cfg.nr_context_id_comp; j++)
+		ptm.state[i++] = ptm_readl(ptm, cpu, ETMCIDCVRn(j));
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMCIDCMR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMSYNCFR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMEXTINSELR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMTSEVR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMAUXCR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, ETMVMIDCVR);
+	ptm.state[i++] = ptm_readl(ptm, cpu, CS_CLAIMSET);
+	ptm.state[i++] = ptm_readl(ptm, cpu, CS_CLAIMCLR);
+
+	PTM_LOCK(cpu);
+}
+
+static void ptm_restore_reg(int cpu)
+{
+	uint32_t i;
+	int j;
+
+	ptm_os_unlock(NULL);
+	PTM_UNLOCK(cpu);
+
+	i = cpu * MAX_TRACE_REGS;
+
+	ptm_clear_powerdown(cpu);
+	ptm_set_prog(cpu);
+	/* Ensure prog bit doesn't get cleared since we have set it above.
+	 * Power down bit should already be clear in the saved state.
+	 */
+	ptm_writel(ptm, cpu, ptm.state[i++] | BIT(10), ETMCR);
+
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMTRIGGER);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMSR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMTSSCR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMTEEVR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMTECR1);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMFFLR);
+	for (j = 0; j < ptm.cfg.nr_addr_comp; j++) {
+		ptm_writel(ptm, cpu, ptm.state[i++], ETMACVRn(j));
+		ptm_writel(ptm, cpu, ptm.state[i++], ETMACTRn(j));
+	}
+	for (j = 0; j < ptm.cfg.nr_cntr; j++) {
+		ptm_writel(ptm, cpu, ptm.state[i++], ETMCNTRLDVRn(j));
+		ptm_writel(ptm, cpu, ptm.state[i++], ETMCNTENRn(j));
+		ptm_writel(ptm, cpu, ptm.state[i++], ETMCNTRLDEVRn(j));
+		ptm_writel(ptm, cpu, ptm.state[i++], ETMCNTVRn(j));
+	}
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMSQ12EVR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMSQ21EVR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMSQ23EVR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMSQ32EVR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMSQ13EVR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMSQ31EVR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMSQR);
+	for (j = 0; j < ptm.cfg.nr_ext_output; j++)
+		ptm_writel(ptm, cpu, ptm.state[i++], ETMEXTOUTEVRn(j));
+	for (j = 0; j < ptm.cfg.nr_context_id_comp; j++)
+		ptm_writel(ptm, cpu, ptm.state[i++], ETMCIDCVRn(j));
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMCIDCMR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMSYNCFR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMEXTINSELR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMTSEVR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMAUXCR);
+	ptm_writel(ptm, cpu, ptm.state[i++], ETMVMIDCVR);
+	ptm_writel(ptm, cpu, ptm.state[i++], CS_CLAIMSET);
+	ptm_writel(ptm, cpu, ptm.state[i++], CS_CLAIMCLR);
+
+	ptm_clear_prog(cpu);
+
+	PTM_LOCK(cpu);
+}
+
+/* etm_save_reg_check and etm_restore_reg_check should be fast
+ *
+ * These functions will be called either from:
+ * 1. per_cpu idle thread context for idle power collapses.
+ * 2. per_cpu idle thread context for hotplug/suspend power collapse for
+ *    nonboot cpus.
+ * 3. suspend thread context for core0.
+ *
+ * In all cases we are guaranteed to be running on the same cpu for the
+ * entire duration.
+ *
+ * Another assumption is that etm registers won't change after trace_enabled
+ * is set. Current usage model guarantees this doesn't happen.
+ */
+void etm_save_reg_check(void)
+{
+	/* Disabling all kinds of power_collapses when enabling and disabling
+	 * trace provides mutual exclusion to be able to safely access
+	 * ptm.trace_enabled here.
+	 */
+	if (ptm.trace_enabled) {
+		int cpu = smp_processor_id();
+
+		/* Don't save the registers if we just got called from per_cpu
+		 * idle thread context of a nonboot cpu after hotplug/suspend
+		 * power collapse. This is to prevent corruption due to saving
+		 * twice since nonboot cpus start out fresh without the
+		 * corresponding restore.
+		 */
+		if (!(*per_cpu_ptr(ptm.cpu_restore, cpu))) {
+			ptm_save_reg(cpu);
+			*per_cpu_ptr(ptm.cpu_restore, cpu) = 1;
+		}
+	}
+}
+
+void etm_restore_reg_check(void)
+{
+	/* Disabling all kinds of power_collapses when enabling and disabling
+	 * trace provides mutual exclusion to be able to safely access
+	 * ptm.trace_enabled here.
+	 */
+	if (ptm.trace_enabled) {
+		int cpu = smp_processor_id();
+
+		ptm_restore_reg(cpu);
+		*per_cpu_ptr(ptm.cpu_restore, cpu) = 0;
+	}
+}
+
+static void ptm_cfg_rw_init(void)
+{
+	int i;
+
+	ptm.cfg.main_control =				0x00001000;
+	ptm.cfg.trigger_event =				0x0000406F;
+	ptm.cfg.te_start_stop_control =			0x00000000;
+	ptm.cfg.te_event =				0x0000006F;
+	ptm.cfg.te_control =				0x01000000;
+	ptm.cfg.fifofull_level =			0x00000028;
+	for (i = 0; i < ptm.cfg.nr_addr_comp; i++) {
+		ptm.cfg.addr_comp_value[i] =		0x00000000;
+		ptm.cfg.addr_comp_access_type[i] =	0x00000000;
+	}
+	for (i = 0; i < ptm.cfg.nr_cntr; i++) {
+		ptm.cfg.cntr_reload_value[i] =		0x00000000;
+		ptm.cfg.cntr_enable_event[i] =		0x0000406F;
+		ptm.cfg.cntr_reload_event[i] =		0x0000406F;
+		ptm.cfg.cntr_value[i] =			0x00000000;
+	}
+	ptm.cfg.seq_state_12_event =			0x0000406F;
+	ptm.cfg.seq_state_21_event =			0x0000406F;
+	ptm.cfg.seq_state_23_event =			0x0000406F;
+	ptm.cfg.seq_state_32_event =			0x0000406F;
+	ptm.cfg.seq_state_13_event =			0x0000406F;
+	ptm.cfg.seq_state_31_event =			0x0000406F;
+	ptm.cfg.current_seq_state =			0x00000000;
+	for (i = 0; i < ptm.cfg.nr_ext_output; i++)
+		ptm.cfg.ext_output_event[i] =		0x0000406F;
+	for (i = 0; i < ptm.cfg.nr_context_id_comp; i++)
+		ptm.cfg.context_id_comp_value[i] =	0x00000000;
+	ptm.cfg.context_id_comp_mask =			0x00000000;
+	ptm.cfg.sync_freq =				0x00000080;
+	ptm.cfg.extnd_ext_input_sel =			0x00000000;
+	ptm.cfg.ts_event =				0x0000406F;
+	ptm.cfg.aux_control =				0x00000000;
+	ptm.cfg.vmid_comp_value =			0x00000000;
+}
+
+static void ptm_cfg_ro_init(void)
+{
+	/* use cpu 0 for setup */
+	int cpu = 0;
+
+	ptm_os_unlock(NULL);
+	smp_call_function(ptm_os_unlock, NULL, 1);
+	PTM_UNLOCK(cpu);
+	ptm_clear_powerdown(cpu);
+	ptm_set_prog(cpu);
+
+	/* find all capabilities */
+	ptm.cfg.config_code	=	ptm_readl(ptm, cpu, ETMCCR);
+	ptm.cfg.nr_addr_comp =		BMVAL(ptm.cfg.config_code, 0, 3) * 2;
+	ptm.cfg.nr_cntr =		BMVAL(ptm.cfg.config_code, 13, 15);
+	ptm.cfg.nr_ext_input =		BMVAL(ptm.cfg.config_code, 17, 19);
+	ptm.cfg.nr_ext_output =		BMVAL(ptm.cfg.config_code, 20, 22);
+	ptm.cfg.nr_context_id_comp =	BMVAL(ptm.cfg.config_code, 24, 25);
+
+	ptm.cfg.config_code_extn =	ptm_readl(ptm, cpu, ETMCCER);
+	ptm.cfg.nr_extnd_ext_input_sel =
+					BMVAL(ptm.cfg.config_code_extn, 0, 2);
+	ptm.cfg.nr_instr_resources =	BMVAL(ptm.cfg.config_code_extn, 13, 15);
+
+	ptm.cfg.system_config =		ptm_readl(ptm, cpu, ETMSCR);
+	ptm.cfg.fifofull_supported =	BVAL(ptm.cfg.system_config, 8);
+	ptm.cfg.nr_procs_supported =	BMVAL(ptm.cfg.system_config, 12, 14);
+
+	ptm_set_powerdown(cpu);
+	PTM_LOCK(cpu);
+}
+
+static int __devinit ptm_probe(struct platform_device *pdev)
+{
+	int ret, cpu;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -EINVAL;
+		goto err_res;
+	}
+
+	ptm.base = ioremap_nocache(res->start, resource_size(res));
+	if (!ptm.base) {
+		ret = -EINVAL;
+		goto err_ioremap;
+	}
+
+	ptm.dev = &pdev->dev;
+
+	ptm.state = kzalloc(MAX_STATE_SIZE * sizeof(uint32_t), GFP_KERNEL);
+	if (!ptm.state) {
+		ret = -ENOMEM;
+		goto err_kzalloc;
+	}
+
+	ret = misc_register(&ptm_misc);
+	if (ret)
+		goto err_misc;
+
+	ptm.qdss_at_clk = clk_get(NULL, "qdss_at_clk");
+	if (IS_ERR(ptm.qdss_at_clk)) {
+		ret = PTR_ERR(ptm.qdss_at_clk);
+		goto err_at;
+	}
+	ret = clk_set_rate(ptm.qdss_at_clk, 300000000);
+	if (ret)
+		goto err_at_rate;
+	ret = clk_enable(ptm.qdss_at_clk);
+	if (ret)
+		goto err_at_enable;
+
+	ptm.qdss_pclkdbg_clk = clk_get(NULL, "qdss_pclkdbg_clk");
+	if (IS_ERR(ptm.qdss_pclkdbg_clk)) {
+		ret = PTR_ERR(ptm.qdss_pclkdbg_clk);
+		goto err_pclkdbg;
+	}
+	ret = clk_enable(ptm.qdss_pclkdbg_clk);
+	if (ret)
+		goto err_pclkdbg_enable;
+
+	ptm.qdss_pclk = clk_get(NULL, "qdss_pclk");
+	if (IS_ERR(ptm.qdss_pclk)) {
+		ret = PTR_ERR(ptm.qdss_pclk);
+		goto err_pclk;
+	}
+
+	ret = clk_enable(ptm.qdss_pclk);
+	if (ret)
+		goto err_pclk_enable;
+
+	ptm.qdss_traceclkin_clk = clk_get(NULL, "qdss_traceclkin_clk");
+	if (IS_ERR(ptm.qdss_traceclkin_clk)) {
+		ret = PTR_ERR(ptm.qdss_traceclkin_clk);
+		goto err_traceclkin;
+	}
+	ret = clk_set_rate(ptm.qdss_traceclkin_clk, 300000000);
+	if (ret)
+		goto err_traceclkin_rate;
+	ret = clk_enable(ptm.qdss_traceclkin_clk);
+	if (ret)
+		goto err_traceclkin_enable;
+
+	ptm.qdss_tsctr_clk = clk_get(NULL, "qdss_tsctr_clk");
+	if (IS_ERR(ptm.qdss_tsctr_clk)) {
+		ret = PTR_ERR(ptm.qdss_tsctr_clk);
+		goto err_tsctr;
+	}
+	ret = clk_set_rate(ptm.qdss_tsctr_clk, 400000000);
+	if (ret)
+		goto err_tsctr_rate;
+
+	ret = clk_enable(ptm.qdss_tsctr_clk);
+	if (ret)
+		goto err_tsctr_enable;
+
+	ptm_cfg_ro_init();
+	ptm_cfg_rw_init();
+
+	ptm.trace_enabled = false;
+
+	ptm.cpu_restore = alloc_percpu(int);
+	if (!ptm.cpu_restore) {
+		ret = -ENOMEM;
+		goto err_percpu;
+	}
+
+	for_each_possible_cpu(cpu)
+		*per_cpu_ptr(ptm.cpu_restore, cpu) = 0;
+
+	wake_lock_init(&ptm.wake_lock, WAKE_LOCK_SUSPEND, "msm_ptm");
+	pm_qos_add_request(&ptm.qos_req, PM_QOS_CPU_DMA_LATENCY,
+						PM_QOS_DEFAULT_VALUE);
+	atomic_set(&ptm.in_use, 0);
+
+	clk_disable(ptm.qdss_tsctr_clk);
+	clk_disable(ptm.qdss_traceclkin_clk);
+	clk_disable(ptm.qdss_pclk);
+	clk_disable(ptm.qdss_pclkdbg_clk);
+	clk_disable(ptm.qdss_at_clk);
+
+	dev_info(ptm.dev, "PTM intialized.\n");
+
+	if (trace_on_boot) {
+		if (!ptm_trace_enable())
+			dev_info(ptm.dev, "tracing enabled\n");
+		else
+			dev_err(ptm.dev, "error enabling trace\n");
+	}
+
+	return 0;
+
+err_percpu:
+	clk_disable(ptm.qdss_tsctr_clk);
+err_tsctr_enable:
+err_tsctr_rate:
+	clk_put(ptm.qdss_tsctr_clk);
+err_tsctr:
+	clk_disable(ptm.qdss_traceclkin_clk);
+err_traceclkin_enable:
+err_traceclkin_rate:
+	clk_put(ptm.qdss_traceclkin_clk);
+err_traceclkin:
+	clk_disable(ptm.qdss_pclk);
+err_pclk_enable:
+	clk_put(ptm.qdss_pclk);
+err_pclk:
+	clk_disable(ptm.qdss_pclkdbg_clk);
+err_pclkdbg_enable:
+	clk_put(ptm.qdss_pclkdbg_clk);
+err_pclkdbg:
+	clk_disable(ptm.qdss_at_clk);
+err_at_enable:
+err_at_rate:
+	clk_put(ptm.qdss_at_clk);
+err_at:
+	misc_deregister(&ptm_misc);
+err_misc:
+	kfree(ptm.state);
+err_kzalloc:
+	iounmap(ptm.base);
+err_ioremap:
+err_res:
+	return ret;
+}
+
+static int __devexit ptm_remove(struct platform_device *pdev)
+{
+	if (ptm.trace_enabled)
+		ptm_trace_disable();
+	pm_qos_remove_request(&ptm.qos_req);
+	wake_lock_destroy(&ptm.wake_lock);
+	free_percpu(ptm.cpu_restore);
+	clk_put(ptm.qdss_tsctr_clk);
+	clk_put(ptm.qdss_traceclkin_clk);
+	clk_put(ptm.qdss_pclk);
+	clk_put(ptm.qdss_pclkdbg_clk);
+	clk_put(ptm.qdss_at_clk);
+	misc_deregister(&ptm_misc);
+	kfree(ptm.state);
+	iounmap(ptm.base);
+
+	return 0;
+}
+
+static struct platform_driver ptm_driver = {
+	.probe          = ptm_probe,
+	.remove         = __devexit_p(ptm_remove),
+	.driver         = {
+		.name   = "msm_ptm",
+	},
+};
+
+static int __init ptm_init(void)
+{
+	return platform_driver_register(&ptm_driver);
+}
+module_init(ptm_init);
+
+static void __exit ptm_exit(void)
+{
+	platform_driver_unregister(&ptm_driver);
+}
+module_exit(ptm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Coresight Program Flow Trace driver");
diff --git a/arch/arm/mach-msm/qdss-tpiu.c b/arch/arm/mach-msm/qdss-tpiu.c
new file mode 100644
index 0000000..def7467
--- /dev/null
+++ b/arch/arm/mach-msm/qdss-tpiu.c
@@ -0,0 +1,138 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include "qdss.h"
+
+#define tpiu_writel(tpiu, val, off)	__raw_writel((val), tpiu.base + off)
+#define tpiu_readl(tpiu, off)		__raw_readl(tpiu.base + off)
+
+#define TPIU_SUPPORTED_PORT_SIZE			(0x000)
+#define TPIU_CURRENT_PORT_SIZE				(0x004)
+#define TPIU_SUPPORTED_TRIGGER_MODES			(0x100)
+#define TPIU_TRIGGER_COUNTER_VALUE			(0x104)
+#define TPIU_TRIGGER_MULTIPLIER				(0x108)
+#define TPIU_SUPPORTED_TEST_PATTERNM			(0x200)
+#define TPIU_CURRENT_TEST_PATTERNM			(0x204)
+#define TPIU_TEST_PATTERN_REPEAT_COUNTER		(0x208)
+#define TPIU_FORMATTER_AND_FLUSH_STATUS			(0x300)
+#define TPIU_FORMATTER_AND_FLUSH_CONTROL		(0x304)
+#define TPIU_FORMATTER_SYNCHRONIZATION_COUNTER		(0x308)
+#define TPIU_EXTCTL_IN_PORT				(0x400)
+#define TPIU_EXTCTL_OUT_PORT				(0x404)
+#define TPIU_ITTRFLINACK				(0xEE4)
+#define TPIU_ITTRFLIN					(0xEE8)
+#define TPIU_ITATBDATA0					(0xEEC)
+#define TPIU_ITATBCTR2					(0xEF0)
+#define TPIU_ITATBCTR1					(0xEF4)
+#define TPIU_ITATBCTR0					(0xEF8)
+
+
+#define TPIU_LOCK()							\
+do {									\
+	mb();								\
+	tpiu_writel(tpiu, MAGIC2, CS_LAR);				\
+} while (0)
+#define TPIU_UNLOCK()							\
+do {									\
+	tpiu_writel(tpiu, MAGIC1, CS_LAR);				\
+	mb();								\
+} while (0)
+
+struct tpiu_ctx {
+	void __iomem	*base;
+	bool		enabled;
+	atomic_t	in_use;
+	struct device	*dev;
+};
+
+static struct tpiu_ctx tpiu;
+
+static void __tpiu_disable(void)
+{
+	TPIU_UNLOCK();
+
+	tpiu_writel(tpiu, 0x3000, TPIU_FORMATTER_AND_FLUSH_CONTROL);
+	tpiu_writel(tpiu, 0x3040, TPIU_FORMATTER_AND_FLUSH_CONTROL);
+
+	TPIU_LOCK();
+}
+
+void tpiu_disable(void)
+{
+	__tpiu_disable();
+	tpiu.enabled = false;
+	dev_info(tpiu.dev, "tpiu disabled\n");
+}
+
+static int __devinit tpiu_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -EINVAL;
+		goto err_res;
+	}
+
+	tpiu.base = ioremap_nocache(res->start, resource_size(res));
+	if (!tpiu.base) {
+		ret = -EINVAL;
+		goto err_ioremap;
+	}
+
+	tpiu.dev = &pdev->dev;
+
+	return 0;
+
+err_ioremap:
+err_res:
+	return ret;
+}
+
+static int __devexit tpiu_remove(struct platform_device *pdev)
+{
+	if (tpiu.enabled)
+		tpiu_disable();
+	iounmap(tpiu.base);
+
+	return 0;
+}
+
+static struct platform_driver tpiu_driver = {
+	.probe          = tpiu_probe,
+	.remove         = __devexit_p(tpiu_remove),
+	.driver         = {
+		.name   = "msm_tpiu",
+	},
+};
+
+static int __init tpiu_init(void)
+{
+	return platform_driver_register(&tpiu_driver);
+}
+module_init(tpiu_init);
+
+static void __exit tpiu_exit(void)
+{
+	platform_driver_unregister(&tpiu_driver);
+}
+module_exit(tpiu_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Coresight Trace Port Interface Unit");
diff --git a/arch/arm/mach-msm/qdss.h b/arch/arm/mach-msm/qdss.h
new file mode 100644
index 0000000..b94e645
--- /dev/null
+++ b/arch/arm/mach-msm/qdss.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_QDSS_H_
+#define _ARCH_ARM_MACH_MSM_QDSS_H_
+
+/* Coresight management registers (0xF00-0xFCC) */
+#define CS_ITCTRL		(0xF00)
+#define CS_CLAIMSET		(0xFA0)
+#define CS_CLAIMCLR		(0xFA4)
+#define CS_LAR			(0xFB0)
+#define CS_LSR			(0xFB4)
+#define CS_AUTHSTATUS		(0xFB8)
+#define CS_DEVID		(0xFC8)
+#define CS_DEVTYPE		(0xFCC)
+/* Peripheral id registers (0xFD0-0xFEC) */
+#define CS_PIDR4		(0xFD0)
+#define CS_PIDR5		(0xFD4)
+#define CS_PIDR6		(0xFD8)
+#define CS_PIDR7		(0xFDC)
+#define CS_PIDR0		(0xFE0)
+#define CS_PIDR1		(0xFE4)
+#define CS_PIDR2		(0xFE8)
+#define CS_PIDR3		(0xFEC)
+/* Component id registers (0xFF0-0xFFC) */
+#define CS_CIDR0		(0xFF0)
+#define CS_CIDR1		(0xFF4)
+#define CS_CIDR2		(0xFF8)
+#define CS_CIDR3		(0xFFC)
+
+
+#define TIMEOUT_US		100
+#define MAGIC1			0xC5ACCE55
+#define MAGIC2			0x0
+
+#define BM(lsb, msb)		((BIT(msb) - BIT(lsb)) + BIT(msb))
+#define BMVAL(val, lsb, msb)	((val & BM(lsb, msb)) >> lsb)
+#define BVAL(val, n)		((val & BIT(n)) >> n)
+
+/* TODO: clean this up */
+void etb_enable(void);
+void etb_disable(void);
+void etb_dump(void);
+void tpiu_disable(void);
+void funnel_enable(uint8_t id, uint32_t port_mask);
+void funnel_disable(uint8_t id, uint32_t port_mask);
+
+#endif