linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V2 00/30] Coresight integration with perf
@ 2015-10-18 18:24 Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 01/30] coresight: etm3x: moving etm_readl/writel to header file Mathieu Poirier
                   ` (29 more replies)
  0 siblings, 30 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset aims to integrate configuration and control of
the Coresight tracers with the perf sub-system.

The goal is to use PMUs to represent tracers and the auxiliary
buffer enhancement to collect processor traces.  As such a lot
of work is done to move the current Coresight sysFS oriented
configuration and control commands to perf's AUX API.

For the time being the work concentrates on ETMv3 and ETB1.0
sink buffers.  Work on ETMv4 and other type of sink buffers
will follow once a foundation has been established.

Best regards,
Mathieu

Changes since V1:
 * Fixed typos in typographical error in documentation.
 * Moved to a multi session support scheme.
 * Split static and dynamic tracer configuration.
 * Fixed configuration for user and kernel space tracing.
 * Using WARN_ON_ONCE() rather than WARN_ON().
 * Implemented strategy to prevent tracers from being used simultaneously.
 * Changed sink_ops::unset_buffer() to sink_ops::reset_buffer().
 * Moves ETM's sysFS interface from driver core to dedicated file.
 * Removed spinlock in "etm_cpu_id()".
 * Aggregated PMU driver pieces in a single patch.
 * Added user space changes and rebased everything to v4.3-rc5. 

Mathieu Poirier (30):
  coresight: etm3x: moving etm_readl/writel to header file
  coresight: etm3x: moving sysFS entries to dedicated file
  coresight: etm3x: unlocking tracers in default arch init
  coresight: etm3x: splitting struct etm_drvdata
  coresight: etm3x: set progbit to stop trace collection
  coresight: clearly labeling source operarions
  coresight: etm3x: moving etm_drvdata::enable to atomic field
  coresight: etm3x: implementing 'cpu_id()' API
  coresight: etm3x: changing default trace configuration
  coresight: etm3x: consolidating initial config
  coresight: etm3x: implementing user/kernel mode tracing
  coresight: etm3x: adding perf_get/set_config() API
  coresight: etm3x: implementing perf_enable/disable() API
  coresight: etm3x: implementing perf_start/stop() API
  coresight: making coresight_build_paths() public
  coresight: keeping track of enabled sink buffers
  perf: changing pmu::setup_aux() parameter to include event
  coresight: etb10: moving to local atomic operations
  coresight: etb10: implementing the setup_aux() API
  coresight: etb10: implementing buffer set/reset() API
  coresight: etb10: implementing buffer update API
  coresight: etm-perf: new PMU driver for ETM tracers
  coresight: updating documentation to reflect integration with perf
  perf tools: making function set_max_cpu_num() non static
  perf tools: adding perf_session to *info_prive_size()
  perf tools: making source devices path broadly accessible
  perf build: adding X86 auxiliary specific flags
  perf tools: making coresight PMU listable
  perf tools: adding coresight define for auxtrace
  perf tools: adding coresight etm PMU record capabilities

 Documentation/trace/coresight.txt                  |  138 +-
 arch/x86/kernel/cpu/perf_event_intel_bts.c         |    4 +-
 arch/x86/kernel/cpu/perf_event_intel_pt.c          |    5 +-
 drivers/hwtracing/coresight/Makefile               |    4 +-
 drivers/hwtracing/coresight/coresight-etb10.c      |  247 ++-
 drivers/hwtracing/coresight/coresight-etm-perf.c   |  533 ++++++
 drivers/hwtracing/coresight/coresight-etm-perf.h   |   27 +
 drivers/hwtracing/coresight/coresight-etm.h        |  151 +-
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    | 1442 ++++++++++++++++
 drivers/hwtracing/coresight/coresight-etm3x.c      | 1724 ++++----------------
 drivers/hwtracing/coresight/coresight-etm4x.c      |    8 +-
 drivers/hwtracing/coresight/coresight-priv.h       |    4 +
 drivers/hwtracing/coresight/coresight.c            |   27 +-
 include/linux/coresight.h                          |   51 +-
 include/linux/perf_event.h                         |    2 +-
 kernel/events/ring_buffer.c                        |    2 +-
 tools/perf/arch/arm/util/Build                     |    2 +
 tools/perf/arch/arm/util/auxtrace.c                |   48 +
 tools/perf/arch/arm/util/cs_etm.c                  |  444 +++++
 tools/perf/arch/arm/util/cs_etm.h                  |   37 +
 tools/perf/arch/arm/util/pmu.c                     |   18 +
 tools/perf/arch/x86/util/Build                     |    6 +-
 tools/perf/arch/x86/util/intel-bts.c               |    4 +-
 tools/perf/arch/x86/util/intel-pt.c                |    4 +-
 tools/perf/arch/x86/util/pmu.c                     |    2 +-
 tools/perf/builtin-inject.c                        |    2 +-
 tools/perf/builtin-record.c                        |    2 +-
 tools/perf/config/Makefile                         |   19 +-
 tools/perf/util/Build                              |    6 +-
 tools/perf/util/auxtrace.c                         |    8 +-
 tools/perf/util/auxtrace.h                         |    9 +-
 tools/perf/util/cpumap.c                           |    2 +-
 tools/perf/util/cpumap.h                           |    1 +
 tools/perf/util/intel-bts.h                        |   11 +
 tools/perf/util/intel-pt-decoder/Build             |    2 +-
 tools/perf/util/intel-pt.h                         |   15 +
 tools/perf/util/pmu.c                              |    2 -
 tools/perf/util/pmu.h                              |    1 +
 38 files changed, 3531 insertions(+), 1483 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.c
 create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.h
 create mode 100644 drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
 create mode 100644 tools/perf/arch/arm/util/auxtrace.c
 create mode 100644 tools/perf/arch/arm/util/cs_etm.c
 create mode 100644 tools/perf/arch/arm/util/cs_etm.h
 create mode 100644 tools/perf/arch/arm/util/pmu.c

-- 
1.9.1

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH V2 01/30] coresight: etm3x: moving etm_readl/writel to header file
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-19 18:37   ` Greg KH
  2015-10-18 18:24 ` [PATCH V2 02/30] coresight: etm3x: moving sysFS entries to dedicated file Mathieu Poirier
                   ` (28 subsequent siblings)
  29 siblings, 1 reply; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Moving functions etm_readl/writel to file "coresight-etm.h"
for access by code outside of the main ETM3x driver.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h   | 29 +++++++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-etm3x.c | 29 ---------------------------
 2 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index b4481eb29304..34f7db881fa7 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -251,4 +251,33 @@ enum etm_addr_type {
 	ETM_ADDR_TYPE_START,
 	ETM_ADDR_TYPE_STOP,
 };
+
+static inline void etm_writel(struct etm_drvdata *drvdata,
+			      u32 val, u32 off)
+{
+	if (drvdata->use_cp14) {
+		if (etm_writel_cp14(off, val)) {
+			dev_err(drvdata->dev,
+				"invalid CP14 access to ETM reg: %#x", off);
+		}
+	} else {
+		writel_relaxed(val, drvdata->base + off);
+	}
+}
+
+static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
+{
+	u32 val;
+
+	if (drvdata->use_cp14) {
+		if (etm_readl_cp14(off, &val)) {
+			dev_err(drvdata->dev,
+				"invalid CP14 access to ETM reg: %#x", off);
+		}
+	} else {
+		val = readl_relaxed(drvdata->base + off);
+	}
+
+	return val;
+}
 #endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index d630b7ece735..c1dc095c3fb0 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -42,35 +42,6 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
 static int etm_count;
 static struct etm_drvdata *etmdrvdata[NR_CPUS];
 
-static inline void etm_writel(struct etm_drvdata *drvdata,
-			      u32 val, u32 off)
-{
-	if (drvdata->use_cp14) {
-		if (etm_writel_cp14(off, val)) {
-			dev_err(drvdata->dev,
-				"invalid CP14 access to ETM reg: %#x", off);
-		}
-	} else {
-		writel_relaxed(val, drvdata->base + off);
-	}
-}
-
-static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
-{
-	u32 val;
-
-	if (drvdata->use_cp14) {
-		if (etm_readl_cp14(off, &val)) {
-			dev_err(drvdata->dev,
-				"invalid CP14 access to ETM reg: %#x", off);
-		}
-	} else {
-		val = readl_relaxed(drvdata->base + off);
-	}
-
-	return val;
-}
-
 /*
  * Memory mapped writes to clear os lock are not supported on some processors
  * and OS lock must be unlocked before any memory mapped access on such
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 02/30] coresight: etm3x: moving sysFS entries to dedicated file
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 01/30] coresight: etm3x: moving etm_readl/writel to header file Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 03/30] coresight: etm3x: unlocking tracers in default arch init Mathieu Poirier
                   ` (27 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

SysFS entries are big enough to justify their own file.
As such moving all sysFS related declarations to a dedicated
location.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/Makefile               |    3 +-
 drivers/hwtracing/coresight/coresight-etm.h        |    4 +
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    | 1218 +++++++++++++++++++
 drivers/hwtracing/coresight/coresight-etm3x.c      | 1234 +-------------------
 4 files changed, 1241 insertions(+), 1218 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-etm3x-sysfs.c

diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 99f8e5f6256e..233d66cf22d3 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o
 obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o
 obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \
 					   coresight-replicator.o
-obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o
+obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \
+					coresight-etm3x-sysfs.o
 obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
 obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o
diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 34f7db881fa7..9a30aa392ed9 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -280,4 +280,8 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
 
 	return val;
 }
+
+extern const struct attribute_group *coresight_etm_groups[];
+int etm_get_trace_id(struct etm_drvdata *drvdata);
+void etm_set_default(struct etm_drvdata *drvdata);
 #endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
new file mode 100644
index 000000000000..f409f5a88e95
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -0,0 +1,1218 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/pm_runtime.h>
+#include <linux/sysfs.h>
+#include "coresight-etm.h"
+
+static ssize_t nr_addr_cmp_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->nr_addr_cmp;
+	return sprintf(buf, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(nr_addr_cmp);
+
+static ssize_t nr_cntr_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->nr_cntr;
+	return sprintf(buf, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(nr_cntr);
+
+static ssize_t nr_ctxid_cmp_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->nr_ctxid_cmp;
+	return sprintf(buf, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(nr_ctxid_cmp);
+
+static ssize_t etmsr_show(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	unsigned long flags, val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	pm_runtime_get_sync(drvdata->dev);
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	CS_UNLOCK(drvdata->base);
+
+	val = etm_readl(drvdata, ETMSR);
+
+	CS_LOCK(drvdata->base);
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	pm_runtime_put(drvdata->dev);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+static DEVICE_ATTR_RO(etmsr);
+
+static ssize_t reset_store(struct device *dev,
+			   struct device_attribute *attr,
+			   const char *buf, size_t size)
+{
+	int i, ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val) {
+		spin_lock(&drvdata->spinlock);
+		drvdata->mode = ETM_MODE_EXCLUDE;
+		drvdata->ctrl = 0x0;
+		drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL;
+		drvdata->startstop_ctrl = 0x0;
+		drvdata->addr_idx = 0x0;
+		for (i = 0; i < drvdata->nr_addr_cmp; i++) {
+			drvdata->addr_val[i] = 0x0;
+			drvdata->addr_acctype[i] = 0x0;
+			drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE;
+		}
+		drvdata->cntr_idx = 0x0;
+
+		etm_set_default(drvdata);
+		spin_unlock(&drvdata->spinlock);
+	}
+
+	return size;
+}
+static DEVICE_ATTR_WO(reset);
+
+static ssize_t mode_show(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->mode;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t mode_store(struct device *dev,
+			  struct device_attribute *attr,
+			  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->mode = val & ETM_MODE_ALL;
+
+	if (drvdata->mode & ETM_MODE_EXCLUDE)
+		drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC;
+	else
+		drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC;
+
+	if (drvdata->mode & ETM_MODE_CYCACC)
+		drvdata->ctrl |= ETMCR_CYC_ACC;
+	else
+		drvdata->ctrl &= ~ETMCR_CYC_ACC;
+
+	if (drvdata->mode & ETM_MODE_STALL) {
+		if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) {
+			dev_warn(drvdata->dev, "stall mode not supported\n");
+			ret = -EINVAL;
+			goto err_unlock;
+		}
+		drvdata->ctrl |= ETMCR_STALL_MODE;
+	 } else
+		drvdata->ctrl &= ~ETMCR_STALL_MODE;
+
+	if (drvdata->mode & ETM_MODE_TIMESTAMP) {
+		if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) {
+			dev_warn(drvdata->dev, "timestamp not supported\n");
+			ret = -EINVAL;
+			goto err_unlock;
+		}
+		drvdata->ctrl |= ETMCR_TIMESTAMP_EN;
+	} else
+		drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN;
+
+	if (drvdata->mode & ETM_MODE_CTXID)
+		drvdata->ctrl |= ETMCR_CTXID_SIZE;
+	else
+		drvdata->ctrl &= ~ETMCR_CTXID_SIZE;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+
+err_unlock:
+	spin_unlock(&drvdata->spinlock);
+	return ret;
+}
+static DEVICE_ATTR_RW(mode);
+
+static ssize_t trigger_event_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->trigger_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t trigger_event_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->trigger_event = val & ETM_EVENT_MASK;
+
+	return size;
+}
+static DEVICE_ATTR_RW(trigger_event);
+
+static ssize_t enable_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->enable_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t enable_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->enable_event = val & ETM_EVENT_MASK;
+
+	return size;
+}
+static DEVICE_ATTR_RW(enable_event);
+
+static ssize_t fifofull_level_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->fifofull_level;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t fifofull_level_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->fifofull_level = val;
+
+	return size;
+}
+static DEVICE_ATTR_RW(fifofull_level);
+
+static ssize_t addr_idx_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->addr_idx;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_idx_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val >= drvdata->nr_addr_cmp)
+		return -EINVAL;
+
+	/*
+	 * Use spinlock to ensure index doesn't change while it gets
+	 * dereferenced multiple times within a spinlock block elsewhere.
+	 */
+	spin_lock(&drvdata->spinlock);
+	drvdata->addr_idx = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_idx);
+
+static ssize_t addr_single_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	u8 idx;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EINVAL;
+	}
+
+	val = drvdata->addr_val[idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_single_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t size)
+{
+	u8 idx;
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EINVAL;
+	}
+
+	drvdata->addr_val[idx] = val;
+	drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_single);
+
+static ssize_t addr_range_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	u8 idx;
+	unsigned long val1, val2;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (idx % 2 != 0) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	val1 = drvdata->addr_val[idx];
+	val2 = drvdata->addr_val[idx + 1];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx %#lx\n", val1, val2);
+}
+
+static ssize_t addr_range_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	u8 idx;
+	unsigned long val1, val2;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
+		return -EINVAL;
+	/* Lower address comparator cannot have a higher address value */
+	if (val1 > val2)
+		return -EINVAL;
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (idx % 2 != 0) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	drvdata->addr_val[idx] = val1;
+	drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE;
+	drvdata->addr_val[idx + 1] = val2;
+	drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
+	drvdata->enable_ctrl1 |= (1 << (idx/2));
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_range);
+
+static ssize_t addr_start_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	u8 idx;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	val = drvdata->addr_val[idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_start_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	u8 idx;
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	drvdata->addr_val[idx] = val;
+	drvdata->addr_type[idx] = ETM_ADDR_TYPE_START;
+	drvdata->startstop_ctrl |= (1 << idx);
+	drvdata->enable_ctrl1 |= BIT(25);
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_start);
+
+static ssize_t addr_stop_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	u8 idx;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	val = drvdata->addr_val[idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_stop_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t size)
+{
+	u8 idx;
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	idx = drvdata->addr_idx;
+	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+		spin_unlock(&drvdata->spinlock);
+		return -EPERM;
+	}
+
+	drvdata->addr_val[idx] = val;
+	drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP;
+	drvdata->startstop_ctrl |= (1 << (idx + 16));
+	drvdata->enable_ctrl1 |= ETMTECR1_START_STOP;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_stop);
+
+static ssize_t addr_acctype_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->addr_acctype[drvdata->addr_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t addr_acctype_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->addr_acctype[drvdata->addr_idx] = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(addr_acctype);
+
+static ssize_t cntr_idx_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->cntr_idx;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t cntr_idx_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val >= drvdata->nr_cntr)
+		return -EINVAL;
+	/*
+	 * Use spinlock to ensure index doesn't change while it gets
+	 * dereferenced multiple times within a spinlock block elsewhere.
+	 */
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_idx = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_idx);
+
+static ssize_t cntr_rld_val_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->cntr_rld_val[drvdata->cntr_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t cntr_rld_val_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_rld_val[drvdata->cntr_idx] = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_rld_val);
+
+static ssize_t cntr_event_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->cntr_event[drvdata->cntr_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t cntr_event_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_event);
+
+static ssize_t cntr_rld_event_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->cntr_rld_event[drvdata->cntr_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t cntr_rld_event_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_rld_event);
+
+static ssize_t cntr_val_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	int i, ret = 0;
+	u32 val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!drvdata->enable) {
+		spin_lock(&drvdata->spinlock);
+		for (i = 0; i < drvdata->nr_cntr; i++)
+			ret += sprintf(buf, "counter %d: %x\n",
+				       i, drvdata->cntr_val[i]);
+		spin_unlock(&drvdata->spinlock);
+		return ret;
+	}
+
+	for (i = 0; i < drvdata->nr_cntr; i++) {
+		val = etm_readl(drvdata, ETMCNTVRn(i));
+		ret += sprintf(buf, "counter %d: %x\n", i, val);
+	}
+
+	return ret;
+}
+
+static ssize_t cntr_val_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->cntr_val[drvdata->cntr_idx] = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(cntr_val);
+
+static ssize_t seq_12_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_12_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_12_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_12_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_12_event);
+
+static ssize_t seq_21_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_21_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_21_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_21_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_21_event);
+
+static ssize_t seq_23_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_23_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_23_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_23_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_23_event);
+
+static ssize_t seq_31_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_31_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_31_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_31_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_31_event);
+
+static ssize_t seq_32_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_32_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_32_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_32_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_32_event);
+
+static ssize_t seq_13_event_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->seq_13_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_13_event_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->seq_13_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(seq_13_event);
+
+static ssize_t seq_curr_state_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	unsigned long val, flags;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	if (!drvdata->enable) {
+		val = drvdata->seq_curr_state;
+		goto out;
+	}
+
+	pm_runtime_get_sync(drvdata->dev);
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	CS_UNLOCK(drvdata->base);
+	val = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
+	CS_LOCK(drvdata->base);
+
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	pm_runtime_put(drvdata->dev);
+out:
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t seq_curr_state_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val > ETM_SEQ_STATE_MAX_VAL)
+		return -EINVAL;
+
+	drvdata->seq_curr_state = val;
+
+	return size;
+}
+static DEVICE_ATTR_RW(seq_curr_state);
+
+static ssize_t ctxid_idx_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->ctxid_idx;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t ctxid_idx_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	if (val >= drvdata->nr_ctxid_cmp)
+		return -EINVAL;
+
+	/*
+	 * Use spinlock to ensure index doesn't change while it gets
+	 * dereferenced multiple times within a spinlock block elsewhere.
+	 */
+	spin_lock(&drvdata->spinlock);
+	drvdata->ctxid_idx = val;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(ctxid_idx);
+
+static ssize_t ctxid_pid_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	spin_lock(&drvdata->spinlock);
+	val = drvdata->ctxid_vpid[drvdata->ctxid_idx];
+	spin_unlock(&drvdata->spinlock);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t ctxid_pid_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t size)
+{
+	int ret;
+	unsigned long vpid, pid;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &vpid);
+	if (ret)
+		return ret;
+
+	pid = coresight_vpid_to_pid(vpid);
+
+	spin_lock(&drvdata->spinlock);
+	drvdata->ctxid_pid[drvdata->ctxid_idx] = pid;
+	drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid;
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(ctxid_pid);
+
+static ssize_t ctxid_mask_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->ctxid_mask;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t ctxid_mask_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->ctxid_mask = val;
+	return size;
+}
+static DEVICE_ATTR_RW(ctxid_mask);
+
+static ssize_t sync_freq_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->sync_freq;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t sync_freq_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->sync_freq = val & ETM_SYNC_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(sync_freq);
+
+static ssize_t timestamp_event_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->timestamp_event;
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t timestamp_event_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->timestamp_event = val & ETM_EVENT_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(timestamp_event);
+
+static ssize_t cpu_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	int val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = drvdata->cpu;
+	return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+
+}
+static DEVICE_ATTR_RO(cpu);
+
+static ssize_t traceid_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	val = etm_get_trace_id(drvdata);
+
+	return sprintf(buf, "%#lx\n", val);
+}
+
+static ssize_t traceid_store(struct device *dev,
+			     struct device_attribute *attr,
+			     const char *buf, size_t size)
+{
+	int ret;
+	unsigned long val;
+	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	ret = kstrtoul(buf, 16, &val);
+	if (ret)
+		return ret;
+
+	drvdata->traceid = val & ETM_TRACEID_MASK;
+	return size;
+}
+static DEVICE_ATTR_RW(traceid);
+
+static struct attribute *coresight_etm_attrs[] = {
+	&dev_attr_nr_addr_cmp.attr,
+	&dev_attr_nr_cntr.attr,
+	&dev_attr_nr_ctxid_cmp.attr,
+	&dev_attr_etmsr.attr,
+	&dev_attr_reset.attr,
+	&dev_attr_mode.attr,
+	&dev_attr_trigger_event.attr,
+	&dev_attr_enable_event.attr,
+	&dev_attr_fifofull_level.attr,
+	&dev_attr_addr_idx.attr,
+	&dev_attr_addr_single.attr,
+	&dev_attr_addr_range.attr,
+	&dev_attr_addr_start.attr,
+	&dev_attr_addr_stop.attr,
+	&dev_attr_addr_acctype.attr,
+	&dev_attr_cntr_idx.attr,
+	&dev_attr_cntr_rld_val.attr,
+	&dev_attr_cntr_event.attr,
+	&dev_attr_cntr_rld_event.attr,
+	&dev_attr_cntr_val.attr,
+	&dev_attr_seq_12_event.attr,
+	&dev_attr_seq_21_event.attr,
+	&dev_attr_seq_23_event.attr,
+	&dev_attr_seq_31_event.attr,
+	&dev_attr_seq_32_event.attr,
+	&dev_attr_seq_13_event.attr,
+	&dev_attr_seq_curr_state.attr,
+	&dev_attr_ctxid_idx.attr,
+	&dev_attr_ctxid_pid.attr,
+	&dev_attr_ctxid_mask.attr,
+	&dev_attr_sync_freq.attr,
+	&dev_attr_timestamp_event.attr,
+	&dev_attr_traceid.attr,
+	&dev_attr_cpu.attr,
+	NULL,
+};
+
+#define coresight_simple_func(name, offset)                             \
+static ssize_t name##_show(struct device *_dev,                         \
+			   struct device_attribute *attr, char *buf)    \
+{                                                                       \
+	struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent);    \
+	return scnprintf(buf, PAGE_SIZE, "0x%x\n",                      \
+			 readl_relaxed(drvdata->base + offset));        \
+}                                                                       \
+DEVICE_ATTR_RO(name)
+
+coresight_simple_func(etmccr, ETMCCR);
+coresight_simple_func(etmccer, ETMCCER);
+coresight_simple_func(etmscr, ETMSCR);
+coresight_simple_func(etmidr, ETMIDR);
+coresight_simple_func(etmcr, ETMCR);
+coresight_simple_func(etmtraceidr, ETMTRACEIDR);
+coresight_simple_func(etmteevr, ETMTEEVR);
+coresight_simple_func(etmtssvr, ETMTSSCR);
+coresight_simple_func(etmtecr1, ETMTECR1);
+coresight_simple_func(etmtecr2, ETMTECR2);
+
+static struct attribute *coresight_etm_mgmt_attrs[] = {
+	&dev_attr_etmccr.attr,
+	&dev_attr_etmccer.attr,
+	&dev_attr_etmscr.attr,
+	&dev_attr_etmidr.attr,
+	&dev_attr_etmcr.attr,
+	&dev_attr_etmtraceidr.attr,
+	&dev_attr_etmteevr.attr,
+	&dev_attr_etmtssvr.attr,
+	&dev_attr_etmtecr1.attr,
+	&dev_attr_etmtecr2.attr,
+	NULL,
+};
+
+static const struct attribute_group coresight_etm_group = {
+	.attrs = coresight_etm_attrs,
+};
+
+static const struct attribute_group coresight_etm_mgmt_group = {
+	.attrs = coresight_etm_mgmt_attrs,
+	.name = "mgmt",
+};
+
+const struct attribute_group *coresight_etm_groups[] = {
+	&coresight_etm_group,
+	&coresight_etm_mgmt_group,
+	NULL,
+};
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index c1dc095c3fb0..2a1950d0753d 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -186,7 +186,7 @@ static void etm_clr_prog(struct etm_drvdata *drvdata)
 	}
 }
 
-static void etm_set_default(struct etm_drvdata *drvdata)
+void etm_set_default(struct etm_drvdata *drvdata)
 {
 	int i;
 
@@ -286,15 +286,18 @@ static void etm_enable_hw(void *info)
 	dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
 }
 
-static int etm_trace_id(struct coresight_device *csdev)
+int etm_get_trace_id(struct etm_drvdata *drvdata)
 {
-	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	unsigned long flags;
 	int trace_id = -1;
 
+	if (!drvdata)
+		goto out;
+
 	if (!drvdata->enable)
 		return drvdata->traceid;
-	pm_runtime_get_sync(csdev->dev.parent);
+
+	pm_runtime_get_sync(drvdata->dev);
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
@@ -303,9 +306,18 @@ static int etm_trace_id(struct coresight_device *csdev)
 	CS_LOCK(drvdata->base);
 
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(csdev->dev.parent);
+	pm_runtime_put(drvdata->dev);
 
+out:
 	return trace_id;
+
+}
+
+static int etm_trace_id(struct coresight_device *csdev)
+{
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	return etm_get_trace_id(drvdata);
 }
 
 static int etm_enable(struct coresight_device *csdev)
@@ -401,1218 +413,6 @@ static const struct coresight_ops etm_cs_ops = {
 	.source_ops	= &etm_source_ops,
 };
 
-static ssize_t nr_addr_cmp_show(struct device *dev,
-				struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->nr_addr_cmp;
-	return sprintf(buf, "%#lx\n", val);
-}
-static DEVICE_ATTR_RO(nr_addr_cmp);
-
-static ssize_t nr_cntr_show(struct device *dev,
-			    struct device_attribute *attr, char *buf)
-{	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->nr_cntr;
-	return sprintf(buf, "%#lx\n", val);
-}
-static DEVICE_ATTR_RO(nr_cntr);
-
-static ssize_t nr_ctxid_cmp_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->nr_ctxid_cmp;
-	return sprintf(buf, "%#lx\n", val);
-}
-static DEVICE_ATTR_RO(nr_ctxid_cmp);
-
-static ssize_t etmsr_show(struct device *dev,
-			  struct device_attribute *attr, char *buf)
-{
-	unsigned long flags, val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	pm_runtime_get_sync(drvdata->dev);
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	CS_UNLOCK(drvdata->base);
-
-	val = etm_readl(drvdata, ETMSR);
-
-	CS_LOCK(drvdata->base);
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(drvdata->dev);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-static DEVICE_ATTR_RO(etmsr);
-
-static ssize_t reset_store(struct device *dev,
-			   struct device_attribute *attr,
-			   const char *buf, size_t size)
-{
-	int i, ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val) {
-		spin_lock(&drvdata->spinlock);
-		drvdata->mode = ETM_MODE_EXCLUDE;
-		drvdata->ctrl = 0x0;
-		drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL;
-		drvdata->startstop_ctrl = 0x0;
-		drvdata->addr_idx = 0x0;
-		for (i = 0; i < drvdata->nr_addr_cmp; i++) {
-			drvdata->addr_val[i] = 0x0;
-			drvdata->addr_acctype[i] = 0x0;
-			drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE;
-		}
-		drvdata->cntr_idx = 0x0;
-
-		etm_set_default(drvdata);
-		spin_unlock(&drvdata->spinlock);
-	}
-
-	return size;
-}
-static DEVICE_ATTR_WO(reset);
-
-static ssize_t mode_show(struct device *dev,
-			 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->mode;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t mode_store(struct device *dev,
-			  struct device_attribute *attr,
-			  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->mode = val & ETM_MODE_ALL;
-
-	if (drvdata->mode & ETM_MODE_EXCLUDE)
-		drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC;
-	else
-		drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC;
-
-	if (drvdata->mode & ETM_MODE_CYCACC)
-		drvdata->ctrl |= ETMCR_CYC_ACC;
-	else
-		drvdata->ctrl &= ~ETMCR_CYC_ACC;
-
-	if (drvdata->mode & ETM_MODE_STALL) {
-		if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) {
-			dev_warn(drvdata->dev, "stall mode not supported\n");
-			ret = -EINVAL;
-			goto err_unlock;
-		}
-		drvdata->ctrl |= ETMCR_STALL_MODE;
-	 } else
-		drvdata->ctrl &= ~ETMCR_STALL_MODE;
-
-	if (drvdata->mode & ETM_MODE_TIMESTAMP) {
-		if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) {
-			dev_warn(drvdata->dev, "timestamp not supported\n");
-			ret = -EINVAL;
-			goto err_unlock;
-		}
-		drvdata->ctrl |= ETMCR_TIMESTAMP_EN;
-	} else
-		drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN;
-
-	if (drvdata->mode & ETM_MODE_CTXID)
-		drvdata->ctrl |= ETMCR_CTXID_SIZE;
-	else
-		drvdata->ctrl &= ~ETMCR_CTXID_SIZE;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-
-err_unlock:
-	spin_unlock(&drvdata->spinlock);
-	return ret;
-}
-static DEVICE_ATTR_RW(mode);
-
-static ssize_t trigger_event_show(struct device *dev,
-				  struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->trigger_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t trigger_event_store(struct device *dev,
-				   struct device_attribute *attr,
-				   const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->trigger_event = val & ETM_EVENT_MASK;
-
-	return size;
-}
-static DEVICE_ATTR_RW(trigger_event);
-
-static ssize_t enable_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->enable_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t enable_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->enable_event = val & ETM_EVENT_MASK;
-
-	return size;
-}
-static DEVICE_ATTR_RW(enable_event);
-
-static ssize_t fifofull_level_show(struct device *dev,
-				   struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->fifofull_level;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t fifofull_level_store(struct device *dev,
-				    struct device_attribute *attr,
-				    const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->fifofull_level = val;
-
-	return size;
-}
-static DEVICE_ATTR_RW(fifofull_level);
-
-static ssize_t addr_idx_show(struct device *dev,
-			     struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->addr_idx;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_idx_store(struct device *dev,
-			      struct device_attribute *attr,
-			      const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val >= drvdata->nr_addr_cmp)
-		return -EINVAL;
-
-	/*
-	 * Use spinlock to ensure index doesn't change while it gets
-	 * dereferenced multiple times within a spinlock block elsewhere.
-	 */
-	spin_lock(&drvdata->spinlock);
-	drvdata->addr_idx = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_idx);
-
-static ssize_t addr_single_show(struct device *dev,
-				struct device_attribute *attr, char *buf)
-{
-	u8 idx;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EINVAL;
-	}
-
-	val = drvdata->addr_val[idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_single_store(struct device *dev,
-				 struct device_attribute *attr,
-				 const char *buf, size_t size)
-{
-	u8 idx;
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EINVAL;
-	}
-
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_single);
-
-static ssize_t addr_range_show(struct device *dev,
-			       struct device_attribute *attr, char *buf)
-{
-	u8 idx;
-	unsigned long val1, val2;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (idx % 2 != 0) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
-	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	val1 = drvdata->addr_val[idx];
-	val2 = drvdata->addr_val[idx + 1];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx %#lx\n", val1, val2);
-}
-
-static ssize_t addr_range_store(struct device *dev,
-			      struct device_attribute *attr,
-			      const char *buf, size_t size)
-{
-	u8 idx;
-	unsigned long val1, val2;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
-		return -EINVAL;
-	/* Lower address comparator cannot have a higher address value */
-	if (val1 > val2)
-		return -EINVAL;
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (idx % 2 != 0) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
-	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	drvdata->addr_val[idx] = val1;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE;
-	drvdata->addr_val[idx + 1] = val2;
-	drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
-	drvdata->enable_ctrl1 |= (1 << (idx/2));
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_range);
-
-static ssize_t addr_start_show(struct device *dev,
-			       struct device_attribute *attr, char *buf)
-{
-	u8 idx;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	val = drvdata->addr_val[idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_start_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t size)
-{
-	u8 idx;
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_START;
-	drvdata->startstop_ctrl |= (1 << idx);
-	drvdata->enable_ctrl1 |= BIT(25);
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_start);
-
-static ssize_t addr_stop_show(struct device *dev,
-			      struct device_attribute *attr, char *buf)
-{
-	u8 idx;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	val = drvdata->addr_val[idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_stop_store(struct device *dev,
-			       struct device_attribute *attr,
-			       const char *buf, size_t size)
-{
-	u8 idx;
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
-		spin_unlock(&drvdata->spinlock);
-		return -EPERM;
-	}
-
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP;
-	drvdata->startstop_ctrl |= (1 << (idx + 16));
-	drvdata->enable_ctrl1 |= ETMTECR1_START_STOP;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_stop);
-
-static ssize_t addr_acctype_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->addr_acctype[drvdata->addr_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t addr_acctype_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->addr_acctype[drvdata->addr_idx] = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(addr_acctype);
-
-static ssize_t cntr_idx_show(struct device *dev,
-			     struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->cntr_idx;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t cntr_idx_store(struct device *dev,
-			      struct device_attribute *attr,
-			      const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val >= drvdata->nr_cntr)
-		return -EINVAL;
-	/*
-	 * Use spinlock to ensure index doesn't change while it gets
-	 * dereferenced multiple times within a spinlock block elsewhere.
-	 */
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_idx = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_idx);
-
-static ssize_t cntr_rld_val_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_rld_val[drvdata->cntr_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t cntr_rld_val_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_rld_val[drvdata->cntr_idx] = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_rld_val);
-
-static ssize_t cntr_event_show(struct device *dev,
-			       struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_event[drvdata->cntr_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t cntr_event_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_event);
-
-static ssize_t cntr_rld_event_show(struct device *dev,
-				   struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_rld_event[drvdata->cntr_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t cntr_rld_event_store(struct device *dev,
-				    struct device_attribute *attr,
-				    const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_rld_event);
-
-static ssize_t cntr_val_show(struct device *dev,
-			     struct device_attribute *attr, char *buf)
-{
-	int i, ret = 0;
-	u32 val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	if (!drvdata->enable) {
-		spin_lock(&drvdata->spinlock);
-		for (i = 0; i < drvdata->nr_cntr; i++)
-			ret += sprintf(buf, "counter %d: %x\n",
-				       i, drvdata->cntr_val[i]);
-		spin_unlock(&drvdata->spinlock);
-		return ret;
-	}
-
-	for (i = 0; i < drvdata->nr_cntr; i++) {
-		val = etm_readl(drvdata, ETMCNTVRn(i));
-		ret += sprintf(buf, "counter %d: %x\n", i, val);
-	}
-
-	return ret;
-}
-
-static ssize_t cntr_val_store(struct device *dev,
-			      struct device_attribute *attr,
-			      const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_val[drvdata->cntr_idx] = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(cntr_val);
-
-static ssize_t seq_12_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_12_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_12_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_12_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_12_event);
-
-static ssize_t seq_21_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_21_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_21_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_21_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_21_event);
-
-static ssize_t seq_23_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_23_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_23_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_23_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_23_event);
-
-static ssize_t seq_31_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_31_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_31_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_31_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_31_event);
-
-static ssize_t seq_32_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_32_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_32_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_32_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_32_event);
-
-static ssize_t seq_13_event_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->seq_13_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_13_event_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->seq_13_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(seq_13_event);
-
-static ssize_t seq_curr_state_show(struct device *dev,
-				   struct device_attribute *attr, char *buf)
-{
-	unsigned long val, flags;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	if (!drvdata->enable) {
-		val = drvdata->seq_curr_state;
-		goto out;
-	}
-
-	pm_runtime_get_sync(drvdata->dev);
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-
-	CS_UNLOCK(drvdata->base);
-	val = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
-	CS_LOCK(drvdata->base);
-
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(drvdata->dev);
-out:
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t seq_curr_state_store(struct device *dev,
-				    struct device_attribute *attr,
-				    const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val > ETM_SEQ_STATE_MAX_VAL)
-		return -EINVAL;
-
-	drvdata->seq_curr_state = val;
-
-	return size;
-}
-static DEVICE_ATTR_RW(seq_curr_state);
-
-static ssize_t ctxid_idx_show(struct device *dev,
-			      struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->ctxid_idx;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t ctxid_idx_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	if (val >= drvdata->nr_ctxid_cmp)
-		return -EINVAL;
-
-	/*
-	 * Use spinlock to ensure index doesn't change while it gets
-	 * dereferenced multiple times within a spinlock block elsewhere.
-	 */
-	spin_lock(&drvdata->spinlock);
-	drvdata->ctxid_idx = val;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(ctxid_idx);
-
-static ssize_t ctxid_pid_show(struct device *dev,
-			      struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	spin_lock(&drvdata->spinlock);
-	val = drvdata->ctxid_vpid[drvdata->ctxid_idx];
-	spin_unlock(&drvdata->spinlock);
-
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t ctxid_pid_store(struct device *dev,
-			       struct device_attribute *attr,
-			       const char *buf, size_t size)
-{
-	int ret;
-	unsigned long vpid, pid;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &vpid);
-	if (ret)
-		return ret;
-
-	pid = coresight_vpid_to_pid(vpid);
-
-	spin_lock(&drvdata->spinlock);
-	drvdata->ctxid_pid[drvdata->ctxid_idx] = pid;
-	drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid;
-	spin_unlock(&drvdata->spinlock);
-
-	return size;
-}
-static DEVICE_ATTR_RW(ctxid_pid);
-
-static ssize_t ctxid_mask_show(struct device *dev,
-			       struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->ctxid_mask;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t ctxid_mask_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->ctxid_mask = val;
-	return size;
-}
-static DEVICE_ATTR_RW(ctxid_mask);
-
-static ssize_t sync_freq_show(struct device *dev,
-			      struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->sync_freq;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t sync_freq_store(struct device *dev,
-			       struct device_attribute *attr,
-			       const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->sync_freq = val & ETM_SYNC_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(sync_freq);
-
-static ssize_t timestamp_event_show(struct device *dev,
-				    struct device_attribute *attr, char *buf)
-{
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->timestamp_event;
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t timestamp_event_store(struct device *dev,
-				     struct device_attribute *attr,
-				     const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->timestamp_event = val & ETM_EVENT_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(timestamp_event);
-
-static ssize_t cpu_show(struct device *dev,
-			struct device_attribute *attr, char *buf)
-{
-	int val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	val = drvdata->cpu;
-	return scnprintf(buf, PAGE_SIZE, "%d\n", val);
-
-}
-static DEVICE_ATTR_RO(cpu);
-
-static ssize_t traceid_show(struct device *dev,
-			    struct device_attribute *attr, char *buf)
-{
-	unsigned long val, flags;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	if (!drvdata->enable) {
-		val = drvdata->traceid;
-		goto out;
-	}
-
-	pm_runtime_get_sync(drvdata->dev);
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	CS_UNLOCK(drvdata->base);
-
-	val = (etm_readl(drvdata, ETMTRACEIDR) & ETM_TRACEID_MASK);
-
-	CS_LOCK(drvdata->base);
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	pm_runtime_put(drvdata->dev);
-out:
-	return sprintf(buf, "%#lx\n", val);
-}
-
-static ssize_t traceid_store(struct device *dev,
-			     struct device_attribute *attr,
-			     const char *buf, size_t size)
-{
-	int ret;
-	unsigned long val;
-	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
-
-	ret = kstrtoul(buf, 16, &val);
-	if (ret)
-		return ret;
-
-	drvdata->traceid = val & ETM_TRACEID_MASK;
-	return size;
-}
-static DEVICE_ATTR_RW(traceid);
-
-static struct attribute *coresight_etm_attrs[] = {
-	&dev_attr_nr_addr_cmp.attr,
-	&dev_attr_nr_cntr.attr,
-	&dev_attr_nr_ctxid_cmp.attr,
-	&dev_attr_etmsr.attr,
-	&dev_attr_reset.attr,
-	&dev_attr_mode.attr,
-	&dev_attr_trigger_event.attr,
-	&dev_attr_enable_event.attr,
-	&dev_attr_fifofull_level.attr,
-	&dev_attr_addr_idx.attr,
-	&dev_attr_addr_single.attr,
-	&dev_attr_addr_range.attr,
-	&dev_attr_addr_start.attr,
-	&dev_attr_addr_stop.attr,
-	&dev_attr_addr_acctype.attr,
-	&dev_attr_cntr_idx.attr,
-	&dev_attr_cntr_rld_val.attr,
-	&dev_attr_cntr_event.attr,
-	&dev_attr_cntr_rld_event.attr,
-	&dev_attr_cntr_val.attr,
-	&dev_attr_seq_12_event.attr,
-	&dev_attr_seq_21_event.attr,
-	&dev_attr_seq_23_event.attr,
-	&dev_attr_seq_31_event.attr,
-	&dev_attr_seq_32_event.attr,
-	&dev_attr_seq_13_event.attr,
-	&dev_attr_seq_curr_state.attr,
-	&dev_attr_ctxid_idx.attr,
-	&dev_attr_ctxid_pid.attr,
-	&dev_attr_ctxid_mask.attr,
-	&dev_attr_sync_freq.attr,
-	&dev_attr_timestamp_event.attr,
-	&dev_attr_traceid.attr,
-	&dev_attr_cpu.attr,
-	NULL,
-};
-
-#define coresight_simple_func(name, offset)                             \
-static ssize_t name##_show(struct device *_dev,                         \
-			   struct device_attribute *attr, char *buf)    \
-{                                                                       \
-	struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent);    \
-	return scnprintf(buf, PAGE_SIZE, "0x%x\n",                      \
-			 readl_relaxed(drvdata->base + offset));        \
-}                                                                       \
-DEVICE_ATTR_RO(name)
-
-coresight_simple_func(etmccr, ETMCCR);
-coresight_simple_func(etmccer, ETMCCER);
-coresight_simple_func(etmscr, ETMSCR);
-coresight_simple_func(etmidr, ETMIDR);
-coresight_simple_func(etmcr, ETMCR);
-coresight_simple_func(etmtraceidr, ETMTRACEIDR);
-coresight_simple_func(etmteevr, ETMTEEVR);
-coresight_simple_func(etmtssvr, ETMTSSCR);
-coresight_simple_func(etmtecr1, ETMTECR1);
-coresight_simple_func(etmtecr2, ETMTECR2);
-
-static struct attribute *coresight_etm_mgmt_attrs[] = {
-	&dev_attr_etmccr.attr,
-	&dev_attr_etmccer.attr,
-	&dev_attr_etmscr.attr,
-	&dev_attr_etmidr.attr,
-	&dev_attr_etmcr.attr,
-	&dev_attr_etmtraceidr.attr,
-	&dev_attr_etmteevr.attr,
-	&dev_attr_etmtssvr.attr,
-	&dev_attr_etmtecr1.attr,
-	&dev_attr_etmtecr2.attr,
-	NULL,
-};
-
-static const struct attribute_group coresight_etm_group = {
-	.attrs = coresight_etm_attrs,
-};
-
-
-static const struct attribute_group coresight_etm_mgmt_group = {
-	.attrs = coresight_etm_mgmt_attrs,
-	.name = "mgmt",
-};
-
-static const struct attribute_group *coresight_etm_groups[] = {
-	&coresight_etm_group,
-	&coresight_etm_mgmt_group,
-	NULL,
-};
-
 static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action,
 			    void *hcpu)
 {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 03/30] coresight: etm3x: unlocking tracers in default arch init
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 01/30] coresight: etm3x: moving etm_readl/writel to header file Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 02/30] coresight: etm3x: moving sysFS entries to dedicated file Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 04/30] coresight: etm3x: splitting struct etm_drvdata Mathieu Poirier
                   ` (26 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Calling function 'smp_call_function_single()' to unlock a
tracer and calling it right after to perform the default
initialisation doesn't make sense.

Moving 'etm_os_unlock()' just before making the default
initialisation results in the same outcome while saving
one call to 'smp_call_function_single()'.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 2a1950d0753d..737b6164f4a5 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -47,11 +47,11 @@ static struct etm_drvdata *etmdrvdata[NR_CPUS];
  * and OS lock must be unlocked before any memory mapped access on such
  * processors, otherwise memory mapped reads/writes will be invalid.
  */
-static void etm_os_unlock(void *info)
+static void etm_os_unlock(struct etm_drvdata *drvdata)
 {
-	struct etm_drvdata *drvdata = (struct etm_drvdata *)info;
 	/* Writing any value to ETMOSLAR unlocks the trace registers */
 	etm_writel(drvdata, 0x0, ETMOSLAR);
+	drvdata->os_unlock = true;
 	isb();
 }
 
@@ -478,6 +478,9 @@ static void etm_init_arch_data(void *info)
 	u32 etmccr;
 	struct etm_drvdata *drvdata = info;
 
+	/* Make sure all registers are accessible */
+	etm_os_unlock(drvdata);
+
 	CS_UNLOCK(drvdata->base);
 
 	/* First dummy read */
@@ -602,9 +605,6 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
 	get_online_cpus();
 	etmdrvdata[drvdata->cpu] = drvdata;
 
-	if (!smp_call_function_single(drvdata->cpu, etm_os_unlock, drvdata, 1))
-		drvdata->os_unlock = true;
-
 	if (smp_call_function_single(drvdata->cpu,
 				     etm_init_arch_data,  drvdata, 1))
 		dev_err(dev, "ETM arch init failed\n");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 04/30] coresight: etm3x: splitting struct etm_drvdata
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (2 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 03/30] coresight: etm3x: unlocking tracers in default arch init Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 05/30] coresight: etm3x: set progbit to stop trace collection Mathieu Poirier
                   ` (25 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Structure "etm_drvdata" carries the tracer's static configuration
(as laid out in hardware) and the session specific configuration
determined by users.  This doesn't work when tracers are to support
multiple, simultaneous, tracing session.

As such splitting "etm_drvdata" in two sections, one for the HW
specific data and another for user configuration.  The latter is
a pointer to a newly created "etm_config" structure.

That way several tracing session configuration can be created and
associated, when required, to a tracer.

Also taking care of up-lifting all the code affected by this new
arrangement.  No loss or gain of functionality (other than what is
mentioned above) is introduced by this patch.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h        | 102 ++---
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    | 460 +++++++++++++++------
 drivers/hwtracing/coresight/coresight-etm3x.c      | 176 +++++---
 3 files changed, 504 insertions(+), 234 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 9a30aa392ed9..9317510d555f 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -136,29 +136,9 @@
 #define ETM_DEFAULT_EVENT_VAL	(ETM_HARD_WIRE_RES_A	|	\
 				 ETM_ADD_COMP_0		|	\
 				 ETM_EVENT_NOT_A)
+
 /**
- * struct etm_drvdata - specifics associated to an ETM component
- * @base:	memory mapped base address for this component.
- * @dev:	the device entity associated to this component.
- * @atclk:	optional clock for the core parts of the ETM.
- * @csdev:	component vitals needed by the framework.
- * @spinlock:	only one at a time pls.
- * @cpu:	the cpu this component is affined to.
- * @port_size:	port size as reported by ETMCR bit 4-6 and 21.
- * @arch:	ETM/PTM version number.
- * @use_cpu14:	true if management registers need to be accessed via CP14.
- * @enable:	is this ETM/PTM currently tracing.
- * @sticky_enable: true if ETM base configuration has been done.
- * @boot_enable:true if we should start tracing at boot time.
- * @os_unlock:	true if access to management registers is allowed.
- * @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR.
- * @nr_cntr:	Number of counters as found in ETMCCR bit 13-15.
- * @nr_ext_inp:	Number of external input as found in ETMCCR bit 17-19.
- * @nr_ext_out:	Number of external output as found in ETMCCR bit 20-22.
- * @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25.
- * @etmccr:	value of register ETMCCR.
- * @etmccer:	value of register ETMCCER.
- * @traceid:	value of the current ID for this component.
+ * struct etm_config - configuration information related to an ETM
  * @mode:	controls various modes supported by this ETM/PTM.
  * @ctrl:	used in conjunction with @mode.
  * @trigger_event: setting for register ETMTRIGGER.
@@ -189,30 +169,9 @@
  * @ctxid_mask: mask applicable to all the context IDs.
  * @sync_freq:	Synchronisation frequency.
  * @timestamp_event: Defines an event that requests the insertion
-		     of a timestamp into the trace stream.
+ *		     of a timestamp into the trace stream.
  */
-struct etm_drvdata {
-	void __iomem			*base;
-	struct device			*dev;
-	struct clk			*atclk;
-	struct coresight_device		*csdev;
-	spinlock_t			spinlock;
-	int				cpu;
-	int				port_size;
-	u8				arch;
-	bool				use_cp14;
-	bool				enable;
-	bool				sticky_enable;
-	bool				boot_enable;
-	bool				os_unlock;
-	u8				nr_addr_cmp;
-	u8				nr_cntr;
-	u8				nr_ext_inp;
-	u8				nr_ext_out;
-	u8				nr_ctxid_cmp;
-	u32				etmccr;
-	u32				etmccer;
-	u32				traceid;
+struct etm_config {
 	u32				mode;
 	u32				ctrl;
 	u32				trigger_event;
@@ -244,6 +203,56 @@ struct etm_drvdata {
 	u32				timestamp_event;
 };
 
+/**
+ * struct etm_drvdata - specifics associated to an ETM component
+ * @base:	memory mapped base address for this component.
+ * @dev:	the device entity associated to this component.
+ * @atclk:	optional clock for the core parts of the ETM.
+ * @csdev:	component vitals needed by the framework.
+ * @spinlock:	only one at a time pls.
+ * @cpu:	the cpu this component is affined to.
+ * @port_size:	port size as reported by ETMCR bit 4-6 and 21.
+ * @arch:	ETM/PTM version number.
+ * @use_cpu14:	true if management registers need to be accessed via CP14.
+ * @enable:	is this ETM/PTM currently tracing.
+ * @sticky_enable: true if ETM base configuration has been done.
+ * @boot_enable:true if we should start tracing at boot time.
+ * @os_unlock:	true if access to management registers is allowed.
+ * @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR.
+ * @nr_cntr:	Number of counters as found in ETMCCR bit 13-15.
+ * @nr_ext_inp:	Number of external input as found in ETMCCR bit 17-19.
+ * @nr_ext_out:	Number of external output as found in ETMCCR bit 20-22.
+ * @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25.
+ * @etmccr:	value of register ETMCCR.
+ * @etmccer:	value of register ETMCCER.
+ * @traceid:	value of the current ID for this component.
+ * @config:	structure holding configuration parameters.
+ */
+struct etm_drvdata {
+	void __iomem			*base;
+	struct device			*dev;
+	struct clk			*atclk;
+	struct coresight_device		*csdev;
+	spinlock_t			spinlock;
+	int				cpu;
+	int				port_size;
+	u8				arch;
+	bool				use_cp14;
+	bool				enable;
+	bool				sticky_enable;
+	bool				boot_enable;
+	bool				os_unlock;
+	u8				nr_addr_cmp;
+	u8				nr_cntr;
+	u8				nr_ext_inp;
+	u8				nr_ext_out;
+	u8				nr_ctxid_cmp;
+	u32				etmccr;
+	u32				etmccer;
+	u32				traceid;
+	struct etm_config		*config;
+};
+
 enum etm_addr_type {
 	ETM_ADDR_TYPE_NONE,
 	ETM_ADDR_TYPE_SINGLE,
@@ -283,5 +292,6 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
 
 extern const struct attribute_group *coresight_etm_groups[];
 int etm_get_trace_id(struct etm_drvdata *drvdata);
-void etm_set_default(struct etm_drvdata *drvdata);
+void etm_set_default(struct etm_config *config);
+struct etm_config *get_etm_config(struct etm_drvdata *drvdata);
 #endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
index f409f5a88e95..410d29e2b90c 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -78,6 +78,10 @@ static ssize_t reset_store(struct device *dev,
 	int i, ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -85,19 +89,19 @@ static ssize_t reset_store(struct device *dev,
 
 	if (val) {
 		spin_lock(&drvdata->spinlock);
-		drvdata->mode = ETM_MODE_EXCLUDE;
-		drvdata->ctrl = 0x0;
-		drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL;
-		drvdata->startstop_ctrl = 0x0;
-		drvdata->addr_idx = 0x0;
+		config->mode = ETM_MODE_EXCLUDE;
+		config->ctrl = 0x0;
+		config->trigger_event = ETM_DEFAULT_EVENT_VAL;
+		config->startstop_ctrl = 0x0;
+		config->addr_idx = 0x0;
 		for (i = 0; i < drvdata->nr_addr_cmp; i++) {
-			drvdata->addr_val[i] = 0x0;
-			drvdata->addr_acctype[i] = 0x0;
-			drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE;
+			config->addr_val[i] = 0x0;
+			config->addr_acctype[i] = 0x0;
+			config->addr_type[i] = ETM_ADDR_TYPE_NONE;
 		}
-		drvdata->cntr_idx = 0x0;
+		config->cntr_idx = 0x0;
 
-		etm_set_default(drvdata);
+		etm_set_default(config);
 		spin_unlock(&drvdata->spinlock);
 	}
 
@@ -110,8 +114,12 @@ static ssize_t mode_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
-	val = drvdata->mode;
+	val = config->mode;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -122,48 +130,52 @@ static ssize_t mode_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->mode = val & ETM_MODE_ALL;
+	config->mode = val & ETM_MODE_ALL;
 
-	if (drvdata->mode & ETM_MODE_EXCLUDE)
-		drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC;
+	if (config->mode & ETM_MODE_EXCLUDE)
+		config->enable_ctrl1 |= ETMTECR1_INC_EXC;
 	else
-		drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC;
+		config->enable_ctrl1 &= ~ETMTECR1_INC_EXC;
 
-	if (drvdata->mode & ETM_MODE_CYCACC)
-		drvdata->ctrl |= ETMCR_CYC_ACC;
+	if (config->mode & ETM_MODE_CYCACC)
+		config->ctrl |= ETMCR_CYC_ACC;
 	else
-		drvdata->ctrl &= ~ETMCR_CYC_ACC;
+		config->ctrl &= ~ETMCR_CYC_ACC;
 
-	if (drvdata->mode & ETM_MODE_STALL) {
+	if (config->mode & ETM_MODE_STALL) {
 		if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) {
 			dev_warn(drvdata->dev, "stall mode not supported\n");
 			ret = -EINVAL;
 			goto err_unlock;
 		}
-		drvdata->ctrl |= ETMCR_STALL_MODE;
+		config->ctrl |= ETMCR_STALL_MODE;
 	 } else
-		drvdata->ctrl &= ~ETMCR_STALL_MODE;
+		config->ctrl &= ~ETMCR_STALL_MODE;
 
-	if (drvdata->mode & ETM_MODE_TIMESTAMP) {
+	if (config->mode & ETM_MODE_TIMESTAMP) {
 		if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) {
 			dev_warn(drvdata->dev, "timestamp not supported\n");
 			ret = -EINVAL;
 			goto err_unlock;
 		}
-		drvdata->ctrl |= ETMCR_TIMESTAMP_EN;
+		config->ctrl |= ETMCR_TIMESTAMP_EN;
 	} else
-		drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN;
+		config->ctrl &= ~ETMCR_TIMESTAMP_EN;
 
-	if (drvdata->mode & ETM_MODE_CTXID)
-		drvdata->ctrl |= ETMCR_CTXID_SIZE;
+	if (config->mode & ETM_MODE_CTXID)
+		config->ctrl |= ETMCR_CTXID_SIZE;
 	else
-		drvdata->ctrl &= ~ETMCR_CTXID_SIZE;
+		config->ctrl &= ~ETMCR_CTXID_SIZE;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -179,8 +191,12 @@ static ssize_t trigger_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
-	val = drvdata->trigger_event;
+	val = config->trigger_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -191,12 +207,16 @@ static ssize_t trigger_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->trigger_event = val & ETM_EVENT_MASK;
+	config->trigger_event = val & ETM_EVENT_MASK;
 
 	return size;
 }
@@ -207,8 +227,12 @@ static ssize_t enable_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
 
-	val = drvdata->enable_event;
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
+
+	val = config->enable_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -219,12 +243,16 @@ static ssize_t enable_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->enable_event = val & ETM_EVENT_MASK;
+	config->enable_event = val & ETM_EVENT_MASK;
 
 	return size;
 }
@@ -235,8 +263,12 @@ static ssize_t fifofull_level_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
 
-	val = drvdata->fifofull_level;
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
+
+	val = config->fifofull_level;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -247,12 +279,16 @@ static ssize_t fifofull_level_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->fifofull_level = val;
+	config->fifofull_level = val;
 
 	return size;
 }
@@ -263,8 +299,12 @@ static ssize_t addr_idx_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
 
-	val = drvdata->addr_idx;
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
+
+	val = config->addr_idx;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -275,6 +315,10 @@ static ssize_t addr_idx_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -288,7 +332,7 @@ static ssize_t addr_idx_store(struct device *dev,
 	 * dereferenced multiple times within a spinlock block elsewhere.
 	 */
 	spin_lock(&drvdata->spinlock);
-	drvdata->addr_idx = val;
+	config->addr_idx = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -301,16 +345,20 @@ static ssize_t addr_single_show(struct device *dev,
 	u8 idx;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EINVAL;
 	}
 
-	val = drvdata->addr_val[idx];
+	val = config->addr_val[idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -324,21 +372,25 @@ static ssize_t addr_single_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EINVAL;
 	}
 
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
+	config->addr_val[idx] = val;
+	config->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -351,23 +403,27 @@ static ssize_t addr_range_show(struct device *dev,
 	u8 idx;
 	unsigned long val1, val2;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
+	idx = config->addr_idx;
 	if (idx % 2 != 0) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
-	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
-	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+	if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	val1 = drvdata->addr_val[idx];
-	val2 = drvdata->addr_val[idx + 1];
+	val1 = config->addr_val[idx];
+	val2 = config->addr_val[idx + 1];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx %#lx\n", val1, val2);
@@ -380,6 +436,10 @@ static ssize_t addr_range_store(struct device *dev,
 	u8 idx;
 	unsigned long val1, val2;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	if (sscanf(buf, "%lx %lx", &val1, &val2) != 2)
 		return -EINVAL;
@@ -388,24 +448,24 @@ static ssize_t addr_range_store(struct device *dev,
 		return -EINVAL;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
+	idx = config->addr_idx;
 	if (idx % 2 != 0) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
-	if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
-	      (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
-	       drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
+	if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
+	       config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
+	      (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
+	       config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	drvdata->addr_val[idx] = val1;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE;
-	drvdata->addr_val[idx + 1] = val2;
-	drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
-	drvdata->enable_ctrl1 |= (1 << (idx/2));
+	config->addr_val[idx] = val1;
+	config->addr_type[idx] = ETM_ADDR_TYPE_RANGE;
+	config->addr_val[idx + 1] = val2;
+	config->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE;
+	config->enable_ctrl1 |= (1 << (idx/2));
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -418,16 +478,20 @@ static ssize_t addr_start_show(struct device *dev,
 	u8 idx;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_START)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	val = drvdata->addr_val[idx];
+	val = config->addr_val[idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -441,23 +505,27 @@ static ssize_t addr_start_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_START)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_START;
-	drvdata->startstop_ctrl |= (1 << idx);
-	drvdata->enable_ctrl1 |= BIT(25);
+	config->addr_val[idx] = val;
+	config->addr_type[idx] = ETM_ADDR_TYPE_START;
+	config->startstop_ctrl |= (1 << idx);
+	config->enable_ctrl1 |= BIT(25);
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -470,16 +538,20 @@ static ssize_t addr_stop_show(struct device *dev,
 	u8 idx;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	val = drvdata->addr_val[idx];
+	val = config->addr_val[idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -493,23 +565,27 @@ static ssize_t addr_stop_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	idx = drvdata->addr_idx;
-	if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
-	      drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
+	idx = config->addr_idx;
+	if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
+	      config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
 		spin_unlock(&drvdata->spinlock);
 		return -EPERM;
 	}
 
-	drvdata->addr_val[idx] = val;
-	drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP;
-	drvdata->startstop_ctrl |= (1 << (idx + 16));
-	drvdata->enable_ctrl1 |= ETMTECR1_START_STOP;
+	config->addr_val[idx] = val;
+	config->addr_type[idx] = ETM_ADDR_TYPE_STOP;
+	config->startstop_ctrl |= (1 << (idx + 16));
+	config->enable_ctrl1 |= ETMTECR1_START_STOP;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -521,9 +597,13 @@ static ssize_t addr_acctype_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->addr_acctype[drvdata->addr_idx];
+	val = config->addr_acctype[config->addr_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -536,13 +616,17 @@ static ssize_t addr_acctype_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->addr_acctype[drvdata->addr_idx] = val;
+	config->addr_acctype[config->addr_idx] = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -554,8 +638,12 @@ static ssize_t cntr_idx_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
 
-	val = drvdata->cntr_idx;
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
+
+	val = config->cntr_idx;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -566,6 +654,10 @@ static ssize_t cntr_idx_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -578,7 +670,7 @@ static ssize_t cntr_idx_store(struct device *dev,
 	 * dereferenced multiple times within a spinlock block elsewhere.
 	 */
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_idx = val;
+	config->cntr_idx = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -590,9 +682,13 @@ static ssize_t cntr_rld_val_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_rld_val[drvdata->cntr_idx];
+	val = config->cntr_rld_val[config->cntr_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -605,13 +701,17 @@ static ssize_t cntr_rld_val_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_rld_val[drvdata->cntr_idx] = val;
+	config->cntr_rld_val[config->cntr_idx] = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -623,9 +723,13 @@ static ssize_t cntr_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_event[drvdata->cntr_idx];
+	val = config->cntr_event[config->cntr_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -638,13 +742,17 @@ static ssize_t cntr_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
+	config->cntr_event[config->cntr_idx] = val & ETM_EVENT_MASK;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -656,9 +764,13 @@ static ssize_t cntr_rld_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->cntr_rld_event[drvdata->cntr_idx];
+	val = config->cntr_rld_event[config->cntr_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -671,13 +783,17 @@ static ssize_t cntr_rld_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK;
+	config->cntr_rld_event[config->cntr_idx] = val & ETM_EVENT_MASK;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -690,12 +806,16 @@ static ssize_t cntr_val_show(struct device *dev,
 	int i, ret = 0;
 	u32 val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	if (!drvdata->enable) {
 		spin_lock(&drvdata->spinlock);
 		for (i = 0; i < drvdata->nr_cntr; i++)
 			ret += sprintf(buf, "counter %d: %x\n",
-				       i, drvdata->cntr_val[i]);
+				       i, config->cntr_val[i]);
 		spin_unlock(&drvdata->spinlock);
 		return ret;
 	}
@@ -715,13 +835,17 @@ static ssize_t cntr_val_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->cntr_val[drvdata->cntr_idx] = val;
+	config->cntr_val[config->cntr_idx] = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -733,8 +857,12 @@ static ssize_t seq_12_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
 
-	val = drvdata->seq_12_event;
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
+
+	val = config->seq_12_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -745,12 +873,16 @@ static ssize_t seq_12_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_12_event = val & ETM_EVENT_MASK;
+	config->seq_12_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_12_event);
@@ -760,8 +892,12 @@ static ssize_t seq_21_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
 
-	val = drvdata->seq_21_event;
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
+
+	val = config->seq_21_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -772,12 +908,16 @@ static ssize_t seq_21_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_21_event = val & ETM_EVENT_MASK;
+	config->seq_21_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_21_event);
@@ -787,8 +927,12 @@ static ssize_t seq_23_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
-	val = drvdata->seq_23_event;
+	val = config->seq_23_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -799,12 +943,16 @@ static ssize_t seq_23_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_23_event = val & ETM_EVENT_MASK;
+	config->seq_23_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_23_event);
@@ -814,8 +962,12 @@ static ssize_t seq_31_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
-	val = drvdata->seq_31_event;
+	val = config->seq_31_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -826,12 +978,16 @@ static ssize_t seq_31_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_31_event = val & ETM_EVENT_MASK;
+	config->seq_31_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_31_event);
@@ -841,8 +997,12 @@ static ssize_t seq_32_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
-	val = drvdata->seq_32_event;
+	val = config->seq_32_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -853,12 +1013,16 @@ static ssize_t seq_32_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_32_event = val & ETM_EVENT_MASK;
+	config->seq_32_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_32_event);
@@ -868,8 +1032,12 @@ static ssize_t seq_13_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
 
-	val = drvdata->seq_13_event;
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
+
+	val = config->seq_13_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -880,12 +1048,16 @@ static ssize_t seq_13_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->seq_13_event = val & ETM_EVENT_MASK;
+	config->seq_13_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(seq_13_event);
@@ -895,9 +1067,13 @@ static ssize_t seq_curr_state_show(struct device *dev,
 {
 	unsigned long val, flags;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	if (!drvdata->enable) {
-		val = drvdata->seq_curr_state;
+		val = config->seq_curr_state;
 		goto out;
 	}
 
@@ -921,6 +1097,10 @@ static ssize_t seq_curr_state_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -929,7 +1109,7 @@ static ssize_t seq_curr_state_store(struct device *dev,
 	if (val > ETM_SEQ_STATE_MAX_VAL)
 		return -EINVAL;
 
-	drvdata->seq_curr_state = val;
+	config->seq_curr_state = val;
 
 	return size;
 }
@@ -940,8 +1120,12 @@ static ssize_t ctxid_idx_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
 
-	val = drvdata->ctxid_idx;
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
+
+	val = config->ctxid_idx;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -952,6 +1136,10 @@ static ssize_t ctxid_idx_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
@@ -965,7 +1153,7 @@ static ssize_t ctxid_idx_store(struct device *dev,
 	 * dereferenced multiple times within a spinlock block elsewhere.
 	 */
 	spin_lock(&drvdata->spinlock);
-	drvdata->ctxid_idx = val;
+	config->ctxid_idx = val;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -977,9 +1165,13 @@ static ssize_t ctxid_pid_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	spin_lock(&drvdata->spinlock);
-	val = drvdata->ctxid_vpid[drvdata->ctxid_idx];
+	val = config->ctxid_vpid[config->ctxid_idx];
 	spin_unlock(&drvdata->spinlock);
 
 	return sprintf(buf, "%#lx\n", val);
@@ -992,6 +1184,10 @@ static ssize_t ctxid_pid_store(struct device *dev,
 	int ret;
 	unsigned long vpid, pid;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &vpid);
 	if (ret)
@@ -1000,8 +1196,8 @@ static ssize_t ctxid_pid_store(struct device *dev,
 	pid = coresight_vpid_to_pid(vpid);
 
 	spin_lock(&drvdata->spinlock);
-	drvdata->ctxid_pid[drvdata->ctxid_idx] = pid;
-	drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid;
+	config->ctxid_pid[config->ctxid_idx] = pid;
+	config->ctxid_vpid[config->ctxid_idx] = vpid;
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
@@ -1013,8 +1209,12 @@ static ssize_t ctxid_mask_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
-	val = drvdata->ctxid_mask;
+	val = config->ctxid_mask;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -1025,12 +1225,16 @@ static ssize_t ctxid_mask_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->ctxid_mask = val;
+	config->ctxid_mask = val;
 	return size;
 }
 static DEVICE_ATTR_RW(ctxid_mask);
@@ -1040,8 +1244,12 @@ static ssize_t sync_freq_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
-	val = drvdata->sync_freq;
+	val = config->sync_freq;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -1052,12 +1260,16 @@ static ssize_t sync_freq_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->sync_freq = val & ETM_SYNC_MASK;
+	config->sync_freq = val & ETM_SYNC_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(sync_freq);
@@ -1067,8 +1279,12 @@ static ssize_t timestamp_event_show(struct device *dev,
 {
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
-	val = drvdata->timestamp_event;
+	val = config->timestamp_event;
 	return sprintf(buf, "%#lx\n", val);
 }
 
@@ -1079,12 +1295,16 @@ static ssize_t timestamp_event_store(struct device *dev,
 	int ret;
 	unsigned long val;
 	struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return -EINVAL;
 
 	ret = kstrtoul(buf, 16, &val);
 	if (ret)
 		return ret;
 
-	drvdata->timestamp_event = val & ETM_EVENT_MASK;
+	config->timestamp_event = val & ETM_EVENT_MASK;
 	return size;
 }
 static DEVICE_ATTR_RW(timestamp_event);
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 737b6164f4a5..966955f5b0f9 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -41,6 +41,7 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
 /* The number of ETM/PTM currently registered */
 static int etm_count;
 static struct etm_drvdata *etmdrvdata[NR_CPUS];
+static void etm_init_default_data(struct etm_config *config);
 
 /*
  * Memory mapped writes to clear os lock are not supported on some processors
@@ -186,36 +187,57 @@ static void etm_clr_prog(struct etm_drvdata *drvdata)
 	}
 }
 
-void etm_set_default(struct etm_drvdata *drvdata)
+struct etm_config *get_etm_config(struct etm_drvdata *drvdata)
 {
-	int i;
+	struct  etm_config *config;
 
-	drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->enable_event = ETM_HARD_WIRE_RES_A;
+	if (drvdata->config)
+		goto out;
 
-	drvdata->seq_12_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_21_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_23_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_31_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_32_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->seq_13_event = ETM_DEFAULT_EVENT_VAL;
-	drvdata->timestamp_event = ETM_DEFAULT_EVENT_VAL;
+	config = kzalloc(sizeof(struct etm_config), GFP_KERNEL);
+	if (!config)
+		return NULL;
 
-	for (i = 0; i < drvdata->nr_cntr; i++) {
-		drvdata->cntr_rld_val[i] = 0x0;
-		drvdata->cntr_event[i] = ETM_DEFAULT_EVENT_VAL;
-		drvdata->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL;
-		drvdata->cntr_val[i] = 0x0;
+	/* Set default config */
+	etm_init_default_data(config);
+	drvdata->config = config;
+out:
+	return drvdata->config;
+}
+
+void etm_set_default(struct etm_config *config)
+{
+	int i;
+
+	if (WARN_ON_ONCE(!config))
+		return;
+
+	config->trigger_event = ETM_DEFAULT_EVENT_VAL;
+	config->enable_event = ETM_HARD_WIRE_RES_A;
+
+	config->seq_12_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_21_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_23_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_31_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_32_event = ETM_DEFAULT_EVENT_VAL;
+	config->seq_13_event = ETM_DEFAULT_EVENT_VAL;
+	config->timestamp_event = ETM_DEFAULT_EVENT_VAL;
+
+	for (i = 0; i < ETM_MAX_CNTR; i++) {
+		config->cntr_rld_val[i] = 0x0;
+		config->cntr_event[i] = ETM_DEFAULT_EVENT_VAL;
+		config->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL;
+		config->cntr_val[i] = 0x0;
 	}
 
-	drvdata->seq_curr_state = 0x0;
-	drvdata->ctxid_idx = 0x0;
-	for (i = 0; i < drvdata->nr_ctxid_cmp; i++) {
-		drvdata->ctxid_pid[i] = 0x0;
-		drvdata->ctxid_vpid[i] = 0x0;
+	config->seq_curr_state = 0x0;
+	config->ctxid_idx = 0x0;
+	for (i = 0; i < ETM_MAX_CTXID_CMP; i++) {
+		config->ctxid_pid[i] = 0x0;
+		config->ctxid_vpid[i] = 0x0;
 	}
 
-	drvdata->ctxid_mask = 0x0;
+	config->ctxid_mask = 0x0;
 }
 
 static void etm_enable_hw(void *info)
@@ -223,6 +245,10 @@ static void etm_enable_hw(void *info)
 	int i;
 	u32 etmcr;
 	struct etm_drvdata *drvdata = info;
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return;
 
 	CS_UNLOCK(drvdata->base);
 
@@ -238,39 +264,39 @@ static void etm_enable_hw(void *info)
 	etmcr = etm_readl(drvdata, ETMCR);
 	etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG);
 	etmcr |= drvdata->port_size;
-	etm_writel(drvdata, drvdata->ctrl | etmcr, ETMCR);
-	etm_writel(drvdata, drvdata->trigger_event, ETMTRIGGER);
-	etm_writel(drvdata, drvdata->startstop_ctrl, ETMTSSCR);
-	etm_writel(drvdata, drvdata->enable_event, ETMTEEVR);
-	etm_writel(drvdata, drvdata->enable_ctrl1, ETMTECR1);
-	etm_writel(drvdata, drvdata->fifofull_level, ETMFFLR);
+	etm_writel(drvdata, config->ctrl | etmcr, ETMCR);
+	etm_writel(drvdata, config->trigger_event, ETMTRIGGER);
+	etm_writel(drvdata, config->startstop_ctrl, ETMTSSCR);
+	etm_writel(drvdata, config->enable_event, ETMTEEVR);
+	etm_writel(drvdata, config->enable_ctrl1, ETMTECR1);
+	etm_writel(drvdata, config->fifofull_level, ETMFFLR);
 	for (i = 0; i < drvdata->nr_addr_cmp; i++) {
-		etm_writel(drvdata, drvdata->addr_val[i], ETMACVRn(i));
-		etm_writel(drvdata, drvdata->addr_acctype[i], ETMACTRn(i));
+		etm_writel(drvdata, config->addr_val[i], ETMACVRn(i));
+		etm_writel(drvdata, config->addr_acctype[i], ETMACTRn(i));
 	}
 	for (i = 0; i < drvdata->nr_cntr; i++) {
-		etm_writel(drvdata, drvdata->cntr_rld_val[i], ETMCNTRLDVRn(i));
-		etm_writel(drvdata, drvdata->cntr_event[i], ETMCNTENRn(i));
-		etm_writel(drvdata, drvdata->cntr_rld_event[i],
+		etm_writel(drvdata, config->cntr_rld_val[i], ETMCNTRLDVRn(i));
+		etm_writel(drvdata, config->cntr_event[i], ETMCNTENRn(i));
+		etm_writel(drvdata, config->cntr_rld_event[i],
 			   ETMCNTRLDEVRn(i));
-		etm_writel(drvdata, drvdata->cntr_val[i], ETMCNTVRn(i));
+		etm_writel(drvdata, config->cntr_val[i], ETMCNTVRn(i));
 	}
-	etm_writel(drvdata, drvdata->seq_12_event, ETMSQ12EVR);
-	etm_writel(drvdata, drvdata->seq_21_event, ETMSQ21EVR);
-	etm_writel(drvdata, drvdata->seq_23_event, ETMSQ23EVR);
-	etm_writel(drvdata, drvdata->seq_31_event, ETMSQ31EVR);
-	etm_writel(drvdata, drvdata->seq_32_event, ETMSQ32EVR);
-	etm_writel(drvdata, drvdata->seq_13_event, ETMSQ13EVR);
-	etm_writel(drvdata, drvdata->seq_curr_state, ETMSQR);
+	etm_writel(drvdata, config->seq_12_event, ETMSQ12EVR);
+	etm_writel(drvdata, config->seq_21_event, ETMSQ21EVR);
+	etm_writel(drvdata, config->seq_23_event, ETMSQ23EVR);
+	etm_writel(drvdata, config->seq_31_event, ETMSQ31EVR);
+	etm_writel(drvdata, config->seq_32_event, ETMSQ32EVR);
+	etm_writel(drvdata, config->seq_13_event, ETMSQ13EVR);
+	etm_writel(drvdata, config->seq_curr_state, ETMSQR);
 	for (i = 0; i < drvdata->nr_ext_out; i++)
 		etm_writel(drvdata, ETM_DEFAULT_EVENT_VAL, ETMEXTOUTEVRn(i));
 	for (i = 0; i < drvdata->nr_ctxid_cmp; i++)
-		etm_writel(drvdata, drvdata->ctxid_pid[i], ETMCIDCVRn(i));
-	etm_writel(drvdata, drvdata->ctxid_mask, ETMCIDCMR);
-	etm_writel(drvdata, drvdata->sync_freq, ETMSYNCFR);
+		etm_writel(drvdata, config->ctxid_pid[i], ETMCIDCVRn(i));
+	etm_writel(drvdata, config->ctxid_mask, ETMCIDCMR);
+	etm_writel(drvdata, config->sync_freq, ETMSYNCFR);
 	/* No external input selected */
 	etm_writel(drvdata, 0x0, ETMEXTINSELR);
-	etm_writel(drvdata, drvdata->timestamp_event, ETMTSEVR);
+	etm_writel(drvdata, config->timestamp_event, ETMTSEVR);
 	/* No auxiliary control selected */
 	etm_writel(drvdata, 0x0, ETMAUXCR);
 	etm_writel(drvdata, drvdata->traceid, ETMTRACEIDR);
@@ -278,7 +304,7 @@ static void etm_enable_hw(void *info)
 	etm_writel(drvdata, 0x0, ETMVMIDCVR);
 
 	/* Ensures trace output is enabled from this ETM */
-	etm_writel(drvdata, drvdata->ctrl | ETMCR_ETM_EN | etmcr, ETMCR);
+	etm_writel(drvdata, config->ctrl | ETMCR_ETM_EN | etmcr, ETMCR);
 
 	etm_clr_prog(drvdata);
 	CS_LOCK(drvdata->base);
@@ -357,6 +383,10 @@ static void etm_disable_hw(void *info)
 {
 	int i;
 	struct etm_drvdata *drvdata = info;
+	struct etm_config *config = get_etm_config(drvdata);
+
+	if (WARN_ON_ONCE(!config))
+		return;
 
 	CS_UNLOCK(drvdata->base);
 	etm_set_prog(drvdata);
@@ -365,10 +395,14 @@ static void etm_disable_hw(void *info)
 	etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR);
 
 	/* Read back sequencer and counters for post trace analysis */
-	drvdata->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
+	config->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
 
 	for (i = 0; i < drvdata->nr_cntr; i++)
-		drvdata->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i));
+		config->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i));
+
+	kfree(drvdata->config);
+	/* Get ready for another session */
+	drvdata->config = NULL;
 
 	etm_set_pwrdwn(drvdata);
 	CS_LOCK(drvdata->base);
@@ -517,14 +551,8 @@ static void etm_init_arch_data(void *info)
 	CS_LOCK(drvdata->base);
 }
 
-static void etm_init_default_data(struct etm_drvdata *drvdata)
+static void etm_init_default_data(struct etm_config *config)
 {
-	/*
-	 * A trace ID of value 0 is invalid, so let's start at some
-	 * random value that fits in 7 bits and will be just as good.
-	 */
-	static int etm3x_traceid = 0x10;
-
 	u32 flags = (1 << 0 | /* instruction execute*/
 		     3 << 3 | /* ARM instruction */
 		     0 << 5 | /* No data value comparison */
@@ -532,6 +560,29 @@ static void etm_init_default_data(struct etm_drvdata *drvdata)
 		     0 << 8 | /* Ignore context ID */
 		     0 << 10); /* Security ignored */
 
+	if (WARN_ON_ONCE(!config))
+		return;
+
+	config->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN);
+	config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1;
+	config->addr_val[0] = (u32) _stext;
+	config->addr_val[1] = (u32) _etext;
+	config->addr_acctype[0] = flags;
+	config->addr_acctype[1] = flags;
+	config->addr_type[0] = ETM_ADDR_TYPE_RANGE;
+	config->addr_type[1] = ETM_ADDR_TYPE_RANGE;
+
+	etm_set_default(config);
+}
+
+static void etm_init_trace_id(struct etm_drvdata *drvdata)
+{
+	/*
+	 * A trace ID of value 0 is invalid, so let's start at some
+	 * random value that fits in 7 bits and will be just as good.
+	 */
+	static int etm3x_traceid = 0x10;
+
 	/*
 	 * Initial configuration only - guarantees sources handled by
 	 * this driver have a unique ID at startup time but not between
@@ -539,18 +590,6 @@ static void etm_init_default_data(struct etm_drvdata *drvdata)
 	 * framework.
 	 */
 	drvdata->traceid = etm3x_traceid++;
-	drvdata->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN);
-	drvdata->enable_ctrl1 = ETMTECR1_ADDR_COMP_1;
-	if (drvdata->nr_addr_cmp >= 2) {
-		drvdata->addr_val[0] = (u32) _stext;
-		drvdata->addr_val[1] = (u32) _etext;
-		drvdata->addr_acctype[0] = flags;
-		drvdata->addr_acctype[1] = flags;
-		drvdata->addr_type[0] = ETM_ADDR_TYPE_RANGE;
-		drvdata->addr_type[1] = ETM_ADDR_TYPE_RANGE;
-	}
-
-	etm_set_default(drvdata);
 }
 
 static int etm_probe(struct amba_device *adev, const struct amba_id *id)
@@ -618,7 +657,8 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
 		ret = -EINVAL;
 		goto err_arch_supported;
 	}
-	etm_init_default_data(drvdata);
+
+	etm_init_trace_id(drvdata);
 
 	desc->type = CORESIGHT_DEV_TYPE_SOURCE;
 	desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 05/30] coresight: etm3x: set progbit to stop trace collection
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (3 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 04/30] coresight: etm3x: splitting struct etm_drvdata Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 06/30] coresight: clearly labeling source operarions Mathieu Poirier
                   ` (24 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

There is no need to use the event enable's "always false" event to
stop trace collection.  For that purpose setting the programming bit
(ETMCR:10) is enough.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 966955f5b0f9..713ffe1b761f 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -391,9 +391,6 @@ static void etm_disable_hw(void *info)
 	CS_UNLOCK(drvdata->base);
 	etm_set_prog(drvdata);
 
-	/* Program trace enable to low by using always false event */
-	etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR);
-
 	/* Read back sequencer and counters for post trace analysis */
 	config->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK);
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 06/30] coresight: clearly labeling source operarions
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (4 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 05/30] coresight: etm3x: set progbit to stop trace collection Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 07/30] coresight: etm3x: moving etm_drvdata::enable to atomic field Mathieu Poirier
                   ` (23 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

When integrating coresight with Perf, it is important to
clearly identify which operations are used by sysFS and
which ones by Perf.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c |  8 ++++----
 drivers/hwtracing/coresight/coresight-etm4x.c |  8 ++++----
 drivers/hwtracing/coresight/coresight.c       |  8 ++++----
 include/linux/coresight.h                     | 12 ++++++------
 4 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 713ffe1b761f..3d7fa0b2acf9 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -346,7 +346,7 @@ static int etm_trace_id(struct coresight_device *csdev)
 	return etm_get_trace_id(drvdata);
 }
 
-static int etm_enable(struct coresight_device *csdev)
+static int sysfs_etm_enable(struct coresight_device *csdev)
 {
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	int ret;
@@ -407,7 +407,7 @@ static void etm_disable_hw(void *info)
 	dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
 }
 
-static void etm_disable(struct coresight_device *csdev)
+static void sysfs_etm_disable(struct coresight_device *csdev)
 {
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
@@ -436,8 +436,8 @@ static void etm_disable(struct coresight_device *csdev)
 
 static const struct coresight_ops_source etm_source_ops = {
 	.trace_id	= etm_trace_id,
-	.enable		= etm_enable,
-	.disable	= etm_disable,
+	.sysfs_enable	= sysfs_etm_enable,
+	.sysfs_disable	= sysfs_etm_disable,
 };
 
 static const struct coresight_ops etm_cs_ops = {
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 254a81a4e6f4..0808cc5a51a0 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -180,7 +180,7 @@ static void etm4_enable_hw(void *info)
 	dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
 }
 
-static int etm4_enable(struct coresight_device *csdev)
+static int sysfs_etm4_enable(struct coresight_device *csdev)
 {
 	struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 	int ret;
@@ -231,7 +231,7 @@ static void etm4_disable_hw(void *info)
 	dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
 }
 
-static void etm4_disable(struct coresight_device *csdev)
+static void sysfs_etm4_disable(struct coresight_device *csdev)
 {
 	struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
@@ -261,8 +261,8 @@ static void etm4_disable(struct coresight_device *csdev)
 
 static const struct coresight_ops_source etm4_source_ops = {
 	.trace_id	= etm4_trace_id,
-	.enable		= etm4_enable,
-	.disable	= etm4_disable,
+	.sysfs_enable	= sysfs_etm4_enable,
+	.sysfs_disable	= sysfs_etm4_disable,
 };
 
 static const struct coresight_ops etm4_cs_ops = {
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index a3dcafb81700..d318a338a517 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -212,8 +212,8 @@ static int coresight_enable_source(struct coresight_device *csdev)
 	}
 
 	if (!csdev->enable) {
-		if (source_ops(csdev)->enable) {
-			ret = source_ops(csdev)->enable(csdev);
+		if (source_ops(csdev)->sysfs_enable) {
+			ret = source_ops(csdev)->sysfs_enable(csdev);
 			if (ret)
 				return ret;
 		}
@@ -228,8 +228,8 @@ static int coresight_enable_source(struct coresight_device *csdev)
 static void coresight_disable_source(struct coresight_device *csdev)
 {
 	if (atomic_dec_return(csdev->refcnt) == 0) {
-		if (source_ops(csdev)->disable) {
-			source_ops(csdev)->disable(csdev);
+		if (source_ops(csdev)->sysfs_disable) {
+			source_ops(csdev)->sysfs_disable(csdev);
 			csdev->enable = false;
 		}
 	}
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index a7cabfa23b55..97155527dbdd 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -205,15 +205,15 @@ struct coresight_ops_link {
 /**
  * struct coresight_ops_source - basic operations for a source
  * Operations available for sources.
- * @trace_id:	returns the value of the component's trace ID as known
-		to the HW.
- * @enable:	enables tracing for a source.
- * @disable:	disables tracing for a source.
+ * @trace_id:		returns the value of the component's trace ID as known
+			to the HW.
+ * @sysfs_enable:	enables tracing for a source, from sysFS.
+ * @sysfs_disable:	disables tracing for a source, from sysFS.
  */
 struct coresight_ops_source {
 	int (*trace_id)(struct coresight_device *csdev);
-	int (*enable)(struct coresight_device *csdev);
-	void (*disable)(struct coresight_device *csdev);
+	int (*sysfs_enable)(struct coresight_device *csdev);
+	void (*sysfs_disable)(struct coresight_device *csdev);
 };
 
 struct coresight_ops {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 07/30] coresight: etm3x: moving etm_drvdata::enable to atomic field
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (5 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 06/30] coresight: clearly labeling source operarions Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 08/30] coresight: etm3x: implementing 'cpu_id()' API Mathieu Poirier
                   ` (22 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Moving etm_drvdata::enable to an atomic type that gives the
'state' of the tracer, i.e disabled, handled via sysFS or Perf.
That way a tracer can't be used if it is already marshaled by
another subsystem.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h         | 11 +++++++++--
 drivers/hwtracing/coresight/coresight-etm3x-sysfs.c |  4 ++--
 drivers/hwtracing/coresight/coresight-etm3x.c       | 21 +++++++++++++++------
 3 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 9317510d555f..0996537ff142 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -13,6 +13,7 @@
 #ifndef _CORESIGHT_CORESIGHT_ETM_H
 #define _CORESIGHT_CORESIGHT_ETM_H
 
+#include <asm/local.h>
 #include <linux/spinlock.h>
 #include "coresight-priv.h"
 
@@ -214,7 +215,7 @@ struct etm_config {
  * @port_size:	port size as reported by ETMCR bit 4-6 and 21.
  * @arch:	ETM/PTM version number.
  * @use_cpu14:	true if management registers need to be accessed via CP14.
- * @enable:	is this ETM/PTM currently tracing.
+ * @state:	this tracer's state, i.e sysFS, Perf or disabled.
  * @sticky_enable: true if ETM base configuration has been done.
  * @boot_enable:true if we should start tracing at boot time.
  * @os_unlock:	true if access to management registers is allowed.
@@ -238,7 +239,7 @@ struct etm_drvdata {
 	int				port_size;
 	u8				arch;
 	bool				use_cp14;
-	bool				enable;
+	local_t				state;
 	bool				sticky_enable;
 	bool				boot_enable;
 	bool				os_unlock;
@@ -253,6 +254,12 @@ struct etm_drvdata {
 	struct etm_config		*config;
 };
 
+enum etm_state {
+	ETM_STATE_DISABLED,
+	ETM_STATE_SYSFS,
+	ETM_STATE_PERF,
+};
+
 enum etm_addr_type {
 	ETM_ADDR_TYPE_NONE,
 	ETM_ADDR_TYPE_SINGLE,
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
index 410d29e2b90c..bc8f04214567 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -811,7 +811,7 @@ static ssize_t cntr_val_show(struct device *dev,
 	if (WARN_ON_ONCE(!config))
 		return -EINVAL;
 
-	if (!drvdata->enable) {
+	if (!local_read(&drvdata->state)) {
 		spin_lock(&drvdata->spinlock);
 		for (i = 0; i < drvdata->nr_cntr; i++)
 			ret += sprintf(buf, "counter %d: %x\n",
@@ -1072,7 +1072,7 @@ static ssize_t seq_curr_state_show(struct device *dev,
 	if (WARN_ON_ONCE(!config))
 		return -EINVAL;
 
-	if (!drvdata->enable) {
+	if (!local_read(&drvdata->state)) {
 		val = config->seq_curr_state;
 		goto out;
 	}
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 3d7fa0b2acf9..3fe6433764d4 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -320,7 +320,7 @@ int etm_get_trace_id(struct etm_drvdata *drvdata)
 	if (!drvdata)
 		goto out;
 
-	if (!drvdata->enable)
+	if (!local_read(&drvdata->state))
 		return drvdata->traceid;
 
 	pm_runtime_get_sync(drvdata->dev);
@@ -354,6 +354,12 @@ static int sysfs_etm_enable(struct coresight_device *csdev)
 	pm_runtime_get_sync(csdev->dev.parent);
 	spin_lock(&drvdata->spinlock);
 
+	if (local_cmpxchg(&drvdata->state,
+			  ETM_STATE_DISABLED, ETM_STATE_SYSFS)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	/*
 	 * Configure the ETM only if the CPU is online.  If it isn't online
 	 * hw configuration will take place when 'CPU_STARTING' is received
@@ -366,17 +372,20 @@ static int sysfs_etm_enable(struct coresight_device *csdev)
 			goto err;
 	}
 
-	drvdata->enable = true;
 	drvdata->sticky_enable = true;
 
 	spin_unlock(&drvdata->spinlock);
 
 	dev_info(drvdata->dev, "ETM tracing enabled\n");
 	return 0;
-err:
+out:
 	spin_unlock(&drvdata->spinlock);
 	pm_runtime_put(csdev->dev.parent);
 	return ret;
+
+err:
+	local_set(&drvdata->state, ETM_STATE_DISABLED);
+	goto out;
 }
 
 static void etm_disable_hw(void *info)
@@ -425,7 +434,7 @@ static void sysfs_etm_disable(struct coresight_device *csdev)
 	 * ensures that register writes occur when cpu is powered.
 	 */
 	smp_call_function_single(drvdata->cpu, etm_disable_hw, drvdata, 1);
-	drvdata->enable = false;
+	local_set(&drvdata->state, ETM_STATE_DISABLED);
 
 	spin_unlock(&drvdata->spinlock);
 	put_online_cpus();
@@ -460,7 +469,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action,
 			etmdrvdata[cpu]->os_unlock = true;
 		}
 
-		if (etmdrvdata[cpu]->enable)
+		if (local_read(&etmdrvdata[cpu]->state))
 			etm_enable_hw(etmdrvdata[cpu]);
 		spin_unlock(&etmdrvdata[cpu]->spinlock);
 		break;
@@ -473,7 +482,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action,
 
 	case CPU_DYING:
 		spin_lock(&etmdrvdata[cpu]->spinlock);
-		if (etmdrvdata[cpu]->enable)
+		if (local_read(&etmdrvdata[cpu]->state))
 			etm_disable_hw(etmdrvdata[cpu]);
 		spin_unlock(&etmdrvdata[cpu]->spinlock);
 		break;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 08/30] coresight: etm3x: implementing 'cpu_id()' API
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (6 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 07/30] coresight: etm3x: moving etm_drvdata::enable to atomic field Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 09/30] coresight: etm3x: changing default trace configuration Mathieu Poirier
                   ` (21 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Adding an interface to lookup the CPU a tracer has been affined
to along with a source operation allowing external customers to
access it.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 8 ++++++++
 include/linux/coresight.h                     | 3 +++
 2 files changed, 11 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 3fe6433764d4..e199746bba05 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -312,6 +312,13 @@ static void etm_enable_hw(void *info)
 	dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
 }
 
+static int etm_cpu_id(struct coresight_device *csdev)
+{
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	return drvdata->cpu;
+}
+
 int etm_get_trace_id(struct etm_drvdata *drvdata)
 {
 	unsigned long flags;
@@ -444,6 +451,7 @@ static void sysfs_etm_disable(struct coresight_device *csdev)
 }
 
 static const struct coresight_ops_source etm_source_ops = {
+	.cpu_id		= etm_cpu_id,
 	.trace_id	= etm_trace_id,
 	.sysfs_enable	= sysfs_etm_enable,
 	.sysfs_disable	= sysfs_etm_disable,
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 97155527dbdd..f9210df15f03 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -205,12 +205,15 @@ struct coresight_ops_link {
 /**
  * struct coresight_ops_source - basic operations for a source
  * Operations available for sources.
+ * @cpu_id:		returns the value of the CPU number this component
+ *			is associated to.
  * @trace_id:		returns the value of the component's trace ID as known
 			to the HW.
  * @sysfs_enable:	enables tracing for a source, from sysFS.
  * @sysfs_disable:	disables tracing for a source, from sysFS.
  */
 struct coresight_ops_source {
+	int (*cpu_id)(struct coresight_device *csdev);
 	int (*trace_id)(struct coresight_device *csdev);
 	int (*sysfs_enable)(struct coresight_device *csdev);
 	void (*sysfs_disable)(struct coresight_device *csdev);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 09/30] coresight: etm3x: changing default trace configuration
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (7 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 08/30] coresight: etm3x: implementing 'cpu_id()' API Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 10/30] coresight: etm3x: consolidating initial config Mathieu Poirier
                   ` (20 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Changing default configuration to include the entire address
range rather than just the kernel.  That way traces are more
inclusive and it is easier to narrow down if needed.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h   |  2 ++
 drivers/hwtracing/coresight/coresight-etm3x.c | 29 ++++++++++++---------------
 2 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 0996537ff142..f33aeb4e545d 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -146,6 +146,7 @@
  * @startstop_ctrl: setting for register ETMTSSCR.
  * @enable_event: setting for register ETMTEEVR.
  * @enable_ctrl1: setting for register ETMTECR1.
+ * @enable_ctrl2: setting for register ETMTECR2.
  * @fifofull_level: setting for register ETMFFLR.
  * @addr_idx:	index for the address comparator selection.
  * @addr_val:	value for address comparator register.
@@ -179,6 +180,7 @@ struct etm_config {
 	u32				startstop_ctrl;
 	u32				enable_event;
 	u32				enable_ctrl1;
+	u32				enable_ctrl2;
 	u32				fifofull_level;
 	u8				addr_idx;
 	u32				addr_val[ETM_MAX_ADDR_CMP];
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index e199746bba05..8bd161584f85 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -567,26 +567,23 @@ static void etm_init_arch_data(void *info)
 
 static void etm_init_default_data(struct etm_config *config)
 {
-	u32 flags = (1 << 0 | /* instruction execute*/
-		     3 << 3 | /* ARM instruction */
-		     0 << 5 | /* No data value comparison */
-		     0 << 7 | /* No exact mach */
-		     0 << 8 | /* Ignore context ID */
-		     0 << 10); /* Security ignored */
-
 	if (WARN_ON_ONCE(!config))
 		return;
 
-	config->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN);
-	config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1;
-	config->addr_val[0] = (u32) _stext;
-	config->addr_val[1] = (u32) _etext;
-	config->addr_acctype[0] = flags;
-	config->addr_acctype[1] = flags;
-	config->addr_type[0] = ETM_ADDR_TYPE_RANGE;
-	config->addr_type[1] = ETM_ADDR_TYPE_RANGE;
-
 	etm_set_default(config);
+
+	/*
+	 * Taken verbatim from the TRM:
+	 *
+	 * To trace all memory:
+	 *  set bit [24] in register 0x009, the ETMTECR1, to 1
+	 *  set all other bits in register 0x009, the ETMTECR1, to 0
+	 *  set all bits in register 0x007, the ETMTECR2, to 0
+	 *  set register 0x008, the ETMTEEVR, to 0x6F (TRUE).
+	 */
+	config->enable_ctrl1 = BIT(24);
+	config->enable_ctrl2 = 0x0;
+	config->enable_event = ETM_HARD_WIRE_RES_A;
 }
 
 static void etm_init_trace_id(struct etm_drvdata *drvdata)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 10/30] coresight: etm3x: consolidating initial config
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (8 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 09/30] coresight: etm3x: changing default trace configuration Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 11/30] coresight: etm3x: implementing user/kernel mode tracing Mathieu Poirier
                   ` (19 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

There is really no point having two functions to take care
of doing the initials tracer configuration.  As such moving
everything to 'etm_set_default()'.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 37 ++++++++++-----------------
 1 file changed, 14 insertions(+), 23 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 8bd161584f85..cc0b08437419 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -41,7 +41,6 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
 /* The number of ETM/PTM currently registered */
 static int etm_count;
 static struct etm_drvdata *etmdrvdata[NR_CPUS];
-static void etm_init_default_data(struct etm_config *config);
 
 /*
  * Memory mapped writes to clear os lock are not supported on some processors
@@ -199,7 +198,7 @@ struct etm_config *get_etm_config(struct etm_drvdata *drvdata)
 		return NULL;
 
 	/* Set default config */
-	etm_init_default_data(config);
+	etm_set_default(config);
 	drvdata->config = config;
 out:
 	return drvdata->config;
@@ -212,6 +211,19 @@ void etm_set_default(struct etm_config *config)
 	if (WARN_ON_ONCE(!config))
 		return;
 
+	/*
+	 * Taken verbatim from the TRM:
+	 *
+	 * To trace all memory:
+	 *  set bit [24] in register 0x009, the ETMTECR1, to 1
+	 *  set all other bits in register 0x009, the ETMTECR1, to 0
+	 *  set all bits in register 0x007, the ETMTECR2, to 0
+	 *  set register 0x008, the ETMTEEVR, to 0x6F (TRUE).
+	 */
+	config->enable_ctrl1 = BIT(24);
+	config->enable_ctrl2 = 0x0;
+	config->enable_event = ETM_HARD_WIRE_RES_A;
+
 	config->trigger_event = ETM_DEFAULT_EVENT_VAL;
 	config->enable_event = ETM_HARD_WIRE_RES_A;
 
@@ -565,27 +577,6 @@ static void etm_init_arch_data(void *info)
 	CS_LOCK(drvdata->base);
 }
 
-static void etm_init_default_data(struct etm_config *config)
-{
-	if (WARN_ON_ONCE(!config))
-		return;
-
-	etm_set_default(config);
-
-	/*
-	 * Taken verbatim from the TRM:
-	 *
-	 * To trace all memory:
-	 *  set bit [24] in register 0x009, the ETMTECR1, to 1
-	 *  set all other bits in register 0x009, the ETMTECR1, to 0
-	 *  set all bits in register 0x007, the ETMTECR2, to 0
-	 *  set register 0x008, the ETMTEEVR, to 0x6F (TRUE).
-	 */
-	config->enable_ctrl1 = BIT(24);
-	config->enable_ctrl2 = 0x0;
-	config->enable_event = ETM_HARD_WIRE_RES_A;
-}
-
 static void etm_init_trace_id(struct etm_drvdata *drvdata)
 {
 	/*
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 11/30] coresight: etm3x: implementing user/kernel mode tracing
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (9 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 10/30] coresight: etm3x: consolidating initial config Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 12/30] coresight: etm3x: adding perf_get/set_config() API Mathieu Poirier
                   ` (18 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Adding new mode to limit tracing to kernel or user space.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm.h        |  9 +++-
 .../hwtracing/coresight/coresight-etm3x-sysfs.c    |  4 ++
 drivers/hwtracing/coresight/coresight-etm3x.c      | 60 ++++++++++++++++++++++
 3 files changed, 72 insertions(+), 1 deletion(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index f33aeb4e545d..ebc710df27dc 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -110,7 +110,12 @@
 #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_MODE_EXCL_KERN	BIT(5)
+#define ETM_MODE_EXCL_USER	BIT(6)
+#define ETM_MODE_ALL		(ETM_MODE_EXCLUDE | ETM_MODE_CYCACC | \
+				 ETM_MODE_STALL | ETM_MODE_TIMESTAMP | \
+				 ETM_MODE_CTXID | ETM_MODE_EXCL_KERN | \
+				 ETM_MODE_EXCL_USER)
 
 #define ETM_SQR_MASK		0x3
 #define ETM_TRACEID_MASK	0x3f
@@ -302,5 +307,7 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off)
 extern const struct attribute_group *coresight_etm_groups[];
 int etm_get_trace_id(struct etm_drvdata *drvdata);
 void etm_set_default(struct etm_config *config);
+void etm_config_trace_mode(struct etm_drvdata *drvdata,
+			   struct etm_config *config, u32 mode);
 struct etm_config *get_etm_config(struct etm_drvdata *drvdata);
 #endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
index bc8f04214567..1e0908c0ca6d 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -176,6 +176,10 @@ static ssize_t mode_store(struct device *dev,
 		config->ctrl |= ETMCR_CTXID_SIZE;
 	else
 		config->ctrl &= ~ETMCR_CTXID_SIZE;
+
+	if (config->mode & (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER))
+		etm_config_trace_mode(drvdata, config, config->mode);
+
 	spin_unlock(&drvdata->spinlock);
 
 	return size;
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index cc0b08437419..8a133d761f6a 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -252,6 +252,66 @@ void etm_set_default(struct etm_config *config)
 	config->ctxid_mask = 0x0;
 }
 
+void etm_config_trace_mode(struct etm_drvdata *drvdata,
+			   struct etm_config *config,
+			   u32 mode)
+{
+	u32 flags;
+
+	mode &= (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER);
+
+	/* excluding kernel AND user space doesn't make sense */
+	WARN_ON_ONCE(mode == (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER));
+
+	/* nothing to do if neither flags are set */
+	if (!(mode & ETM_MODE_EXCL_KERN) && !(mode & ETM_MODE_EXCL_USER))
+		return;
+
+	flags = (1 << 0 |	/* instruction execute*/
+		 3 << 3 |	/* ARM instruction */
+		 0 << 5 |	/* No data value comparison */
+		 0 << 7 |	/* No exact mach */
+		 0 << 8);	/* Ignore context ID */
+
+	/* No need to worry about single address comparators. */
+	config->enable_ctrl2 = 0x0;
+
+	/* Bit 0 is address range comparator 1 */
+	config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1;
+
+	/* On ETMv3.5:
+	 * ETMACTRn[13,11] == Non-secure state comparison control
+	 * ETMACTRn[12,10] == Secure state comparison control
+	 *
+	 * b00 == Match in all modes in this state
+	 * b01 == Do not match in any more in this state
+	 * b10 == Match in all modes excepts user mode in this state
+	 * b11 == Match only in user mode in this state
+	 */
+
+	/* Tracing in secure mode is not supported at this time */
+	flags |= (0 << 12 | 1 << 10);
+
+	if (mode & ETM_MODE_EXCL_USER) {
+		/* exclude user, match all modes except user mode */
+		flags |= (1 << 13 | 0 << 11);
+	} else {
+		/* exclude kernel, match only in user mode */
+		flags |= (1 << 13 | 1 << 11);
+	}
+
+	/* The ETMEEVR register is already set to "hard wire A", and as
+	 * such all there is to do is setup an address comparator that spans
+	 * the entire address range and configure the state and mode bits
+	 */
+	config->addr_val[0] = (u32) 0x0;
+	config->addr_val[1] = (u32) ~0x0;
+	config->addr_acctype[0] = flags;
+	config->addr_acctype[1] = flags;
+	config->addr_type[0] = ETM_ADDR_TYPE_RANGE;
+	config->addr_type[1] = ETM_ADDR_TYPE_RANGE;
+}
+
 static void etm_enable_hw(void *info)
 {
 	int i;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 12/30] coresight: etm3x: adding perf_get/set_config() API
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (10 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 11/30] coresight: etm3x: implementing user/kernel mode tracing Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 13/30] coresight: etm3x: implementing perf_enable/disable() API Mathieu Poirier
                   ` (17 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Adding a source operation to build a tracer configuration from
a perf_event.  That way possibly complex parsing of the information
coveyed by the event doesn't have to be carried out every time
the configuration is needed.

Since event configuration can change between concurrent sessions,
the possibility of associating a tracer with a configuration is
also provided.  As such Perf can assign session configuration to
tracers as it sees fit.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 70 +++++++++++++++++++++++++--
 include/linux/coresight.h                     |  5 ++
 2 files changed, 71 insertions(+), 4 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 8a133d761f6a..0994bf0a8334 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -31,6 +31,7 @@
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
 #include <linux/clk.h>
+#include <linux/perf_event.h>
 #include <asm/sections.h>
 
 #include "coresight-etm.h"
@@ -312,6 +313,40 @@ void etm_config_trace_mode(struct etm_drvdata *drvdata,
 	config->addr_type[1] = ETM_ADDR_TYPE_RANGE;
 }
 
+#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN)
+
+static int etm_parse_event_config(struct etm_drvdata *drvdata,
+				  struct etm_config *config,
+				  struct perf_event *event)
+{
+	u32 mode = 0;
+	u64 event_config = event->attr.config;
+
+	if (event->attr.exclude_kernel)
+		mode = ETM_MODE_EXCL_KERN;
+
+	if (event->attr.exclude_user)
+		mode = ETM_MODE_EXCL_USER;
+
+	/*
+	 * By default the tracers are configured to trace the whole address
+	 * range.  Narrow the field only if requested by user space.
+	 */
+	if (mode)
+		etm_config_trace_mode(drvdata, config, mode);
+
+	/*
+	 * At this time only cycle accurate and timestamp options are
+	 * available.
+	 */
+	if (event_config & ~ETM3X_SUPPORTED_OPTIONS)
+		return -EINVAL;
+
+	config->ctrl = event_config;
+
+	return 0;
+}
+
 static void etm_enable_hw(void *info)
 {
 	int i;
@@ -425,6 +460,31 @@ static int etm_trace_id(struct coresight_device *csdev)
 	return etm_get_trace_id(drvdata);
 }
 
+static void *perf_etm_get_config(struct coresight_device *csdev,
+				 struct perf_event *event)
+{
+	struct etm_config *config = NULL;
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	config = kzalloc(sizeof(struct etm_config), GFP_KERNEL);
+	if (!config)
+		return config;
+
+	etm_set_default(config);
+
+	if (etm_parse_event_config(drvdata, config, event))
+		return ERR_PTR(-EINVAL);
+
+	return config;
+}
+
+static void perf_etm_set_config(struct coresight_device *csdev, void *config)
+{
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	drvdata->config = config;
+}
+
 static int sysfs_etm_enable(struct coresight_device *csdev)
 {
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -523,10 +583,12 @@ static void sysfs_etm_disable(struct coresight_device *csdev)
 }
 
 static const struct coresight_ops_source etm_source_ops = {
-	.cpu_id		= etm_cpu_id,
-	.trace_id	= etm_trace_id,
-	.sysfs_enable	= sysfs_etm_enable,
-	.sysfs_disable	= sysfs_etm_disable,
+	.cpu_id			= etm_cpu_id,
+	.trace_id		= etm_trace_id,
+	.perf_get_config	= perf_etm_get_config,
+	.perf_set_config	= perf_etm_set_config,
+	.sysfs_enable		= sysfs_etm_enable,
+	.sysfs_disable		= sysfs_etm_disable,
 };
 
 static const struct coresight_ops etm_cs_ops = {
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index f9210df15f03..32463da877eb 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -209,12 +209,17 @@ struct coresight_ops_link {
  *			is associated to.
  * @trace_id:		returns the value of the component's trace ID as known
 			to the HW.
+ * @perf_get_config:	builds the ETM configuration after event' specifics.
+ * @perf_set_config:	associate a tracer with a configuration..
  * @sysfs_enable:	enables tracing for a source, from sysFS.
  * @sysfs_disable:	disables tracing for a source, from sysFS.
  */
 struct coresight_ops_source {
 	int (*cpu_id)(struct coresight_device *csdev);
 	int (*trace_id)(struct coresight_device *csdev);
+	void *(*perf_get_config)(struct coresight_device *csdev,
+				 struct perf_event *event);
+	void (*perf_set_config)(struct coresight_device *csdev, void *config);
 	int (*sysfs_enable)(struct coresight_device *csdev);
 	void (*sysfs_disable)(struct coresight_device *csdev);
 };
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 13/30] coresight: etm3x: implementing perf_enable/disable() API
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (11 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 12/30] coresight: etm3x: adding perf_get/set_config() API Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 14/30] coresight: etm3x: implementing perf_start/stop() API Mathieu Poirier
                   ` (16 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

That way traces can be enable and disabled automatically
from the Perf subystem using the PMU abstraction.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 50 ++++++++++++++++++++++++---
 include/linux/coresight.h                     |  4 +++
 2 files changed, 50 insertions(+), 4 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 0994bf0a8334..f565554744fe 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -369,8 +369,10 @@ static void etm_enable_hw(void *info)
 	etm_set_prog(drvdata);
 
 	etmcr = etm_readl(drvdata, ETMCR);
-	etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG);
+	/* Clear setting from a previous run if need be */
+	etmcr &= ~ETM3X_SUPPORTED_OPTIONS;
 	etmcr |= drvdata->port_size;
+	etmcr |= ETMCR_ETM_EN;
 	etm_writel(drvdata, config->ctrl | etmcr, ETMCR);
 	etm_writel(drvdata, config->trigger_event, ETMTRIGGER);
 	etm_writel(drvdata, config->startstop_ctrl, ETMTSSCR);
@@ -410,9 +412,6 @@ static void etm_enable_hw(void *info)
 	/* No VMID comparator value selected */
 	etm_writel(drvdata, 0x0, ETMVMIDCVR);
 
-	/* Ensures trace output is enabled from this ETM */
-	etm_writel(drvdata, config->ctrl | ETMCR_ETM_EN | etmcr, ETMCR);
-
 	etm_clr_prog(drvdata);
 	CS_LOCK(drvdata->base);
 
@@ -485,6 +484,47 @@ static void perf_etm_set_config(struct coresight_device *csdev, void *config)
 	drvdata->config = config;
 }
 
+static int perf_etm_enable(struct coresight_device *csdev)
+{
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
+		return -EINVAL;
+
+	if (local_cmpxchg(&drvdata->state,
+			  ETM_STATE_DISABLED, ETM_STATE_PERF))
+		return -EBUSY;
+
+	etm_enable_hw(drvdata);
+
+	return 0;
+}
+
+static int perf_etm_disable(struct coresight_device *csdev)
+{
+	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
+		return -EINVAL;
+
+	CS_UNLOCK(drvdata->base);
+
+	/* setting the prog bit disables tracing immediately */
+	etm_set_prog(drvdata);
+	/* Get ready for another session */
+	drvdata->config = NULL;
+	/*
+	 * There is no way to know when the tracer will be used again so
+	 * power down the tracer.
+	 */
+	etm_set_pwrdwn(drvdata);
+	local_set(&drvdata->state, ETM_STATE_DISABLED);
+
+	CS_LOCK(drvdata->base);
+
+	return 0;
+}
+
 static int sysfs_etm_enable(struct coresight_device *csdev)
 {
 	struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -587,6 +627,8 @@ static const struct coresight_ops_source etm_source_ops = {
 	.trace_id		= etm_trace_id,
 	.perf_get_config	= perf_etm_get_config,
 	.perf_set_config	= perf_etm_set_config,
+	.perf_enable		= perf_etm_enable,
+	.perf_disable		= perf_etm_disable,
 	.sysfs_enable		= sysfs_etm_enable,
 	.sysfs_disable		= sysfs_etm_disable,
 };
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 32463da877eb..b853d722346b 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -211,6 +211,8 @@ struct coresight_ops_link {
 			to the HW.
  * @perf_get_config:	builds the ETM configuration after event' specifics.
  * @perf_set_config:	associate a tracer with a configuration..
+ * @perf_enable:	enables tracing for a source, from Perf.
+ * @perf_disable:	disables tracing for a source, from Perf.
  * @sysfs_enable:	enables tracing for a source, from sysFS.
  * @sysfs_disable:	disables tracing for a source, from sysFS.
  */
@@ -220,6 +222,8 @@ struct coresight_ops_source {
 	void *(*perf_get_config)(struct coresight_device *csdev,
 				 struct perf_event *event);
 	void (*perf_set_config)(struct coresight_device *csdev, void *config);
+	int (*perf_enable)(struct coresight_device *csdev);
+	int (*perf_disable)(struct coresight_device *csdev);
 	int (*sysfs_enable)(struct coresight_device *csdev);
 	void (*sysfs_disable)(struct coresight_device *csdev);
 };
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 14/30] coresight: etm3x: implementing perf_start/stop() API
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (12 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 13/30] coresight: etm3x: implementing perf_enable/disable() API Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 15/30] coresight: making coresight_build_paths() public Mathieu Poirier
                   ` (15 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Adding an API to deal with runtime PM (and anything else)
that might be required by a tracer before and after it is
to be used by the Perf framework.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etm3x.c | 12 ++++++++++++
 include/linux/coresight.h                     |  5 +++++
 2 files changed, 17 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index f565554744fe..9b4c0359ca29 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -459,6 +459,16 @@ static int etm_trace_id(struct coresight_device *csdev)
 	return etm_get_trace_id(drvdata);
 }
 
+static int perf_etm_start(struct coresight_device *csdev)
+{
+	return pm_runtime_get_sync(csdev->dev.parent);
+}
+
+static int perf_etm_stop(struct coresight_device *csdev)
+{
+	return pm_runtime_put(csdev->dev.parent);
+}
+
 static void *perf_etm_get_config(struct coresight_device *csdev,
 				 struct perf_event *event)
 {
@@ -625,6 +635,8 @@ static void sysfs_etm_disable(struct coresight_device *csdev)
 static const struct coresight_ops_source etm_source_ops = {
 	.cpu_id			= etm_cpu_id,
 	.trace_id		= etm_trace_id,
+	.perf_start		= perf_etm_start,
+	.perf_stop		= perf_etm_stop,
 	.perf_get_config	= perf_etm_get_config,
 	.perf_set_config	= perf_etm_set_config,
 	.perf_enable		= perf_etm_enable,
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index b853d722346b..0601da01eeb2 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -209,6 +209,9 @@ struct coresight_ops_link {
  *			is associated to.
  * @trace_id:		returns the value of the component's trace ID as known
 			to the HW.
+ * @perf_start:		operations to be done before a tracer can be used.
+ * @perf_stop:		operations to be done when a tracer is
+ *			no longer needed.
  * @perf_get_config:	builds the ETM configuration after event' specifics.
  * @perf_set_config:	associate a tracer with a configuration..
  * @perf_enable:	enables tracing for a source, from Perf.
@@ -219,6 +222,8 @@ struct coresight_ops_link {
 struct coresight_ops_source {
 	int (*cpu_id)(struct coresight_device *csdev);
 	int (*trace_id)(struct coresight_device *csdev);
+	int (*perf_start)(struct coresight_device *csdev);
+	int (*perf_stop)(struct coresight_device *csdev);
 	void *(*perf_get_config)(struct coresight_device *csdev,
 				 struct perf_event *event);
 	void (*perf_set_config)(struct coresight_device *csdev, void *config);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 15/30] coresight: making coresight_build_paths() public
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (13 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 14/30] coresight: etm3x: implementing perf_start/stop() API Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 16/30] coresight: keeping track of enabled sink buffers Mathieu Poirier
                   ` (14 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

That way a path can be built outside of the core framework,
something useful when a PMU is initialised from the perf
subsystem.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-priv.h | 3 +++
 drivers/hwtracing/coresight/coresight.c      | 5 ++---
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 62fcd98cc7cf..8a52fdcb4bd6 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -52,6 +52,9 @@ static inline void CS_UNLOCK(void __iomem *addr)
 	} while (0);
 }
 
+int coresight_build_paths(struct coresight_device *csdev,
+			  struct list_head *path, bool enable);
+
 #ifdef CONFIG_CORESIGHT_SOURCE_ETM3X
 extern int etm_readl_cp14(u32 off, unsigned int *val);
 extern int etm_writel_cp14(u32 off, u32 val);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index d318a338a517..083080d435af 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -301,9 +301,8 @@ static int coresight_disable_path(struct list_head *path)
 	return 0;
 }
 
-static int coresight_build_paths(struct coresight_device *csdev,
-				 struct list_head *path,
-				 bool enable)
+int coresight_build_paths(struct coresight_device *csdev,
+			  struct list_head *path, bool enable)
 {
 	int i, ret = -EINVAL;
 	struct coresight_connection *conn;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 16/30] coresight: keeping track of enabled sink buffers
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (14 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 15/30] coresight: making coresight_build_paths() public Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 17/30] perf: changing pmu::setup_aux() parameter to include event Mathieu Poirier
                   ` (13 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Keep track of enabled sink buffers as a path between source
and sinks is being built.  That way sinks associated to a
source can be accessed quickly.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-priv.h |  3 ++-
 drivers/hwtracing/coresight/coresight.c      | 16 ++++++++++------
 include/linux/coresight.h                    |  2 ++
 3 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 8a52fdcb4bd6..3d7467f315ed 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -53,7 +53,8 @@ static inline void CS_UNLOCK(void __iomem *addr)
 }
 
 int coresight_build_paths(struct coresight_device *csdev,
-			  struct list_head *path, bool enable);
+			  struct list_head *path,
+			  struct list_head *sinks, bool enable);
 
 #ifdef CONFIG_CORESIGHT_SOURCE_ETM3X
 extern int etm_readl_cp14(u32 off, unsigned int *val);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 083080d435af..a40519d5a9e2 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -302,7 +302,8 @@ static int coresight_disable_path(struct list_head *path)
 }
 
 int coresight_build_paths(struct coresight_device *csdev,
-			  struct list_head *path, bool enable)
+			  struct list_head *path,
+			  struct list_head *sinks, bool enable)
 {
 	int i, ret = -EINVAL;
 	struct coresight_connection *conn;
@@ -312,15 +313,18 @@ int coresight_build_paths(struct coresight_device *csdev,
 	if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
 	    csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
 	    csdev->activated) {
-		if (enable)
+		if (enable) {
 			ret = coresight_enable_path(path);
-		else
+			if (!ret && sinks)
+				list_add(&csdev->sinks, sinks);
+		} else {
 			ret = coresight_disable_path(path);
+		}
 	} else {
 		for (i = 0; i < csdev->nr_outport; i++) {
 			conn = &csdev->conns[i];
 			if (coresight_build_paths(conn->child_dev,
-						    path, enable) == 0)
+						  path, sinks, enable) == 0)
 				ret = 0;
 		}
 	}
@@ -347,7 +351,7 @@ int coresight_enable(struct coresight_device *csdev)
 	if (csdev->enable)
 		goto out;
 
-	if (coresight_build_paths(csdev, &path, true)) {
+	if (coresight_build_paths(csdev, &path, NULL, true)) {
 		dev_err(&csdev->dev, "building path(s) failed\n");
 		goto out;
 	}
@@ -373,7 +377,7 @@ void coresight_disable(struct coresight_device *csdev)
 		goto out;
 
 	coresight_disable_source(csdev);
-	if (coresight_build_paths(csdev, &path, false))
+	if (coresight_build_paths(csdev, &path, NULL, false))
 		dev_err(&csdev->dev, "releasing path(s) failed\n");
 
 out:
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 0601da01eeb2..dd530a6d3e21 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -153,6 +153,7 @@ struct coresight_connection {
  * @dev:	The device entity associated to this component.
  * @refcnt:	keep track of what is in use.
  * @path_link:	link of current component into the path being enabled.
+ * @sinks:	link of currently enabled sinks for a source.
  * @orphan:	true if the component has connections that haven't been linked.
  * @enable:	'true' if component is currently part of an active path.
  * @activated:	'true' only if a _sink_ has been activated.  A sink can be
@@ -169,6 +170,7 @@ struct coresight_device {
 	struct device dev;
 	atomic_t *refcnt;
 	struct list_head path_link;
+	struct list_head sinks;
 	bool orphan;
 	bool enable;	/* true only if configured as part of a path */
 	bool activated;	/* true only if a sink is part of a path */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 17/30] perf: changing pmu::setup_aux() parameter to include event
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (15 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 16/30] coresight: keeping track of enabled sink buffers Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-19 13:34   ` Alexander Shishkin
  2015-10-18 18:24 ` [PATCH V2 18/30] coresight: etb10: moving to local atomic operations Mathieu Poirier
                   ` (12 subsequent siblings)
  29 siblings, 1 reply; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

For some tracers the event carries information to be embedded
in the private structure returned by setup_aux().

As such changing the first parameter to be of type struct perf_event *
so that all the necessary information can be conveyed.  Also changing
current customer of the API to reflect the modification.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 arch/x86/kernel/cpu/perf_event_intel_bts.c | 4 +++-
 arch/x86/kernel/cpu/perf_event_intel_pt.c  | 5 +++--
 include/linux/perf_event.h                 | 2 +-
 kernel/events/ring_buffer.c                | 2 +-
 4 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kernel/cpu/perf_event_intel_bts.c b/arch/x86/kernel/cpu/perf_event_intel_bts.c
index d1c0f254afbe..773600b2f313 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_bts.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_bts.c
@@ -68,10 +68,12 @@ static size_t buf_size(struct page *page)
 }
 
 static void *
-bts_buffer_setup_aux(int cpu, void **pages, int nr_pages, bool overwrite)
+bts_buffer_setup_aux(struct perf_event *event, void **pages,
+		     int nr_pages, bool overwrite)
 {
 	struct bts_buffer *buf;
 	struct page *page;
+	int cpu = event->cpu;
 	int node = (cpu == -1) ? cpu : cpu_to_node(cpu);
 	unsigned long offset;
 	size_t size = nr_pages << PAGE_SHIFT;
diff --git a/arch/x86/kernel/cpu/perf_event_intel_pt.c b/arch/x86/kernel/cpu/perf_event_intel_pt.c
index 42169283448b..f8b881210648 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_pt.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_pt.c
@@ -863,10 +863,11 @@ static int pt_buffer_init_topa(struct pt_buffer *buf, unsigned long nr_pages,
  * Return:	Our private PT buffer structure.
  */
 static void *
-pt_buffer_setup_aux(int cpu, void **pages, int nr_pages, bool snapshot)
+pt_buffer_setup_aux(struct perf_event *event, void **pages,
+		    int nr_pages, bool snapshot)
 {
 	struct pt_buffer *buf;
-	int node, ret;
+	int node, ret, cpu = event->cpu;
 
 	if (!nr_pages)
 		return NULL;
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 092a0e8a479a..0d9964b5ed4d 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -292,7 +292,7 @@ struct pmu {
 	/*
 	 * Set up pmu-private data structures for an AUX area
 	 */
-	void *(*setup_aux)		(int cpu, void **pages,
+	void *(*setup_aux)		(struct perf_event *event, void **pages,
 					 int nr_pages, bool overwrite);
 					/* optional */
 
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index 182bc30899d5..87171b700c77 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -522,7 +522,7 @@ int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event,
 			goto out;
 	}
 
-	rb->aux_priv = event->pmu->setup_aux(event->cpu, rb->aux_pages, nr_pages,
+	rb->aux_priv = event->pmu->setup_aux(event, rb->aux_pages, nr_pages,
 					     overwrite);
 	if (!rb->aux_priv)
 		goto out;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 18/30] coresight: etb10: moving to local atomic operations
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (16 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 17/30] perf: changing pmu::setup_aux() parameter to include event Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 19/30] coresight: etb10: implementing the setup_aux() API Mathieu Poirier
                   ` (11 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Moving to use local atomic operations to take advantage of the
lockless implementation, something that will come handy when
the ETB is accessed from the Perf subsystem.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etb10.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index 77d0f9c1118d..ecdbe0dd4d08 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -84,7 +84,7 @@ struct etb_drvdata {
 	struct coresight_device	*csdev;
 	struct miscdevice	miscdev;
 	spinlock_t		spinlock;
-	atomic_t		in_use;
+	local_t			in_use;
 	u8			*buf;
 	u32			buffer_depth;
 	bool			enable;
@@ -281,7 +281,7 @@ static int etb_open(struct inode *inode, struct file *file)
 	struct etb_drvdata *drvdata = container_of(file->private_data,
 						   struct etb_drvdata, miscdev);
 
-	if (atomic_cmpxchg(&drvdata->in_use, 0, 1))
+	if (local_cmpxchg(&drvdata->in_use, 0, 1))
 		return -EBUSY;
 
 	dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__);
@@ -317,7 +317,7 @@ static int etb_release(struct inode *inode, struct file *file)
 {
 	struct etb_drvdata *drvdata = container_of(file->private_data,
 						   struct etb_drvdata, miscdev);
-	atomic_set(&drvdata->in_use, 0);
+	local_set(&drvdata->in_use, 0);
 
 	dev_dbg(drvdata->dev, "%s: released\n", __func__);
 	return 0;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 19/30] coresight: etb10: implementing the setup_aux() API
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (17 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 18/30] coresight: etb10: moving to local atomic operations Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-19 13:44   ` Alexander Shishkin
  2015-10-20 11:37   ` Alexander Shishkin
  2015-10-18 18:24 ` [PATCH V2 20/30] coresight: etb10: implementing buffer set/reset() API Mathieu Poirier
                   ` (10 subsequent siblings)
  29 siblings, 2 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Adding an ETB10 specific auxiliary area setup operation to be
used by the perf framework when events are initialised.

Part of this operation involves modeling the mmap'ed area based
on the specific ways a sink buffer gathers information.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etb10.c | 55 +++++++++++++++++++++++++++
 include/linux/coresight.h                     |  3 ++
 2 files changed, 58 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index ecdbe0dd4d08..7f34e7af465e 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -27,6 +27,9 @@
 #include <linux/coresight.h>
 #include <linux/amba/bus.h>
 #include <linux/clk.h>
+#include <linux/mm.h>
+
+#include <asm/local.h>
 
 #include "coresight-priv.h"
 
@@ -64,6 +67,32 @@
 #define ETB_FRAME_SIZE_WORDS	4
 
 /**
+ * struct cs_buffer - keep track of a recording session' specifics
+ * @cur:	index of the current buffer
+ * @nr_pages:	max number of pages granted to us
+ * @nr_bufs:	number of clustered pages
+ * @offset:	offset within the current buffer
+ * @size:	how much space we have for this run
+ * @data_size:	how much we collected in this run
+ * @head:	head of the ring buffer
+ * @lost:	other than zero if we had a HW buffer wrap around
+ * @snapshot:	is this run in snapshot mode
+ * @addr:	virtual address this buffer starts at
+ */
+struct cs_buffers {
+	unsigned int		cur;
+	unsigned int		nr_pages;
+	unsigned int		nr_bufs;
+	unsigned long		offset;
+	unsigned long		size;
+	local_t			data_size;
+	local_t			head;
+	local_t			lost;
+	bool			snapshot;
+	void			*addr[0];
+};
+
+/**
  * struct etb_drvdata - specifics associated to an ETB component
  * @base:	memory mapped base address for this component.
  * @dev:	the device entity associated to this component.
@@ -252,9 +281,35 @@ static void etb_disable(struct coresight_device *csdev)
 	dev_info(drvdata->dev, "ETB disabled\n");
 }
 
+static void *etb_setup_aux(struct coresight_device *csdev, int cpu,
+			   void **pages, int nr_pages, bool overwrite)
+{
+	int node, pg;
+	struct cs_buffers *buf;
+
+	if (cpu == -1)
+		cpu = smp_processor_id();
+	node = cpu_to_node(cpu);
+
+	buf = kzalloc_node(offsetof(struct cs_buffers, addr[nr_pages]),
+			   GFP_KERNEL, node);
+	if (!buf)
+		return NULL;
+
+	buf->snapshot = overwrite;
+	buf->nr_pages = nr_pages;
+
+	/* Record information about buffers */
+	for (pg = 0; pg < buf->nr_pages; pg++)
+		buf->addr[pg] = pages[pg];
+
+	return buf;
+}
+
 static const struct coresight_ops_sink etb_sink_ops = {
 	.enable		= etb_enable,
 	.disable	= etb_disable,
+	.setup_aux	= etb_setup_aux,
 };
 
 static const struct coresight_ops etb_cs_ops = {
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index dd530a6d3e21..b1c25eba83b4 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -187,10 +187,13 @@ struct coresight_device {
  * Operations available for sinks
  * @enable:	enables the sink.
  * @disable:	disables the sink.
+ * @setup_aux:	initialises perf's ring buffer for trace collection.
  */
 struct coresight_ops_sink {
 	int (*enable)(struct coresight_device *csdev);
 	void (*disable)(struct coresight_device *csdev);
+	void *(*setup_aux)(struct coresight_device *csdev, int cpu,
+			   void **pages, int nr_pages, bool overwrite);
 };
 
 /**
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 20/30] coresight: etb10: implementing buffer set/reset() API
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (18 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 19/30] coresight: etb10: implementing the setup_aux() API Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-20  9:56   ` Alexander Shishkin
  2015-10-18 18:24 ` [PATCH V2 21/30] coresight: etb10: implementing buffer update API Mathieu Poirier
                   ` (9 subsequent siblings)
  29 siblings, 1 reply; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Implementing perf related APIs to activate and terminate
a trace session.  More specifically dealing with the sink
buffer's internal mechanic along with perf's API to start
and stop interactions with the ring buffers.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etb10.c | 58 +++++++++++++++++++++++++++
 include/linux/coresight.h                     |  9 +++++
 2 files changed, 67 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index 7f34e7af465e..ca2c4b42464d 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -28,6 +28,7 @@
 #include <linux/amba/bus.h>
 #include <linux/clk.h>
 #include <linux/mm.h>
+#include <linux/perf_event.h>
 
 #include <asm/local.h>
 
@@ -306,10 +307,67 @@ static void *etb_setup_aux(struct coresight_device *csdev, int cpu,
 	return buf;
 }
 
+static int etb_set_buffer(struct coresight_device *csdev,
+			  struct perf_output_handle *handle,
+			  void *sink_config)
+{
+	int ret = 0;
+	unsigned long head;
+	struct cs_buffers *buf = sink_config;
+	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	/* This sink can only be used by a single session */
+	if (local_xchg(&drvdata->in_use, 1))
+		return -EBUSY;
+
+	/* how much space do we have in this session */
+	buf->size = handle->size;
+
+	/* wrap head around to the amount of space we have */
+	head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1);
+
+	/* find the page to write to */
+	buf->cur = head / PAGE_SIZE;
+
+	/* and offset within that page */
+	buf->offset = head % PAGE_SIZE;
+
+	local_set(&buf->head, head);
+	local_set(&buf->data_size, 0);
+
+	return ret;
+}
+
+static void etb_reset_buffer(struct coresight_device *csdev,
+			     struct perf_output_handle *handle,
+			     void *sink_config)
+{
+	struct cs_buffers *buf = sink_config;
+	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (buf) {
+		/*
+		 * In snapshot mode ->data_size holds the new address of the
+		 * ring buffer's head.  The size itself is the whole address
+		 * range since we want the latest information.
+		 */
+		if (buf->snapshot)
+			handle->head = local_xchg(&buf->data_size,
+						  buf->nr_pages << PAGE_SHIFT);
+
+		perf_aux_output_end(handle, local_xchg(&buf->data_size, 0),
+				    local_xchg(&buf->lost, 0));
+	}
+
+	local_set(&drvdata->in_use, 0);
+}
+
 static const struct coresight_ops_sink etb_sink_ops = {
 	.enable		= etb_enable,
 	.disable	= etb_disable,
 	.setup_aux	= etb_setup_aux,
+	.set_buffer	= etb_set_buffer,
+	.reset_buffer	= etb_reset_buffer,
 };
 
 static const struct coresight_ops etb_cs_ops = {
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index b1c25eba83b4..78202d5ea58a 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -15,6 +15,7 @@
 
 #include <linux/device.h>
 #include <linux/sched.h>
+#include <linux/perf_event.h>
 
 /* Peripheral id registers (0xFD0-0xFEC) */
 #define CORESIGHT_PERIPHIDR4	0xfd0
@@ -188,12 +189,20 @@ struct coresight_device {
  * @enable:	enables the sink.
  * @disable:	disables the sink.
  * @setup_aux:	initialises perf's ring buffer for trace collection.
+ * @set_buffer:	initialises buffer mechanic before a trace session.
+ * @reset_buffer: finalises buffer mechanic after a trace session.
  */
 struct coresight_ops_sink {
 	int (*enable)(struct coresight_device *csdev);
 	void (*disable)(struct coresight_device *csdev);
 	void *(*setup_aux)(struct coresight_device *csdev, int cpu,
 			   void **pages, int nr_pages, bool overwrite);
+	int (*set_buffer)(struct coresight_device *csdev,
+			  struct perf_output_handle *handle,
+			  void *sink_config);
+	void (*reset_buffer)(struct coresight_device *csdev,
+			     struct perf_output_handle *handle,
+			     void *sink_config);
 };
 
 /**
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 21/30] coresight: etb10: implementing buffer update API
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (19 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 20/30] coresight: etb10: implementing buffer set/reset() API Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 22/30] coresight: etm-perf: new PMU driver for ETM tracers Mathieu Poirier
                   ` (8 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Implementing buffer API to update the location of the ETB
internal ring buffer once a trace session has ended.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-etb10.c | 128 ++++++++++++++++++++++++++
 include/linux/coresight.h                     |  14 ++-
 2 files changed, 137 insertions(+), 5 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index ca2c4b42464d..8e469d097955 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -28,6 +28,7 @@
 #include <linux/amba/bus.h>
 #include <linux/clk.h>
 #include <linux/mm.h>
+#include <linux/circ_buf.h>
 #include <linux/perf_event.h>
 
 #include <asm/local.h>
@@ -362,12 +363,139 @@ static void etb_reset_buffer(struct coresight_device *csdev,
 	local_set(&drvdata->in_use, 0);
 }
 
+static void etb_update_buffer(struct coresight_device *csdev,
+			      struct perf_output_handle *handle,
+			      void *sink_config)
+{
+	int i, cur;
+	u8 *buf_ptr;
+	u32 read_ptr, write_ptr, start;
+	u32 status, read_data, words;
+	unsigned long flags, offset;
+	struct cs_buffers *buf = sink_config;
+	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (!buf)
+		return;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (!drvdata->enable)
+		goto out;
+
+	etb_disable_hw(drvdata);
+	CS_UNLOCK(drvdata->base);
+
+	/* unit is in words, not bytes */
+	read_ptr = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER);
+	write_ptr = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER);
+
+	/*
+	 * Entries should be aligned to the frame size.  If they are not
+	 * go back to the last alignement point to give decoding tools a
+	 * chance to fix things.
+	 */
+	if (write_ptr % ETB_FRAME_SIZE_WORDS) {
+		dev_err(drvdata->dev,
+			"write_ptr: %lu not aligned to formatter frame size\n",
+			(unsigned long)write_ptr);
+
+		write_ptr &= ~ETB_FRAME_SIZE_WORDS;
+		local_inc(&buf->lost);
+	}
+
+	/*
+	 * Get a hold of the status register and see if a wrap around
+	 * has occurred.  If so adjust things accordingly.  Otherwise
+	 * start at the beginning and go until the write pointer has
+	 * been reached.
+	 */
+	status = readl_relaxed(drvdata->base + ETB_STATUS_REG);
+	if (status & ETB_STATUS_RAM_FULL) {
+		local_inc(&buf->lost);
+		words = drvdata->buffer_depth;
+		start = write_ptr;
+	} else {
+		words = CIRC_CNT(write_ptr, read_ptr, drvdata->buffer_depth);
+		start = read_ptr;
+	}
+
+	/*
+	 * Make sure we don't overwrite data that hasn't been consumed yet.
+	 * It is entirely possible that the HW buffer has more data than the
+	 * ring buffer can currently handle.  If so adjust the start address
+	 * to take only the last traces.
+	 *
+	 * In snapshot mode we are looking to get the latest traces only and as
+	 * such, we don't care about not overwriting data that hasn't been
+	 * processed by user space.
+	 *
+	 * Since metrics related to ETBs is in words, multiply by the
+	 * amount of byte per word to have the right units.
+	 */
+	if (!buf->snapshot && words * ETB_FRAME_SIZE_WORDS > handle->size) {
+		unsigned int capacity = drvdata->buffer_depth;
+
+		/* make sure new sizes are still multiples the frame size */
+		words = handle->size / ETB_FRAME_SIZE_WORDS;
+		/* advance the start pointer to get the latest trace data */
+		start += capacity - words;
+		/* wrap around if we've reach the end of the HW buffer */
+		start &= capacity - 1;
+		/* let the decoder know we've skipped ahead */
+		local_inc(&buf->lost);
+	}
+
+	/* finally tell HW where we want to start reading from */
+	writel_relaxed(start, drvdata->base + ETB_RAM_READ_POINTER);
+
+	cur = buf->cur;
+	offset = buf->offset;
+	for (i = 0; i < words; i++) {
+		buf_ptr = buf->addr[cur] + offset;
+		read_data = readl_relaxed(drvdata->base +
+					  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;
+
+		offset += 4;
+		if (offset >= PAGE_SIZE) {
+			offset = 0;
+			cur++;
+			/* wrap around at the end of the buffer */
+			cur &= buf->nr_pages - 1;
+		}
+	}
+
+	/* reset ETB buffer for next run */
+	writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER);
+	writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER);
+
+	/*
+	 * In snapshot mode all we have to do is communicate to
+	 * perf_aux_output_end() the address of the current head.  In full
+	 * trace mode the same function expects a size to move rb->aux_head
+	 * forward.
+	 */
+	if (buf->snapshot)
+		local_set(&buf->data_size, (cur * PAGE_SIZE) + offset);
+	else
+		local_add(words * ETB_FRAME_SIZE_WORDS, &buf->data_size);
+
+	CS_LOCK(drvdata->base);
+	etb_enable_hw(drvdata);
+out:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+}
+
 static const struct coresight_ops_sink etb_sink_ops = {
 	.enable		= etb_enable,
 	.disable	= etb_disable,
 	.setup_aux	= etb_setup_aux,
 	.set_buffer	= etb_set_buffer,
 	.reset_buffer	= etb_reset_buffer,
+	.update_buffer	= etb_update_buffer,
 };
 
 static const struct coresight_ops etb_cs_ops = {
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 78202d5ea58a..cdf401d51998 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -186,11 +186,12 @@ struct coresight_device {
 /**
  * struct coresight_ops_sink - basic operations for a sink
  * Operations available for sinks
- * @enable:	enables the sink.
- * @disable:	disables the sink.
- * @setup_aux:	initialises perf's ring buffer for trace collection.
- * @set_buffer:	initialises buffer mechanic before a trace session.
- * @reset_buffer: finalises buffer mechanic after a trace session.
+ * @enable:		enables the sink.
+ * @disable:		disables the sink.
+ * @setup_aux:		initialises perf's ring buffer for trace collection.
+ * @set_buffer:		initialises buffer mechanic before a trace session.
+ * @reset_buffer:	finalises buffer mechanic after a trace session.
+ * @update_buffer:	update buffer pointers after a trace session.
  */
 struct coresight_ops_sink {
 	int (*enable)(struct coresight_device *csdev);
@@ -203,6 +204,9 @@ struct coresight_ops_sink {
 	void (*reset_buffer)(struct coresight_device *csdev,
 			     struct perf_output_handle *handle,
 			     void *sink_config);
+	void (*update_buffer)(struct coresight_device *csdev,
+			      struct perf_output_handle *handle,
+			      void *sink_config);
 };
 
 /**
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 22/30] coresight: etm-perf: new PMU driver for ETM tracers
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (20 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 21/30] coresight: etb10: implementing buffer update API Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-19 15:37   ` Alexander Shishkin
  2015-10-20  9:34   ` Alexander Shishkin
  2015-10-18 18:24 ` [PATCH V2 23/30] coresight: updating documentation to reflect integration with perf Mathieu Poirier
                   ` (7 subsequent siblings)
  29 siblings, 2 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Perf is a well known and used tool for performance monitoring
and much more. A such it is an ideal condaditate for integration
with coresight based HW tracing.

This patch introduces a PMU that represent a coresight tracer to
the Perf core.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/Makefile             |   3 +-
 drivers/hwtracing/coresight/coresight-etm-perf.c | 533 +++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-etm-perf.h |  27 ++
 drivers/hwtracing/coresight/coresight-etm3x.c    |   7 +
 4 files changed, 569 insertions(+), 1 deletion(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.c
 create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.h

diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 233d66cf22d3..cf8c6d689747 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o
 obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \
 					   coresight-replicator.o
 obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \
-					coresight-etm3x-sysfs.o
+					coresight-etm3x-sysfs.o \
+					coresight-etm-perf.o
 obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
 obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
new file mode 100644
index 000000000000..dbd02277fcda
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/coresight.h>
+#include <linux/cpumask.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/perf_event.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "coresight-priv.h"
+
+#define CORESIGHT_ETM_PMU_NAME  "cs_etm"
+
+static struct pmu etm_pmu;
+static bool etm_perf_up;
+
+/**
+ * struct etm_event_data - Coresight specifics associated to an event
+ * @mask:		hold the CPU(s) this event was set for.
+ * @source_config:	per CPU tracer configuration associated to a
+ *			trace session.
+ * @sink_config:	per CPU AUX configuration associated to a
+ *			trace session.
+ * @sink:		sink associated to a CPU.
+ */
+struct etm_event_data {
+	cpumask_t mask;
+	void **source_config;
+	void **sink_config;
+	void **sink;
+};
+
+static DEFINE_PER_CPU(struct perf_output_handle, ctx_handle);
+static DEFINE_PER_CPU(struct coresight_device *, csdev_src);
+
+/* ETMCR is 'config' */
+PMU_FORMAT_ATTR(cycacc,		"config:12");
+PMU_FORMAT_ATTR(timestamp,	"config:28");
+
+static struct attribute *etm_config_formats_attr[] = {
+	&format_attr_cycacc.attr,
+	&format_attr_timestamp.attr,
+	NULL,
+};
+
+static struct attribute_group etm_pmu_format_group = {
+	.name   = "format",
+	.attrs  = etm_config_formats_attr,
+};
+
+static const struct attribute_group *etm_pmu_attr_groups[] = {
+	&etm_pmu_format_group,
+	NULL,
+};
+
+static void etm_event_read(struct perf_event *event) {}
+
+/**
+ * etm_event_build_path() - setup a path between source and sink
+ * @cpu:	The CPU the tracer is associated to.
+ * @build:	Whether the path should be setup or thorned down.
+ *
+ * Return:	The _first_ sink buffer discovered during the walkthrough.
+ */
+static struct coresight_device *etm_event_build_path(int cpu, bool build)
+{
+	int ret = 0;
+	LIST_HEAD(path);
+	LIST_HEAD(sinks);
+	struct coresight_device *csdev_source;
+	struct coresight_device *csdev_sink = NULL;
+
+	csdev_source = per_cpu(csdev_src, cpu);
+
+	if (!csdev_source)
+		return ERR_PTR(-EINVAL);
+
+	if (csdev_source->type != CORESIGHT_DEV_TYPE_SOURCE)
+		return ERR_PTR(-EINVAL);
+
+	if (build) {
+		ret = coresight_build_paths(csdev_source, &path, &sinks, build);
+		if (ret) {
+			dev_dbg(&csdev_source->dev,
+				"creating path(s) failed\n");
+			goto out;
+		}
+
+		/* Everything is good, record first enabled sink buffer */
+		csdev_sink = list_first_entry(&sinks,
+					      struct coresight_device, sinks);
+	} else {
+		ret = coresight_build_paths(csdev_source, &path, NULL, build);
+		if (ret)
+			dev_dbg(&csdev_source->dev,
+				"releasing path(s) failed\n");
+	}
+
+out:
+	return csdev_sink;
+}
+
+static int etm_event_pmu_start(struct perf_event *event)
+{
+	int cpu, ret;
+	cpumask_t mask;
+	struct coresight_device *csdev;
+
+	cpumask_clear(&mask);
+	if (event->cpu != -1)
+		cpumask_set_cpu(event->cpu, &mask);
+	else
+		cpumask_copy(&mask, cpu_online_mask);
+
+	for_each_cpu(cpu, &mask) {
+		csdev = per_cpu(csdev_src, cpu);
+
+		if (!source_ops(csdev)->perf_start)
+			continue;
+
+		ret = source_ops(csdev)->perf_start(csdev);
+		if (ret)
+			goto err;
+	}
+
+out:
+	return ret;
+err:
+	for_each_cpu(cpu, &mask) {
+		csdev = per_cpu(csdev_src, cpu);
+
+		if (!source_ops(csdev)->perf_stop)
+			continue;
+		source_ops(csdev)->perf_stop(csdev);
+	}
+
+	goto out;
+}
+
+static void etm_event_destroy(struct perf_event *event)
+{
+	int cpu;
+	cpumask_t mask;
+	struct coresight_device *csdev;
+
+	cpumask_clear(&mask);
+	if (event->cpu != -1)
+		cpumask_set_cpu(event->cpu, &mask);
+	else
+		cpumask_copy(&mask, cpu_online_mask);
+
+	for_each_cpu(cpu, &mask) {
+		csdev = per_cpu(csdev_src, cpu);
+		etm_event_build_path(cpu, false);
+
+		if (!source_ops(csdev)->perf_stop)
+			continue;
+		source_ops(csdev)->perf_stop(csdev);
+	}
+}
+
+static int etm_event_init(struct perf_event *event)
+{
+	int ret;
+
+	if (event->attr.type != etm_pmu.type)
+		return -ENOENT;
+
+	if (event->cpu >= nr_cpu_ids)
+		return -EINVAL;
+
+	ret = etm_event_pmu_start(event);
+	if (ret)
+		return ret;
+
+	event->destroy = etm_event_destroy;
+
+	return 0;
+}
+
+static void *alloc_event_data(int cpu)
+{
+	int size;
+	struct etm_event_data *event_data;
+	void *source_config, *sink_config, *sink;
+
+	event_data = kzalloc(sizeof(struct etm_event_data), GFP_KERNEL);
+	 if (!event_data)
+		return NULL;
+
+	if (cpu != -1)
+		size = 1;
+	else
+		size = num_online_cpus();
+
+	source_config = kcalloc(size, sizeof(void *), GFP_KERNEL);
+	if (!source_config)
+		goto source_config_err;
+
+	sink_config = kcalloc(size, sizeof(void *), GFP_KERNEL);
+	if (!sink_config)
+		goto sink_config_err;
+
+	sink = kcalloc(size, sizeof(void *), GFP_KERNEL);
+	if (!sink)
+		goto sink_err;
+
+	cpumask_clear(&event_data->mask);
+	event_data->source_config = source_config;
+	event_data->sink_config = sink_config;
+	event_data->sink = sink;
+
+out:
+	return event_data;
+
+sink_err:
+	kfree(sink_config);
+sink_config_err:
+	kfree(source_config);
+source_config_err:
+	kfree(event_data);
+	event_data = NULL;
+	goto out;
+}
+
+static void free_event_data(struct etm_event_data *event_data)
+{
+	int cpu;
+	cpumask_t *mask = &event_data->mask;
+
+	for_each_cpu(cpu, mask) {
+		kfree(event_data->source_config[cpu]);
+		kfree(event_data->sink_config[cpu]);
+		kfree(event_data->sink[cpu]);
+	}
+
+	kfree(event_data->source_config);
+	kfree(event_data->sink_config);
+	kfree(event_data->sink);
+	kfree(event_data);
+}
+
+static void *etm_setup_aux(struct perf_event *event, void **pages,
+			   int nr_pages, bool overwrite)
+{
+	int cpu;
+	cpumask_t *mask;
+	struct etm_event_data *event_data = NULL;
+	struct coresight_device *csdev;
+
+	event_data = alloc_event_data(event->cpu);
+	if (!event_data)
+		return NULL;
+
+	mask = &event_data->mask;
+
+	if (event->cpu != -1)
+		cpumask_set_cpu(event->cpu, mask);
+	else
+		cpumask_copy(mask, cpu_online_mask);
+
+	for_each_cpu(cpu, mask) {
+		struct coresight_device *sink;
+
+		csdev = per_cpu(csdev_src, cpu);
+		if (!csdev)
+			goto err;
+
+		/* Get the tracer's config from perf */
+		if (!source_ops(csdev)->perf_get_config)
+			goto err;
+
+		event_data->source_config[cpu] =
+			source_ops(csdev)->perf_get_config(csdev, event);
+
+		if (!event_data->source_config[cpu])
+			goto err;
+
+		/*
+		 * Get a handle on the sink buffer associated
+		 * with this tracer.
+		 */
+		event_data->sink[cpu] = (void *)etm_event_build_path(cpu, true);
+
+		if (!event_data->sink[cpu])
+			goto err;
+
+		sink = event_data->sink[cpu];
+
+		if (!sink_ops(sink)->setup_aux)
+			goto err;
+
+		/* Finally get the AUX specific data from the sink buffer */
+		event_data->sink_config[cpu] =
+				sink_ops(sink)->setup_aux(sink, cpu, pages,
+							  nr_pages, overwrite);
+		if (!event_data->sink_config[cpu])
+			goto err;
+	}
+
+out:
+	return event_data;
+
+err:
+	for_each_cpu(cpu, mask) {
+		etm_event_build_path(cpu, false);
+	}
+
+	free_event_data(event_data);
+	event_data = NULL;
+	goto out;
+}
+
+static void etm_free_aux(void *data)
+{
+	free_event_data(data);
+}
+
+static void etm_event_stop(struct perf_event *event, int mode)
+{
+	int cpu = smp_processor_id();
+	struct coresight_device *csdev = per_cpu(csdev_src, cpu);
+
+	if (event->hw.state == PERF_HES_STOPPED)
+		return;
+
+	if (!csdev)
+		return;
+
+	/* stop tracer */
+	if (!source_ops(csdev)->perf_disable)
+		return;
+
+	if (source_ops(csdev)->perf_disable(csdev))
+		return;
+
+	/* tell the core */
+	event->hw.state = PERF_HES_STOPPED;
+
+
+	if (mode & PERF_EF_UPDATE) {
+		struct coresight_device *sink;
+		struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
+		struct etm_event_data *event_data = perf_get_aux(handle);
+
+		if (WARN_ON_ONCE(handle->event != event))
+			return;
+
+		if (WARN_ON_ONCE(!event_data))
+			return;
+
+		sink = event_data->sink[cpu];
+		if (WARN_ON_ONCE(!sink))
+			return;
+
+		/* update trace information */
+		if (!sink_ops(sink)->update_buffer)
+			return;
+
+		sink_ops(sink)->update_buffer(sink, handle,
+					      event_data->sink_config[cpu]);
+	}
+}
+
+static void etm_event_start(struct perf_event *event, int flags)
+{
+	int cpu = smp_processor_id();
+	struct coresight_device *csdev = per_cpu(csdev_src, cpu);
+
+	if (!csdev)
+		goto fail;
+
+	/* tell the perf core the event is alive */
+	event->hw.state = 0;
+
+	if (!source_ops(csdev)->perf_enable)
+		goto fail;
+
+	if (source_ops(csdev)->perf_enable(csdev))
+		goto fail;
+
+	return;
+
+fail:
+	event->hw.state = PERF_HES_STOPPED;
+}
+
+static void etm_event_del(struct perf_event *event, int mode)
+{
+	int cpu = smp_processor_id();
+	struct coresight_device *sink;
+	struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
+	struct etm_event_data *event_data = perf_get_aux(handle);
+
+	if (WARN_ON_ONCE(!event_data))
+		return;
+
+	sink = event_data->sink[cpu];
+	if (!sink)
+		return;
+
+	etm_event_stop(event, PERF_EF_UPDATE);
+
+	if (!sink_ops(sink)->reset_buffer)
+		return;
+
+	sink_ops(sink)->reset_buffer(sink, handle,
+				     event_data->sink_config[cpu]);
+}
+
+static int etm_event_add(struct perf_event *event, int mode)
+{
+
+	int ret = -EBUSY, cpu = smp_processor_id();
+	struct etm_event_data *event_data;
+	struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
+	struct hw_perf_event *hwc = &event->hw;
+	struct coresight_device *csdev = per_cpu(csdev_src, cpu);
+	struct coresight_device *sink;
+
+	if (handle->event)
+		goto out;
+
+	event_data = perf_aux_output_begin(handle, event);
+	ret = -EINVAL;
+	if (WARN_ON_ONCE(!event_data))
+		goto fail_stop;
+
+	sink = event_data->sink[cpu];
+	if (!sink)
+		goto fail_end_stop;
+
+	if (!sink_ops(sink)->set_buffer)
+		goto fail_end_stop;
+
+	ret = sink_ops(sink)->set_buffer(sink, handle,
+					 event_data->sink_config[cpu]);
+	if (ret)
+		goto fail_end_stop;
+
+	if (!source_ops(csdev)->perf_set_config) {
+		ret = -EINVAL;
+		goto fail_end_stop;
+	}
+
+	source_ops(csdev)->perf_set_config(csdev,
+					   event_data->source_config[cpu]);
+
+	if (mode & PERF_EF_START) {
+		etm_event_start(event, 0);
+		if (hwc->state & PERF_HES_STOPPED) {
+			etm_event_del(event, 0);
+			return -EBUSY;
+		}
+	}
+
+out:
+	return ret;
+
+fail_end_stop:
+	perf_aux_output_end(handle, 0, true);
+fail_stop:
+	hwc->state = PERF_HES_STOPPED;
+	goto out;
+}
+
+int etm_perf_symlink(struct coresight_device *csdev, bool link)
+{
+	char entry[sizeof("cpu9999999")];
+	int ret = 0, cpu = source_ops(csdev)->cpu_id(csdev);
+	struct device *pmu_dev = etm_pmu.dev;
+	struct device *cs_dev = &csdev->dev;
+
+	sprintf(entry, "cpu%d", cpu);
+
+	if (!etm_perf_up)
+		return -EPROBE_DEFER;
+
+	if (link) {
+		ret = sysfs_create_link(&pmu_dev->kobj, &cs_dev->kobj, entry);
+		if (ret)
+			return ret;
+		per_cpu(csdev_src, cpu) = csdev;
+	} else {
+		sysfs_remove_link(&pmu_dev->kobj, entry);
+		per_cpu(csdev_src, cpu) = NULL;
+	}
+
+	return 0;
+}
+
+static int __init etm_perf_init(void)
+{
+	int ret;
+
+	etm_pmu.capabilities	= PERF_PMU_CAP_EXCLUSIVE;
+
+	etm_pmu.attr_groups	= etm_pmu_attr_groups;
+	etm_pmu.task_ctx_nr	= perf_sw_context;
+	etm_pmu.read		= etm_event_read;
+	etm_pmu.event_init	= etm_event_init;
+	etm_pmu.setup_aux	= etm_setup_aux;
+	etm_pmu.free_aux	= etm_free_aux;
+	etm_pmu.stop		= etm_event_stop;
+	etm_pmu.start		= etm_event_start;
+	etm_pmu.del		= etm_event_del;
+	etm_pmu.add		= etm_event_add;
+
+	ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1);
+	if (ret == 0)
+		etm_perf_up = true;
+
+	return ret;
+}
+module_init(etm_perf_init);
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h
new file mode 100644
index 000000000000..4dd900f2362a
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2014-2015, 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.
+ */
+
+#ifndef _CORESIGHT_ETM_PERF_H
+#define _CORESIGHT_ETM_PERF_H
+
+struct coresight_device;
+
+#ifdef CONFIG_CORESIGHT
+int etm_perf_symlink(struct coresight_device *csdev, bool link);
+
+#else
+static inline int etm_perf_symlink(struct coresight_device *csdev, bool link)
+{ return -EINVAL; }
+
+#endif /* CONFIG_CORESIGHT */
+
+#endif
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 9b4c0359ca29..7407c7ecf668 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -35,6 +35,7 @@
 #include <asm/sections.h>
 
 #include "coresight-etm.h"
+#include "coresight-etm-perf.h"
 
 static int boot_enable;
 module_param_named(boot_enable, boot_enable, int, S_IRUGO);
@@ -850,6 +851,12 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
 		goto err_arch_supported;
 	}
 
+	ret = etm_perf_symlink(drvdata->csdev, true);
+	if (ret) {
+		coresight_unregister(drvdata->csdev);
+		goto err_arch_supported;
+	}
+
 	pm_runtime_put(&adev->dev);
 	dev_info(dev, "%s initialized\n", (char *)id->data);
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 23/30] coresight: updating documentation to reflect integration with perf
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (21 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 22/30] coresight: etm-perf: new PMU driver for ETM tracers Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 24/30] perf tools: making function set_max_cpu_num() non static Mathieu Poirier
                   ` (6 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Adding a new section giving information on how coresight has been
integrated with the perf subsystem along with a general idea of how
to control tracing from the perf tool cmd line.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 Documentation/trace/coresight.txt | 138 ++++++++++++++++++++++++++++++++++----
 1 file changed, 124 insertions(+), 14 deletions(-)

diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt
index 0a5c3290e732..9515206b4b1a 100644
--- a/Documentation/trace/coresight.txt
+++ b/Documentation/trace/coresight.txt
@@ -20,13 +20,13 @@ Components are generally categorised as source, link and sinks and are
 
 "Sources" generate a compressed stream representing the processor instruction
 path based on tracing scenarios as configured by users.  From there the stream
-flows through the coresight system (via ATB bus) using links that are connecting
-the emanating source to a sink(s).  Sinks serve as endpoints to the coresight
+flows through the Coresight system (via ATB bus) using links that are connecting
+the emanating source to a sink(s).  Sinks serve as endpoints to the Coresight
 implementation, either storing the compressed stream in a memory buffer or
 creating an interface to the outside world where data can be transferred to a
-host without fear of filling up the onboard coresight memory buffer.
+host without fear of filling up the onboard Coresight memory buffer.
 
-At typical coresight system would look like this:
+At typical Coresight system would look like this:
 
   *****************************************************************
  **************************** AMBA AXI  ****************************===||
@@ -83,8 +83,8 @@ While on target configuration of the components is done via the APB bus,
 all trace data are carried out-of-band on the ATB bus.  The CTM provides
 a way to aggregate and distribute signals between CoreSight components.
 
-The coresight framework provides a central point to represent, configure and
-manage coresight devices on a platform.  This first implementation centers on
+The Coresight framework provides a central point to represent, configure and
+manage Coresight devices on a platform.  This first implementation centers on
 the basic tracing functionality, enabling components such ETM/PTM, funnel,
 replicator, TMC, TPIU and ETB.  Future work will enable more
 intricate IP blocks such as STM and CTI.
@@ -129,11 +129,11 @@ expected to be added as the solution matures.
 Framework and implementation
 ----------------------------
 
-The coresight framework provides a central point to represent, configure and
-manage coresight devices on a platform.  Any coresight compliant device can
+The Coresight framework provides a central point to represent, configure and
+manage Coresight devices on a platform.  Any Coresight compliant device can
 register with the framework for as long as they use the right APIs:
 
-struct coresight_device *coresight_register(struct coresight_desc *desc);
+struct Coresight_device *coresight_register(struct coresight_desc *desc);
 void coresight_unregister(struct coresight_device *csdev);
 
 The registering function is taking a "struct coresight_device *csdev" and
@@ -193,10 +193,120 @@ the information carried in "THIS_MODULE".
 How to use
 ----------
 
-Before trace collection can start, a coresight sink needs to be identify.
-There is no limit on the amount of sinks (nor sources) that can be enabled at
-any given moment.  As a generic operation, all device pertaining to the sink
-class will have an "active" entry in sysfs:
+There is two ways to use the Coresight framework: 1) using the perf cmd line
+tool and 2) interacting directly with the Coresight devices using the sysFS
+interface.  The latter will slowly be faded out as more functionality become
+available from the perf cmd line tool but for the time being both are still
+supported.  The following sections provide details on using both methods.
+
+1) Using perf framework:
+
+Coresight tracers like ETM and PTM are represented using the Perf framework's
+Performance Monitoring Unit (PMU).  As such the perf framework takes charge of
+controlling when tracing happens based on when the process(es) of interest are
+scheduled.  When configure in a system, Coresight PMUs will be listed when
+queried by the perf command line tool:
+
+linaro at linaro-nano:~$ ./perf list pmu
+
+List of pre-defined events (to be used in -e):
+
+  cs_etm//                                           [Kernel PMU event]
+
+linaro at linaro-nano:~$
+
+Regardless of the amount ETM/PTM IP block in a system (usually equal to the
+amount of processor core), the "cs_etm" PMU will be listed only once.
+
+Before a trace can be configured and started a Coresight sink needs to be
+selected using the sysFS method (see below).  This is only temporary until
+sink selection can be made from the command line tool.
+
+linaro at linaro-nano:~$ ls /sys/bus/coresight/devices
+20010000.etb  20030000.tpiu  20040000.funnel  2201c000.ptm
+2201d000.ptm  2203c000.etm  2203d000.etm  2203e000.etm  replicator
+
+linaro at linaro-nano:~$ echo 1 > /sys/bus/coresight/devices/20010000.etb/enable_sink
+
+Once a sink has been selected configuring a Coresight PMU works the same way as
+any other PMU.  As such tracing can happen for a single CPU, a group of CPU, per
+thread or a combination of those:
+
+linaro at linaro-nano:~$ perf record -e cs_etm// --per-thread <command>
+
+linaro at linaro-nano:~$ perf record -C 0,2-3 -e cs_etm// <command>
+
+Tracing limited to user and kernel space can also be used to narrow the amount
+of collected traces:
+
+linaro at linaro-nano:~$ perf record -e cs_etm//u --per-thread <command>
+
+linaro at linaro-nano:~$ perf record -C 0,2-3 -e cs_etm//k <command>
+
+As of this writing two ETM/PTM specific options have are available: cycle
+accurate and timestamp (please refer to the Embedded Trace Macrocell reference
+manual for details on these options).  By default both are disabled but using
+the "cycacc" and "timestamp" mnemonic within the double '/' will see those
+options configure for the upcoming trace run:
+
+linaro at linaro-nano:~$ perf record -e cs_etm/cycacc/ --per-thread <command>
+
+linaro at linaro-nano:~$ perf record -C 0,2-3 -e cs_etm/cycacc,timestamp/ <command>
+
+The Coresight PMUs can be configured to work in "full trace" or "snapshot" mode.
+In full trace mode trace acquisition is enabled from beginning to end with trace
+data being recorded continuously:
+
+linaro at linaro-nano:~$ perf record -e cs_etm// dd if=/dev/random of=./test.txt bs=1k count=1000
+
+Since this can lead to a significant amount of data and because some devices are
+limited in disk space snapshot mode can be used instead.  In snapshot mode
+traces are still collected in the ring buffer but not communicated to user
+space.  The ring buffer is allowed to wrap around, providing the latest
+information before an event of interest happens.  Significant events are
+communicated by sending a USR2 signal to the user space command line tool.
+From there the tool will stop trace collection and harvest data from the ring
+buffer before re-enabling traces.  Snapshot mode can be invoked using '-S' when
+launching a trace collection:
+
+linaro at linaro-nano:~$ perf record -S -e cs_etm// dd if=/dev/random of=./test.txt bs=1k count=1000
+
+Trace data collected during trace runs ends up in the "perf.data" file.  Trace
+configuration information necessary for trace decoding is also embedded in the
+"perf.data" file.  Two new headers, 'PERF_RECORD_AUXTRACE_INFO' and
+'PERF_RECORD_AUXTRACE' have been added to the list of event types in order to
+find out where the different sections start.
+
+It is worth noting that a set of metadata information exists for each tracer
+that participated in a trace run.  As such if 5 processors have been engaged,
+5 sets of metadata will be found in the perf.data file.  This is to ensure that
+tracer decompression tools have all the information they need in order to
+process the trace data.
+
+Metadata information is collected directly from the ETM/PTM management registers
+using the sysFS interface.  Since there is no way for the perf command line
+tool to associate a CPU with a tracer, a symbolic link has been created between
+the cs_etm sysFS event directory and each Coresight tracer:
+
+linaro at linaro-nano:~$ ls /sys/bus/event_source/devices/cs_etm
+cpu0  cpu1  cpu2  cpu3  cpu4  format  perf_event_mux_interval_ms
+power  subsystem  type  uevent
+
+linaro at linaro-nano:~$ ls /sys/bus/event_source/devices/cs_etm/cpu0/mgmt/
+etmccer  etmccr  etmcr  etmidr  etmscr  etmtecr1  etmtecr2
+etmteevr  etmtraceidr  etmtssvr
+
+2) Using the sysFS interface:
+
+Most, if not all, configuration registers are made available to users via the
+sysFS interface.  Until all Coresight ETM drivers have been converted to perf,
+it will also be possible to start and stop traces from sysFS.
+
+As with the perf method described above, a Coresight sink needs to be identify
+before trace collection can commence.  Using the sysFS method _only_, there is
+no limit on the amount of sinks (nor sources) that can be enabled at
+any given moment.  As a generic operation, all devices pertaining to the sink
+class will have an "enable_sink" entry in sysfs:
 
 root:/sys/bus/coresight/devices# ls
 replicator  20030000.tpiu    2201c000.ptm  2203c000.etm  2203e000.etm
@@ -246,7 +356,7 @@ The file cstrace.bin can be decompressed using "ptm2human", DS-5 or Trace32.
 
 Following is a DS-5 output of an experimental loop that increments a variable up
 to a certain value.  The example is simple and yet provides a glimpse of the
-wealth of possibilities that coresight provides.
+wealth of possibilities that Coresight provides.
 
 Info                                    Tracing enabled
 Instruction     106378866       0x8026B53C      E52DE004        false   PUSH     {lr}
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 24/30] perf tools: making function set_max_cpu_num() non static
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (22 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 23/30] coresight: updating documentation to reflect integration with perf Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 25/30] perf tools: adding perf_session to *info_prive_size() Mathieu Poirier
                   ` (5 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

For memory allocation purpuses, code located in other places
then util/cpumap.c may want to know how many CPUs the system has.

This patch is making function set_max_cpu_num() available to
other parts of the perf tool so that global variable
@max_cpu_num gets the right value when referenced by cpu__max_cpu().

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 tools/perf/util/cpumap.c | 2 +-
 tools/perf/util/cpumap.h | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 3667e2123e5b..97ef46e4a0f6 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -373,7 +373,7 @@ out:
 }
 
 /* Determine highest possible cpu in the system for sparse allocation */
-static void set_max_cpu_num(void)
+void set_max_cpu_num(void)
 {
 	const char *mnt;
 	char path[PATH_MAX];
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 0af9cecb4c51..6f7dce7dcca6 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -14,6 +14,7 @@ struct cpu_map {
 	int map[];
 };
 
+void set_max_cpu_num(void);
 struct cpu_map *cpu_map__new(const char *cpu_list);
 struct cpu_map *cpu_map__dummy_new(void);
 struct cpu_map *cpu_map__read(FILE *file);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 25/30] perf tools: adding perf_session to *info_prive_size()
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (23 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 24/30] perf tools: making function set_max_cpu_num() non static Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 26/30] perf tools: making source devices path broadly accessible Mathieu Poirier
                   ` (4 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

On some architecture the size of the private header may
be dependent on the number of tracers used in the session.  As
such adding a "struct perf_session" parameter, which should
contain all the required information.

Also adjusting the existing client of the interface to take
the new parameter into account.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 tools/perf/arch/x86/util/intel-bts.c | 4 +++-
 tools/perf/arch/x86/util/intel-pt.c  | 4 +++-
 tools/perf/util/auxtrace.c           | 7 ++++---
 tools/perf/util/auxtrace.h           | 6 ++++--
 4 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
index 9b94ce520917..6efdc5e86d97 100644
--- a/tools/perf/arch/x86/util/intel-bts.c
+++ b/tools/perf/arch/x86/util/intel-bts.c
@@ -60,7 +60,9 @@ struct branch {
 	u64 misc;
 };
 
-static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused)
+static size_t
+intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
+			 struct perf_session *session __maybe_unused)
 {
 	return INTEL_BTS_AUXTRACE_PRIV_SIZE;
 }
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
index 2ca10d796c0b..1fd9a16f668c 100644
--- a/tools/perf/arch/x86/util/intel-pt.c
+++ b/tools/perf/arch/x86/util/intel-pt.c
@@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
 	return attr;
 }
 
-static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused)
+static size_t
+intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused,
+			struct perf_session *session __maybe_unused)
 {
 	return INTEL_PT_AUXTRACE_PRIV_SIZE;
 }
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index a980e7c50ee0..430df9575dbe 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
 			 heap_array[last].ordinal);
 }
 
-size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
+size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
+				       struct perf_session *session)
 {
 	if (itr)
-		return itr->info_priv_size(itr);
+		return itr->info_priv_size(itr, session);
 	return 0;
 }
 
@@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
 	int err;
 
 	pr_debug2("Synthesizing auxtrace information\n");
-	priv_size = auxtrace_record__info_priv_size(itr);
+	priv_size = auxtrace_record__info_priv_size(itr, session);
 	ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
 	if (!ev)
 		return -ENOMEM;
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index bf72b77a588a..d76177169484 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -289,7 +289,8 @@ struct auxtrace_record {
 	int (*recording_options)(struct auxtrace_record *itr,
 				 struct perf_evlist *evlist,
 				 struct record_opts *opts);
-	size_t (*info_priv_size)(struct auxtrace_record *itr);
+	size_t (*info_priv_size)(struct auxtrace_record *itr,
+				 struct perf_session *session);
 	int (*info_fill)(struct auxtrace_record *itr,
 			 struct perf_session *session,
 			 struct auxtrace_info_event *auxtrace_info,
@@ -425,7 +426,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
 int auxtrace_record__options(struct auxtrace_record *itr,
 			     struct perf_evlist *evlist,
 			     struct record_opts *opts);
-size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
+size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
+				       struct perf_session *session);
 int auxtrace_record__info_fill(struct auxtrace_record *itr,
 			       struct perf_session *session,
 			       struct auxtrace_info_event *auxtrace_info,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 26/30] perf tools: making source devices path broadly accessible
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (24 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 25/30] perf tools: adding perf_session to *info_prive_size() Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 27/30] perf build: adding X86 auxiliary specific flags Mathieu Poirier
                   ` (3 subsequent siblings)
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Other perf tools may want access to the event source device
directory.  As such moving the path definition to pmu.h for
easy inclusion by other clients.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 tools/perf/util/pmu.c | 2 --
 tools/perf/util/pmu.h | 1 +
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 89c91a1a67e7..e1f2dc23a253 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -20,8 +20,6 @@ struct perf_pmu_format {
 	struct list_head list;
 };
 
-#define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
-
 int perf_pmu_parse(struct list_head *list, char *name);
 extern FILE *perf_pmu_in;
 
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 5d7e84466bee..3fbb791a955a 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -35,6 +35,7 @@ struct perf_pmu_info {
 };
 
 #define UNIT_MAX_LEN	31 /* max length for event unit name */
+#define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
 
 struct perf_pmu_alias {
 	char *name;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 27/30] perf build: adding X86 auxiliary specific flags
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (25 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 26/30] perf tools: making source devices path broadly accessible Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-19 10:40   ` Adrian Hunter
  2015-10-18 18:24 ` [PATCH V2 28/30] perf tools: making coresight PMU listable Mathieu Poirier
                   ` (2 subsequent siblings)
  29 siblings, 1 reply; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Adding an X86 specific flag to split AUX components that are
generic and specific to architectures.  That way the auxiliary
area mechanic can be compiled in for other architecture without
including X86 specific code.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 tools/perf/arch/x86/util/Build         |  6 +++---
 tools/perf/arch/x86/util/pmu.c         |  2 +-
 tools/perf/builtin-inject.c            |  2 +-
 tools/perf/builtin-record.c            |  2 +-
 tools/perf/config/Makefile             |  3 ++-
 tools/perf/util/Build                  |  6 +++---
 tools/perf/util/auxtrace.h             |  2 +-
 tools/perf/util/intel-bts.h            | 11 +++++++++++
 tools/perf/util/intel-pt-decoder/Build |  2 +-
 tools/perf/util/intel-pt.h             | 15 +++++++++++++++
 10 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
index ff63649fa9ac..f6b1ba8b6aad 100644
--- a/tools/perf/arch/x86/util/Build
+++ b/tools/perf/arch/x86/util/Build
@@ -9,6 +9,6 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
 libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
 libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
 
-libperf-$(CONFIG_AUXTRACE) += auxtrace.o
-libperf-$(CONFIG_AUXTRACE) += intel-pt.o
-libperf-$(CONFIG_AUXTRACE) += intel-bts.o
+libperf-$(CONFIG_AUXTRACE_X86) += auxtrace.o
+libperf-$(CONFIG_AUXTRACE_X86) += intel-pt.o
+libperf-$(CONFIG_AUXTRACE_X86) += intel-bts.o
diff --git a/tools/perf/arch/x86/util/pmu.c b/tools/perf/arch/x86/util/pmu.c
index 79fe07158d00..eef512b9a1bf 100644
--- a/tools/perf/arch/x86/util/pmu.c
+++ b/tools/perf/arch/x86/util/pmu.c
@@ -8,7 +8,7 @@
 
 struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
 {
-#ifdef HAVE_AUXTRACE_SUPPORT
+#ifdef HAVE_AUXTRACE_SUPPORT_X86
 	if (!strcmp(pmu->name, INTEL_PT_PMU_NAME))
 		return intel_pt_pmu_default_config(pmu);
 	if (!strcmp(pmu->name, INTEL_BTS_PMU_NAME))
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index f62c49b35be0..c19e034b4023 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -95,7 +95,7 @@ static int perf_event__repipe_attr(struct perf_tool *tool,
 	return perf_event__repipe_synth(tool, event);
 }
 
-#ifdef HAVE_AUXTRACE_SUPPORT
+#ifdef HAVE_AUXTRACE_SUPPORT_X86
 
 static int copy_bytes(struct perf_inject *inject, int fd, off_t size)
 {
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 142eeb341b29..39cbbdb07891 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -141,7 +141,7 @@ static void record__sig_exit(void)
 	raise(signr);
 }
 
-#ifdef HAVE_AUXTRACE_SUPPORT
+#ifdef HAVE_AUXTRACE_SUPPORT_X86
 
 static int record__process_auxtrace(struct perf_tool *tool,
 				    union perf_event *event, void *data1,
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 38a08539f4bf..5fd4843c691d 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -631,7 +631,8 @@ ifndef NO_AUXTRACE
     NO_AUXTRACE := 1
   else
     $(call detected,CONFIG_AUXTRACE)
-    CFLAGS += -DHAVE_AUXTRACE_SUPPORT
+    $(call detected,CONFIG_AUXTRACE_X86)
+    CFLAGS += -DHAVE_AUXTRACE_SUPPORT_X86
   endif
 endif
 
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index e5f18a288b74..448d83b00925 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -80,9 +80,9 @@ libperf-$(CONFIG_AUXTRACE) += tsc.o
 libperf-y += cloexec.o
 libperf-y += thread-stack.o
 libperf-$(CONFIG_AUXTRACE) += auxtrace.o
-libperf-$(CONFIG_AUXTRACE) += intel-pt-decoder/
-libperf-$(CONFIG_AUXTRACE) += intel-pt.o
-libperf-$(CONFIG_AUXTRACE) += intel-bts.o
+libperf-$(CONFIG_AUXTRACE_X86) += intel-pt-decoder/
+libperf-$(CONFIG_AUXTRACE_X86) += intel-pt.o
+libperf-$(CONFIG_AUXTRACE_X86) += intel-bts.o
 libperf-y += parse-branch-options.o
 libperf-y += parse-regs-options.o
 
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index d76177169484..b9ac99fb5a17 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -309,7 +309,7 @@ struct auxtrace_record {
 	unsigned int alignment;
 };
 
-#ifdef HAVE_AUXTRACE_SUPPORT
+#ifdef HAVE_AUXTRACE_SUPPORT_X86
 
 /*
  * In snapshot mode the mmapped page is read-only which makes using
diff --git a/tools/perf/util/intel-bts.h b/tools/perf/util/intel-bts.h
index ca65e21b3e83..1ef679a9e9c2 100644
--- a/tools/perf/util/intel-bts.h
+++ b/tools/perf/util/intel-bts.h
@@ -35,9 +35,20 @@ struct perf_tool;
 union perf_event;
 struct perf_session;
 
+#ifdef HAVE_AUXTRACE_SUPPORT_X86
 struct auxtrace_record *intel_bts_recording_init(int *err);
 
 int intel_bts_process_auxtrace_info(union perf_event *event,
 				    struct perf_session *session);
+#else
+static inline
+struct auxtrace_record *intel_bts_recording_init(int *err __maybe_unused)
+{ return NULL; }
+
+static inline int
+intel_bts_process_auxtrace_info(union perf_event *event __maybe_unused,
+				struct perf_session *session __maybe_unused)
+{ return -EINVAL; }
+#endif
 
 #endif
diff --git a/tools/perf/util/intel-pt-decoder/Build b/tools/perf/util/intel-pt-decoder/Build
index 2386322ece4f..76437e99c659 100644
--- a/tools/perf/util/intel-pt-decoder/Build
+++ b/tools/perf/util/intel-pt-decoder/Build
@@ -1,4 +1,4 @@
-libperf-$(CONFIG_AUXTRACE) += intel-pt-pkt-decoder.o intel-pt-insn-decoder.o intel-pt-log.o intel-pt-decoder.o
+libperf-$(CONFIG_AUXTRACE_X86) += intel-pt-pkt-decoder.o intel-pt-insn-decoder.o intel-pt-log.o intel-pt-decoder.o
 
 inat_tables_script = util/intel-pt-decoder/gen-insn-attr-x86.awk
 inat_tables_maps = util/intel-pt-decoder/x86-opcode-map.txt
diff --git a/tools/perf/util/intel-pt.h b/tools/perf/util/intel-pt.h
index 0065949df693..94dd268718f6 100644
--- a/tools/perf/util/intel-pt.h
+++ b/tools/perf/util/intel-pt.h
@@ -46,11 +46,26 @@ struct perf_session;
 struct perf_event_attr;
 struct perf_pmu;
 
+#ifdef HAVE_AUXTRACE_SUPPORT_X86
 struct auxtrace_record *intel_pt_recording_init(int *err);
 
 int intel_pt_process_auxtrace_info(union perf_event *event,
 				   struct perf_session *session);
 
 struct perf_event_attr *intel_pt_pmu_default_config(struct perf_pmu *pmu);
+#else
+static inline
+struct auxtrace_record *intel_pt_recording_init(int *err __maybe_unused)
+{ return NULL; }
+
+static inline
+int intel_pt_process_auxtrace_info(union perf_event *event __maybe_unused,
+				   struct perf_session *session __maybe_unused)
+{ return -EINVAL; }
+
+static inline struct perf_event_attr
+*intel_pt_pmu_default_config(struct perf_pmu *pmu __maybe_unused)
+{ return NULL; }
+#endif
 
 #endif
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 28/30] perf tools: making coresight PMU listable
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (26 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 27/30] perf build: adding X86 auxiliary specific flags Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 29/30] perf tools: adding coresight define for auxtrace Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 30/30] perf tools: adding coresight etm PMU record capabilities Mathieu Poirier
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Adding the required mechanic allowing 'perf list pmu' to
discover coresight ETM/PTM tracers.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 tools/perf/arch/arm/util/Build |  2 ++
 tools/perf/arch/arm/util/pmu.c | 18 ++++++++++++++++++
 tools/perf/builtin-inject.c    |  2 +-
 tools/perf/builtin-record.c    |  2 +-
 tools/perf/config/Makefile     | 20 ++++++++++++++------
 tools/perf/util/auxtrace.h     |  2 +-
 6 files changed, 37 insertions(+), 9 deletions(-)
 create mode 100644 tools/perf/arch/arm/util/pmu.c

diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
index d22e3d07de3d..371a3bf12297 100644
--- a/tools/perf/arch/arm/util/Build
+++ b/tools/perf/arch/arm/util/Build
@@ -2,3 +2,5 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
 
 libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
 libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
+
+libperf-$(CONFIG_AUXTRACE_ARM) += pmu.o
diff --git a/tools/perf/arch/arm/util/pmu.c b/tools/perf/arch/arm/util/pmu.c
new file mode 100644
index 000000000000..2870a4e04e48
--- /dev/null
+++ b/tools/perf/arch/arm/util/pmu.c
@@ -0,0 +1,18 @@
+#include <string.h>
+
+#include <linux/coresight-pmu.h>
+#include <linux/perf_event.h>
+
+#include "../../util/pmu.h"
+
+struct perf_event_attr
+*perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
+{
+#ifdef HAVE_AUXTRACE_SUPPORT_ARM
+	if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) {
+		/* add ETM default config here */
+		pmu->selectable = true;
+	}
+#endif
+	return NULL;
+}
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index c19e034b4023..a567cd628156 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -95,7 +95,7 @@ static int perf_event__repipe_attr(struct perf_tool *tool,
 	return perf_event__repipe_synth(tool, event);
 }
 
-#ifdef HAVE_AUXTRACE_SUPPORT_X86
+#if defined(HAVE_AUXTRACE_SUPPORT_X86) || defined(HAVE_AUXTRACE_SUPPORT_ARM)
 
 static int copy_bytes(struct perf_inject *inject, int fd, off_t size)
 {
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 39cbbdb07891..f37f890f676e 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -141,7 +141,7 @@ static void record__sig_exit(void)
 	raise(signr);
 }
 
-#ifdef HAVE_AUXTRACE_SUPPORT_X86
+#if defined(HAVE_AUXTRACE_SUPPORT_X86) || defined(HAVE_AUXTRACE_SUPPORT_ARM)
 
 static int record__process_auxtrace(struct perf_tool *tool,
 				    union perf_event *event, void *data1,
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 5fd4843c691d..62fa7596b507 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -626,13 +626,21 @@ ifdef LIBBABELTRACE
 endif
 
 ifndef NO_AUXTRACE
-  ifeq ($(feature-get_cpuid), 0)
-    msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
-    NO_AUXTRACE := 1
+  ifeq ($(ARCH),x86)
+    ifeq ($(feature-get_cpuid), 0)
+      msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
+      NO_AUXTRACE := 1
+    else
+      $(call detected,CONFIG_AUXTRACE)
+      $(call detected,CONFIG_AUXTRACE_X86)
+      CFLAGS += -DHAVE_AUXTRACE_SUPPORT_X86
+    endif
   else
-    $(call detected,CONFIG_AUXTRACE)
-    $(call detected,CONFIG_AUXTRACE_X86)
-    CFLAGS += -DHAVE_AUXTRACE_SUPPORT_X86
+    ifeq ($(ARCH),$(filter $(ARCH), arm arm64))
+      $(call detected,CONFIG_AUXTRACE)
+      $(call detected,CONFIG_AUXTRACE_ARM)
+      CFLAGS += -DHAVE_AUXTRACE_SUPPORT_ARM
+    endif
   endif
 endif
 
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index b9ac99fb5a17..ed1c940be883 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -309,7 +309,7 @@ struct auxtrace_record {
 	unsigned int alignment;
 };
 
-#ifdef HAVE_AUXTRACE_SUPPORT_X86
+#if defined(HAVE_AUXTRACE_SUPPORT_X86) || defined(HAVE_AUXTRACE_SUPPORT_ARM)
 
 /*
  * In snapshot mode the mmapped page is read-only which makes using
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 29/30] perf tools: adding coresight define for auxtrace
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (27 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 28/30] perf tools: making coresight PMU listable Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  2015-10-18 18:24 ` [PATCH V2 30/30] perf tools: adding coresight etm PMU record capabilities Mathieu Poirier
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Adding new define for auxiliary traces collected by ETM tracers.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 tools/perf/util/auxtrace.c | 1 +
 tools/perf/util/auxtrace.h | 1 +
 2 files changed, 2 insertions(+)

diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 430df9575dbe..666773d4e942 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -892,6 +892,7 @@ int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
 		return intel_pt_process_auxtrace_info(event, session);
 	case PERF_AUXTRACE_INTEL_BTS:
 		return intel_bts_process_auxtrace_info(event, session);
+	case PERF_AUXTRACE_CS_ETM:
 	case PERF_AUXTRACE_UNKNOWN:
 	default:
 		return -EINVAL;
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index ed1c940be883..35569d25d1d3 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -41,6 +41,7 @@ enum auxtrace_type {
 	PERF_AUXTRACE_UNKNOWN,
 	PERF_AUXTRACE_INTEL_PT,
 	PERF_AUXTRACE_INTEL_BTS,
+	PERF_AUXTRACE_CS_ETM,
 };
 
 enum itrace_period_type {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 30/30] perf tools: adding coresight etm PMU record capabilities
  2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
                   ` (28 preceding siblings ...)
  2015-10-18 18:24 ` [PATCH V2 29/30] perf tools: adding coresight define for auxtrace Mathieu Poirier
@ 2015-10-18 18:24 ` Mathieu Poirier
  29 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-18 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Coresight ETMs are IP blocks allowing to perform HW assisted tracing
on a CPU core.  This patch introduce the required auxiliary API
functions allowing the perf core to interact with a tracer.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 tools/perf/arch/arm/util/Build      |   2 +-
 tools/perf/arch/arm/util/auxtrace.c |  48 ++++
 tools/perf/arch/arm/util/cs_etm.c   | 444 ++++++++++++++++++++++++++++++++++++
 tools/perf/arch/arm/util/cs_etm.h   |  37 +++
 4 files changed, 530 insertions(+), 1 deletion(-)
 create mode 100644 tools/perf/arch/arm/util/auxtrace.c
 create mode 100644 tools/perf/arch/arm/util/cs_etm.c
 create mode 100644 tools/perf/arch/arm/util/cs_etm.h

diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
index 371a3bf12297..87545604b029 100644
--- a/tools/perf/arch/arm/util/Build
+++ b/tools/perf/arch/arm/util/Build
@@ -3,4 +3,4 @@ libperf-$(CONFIG_DWARF) += dwarf-regs.o
 libperf-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
 libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
 
-libperf-$(CONFIG_AUXTRACE_ARM) += pmu.o
+libperf-$(CONFIG_AUXTRACE_ARM) += pmu.o auxtrace.o cs_etm.o
diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
new file mode 100644
index 000000000000..4988fdf7cb8a
--- /dev/null
+++ b/tools/perf/arch/arm/util/auxtrace.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdbool.h>
+#include <linux/coresight-pmu.h>
+
+#include "../../util/auxtrace.h"
+#include "../../util/evlist.h"
+#include "../../util/pmu.h"
+#include "cs_etm.h"
+
+struct auxtrace_record
+*auxtrace_record__init(struct perf_evlist *evlist, int *err)
+{
+	struct perf_pmu	*cs_etm_pmu;
+	struct perf_evsel *evsel;
+	bool found_etm = false;
+
+	cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
+
+	 if (evlist) {
+		evlist__for_each(evlist, evsel) {
+			if (cs_etm_pmu &&
+			    evsel->attr.type == cs_etm_pmu->type)
+				found_etm = true;
+		}
+	}
+
+	if (found_etm)
+		return cs_etm_record_init(err);
+
+	*err = -EINVAL;
+	return NULL;
+}
diff --git a/tools/perf/arch/arm/util/cs_etm.c b/tools/perf/arch/arm/util/cs_etm.c
new file mode 100644
index 000000000000..2e89b44dd4da
--- /dev/null
+++ b/tools/perf/arch/arm/util/cs_etm.c
@@ -0,0 +1,444 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <api/fs/fs.h>
+#include <linux/bitops.h>
+#include <linux/coresight-pmu.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/types.h>
+
+#include "../../perf.h"
+#include "../../util/auxtrace.h"
+#include "../../util/cpumap.h"
+#include "../../util/evlist.h"
+#include "../../util/pmu.h"
+#include "../../util/thread_map.h"
+#include "cs_etm.h"
+
+#include <stdlib.h>
+
+/* Also used by Intel - should go to a common header */
+#define KiB(x) ((x) * 1024)
+#define MiB(x) ((x) * 1024 * 1024)
+
+struct cs_etm_recording {
+	struct auxtrace_record	itr;
+	struct perf_pmu		*cs_etm_pmu;
+	struct perf_evlist	*evlist;
+	bool			snapshot_mode;
+	size_t			snapshot_size;
+};
+
+static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
+					 struct record_opts *opts,
+					 const char *str)
+{
+	struct cs_etm_recording *ptr =
+				container_of(itr, struct cs_etm_recording, itr);
+	unsigned long long snapshot_size = 0;
+	char *endptr;
+
+	if (str) {
+		snapshot_size = strtoull(str, &endptr, 0);
+		if (*endptr || snapshot_size > SIZE_MAX)
+			return -1;
+	}
+
+	opts->auxtrace_snapshot_mode = true;
+	opts->auxtrace_snapshot_size = snapshot_size;
+	ptr->snapshot_size = snapshot_size;
+
+	return 0;
+}
+
+static int cs_etm_recording_options(struct auxtrace_record *itr,
+				    struct perf_evlist *evlist,
+				    struct record_opts *opts)
+{
+	struct cs_etm_recording *ptr =
+				container_of(itr, struct cs_etm_recording, itr);
+	struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
+	struct perf_evsel *evsel, *cs_etm_evsel = NULL;
+	const struct cpu_map *cpus = evlist->cpus;
+	bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
+
+	ptr->evlist = evlist;
+	ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
+
+	evlist__for_each(evlist, evsel) {
+		if (evsel->attr.type == cs_etm_pmu->type) {
+			if (cs_etm_evsel) {
+				pr_err("There may be only one %s event\n",
+				       CORESIGHT_ETM_PMU_NAME);
+				return -EINVAL;
+			}
+			evsel->attr.freq = 0;
+			evsel->attr.sample_period = 1;
+			cs_etm_evsel = evsel;
+			opts->full_auxtrace = true;
+		}
+	}
+
+	/* no need to continue if at least one event of interest was found */
+	if (!cs_etm_evsel)
+		return 0;
+
+	if (opts->use_clockid) {
+		pr_err("Cannot use clockid (-k option) with %s\n",
+		       CORESIGHT_ETM_PMU_NAME);
+		return -EINVAL;
+	}
+
+	/* we are in snapshot mode */
+	if (opts->auxtrace_snapshot_mode) {
+		/*
+		 * No size were given to '-S' or '-m,', so go with
+		 * the default
+		 */
+		if (!opts->auxtrace_snapshot_size &&
+		    !opts->auxtrace_mmap_pages) {
+			if (privileged) {
+				opts->auxtrace_mmap_pages = MiB(4) / page_size;
+			} else {
+				opts->auxtrace_mmap_pages =
+							KiB(128) / page_size;
+				if (opts->mmap_pages == UINT_MAX)
+					opts->mmap_pages = KiB(256) / page_size;
+			}
+		} else if (!opts->auxtrace_mmap_pages && !privileged &&
+						opts->mmap_pages == UINT_MAX) {
+			opts->mmap_pages = KiB(256) / page_size;
+		}
+
+		/*
+		 * '-m,xyz' was specified but no snapshot size, so make the
+		 * snapshot size as big as the auxtrace mmap area.
+		 */
+		if (!opts->auxtrace_snapshot_size) {
+			opts->auxtrace_snapshot_size =
+				opts->auxtrace_mmap_pages * (size_t)page_size;
+		}
+
+		/*
+		 * -Sxyz was specified but no auxtrace mmap area, so make the
+		 * auxtrace mmap area big enough to fit the requested snapshot
+		 * size.
+		 */
+		if (!opts->auxtrace_mmap_pages) {
+			size_t sz = opts->auxtrace_snapshot_size;
+
+			sz = round_up(sz, page_size) / page_size;
+			opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
+		}
+
+		/* Snapshost size can't be bigger than the auxtrace area */
+		if (opts->auxtrace_snapshot_size >
+				opts->auxtrace_mmap_pages * (size_t)page_size) {
+			pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
+			       opts->auxtrace_snapshot_size,
+			       opts->auxtrace_mmap_pages * (size_t)page_size);
+			return -EINVAL;
+		}
+
+		/* Something went wrong somewhere - this shouldn't happen */
+		if (!opts->auxtrace_snapshot_size ||
+		    !opts->auxtrace_mmap_pages) {
+			pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
+			return -EINVAL;
+		}
+	}
+
+	/* We are in full trace mode but '-m,xyz' wasn't specified */
+	 if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
+		if (privileged) {
+			opts->auxtrace_mmap_pages = MiB(4) / page_size;
+		} else {
+			opts->auxtrace_mmap_pages = KiB(128) / page_size;
+			if (opts->mmap_pages == UINT_MAX)
+				opts->mmap_pages = KiB(256) / page_size;
+		}
+
+	}
+
+	/* Validate auxtrace_mmap_pages provided by user */
+	if (opts->auxtrace_mmap_pages) {
+		unsigned int max_page = (KiB(128) / page_size);
+		size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
+
+		if (!privileged &&
+		    opts->auxtrace_mmap_pages > max_page) {
+			opts->auxtrace_mmap_pages = max_page;
+			pr_err("auxtrace too big, truncating to %d\n",
+			       max_page);
+		}
+
+		if (!is_power_of_2(sz)) {
+			pr_err("Invalid mmap size for %s: must be a power of 2\n",
+			       CORESIGHT_ETM_PMU_NAME);
+			return -EINVAL;
+		}
+	}
+
+	if (opts->auxtrace_snapshot_mode)
+		pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME,
+			  opts->auxtrace_snapshot_size);
+
+	if (cs_etm_evsel) {
+		/*
+		 * To obtain the auxtrace buffer file descriptor, the auxtrace
+		 * event must come first.
+		 */
+		perf_evlist__to_front(evlist, cs_etm_evsel);
+		/*
+		 * In the case of per-cpu mmaps, we need the CPU on the
+		 * AUX event.
+		 */
+		if (!cpu_map__empty(cpus))
+			perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
+	}
+
+	/* Add dummy event to keep tracking */
+	if (opts->full_auxtrace) {
+		struct perf_evsel *tracking_evsel;
+		int err;
+
+		err = parse_events(evlist, "dummy:u", NULL);
+		if (err)
+			return err;
+
+		tracking_evsel = perf_evlist__last(evlist);
+		perf_evlist__set_tracking_event(evlist, tracking_evsel);
+
+		tracking_evsel->attr.freq = 0;
+		tracking_evsel->attr.sample_period = 1;
+
+		/* In per-cpu case, always need the time of mmap events etc */
+		if (!cpu_map__empty(cpus))
+			perf_evsel__set_sample_bit(tracking_evsel, TIME);
+	}
+
+	return 0;
+}
+
+static size_t
+cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
+		      struct perf_session *session __maybe_unused)
+{
+	int records;
+	const struct cpu_map *cpus = session->evlist->cpus;
+
+	if (!cpu_map__empty(cpus)) {
+		records = cpu_map__nr(cpus);
+		goto out;
+	}
+
+	set_max_cpu_num();
+	records = cpu__max_cpu();
+out:
+	return records * CS_ETM_PRIV_SIZE;
+}
+
+static const char *metadata[CS_ETM_PRIV_MAX] = {
+	[CS_ETM_ETMCR]		= "etmcr",
+	[CS_ETM_ETMCCER]	= "etmccer",
+	[CS_ETM_ETMIDR]		= "etmidr",
+	[CS_ETM_ETMTRACEIDR]	= "etmtraceidr",
+};
+
+static int cs_etm_get_metadata(int cpu, int offset,
+			       struct cs_etm_recording *ptr,
+			       struct auxtrace_info_event *info)
+{
+	int i, ret = 0;
+	const char *sysfs = sysfs__mountpoint();
+
+	if (!sysfs)
+		return -EINVAL;
+
+	info->priv[offset + CS_ETM_MAGIC] = __perf_cs_etm_magic;
+	info->priv[offset + CS_ETM_CPU] = cpu;
+	info->priv[offset + CS_ETM_SNAPSHOT] = ptr->snapshot_mode;
+
+	/* get metadata from sysfs */
+	for (i = CS_ETM_ETMCR; i < CS_ETM_PRIV_MAX && !ret; i++) {
+		FILE *file;
+		char path[PATH_MAX], value[128];
+
+		snprintf(path, PATH_MAX,
+			 "%s" EVENT_SOURCE_DEVICE_PATH "%s/cpu%d/mgmt/%s",
+			 sysfs, CORESIGHT_ETM_PMU_NAME, cpu, metadata[i]);
+
+		file = fopen(path, "r");
+		if (!file)
+			return -EINVAL;
+
+		if (fscanf(file, "%s", value) != 1)
+			ret = -EINVAL;
+
+		fclose(file);
+
+		info->priv[offset + i] = strtol(value, NULL, 16);
+	}
+
+	return 0;
+}
+
+static int cs_etm_info_fill(struct auxtrace_record *itr,
+			    struct perf_session *session,
+			    struct auxtrace_info_event *auxtrace_info,
+			    size_t priv_size)
+{
+	int i, nr_cpu, ret = 0;
+	const struct cpu_map *cpus = session->evlist->cpus;
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+
+	if (priv_size != cs_etm_info_priv_size(itr, session))
+		return -EINVAL;
+
+	if (!session->evlist->nr_mmaps)
+		return -EINVAL;
+
+	auxtrace_info->type = PERF_AUXTRACE_CS_ETM;
+
+	/* cpu map is not empty, we have specific CPUs to work with */
+	if (!cpu_map__empty(cpus)) {
+		for (i = 0; i < cpu_map__nr(cpus); i++)
+			ret = cs_etm_get_metadata(cpus->map[i],
+						    i * CS_ETM_PRIV_MAX,
+						    ptr, auxtrace_info);
+		if (ret)
+			goto out;
+	}
+
+	/* get configuration for all CPUs in the system */
+	nr_cpu = cpu__max_cpu();
+	for (i = 0; i < nr_cpu; i++) {
+		ret = cs_etm_get_metadata(i, i * CS_ETM_PRIV_MAX,
+					    ptr, auxtrace_info);
+		if (ret)
+			goto out;
+	}
+
+out:
+	return ret;
+}
+
+static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
+				int idx, struct auxtrace_mmap *mm,
+				unsigned char *data __maybe_unused,
+				u64 *head, u64 *old)
+{
+	pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
+		  __func__, idx, (size_t)*old, (size_t)*head, mm->len);
+
+	*old = *head;
+	*head += mm->len;
+
+	return 0;
+}
+
+static int cs_etm_snapshot_start(struct auxtrace_record *itr)
+{
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	struct perf_evsel *evsel;
+
+	evlist__for_each(ptr->evlist, evsel) {
+		if (evsel->attr.type == ptr->cs_etm_pmu->type)
+			return perf_evlist__disable_event(ptr->evlist, evsel);
+	}
+	return -EINVAL;
+}
+
+static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
+{
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	struct perf_evsel *evsel;
+
+	evlist__for_each(ptr->evlist, evsel) {
+		if (evsel->attr.type == ptr->cs_etm_pmu->type)
+			return perf_evlist__enable_event(ptr->evlist, evsel);
+	}
+	return -EINVAL;
+}
+
+static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused)
+{
+	return (((u64) rand() <<  0) & 0x00000000FFFFFFFFull) |
+		(((u64) rand() << 32) & 0xFFFFFFFF00000000ull);
+}
+
+static void cs_etm_recording_free(struct auxtrace_record *itr)
+{
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	free(ptr);
+}
+
+static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
+{
+	struct cs_etm_recording *ptr =
+			container_of(itr, struct cs_etm_recording, itr);
+	struct perf_evsel *evsel;
+
+	evlist__for_each(ptr->evlist, evsel) {
+		if (evsel->attr.type == ptr->cs_etm_pmu->type)
+			return perf_evlist__enable_event_idx(ptr->evlist,
+							     evsel, idx);
+	}
+
+	return -EINVAL;
+}
+
+struct auxtrace_record *cs_etm_record_init(int *err)
+{
+	struct perf_pmu *cs_etm_pmu;
+	struct cs_etm_recording *ptr;
+
+	cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
+
+	if (!cs_etm_pmu) {
+		*err = -EINVAL;
+		goto out;
+	}
+
+	ptr = zalloc(sizeof(struct cs_etm_recording));
+	if (!ptr) {
+		*err = -ENOMEM;
+		goto out;
+	}
+
+	ptr->cs_etm_pmu			= cs_etm_pmu;
+	ptr->itr.parse_snapshot_options	= cs_etm_parse_snapshot_options;
+	ptr->itr.recording_options	= cs_etm_recording_options;
+	ptr->itr.info_priv_size		= cs_etm_info_priv_size;
+	ptr->itr.info_fill		= cs_etm_info_fill;
+	ptr->itr.find_snapshot		= cs_etm_find_snapshot;
+	ptr->itr.snapshot_start		= cs_etm_snapshot_start;
+	ptr->itr.snapshot_finish	= cs_etm_snapshot_finish;
+	ptr->itr.reference		= cs_etm_reference;
+	ptr->itr.free			= cs_etm_recording_free;
+	ptr->itr.read_finish		= cs_etm_read_finish;
+
+	*err = 0;
+	return &ptr->itr;
+out:
+	return NULL;
+}
diff --git a/tools/perf/arch/arm/util/cs_etm.h b/tools/perf/arch/arm/util/cs_etm.h
new file mode 100644
index 000000000000..b3b3f16d80e3
--- /dev/null
+++ b/tools/perf/arch/arm/util/cs_etm.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright(C) 2015 Linaro Limited. All rights reserved.
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDE__PERF_CS_ETM_H__
+#define INCLUDE__PERF_CS_ETM_H__
+
+enum {
+	CS_ETM_MAGIC,
+	CS_ETM_CPU,
+	CS_ETM_SNAPSHOT,
+	CS_ETM_ETMCR,
+	CS_ETM_ETMCCER,
+	CS_ETM_ETMIDR,
+	CS_ETM_ETMTRACEIDR,
+	CS_ETM_PRIV_MAX,
+};
+
+static const u64 __perf_cs_etm_magic   = 0x3030303030303030ULL;
+#define CS_ETM_PRIV_SIZE (CS_ETM_PRIV_MAX * sizeof(u64))
+
+struct auxtrace_record *cs_etm_record_init(int *err);
+
+#endif
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH V2 27/30] perf build: adding X86 auxiliary specific flags
  2015-10-18 18:24 ` [PATCH V2 27/30] perf build: adding X86 auxiliary specific flags Mathieu Poirier
@ 2015-10-19 10:40   ` Adrian Hunter
  0 siblings, 0 replies; 43+ messages in thread
From: Adrian Hunter @ 2015-10-19 10:40 UTC (permalink / raw)
  To: linux-arm-kernel

On 18/10/15 21:24, Mathieu Poirier wrote:
> Adding an X86 specific flag to split AUX components that are
> generic and specific to architectures.  That way the auxiliary
> area mechanic can be compiled in for other architecture without
> including X86 specific code.

The idea is that you *should* be able to take a perf.data file from one
architecture and process it on another architecture.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH V2 17/30] perf: changing pmu::setup_aux() parameter to include event
  2015-10-18 18:24 ` [PATCH V2 17/30] perf: changing pmu::setup_aux() parameter to include event Mathieu Poirier
@ 2015-10-19 13:34   ` Alexander Shishkin
  0 siblings, 0 replies; 43+ messages in thread
From: Alexander Shishkin @ 2015-10-19 13:34 UTC (permalink / raw)
  To: linux-arm-kernel

Mathieu Poirier <mathieu.poirier@linaro.org> writes:

> For some tracers the event carries information to be embedded
> in the private structure returned by setup_aux().

You need to mention here what these tracers are and which bits of
event's information they need in their setup_aux(). Right now I can look
it up in this patchset, but when this code gets merged it will make it
easier to understand why this change was made.

Now, I understand that you're interested in event::attr in your
setup_aux(), I have more comments on that in that other patch. :)

Cheers,
--
Alex

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH V2 19/30] coresight: etb10: implementing the setup_aux() API
  2015-10-18 18:24 ` [PATCH V2 19/30] coresight: etb10: implementing the setup_aux() API Mathieu Poirier
@ 2015-10-19 13:44   ` Alexander Shishkin
  2015-10-20 16:40     ` Mathieu Poirier
  2015-10-20 11:37   ` Alexander Shishkin
  1 sibling, 1 reply; 43+ messages in thread
From: Alexander Shishkin @ 2015-10-19 13:44 UTC (permalink / raw)
  To: linux-arm-kernel

Mathieu Poirier <mathieu.poirier@linaro.org> writes:

> Adding an ETB10 specific auxiliary area setup operation to be
> used by the perf framework when events are initialised.
>
> Part of this operation involves modeling the mmap'ed area based
> on the specific ways a sink buffer gathers information.

It really doesn't seem to be ETB10 specific at all. When you add more
sinks, you'll probably end up copying this code every time.

Furthermore,

> +static void *etb_setup_aux(struct coresight_device *csdev, int cpu,
> +			   void **pages, int nr_pages, bool overwrite)
> +{
> +	int node, pg;
> +	struct cs_buffers *buf;
> +
> +	if (cpu == -1)
> +		cpu = smp_processor_id();
> +	node = cpu_to_node(cpu);
> +
> +	buf = kzalloc_node(offsetof(struct cs_buffers, addr[nr_pages]),
> +			   GFP_KERNEL, node);
> +	if (!buf)
> +		return NULL;
> +
> +	buf->snapshot = overwrite;
> +	buf->nr_pages = nr_pages;
> +
> +	/* Record information about buffers */
> +	for (pg = 0; pg < buf->nr_pages; pg++)
> +		buf->addr[pg] = pages[pg];
> +
> +	return buf;
> +}
> +

this one is so generic that I'm tempted to move this into perf's
ring_buffer code, because by the looks of it we'll need it pretty much
in every setup_aux().

Regards,
--
Alex

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH V2 22/30] coresight: etm-perf: new PMU driver for ETM tracers
  2015-10-18 18:24 ` [PATCH V2 22/30] coresight: etm-perf: new PMU driver for ETM tracers Mathieu Poirier
@ 2015-10-19 15:37   ` Alexander Shishkin
  2015-10-20 16:43     ` Mathieu Poirier
  2015-10-20  9:34   ` Alexander Shishkin
  1 sibling, 1 reply; 43+ messages in thread
From: Alexander Shishkin @ 2015-10-19 15:37 UTC (permalink / raw)
  To: linux-arm-kernel

Mathieu Poirier <mathieu.poirier@linaro.org> writes:

> +static int etm_event_pmu_start(struct perf_event *event)
> +{
> +	int cpu, ret;
> +	cpumask_t mask;
> +	struct coresight_device *csdev;
> +
> +	cpumask_clear(&mask);
> +	if (event->cpu != -1)
> +		cpumask_set_cpu(event->cpu, &mask);
> +	else
> +		cpumask_copy(&mask, cpu_online_mask);
> +
> +	for_each_cpu(cpu, &mask) {
> +		csdev = per_cpu(csdev_src, cpu);
> +
> +		if (!source_ops(csdev)->perf_start)
> +			continue;
> +
> +		ret = source_ops(csdev)->perf_start(csdev);
> +		if (ret)
> +			goto err;

So long as "perf_start" and "perf_stop" here mean
"pm_runtime_get()/put()", this can work, but in that case maybe a better
name should be used, because no real starting or stopping of anything
takes place here. Since pmu::event_init and event::destroy happen in
allocation/deallocation paths and at event scheduling, it's not a good
idea to actually start anything here.

Regards,
--
Alex

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH V2 01/30] coresight: etm3x: moving etm_readl/writel to header file
  2015-10-18 18:24 ` [PATCH V2 01/30] coresight: etm3x: moving etm_readl/writel to header file Mathieu Poirier
@ 2015-10-19 18:37   ` Greg KH
  0 siblings, 0 replies; 43+ messages in thread
From: Greg KH @ 2015-10-19 18:37 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Oct 18, 2015 at 12:24:18PM -0600, Mathieu Poirier wrote:
> Moving functions etm_readl/writel to file "coresight-etm.h"
> for access by code outside of the main ETM3x driver.
> 
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>  drivers/hwtracing/coresight/coresight-etm.h   | 29 +++++++++++++++++++++++++++
>  drivers/hwtracing/coresight/coresight-etm3x.c | 29 ---------------------------
>  2 files changed, 29 insertions(+), 29 deletions(-)

Can you remake this series use -M to 'git format-patch' so that we can
see the renames easier and they don't show up as 'delete and add'
patches?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH V2 22/30] coresight: etm-perf: new PMU driver for ETM tracers
  2015-10-18 18:24 ` [PATCH V2 22/30] coresight: etm-perf: new PMU driver for ETM tracers Mathieu Poirier
  2015-10-19 15:37   ` Alexander Shishkin
@ 2015-10-20  9:34   ` Alexander Shishkin
  2015-10-20 19:15     ` Mathieu Poirier
  1 sibling, 1 reply; 43+ messages in thread
From: Alexander Shishkin @ 2015-10-20  9:34 UTC (permalink / raw)
  To: linux-arm-kernel

Mathieu Poirier <mathieu.poirier@linaro.org> writes:

> +static void *etm_setup_aux(struct perf_event *event, void **pages,
> +			   int nr_pages, bool overwrite)
> +{
> +	int cpu;
> +	cpumask_t *mask;
> +	struct etm_event_data *event_data = NULL;
> +	struct coresight_device *csdev;
> +
> +	event_data = alloc_event_data(event->cpu);
> +	if (!event_data)
> +		return NULL;
> +
> +	mask = &event_data->mask;
> +
> +	if (event->cpu != -1)
> +		cpumask_set_cpu(event->cpu, mask);
> +	else
> +		cpumask_copy(mask, cpu_online_mask);
> +
> +	for_each_cpu(cpu, mask) {
> +		struct coresight_device *sink;
> +
> +		csdev = per_cpu(csdev_src, cpu);
> +		if (!csdev)
> +			goto err;
> +
> +		/* Get the tracer's config from perf */
> +		if (!source_ops(csdev)->perf_get_config)
> +			goto err;
> +
> +		event_data->source_config[cpu] =
> +			source_ops(csdev)->perf_get_config(csdev, event);
> +
> +		if (!event_data->source_config[cpu])
> +			goto err;
> +
> +		/*
> +		 * Get a handle on the sink buffer associated
> +		 * with this tracer.
> +		 */
> +		event_data->sink[cpu] = (void *)etm_event_build_path(cpu, true);

There are several problems here. What is created/allocated during
setup_aux(), has to be undone in free_aux(), however, the effect of
build_path() will only be undone in the event::destroy() path. So if the
user unmaps the aux buffer and then maps it again, we'll go ahead and
try to build the path again. (Btw, coresight_build_paths() and other
non-static functions and especially exported ones are really lacking
documentation at the moment).

It really looks like this has to be done in pmu::add(), so that the
source<=>sink connection exists only while the event is scheduled and
otherwise other events are free to connect their sources to these
sinks. And at pmu::del() the connection has to be torn down. This way we
can have a sensible multisession support. That is, provided my
understanding of the coresight driver architecture is correct.

Also, you won't have to configure things on multiple cpus for cpu==-1 if
you keep the source<=>sink connection only between pmu::add() and
pmu::del(), as an event can only be scheduled on one cpu at a time,
which should make things simpler.

> +
> +		if (!event_data->sink[cpu])
> +			goto err;
> +
> +		sink = event_data->sink[cpu];
> +
> +		if (!sink_ops(sink)->setup_aux)
> +			goto err;
> +
> +		/* Finally get the AUX specific data from the sink buffer */
> +		event_data->sink_config[cpu] =
> +				sink_ops(sink)->setup_aux(sink, cpu, pages,
> +							  nr_pages, overwrite);

Now this is a sensible thing to do. I understand that you'll have to
know which sink you're using so that you can pick the right sink_ops and
build an appropriate configuration, but perhaps it also makes sense to
release it once you got the sink_config.

> +static void etm_event_stop(struct perf_event *event, int mode)
> +{
> +	int cpu = smp_processor_id();
> +	struct coresight_device *csdev = per_cpu(csdev_src, cpu);
> +
> +	if (event->hw.state == PERF_HES_STOPPED)
> +		return;
> +
> +	if (!csdev)
> +		return;
> +
> +	/* stop tracer */
> +	if (!source_ops(csdev)->perf_disable)
> +		return;

This really shouldn't happen. It makes sense to make sure that we have
all the callbacks that we rely on in pmu::event_init() or pmu::add() and
refuse to start if we don't, but at this point we really shouldn't end
up in a situation where we suddenly don't have one of the callbacks.

> +	if (source_ops(csdev)->perf_disable(csdev))
> +		return;

This has a similar problem. I'd say that this callback should not be
able to fail and return anything other than success.

> +	/* tell the core */
> +	event->hw.state = PERF_HES_STOPPED;
> +
> +
> +	if (mode & PERF_EF_UPDATE) {
> +		struct coresight_device *sink;
> +		struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
> +		struct etm_event_data *event_data = perf_get_aux(handle);
> +
> +		if (WARN_ON_ONCE(handle->event != event))
> +			return;
> +
> +		if (WARN_ON_ONCE(!event_data))
> +			return;
> +
> +		sink = event_data->sink[cpu];
> +		if (WARN_ON_ONCE(!sink))
> +			return;
> +
> +		/* update trace information */
> +		if (!sink_ops(sink)->update_buffer)
> +			return;
> +
> +		sink_ops(sink)->update_buffer(sink, handle,
> +					      event_data->sink_config[cpu]);
> +	}
> +}
> +
> +static void etm_event_start(struct perf_event *event, int flags)
> +{
> +	int cpu = smp_processor_id();
> +	struct coresight_device *csdev = per_cpu(csdev_src, cpu);
> +
> +	if (!csdev)
> +		goto fail;
> +
> +	/* tell the perf core the event is alive */
> +	event->hw.state = 0;
> +
> +	if (!source_ops(csdev)->perf_enable)
> +		goto fail;

Same here.

> +
> +	if (source_ops(csdev)->perf_enable(csdev))
> +		goto fail;

This may fail, I suppose.

> +
> +	return;
> +
> +fail:
> +	event->hw.state = PERF_HES_STOPPED;
> +}
> +
> +static void etm_event_del(struct perf_event *event, int mode)
> +{
> +	int cpu = smp_processor_id();
> +	struct coresight_device *sink;
> +	struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
> +	struct etm_event_data *event_data = perf_get_aux(handle);
> +
> +	if (WARN_ON_ONCE(!event_data))
> +		return;
> +
> +	sink = event_data->sink[cpu];
> +	if (!sink)
> +		return;

This also shouldn't be able to prevent us from stopping the event.

> +
> +	etm_event_stop(event, PERF_EF_UPDATE);
> +
> +	if (!sink_ops(sink)->reset_buffer)
> +		return;
> +
> +	sink_ops(sink)->reset_buffer(sink, handle,
> +				     event_data->sink_config[cpu]);
> +}
> +
> +static int etm_event_add(struct perf_event *event, int mode)
> +{
> +
> +	int ret = -EBUSY, cpu = smp_processor_id();
> +	struct etm_event_data *event_data;
> +	struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct coresight_device *csdev = per_cpu(csdev_src, cpu);
> +	struct coresight_device *sink;
> +
> +	if (handle->event)
> +		goto out;
> +
> +	event_data = perf_aux_output_begin(handle, event);
> +	ret = -EINVAL;
> +	if (WARN_ON_ONCE(!event_data))
> +		goto fail_stop;
> +
> +	sink = event_data->sink[cpu];

So if you're able to fetch the sink right here and release it in
_del(). Of course, this being a hot path and an atomic context needs to
be taken into account.

Regards,
--
Alex

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH V2 20/30] coresight: etb10: implementing buffer set/reset() API
  2015-10-18 18:24 ` [PATCH V2 20/30] coresight: etb10: implementing buffer set/reset() API Mathieu Poirier
@ 2015-10-20  9:56   ` Alexander Shishkin
  2015-10-20 17:30     ` Mathieu Poirier
  0 siblings, 1 reply; 43+ messages in thread
From: Alexander Shishkin @ 2015-10-20  9:56 UTC (permalink / raw)
  To: linux-arm-kernel

Mathieu Poirier <mathieu.poirier@linaro.org> writes:

> Implementing perf related APIs to activate and terminate
> a trace session.  More specifically dealing with the sink
> buffer's internal mechanic along with perf's API to start
> and stop interactions with the ring buffers.

A matter of preference, but I'd say that it would be easier to review
this part if you merged all the buffer related patches together.

> +static void etb_reset_buffer(struct coresight_device *csdev,
> +			     struct perf_output_handle *handle,
> +			     void *sink_config)
> +{
> +	struct cs_buffers *buf = sink_config;
> +	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> +
> +	if (buf) {
> +		/*
> +		 * In snapshot mode ->data_size holds the new address of the
> +		 * ring buffer's head.  The size itself is the whole address
> +		 * range since we want the latest information.
> +		 */
> +		if (buf->snapshot)
> +			handle->head = local_xchg(&buf->data_size,
> +						  buf->nr_pages << PAGE_SHIFT);

Does it make sense to do this in etb_update_buffer() instead?

> +		perf_aux_output_end(handle, local_xchg(&buf->data_size, 0),
> +				    local_xchg(&buf->lost, 0));

The corresponding perf_aux_output_begin() is done in etm_event_add(),
I'd suggest that you do this in etm_event_del(),
unconditionally. Otherwise you're risking ending up with a refcount leak
and all sorts of horror.

Regards,
--
Alex

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH V2 19/30] coresight: etb10: implementing the setup_aux() API
  2015-10-18 18:24 ` [PATCH V2 19/30] coresight: etb10: implementing the setup_aux() API Mathieu Poirier
  2015-10-19 13:44   ` Alexander Shishkin
@ 2015-10-20 11:37   ` Alexander Shishkin
  1 sibling, 0 replies; 43+ messages in thread
From: Alexander Shishkin @ 2015-10-20 11:37 UTC (permalink / raw)
  To: linux-arm-kernel

Mathieu Poirier <mathieu.poirier@linaro.org> writes:

>  /**
> + * struct cs_buffer - keep track of a recording session' specifics
> + * @cur:	index of the current buffer
> + * @nr_pages:	max number of pages granted to us
> + * @nr_bufs:	number of clustered pages
> + * @offset:	offset within the current buffer
> + * @size:	how much space we have for this run
> + * @data_size:	how much we collected in this run
> + * @head:	head of the ring buffer
> + * @lost:	other than zero if we had a HW buffer wrap around
> + * @snapshot:	is this run in snapshot mode
> + * @addr:	virtual address this buffer starts at
> + */
> +struct cs_buffers {
> +	unsigned int		cur;
> +	unsigned int		nr_pages;
> +	unsigned int		nr_bufs;

This one is not really used.

> +	unsigned long		offset;
> +	unsigned long		size;

And this one seems to be only set in one place.

> +	local_t			data_size;
> +	local_t			head;

And so is this one.

> +	local_t			lost;
> +	bool			snapshot;
> +	void			*addr[0];

And this one seems to be a copy of what perf's ring buffer gives us.

> +static void *etb_setup_aux(struct coresight_device *csdev, int cpu,
> +			   void **pages, int nr_pages, bool overwrite)
> +{
> +	int node, pg;
> +	struct cs_buffers *buf;
> +
> +	if (cpu == -1)
> +		cpu = smp_processor_id();
> +	node = cpu_to_node(cpu);
> +
> +	buf = kzalloc_node(offsetof(struct cs_buffers, addr[nr_pages]),
> +			   GFP_KERNEL, node);
> +	if (!buf)
> +		return NULL;
> +
> +	buf->snapshot = overwrite;
> +	buf->nr_pages = nr_pages;
> +
> +	/* Record information about buffers */
> +	for (pg = 0; pg < buf->nr_pages; pg++)
> +		buf->addr[pg] = pages[pg];

Yes, buf::addr is a copy of @pages. You could save some space by just
saving @pages, it's going to be around until pmu::free_aux().

Regards,
--
Alex

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH V2 19/30] coresight: etb10: implementing the setup_aux() API
  2015-10-19 13:44   ` Alexander Shishkin
@ 2015-10-20 16:40     ` Mathieu Poirier
  0 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-20 16:40 UTC (permalink / raw)
  To: linux-arm-kernel

On 19 October 2015 at 07:44, Alexander Shishkin
<alexander.shishkin@linux.intel.com> wrote:
> Mathieu Poirier <mathieu.poirier@linaro.org> writes:
>
>> Adding an ETB10 specific auxiliary area setup operation to be
>> used by the perf framework when events are initialised.
>>
>> Part of this operation involves modeling the mmap'ed area based
>> on the specific ways a sink buffer gathers information.
>
> It really doesn't seem to be ETB10 specific at all. When you add more
> sinks, you'll probably end up copying this code every time.

That will depend on how that specific sinks work, but indeed, it is
pretty generic.

>
> Furthermore,
>
>> +static void *etb_setup_aux(struct coresight_device *csdev, int cpu,
>> +                        void **pages, int nr_pages, bool overwrite)
>> +{
>> +     int node, pg;
>> +     struct cs_buffers *buf;
>> +
>> +     if (cpu == -1)
>> +             cpu = smp_processor_id();
>> +     node = cpu_to_node(cpu);
>> +
>> +     buf = kzalloc_node(offsetof(struct cs_buffers, addr[nr_pages]),
>> +                        GFP_KERNEL, node);
>> +     if (!buf)
>> +             return NULL;
>> +
>> +     buf->snapshot = overwrite;
>> +     buf->nr_pages = nr_pages;
>> +
>> +     /* Record information about buffers */
>> +     for (pg = 0; pg < buf->nr_pages; pg++)
>> +             buf->addr[pg] = pages[pg];
>> +
>> +     return buf;
>> +}
>> +
>
> this one is so generic that I'm tempted to move this into perf's
> ring_buffer code, because by the looks of it we'll need it pretty much
> in every setup_aux().

It is tempting but I suggest we wait to see what kind of trend we get
before moving ahead with this.  There is always opportunity for
further consolidation should the need arise.

>
> Regards,
> --
> Alex

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH V2 22/30] coresight: etm-perf: new PMU driver for ETM tracers
  2015-10-19 15:37   ` Alexander Shishkin
@ 2015-10-20 16:43     ` Mathieu Poirier
  0 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-20 16:43 UTC (permalink / raw)
  To: linux-arm-kernel

On 19 October 2015 at 09:37, Alexander Shishkin
<alexander.shishkin@linux.intel.com> wrote:
> Mathieu Poirier <mathieu.poirier@linaro.org> writes:
>
>> +static int etm_event_pmu_start(struct perf_event *event)
>> +{
>> +     int cpu, ret;
>> +     cpumask_t mask;
>> +     struct coresight_device *csdev;
>> +
>> +     cpumask_clear(&mask);
>> +     if (event->cpu != -1)
>> +             cpumask_set_cpu(event->cpu, &mask);
>> +     else
>> +             cpumask_copy(&mask, cpu_online_mask);
>> +
>> +     for_each_cpu(cpu, &mask) {
>> +             csdev = per_cpu(csdev_src, cpu);
>> +
>> +             if (!source_ops(csdev)->perf_start)
>> +                     continue;
>> +
>> +             ret = source_ops(csdev)->perf_start(csdev);
>> +             if (ret)
>> +                     goto err;
>
> So long as "perf_start" and "perf_stop" here mean
> "pm_runtime_get()/put()", this can work, but in that case maybe a better
> name should be used, because no real starting or stopping of anything
> takes place here.

You're correct, nothing else than pm_runtime operations should be
happening in there.  I will revise the naming convention.

> Since pmu::event_init and event::destroy happen in
> allocation/deallocation paths and at event scheduling, it's not a good
> idea to actually start anything here.
>
> Regards,
> --
> Alex

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH V2 20/30] coresight: etb10: implementing buffer set/reset() API
  2015-10-20  9:56   ` Alexander Shishkin
@ 2015-10-20 17:30     ` Mathieu Poirier
  0 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-20 17:30 UTC (permalink / raw)
  To: linux-arm-kernel

On 20 October 2015 at 03:56, Alexander Shishkin
<alexander.shishkin@linux.intel.com> wrote:
> Mathieu Poirier <mathieu.poirier@linaro.org> writes:
>
>> Implementing perf related APIs to activate and terminate
>> a trace session.  More specifically dealing with the sink
>> buffer's internal mechanic along with perf's API to start
>> and stop interactions with the ring buffers.
>
> A matter of preference, but I'd say that it would be easier to review
> this part if you merged all the buffer related patches together.
>
>> +static void etb_reset_buffer(struct coresight_device *csdev,
>> +                          struct perf_output_handle *handle,
>> +                          void *sink_config)
>> +{
>> +     struct cs_buffers *buf = sink_config;
>> +     struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
>> +
>> +     if (buf) {
>> +             /*
>> +              * In snapshot mode ->data_size holds the new address of the
>> +              * ring buffer's head.  The size itself is the whole address
>> +              * range since we want the latest information.
>> +              */
>> +             if (buf->snapshot)
>> +                     handle->head = local_xchg(&buf->data_size,
>> +                                               buf->nr_pages << PAGE_SHIFT);
>
> Does it make sense to do this in etb_update_buffer() instead?

I toyed with that idea for a while.  I would make sense if the ETB was
generating an interrupt when it is full but since cross-triggers
aren't implemented yet I didn't want to introduce code I can't test.

>
>> +             perf_aux_output_end(handle, local_xchg(&buf->data_size, 0),
>> +                                 local_xchg(&buf->lost, 0));
>
> The corresponding perf_aux_output_begin() is done in etm_event_add(),
> I'd suggest that you do this in etm_event_del(),

I'm in total agreement.  Since perf_aux_output_begin() is called in
etm_event_add(), perf_aux_output_end() should really be called in
etm_event_del().  The problem is that "buf->data_size" and "buf->lost"
are specific to the sink buffer and shouldn't be made public outside
of it.  Let me think about this further.

> unconditionally. Otherwise you're risking ending up with a refcount leak
> and all sorts of horror.
>
> Regards,
> --
> Alex

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH V2 22/30] coresight: etm-perf: new PMU driver for ETM tracers
  2015-10-20  9:34   ` Alexander Shishkin
@ 2015-10-20 19:15     ` Mathieu Poirier
  0 siblings, 0 replies; 43+ messages in thread
From: Mathieu Poirier @ 2015-10-20 19:15 UTC (permalink / raw)
  To: linux-arm-kernel

On 20 October 2015 at 03:34, Alexander Shishkin
<alexander.shishkin@linux.intel.com> wrote:
> Mathieu Poirier <mathieu.poirier@linaro.org> writes:
>
>> +static void *etm_setup_aux(struct perf_event *event, void **pages,
>> +                        int nr_pages, bool overwrite)
>> +{
>> +     int cpu;
>> +     cpumask_t *mask;
>> +     struct etm_event_data *event_data = NULL;
>> +     struct coresight_device *csdev;
>> +
>> +     event_data = alloc_event_data(event->cpu);
>> +     if (!event_data)
>> +             return NULL;
>> +
>> +     mask = &event_data->mask;
>> +
>> +     if (event->cpu != -1)
>> +             cpumask_set_cpu(event->cpu, mask);
>> +     else
>> +             cpumask_copy(mask, cpu_online_mask);
>> +
>> +     for_each_cpu(cpu, mask) {
>> +             struct coresight_device *sink;
>> +
>> +             csdev = per_cpu(csdev_src, cpu);
>> +             if (!csdev)
>> +                     goto err;
>> +
>> +             /* Get the tracer's config from perf */
>> +             if (!source_ops(csdev)->perf_get_config)
>> +                     goto err;
>> +
>> +             event_data->source_config[cpu] =
>> +                     source_ops(csdev)->perf_get_config(csdev, event);
>> +
>> +             if (!event_data->source_config[cpu])
>> +                     goto err;
>> +
>> +             /*
>> +              * Get a handle on the sink buffer associated
>> +              * with this tracer.
>> +              */
>> +             event_data->sink[cpu] = (void *)etm_event_build_path(cpu, true);
>
> There are several problems here. What is created/allocated during
> setup_aux(), has to be undone in free_aux(), however, the effect of
> build_path() will only be undone in the event::destroy() path. So if the
> user unmaps the aux buffer and then maps it again, we'll go ahead and
> try to build the path again. (Btw, coresight_build_paths() and other
> non-static functions and especially exported ones are really lacking
> documentation at the moment).
>
> It really looks like this has to be done in pmu::add(), so that the
> source<=>sink connection exists only while the event is scheduled and
> otherwise other events are free to connect their sources to these
> sinks. And at pmu::del() the connection has to be torn down. This way we
> can have a sensible multisession support. That is, provided my
> understanding of the coresight driver architecture is correct.
>
> Also, you won't have to configure things on multiple cpus for cpu==-1 if
> you keep the source<=>sink connection only between pmu::add() and
> pmu::del(), as an event can only be scheduled on one cpu at a time,
> which should make things simpler.

I am well aware of all this...  Currently the process of building a
path is too heavy to be done at context switch time.  To be efficient
the components of a path would have to be kept in a linked list that
is then enabled/disabled when the time comes.  I've been meaning to do
something better for a while now.  This might be the perfect time to
address the problem.

Thanks for reviewing the patch set,
Mathieu

>
>> +
>> +             if (!event_data->sink[cpu])
>> +                     goto err;
>> +
>> +             sink = event_data->sink[cpu];
>> +
>> +             if (!sink_ops(sink)->setup_aux)
>> +                     goto err;
>> +
>> +             /* Finally get the AUX specific data from the sink buffer */
>> +             event_data->sink_config[cpu] =
>> +                             sink_ops(sink)->setup_aux(sink, cpu, pages,
>> +                                                       nr_pages, overwrite);
>
> Now this is a sensible thing to do. I understand that you'll have to
> know which sink you're using so that you can pick the right sink_ops and
> build an appropriate configuration, but perhaps it also makes sense to
> release it once you got the sink_config.
>
>> +static void etm_event_stop(struct perf_event *event, int mode)
>> +{
>> +     int cpu = smp_processor_id();
>> +     struct coresight_device *csdev = per_cpu(csdev_src, cpu);
>> +
>> +     if (event->hw.state == PERF_HES_STOPPED)
>> +             return;
>> +
>> +     if (!csdev)
>> +             return;
>> +
>> +     /* stop tracer */
>> +     if (!source_ops(csdev)->perf_disable)
>> +             return;
>
> This really shouldn't happen. It makes sense to make sure that we have
> all the callbacks that we rely on in pmu::event_init() or pmu::add() and
> refuse to start if we don't, but at this point we really shouldn't end
> up in a situation where we suddenly don't have one of the callbacks.
>
>> +     if (source_ops(csdev)->perf_disable(csdev))
>> +             return;
>
> This has a similar problem. I'd say that this callback should not be
> able to fail and return anything other than success.
>
>> +     /* tell the core */
>> +     event->hw.state = PERF_HES_STOPPED;
>> +
>> +
>> +     if (mode & PERF_EF_UPDATE) {
>> +             struct coresight_device *sink;
>> +             struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
>> +             struct etm_event_data *event_data = perf_get_aux(handle);
>> +
>> +             if (WARN_ON_ONCE(handle->event != event))
>> +                     return;
>> +
>> +             if (WARN_ON_ONCE(!event_data))
>> +                     return;
>> +
>> +             sink = event_data->sink[cpu];
>> +             if (WARN_ON_ONCE(!sink))
>> +                     return;
>> +
>> +             /* update trace information */
>> +             if (!sink_ops(sink)->update_buffer)
>> +                     return;
>> +
>> +             sink_ops(sink)->update_buffer(sink, handle,
>> +                                           event_data->sink_config[cpu]);
>> +     }
>> +}
>> +
>> +static void etm_event_start(struct perf_event *event, int flags)
>> +{
>> +     int cpu = smp_processor_id();
>> +     struct coresight_device *csdev = per_cpu(csdev_src, cpu);
>> +
>> +     if (!csdev)
>> +             goto fail;
>> +
>> +     /* tell the perf core the event is alive */
>> +     event->hw.state = 0;
>> +
>> +     if (!source_ops(csdev)->perf_enable)
>> +             goto fail;
>
> Same here.
>
>> +
>> +     if (source_ops(csdev)->perf_enable(csdev))
>> +             goto fail;
>
> This may fail, I suppose.
>
>> +
>> +     return;
>> +
>> +fail:
>> +     event->hw.state = PERF_HES_STOPPED;
>> +}
>> +
>> +static void etm_event_del(struct perf_event *event, int mode)
>> +{
>> +     int cpu = smp_processor_id();
>> +     struct coresight_device *sink;
>> +     struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
>> +     struct etm_event_data *event_data = perf_get_aux(handle);
>> +
>> +     if (WARN_ON_ONCE(!event_data))
>> +             return;
>> +
>> +     sink = event_data->sink[cpu];
>> +     if (!sink)
>> +             return;
>
> This also shouldn't be able to prevent us from stopping the event.
>
>> +
>> +     etm_event_stop(event, PERF_EF_UPDATE);
>> +
>> +     if (!sink_ops(sink)->reset_buffer)
>> +             return;
>> +
>> +     sink_ops(sink)->reset_buffer(sink, handle,
>> +                                  event_data->sink_config[cpu]);
>> +}
>> +
>> +static int etm_event_add(struct perf_event *event, int mode)
>> +{
>> +
>> +     int ret = -EBUSY, cpu = smp_processor_id();
>> +     struct etm_event_data *event_data;
>> +     struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle);
>> +     struct hw_perf_event *hwc = &event->hw;
>> +     struct coresight_device *csdev = per_cpu(csdev_src, cpu);
>> +     struct coresight_device *sink;
>> +
>> +     if (handle->event)
>> +             goto out;
>> +
>> +     event_data = perf_aux_output_begin(handle, event);
>> +     ret = -EINVAL;
>> +     if (WARN_ON_ONCE(!event_data))
>> +             goto fail_stop;
>> +
>> +     sink = event_data->sink[cpu];
>
> So if you're able to fetch the sink right here and release it in
> _del(). Of course, this being a hot path and an atomic context needs to
> be taken into account.
>
> Regards,
> --
> Alex

^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2015-10-20 19:15 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-10-18 18:24 [PATCH V2 00/30] Coresight integration with perf Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 01/30] coresight: etm3x: moving etm_readl/writel to header file Mathieu Poirier
2015-10-19 18:37   ` Greg KH
2015-10-18 18:24 ` [PATCH V2 02/30] coresight: etm3x: moving sysFS entries to dedicated file Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 03/30] coresight: etm3x: unlocking tracers in default arch init Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 04/30] coresight: etm3x: splitting struct etm_drvdata Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 05/30] coresight: etm3x: set progbit to stop trace collection Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 06/30] coresight: clearly labeling source operarions Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 07/30] coresight: etm3x: moving etm_drvdata::enable to atomic field Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 08/30] coresight: etm3x: implementing 'cpu_id()' API Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 09/30] coresight: etm3x: changing default trace configuration Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 10/30] coresight: etm3x: consolidating initial config Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 11/30] coresight: etm3x: implementing user/kernel mode tracing Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 12/30] coresight: etm3x: adding perf_get/set_config() API Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 13/30] coresight: etm3x: implementing perf_enable/disable() API Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 14/30] coresight: etm3x: implementing perf_start/stop() API Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 15/30] coresight: making coresight_build_paths() public Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 16/30] coresight: keeping track of enabled sink buffers Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 17/30] perf: changing pmu::setup_aux() parameter to include event Mathieu Poirier
2015-10-19 13:34   ` Alexander Shishkin
2015-10-18 18:24 ` [PATCH V2 18/30] coresight: etb10: moving to local atomic operations Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 19/30] coresight: etb10: implementing the setup_aux() API Mathieu Poirier
2015-10-19 13:44   ` Alexander Shishkin
2015-10-20 16:40     ` Mathieu Poirier
2015-10-20 11:37   ` Alexander Shishkin
2015-10-18 18:24 ` [PATCH V2 20/30] coresight: etb10: implementing buffer set/reset() API Mathieu Poirier
2015-10-20  9:56   ` Alexander Shishkin
2015-10-20 17:30     ` Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 21/30] coresight: etb10: implementing buffer update API Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 22/30] coresight: etm-perf: new PMU driver for ETM tracers Mathieu Poirier
2015-10-19 15:37   ` Alexander Shishkin
2015-10-20 16:43     ` Mathieu Poirier
2015-10-20  9:34   ` Alexander Shishkin
2015-10-20 19:15     ` Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 23/30] coresight: updating documentation to reflect integration with perf Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 24/30] perf tools: making function set_max_cpu_num() non static Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 25/30] perf tools: adding perf_session to *info_prive_size() Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 26/30] perf tools: making source devices path broadly accessible Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 27/30] perf build: adding X86 auxiliary specific flags Mathieu Poirier
2015-10-19 10:40   ` Adrian Hunter
2015-10-18 18:24 ` [PATCH V2 28/30] perf tools: making coresight PMU listable Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 29/30] perf tools: adding coresight define for auxtrace Mathieu Poirier
2015-10-18 18:24 ` [PATCH V2 30/30] perf tools: adding coresight etm PMU record capabilities Mathieu Poirier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).