coresight: support for etr to usb output

Support for etr to usb trace output using QDSS BAM to USB BAM
transfers.

Change-Id: Ibb6f8d0cf1d9799668cb22ccaa13966139c8dea5
Signed-off-by: Pratik Patel <pratikp@codeaurora.org>
diff --git a/drivers/coresight/Makefile b/drivers/coresight/Makefile
index bded488..4ee93cf 100644
--- a/drivers/coresight/Makefile
+++ b/drivers/coresight/Makefile
@@ -1,3 +1,3 @@
 
 obj-$(CONFIG_OF) += of_coresight.o
-obj-$(CONFIG_MSM_QDSS) += coresight.o coresight-tmc.o coresight-tpiu.o coresight-etb.o coresight-funnel.o coresight-replicator.o coresight-stm.o coresight-etm.o
+obj-$(CONFIG_MSM_QDSS) += coresight.o coresight-csr.o coresight-tmc.o coresight-tpiu.o coresight-etb.o coresight-funnel.o coresight-replicator.o coresight-stm.o coresight-etm.o
diff --git a/drivers/coresight/coresight-csr.c b/drivers/coresight/coresight-csr.c
new file mode 100644
index 0000000..e9ac904
--- /dev/null
+++ b/drivers/coresight/coresight-csr.c
@@ -0,0 +1,202 @@
+/* Copyright (c) 2012, The Linux Foundation. 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 <linux/slab.h>
+#include <linux/of_coresight.h>
+#include <linux/coresight.h>
+
+#include "coresight-priv.h"
+
+#define csr_writel(drvdata, val, off)	__raw_writel((val), drvdata->base + off)
+#define csr_readl(drvdata, off)		__raw_readl(drvdata->base + off)
+
+#define CSR_LOCK(drvdata)						\
+do {									\
+	mb();								\
+	csr_writel(drvdata, 0x0, CORESIGHT_LAR);			\
+} while (0)
+#define CSR_UNLOCK(drvdata)						\
+do {									\
+	csr_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR);		\
+	mb();								\
+} while (0)
+
+#define CSR_SWDBGPWRCTRL	(0x000)
+#define CSR_SWDBGPWRACK		(0x004)
+#define CSR_SWSPADREG0		(0x008)
+#define CSR_SWSPADREG1		(0x00C)
+#define CSR_STMTRANSCTRL	(0x010)
+#define CSR_STMAWIDCTRL		(0x014)
+#define CSR_STMCHNOFST0		(0x018)
+#define CSR_STMCHNOFST1		(0x01C)
+#define CSR_STMEXTHWCTRL0	(0x020)
+#define CSR_STMEXTHWCTRL1	(0x024)
+#define CSR_STMEXTHWCTRL2	(0x028)
+#define CSR_STMEXTHWCTRL3	(0x02C)
+#define CSR_USBBAMCTRL		(0x030)
+#define CSR_USBFLSHCTRL		(0x034)
+#define CSR_TIMESTAMPCTRL	(0x038)
+#define CSR_AOTIMEVAL0		(0x03C)
+#define CSR_AOTIMEVAL1		(0x040)
+#define CSR_QDSSTIMEVAL0	(0x044)
+#define CSR_QDSSTIMEVAL1	(0x048)
+#define CSR_QDSSTIMELOAD0	(0x04C)
+#define CSR_QDSSTIMELOAD1	(0x050)
+#define CSR_DAPMSAVAL		(0x054)
+#define CSR_QDSSCLKVOTE		(0x058)
+#define CSR_QDSSCLKIPI		(0x05C)
+#define CSR_QDSSPWRREQIGNORE	(0x060)
+#define CSR_QDSSSPARE		(0x064)
+#define CSR_IPCAT		(0x068)
+
+#define BLKSIZE_256		0
+#define BLKSIZE_512		1
+#define BLKSIZE_1024		2
+#define BLKSIZE_2048		3
+
+struct csr_drvdata {
+	void __iomem		*base;
+	struct device		*dev;
+	struct coresight_device	*csdev;
+};
+
+static struct csr_drvdata *csrdrvdata;
+
+void msm_qdss_csr_enable_bam_to_usb(void)
+{
+	struct csr_drvdata *drvdata = csrdrvdata;
+	uint32_t usbbamctrl, usbflshctrl;
+
+	CSR_UNLOCK(drvdata);
+
+	usbbamctrl = csr_readl(drvdata, CSR_USBBAMCTRL);
+	usbbamctrl = (usbbamctrl & ~0x3) | BLKSIZE_256;
+	csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
+
+	usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL);
+	usbflshctrl = (usbflshctrl & ~0x3FFFC) | (0x1000 << 2);
+	csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
+	usbflshctrl |= 0x2;
+	csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
+
+	usbbamctrl |= 0x4;
+	csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
+
+	CSR_LOCK(drvdata);
+}
+EXPORT_SYMBOL_GPL(msm_qdss_csr_enable_bam_to_usb);
+
+void msm_qdss_csr_disable_bam_to_usb(void)
+{
+	struct csr_drvdata *drvdata = csrdrvdata;
+	uint32_t usbbamctrl;
+
+	CSR_UNLOCK(drvdata);
+
+	usbbamctrl = csr_readl(drvdata, CSR_USBBAMCTRL);
+	usbbamctrl &= (~0x4);
+	csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
+
+	CSR_LOCK(drvdata);
+}
+EXPORT_SYMBOL_GPL(msm_qdss_csr_disable_bam_to_usb);
+
+static int __devinit csr_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct coresight_platform_data *pdata;
+	struct csr_drvdata *drvdata;
+	struct resource *res;
+	struct coresight_desc *desc;
+
+	if (pdev->dev.of_node) {
+		pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+		pdev->dev.platform_data = pdata;
+	}
+
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+	/* Store the driver data pointer for use in exported functions */
+	csrdrvdata = drvdata;
+	drvdata->dev = &pdev->dev;
+	platform_set_drvdata(pdev, drvdata);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
+	if (!drvdata->base)
+		return -ENOMEM;
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc)
+		return -ENOMEM;
+	desc->type = CORESIGHT_DEV_TYPE_NONE;
+	desc->pdata = pdev->dev.platform_data;
+	desc->dev = &pdev->dev;
+	desc->owner = THIS_MODULE;
+	drvdata->csdev = coresight_register(desc);
+	if (IS_ERR(drvdata->csdev))
+		return PTR_ERR(drvdata->csdev);
+
+	dev_info(dev, "CSR initialized\n");
+	return 0;
+}
+
+static int __devexit csr_remove(struct platform_device *pdev)
+{
+	struct csr_drvdata *drvdata = platform_get_drvdata(pdev);
+
+	coresight_unregister(drvdata->csdev);
+	return 0;
+}
+
+static struct of_device_id csr_match[] = {
+	{.compatible = "qcom,coresight-csr"},
+	{}
+};
+
+static struct platform_driver csr_driver = {
+	.probe          = csr_probe,
+	.remove         = __devexit_p(csr_remove),
+	.driver         = {
+		.name   = "coresight-csr",
+		.owner	= THIS_MODULE,
+		.of_match_table = csr_match,
+	},
+};
+
+static int __init csr_init(void)
+{
+	return platform_driver_register(&csr_driver);
+}
+module_init(csr_init);
+
+static void __exit csr_exit(void)
+{
+	platform_driver_unregister(&csr_driver);
+}
+module_exit(csr_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CoreSight CSR driver");
diff --git a/drivers/coresight/coresight-priv.h b/drivers/coresight/coresight-priv.h
index a28a3a5..2b00242 100644
--- a/drivers/coresight/coresight-priv.h
+++ b/drivers/coresight/coresight-priv.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. 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
@@ -36,4 +36,12 @@
 #define BMVAL(val, lsb, msb)	((val & BM(lsb, msb)) >> lsb)
 #define BVAL(val, n)		((val & BIT(n)) >> n)
 
+#ifdef CONFIG_MSM_QDSS
+extern void msm_qdss_csr_enable_bam_to_usb(void);
+extern void msm_qdss_csr_disable_bam_to_usb(void);
+#else
+static inline void msm_qdss_csr_enable_bam_to_usb(void) {}
+static inline void msm_qdss_csr_disable_bam_to_usb(void) {}
+#endif
+
 #endif
diff --git a/drivers/coresight/coresight-tmc.c b/drivers/coresight/coresight-tmc.c
index 1c85aff..d27fe86 100644
--- a/drivers/coresight/coresight-tmc.c
+++ b/drivers/coresight/coresight-tmc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. 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
@@ -28,7 +28,10 @@
 #include <linux/clk.h>
 #include <linux/of_coresight.h>
 #include <linux/coresight.h>
+#include <linux/usb/usb_qdss.h>
 #include <mach/memory.h>
+#include <mach/sps.h>
+#include <mach/usb_bam.h>
 
 #include "coresight-priv.h"
 
@@ -74,6 +77,8 @@
 #define TMC_ITATBCTR0		(0xEF8)
 
 #define BYTES_PER_WORD		4
+#define TMC_ETR_BAM_PIPE_INDEX	0
+#define TMC_ETR_BAM_NR_PIPES	2
 
 enum tmc_config_type {
 	TMC_CONFIG_TYPE_ETB,
@@ -87,6 +92,12 @@
 	TMC_MODE_HARDWARE_FIFO,
 };
 
+enum tmc_etr_out_mode {
+	TMC_ETR_OUT_MODE_NONE,
+	TMC_ETR_OUT_MODE_MEM,
+	TMC_ETR_OUT_MODE_USB,
+};
+
 enum tmc_mem_intf_width {
 	TMC_MEM_INTF_WIDTH_32BITS	= 0x2,
 	TMC_MEM_INTF_WIDTH_64BITS	= 0x3,
@@ -94,6 +105,19 @@
 	TMC_MEM_INTF_WIDTH_256BITS	= 0x5,
 };
 
+struct tmc_etr_bam_data {
+	struct sps_bam_props	props;
+	uint32_t		handle;
+	struct sps_pipe		*pipe;
+	struct sps_connect	connect;
+	uint32_t		src_pipe_idx;
+	uint32_t		dest;
+	uint32_t		dest_pipe_idx;
+	struct sps_mem_buffer	desc_fifo;
+	struct sps_mem_buffer	data_fifo;
+	bool			enable;
+};
+
 struct tmc_drvdata {
 	void __iomem		*base;
 	struct device		*dev;
@@ -101,12 +125,18 @@
 	struct miscdevice	miscdev;
 	struct clk		*clk;
 	spinlock_t		spinlock;
+	struct mutex		read_lock;
 	int			read_count;
 	bool			reading;
 	char			*buf;
 	unsigned long		paddr;
 	void __iomem		*vaddr;
 	uint32_t		size;
+	struct mutex		usb_lock;
+	struct usb_qdss_ch	*usbch;
+	struct tmc_etr_bam_data	*bamdata;
+	enum tmc_etr_out_mode	out_mode;
+	bool			enable_to_bam;
 	bool			enable;
 	enum tmc_config_type	config_type;
 	uint32_t		trigger_cntr;
@@ -154,6 +184,151 @@
 	tmc_writel(drvdata, 0x0, TMC_CTL);
 }
 
+static void tmc_etr_fill_usb_bam_data(struct tmc_drvdata *drvdata)
+{
+	struct tmc_etr_bam_data *bamdata = drvdata->bamdata;
+
+	get_bam2bam_connection_info(0, PEER_PERIPHERAL_TO_USB,
+				    &bamdata->dest,
+				    &bamdata->dest_pipe_idx,
+				    &bamdata->src_pipe_idx,
+				    &bamdata->desc_fifo,
+				    &bamdata->data_fifo);
+}
+
+static void __tmc_etr_enable_to_bam(struct tmc_drvdata *drvdata)
+{
+	struct tmc_etr_bam_data *bamdata = drvdata->bamdata;
+	uint32_t axictl;
+
+	if (drvdata->enable_to_bam)
+		return;
+
+	/* Configure and enable required CSR registers */
+	msm_qdss_csr_enable_bam_to_usb();
+
+	/* Configure and enable ETR for usb bam output */
+
+	TMC_UNLOCK(drvdata);
+
+	tmc_writel(drvdata, bamdata->data_fifo.size / BYTES_PER_WORD,
+		   TMC_RSZ);
+	tmc_writel(drvdata, TMC_MODE_CIRCULAR_BUFFER, TMC_MODE);
+
+	axictl = tmc_readl(drvdata, TMC_AXICTL);
+	axictl |= (0xF << 8);
+	tmc_writel(drvdata, axictl, TMC_AXICTL);
+	axictl &= ~(0x1 << 7);
+	tmc_writel(drvdata, axictl, TMC_AXICTL);
+	axictl = (axictl & ~0x3) | 0x2;
+	tmc_writel(drvdata, axictl, TMC_AXICTL);
+
+	tmc_writel(drvdata, bamdata->data_fifo.phys_base, TMC_DBALO);
+	tmc_writel(drvdata, 0x0, TMC_DBAHI);
+	tmc_writel(drvdata, 0x133, TMC_FFCR);
+	tmc_writel(drvdata, drvdata->trigger_cntr, TMC_TRG);
+	__tmc_enable(drvdata);
+
+	TMC_LOCK(drvdata);
+
+	drvdata->enable_to_bam = true;
+}
+
+static int tmc_etr_bam_enable(struct tmc_drvdata *drvdata)
+{
+	struct tmc_etr_bam_data *bamdata = drvdata->bamdata;
+	int ret;
+
+	if (bamdata->enable)
+		return 0;
+
+	/* Configure and enable ndp bam */
+
+	bamdata->pipe = sps_alloc_endpoint();
+	if (!bamdata->pipe)
+		return -ENOMEM;
+
+	ret = sps_get_config(bamdata->pipe, &bamdata->connect);
+	if (ret)
+		goto err;
+
+	bamdata->connect.mode = SPS_MODE_SRC;
+	bamdata->connect.source = bamdata->handle;
+	bamdata->connect.event_thresh = 0x4;
+	bamdata->connect.src_pipe_index = TMC_ETR_BAM_PIPE_INDEX;
+	bamdata->connect.options = SPS_O_AUTO_ENABLE;
+
+	bamdata->connect.destination = bamdata->dest;
+	bamdata->connect.dest_pipe_index = bamdata->dest_pipe_idx;
+	bamdata->connect.desc = bamdata->desc_fifo;
+	bamdata->connect.data = bamdata->data_fifo;
+
+	ret = sps_connect(bamdata->pipe, &bamdata->connect);
+	if (ret)
+		goto err;
+
+	bamdata->enable = true;
+	return 0;
+err:
+	sps_free_endpoint(bamdata->pipe);
+	return ret;
+}
+
+static void __tmc_etr_disable_to_bam(struct tmc_drvdata *drvdata)
+{
+	if (!drvdata->enable_to_bam)
+		return;
+
+	TMC_UNLOCK(drvdata);
+
+	tmc_flush_and_stop(drvdata);
+	__tmc_disable(drvdata);
+
+	TMC_LOCK(drvdata);
+
+	/* Disable CSR registers */
+	msm_qdss_csr_disable_bam_to_usb();
+	drvdata->enable_to_bam = false;
+}
+
+static void tmc_etr_bam_disable(struct tmc_drvdata *drvdata)
+{
+	struct tmc_etr_bam_data *bamdata = drvdata->bamdata;
+
+	if (!bamdata->enable)
+		return;
+
+	sps_disconnect(bamdata->pipe);
+	sps_free_endpoint(bamdata->pipe);
+	bamdata->enable = false;
+}
+
+static void usb_notifier(void *priv, unsigned int event,
+			struct qdss_request *d_req, struct usb_qdss_ch *ch)
+{
+	struct tmc_drvdata *drvdata = priv;
+	unsigned long flags;
+	int ret = 0;
+
+	mutex_lock(&drvdata->usb_lock);
+	if (event == USB_QDSS_CONNECT) {
+		tmc_etr_fill_usb_bam_data(drvdata);
+		ret = tmc_etr_bam_enable(drvdata);
+		if (ret)
+			dev_err(drvdata->dev, "ETR BAM enable failed\n");
+
+		spin_lock_irqsave(&drvdata->spinlock, flags);
+		__tmc_etr_enable_to_bam(drvdata);
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	} else if (event == USB_QDSS_DISCONNECT) {
+		spin_lock_irqsave(&drvdata->spinlock, flags);
+		__tmc_etr_disable_to_bam(drvdata);
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		tmc_etr_bam_disable(drvdata);
+	}
+	mutex_unlock(&drvdata->usb_lock);
+}
+
 static void __tmc_etb_enable(struct tmc_drvdata *drvdata)
 {
 	/* Zero out the memory to help with debug */
@@ -169,7 +344,7 @@
 	TMC_LOCK(drvdata);
 }
 
-static void __tmc_etr_enable(struct tmc_drvdata *drvdata)
+static void __tmc_etr_enable_to_mem(struct tmc_drvdata *drvdata)
 {
 	uint32_t axictl;
 
@@ -192,6 +367,7 @@
 	tmc_writel(drvdata, drvdata->paddr, TMC_DBALO);
 	tmc_writel(drvdata, 0x0, TMC_DBAHI);
 	tmc_writel(drvdata, 0x133, TMC_FFCR);
+	tmc_writel(drvdata, drvdata->trigger_cntr, TMC_TRG);
 	__tmc_enable(drvdata);
 
 	TMC_LOCK(drvdata);
@@ -218,17 +394,30 @@
 	if (ret)
 		return ret;
 
+	mutex_lock(&drvdata->usb_lock);
+	if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
+		if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
+			drvdata->usbch = usb_qdss_open("qdss", drvdata,
+						       usb_notifier);
+			if (IS_ERR(drvdata->usbch)) {
+				dev_err(drvdata->dev, "usb_qdss_open failed\n");
+				ret = PTR_ERR(drvdata->usbch);
+				goto err0;
+			}
+		}
+	}
+
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading) {
-		spin_unlock_irqrestore(&drvdata->spinlock, flags);
-		clk_disable_unprepare(drvdata->clk);
-		return -EBUSY;
+		ret = -EBUSY;
+		goto err1;
 	}
 
 	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
 		__tmc_etb_enable(drvdata);
 	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		__tmc_etr_enable(drvdata);
+		if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
+			__tmc_etr_enable_to_mem(drvdata);
 	} else {
 		if (mode == TMC_MODE_CIRCULAR_BUFFER)
 			__tmc_etb_enable(drvdata);
@@ -237,9 +426,19 @@
 	}
 	drvdata->enable = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	mutex_unlock(&drvdata->usb_lock);
 
 	dev_info(drvdata->dev, "TMC enabled\n");
 	return 0;
+err1:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
+		if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB)
+			usb_qdss_close(drvdata->usbch);
+err0:
+	mutex_unlock(&drvdata->usb_lock);
+	clk_disable_unprepare(drvdata->clk);
+	return ret;
 }
 
 static int tmc_enable_sink(struct coresight_device *csdev)
@@ -311,7 +510,7 @@
 		drvdata->buf = drvdata->vaddr;
 }
 
-static void __tmc_etr_disable(struct tmc_drvdata *drvdata)
+static void __tmc_etr_disable_to_mem(struct tmc_drvdata *drvdata)
 {
 	TMC_UNLOCK(drvdata);
 
@@ -335,7 +534,9 @@
 static void tmc_disable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
 {
 	unsigned long flags;
+	bool etr_bam_disable = false;
 
+	mutex_lock(&drvdata->usb_lock);
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading)
 		goto out;
@@ -343,7 +544,10 @@
 	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
 		__tmc_etb_disable(drvdata);
 	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		__tmc_etr_disable(drvdata);
+		if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
+			__tmc_etr_disable_to_mem(drvdata);
+		else if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB)
+			etr_bam_disable = true;
 	} else {
 		if (mode == TMC_MODE_CIRCULAR_BUFFER)
 			__tmc_etb_disable(drvdata);
@@ -354,6 +558,20 @@
 	drvdata->enable = false;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
+	if (etr_bam_disable) {
+		if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
+			if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
+				spin_lock_irqsave(&drvdata->spinlock, flags);
+				__tmc_etr_disable_to_bam(drvdata);
+				spin_unlock_irqrestore(&drvdata->spinlock,
+						       flags);
+				tmc_etr_bam_disable(drvdata);
+				usb_qdss_close(drvdata->usbch);
+			}
+		}
+	}
+	mutex_unlock(&drvdata->usb_lock);
+
 	clk_disable_unprepare(drvdata->clk);
 
 	dev_info(drvdata->dev, "TMC disabled\n");
@@ -387,7 +605,8 @@
 	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
 		__tmc_etb_disable(drvdata);
 	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		__tmc_etr_disable(drvdata);
+		if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
+			__tmc_etr_disable_to_mem(drvdata);
 	} else {
 		mode = tmc_readl(drvdata, TMC_MODE);
 		if (mode == TMC_MODE_CIRCULAR_BUFFER)
@@ -442,7 +661,12 @@
 	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
 		__tmc_etb_disable(drvdata);
 	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		__tmc_etr_disable(drvdata);
+		if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) {
+			__tmc_etr_disable_to_mem(drvdata);
+		} else {
+			ret = -ENODEV;
+			goto err;
+		}
 	} else {
 		mode = tmc_readl(drvdata, TMC_MODE);
 		if (mode == TMC_MODE_CIRCULAR_BUFFER) {
@@ -475,7 +699,8 @@
 	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
 		__tmc_etb_enable(drvdata);
 	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		__tmc_etr_enable(drvdata);
+		if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
+			__tmc_etr_enable_to_mem(drvdata);
 	} else {
 		mode = tmc_readl(drvdata, TMC_MODE);
 		if (mode == TMC_MODE_CIRCULAR_BUFFER)
@@ -494,17 +719,23 @@
 						   struct tmc_drvdata, miscdev);
 	int ret = 0;
 
+	mutex_lock(&drvdata->read_lock);
 	if (drvdata->read_count++)
 		goto out;
 
 	ret = tmc_read_prepare(drvdata);
 	if (ret)
-		return ret;
+		goto err;
 out:
+	mutex_unlock(&drvdata->read_lock);
 	nonseekable_open(inode, file);
 
 	dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__);
 	return 0;
+err:
+	drvdata->read_count--;
+	mutex_unlock(&drvdata->read_lock);
+	return ret;
 }
 
 static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
@@ -541,6 +772,7 @@
 	struct tmc_drvdata *drvdata = container_of(file->private_data,
 						   struct tmc_drvdata, miscdev);
 
+	mutex_lock(&drvdata->read_lock);
 	if (--drvdata->read_count) {
 		if (drvdata->read_count < 0) {
 			WARN_ONCE(1, "mismatched close\n");
@@ -551,6 +783,7 @@
 
 	tmc_read_unprepare(drvdata);
 out:
+	mutex_unlock(&drvdata->read_lock);
 	dev_dbg(drvdata->dev, "%s: released\n", __func__);
 	return 0;
 }
@@ -588,6 +821,87 @@
 static DEVICE_ATTR(trigger_cntr, S_IRUGO | S_IWUSR, tmc_show_trigger_cntr,
 		   tmc_store_trigger_cntr);
 
+static ssize_t tmc_etr_show_out_mode(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n",
+			 drvdata->out_mode == TMC_ETR_OUT_MODE_MEM ?
+			 "mem" : "usb");
+}
+
+static ssize_t tmc_etr_store_out_mode(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t size)
+{
+	struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	char str[10] = "";
+	unsigned long flags;
+	bool etr_bam_flag = false;
+	int ret;
+
+	if (strlen(buf) >= 10)
+		return -EINVAL;
+	if (sscanf(buf, "%s", str) != 1)
+		return -EINVAL;
+
+	mutex_lock(&drvdata->usb_lock);
+	if (!strcmp(str, "mem")) {
+		if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
+			goto out;
+
+		spin_lock_irqsave(&drvdata->spinlock, flags);
+		if (drvdata->enable) {
+			__tmc_etr_disable_to_bam(drvdata);
+			__tmc_etr_enable_to_mem(drvdata);
+			etr_bam_flag = true;
+		}
+		drvdata->out_mode = TMC_ETR_OUT_MODE_MEM;
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+		if (etr_bam_flag) {
+			tmc_etr_bam_disable(drvdata);
+			usb_qdss_close(drvdata->usbch);
+		}
+	} else if (!strcmp(str, "usb")) {
+		if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB)
+			goto out;
+
+		spin_lock_irqsave(&drvdata->spinlock, flags);
+		if (drvdata->enable) {
+			if (drvdata->reading) {
+				ret = -EBUSY;
+				goto err1;
+			}
+			__tmc_etr_disable_to_mem(drvdata);
+			etr_bam_flag = true;
+		}
+		drvdata->out_mode = TMC_ETR_OUT_MODE_USB;
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+		if (etr_bam_flag) {
+			drvdata->usbch = usb_qdss_open("qdss", drvdata,
+						       usb_notifier);
+			if (IS_ERR(drvdata->usbch)) {
+				dev_err(drvdata->dev, "usb_qdss_open failed\n");
+				ret = PTR_ERR(drvdata->usbch);
+				goto err0;
+			}
+		}
+	}
+out:
+	mutex_unlock(&drvdata->usb_lock);
+	return size;
+err1:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+err0:
+	mutex_unlock(&drvdata->usb_lock);
+	return ret;
+}
+static DEVICE_ATTR(out_mode, S_IRUGO | S_IWUSR, tmc_etr_show_out_mode,
+		   tmc_etr_store_out_mode);
+
 static struct attribute *tmc_attrs[] = {
 	&dev_attr_trigger_cntr.attr,
 	NULL,
@@ -597,6 +911,15 @@
 	.attrs = tmc_attrs,
 };
 
+static struct attribute *tmc_etr_attrs[] = {
+	&dev_attr_out_mode.attr,
+	NULL,
+};
+
+static struct attribute_group tmc_etr_attr_grp = {
+	.attrs = tmc_etr_attrs,
+};
+
 static const struct attribute_group *tmc_etb_attr_grps[] = {
 	&tmc_attr_grp,
 	NULL,
@@ -604,6 +927,7 @@
 
 static const struct attribute_group *tmc_etr_attr_grps[] = {
 	&tmc_attr_grp,
+	&tmc_etr_attr_grp,
 	NULL,
 };
 
@@ -612,6 +936,46 @@
 	NULL,
 };
 
+static int __devinit tmc_etr_bam_init(struct platform_device *pdev,
+				      struct tmc_drvdata *drvdata)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct tmc_etr_bam_data *bamdata;
+
+	bamdata = devm_kzalloc(dev, sizeof(*bamdata), GFP_KERNEL);
+	if (!bamdata)
+		return -ENOMEM;
+	drvdata->bamdata = bamdata;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res)
+		return -ENODEV;
+
+	bamdata->props.phys_addr = res->start;
+	bamdata->props.virt_addr = devm_ioremap(dev, res->start,
+						resource_size(res));
+	if (!bamdata->props.virt_addr)
+		return -ENOMEM;
+	bamdata->props.virt_size = resource_size(res);
+
+	bamdata->props.event_threshold = 0x4; /* Pipe event threshold */
+	bamdata->props.summing_threshold = 0x10; /* BAM event threshold */
+	bamdata->props.irq = 0;
+	bamdata->props.num_pipes = TMC_ETR_BAM_NR_PIPES;
+
+	return sps_register_bam_device(&bamdata->props, &bamdata->handle);
+}
+
+static void tmc_etr_bam_exit(struct tmc_drvdata *drvdata)
+{
+	struct tmc_etr_bam_data *bamdata = drvdata->bamdata;
+
+	if (!bamdata->handle)
+		return;
+	sps_deregister_bam_device(bamdata->handle);
+}
+
 static int __devinit tmc_probe(struct platform_device *pdev)
 {
 	int ret;
@@ -644,6 +1008,8 @@
 		return -ENOMEM;
 
 	spin_lock_init(&drvdata->spinlock);
+	mutex_init(&drvdata->read_lock);
+	mutex_init(&drvdata->usb_lock);
 
 	drvdata->clk = devm_clk_get(dev, "core_clk");
 	if (IS_ERR(drvdata->clk))
@@ -679,6 +1045,11 @@
 			goto err0;
 		}
 		memset(drvdata->vaddr, 0, drvdata->size);
+		drvdata->out_mode = TMC_ETR_OUT_MODE_MEM;
+
+		ret = tmc_etr_bam_init(pdev, drvdata);
+		if (ret)
+			goto err0;
 	} else {
 		drvdata->buf = devm_kzalloc(dev, drvdata->size, GFP_KERNEL);
 		if (!drvdata->buf)
@@ -688,7 +1059,7 @@
 	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
 	if (!desc) {
 		ret = -ENOMEM;
-		goto err0;
+		goto err1;
 	}
 	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
 		desc->type = CORESIGHT_DEV_TYPE_SINK;
@@ -701,7 +1072,7 @@
 		drvdata->csdev = coresight_register(desc);
 		if (IS_ERR(drvdata->csdev)) {
 			ret = PTR_ERR(drvdata->csdev);
-			goto err0;
+			goto err1;
 		}
 	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
 		desc->type = CORESIGHT_DEV_TYPE_SINK;
@@ -714,7 +1085,7 @@
 		drvdata->csdev = coresight_register(desc);
 		if (IS_ERR(drvdata->csdev)) {
 			ret = PTR_ERR(drvdata->csdev);
-			goto err0;
+			goto err1;
 		}
 	} else {
 		desc->type = CORESIGHT_DEV_TYPE_LINKSINK;
@@ -728,7 +1099,7 @@
 		drvdata->csdev = coresight_register(desc);
 		if (IS_ERR(drvdata->csdev)) {
 			ret = PTR_ERR(drvdata->csdev);
-			goto err0;
+			goto err1;
 		}
 	}
 
@@ -738,12 +1109,14 @@
 	drvdata->miscdev.fops = &tmc_fops;
 	ret = misc_register(&drvdata->miscdev);
 	if (ret)
-		goto err1;
+		goto err2;
 
 	dev_info(dev, "TMC initialized\n");
 	return 0;
-err1:
+err2:
 	coresight_unregister(drvdata->csdev);
+err1:
+	tmc_etr_bam_exit(drvdata);
 err0:
 	free_contiguous_memory_by_paddr(drvdata->paddr);
 	return ret;
@@ -755,6 +1128,7 @@
 
 	misc_deregister(&drvdata->miscdev);
 	coresight_unregister(drvdata->csdev);
+	tmc_etr_bam_exit(drvdata);
 	free_contiguous_memory_by_paddr(drvdata->paddr);
 	return 0;
 }
diff --git a/drivers/coresight/coresight.c b/drivers/coresight/coresight.c
index f76d303..cccb5a7 100644
--- a/drivers/coresight/coresight.c
+++ b/drivers/coresight/coresight.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. 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
@@ -496,6 +496,9 @@
 
 static struct device_type coresight_dev_type[] = {
 	{
+		.name = "none",
+	},
+	{
 		.name = "sink",
 		.groups = coresight_attr_grps_sink,
 	},