From 691a0a367e822826637c267b04f9dbd42340aa9b Mon Sep 17 00:00:00 2001
From: Mike Bazov <mike@perception-point.io>
Date: Sun, 19 Aug 2018 13:50:34 +0300
Subject: [PATCH] coresight, etm-api: introduce ETM api for kernel clients

introduce ETM api for kernel clients. a small POC,
work in progress.

Change-Id: Ie460306a1f48993836f18e44762f70407eeb65d2
---
 drivers/hwtracing/coresight/Makefile             |   2 +-
 drivers/hwtracing/coresight/coresight-etb10.c    |   5 +-
 drivers/hwtracing/coresight/coresight-etm-api.c  | 174 ++++++++++++++++++
 drivers/hwtracing/coresight/coresight-etm-perf.c |   3 +-
 drivers/hwtracing/coresight/coresight-etm3x.c    |   3 +
 drivers/hwtracing/coresight/coresight-etm4x.c    |  77 +++++++-
 drivers/hwtracing/coresight/coresight-priv.h     |   3 +
 drivers/hwtracing/coresight/coresight-stm.c      |   4 +-
 drivers/hwtracing/coresight/coresight-tmc-etf.c  |  11 +-
 drivers/hwtracing/coresight/coresight-tmc-etr.c  | 218 ++++++++++++++++++++++-
 drivers/hwtracing/coresight/coresight-tmc.h      |   7 +
 drivers/hwtracing/coresight/coresight.c          |  14 ++
 include/linux/coresight.h                        |  35 +++-
 13 files changed, 536 insertions(+), 20 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-etm-api.c

diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 41870ded51a3..8f0af5e7cabb 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -2,7 +2,7 @@
 #
 # Makefile for CoreSight drivers.
 #
-obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o
+obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o coresight-etm-api.o
 obj-$(CONFIG_OF) += of_coresight.o
 obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \
 					     coresight-tmc-etf.o \
diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index 9fd77fdc1244..f5c438559900 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -286,12 +286,15 @@ static void etb_disable(struct coresight_device *csdev)
 	dev_dbg(drvdata->dev, "ETB disabled\n");
 }
 
-static void *etb_alloc_buffer(struct coresight_device *csdev, int cpu,
+static void *etb_alloc_buffer(struct coresight_device *csdev, u32 mode, int cpu,
 			      void **pages, int nr_pages, bool overwrite)
 {
 	int node;
 	struct cs_buffers *buf;
 
+	if (mode != CS_MODE_PERF)
+		return NULL;
+
 	if (cpu == -1)
 		cpu = smp_processor_id();
 	node = cpu_to_node(cpu);
diff --git a/drivers/hwtracing/coresight/coresight-etm-api.c b/drivers/hwtracing/coresight/coresight-etm-api.c
new file mode 100644
index 000000000000..0e203fc7c3b3
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm-api.c
@@ -0,0 +1,174 @@
+#include <linux/coresight.h>
+
+#include "coresight-priv.h"
+
+
+static DEFINE_PER_CPU(struct coresight_device *, csdev_src);
+
+void coresight_etm_register(struct coresight_device *csdev)
+{
+       int cpu = source_ops(csdev)->cpu_id(csdev);
+
+       per_cpu(csdev_src, cpu) = csdev;
+}
+
+/**
+ * coresight_etm_alloc_buffer - allocate a buffer with fixed size for the
+ *    activated sink.
+ */
+int coresight_etm_alloc_buffer(size_t buffer_size)
+{
+	int err = 0;
+	void *buf = NULL;
+	size_t nr_pages = 0;
+	struct coresight_device *sink = NULL;
+
+	sink = coresight_get_enabled_sink(false);
+	if (!sink) {
+		err = -ENOENT;
+		goto out;
+	}
+
+	if (!sink_ops(sink)->alloc_buffer) {
+		err = -ENOSYS;
+		goto out;
+	}
+
+	nr_pages = PAGE_ALIGN(buffer_size) >> PAGE_SHIFT;
+
+	/* We trust the underlying sink implementation support
+	 * buffer allocation concurrently. */
+	buf = sink_ops(sink)->alloc_buffer(sink, CS_MODE_API, 0,
+	                                   NULL, nr_pages, false);
+	if (IS_ERR(buf))
+		err = PTR_ERR(buf);
+out:
+	return err;
+}
+EXPORT_SYMBOL_GPL(coresight_etm_alloc_buffer);
+
+/**
+ * coresight_etm_create_session - create a coresight session.
+ * in the implementation terms this means creating a path from a source
+ * to a sink, and enabling the whole path except the source.
+ */
+coresight_session_t coresight_etm_create_session(int cpu)
+{
+	int err = 0;
+	struct list_head *path = NULL;
+	struct coresight_device *source = NULL, *sink = NULL;
+
+	if (cpu < 0 || cpu >= NR_CPUS)
+		return ERR_PTR(-EINVAL);
+
+	source = per_cpu(csdev_src, cpu);
+
+	/* Currently, the only way of activating a sink is via sysfs, since
+	 * there is no comfortable way of exporting the sink/link types
+	 * to the user, as the devtypes are extremely generic, where the user
+	 * might want to be more specific. Also, don't deactivate the sink,
+	 * the user will disable it as when wishes via sysfs. */
+	sink = coresight_get_enabled_sink(false);
+	if (!sink)
+		return ERR_PTR(-ENOENT);
+
+	path = coresight_build_path(source, sink);
+	if (IS_ERR(path)) {
+		return path;
+	}
+
+	err = coresight_enable_path(path, CS_MODE_API);
+	if (err) {
+		coresight_release_path(path);
+		path = ERR_PTR(err);
+	}
+
+	return path;
+}
+EXPORT_SYMBOL_GPL(coresight_etm_create_session);
+
+/**
+ * coresight_etm_enable: make the source start playing trace data.
+ */
+int coresight_etm_play(coresight_session_t session, struct etm_config *config)
+{
+	struct list_head *path = NULL;
+	struct coresight_device *source = NULL;
+
+	if (!session)
+		return -EINVAL;
+
+	path = session;
+	source = coresight_get_source(path);
+	if (!source)
+		return -ENXIO;
+
+	return source_ops(source)->enable(source, config, CS_MODE_API);
+}
+EXPORT_SYMBOL_GPL(coresight_etm_play);
+
+/**
+ * coresight_etm_disable: pause the source from playing trace data.
+ */
+void coresight_etm_pause(coresight_session_t session)
+{
+	struct list_head *path = NULL;
+	struct coresight_device *source = NULL;
+
+	if (!session)
+		return;
+
+	path = session;
+	source = coresight_get_source(path);
+	if (!source)
+		return;
+
+	source_ops(source)->disable(source, NULL);
+}
+EXPORT_SYMBOL_GPL(coresight_etm_pause);
+
+/**
+ * coresight_etm_destroy_session: destroy a coresight session.
+ * in the implementation terms this means disabling and releasing a coresight
+ * path.
+ */
+void coresight_etm_destroy_session(coresight_session_t session)
+{
+	struct list_head *path = NULL;
+
+	if (!session)
+		return;
+
+	path = session;
+
+	coresight_disable_path(path);
+	coresight_release_path(path);
+}
+EXPORT_SYMBOL_GPL(coresight_etm_destroy_session);
+
+/**
+ * coresight_etm_read: read trace data from the session's sink.
+ *
+ * NOTE: It is the client's responsibility to pause all etm sessions related
+ * to the same sink to avoid trace data loss.
+ */
+int coresight_etm_read(coresight_session_t session, void *buf, size_t size)
+{
+	struct list_head *path = NULL;
+	struct coresight_device *sink = NULL;
+
+	if (!session)
+		return -EINVAL;
+
+	path = session;
+
+	sink = coresight_get_sink(path);
+	if (!sink)
+		return -ENOENT;
+
+	if (!sink_ops(sink)->read)
+		return -ENOSYS;
+
+	return sink_ops(sink)->read(sink, buf, size);
+}
+EXPORT_SYMBOL_GPL(coresight_etm_read);
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index abe8249b893b..bf02d4c69129 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -250,7 +250,8 @@ static void *etm_setup_aux(int event_cpu, void **pages,
 
 	/* Allocate the sink buffer for this session */
 	event_data->snk_config =
-			sink_ops(sink)->alloc_buffer(sink, cpu, pages,
+			sink_ops(sink)->alloc_buffer(sink, CS_MODE_PERF,
+			                             cpu, pages,
 						     nr_pages, overwrite);
 	if (!event_data->snk_config)
 		goto err;
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 9ce8fba20b0f..2cfb682ad48b 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -529,6 +529,9 @@ static int etm_enable(struct coresight_device *csdev,
 	case CS_MODE_PERF:
 		ret = etm_enable_perf(csdev, event);
 		break;
+	case CS_MODE_API:
+		ret = -ENOSYS;
+		break;
 	default:
 		ret = -EINVAL;
 	}
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index c1dcc7c289a5..212a550e8f6a 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -31,6 +31,7 @@
 
 #include "coresight-etm4x.h"
 #include "coresight-etm-perf.h"
+#include "coresight-priv.h"
 
 static int boot_enable;
 module_param_named(boot_enable, boot_enable, int, S_IRUGO);
@@ -39,6 +40,7 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
 static int etm4_count;
 static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
 static void etm4_set_default_config(struct etmv4_config *config);
+static void etm4_set_default_filter(struct etmv4_config *config);
 static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
 				  struct perf_event *event);
 
@@ -226,6 +228,42 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
 	return ret;
 }
 
+static void etm4_apply_config(struct coresight_device *csdev,
+                              struct etm_config *etm_config)
+{
+	struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+	struct etmv4_config *config = &drvdata->config;
+
+	/* Clear configuration from previous run */
+	memset(config, 0, sizeof(struct etmv4_config));
+
+	if (!etm_config->kernel)
+		config->mode = ETM_MODE_EXCL_KERN;
+
+	if (!etm_config->user)
+		config->mode = ETM_MODE_EXCL_USER;
+
+	/* Always start from the default config */
+	etm4_set_default_config(config);
+	etm4_set_default_filter(config);
+
+	if (etm_config->cycacc) {
+		config->cfg |= BIT(4);
+		/* TRM: Must program this for cycacc to work */
+		config->ccctlr = ETM_CYC_THRESHOLD_DEFAULT;
+	}
+	if (etm_config->timestamp)
+		/* bit[11], Global timestamp tracing bit */
+		config->cfg |= BIT(11);
+
+	/* return stack - enable if selected and supported */
+	if (etm_config->retstack && drvdata->retstack)
+		/* bit[12], Return stack enable bit */
+		config->cfg |= BIT(12);
+
+	drvdata->trcid = etm_config->trace_id;
+}
+
 static int etm4_enable_perf(struct coresight_device *csdev,
 			    struct perf_event *event)
 {
@@ -275,8 +313,31 @@ static int etm4_enable_sysfs(struct coresight_device *csdev)
 	return ret;
 }
 
+static int etm4_enable_api(struct coresight_device *csdev,
+                           struct etm_config *config)
+{
+	int ret = 0;
+
+	struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock(&drvdata->spinlock);
+
+	/* Apply the request config. */
+	etm4_apply_config(csdev, config);
+
+	/*
+	 * Executing etm4_enable_hw on the cpu whose ETM is being enabled
+	 * ensures that register writes occur when cpu is powered.
+	 */
+	ret = smp_call_function_single(drvdata->cpu,
+				       etm4_enable_hw, drvdata, 1);
+
+	spin_unlock(&drvdata->spinlock);
+	return ret;
+}
+
 static int etm4_enable(struct coresight_device *csdev,
-		       struct perf_event *event, u32 mode)
+		       void *config, u32 mode)
 {
 	int ret;
 	u32 val;
@@ -293,7 +354,10 @@ static int etm4_enable(struct coresight_device *csdev,
 		ret = etm4_enable_sysfs(csdev);
 		break;
 	case CS_MODE_PERF:
-		ret = etm4_enable_perf(csdev, event);
+		ret = etm4_enable_perf(csdev, config);
+		break;
+	case CS_MODE_API:
+		ret = etm4_enable_api(csdev, config);
 		break;
 	default:
 		ret = -EINVAL;
@@ -384,7 +448,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
 }
 
 static void etm4_disable(struct coresight_device *csdev,
-			 struct perf_event *event)
+			 void *config)
 {
 	u32 mode;
 	struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -400,10 +464,11 @@ static void etm4_disable(struct coresight_device *csdev,
 	case CS_MODE_DISABLED:
 		break;
 	case CS_MODE_SYSFS:
+	case CS_MODE_API: /* Disabling via API mode is the same as sysfs mode */
 		etm4_disable_sysfs(csdev);
 		break;
 	case CS_MODE_PERF:
-		etm4_disable_perf(csdev, event);
+		etm4_disable_perf(csdev, config);
 		break;
 	}
 
@@ -826,7 +891,6 @@ static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
 			address = (type == ETM_ADDR_TYPE_START ?
 				   filter->start_addr :
 				   filter->stop_addr);
-
 			/* Configure comparator */
 			etm4_set_start_stop_filter(config, address,
 						   comparator, type);
@@ -1026,6 +1090,7 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
 		goto err_arch_supported;
 	}
 
+
 	pm_runtime_put(&adev->dev);
 	dev_info(dev, "CPU%d: ETM v%d.%d initialized\n",
 		 drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf);
@@ -1035,6 +1100,8 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
 		drvdata->boot_enable = true;
 	}
 
+	coresight_etm_register(drvdata->csdev);
+
 	return 0;
 
 err_arch_supported:
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index c11da5564a67..38bbcfc2b761 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -72,6 +72,7 @@ enum cs_mode {
 	CS_MODE_DISABLED,
 	CS_MODE_SYSFS,
 	CS_MODE_PERF,
+	CS_MODE_API
 };
 
 /**
@@ -143,6 +144,8 @@ struct coresight_device *coresight_get_enabled_sink(bool reset);
 struct list_head *coresight_build_path(struct coresight_device *csdev,
 				       struct coresight_device *sink);
 void coresight_release_path(struct list_head *path);
+struct coresight_device *coresight_get_source(struct list_head *path);
+void coresight_etm_register(struct coresight_device *csdev);
 
 #ifdef CONFIG_CORESIGHT_SOURCE_ETM3X
 extern int etm_readl_cp14(u32 off, unsigned int *val);
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c
index 35d6f9709274..7cb1d7525dde 100644
--- a/drivers/hwtracing/coresight/coresight-stm.c
+++ b/drivers/hwtracing/coresight/coresight-stm.c
@@ -191,7 +191,7 @@ static void stm_enable_hw(struct stm_drvdata *drvdata)
 }
 
 static int stm_enable(struct coresight_device *csdev,
-		      struct perf_event *event, u32 mode)
+		      void *config, u32 mode)
 {
 	u32 val;
 	struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -254,7 +254,7 @@ static void stm_disable_hw(struct stm_drvdata *drvdata)
 }
 
 static void stm_disable(struct coresight_device *csdev,
-			struct perf_event *event)
+			void *config)
 {
 	struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index 4156c95ce1bb..1f3ae7029a92 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -233,6 +233,9 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev,
 	case CS_MODE_PERF:
 		ret = tmc_enable_etf_sink_perf(csdev, data);
 		break;
+	case CS_MODE_API:
+		ret = -ENOSYS;
+		break;
 	/* We shouldn't be here */
 	default:
 		ret = -EINVAL;
@@ -307,12 +310,16 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
 	dev_dbg(drvdata->dev, "TMC-ETF disabled\n");
 }
 
-static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu,
-				  void **pages, int nr_pages, bool overwrite)
+static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, u32 mode,
+                                  int cpu, void **pages, int nr_pages,
+                                  bool overwrite)
 {
 	int node;
 	struct cs_buffers *buf;
 
+	if (mode != CS_MODE_PERF)
+		return NULL;
+
 	if (cpu == -1)
 		cpu = smp_processor_id();
 	node = cpu_to_node(cpu);
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 56fea4ff947e..7e331ffbd937 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -14,6 +14,14 @@
 #include "coresight-priv.h"
 #include "coresight-tmc.h"
 
+#define __exchange(a, b)  \
+do {                      \
+	typeof (a) tmp;   \
+	tmp = a;          \
+	a = b;            \
+	b = tmp;          \
+} while (0);              \
+
 struct etr_flat_buf {
 	struct device	*dev;
 	dma_addr_t	daddr;
@@ -1037,11 +1045,23 @@ static void tmc_etr_sync_sysfs_buf(struct tmc_drvdata *drvdata)
 	}
 }
 
+static void tmc_etr_sync_api_buf(struct tmc_drvdata *drvdata)
+{
+	struct etr_buf *etr_buf = drvdata->etr_buf;
+
+	if (!etr_buf || drvdata->mode != CS_MODE_API)
+		return;
+
+	/* sync the current buffer */
+	tmc_sync_etr_buf(drvdata);
+}
+
 static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 {
 	CS_UNLOCK(drvdata->base);
 
 	tmc_flush_and_stop(drvdata);
+
 	/*
 	 * When operating in sysFS mode the content of the buffer needs to be
 	 * read before the TMC is disabled.
@@ -1049,14 +1069,23 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 	if (drvdata->mode == CS_MODE_SYSFS)
 		tmc_etr_sync_sysfs_buf(drvdata);
 
-	tmc_disable_hw(drvdata);
+	if (drvdata->mode == CS_MODE_API) {
+		/*
+		 * Sync the API buffer to make the trace data available
+		 * for clients.
+		 */
+		tmc_etr_sync_api_buf(drvdata);
+	}
 
+	tmc_disable_hw(drvdata);
 	CS_LOCK(drvdata->base);
 
 	/* Disable CATU device if this ETR is connected to one */
 	tmc_etr_disable_catu(drvdata);
+
 	/* Reset the ETR buf used by hardware */
 	drvdata->etr_buf = NULL;
+
 }
 
 static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
@@ -1177,9 +1206,9 @@ tmc_etr_setup_perf_buf(struct tmc_drvdata *drvdata, int node, int nr_pages,
 }
 
 
-static void *tmc_alloc_etr_buffer(struct coresight_device *csdev,
-				  int cpu, void **pages, int nr_pages,
-				  bool snapshot)
+static void *tmc_alloc_etr_buffer_perf(struct coresight_device *csdev,
+				       int cpu, void **pages, int nr_pages,
+				       bool snapshot)
 {
 	struct etr_perf_buffer *etr_perf;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -1201,6 +1230,75 @@ static void *tmc_alloc_etr_buffer(struct coresight_device *csdev,
 	return etr_perf;
 }
 
+static void *tmc_alloc_etr_buffer_api(struct coresight_device *csdev,
+                                      __maybe_unused int cpu,
+                                      __maybe_unused void **pages,
+                                      int nr_pages,
+                                      __maybe_unused bool snapshot)
+{
+	void *ret = NULL;
+	unsigned long flags;
+	ssize_t size = 0;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+	struct etr_buf *write = NULL, *read = NULL;
+
+	size = nr_pages << PAGE_SHIFT;
+
+	/* Allocate the double buffer __out__ of the lock, as we may sleep. */
+	write = tmc_alloc_etr_buf(drvdata, size, 0, cpu_to_node(0), pages);
+	if (IS_ERR(write)) {
+		ret = write;
+		write = NULL;
+		goto out;
+	}
+
+	read = tmc_alloc_etr_buf(drvdata, size, 0, cpu_to_node(0), pages);
+	if (IS_ERR(read)) {
+		ret = read;
+		read = NULL;
+		goto out;
+	}
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	/* If we're in a middle of a session, this is bad... */
+	if (drvdata->mode != CS_MODE_DISABLED) {
+		ret = ERR_PTR(-EBUSY);
+		goto out_unlock;
+	}
+
+	__exchange(drvdata->api_double_buf.write, write);
+	__exchange(drvdata->api_double_buf.read, read);
+
+
+out_unlock:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+out:
+	/* free the buffers when the spinlock unlocked */
+	if (write)
+		tmc_free_etr_buf(write);
+	if (read)
+		tmc_free_etr_buf(read);
+
+	return ret;
+}
+
+void *tmc_alloc_etr_buffer(struct coresight_device *csdev, u32 mode,
+                           int cpu, void **pages, int nr_pages, bool snapshot)
+{
+	switch (mode) {
+	case CS_MODE_PERF:
+		return tmc_alloc_etr_buffer_perf(csdev, cpu, pages,
+		                                 nr_pages, snapshot);
+	case CS_MODE_API:
+		return tmc_alloc_etr_buffer_api(csdev, cpu, pages,
+		                                nr_pages, snapshot);
+	default:
+		/* We shouldn't get here. */
+		return ERR_PTR(-EINVAL);
+	}
+}
+
 static void tmc_free_etr_buffer(void *config)
 {
 	struct etr_perf_buffer *etr_perf = config;
@@ -1350,6 +1448,40 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
 	return rc;
 }
 
+static int tmc_enable_etr_sink_api(struct coresight_device *csdev)
+{
+	int rc = 0;
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	/* If the sink is already being used by a different mode,
+	 * we cannot interfere. */
+	if (drvdata->mode != CS_MODE_DISABLED &&
+	    drvdata->mode != CS_MODE_API) {
+		rc = -EBUSY;
+		goto out;
+	}
+
+	/* Only the user can allocate the buffer, we don't allocate for
+	 * him. */
+	if (drvdata->api_double_buf.write == NULL) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if (drvdata->mode == CS_MODE_API)
+		goto out;
+
+	drvdata->mode = CS_MODE_API;
+	tmc_etr_enable_hw(drvdata, drvdata->api_double_buf.write);
+out:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	return rc;
+}
+
+
 static int tmc_enable_etr_sink(struct coresight_device *csdev,
 			       u32 mode, void *data)
 {
@@ -1358,13 +1490,15 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev,
 		return tmc_enable_etr_sink_sysfs(csdev);
 	case CS_MODE_PERF:
 		return tmc_enable_etr_sink_perf(csdev, data);
+	case CS_MODE_API:
+		return tmc_enable_etr_sink_api(csdev);
 	}
 
 	/* We shouldn't be here */
 	return -EINVAL;
 }
 
-static void tmc_disable_etr_sink(struct coresight_device *csdev)
+void tmc_disable_etr_sink(struct coresight_device *csdev)
 {
 	unsigned long flags;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -1386,9 +1520,83 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev)
 	dev_dbg(drvdata->dev, "TMC-ETR disabled\n");
 }
 
+static int tmc_read_etr_sink_api(struct coresight_device *csdev, void *dest_buf,
+                                size_t size)
+{
+	int ret = 0;
+	int read = 0;
+	char *src_buf;
+	long bytes, to_read, src_offset;
+	unsigned long flags;
+	struct tmc_drvdata *drvdata;
+	struct etr_buf *etr_buf;
+
+	if (size == 0 || dest_buf == NULL || csdev == NULL)
+		return -EINVAL;
+
+	drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	if (drvdata->api_double_buf.read == NULL) {
+		/* Nothing to read from... */
+		ret = -ENOENT;
+		goto out;
+	}
+
+	if (drvdata->api_double_buf.write->size > size) {
+		ret = -E2BIG;
+		goto out;
+	}
+
+	/* If we have no data in the read buffer, we need to swap buffers. */
+	if (drvdata->api_double_buf.read->len == 0) {
+		/* Make sure we disable the current session if it's an API
+		 * mode. The disable function will make sure the write
+		 * buffer is synced, so we don't need to worry about that. */
+		if (drvdata->mode == CS_MODE_API) {
+			tmc_etr_disable_hw(drvdata);
+		}
+
+		__exchange(drvdata->api_double_buf.read,
+		           drvdata->api_double_buf.write);
+
+		if (drvdata->mode == CS_MODE_API) {
+			tmc_etr_enable_hw(drvdata,
+			                  drvdata->api_double_buf.write);
+		}
+	}
+
+	etr_buf = drvdata->api_double_buf.read;
+	src_offset = etr_buf->offset;
+	to_read = min(etr_buf->len, (s64) size);
+	while (to_read > 0) {
+		bytes = tmc_etr_buf_get_data(etr_buf, src_offset, to_read,
+					     &src_buf);
+		if (WARN_ON_ONCE(bytes <= 0))
+			break;
+
+		memcpy(dest_buf, src_buf, bytes);
+
+		to_read -= bytes;
+		read += bytes;
+
+		src_offset += bytes;
+		if (src_offset >= etr_buf->size)
+			src_offset -= etr_buf->size;
+	}
+
+	etr_buf->len -= read;
+	ret = read;
+out:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	return ret;
+}
+
 static const struct coresight_ops_sink tmc_etr_sink_ops = {
 	.enable		= tmc_enable_etr_sink,
 	.disable	= tmc_disable_etr_sink,
+	.read           = tmc_read_etr_sink_api,
 	.alloc_buffer	= tmc_alloc_etr_buffer,
 	.update_buffer	= tmc_update_etr_buffer,
 	.free_buffer	= tmc_free_etr_buffer,
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 487c53701e9c..01f108a31943 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -153,6 +153,11 @@ struct etr_buf {
 	void				*private;
 };
 
+struct api_double_buffer {
+    struct etr_buf *write;
+    struct etr_buf *read;
+};
+
 /**
  * struct tmc_drvdata - specifics associated to an TMC component
  * @base:	memory mapped base address for this component.
@@ -172,6 +177,7 @@ struct etr_buf {
  *		device configuration register (DEVID)
  * @perf_data:	PERF buffer for ETR.
  * @sysfs_data:	SYSFS buffer for ETR.
+ * @api_double_buf:	API double buffer for ETR.
  */
 struct tmc_drvdata {
 	void __iomem		*base;
@@ -193,6 +199,7 @@ struct tmc_drvdata {
 	u32			etr_caps;
 	struct etr_buf		*sysfs_buf;
 	void			*perf_data;
+	struct api_double_buffer api_double_buf;
 };
 
 struct etr_buf_operations {
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index e73ca6af4765..14ed8b15e458 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -388,6 +388,20 @@ struct coresight_device *coresight_get_sink(struct list_head *path)
 	return csdev;
 }
 
+struct coresight_device *coresight_get_source(struct list_head *path)
+{
+	struct coresight_device *csdev;
+
+	if (!path)
+		return NULL;
+
+	csdev = list_first_entry(path, struct coresight_node, link)->csdev;
+	if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE)
+		return NULL;
+
+	return csdev;
+}
+
 static int coresight_enabled_sink(struct device *dev, void *data)
 {
 	bool *reset = data;
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 53535821dc25..20c0e273b63a 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -183,6 +183,7 @@ struct coresight_device {
  * Operations available for sinks
  * @enable:		enables the sink.
  * @disable:		disables the sink.
+ * @read:		read trace data from the trace buffer.
  * @alloc_buffer:	initialises perf's ring buffer for trace collection.
  * @free_buffer:	release memory allocated in @get_config.
  * @update_buffer:	update buffer pointers after a trace session.
@@ -190,7 +191,8 @@ struct coresight_device {
 struct coresight_ops_sink {
 	int (*enable)(struct coresight_device *csdev, u32 mode, void *data);
 	void (*disable)(struct coresight_device *csdev);
-	void *(*alloc_buffer)(struct coresight_device *csdev, int cpu,
+	int (*read)(struct coresight_device *csdev, void *dest_buf, size_t size);
+	void *(*alloc_buffer)(struct coresight_device *csdev, u32 mode, int cpu,
 			      void **pages, int nr_pages, bool overwrite);
 	void (*free_buffer)(void *config);
 	unsigned long (*update_buffer)(struct coresight_device *csdev,
@@ -223,9 +225,9 @@ struct coresight_ops_source {
 	int (*cpu_id)(struct coresight_device *csdev);
 	int (*trace_id)(struct coresight_device *csdev);
 	int (*enable)(struct coresight_device *csdev,
-		      struct perf_event *event,  u32 mode);
+		      void *config,  u32 mode);
 	void (*disable)(struct coresight_device *csdev,
-			struct perf_event *event);
+			void *config);
 };
 
 /**
@@ -249,6 +251,26 @@ struct coresight_ops {
 	const struct coresight_ops_helper *helper_ops;
 };
 
+typedef struct list_head *coresight_session_t;
+
+/**
+ * struct etm_config - Generic etm configuration.
+ * @retstack:	enable retstack.
+ * @user:	trace user mode.
+ * @kernel:	trace kernel mode.
+ * @timestamp:	enable timestamp packets.
+ * @cycacc:	enable acc
+ * @trace_id:	trace id.
+ */
+struct etm_config {
+       bool retstack;     /* enable retstack */
+       bool user;         /* trace user mode */
+       bool kernel;       /* trace kernel mode */
+       bool timestamp;    /* timestamp packets */
+       bool cycacc;       /* cyc packets */
+       int trace_id;      /* etm trace id */
+};
+
 #ifdef CONFIG_CORESIGHT
 extern struct coresight_device *
 coresight_register(struct coresight_desc *desc);
@@ -257,6 +279,12 @@ extern int coresight_enable(struct coresight_device *csdev);
 extern void coresight_disable(struct coresight_device *csdev);
 extern int coresight_timeout(void __iomem *addr, u32 offset,
 			     int position, int value);
+extern int coresight_etm_alloc_buffer(size_t buffer_size);
+coresight_session_t coresight_etm_create_session(int cpu);
+void coresight_etm_destroy_session(coresight_session_t session);
+int coresight_etm_play(coresight_session_t session, struct etm_config *config);
+void coresight_etm_pause(coresight_session_t session);
+int coresight_etm_read(coresight_session_t session, void *buf, size_t size);
 #else
 static inline struct coresight_device *
 coresight_register(struct coresight_desc *desc) { return NULL; }
@@ -280,4 +308,5 @@ static inline struct coresight_platform_data *of_get_coresight_platform_data(
 	struct device *dev, const struct device_node *node) { return NULL; }
 #endif
 
+
 #endif
-- 
2.16.2

