cs: move qdss code under drivers

In preparation to create generic coresight drivers code move
existing qdss code under drivers/cs and include/linux

Change-Id: If7697a9961006fe94818e32d19aea75c56ce225d
Signed-off-by: Pratik Patel <pratikp@codeaurora.org>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 286a4d4..7225bdf 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -148,4 +148,6 @@
 
 source "drivers/gud/Kconfig"
 
+source "drivers/cs/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index bea505c..3272e2c 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -140,3 +140,5 @@
 
 #MobiCore
 obj-$(CONFIG_MOBICORE_SUPPORT)  += gud/
+
+obj-$(CONFIG_MSM_QDSS)		+= cs/
diff --git a/drivers/cs/Kconfig b/drivers/cs/Kconfig
new file mode 100644
index 0000000..1219af1
--- /dev/null
+++ b/drivers/cs/Kconfig
@@ -0,0 +1,32 @@
+config MSM_QDSS
+	bool "CoreSight tracing"
+	help
+	  Enables support for CoreSight tracing. This uses CoreSight trace
+	  components and buses to support both hardware (eg. processor ETM)
+	  and hardware assisted software instrumentation based (eg. STM)
+	  tracing.
+
+	  For production builds, you should probably say 'N' here to avoid
+	  potential power, performance and memory penalty.
+
+config MSM_QDSS_STM_DEFAULT_ENABLE
+	bool "Turn on CoreSight STM tracing by default"
+	depends on MSM_QDSS
+	help
+	  Turns on CoreSight STM tracing (hardware assisted software
+	  instrumentation based tracing) by default. Otherwise, tracing is
+	  disabled by default but can be enabled via sysfs.
+
+	  For production builds, you should probably say 'N' here to avoid
+	  potential power, performance and memory penalty.
+
+config MSM_QDSS_ETM_DEFAULT_ENABLE
+	bool "Turn on CoreSight ETM tracing by default"
+	depends on MSM_QDSS
+	help
+	  Turns on CoreSight ETM tracing (processor tracing) by default.
+	  Otherwise, tracing is disabled by default but can be enabled via
+	  sysfs.
+
+	  For production builds, you should probably say 'N' here to avoid
+	  potential power, performance and memory penalty.
diff --git a/drivers/cs/Makefile b/drivers/cs/Makefile
new file mode 100644
index 0000000..9a634a1
--- /dev/null
+++ b/drivers/cs/Makefile
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_MSM_QDSS) += qdss.o qdss-etb.o qdss-tpiu.o qdss-funnel.o qdss-stm.o qdss-etm.o
diff --git a/drivers/cs/qdss-etb.c b/drivers/cs/qdss-etb.c
new file mode 100644
index 0000000..ccea2fa
--- /dev/null
+++ b/drivers/cs/qdss-etb.c
@@ -0,0 +1,419 @@
+/* Copyright (c) 2011-2012, 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/init.h>
+#include <linux/types.h>
+#include <linux/device.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/mutex.h>
+
+#include "qdss-priv.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 FRAME_SIZE_WORDS	4
+
+#define ETB_LOCK()							\
+do {									\
+	mb();								\
+	etb_writel(etb, 0x0, CS_LAR);					\
+} while (0)
+#define ETB_UNLOCK()							\
+do {									\
+	etb_writel(etb, CS_UNLOCK_MAGIC, CS_LAR);			\
+	mb();								\
+} while (0)
+
+struct etb_ctx {
+	uint8_t		*buf;
+	void __iomem	*base;
+	bool		enabled;
+	bool		reading;
+	spinlock_t	spinlock;
+	atomic_t	in_use;
+	struct device	*dev;
+	struct kobject	*kobj;
+	uint32_t	trigger_cntr;
+};
+
+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, etb.trigger_cntr, ETB_TRG);
+	etb_writel(etb, BIT(13) | BIT(0), ETB_FFCR);
+	etb_writel(etb, BIT(0), ETB_CTL_REG);
+
+	ETB_LOCK();
+}
+
+void etb_enable(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&etb.spinlock, flags);
+	__etb_enable();
+	etb.enabled = true;
+	dev_info(etb.dev, "ETB enabled\n");
+	spin_unlock_irqrestore(&etb.spinlock, flags);
+}
+
+static void __etb_disable(void)
+{
+	int count;
+	uint32_t ffcr;
+
+	ETB_UNLOCK();
+
+	ffcr = etb_readl(etb, ETB_FFCR);
+	ffcr |= (BIT(12) | BIT(6));
+	etb_writel(etb, ffcr, ETB_FFCR);
+
+	for (count = TIMEOUT_US; BVAL(etb_readl(etb, ETB_FFCR), 6) != 0
+				&& count > 0; count--)
+		udelay(1);
+	WARN(count == 0, "timeout while flushing ETB, ETB_FFCR: %#x\n",
+	     etb_readl(etb, 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, ETB_FFSR: %#x\n",
+	     etb_readl(etb, ETB_FFSR));
+
+	ETB_LOCK();
+}
+
+void etb_disable(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&etb.spinlock, flags);
+	__etb_disable();
+	etb.enabled = false;
+	dev_info(etb.dev, "ETB disabled\n");
+	spin_unlock_irqrestore(&etb.spinlock, flags);
+}
+
+static void __etb_dump(void)
+{
+	int i;
+	uint8_t *buf_ptr;
+	uint32_t read_data;
+	uint32_t read_ptr;
+	uint32_t write_ptr;
+	uint32_t frame_off;
+	uint32_t frame_endoff;
+
+	ETB_UNLOCK();
+
+	read_ptr = etb_readl(etb, ETB_RAM_READ_POINTER);
+	write_ptr = etb_readl(etb, ETB_RAM_WRITE_POINTER);
+
+	frame_off = write_ptr % FRAME_SIZE_WORDS;
+	frame_endoff = FRAME_SIZE_WORDS - frame_off;
+	if (frame_off) {
+		dev_err(etb.dev, "write_ptr: %lu not aligned to formatter "
+				"frame size\n", (unsigned long)write_ptr);
+		dev_err(etb.dev, "frameoff: %lu, frame_endoff: %lu\n",
+			(unsigned long)frame_off, (unsigned long)frame_endoff);
+		write_ptr += frame_endoff;
+	}
+
+	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++ = read_data >> 8;
+		*buf_ptr++ = read_data >> 16;
+		*buf_ptr++ = read_data >> 24;
+	}
+
+	if (frame_off) {
+		buf_ptr -= (frame_endoff * BYTES_PER_WORD);
+		for (i = 0; i < frame_endoff; i++) {
+			*buf_ptr++ = 0x0;
+			*buf_ptr++ = 0x0;
+			*buf_ptr++ = 0x0;
+			*buf_ptr++ = 0x0;
+		}
+	}
+
+	etb_writel(etb, read_ptr, ETB_RAM_READ_POINTER);
+
+	ETB_LOCK();
+}
+
+void etb_dump(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&etb.spinlock, flags);
+	if (etb.enabled) {
+		__etb_disable();
+		__etb_dump();
+		__etb_enable();
+
+		dev_info(etb.dev, "ETB dumped\n");
+	}
+	spin_unlock_irqrestore(&etb.spinlock, flags);
+}
+
+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,
+};
+
+#define ETB_ATTR(__name)						\
+static struct kobj_attribute __name##_attr =				\
+	__ATTR(__name, S_IRUGO | S_IWUSR, __name##_show, __name##_store)
+
+static ssize_t trigger_cntr_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	etb.trigger_cntr = val;
+	return n;
+}
+static ssize_t trigger_cntr_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val = etb.trigger_cntr;
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+ETB_ATTR(trigger_cntr);
+
+static int __devinit etb_sysfs_init(void)
+{
+	int ret;
+
+	etb.kobj = kobject_create_and_add("etb", qdss_get_modulekobj());
+	if (!etb.kobj) {
+		dev_err(etb.dev, "failed to create ETB sysfs kobject\n");
+		ret = -ENOMEM;
+		goto err_create;
+	}
+
+	ret = sysfs_create_file(etb.kobj, &trigger_cntr_attr.attr);
+	if (ret) {
+		dev_err(etb.dev, "failed to create ETB sysfs trigger_cntr"
+		" attribute\n");
+		goto err_file;
+	}
+
+	return 0;
+err_file:
+	kobject_put(etb.kobj);
+err_create:
+	return ret;
+}
+
+static void __devexit etb_sysfs_exit(void)
+{
+	sysfs_remove_file(etb.kobj, &trigger_cntr_attr.attr);
+	kobject_put(etb.kobj);
+}
+
+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;
+
+	spin_lock_init(&etb.spinlock);
+
+	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;
+	}
+
+	etb_sysfs_init();
+
+	dev_info(etb.dev, "ETB initialized\n");
+	return 0;
+
+err_alloc:
+	misc_deregister(&etb_misc);
+err_misc:
+	iounmap(etb.base);
+err_ioremap:
+err_res:
+	dev_err(etb.dev, "ETB init failed\n");
+	return ret;
+}
+
+static int __devexit etb_remove(struct platform_device *pdev)
+{
+	if (etb.enabled)
+		etb_disable();
+	etb_sysfs_exit();
+	kfree(etb.buf);
+	misc_deregister(&etb_misc);
+	iounmap(etb.base);
+
+	return 0;
+}
+
+static struct of_device_id etb_match[] = {
+	{.compatible = "qcom,msm-etb"},
+	{}
+};
+
+static struct platform_driver etb_driver = {
+	.probe          = etb_probe,
+	.remove         = __devexit_p(etb_remove),
+	.driver         = {
+		.name   = "msm_etb",
+		.owner	= THIS_MODULE,
+		.of_match_table = etb_match,
+	},
+};
+
+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 driver");
diff --git a/drivers/cs/qdss-etm.c b/drivers/cs/qdss-etm.c
new file mode 100644
index 0000000..e2a38de
--- /dev/null
+++ b/drivers/cs/qdss-etm.c
@@ -0,0 +1,1336 @@
+/* Copyright (c) 2011-2012, 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/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/smp.h>
+#include <linux/wakelock.h>
+#include <linux/pm_qos.h>
+#include <linux/sysfs.h>
+#include <linux/stat.h>
+#include <asm/sections.h>
+#include <mach/socinfo.h>
+
+#include "qdss-priv.h"
+
+#define etm_writel(etm, cpu, val, off)	\
+			__raw_writel((val), etm.base + (SZ_4K * cpu) + off)
+#define etm_readl(etm, cpu, off)	\
+			__raw_readl(etm.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 ETMSQ31EVR		(0x18C)
+#define ETMSQ32EVR		(0x190)
+#define ETMSQ13EVR		(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		(0x240)
+/* Management registers (0x300-0x314) */
+#define ETMOSLAR		(0x300)
+#define ETMOSLSR		(0x304)
+#define ETMOSSRR		(0x308)
+#define ETMPDCR			(0x310)
+#define ETMPDSR			(0x314)
+
+#define ETM_MAX_ADDR_CMP	(16)
+#define ETM_MAX_CNTR		(4)
+#define ETM_MAX_CTXID_CMP	(3)
+
+#define ETM_MODE_EXCLUDE	BIT(0)
+#define ETM_MODE_CYCACC		BIT(1)
+#define ETM_MODE_STALL		BIT(2)
+#define ETM_MODE_TIMESTAMP	BIT(3)
+#define ETM_MODE_CTXID		BIT(4)
+#define ETM_MODE_ALL		(0x1F)
+
+#define ETM_EVENT_MASK		(0x1FFFF)
+#define ETM_SYNC_MASK		(0xFFF)
+#define ETM_ALL_MASK		(0xFFFFFFFF)
+
+#define ETM_SEQ_STATE_MAX_VAL	(0x2)
+
+enum {
+	ETM_ADDR_TYPE_NONE,
+	ETM_ADDR_TYPE_SINGLE,
+	ETM_ADDR_TYPE_RANGE,
+	ETM_ADDR_TYPE_START,
+	ETM_ADDR_TYPE_STOP,
+};
+
+#define ETM_LOCK(cpu)							\
+do {									\
+	mb();								\
+	etm_writel(etm, cpu, 0x0, CS_LAR);				\
+} while (0)
+#define ETM_UNLOCK(cpu)							\
+do {									\
+	etm_writel(etm, cpu, CS_UNLOCK_MAGIC, CS_LAR);			\
+	mb();								\
+} while (0)
+
+
+#ifdef MODULE_PARAM_PREFIX
+#undef MODULE_PARAM_PREFIX
+#endif
+#define MODULE_PARAM_PREFIX "qdss."
+
+#ifdef CONFIG_MSM_QDSS_ETM_DEFAULT_ENABLE
+static int etm_boot_enable = 1;
+#else
+static int etm_boot_enable;
+#endif
+module_param_named(
+	etm_boot_enable, etm_boot_enable, int, S_IRUGO
+);
+
+struct etm_ctx {
+	void __iomem			*base;
+	bool				enabled;
+	struct wake_lock		wake_lock;
+	struct pm_qos_request		qos_req;
+	struct qdss_source		*src;
+	struct mutex			mutex;
+	struct device			*dev;
+	struct kobject			*kobj;
+	uint8_t				arch;
+	uint8_t				nr_addr_cmp;
+	uint8_t				nr_cntr;
+	uint8_t				nr_ext_inp;
+	uint8_t				nr_ext_out;
+	uint8_t				nr_ctxid_cmp;
+	uint8_t				reset;
+	uint32_t			mode;
+	uint32_t			ctrl;
+	uint32_t			trigger_event;
+	uint32_t			startstop_ctrl;
+	uint32_t			enable_event;
+	uint32_t			enable_ctrl1;
+	uint32_t			fifofull_level;
+	uint8_t				addr_idx;
+	uint32_t			addr_val[ETM_MAX_ADDR_CMP];
+	uint32_t			addr_acctype[ETM_MAX_ADDR_CMP];
+	uint32_t			addr_type[ETM_MAX_ADDR_CMP];
+	uint8_t				cntr_idx;
+	uint32_t			cntr_rld_val[ETM_MAX_CNTR];
+	uint32_t			cntr_event[ETM_MAX_CNTR];
+	uint32_t			cntr_rld_event[ETM_MAX_CNTR];
+	uint32_t			cntr_val[ETM_MAX_CNTR];
+	uint32_t			seq_12_event;
+	uint32_t			seq_21_event;
+	uint32_t			seq_23_event;
+	uint32_t			seq_31_event;
+	uint32_t			seq_32_event;
+	uint32_t			seq_13_event;
+	uint32_t			seq_curr_state;
+	uint8_t				ctxid_idx;
+	uint32_t			ctxid_val[ETM_MAX_CTXID_CMP];
+	uint32_t			ctxid_mask;
+	uint32_t			sync_freq;
+	uint32_t			timestamp_event;
+};
+
+static struct etm_ctx etm = {
+	.trigger_event		= 0x406F,
+	.enable_event		= 0x6F,
+	.enable_ctrl1		= 0x1,
+	.fifofull_level		= 0x28,
+	.addr_val		= {(uint32_t) _stext, (uint32_t) _etext},
+	.addr_type		= {ETM_ADDR_TYPE_RANGE, ETM_ADDR_TYPE_RANGE},
+	.cntr_event		= {[0 ... (ETM_MAX_CNTR - 1)] = 0x406F},
+	.cntr_rld_event		= {[0 ... (ETM_MAX_CNTR - 1)] = 0x406F},
+	.seq_12_event		= 0x406F,
+	.seq_21_event		= 0x406F,
+	.seq_23_event		= 0x406F,
+	.seq_31_event		= 0x406F,
+	.seq_32_event		= 0x406F,
+	.seq_13_event		= 0x406F,
+	.sync_freq		= 0x80,
+	.timestamp_event	= 0x406F,
+};
+
+
+/* ETM clock is derived from the processor clock and gets enabled on a
+ * logical OR of below items on Krait (pass2 onwards):
+ * 1.CPMR[ETMCLKEN] is 1
+ * 2.ETMCR[PD] is 0
+ * 3.ETMPDCR[PU] is 1
+ * 4.Reset is asserted (core or debug)
+ * 5.APB memory mapped requests (eg. EDAP access)
+ *
+ * 1., 2. and 3. above are permanent enables whereas 4. and 5. are temporary
+ * enables
+ *
+ * We rely on 5. to be able to access ETMCR and then use 2. above for ETM
+ * clock vote in the driver and the save-restore code uses 1. above
+ * for its vote
+ */
+static void etm_set_pwrdwn(int cpu)
+{
+	uint32_t etmcr;
+
+	etmcr = etm_readl(etm, cpu, ETMCR);
+	etmcr |= BIT(0);
+	etm_writel(etm, cpu, etmcr, ETMCR);
+}
+
+static void etm_clr_pwrdwn(int cpu)
+{
+	uint32_t etmcr;
+
+	etmcr = etm_readl(etm, cpu, ETMCR);
+	etmcr &= ~BIT(0);
+	etm_writel(etm, cpu, etmcr, ETMCR);
+}
+
+static void etm_set_prog(int cpu)
+{
+	uint32_t etmcr;
+	int count;
+
+	etmcr = etm_readl(etm, cpu, ETMCR);
+	etmcr |= BIT(10);
+	etm_writel(etm, cpu, etmcr, ETMCR);
+
+	for (count = TIMEOUT_US; BVAL(etm_readl(etm, cpu, ETMSR), 1) != 1
+				&& count > 0; count--)
+		udelay(1);
+	WARN(count == 0, "timeout while setting prog bit, ETMSR: %#x\n",
+	     etm_readl(etm, cpu, ETMSR));
+}
+
+static void etm_clr_prog(int cpu)
+{
+	uint32_t etmcr;
+	int count;
+
+	etmcr = etm_readl(etm, cpu, ETMCR);
+	etmcr &= ~BIT(10);
+	etm_writel(etm, cpu, etmcr, ETMCR);
+
+	for (count = TIMEOUT_US; BVAL(etm_readl(etm, cpu, ETMSR), 1) != 0
+				&& count > 0; count--)
+		udelay(1);
+	WARN(count == 0, "timeout while clearing prog bit, ETMSR: %#x\n",
+	     etm_readl(etm, cpu, ETMSR));
+}
+
+static void __etm_enable(int cpu)
+{
+	int i;
+
+	ETM_UNLOCK(cpu);
+	/* Vote for ETM power/clock enable */
+	etm_clr_pwrdwn(cpu);
+	etm_set_prog(cpu);
+
+	etm_writel(etm, cpu, etm.ctrl | BIT(10), ETMCR);
+	etm_writel(etm, cpu, etm.trigger_event, ETMTRIGGER);
+	etm_writel(etm, cpu, etm.startstop_ctrl, ETMTSSCR);
+	etm_writel(etm, cpu, etm.enable_event, ETMTEEVR);
+	etm_writel(etm, cpu, etm.enable_ctrl1, ETMTECR1);
+	etm_writel(etm, cpu, etm.fifofull_level, ETMFFLR);
+	for (i = 0; i < etm.nr_addr_cmp; i++) {
+		etm_writel(etm, cpu, etm.addr_val[i], ETMACVRn(i));
+		etm_writel(etm, cpu, etm.addr_acctype[i], ETMACTRn(i));
+	}
+	for (i = 0; i < etm.nr_cntr; i++) {
+		etm_writel(etm, cpu, etm.cntr_rld_val[i], ETMCNTRLDVRn(i));
+		etm_writel(etm, cpu, etm.cntr_event[i], ETMCNTENRn(i));
+		etm_writel(etm, cpu, etm.cntr_rld_event[i], ETMCNTRLDEVRn(i));
+		etm_writel(etm, cpu, etm.cntr_val[i], ETMCNTVRn(i));
+	}
+	etm_writel(etm, cpu, etm.seq_12_event, ETMSQ12EVR);
+	etm_writel(etm, cpu, etm.seq_21_event, ETMSQ21EVR);
+	etm_writel(etm, cpu, etm.seq_23_event, ETMSQ23EVR);
+	etm_writel(etm, cpu, etm.seq_31_event, ETMSQ31EVR);
+	etm_writel(etm, cpu, etm.seq_32_event, ETMSQ32EVR);
+	etm_writel(etm, cpu, etm.seq_13_event, ETMSQ13EVR);
+	etm_writel(etm, cpu, etm.seq_curr_state, ETMSQR);
+	for (i = 0; i < etm.nr_ext_out; i++)
+		etm_writel(etm, cpu, 0x0000406F, ETMEXTOUTEVRn(i));
+	for (i = 0; i < etm.nr_ctxid_cmp; i++)
+		etm_writel(etm, cpu, etm.ctxid_val[i], ETMCIDCVRn(i));
+	etm_writel(etm, cpu, etm.ctxid_mask, ETMCIDCMR);
+	etm_writel(etm, cpu, etm.sync_freq, ETMSYNCFR);
+	etm_writel(etm, cpu, 0x00000000, ETMEXTINSELR);
+	etm_writel(etm, cpu, etm.timestamp_event, ETMTSEVR);
+	etm_writel(etm, cpu, 0x00000000, ETMAUXCR);
+	etm_writel(etm, cpu, cpu+1, ETMTRACEIDR);
+	etm_writel(etm, cpu, 0x00000000, ETMVMIDCVR);
+
+	etm_clr_prog(cpu);
+	ETM_LOCK(cpu);
+}
+
+static int etm_enable(void)
+{
+	int ret, cpu;
+
+	if (etm.enabled) {
+		dev_err(etm.dev, "ETM tracing already enabled\n");
+		ret = -EPERM;
+		goto err;
+	}
+
+	wake_lock(&etm.wake_lock);
+	/* 1. causes all online cpus to come out of idle PC
+	 * 2. prevents idle PC until save restore flag is enabled atomically
+	 *
+	 * we rely on the user to prevent hotplug on/off racing with this
+	 * operation and to ensure cores where trace is expected to be turned
+	 * on are already hotplugged on
+	 */
+	pm_qos_update_request(&etm.qos_req, 0);
+
+	ret = qdss_enable(etm.src);
+	if (ret)
+		goto err_qdss;
+
+	for_each_online_cpu(cpu)
+		__etm_enable(cpu);
+
+	etm.enabled = true;
+
+	pm_qos_update_request(&etm.qos_req, PM_QOS_DEFAULT_VALUE);
+	wake_unlock(&etm.wake_lock);
+
+	dev_info(etm.dev, "ETM tracing enabled\n");
+	return 0;
+
+err_qdss:
+	pm_qos_update_request(&etm.qos_req, PM_QOS_DEFAULT_VALUE);
+	wake_unlock(&etm.wake_lock);
+err:
+	return ret;
+}
+
+static void __etm_disable(int cpu)
+{
+	ETM_UNLOCK(cpu);
+	etm_set_prog(cpu);
+
+	/* program trace enable to low by using always false event */
+	etm_writel(etm, cpu, 0x6F | BIT(14), ETMTEEVR);
+
+	/* Vote for ETM power/clock disable */
+	etm_set_pwrdwn(cpu);
+	ETM_LOCK(cpu);
+}
+
+static int etm_disable(void)
+{
+	int ret, cpu;
+
+	if (!etm.enabled) {
+		dev_err(etm.dev, "ETM tracing already disabled\n");
+		ret = -EPERM;
+		goto err;
+	}
+
+	wake_lock(&etm.wake_lock);
+	/* 1. causes all online cpus to come out of idle PC
+	 * 2. prevents idle PC until save restore flag is disabled atomically
+	 *
+	 * we rely on the user to prevent hotplug on/off racing with this
+	 * operation and to ensure cores where trace is expected to be turned
+	 * off are already hotplugged on
+	 */
+	pm_qos_update_request(&etm.qos_req, 0);
+
+	for_each_online_cpu(cpu)
+		__etm_disable(cpu);
+
+	qdss_disable(etm.src);
+
+	etm.enabled = false;
+
+	pm_qos_update_request(&etm.qos_req, PM_QOS_DEFAULT_VALUE);
+	wake_unlock(&etm.wake_lock);
+
+	dev_info(etm.dev, "ETM tracing disabled\n");
+	return 0;
+err:
+	return ret;
+}
+
+/* Memory mapped writes to clear os lock not supported */
+static void etm_os_unlock(void *unused)
+{
+	unsigned long value = 0x0;
+
+	asm("mcr p14, 1, %0, c1, c0, 4\n\t" : : "r" (value));
+	asm("isb\n\t");
+}
+
+#define ETM_STORE(__name, mask)						\
+static ssize_t __name##_store(struct kobject *kobj,			\
+			struct kobj_attribute *attr,			\
+			const char *buf, size_t n)			\
+{									\
+	unsigned long val;						\
+									\
+	if (sscanf(buf, "%lx", &val) != 1)				\
+		return -EINVAL;						\
+									\
+	etm.__name = val & mask;					\
+	return n;							\
+}
+
+#define ETM_SHOW(__name)						\
+static ssize_t __name##_show(struct kobject *kobj,			\
+			struct kobj_attribute *attr,			\
+			char *buf)					\
+{									\
+	unsigned long val = etm.__name;					\
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);		\
+}
+
+#define ETM_ATTR(__name)						\
+static struct kobj_attribute __name##_attr =				\
+	__ATTR(__name, S_IRUGO | S_IWUSR, __name##_show, __name##_store)
+#define ETM_ATTR_RO(__name)						\
+static struct kobj_attribute __name##_attr =				\
+	__ATTR(__name, S_IRUGO, __name##_show, NULL)
+
+static ssize_t enabled_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	int ret = 0;
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	mutex_lock(&etm.mutex);
+	if (val)
+		ret = etm_enable();
+	else
+		ret = etm_disable();
+	mutex_unlock(&etm.mutex);
+
+	if (ret)
+		return ret;
+	return n;
+}
+ETM_SHOW(enabled);
+ETM_ATTR(enabled);
+
+ETM_SHOW(nr_addr_cmp);
+ETM_ATTR_RO(nr_addr_cmp);
+ETM_SHOW(nr_cntr);
+ETM_ATTR_RO(nr_cntr);
+ETM_SHOW(nr_ctxid_cmp);
+ETM_ATTR_RO(nr_ctxid_cmp);
+
+/* Reset to trace everything i.e. exclude nothing. */
+static ssize_t reset_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	int i;
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	mutex_lock(&etm.mutex);
+	if (val) {
+		etm.mode = ETM_MODE_EXCLUDE;
+		etm.ctrl = 0x0;
+		if (cpu_is_krait_v1()) {
+			etm.mode |= ETM_MODE_CYCACC;
+			etm.ctrl |= BIT(12);
+		}
+		etm.trigger_event = 0x406F;
+		etm.startstop_ctrl = 0x0;
+		etm.enable_event = 0x6F;
+		etm.enable_ctrl1 = 0x1000000;
+		etm.fifofull_level = 0x28;
+		etm.addr_idx = 0x0;
+		for (i = 0; i < etm.nr_addr_cmp; i++) {
+			etm.addr_val[i] = 0x0;
+			etm.addr_acctype[i] = 0x0;
+			etm.addr_type[i] = ETM_ADDR_TYPE_NONE;
+		}
+		etm.cntr_idx = 0x0;
+		for (i = 0; i < etm.nr_cntr; i++) {
+			etm.cntr_rld_val[i] = 0x0;
+			etm.cntr_event[i] = 0x406F;
+			etm.cntr_rld_event[i] = 0x406F;
+			etm.cntr_val[i] = 0x0;
+		}
+		etm.seq_12_event = 0x406F;
+		etm.seq_21_event = 0x406F;
+		etm.seq_23_event = 0x406F;
+		etm.seq_31_event = 0x406F;
+		etm.seq_32_event = 0x406F;
+		etm.seq_13_event = 0x406F;
+		etm.seq_curr_state = 0x0;
+		etm.ctxid_idx = 0x0;
+		for (i = 0; i < etm.nr_ctxid_cmp; i++)
+			etm.ctxid_val[i] = 0x0;
+		etm.ctxid_mask = 0x0;
+		etm.sync_freq = 0x80;
+		etm.timestamp_event = 0x406F;
+	}
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+ETM_SHOW(reset);
+ETM_ATTR(reset);
+
+static ssize_t mode_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	mutex_lock(&etm.mutex);
+	etm.mode = val & ETM_MODE_ALL;
+
+	if (etm.mode & ETM_MODE_EXCLUDE)
+		etm.enable_ctrl1 |= BIT(24);
+	else
+		etm.enable_ctrl1 &= ~BIT(24);
+
+	if (etm.mode & ETM_MODE_CYCACC)
+		etm.ctrl |= BIT(12);
+	else
+		etm.ctrl &= ~BIT(12);
+
+	if (etm.mode & ETM_MODE_STALL)
+		etm.ctrl |= BIT(7);
+	else
+		etm.ctrl &= ~BIT(7);
+
+	if (etm.mode & ETM_MODE_TIMESTAMP)
+		etm.ctrl |= BIT(28);
+	else
+		etm.ctrl &= ~BIT(28);
+	if (etm.mode & ETM_MODE_CTXID)
+		etm.ctrl |= (BIT(14) | BIT(15));
+	else
+		etm.ctrl &= ~(BIT(14) | BIT(15));
+	mutex_unlock(&etm.mutex);
+
+	return n;
+}
+ETM_SHOW(mode);
+ETM_ATTR(mode);
+
+ETM_STORE(trigger_event, ETM_EVENT_MASK);
+ETM_SHOW(trigger_event);
+ETM_ATTR(trigger_event);
+
+ETM_STORE(enable_event, ETM_EVENT_MASK);
+ETM_SHOW(enable_event);
+ETM_ATTR(enable_event);
+
+ETM_STORE(fifofull_level, ETM_ALL_MASK);
+ETM_SHOW(fifofull_level);
+ETM_ATTR(fifofull_level);
+
+static ssize_t addr_idx_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+	if (val >= etm.nr_addr_cmp)
+		return -EINVAL;
+
+	/* Use mutex to ensure index doesn't change while it gets dereferenced
+	 * multiple times within a mutex block elsewhere.
+	 */
+	mutex_lock(&etm.mutex);
+	etm.addr_idx = val;
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+ETM_SHOW(addr_idx);
+ETM_ATTR(addr_idx);
+
+static ssize_t addr_single_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+	uint8_t idx;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	mutex_lock(&etm.mutex);
+	idx = etm.addr_idx;
+	if (!(etm.addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      etm.addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+		mutex_unlock(&etm.mutex);
+		return -EPERM;
+	}
+
+	etm.addr_val[idx] = val;
+	etm.addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+static ssize_t addr_single_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val;
+	uint8_t idx;
+
+	mutex_lock(&etm.mutex);
+	idx = etm.addr_idx;
+	if (!(etm.addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      etm.addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+		mutex_unlock(&etm.mutex);
+		return -EPERM;
+	}
+
+	val = etm.addr_val[idx];
+	mutex_unlock(&etm.mutex);
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+ETM_ATTR(addr_single);
+
+static ssize_t addr_range_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val1, val2;
+	uint8_t idx;
+
+	if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
+		return -EINVAL;
+	/* lower address comparator cannot have a higher address value */
+	if (val1 > val2)
+		return -EINVAL;
+
+	mutex_lock(&etm.mutex);
+	idx = etm.addr_idx;
+	if (idx % 2 != 0) {
+		mutex_unlock(&etm.mutex);
+		return -EPERM;
+	}
+	if (!((etm.addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       etm.addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (etm.addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       etm.addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+		mutex_unlock(&etm.mutex);
+		return -EPERM;
+	}
+
+	etm.addr_val[idx] = val1;
+	etm.addr_type[idx] = ETM_ADDR_TYPE_RANGE;
+	etm.addr_val[idx + 1] = val2;
+	etm.addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
+	etm.enable_ctrl1 |= (1 << (idx/2));
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+static ssize_t addr_range_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val1, val2;
+	uint8_t idx;
+
+	mutex_lock(&etm.mutex);
+	idx = etm.addr_idx;
+	if (idx % 2 != 0) {
+		mutex_unlock(&etm.mutex);
+		return -EPERM;
+	}
+	if (!((etm.addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       etm.addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (etm.addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       etm.addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+		mutex_unlock(&etm.mutex);
+		return -EPERM;
+	}
+
+	val1 = etm.addr_val[idx];
+	val2 = etm.addr_val[idx + 1];
+	mutex_unlock(&etm.mutex);
+	return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2);
+}
+ETM_ATTR(addr_range);
+
+static ssize_t addr_start_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+	uint8_t idx;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	mutex_lock(&etm.mutex);
+	idx = etm.addr_idx;
+	if (!(etm.addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      etm.addr_type[idx] == ETM_ADDR_TYPE_START)) {
+		mutex_unlock(&etm.mutex);
+		return -EPERM;
+	}
+
+	etm.addr_val[idx] = val;
+	etm.addr_type[idx] = ETM_ADDR_TYPE_START;
+	etm.startstop_ctrl |= (1 << idx);
+	etm.enable_ctrl1 |= BIT(25);
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+static ssize_t addr_start_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val;
+	uint8_t idx;
+
+	mutex_lock(&etm.mutex);
+	idx = etm.addr_idx;
+	if (!(etm.addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      etm.addr_type[idx] == ETM_ADDR_TYPE_START)) {
+		mutex_unlock(&etm.mutex);
+		return -EPERM;
+	}
+
+	val = etm.addr_val[idx];
+	mutex_unlock(&etm.mutex);
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+ETM_ATTR(addr_start);
+
+static ssize_t addr_stop_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+	uint8_t idx;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	mutex_lock(&etm.mutex);
+	idx = etm.addr_idx;
+	if (!(etm.addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      etm.addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+		mutex_unlock(&etm.mutex);
+		return -EPERM;
+	}
+
+	etm.addr_val[idx] = val;
+	etm.addr_type[idx] = ETM_ADDR_TYPE_STOP;
+	etm.startstop_ctrl |= (1 << (idx + 16));
+	etm.enable_ctrl1 |= BIT(25);
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+static ssize_t addr_stop_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val;
+	uint8_t idx;
+
+	mutex_lock(&etm.mutex);
+	idx = etm.addr_idx;
+	if (!(etm.addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      etm.addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+		mutex_unlock(&etm.mutex);
+		return -EPERM;
+	}
+
+	val = etm.addr_val[idx];
+	mutex_unlock(&etm.mutex);
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+ETM_ATTR(addr_stop);
+
+static ssize_t addr_acctype_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	mutex_lock(&etm.mutex);
+	etm.addr_acctype[etm.addr_idx] = val;
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+static ssize_t addr_acctype_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val;
+
+	mutex_lock(&etm.mutex);
+	val = etm.addr_acctype[etm.addr_idx];
+	mutex_unlock(&etm.mutex);
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+ETM_ATTR(addr_acctype);
+
+static ssize_t cntr_idx_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+	if (val >= etm.nr_cntr)
+		return -EINVAL;
+
+	/* Use mutex to ensure index doesn't change while it gets dereferenced
+	 * multiple times within a mutex block elsewhere.
+	 */
+	mutex_lock(&etm.mutex);
+	etm.cntr_idx = val;
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+ETM_SHOW(cntr_idx);
+ETM_ATTR(cntr_idx);
+
+static ssize_t cntr_rld_val_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	mutex_lock(&etm.mutex);
+	etm.cntr_rld_val[etm.cntr_idx] = val;
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+static ssize_t cntr_rld_val_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val;
+	mutex_lock(&etm.mutex);
+	val = etm.cntr_rld_val[etm.cntr_idx];
+	mutex_unlock(&etm.mutex);
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+ETM_ATTR(cntr_rld_val);
+
+static ssize_t cntr_event_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	mutex_lock(&etm.mutex);
+	etm.cntr_event[etm.cntr_idx] = val & ETM_EVENT_MASK;
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+static ssize_t cntr_event_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val;
+
+	mutex_lock(&etm.mutex);
+	val = etm.cntr_event[etm.cntr_idx];
+	mutex_unlock(&etm.mutex);
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+ETM_ATTR(cntr_event);
+
+static ssize_t cntr_rld_event_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	mutex_lock(&etm.mutex);
+	etm.cntr_rld_event[etm.cntr_idx] = val & ETM_EVENT_MASK;
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+static ssize_t cntr_rld_event_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val;
+
+	mutex_lock(&etm.mutex);
+	val = etm.cntr_rld_event[etm.cntr_idx];
+	mutex_unlock(&etm.mutex);
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+ETM_ATTR(cntr_rld_event);
+
+static ssize_t cntr_val_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	mutex_lock(&etm.mutex);
+	etm.cntr_val[etm.cntr_idx] = val;
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+static ssize_t cntr_val_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val;
+
+	mutex_lock(&etm.mutex);
+	val = etm.cntr_val[etm.cntr_idx];
+	mutex_unlock(&etm.mutex);
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+ETM_ATTR(cntr_val);
+
+ETM_STORE(seq_12_event, ETM_EVENT_MASK);
+ETM_SHOW(seq_12_event);
+ETM_ATTR(seq_12_event);
+
+ETM_STORE(seq_21_event, ETM_EVENT_MASK);
+ETM_SHOW(seq_21_event);
+ETM_ATTR(seq_21_event);
+
+ETM_STORE(seq_23_event, ETM_EVENT_MASK);
+ETM_SHOW(seq_23_event);
+ETM_ATTR(seq_23_event);
+
+ETM_STORE(seq_31_event, ETM_EVENT_MASK);
+ETM_SHOW(seq_31_event);
+ETM_ATTR(seq_31_event);
+
+ETM_STORE(seq_32_event, ETM_EVENT_MASK);
+ETM_SHOW(seq_32_event);
+ETM_ATTR(seq_32_event);
+
+ETM_STORE(seq_13_event, ETM_EVENT_MASK);
+ETM_SHOW(seq_13_event);
+ETM_ATTR(seq_13_event);
+
+static ssize_t seq_curr_state_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+	if (val > ETM_SEQ_STATE_MAX_VAL)
+		return -EINVAL;
+
+	etm.seq_curr_state = val;
+	return n;
+}
+ETM_SHOW(seq_curr_state);
+ETM_ATTR(seq_curr_state);
+
+static ssize_t ctxid_idx_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+	if (val >= etm.nr_ctxid_cmp)
+		return -EINVAL;
+
+	/* Use mutex to ensure index doesn't change while it gets dereferenced
+	 * multiple times within a mutex block elsewhere.
+	 */
+	mutex_lock(&etm.mutex);
+	etm.ctxid_idx = val;
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+ETM_SHOW(ctxid_idx);
+ETM_ATTR(ctxid_idx);
+
+static ssize_t ctxid_val_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	mutex_lock(&etm.mutex);
+	etm.ctxid_val[etm.ctxid_idx] = val;
+	mutex_unlock(&etm.mutex);
+	return n;
+}
+static ssize_t ctxid_val_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val;
+
+	mutex_lock(&etm.mutex);
+	val = etm.ctxid_val[etm.ctxid_idx];
+	mutex_unlock(&etm.mutex);
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+ETM_ATTR(ctxid_val);
+
+ETM_STORE(ctxid_mask, ETM_ALL_MASK);
+ETM_SHOW(ctxid_mask);
+ETM_ATTR(ctxid_mask);
+
+ETM_STORE(sync_freq, ETM_SYNC_MASK);
+ETM_SHOW(sync_freq);
+ETM_ATTR(sync_freq);
+
+ETM_STORE(timestamp_event, ETM_EVENT_MASK);
+ETM_SHOW(timestamp_event);
+ETM_ATTR(timestamp_event);
+
+static struct attribute *etm_attrs[] = {
+	&nr_addr_cmp_attr.attr,
+	&nr_cntr_attr.attr,
+	&nr_ctxid_cmp_attr.attr,
+	&reset_attr.attr,
+	&mode_attr.attr,
+	&trigger_event_attr.attr,
+	&enable_event_attr.attr,
+	&fifofull_level_attr.attr,
+	&addr_idx_attr.attr,
+	&addr_single_attr.attr,
+	&addr_range_attr.attr,
+	&addr_start_attr.attr,
+	&addr_stop_attr.attr,
+	&addr_acctype_attr.attr,
+	&cntr_idx_attr.attr,
+	&cntr_rld_val_attr.attr,
+	&cntr_event_attr.attr,
+	&cntr_rld_event_attr.attr,
+	&cntr_val_attr.attr,
+	&seq_12_event_attr.attr,
+	&seq_21_event_attr.attr,
+	&seq_23_event_attr.attr,
+	&seq_31_event_attr.attr,
+	&seq_32_event_attr.attr,
+	&seq_13_event_attr.attr,
+	&seq_curr_state_attr.attr,
+	&ctxid_idx_attr.attr,
+	&ctxid_val_attr.attr,
+	&ctxid_mask_attr.attr,
+	&sync_freq_attr.attr,
+	&timestamp_event_attr.attr,
+	NULL,
+};
+
+static struct attribute_group etm_attr_grp = {
+	.attrs = etm_attrs,
+};
+
+static int __devinit etm_sysfs_init(void)
+{
+	int ret;
+
+	etm.kobj = kobject_create_and_add("etm", qdss_get_modulekobj());
+	if (!etm.kobj) {
+		dev_err(etm.dev, "failed to create ETM sysfs kobject\n");
+		ret = -ENOMEM;
+		goto err_create;
+	}
+
+	ret = sysfs_create_file(etm.kobj, &enabled_attr.attr);
+	if (ret) {
+		dev_err(etm.dev, "failed to create ETM sysfs enabled"
+		" attribute\n");
+		goto err_file;
+	}
+
+	if (sysfs_create_group(etm.kobj, &etm_attr_grp))
+		dev_err(etm.dev, "failed to create ETM sysfs group\n");
+
+	return 0;
+err_file:
+	kobject_put(etm.kobj);
+err_create:
+	return ret;
+}
+
+static void __devexit etm_sysfs_exit(void)
+{
+	sysfs_remove_group(etm.kobj, &etm_attr_grp);
+	sysfs_remove_file(etm.kobj, &enabled_attr.attr);
+	kobject_put(etm.kobj);
+}
+
+static bool __devinit etm_arch_supported(uint8_t arch)
+{
+	switch (arch) {
+	case PFT_ARCH_V1_1:
+		break;
+	default:
+		return false;
+	}
+	return true;
+}
+
+static int __devinit etm_arch_init(void)
+{
+	int ret, i;
+	/* use cpu 0 for setup */
+	int cpu = 0;
+	uint32_t etmidr;
+	uint32_t etmccr;
+
+	/* Unlock OS lock first to allow memory mapped reads and writes */
+	etm_os_unlock(NULL);
+	smp_call_function(etm_os_unlock, NULL, 1);
+	ETM_UNLOCK(cpu);
+	/* Vote for ETM power/clock enable */
+	etm_clr_pwrdwn(cpu);
+	/* Set prog bit. It will be set from reset but this is included to
+	 * ensure it is set
+	 */
+	etm_set_prog(cpu);
+
+	/* find all capabilities */
+	etmidr = etm_readl(etm, cpu, ETMIDR);
+	etm.arch = BMVAL(etmidr, 4, 11);
+	if (etm_arch_supported(etm.arch) == false) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	etmccr = etm_readl(etm, cpu, ETMCCR);
+	etm.nr_addr_cmp = BMVAL(etmccr, 0, 3) * 2;
+	etm.nr_cntr = BMVAL(etmccr, 13, 15);
+	etm.nr_ext_inp = BMVAL(etmccr, 17, 19);
+	etm.nr_ext_out = BMVAL(etmccr, 20, 22);
+	etm.nr_ctxid_cmp = BMVAL(etmccr, 24, 25);
+
+	if (cpu_is_krait_v1()) {
+		/* Krait pass1 doesn't support include filtering and non-cycle
+		 * accurate tracing
+		 */
+		etm.mode = (ETM_MODE_EXCLUDE | ETM_MODE_CYCACC);
+		etm.ctrl = 0x1000;
+		etm.enable_ctrl1 = 0x1000000;
+		for (i = 0; i < etm.nr_addr_cmp; i++) {
+			etm.addr_val[i] = 0x0;
+			etm.addr_acctype[i] = 0x0;
+			etm.addr_type[i] = ETM_ADDR_TYPE_NONE;
+		}
+	}
+
+	/* Vote for ETM power/clock disable */
+	etm_set_pwrdwn(cpu);
+	ETM_LOCK(cpu);
+
+	return 0;
+err:
+	return ret;
+}
+
+static int __devinit etm_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;
+	}
+
+	etm.base = ioremap_nocache(res->start, resource_size(res));
+	if (!etm.base) {
+		ret = -EINVAL;
+		goto err_ioremap;
+	}
+
+	etm.dev = &pdev->dev;
+
+	mutex_init(&etm.mutex);
+	wake_lock_init(&etm.wake_lock, WAKE_LOCK_SUSPEND, "msm_etm");
+	pm_qos_add_request(&etm.qos_req, PM_QOS_CPU_DMA_LATENCY,
+						PM_QOS_DEFAULT_VALUE);
+	etm.src = qdss_get("msm_etm");
+	if (IS_ERR(etm.src)) {
+		ret = PTR_ERR(etm.src);
+		goto err_qdssget;
+	}
+
+	ret = qdss_clk_enable();
+	if (ret)
+		goto err_clk;
+
+	ret = etm_arch_init();
+	if (ret)
+		goto err_arch;
+
+	ret = etm_sysfs_init();
+	if (ret)
+		goto err_sysfs;
+
+	etm.enabled = false;
+
+	qdss_clk_disable();
+
+	dev_info(etm.dev, "ETM initialized\n");
+
+	if (etm_boot_enable)
+		etm_enable();
+
+	return 0;
+
+err_sysfs:
+err_arch:
+	qdss_clk_disable();
+err_clk:
+	qdss_put(etm.src);
+err_qdssget:
+	pm_qos_remove_request(&etm.qos_req);
+	wake_lock_destroy(&etm.wake_lock);
+	mutex_destroy(&etm.mutex);
+	iounmap(etm.base);
+err_ioremap:
+err_res:
+	dev_err(etm.dev, "ETM init failed\n");
+	return ret;
+}
+
+static int __devexit etm_remove(struct platform_device *pdev)
+{
+	if (etm.enabled)
+		etm_disable();
+	etm_sysfs_exit();
+	qdss_put(etm.src);
+	pm_qos_remove_request(&etm.qos_req);
+	wake_lock_destroy(&etm.wake_lock);
+	mutex_destroy(&etm.mutex);
+	iounmap(etm.base);
+
+	return 0;
+}
+
+static struct of_device_id etm_match[] = {
+	{.compatible = "qcom,msm-etm"},
+	{}
+};
+
+static struct platform_driver etm_driver = {
+	.probe          = etm_probe,
+	.remove         = __devexit_p(etm_remove),
+	.driver         = {
+		.name   = "msm_etm",
+		.owner	= THIS_MODULE,
+		.of_match_table = etm_match,
+	},
+};
+
+int __init etm_init(void)
+{
+	return platform_driver_register(&etm_driver);
+}
+module_init(etm_init);
+
+void __exit etm_exit(void)
+{
+	platform_driver_unregister(&etm_driver);
+}
+module_exit(etm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CoreSight Program Flow Trace driver");
diff --git a/drivers/cs/qdss-funnel.c b/drivers/cs/qdss-funnel.c
new file mode 100644
index 0000000..1c19ebd
--- /dev/null
+++ b/drivers/cs/qdss-funnel.c
@@ -0,0 +1,239 @@
+/* Copyright (c) 2011-2012, 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/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include "qdss-priv.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 FUNNEL_FUNCTL			(0x000)
+#define FUNNEL_PRICTL			(0x004)
+#define FUNNEL_ITATBDATA0		(0xEEC)
+#define FUNNEL_ITATBCTR2		(0xEF0)
+#define FUNNEL_ITATBCTR1		(0xEF4)
+#define FUNNEL_ITATBCTR0		(0xEF8)
+
+
+#define FUNNEL_LOCK(id)							\
+do {									\
+	mb();								\
+	funnel_writel(funnel, id, 0x0, CS_LAR);				\
+} while (0)
+#define FUNNEL_UNLOCK(id)						\
+do {									\
+	funnel_writel(funnel, id, CS_UNLOCK_MAGIC, CS_LAR);		\
+	mb();								\
+} while (0)
+
+#define FUNNEL_HOLDTIME_MASK		(0xF00)
+#define FUNNEL_HOLDTIME_SHFT		(0x8)
+#define FUNNEL_HOLDTIME			(0x7 << FUNNEL_HOLDTIME_SHFT)
+
+struct funnel_ctx {
+	void __iomem	*base;
+	bool		enabled;
+	struct mutex	mutex;
+	struct device	*dev;
+	struct kobject	*kobj;
+	uint32_t	priority;
+};
+
+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, FUNNEL_FUNCTL);
+	functl &= ~FUNNEL_HOLDTIME_MASK;
+	functl |= FUNNEL_HOLDTIME;
+	functl |= port_mask;
+	funnel_writel(funnel, id, functl, FUNNEL_FUNCTL);
+	funnel_writel(funnel, id, funnel.priority, FUNNEL_PRICTL);
+
+	FUNNEL_LOCK(id);
+}
+
+void funnel_enable(uint8_t id, uint32_t port_mask)
+{
+	mutex_lock(&funnel.mutex);
+	__funnel_enable(id, port_mask);
+	funnel.enabled = true;
+	dev_info(funnel.dev, "FUNNEL port mask 0x%lx enabled\n",
+					(unsigned long) port_mask);
+	mutex_unlock(&funnel.mutex);
+}
+
+static void __funnel_disable(uint8_t id, uint32_t port_mask)
+{
+	uint32_t functl;
+
+	FUNNEL_UNLOCK(id);
+
+	functl = funnel_readl(funnel, id, FUNNEL_FUNCTL);
+	functl &= ~port_mask;
+	funnel_writel(funnel, id, functl, FUNNEL_FUNCTL);
+
+	FUNNEL_LOCK(id);
+}
+
+void funnel_disable(uint8_t id, uint32_t port_mask)
+{
+	mutex_lock(&funnel.mutex);
+	__funnel_disable(id, port_mask);
+	funnel.enabled = false;
+	dev_info(funnel.dev, "FUNNEL port mask 0x%lx disabled\n",
+					(unsigned long) port_mask);
+	mutex_unlock(&funnel.mutex);
+}
+
+#define FUNNEL_ATTR(__name)						\
+static struct kobj_attribute __name##_attr =				\
+	__ATTR(__name, S_IRUGO | S_IWUSR, __name##_show, __name##_store)
+
+static ssize_t priority_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	funnel.priority = val;
+	return n;
+}
+static ssize_t priority_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val = funnel.priority;
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+FUNNEL_ATTR(priority);
+
+static int __devinit funnel_sysfs_init(void)
+{
+	int ret;
+
+	funnel.kobj = kobject_create_and_add("funnel", qdss_get_modulekobj());
+	if (!funnel.kobj) {
+		dev_err(funnel.dev, "failed to create FUNNEL sysfs kobject\n");
+		ret = -ENOMEM;
+		goto err_create;
+	}
+
+	ret = sysfs_create_file(funnel.kobj, &priority_attr.attr);
+	if (ret) {
+		dev_err(funnel.dev, "failed to create FUNNEL sysfs priority"
+		" attribute\n");
+		goto err_file;
+	}
+
+	return 0;
+err_file:
+	kobject_put(funnel.kobj);
+err_create:
+	return ret;
+}
+
+static void __devexit funnel_sysfs_exit(void)
+{
+	sysfs_remove_file(funnel.kobj, &priority_attr.attr);
+	kobject_put(funnel.kobj);
+}
+
+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;
+
+	mutex_init(&funnel.mutex);
+
+	funnel_sysfs_init();
+
+	dev_info(funnel.dev, "FUNNEL initialized\n");
+	return 0;
+
+err_ioremap:
+err_res:
+	dev_err(funnel.dev, "FUNNEL init failed\n");
+	return ret;
+}
+
+static int __devexit funnel_remove(struct platform_device *pdev)
+{
+	if (funnel.enabled)
+		funnel_disable(0x0, 0xFF);
+	funnel_sysfs_exit();
+	mutex_destroy(&funnel.mutex);
+	iounmap(funnel.base);
+
+	return 0;
+}
+
+static struct of_device_id funnel_match[] = {
+	{.compatible = "qcom,msm-funnel"},
+	{}
+};
+
+static struct platform_driver funnel_driver = {
+	.probe          = funnel_probe,
+	.remove         = __devexit_p(funnel_remove),
+	.driver         = {
+		.name   = "msm_funnel",
+		.owner	= THIS_MODULE,
+		.of_match_table = funnel_match,
+	},
+};
+
+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 driver");
diff --git a/drivers/cs/qdss-priv.h b/drivers/cs/qdss-priv.h
new file mode 100644
index 0000000..868514c
--- /dev/null
+++ b/drivers/cs/qdss-priv.h
@@ -0,0 +1,70 @@
+/* Copyright (c) 2011-2012, 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_
+
+#include <linux/bitops.h>
+#include <linux/qdss.h>
+
+/* Coresight management registers (0xF00-0xFCC)
+ * 0xFA0 - 0xFA4: Management	registers in PFTv1.0
+ *		  Trace		registers in PFTv1.1
+ */
+#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)
+
+/* DBGv7 with baseline CP14 registers implemented */
+#define ARM_DEBUG_ARCH_V7B	(0x3)
+/* DBGv7 with all CP14 registers implemented */
+#define ARM_DEBUG_ARCH_V7	(0x4)
+#define ARM_DEBUG_ARCH_V7_1	(0x5)
+#define ETM_ARCH_V3_3		(0x23)
+#define PFT_ARCH_V1_1		(0x31)
+
+#define TIMEOUT_US		(100)
+#define CS_UNLOCK_MAGIC		(0xC5ACCE55)
+
+#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)
+
+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);
+
+struct kobject *qdss_get_modulekobj(void);
+
+#endif
diff --git a/drivers/cs/qdss-stm.c b/drivers/cs/qdss-stm.c
new file mode 100644
index 0000000..0d44c1a
--- /dev/null
+++ b/drivers/cs/qdss-stm.c
@@ -0,0 +1,602 @@
+/* Copyright (c) 2012, 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/init.h>
+#include <linux/types.h>
+#include <linux/device.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 <asm/unaligned.h>
+#include <mach/stm.h>
+
+#include "qdss-priv.h"
+
+#define stm_writel(stm, val, off)	\
+			__raw_writel((val), stm.base + off)
+#define stm_readl(stm, val, off)	\
+			__raw_readl(stm.base + off)
+
+#define NR_STM_CHANNEL		(32)
+#define BYTES_PER_CHANNEL	(256)
+
+enum {
+	STM_PKT_TYPE_DATA	= 0x98,
+	STM_PKT_TYPE_FLAG	= 0xE8,
+	STM_PKT_TYPE_TRIG	= 0xF8,
+};
+
+enum {
+	STM_OPTION_MARKED	= 0x10,
+};
+
+#define STM_TRACE_BUF_SIZE	(1024)
+
+#define OST_START_TOKEN		(0x30)
+#define OST_VERSION		(0x1)
+
+#define stm_channel_addr(ch)						\
+				(stm.chs.base + (ch * BYTES_PER_CHANNEL))
+#define stm_channel_off(type, opts)	(type & ~opts)
+
+#define STM_LOCK()							\
+do {									\
+	mb();								\
+	stm_writel(stm, 0x0, CS_LAR);					\
+} while (0)
+#define STM_UNLOCK()							\
+do {									\
+	stm_writel(stm, CS_UNLOCK_MAGIC, CS_LAR);			\
+	mb();								\
+} while (0)
+
+#define STMSPER		(0xE00)
+#define STMSPTER	(0xE20)
+#define STMTCSR		(0xE80)
+#define STMSYNCR	(0xE90)
+
+#ifdef CONFIG_MSM_QDSS_STM_DEFAULT_ENABLE
+static int stm_boot_enable = 1;
+#else
+static int stm_boot_enable;
+#endif
+
+module_param_named(
+	stm_boot_enable, stm_boot_enable, int, S_IRUGO
+);
+
+static int stm_boot_nr_channel;
+
+module_param_named(
+	stm_boot_nr_channel, stm_boot_nr_channel, int, S_IRUGO
+);
+
+struct channel_space {
+	void __iomem		*base;
+	unsigned long		*bitmap;
+};
+
+struct stm_ctx {
+	void __iomem		*base;
+	bool			enabled;
+	struct qdss_source	*src;
+	struct device		*dev;
+	struct kobject		*kobj;
+	uint32_t		entity;
+	struct channel_space	chs;
+};
+
+static struct stm_ctx stm = {
+	.entity		= OST_ENTITY_ALL,
+};
+
+
+static void __stm_enable(void)
+{
+	STM_UNLOCK();
+
+	stm_writel(stm, 0x80, STMSYNCR);
+	stm_writel(stm, 0xFFFFFFFF, STMSPTER);
+	stm_writel(stm, 0xFFFFFFFF, STMSPER);
+	stm_writel(stm, 0x30003, STMTCSR);
+
+	STM_LOCK();
+}
+
+static int stm_enable(void)
+{
+	int ret;
+
+	if (stm.enabled) {
+		dev_err(stm.dev, "STM tracing already enabled\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = qdss_enable(stm.src);
+	if (ret)
+		goto err;
+
+	__stm_enable();
+
+	stm.enabled = true;
+
+	dev_info(stm.dev, "STM tracing enabled\n");
+	return 0;
+
+err:
+	return ret;
+}
+
+static void __stm_disable(void)
+{
+	STM_UNLOCK();
+
+	stm_writel(stm, 0x30000, STMTCSR);
+	stm_writel(stm, 0x0, STMSPER);
+	stm_writel(stm, 0x0, STMSPTER);
+
+	STM_LOCK();
+}
+
+static int stm_disable(void)
+{
+	int ret;
+
+	if (!stm.enabled) {
+		dev_err(stm.dev, "STM tracing already disabled\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	__stm_disable();
+
+	qdss_disable(stm.src);
+
+	stm.enabled = false;
+
+	dev_info(stm.dev, "STM tracing disabled\n");
+	return 0;
+
+err:
+	return ret;
+}
+
+static uint32_t stm_channel_alloc(uint32_t off)
+{
+	uint32_t ch;
+
+	do {
+		ch = find_next_zero_bit(stm.chs.bitmap,	NR_STM_CHANNEL, off);
+	} while ((ch < NR_STM_CHANNEL) && test_and_set_bit(ch, stm.chs.bitmap));
+
+	return ch;
+}
+
+static void stm_channel_free(uint32_t ch)
+{
+	clear_bit(ch, stm.chs.bitmap);
+}
+
+static int stm_send(void *addr, const void *data, uint32_t size)
+{
+	uint64_t prepad = 0;
+	uint64_t postpad = 0;
+	char *pad;
+	uint8_t off, endoff;
+	uint32_t len = size;
+
+	/* only 64bit writes are supported, we rely on the compiler to
+	 * generate STRD instruction for the casted 64bit assignments
+	 */
+
+	off = (unsigned long)data & 0x7;
+
+	if (off) {
+		endoff = 8 - off;
+		pad = (char *)&prepad;
+		pad += off;
+
+		while (endoff && size) {
+			*pad++ = *(char *)data++;
+			endoff--;
+			size--;
+		}
+		*(volatile uint64_t __force *)addr = prepad;
+	}
+
+	/* now we are 64bit aligned */
+	while (size >= 8) {
+		*(volatile uint64_t __force *)addr = *(uint64_t *)data;
+		data += 8;
+		size -= 8;
+	}
+
+	if (size) {
+		pad = (char *)&postpad;
+
+		while (size) {
+			*pad++ = *(char *)data++;
+			size--;
+		}
+		*(volatile uint64_t __force *)addr = postpad;
+	}
+
+	return roundup(len + off, 8);
+}
+
+static int stm_trace_ost_header(unsigned long ch_addr, uint32_t options,
+				uint8_t entity_id, uint8_t proto_id,
+				const void *payload_data, uint32_t payload_size)
+{
+	void *addr;
+	uint8_t prepad_size;
+	uint64_t header;
+	char *hdr;
+
+	hdr = (char *)&header;
+
+	hdr[0] = OST_START_TOKEN;
+	hdr[1] = OST_VERSION;
+	hdr[2] = entity_id;
+	hdr[3] = proto_id;
+	prepad_size = (unsigned long)payload_data & 0x7;
+	*(uint32_t *)(hdr + 4) = (prepad_size << 24) | payload_size;
+
+	/* for 64bit writes, header is expected to be of the D32M, D32M */
+	options |= STM_OPTION_MARKED;
+	options &= ~STM_OPTION_TIMESTAMPED;
+	addr =  (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_DATA, options));
+
+	return stm_send(addr, &header, sizeof(header));
+}
+
+static int stm_trace_data(unsigned long ch_addr, uint32_t options,
+			  const void *data, uint32_t size)
+{
+	void *addr;
+
+	options &= ~STM_OPTION_TIMESTAMPED;
+	addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_DATA, options));
+
+	return stm_send(addr, data, size);
+}
+
+static int stm_trace_ost_tail(unsigned long ch_addr, uint32_t options)
+{
+	void *addr;
+	uint64_t tail = 0x0;
+
+	addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_FLAG, options));
+
+	return stm_send(addr, &tail, sizeof(tail));
+}
+
+static inline int __stm_trace(uint32_t options, uint8_t entity_id,
+			      uint8_t proto_id, const void *data, uint32_t size)
+{
+	int len = 0;
+	uint32_t ch;
+	unsigned long ch_addr;
+
+	/* allocate channel and get the channel address */
+	ch = stm_channel_alloc(0);
+	ch_addr = (unsigned long)stm_channel_addr(ch);
+
+	/* send the ost header */
+	len += stm_trace_ost_header(ch_addr, options, entity_id, proto_id, data,
+				    size);
+
+	/* send the payload data */
+	len += stm_trace_data(ch_addr, options, data, size);
+
+	/* send the ost tail */
+	len += stm_trace_ost_tail(ch_addr, options);
+
+	/* we are done, free the channel */
+	stm_channel_free(ch);
+
+	return len;
+}
+
+/**
+ * stm_trace - trace the binary or string data through STM
+ * @options: tracing options - guaranteed, timestamped, etc
+ * @entity_id: entity representing the trace data
+ * @proto_id: protocol id to distinguish between different binary formats
+ * @data: pointer to binary or string data buffer
+ * @size: size of data to send
+ *
+ * Packetizes the data as the payload to an OST packet and sends it over STM
+ *
+ * CONTEXT:
+ * Can be called from any context.
+ *
+ * RETURNS:
+ * number of bytes transfered over STM
+ */
+int stm_trace(uint32_t options, uint8_t entity_id, uint8_t proto_id,
+	      const void *data, uint32_t size)
+{
+	/* we don't support sizes more than 24bits (0 to 23) */
+	if (!(stm.enabled && (stm.entity & entity_id) &&
+	      (size < 0x1000000)))
+		return 0;
+
+	return __stm_trace(options, entity_id, proto_id, data, size);
+}
+EXPORT_SYMBOL(stm_trace);
+
+static ssize_t stm_write(struct file *file, const char __user *data,
+			 size_t size, loff_t *ppos)
+{
+	char *buf;
+
+	if (!stm.enabled)
+		return -EINVAL;
+
+	if (!(stm.entity & OST_ENTITY_DEV_NODE))
+		return size;
+
+	if (size > STM_TRACE_BUF_SIZE)
+		size = STM_TRACE_BUF_SIZE;
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (copy_from_user(buf, data, size)) {
+		kfree(buf);
+		dev_dbg(stm.dev, "%s: copy_from_user failed\n", __func__);
+		return -EFAULT;
+	}
+
+	__stm_trace(STM_OPTION_TIMESTAMPED, OST_ENTITY_DEV_NODE, 0, buf, size);
+
+	kfree(buf);
+
+	return size;
+}
+
+static const struct file_operations stm_fops = {
+	.owner		= THIS_MODULE,
+	.write		= stm_write,
+	.llseek		= no_llseek,
+};
+
+static struct miscdevice stm_misc = {
+	.name		= "msm_stm",
+	.minor		= MISC_DYNAMIC_MINOR,
+	.fops		= &stm_fops,
+};
+
+#define STM_ATTR(__name)						\
+static struct kobj_attribute __name##_attr =				\
+	__ATTR(__name, S_IRUGO | S_IWUSR, __name##_show, __name##_store)
+
+static ssize_t enabled_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	int ret = 0;
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	if (val)
+		ret = stm_enable();
+	else
+		ret = stm_disable();
+
+	if (ret)
+		return ret;
+	return n;
+}
+static ssize_t enabled_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val = stm.enabled;
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+STM_ATTR(enabled);
+
+static ssize_t entity_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	stm.entity = val;
+	return n;
+}
+static ssize_t entity_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val = stm.entity;
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+STM_ATTR(entity);
+
+static int __devinit stm_sysfs_init(void)
+{
+	int ret;
+
+	stm.kobj = kobject_create_and_add("stm", qdss_get_modulekobj());
+	if (!stm.kobj) {
+		dev_err(stm.dev, "failed to create STM sysfs kobject\n");
+		ret = -ENOMEM;
+		goto err_create;
+	}
+
+	ret = sysfs_create_file(stm.kobj, &enabled_attr.attr);
+	if (ret) {
+		dev_err(stm.dev, "failed to create STM sysfs enabled attr\n");
+		goto err_file;
+	}
+
+	if (sysfs_create_file(stm.kobj, &entity_attr.attr))
+		dev_err(stm.dev, "failed to create STM sysfs entity attr\n");
+
+	return 0;
+err_file:
+	kobject_put(stm.kobj);
+err_create:
+	return ret;
+}
+
+static void __devexit stm_sysfs_exit(void)
+{
+	sysfs_remove_file(stm.kobj, &entity_attr.attr);
+	sysfs_remove_file(stm.kobj, &enabled_attr.attr);
+	kobject_put(stm.kobj);
+}
+
+static int __devinit stm_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct resource *res;
+	size_t res_size, bitmap_size;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -EINVAL;
+		goto err_res0;
+	}
+
+	stm.base = ioremap_nocache(res->start, resource_size(res));
+	if (!stm.base) {
+		ret = -EINVAL;
+		goto err_ioremap0;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		ret = -EINVAL;
+		goto err_res1;
+	}
+
+	if (stm_boot_nr_channel) {
+		res_size = min((resource_size_t)(stm_boot_nr_channel *
+				  BYTES_PER_CHANNEL), resource_size(res));
+		bitmap_size = stm_boot_nr_channel * sizeof(long);
+	} else {
+		res_size = min((resource_size_t)(NR_STM_CHANNEL *
+				 BYTES_PER_CHANNEL), resource_size(res));
+		bitmap_size = NR_STM_CHANNEL * sizeof(long);
+	}
+
+	stm.chs.bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+	if (!stm.chs.bitmap) {
+		ret = -ENOMEM;
+		goto err_bitmap;
+	}
+
+	stm.chs.base = ioremap_nocache(res->start, res_size);
+	if (!stm.chs.base) {
+		ret = -EINVAL;
+		goto err_ioremap1;
+	}
+
+	stm.dev = &pdev->dev;
+
+	ret = misc_register(&stm_misc);
+	if (ret)
+		goto err_misc;
+
+	stm.src = qdss_get("msm_stm");
+	if (IS_ERR(stm.src)) {
+		ret = PTR_ERR(stm.src);
+		goto err_qdssget;
+	}
+
+	ret = stm_sysfs_init();
+	if (ret)
+		goto err_sysfs;
+
+	if (stm_boot_enable)
+		stm_enable();
+
+	dev_info(stm.dev, "STM initialized\n");
+	return 0;
+
+err_sysfs:
+	qdss_put(stm.src);
+err_qdssget:
+	misc_deregister(&stm_misc);
+err_misc:
+	iounmap(stm.chs.base);
+err_ioremap1:
+	kfree(stm.chs.bitmap);
+err_bitmap:
+err_res1:
+	iounmap(stm.base);
+err_ioremap0:
+err_res0:
+	dev_err(stm.dev, "STM init failed\n");
+	return ret;
+}
+
+static int __devexit stm_remove(struct platform_device *pdev)
+{
+	if (stm.enabled)
+		stm_disable();
+	stm_sysfs_exit();
+	qdss_put(stm.src);
+	misc_deregister(&stm_misc);
+	iounmap(stm.chs.base);
+	kfree(stm.chs.bitmap);
+	iounmap(stm.base);
+
+	return 0;
+}
+
+static struct of_device_id stm_match[] = {
+	{.compatible = "qcom,msm-stm"},
+	{}
+};
+
+static struct platform_driver stm_driver = {
+	.probe          = stm_probe,
+	.remove         = __devexit_p(stm_remove),
+	.driver         = {
+		.name   = "msm_stm",
+		.owner	= THIS_MODULE,
+		.of_match_table = stm_match,
+	},
+};
+
+static int __init stm_init(void)
+{
+	return platform_driver_register(&stm_driver);
+}
+module_init(stm_init);
+
+static void __exit stm_exit(void)
+{
+	platform_driver_unregister(&stm_driver);
+}
+module_exit(stm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CoreSight System Trace Macrocell driver");
diff --git a/drivers/cs/qdss-tpiu.c b/drivers/cs/qdss-tpiu.c
new file mode 100644
index 0000000..23905f0
--- /dev/null
+++ b/drivers/cs/qdss-tpiu.c
@@ -0,0 +1,148 @@
+/* Copyright (c) 2011-2012, 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/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include "qdss-priv.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_SUPP_PORTSZ				(0x000)
+#define TPIU_CURR_PORTSZ				(0x004)
+#define TPIU_SUPP_TRIGMODES				(0x100)
+#define TPIU_TRIG_CNTRVAL				(0x104)
+#define TPIU_TRIG_MULT					(0x108)
+#define TPIU_SUPP_TESTPATM				(0x200)
+#define TPIU_CURR_TESTPATM				(0x204)
+#define TPIU_TEST_PATREPCNTR				(0x208)
+#define TPIU_FFSR					(0x300)
+#define TPIU_FFCR					(0x304)
+#define TPIU_FSYNC_CNTR					(0x308)
+#define TPIU_EXTCTL_INPORT				(0x400)
+#define TPIU_EXTCTL_OUTPORT				(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, 0x0, CS_LAR);					\
+} while (0)
+#define TPIU_UNLOCK()							\
+do {									\
+	tpiu_writel(tpiu, CS_UNLOCK_MAGIC, CS_LAR);			\
+	mb();								\
+} while (0)
+
+struct tpiu_ctx {
+	void __iomem	*base;
+	bool		enabled;
+	struct device	*dev;
+};
+
+static struct tpiu_ctx tpiu;
+
+static void __tpiu_disable(void)
+{
+	TPIU_UNLOCK();
+
+	tpiu_writel(tpiu, 0x3000, TPIU_FFCR);
+	tpiu_writel(tpiu, 0x3040, TPIU_FFCR);
+
+	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;
+
+	dev_info(tpiu.dev, "TPIU initialized\n");
+	return 0;
+
+err_ioremap:
+err_res:
+	dev_err(tpiu.dev, "TPIU init failed\n");
+	return ret;
+}
+
+static int __devexit tpiu_remove(struct platform_device *pdev)
+{
+	if (tpiu.enabled)
+		tpiu_disable();
+	iounmap(tpiu.base);
+
+	return 0;
+}
+
+static struct of_device_id tpiu_match[] = {
+	{.compatible = "qcom,msm-tpiu"},
+	{}
+};
+
+static struct platform_driver tpiu_driver = {
+	.probe          = tpiu_probe,
+	.remove         = __devexit_p(tpiu_remove),
+	.driver         = {
+		.name   = "msm_tpiu",
+		.owner	= THIS_MODULE,
+		.of_match_table = tpiu_match,
+	},
+};
+
+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 driver");
diff --git a/drivers/cs/qdss.c b/drivers/cs/qdss.c
new file mode 100644
index 0000000..a7e8b85
--- /dev/null
+++ b/drivers/cs/qdss.c
@@ -0,0 +1,388 @@
+/* Copyright (c) 2012, 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/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/clk.h>
+
+#include "qdss-priv.h"
+
+#define MAX_STR_LEN	(65535)
+
+enum {
+	QDSS_CLK_OFF,
+	QDSS_CLK_ON_DBG,
+	QDSS_CLK_ON_HSDBG,
+};
+
+/*
+ * Exclusion rules for structure fields.
+ *
+ * S: qdss.sources_mutex protected.
+ * I: qdss.sink_mutex protected.
+ * C: qdss.clk_mutex protected.
+ */
+struct qdss_ctx {
+	struct kobject			*modulekobj;
+	uint8_t				afamily;
+	struct list_head		sources;	/* S: sources list */
+	struct mutex			sources_mutex;
+	uint8_t				sink_count;	/* I: sink count */
+	struct mutex			sink_mutex;
+	uint8_t				max_clk;
+	struct clk			*clk;
+};
+
+static struct qdss_ctx qdss;
+
+/**
+ * qdss_get - get the qdss source handle
+ * @name: name of the qdss source
+ *
+ * Searches the sources list to get the qdss source handle for this source.
+ *
+ * CONTEXT:
+ * Typically called from init or probe functions
+ *
+ * RETURNS:
+ * pointer to struct qdss_source on success, %NULL on failure
+ */
+struct qdss_source *qdss_get(const char *name)
+{
+	struct qdss_source *src, *source = NULL;
+
+	mutex_lock(&qdss.sources_mutex);
+	list_for_each_entry(src, &qdss.sources, link) {
+		if (src->name) {
+			if (strncmp(src->name, name, MAX_STR_LEN))
+				continue;
+			source = src;
+			break;
+		}
+	}
+	mutex_unlock(&qdss.sources_mutex);
+
+	return source ? source : ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(qdss_get);
+
+/**
+ * qdss_put - release the qdss source handle
+ * @name: name of the qdss source
+ *
+ * CONTEXT:
+ * Typically called from driver remove or exit functions
+ */
+void qdss_put(struct qdss_source *src)
+{
+}
+EXPORT_SYMBOL(qdss_put);
+
+/**
+ * qdss_enable - enable qdss for the source
+ * @src: handle for the source making the call
+ *
+ * Enables qdss block (relevant funnel ports and sink) if not already
+ * enabled, otherwise increments the reference count
+ *
+ * CONTEXT:
+ * Might sleep. Uses a mutex lock. Should be called from a non-atomic context.
+ *
+ * RETURNS:
+ * 0 on success, non-zero on failure
+ */
+int qdss_enable(struct qdss_source *src)
+{
+	int ret;
+
+	if (!src)
+		return -EINVAL;
+
+	ret = qdss_clk_enable();
+	if (ret)
+		goto err;
+
+	if (qdss.afamily) {
+		mutex_lock(&qdss.sink_mutex);
+		if (qdss.sink_count == 0) {
+			etb_disable();
+			tpiu_disable();
+			/* enable ETB first to avoid losing any trace data */
+			etb_enable();
+		}
+		qdss.sink_count++;
+		mutex_unlock(&qdss.sink_mutex);
+	}
+
+	funnel_enable(0x0, src->fport_mask);
+	return 0;
+err:
+	return ret;
+}
+EXPORT_SYMBOL(qdss_enable);
+
+/**
+ * qdss_disable - disable qdss for the source
+ * @src: handle for the source making the call
+ *
+ * Disables qdss block (relevant funnel ports and sink) if the reference count
+ * is one, otherwise decrements the reference count
+ *
+ * CONTEXT:
+ * Might sleep. Uses a mutex lock. Should be called from a non-atomic context.
+ */
+void qdss_disable(struct qdss_source *src)
+{
+	if (!src)
+		return;
+
+	if (qdss.afamily) {
+		mutex_lock(&qdss.sink_mutex);
+		if (WARN(qdss.sink_count == 0, "qdss is unbalanced\n"))
+			goto out;
+		if (qdss.sink_count == 1) {
+			etb_dump();
+			etb_disable();
+		}
+		qdss.sink_count--;
+		mutex_unlock(&qdss.sink_mutex);
+	}
+
+	funnel_disable(0x0, src->fport_mask);
+	qdss_clk_disable();
+	return;
+out:
+	mutex_unlock(&qdss.sink_mutex);
+}
+EXPORT_SYMBOL(qdss_disable);
+
+/**
+ * qdss_disable_sink - force disable the current qdss sink(s)
+ *
+ * Force disable the current qdss sink(s) to stop the sink from accepting any
+ * trace generated subsequent to this call. This function should only be used
+ * as a way to stop the sink from getting polluted with trace data that is
+ * uninteresting after an event of interest has occured.
+ *
+ * CONTEXT:
+ * Can be called from atomic or non-atomic context.
+ */
+void qdss_disable_sink(void)
+{
+	if (qdss.afamily) {
+		etb_dump();
+		etb_disable();
+	}
+}
+EXPORT_SYMBOL(qdss_disable_sink);
+
+/**
+ * qdss_clk_enable - enable qdss clocks
+ *
+ * Enables qdss clocks
+ *
+ * CONTEXT:
+ * Might sleep. Should be called from a non-atomic context.
+ *
+ * RETURNS:
+ * 0 on success, non-zero on failure
+ */
+int qdss_clk_enable(void)
+{
+	return clk_prepare_enable(qdss.clk);
+}
+EXPORT_SYMBOL(qdss_clk_enable);
+
+/**
+ * qdss_clk_disable - disable qdss clocks
+ *
+ * Disables qdss clocks
+ *
+ * CONTEXT:
+ * Might sleep. Should be called from a non-atomic context.
+ */
+void qdss_clk_disable(void)
+{
+	clk_disable_unprepare(qdss.clk);
+}
+EXPORT_SYMBOL(qdss_clk_disable);
+
+struct kobject *qdss_get_modulekobj(void)
+{
+	return qdss.modulekobj;
+}
+
+#define QDSS_ATTR(name)						\
+static struct kobj_attribute name##_attr =				\
+		__ATTR(name, S_IRUGO | S_IWUSR, name##_show, name##_store)
+
+static ssize_t max_clk_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf, size_t n)
+{
+	unsigned long val;
+
+	if (sscanf(buf, "%lx", &val) != 1)
+		return -EINVAL;
+
+	qdss.max_clk = val;
+	return n;
+}
+static ssize_t max_clk_show(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			char *buf)
+{
+	unsigned long val = qdss.max_clk;
+	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+QDSS_ATTR(max_clk);
+
+static void __devinit qdss_add_sources(struct qdss_source *srcs, size_t num)
+{
+	mutex_lock(&qdss.sources_mutex);
+	while (num--) {
+		list_add_tail(&srcs->link, &qdss.sources);
+		srcs++;
+	}
+	mutex_unlock(&qdss.sources_mutex);
+}
+
+static int __init qdss_sysfs_init(void)
+{
+	int ret;
+
+	qdss.modulekobj = kset_find_obj(module_kset, KBUILD_MODNAME);
+	if (!qdss.modulekobj) {
+		pr_err("failed to find QDSS sysfs module kobject\n");
+		ret = -ENOENT;
+		goto err;
+	}
+
+	ret = sysfs_create_file(qdss.modulekobj, &max_clk_attr.attr);
+	if (ret) {
+		pr_err("failed to create QDSS sysfs max_clk attribute\n");
+		goto err;
+	}
+
+	return 0;
+err:
+	return ret;
+}
+
+static void __devexit qdss_sysfs_exit(void)
+{
+	sysfs_remove_file(qdss.modulekobj, &max_clk_attr.attr);
+}
+
+static int __devinit qdss_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_qdss_platform_data *pdata;
+
+	mutex_init(&qdss.sources_mutex);
+	mutex_init(&qdss.sink_mutex);
+
+	INIT_LIST_HEAD(&qdss.sources);
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		goto err_pdata;
+
+	qdss.clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(qdss.clk)) {
+		ret = PTR_ERR(qdss.clk);
+		pr_info("clk get failed\n");
+		goto err_clk_get;
+	}
+
+	ret = clk_set_rate(qdss.clk, QDSS_CLK_ON_DBG);
+	if (ret) {
+		pr_info("clk rate failed\n");
+		goto err_clk_rate;
+	}
+
+	qdss.afamily = pdata->afamily;
+	qdss_add_sources(pdata->src_table, pdata->size);
+
+	pr_info("QDSS arch initialized\n");
+	return 0;
+err_clk_rate:
+	clk_put(qdss.clk);
+err_clk_get:
+err_pdata:
+	mutex_destroy(&qdss.sink_mutex);
+	mutex_destroy(&qdss.sources_mutex);
+	pr_err("QDSS init failed\n");
+	return ret;
+}
+
+static int __devexit qdss_remove(struct platform_device *pdev)
+{
+	qdss_sysfs_exit();
+	clk_put(qdss.clk);
+	mutex_destroy(&qdss.sink_mutex);
+	mutex_destroy(&qdss.sources_mutex);
+
+	return 0;
+}
+
+static struct of_device_id qdss_match[] = {
+	{.compatible = "qcom,msm-qdss"},
+	{}
+};
+
+static struct platform_driver qdss_driver = {
+	.probe          = qdss_probe,
+	.remove         = __devexit_p(qdss_remove),
+	.driver         = {
+		.name   = "msm_qdss",
+		.owner	= THIS_MODULE,
+		.of_match_table = qdss_match,
+	},
+};
+
+static int __init qdss_init(void)
+{
+	return platform_driver_register(&qdss_driver);
+}
+arch_initcall(qdss_init);
+
+static int __init qdss_module_init(void)
+{
+	int ret;
+
+	ret = qdss_sysfs_init();
+	if (ret)
+		goto err_sysfs;
+
+	pr_info("QDSS module initialized\n");
+	return 0;
+err_sysfs:
+	return ret;
+}
+module_init(qdss_module_init);
+
+static void __exit qdss_exit(void)
+{
+	platform_driver_unregister(&qdss_driver);
+}
+module_exit(qdss_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Qualcomm Debug SubSystem Driver");