All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/14] coresight: tmc: make driver usable by Perf
@ 2016-03-22 20:23 ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

This patchset makes the TMC driver usable from Perf by way of the
recently added AUX area functionality.

The first 11 patches remodel the driver so that the functionality
is compartmented enough to be used as building blocks when interfacing
with the Perf subsystem.

Path 12 and 13 implement the AUX area API, taking heavily on the existing
ETB10 implementation.

Finally patch 14 allows the TMC to be used as a link rather than a sink,
something that is required when dealing with an ETF component.

Best regards,
Mathieu

Mathieu Poirier (14):
  coresight: tmc: modifying naming convention
  coresight: tmc: waiting for TMCReady bit before programming
  coresight: tmc: re-implementing tmc_read_prepare/unprepare() functions
  coresight: tmc: introducing new header file
  coresight: tmc: splitting driver in ETB/ETF and ETR components
  coresight: tmc: making prepare/unprepare functions generic
  coresight: tmc: making disable function reusable
  coresight: tmc: allocating memory when needed
  coresight: tmc: adding mode of operation for link/sinks
  coresight: tmc: make sysFS and Perf mode mutually exclusive
  coresight: tmc: keep track of memory width
  coresight: tmc: implementing TMC-ETF AUX space API
  coresight: tmc: implementing TMC-ETR AUX space API
  coresight: configuring ETF in FIFO mode when acting as link

 drivers/hwtracing/coresight/Makefile            |   4 +-
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 606 ++++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 470 ++++++++++++++++++
 drivers/hwtracing/coresight/coresight-tmc.c     | 447 ++---------------
 drivers/hwtracing/coresight/coresight-tmc.h     | 167 +++++++
 drivers/hwtracing/coresight/coresight.c         |  17 +-
 6 files changed, 1296 insertions(+), 415 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-tmc-etf.c
 create mode 100644 drivers/hwtracing/coresight/coresight-tmc-etr.c
 create mode 100644 drivers/hwtracing/coresight/coresight-tmc.h

-- 
2.1.4

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

* [PATCH 00/14] coresight: tmc: make driver usable by Perf
@ 2016-03-22 20:23 ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset makes the TMC driver usable from Perf by way of the
recently added AUX area functionality.

The first 11 patches remodel the driver so that the functionality
is compartmented enough to be used as building blocks when interfacing
with the Perf subsystem.

Path 12 and 13 implement the AUX area API, taking heavily on the existing
ETB10 implementation.

Finally patch 14 allows the TMC to be used as a link rather than a sink,
something that is required when dealing with an ETF component.

Best regards,
Mathieu

Mathieu Poirier (14):
  coresight: tmc: modifying naming convention
  coresight: tmc: waiting for TMCReady bit before programming
  coresight: tmc: re-implementing tmc_read_prepare/unprepare() functions
  coresight: tmc: introducing new header file
  coresight: tmc: splitting driver in ETB/ETF and ETR components
  coresight: tmc: making prepare/unprepare functions generic
  coresight: tmc: making disable function reusable
  coresight: tmc: allocating memory when needed
  coresight: tmc: adding mode of operation for link/sinks
  coresight: tmc: make sysFS and Perf mode mutually exclusive
  coresight: tmc: keep track of memory width
  coresight: tmc: implementing TMC-ETF AUX space API
  coresight: tmc: implementing TMC-ETR AUX space API
  coresight: configuring ETF in FIFO mode when acting as link

 drivers/hwtracing/coresight/Makefile            |   4 +-
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 606 ++++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 470 ++++++++++++++++++
 drivers/hwtracing/coresight/coresight-tmc.c     | 447 ++---------------
 drivers/hwtracing/coresight/coresight-tmc.h     | 167 +++++++
 drivers/hwtracing/coresight/coresight.c         |  17 +-
 6 files changed, 1296 insertions(+), 415 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-tmc-etf.c
 create mode 100644 drivers/hwtracing/coresight/coresight-tmc-etr.c
 create mode 100644 drivers/hwtracing/coresight/coresight-tmc.h

-- 
2.1.4

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

* [PATCH 01/14] coresight: tmc: modifying naming convention
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

According to the TMC architectural state machine, the 'stopped'
state is reached when bit 2 (TMCReady) of the TMC Status register
turns to '1'.  The code is correct but the naming convention isn't.

The 'Triggered' bit occupies position '1' of the TMC Status register
and has nothing to do with the indication of the TMC entering the
stopped state. As such renaming function "tmc_wait_for_triggered()"
and changing the #define to reflect what the code is really doing.

This patch has no effect other than clarifying the semantic.

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

diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index f0a67f984591..8a9e4d789bd8 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -76,7 +76,7 @@
 #define TMC_FFCR_TRIGON_TRIGIN	BIT(8)
 #define TMC_FFCR_STOP_ON_FLUSH	BIT(12)
 
-#define TMC_STS_TRIGGERED_BIT	2
+#define TMC_STS_TMCREADY_BIT	2
 #define TMC_FFCR_FLUSHMAN_BIT	6
 
 enum tmc_config_type {
@@ -131,11 +131,11 @@ struct tmc_drvdata {
 	u32			trigger_cntr;
 };
 
-static void tmc_wait_for_ready(struct tmc_drvdata *drvdata)
+static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
 {
 	/* Ensure formatter, unformatter and hardware fifo are empty */
 	if (coresight_timeout(drvdata->base,
-			      TMC_STS, TMC_STS_TRIGGERED_BIT, 1)) {
+			      TMC_STS, TMC_STS_TMCREADY_BIT, 1)) {
 		dev_err(drvdata->dev,
 			"timeout observed when probing at offset %#x\n",
 			TMC_STS);
@@ -159,7 +159,7 @@ static void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
 			TMC_FFCR);
 	}
 
-	tmc_wait_for_ready(drvdata);
+	tmc_wait_for_tmcready(drvdata);
 }
 
 static void tmc_enable_hw(struct tmc_drvdata *drvdata)
-- 
2.1.4

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

* [PATCH 01/14] coresight: tmc: modifying naming convention
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

According to the TMC architectural state machine, the 'stopped'
state is reached when bit 2 (TMCReady) of the TMC Status register
turns to '1'.  The code is correct but the naming convention isn't.

The 'Triggered' bit occupies position '1' of the TMC Status register
and has nothing to do with the indication of the TMC entering the
stopped state. As such renaming function "tmc_wait_for_triggered()"
and changing the #define to reflect what the code is really doing.

This patch has no effect other than clarifying the semantic.

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

diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index f0a67f984591..8a9e4d789bd8 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -76,7 +76,7 @@
 #define TMC_FFCR_TRIGON_TRIGIN	BIT(8)
 #define TMC_FFCR_STOP_ON_FLUSH	BIT(12)
 
-#define TMC_STS_TRIGGERED_BIT	2
+#define TMC_STS_TMCREADY_BIT	2
 #define TMC_FFCR_FLUSHMAN_BIT	6
 
 enum tmc_config_type {
@@ -131,11 +131,11 @@ struct tmc_drvdata {
 	u32			trigger_cntr;
 };
 
-static void tmc_wait_for_ready(struct tmc_drvdata *drvdata)
+static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
 {
 	/* Ensure formatter, unformatter and hardware fifo are empty */
 	if (coresight_timeout(drvdata->base,
-			      TMC_STS, TMC_STS_TRIGGERED_BIT, 1)) {
+			      TMC_STS, TMC_STS_TMCREADY_BIT, 1)) {
 		dev_err(drvdata->dev,
 			"timeout observed when probing at offset %#x\n",
 			TMC_STS);
@@ -159,7 +159,7 @@ static void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
 			TMC_FFCR);
 	}
 
-	tmc_wait_for_ready(drvdata);
+	tmc_wait_for_tmcready(drvdata);
 }
 
 static void tmc_enable_hw(struct tmc_drvdata *drvdata)
-- 
2.1.4

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

* [PATCH 02/14] coresight: tmc: waiting for TMCReady bit before programming
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

According to the TRM before programming the TMC in circular
buffer mode (and that for any configuration, ETB, ETR, ETF),
the TMCReady bit in the status register has to be set.

This patch adds a check to make sure the state machine is in
a state where it can be configured, and complains otherwise.

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

diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 8a9e4d789bd8..f4ba837a0810 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -179,6 +179,9 @@ static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
 
 	CS_UNLOCK(drvdata->base);
 
+	/* Wait for TMCSReady bit to be set */
+	tmc_wait_for_tmcready(drvdata);
+
 	writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
 	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
 		       TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
@@ -200,6 +203,9 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
 
 	CS_UNLOCK(drvdata->base);
 
+	/* Wait for TMCSReady bit to be set */
+	tmc_wait_for_tmcready(drvdata);
+
 	writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ);
 	writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
 
@@ -229,6 +235,9 @@ static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
 {
 	CS_UNLOCK(drvdata->base);
 
+	/* Wait for TMCSReady bit to be set */
+	tmc_wait_for_tmcready(drvdata);
+
 	writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE);
 	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI,
 		       drvdata->base + TMC_FFCR);
-- 
2.1.4

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

* [PATCH 02/14] coresight: tmc: waiting for TMCReady bit before programming
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

According to the TRM before programming the TMC in circular
buffer mode (and that for any configuration, ETB, ETR, ETF),
the TMCReady bit in the status register has to be set.

This patch adds a check to make sure the state machine is in
a state where it can be configured, and complains otherwise.

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

diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 8a9e4d789bd8..f4ba837a0810 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -179,6 +179,9 @@ static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
 
 	CS_UNLOCK(drvdata->base);
 
+	/* Wait for TMCSReady bit to be set */
+	tmc_wait_for_tmcready(drvdata);
+
 	writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
 	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
 		       TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
@@ -200,6 +203,9 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
 
 	CS_UNLOCK(drvdata->base);
 
+	/* Wait for TMCSReady bit to be set */
+	tmc_wait_for_tmcready(drvdata);
+
 	writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ);
 	writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
 
@@ -229,6 +235,9 @@ static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
 {
 	CS_UNLOCK(drvdata->base);
 
+	/* Wait for TMCSReady bit to be set */
+	tmc_wait_for_tmcready(drvdata);
+
 	writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE);
 	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI,
 		       drvdata->base + TMC_FFCR);
-- 
2.1.4

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

* [PATCH 03/14] coresight: tmc: re-implementing tmc_read_prepare/unprepare() functions
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

In their current implementation the tmc_read_prepare/unprepare()
are a lump of if/else that is difficult to read.  This patch is
alleviating that by using a switch statement.  The latter also
allows for a better control on the error path.

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

diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index f4ba837a0810..208d47dd3083 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -430,7 +430,7 @@ static const struct coresight_ops tmc_etf_cs_ops = {
 
 static int tmc_read_prepare(struct tmc_drvdata *drvdata)
 {
-	int ret;
+	int ret = 0;
 	unsigned long flags;
 	enum tmc_mode mode;
 
@@ -438,25 +438,31 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata)
 	if (!drvdata->enable)
 		goto out;
 
-	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
+	switch (drvdata->config_type) {
+	case TMC_CONFIG_TYPE_ETB:
 		tmc_etb_disable_hw(drvdata);
-	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		tmc_etr_disable_hw(drvdata);
-	} else {
+		break;
+	case TMC_CONFIG_TYPE_ETF:
+		/* There is no point in reading a TMC in HW FIFO mode */
 		mode = readl_relaxed(drvdata->base + TMC_MODE);
-		if (mode == TMC_MODE_CIRCULAR_BUFFER) {
-			tmc_etb_disable_hw(drvdata);
-		} else {
-			ret = -ENODEV;
+		if (mode != TMC_MODE_CIRCULAR_BUFFER) {
+			ret = -EINVAL;
 			goto err;
 		}
+
+		tmc_etb_disable_hw(drvdata);
+		break;
+	case TMC_CONFIG_TYPE_ETR:
+		tmc_etr_disable_hw(drvdata);
+		break;
+	default:
+		ret = -EINVAL;
+		goto err;
 	}
+
 out:
 	drvdata->reading = true;
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-
 	dev_info(drvdata->dev, "TMC read start\n");
-	return 0;
 err:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 	return ret;
@@ -471,20 +477,30 @@ static void tmc_read_unprepare(struct tmc_drvdata *drvdata)
 	if (!drvdata->enable)
 		goto out;
 
-	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
+	switch (drvdata->config_type) {
+	case TMC_CONFIG_TYPE_ETB:
 		tmc_etb_enable_hw(drvdata);
-	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		tmc_etr_enable_hw(drvdata);
-	} else {
+		break;
+	case TMC_CONFIG_TYPE_ETF:
+		/* Make sure we don't re-enable a TMC in HW FIFO mode */
 		mode = readl_relaxed(drvdata->base + TMC_MODE);
-		if (mode == TMC_MODE_CIRCULAR_BUFFER)
-			tmc_etb_enable_hw(drvdata);
+		if (mode != TMC_MODE_CIRCULAR_BUFFER)
+			goto err;
+
+		tmc_etb_enable_hw(drvdata);
+		break;
+	case TMC_CONFIG_TYPE_ETR:
+		tmc_etr_disable_hw(drvdata);
+		break;
+	default:
+		goto err;
 	}
+
 out:
 	drvdata->reading = false;
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-
 	dev_info(drvdata->dev, "TMC read end\n");
+err:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 }
 
 static int tmc_open(struct inode *inode, struct file *file)
-- 
2.1.4

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

* [PATCH 03/14] coresight: tmc: re-implementing tmc_read_prepare/unprepare() functions
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

In their current implementation the tmc_read_prepare/unprepare()
are a lump of if/else that is difficult to read.  This patch is
alleviating that by using a switch statement.  The latter also
allows for a better control on the error path.

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

diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index f4ba837a0810..208d47dd3083 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -430,7 +430,7 @@ static const struct coresight_ops tmc_etf_cs_ops = {
 
 static int tmc_read_prepare(struct tmc_drvdata *drvdata)
 {
-	int ret;
+	int ret = 0;
 	unsigned long flags;
 	enum tmc_mode mode;
 
@@ -438,25 +438,31 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata)
 	if (!drvdata->enable)
 		goto out;
 
-	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
+	switch (drvdata->config_type) {
+	case TMC_CONFIG_TYPE_ETB:
 		tmc_etb_disable_hw(drvdata);
-	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		tmc_etr_disable_hw(drvdata);
-	} else {
+		break;
+	case TMC_CONFIG_TYPE_ETF:
+		/* There is no point in reading a TMC in HW FIFO mode */
 		mode = readl_relaxed(drvdata->base + TMC_MODE);
-		if (mode == TMC_MODE_CIRCULAR_BUFFER) {
-			tmc_etb_disable_hw(drvdata);
-		} else {
-			ret = -ENODEV;
+		if (mode != TMC_MODE_CIRCULAR_BUFFER) {
+			ret = -EINVAL;
 			goto err;
 		}
+
+		tmc_etb_disable_hw(drvdata);
+		break;
+	case TMC_CONFIG_TYPE_ETR:
+		tmc_etr_disable_hw(drvdata);
+		break;
+	default:
+		ret = -EINVAL;
+		goto err;
 	}
+
 out:
 	drvdata->reading = true;
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-
 	dev_info(drvdata->dev, "TMC read start\n");
-	return 0;
 err:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 	return ret;
@@ -471,20 +477,30 @@ static void tmc_read_unprepare(struct tmc_drvdata *drvdata)
 	if (!drvdata->enable)
 		goto out;
 
-	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
+	switch (drvdata->config_type) {
+	case TMC_CONFIG_TYPE_ETB:
 		tmc_etb_enable_hw(drvdata);
-	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		tmc_etr_enable_hw(drvdata);
-	} else {
+		break;
+	case TMC_CONFIG_TYPE_ETF:
+		/* Make sure we don't re-enable a TMC in HW FIFO mode */
 		mode = readl_relaxed(drvdata->base + TMC_MODE);
-		if (mode == TMC_MODE_CIRCULAR_BUFFER)
-			tmc_etb_enable_hw(drvdata);
+		if (mode != TMC_MODE_CIRCULAR_BUFFER)
+			goto err;
+
+		tmc_etb_enable_hw(drvdata);
+		break;
+	case TMC_CONFIG_TYPE_ETR:
+		tmc_etr_disable_hw(drvdata);
+		break;
+	default:
+		goto err;
 	}
+
 out:
 	drvdata->reading = false;
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-
 	dev_info(drvdata->dev, "TMC read end\n");
+err:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 }
 
 static int tmc_open(struct inode *inode, struct file *file)
-- 
2.1.4

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

* [PATCH 04/14] coresight: tmc: introducing new header file
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

The amount of #define, enumeration and structure definition
is big enough to justify moving them to a new header file.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc.c | 102 +----------------------
 drivers/hwtracing/coresight/coresight-tmc.h | 122 ++++++++++++++++++++++++++++
 2 files changed, 123 insertions(+), 101 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-tmc.h

diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 208d47dd3083..64f744724d00 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -29,107 +29,7 @@
 #include <linux/amba/bus.h>
 
 #include "coresight-priv.h"
-
-#define TMC_RSZ			0x004
-#define TMC_STS			0x00c
-#define TMC_RRD			0x010
-#define TMC_RRP			0x014
-#define TMC_RWP			0x018
-#define TMC_TRG			0x01c
-#define TMC_CTL			0x020
-#define TMC_RWD			0x024
-#define TMC_MODE		0x028
-#define TMC_LBUFLEVEL		0x02c
-#define TMC_CBUFLEVEL		0x030
-#define TMC_BUFWM		0x034
-#define TMC_RRPHI		0x038
-#define TMC_RWPHI		0x03c
-#define TMC_AXICTL		0x110
-#define TMC_DBALO		0x118
-#define TMC_DBAHI		0x11c
-#define TMC_FFSR		0x300
-#define TMC_FFCR		0x304
-#define TMC_PSCR		0x308
-#define TMC_ITMISCOP0		0xee0
-#define TMC_ITTRFLIN		0xee8
-#define TMC_ITATBDATA0		0xeec
-#define TMC_ITATBCTR2		0xef0
-#define TMC_ITATBCTR1		0xef4
-#define TMC_ITATBCTR0		0xef8
-
-/* register description */
-/* TMC_CTL - 0x020 */
-#define TMC_CTL_CAPT_EN		BIT(0)
-/* TMC_STS - 0x00C */
-#define TMC_STS_TRIGGERED	BIT(1)
-/* TMC_AXICTL - 0x110 */
-#define TMC_AXICTL_PROT_CTL_B0	BIT(0)
-#define TMC_AXICTL_PROT_CTL_B1	BIT(1)
-#define TMC_AXICTL_SCT_GAT_MODE	BIT(7)
-#define TMC_AXICTL_WR_BURST_LEN 0xF00
-/* TMC_FFCR - 0x304 */
-#define TMC_FFCR_EN_FMT		BIT(0)
-#define TMC_FFCR_EN_TI		BIT(1)
-#define TMC_FFCR_FON_FLIN	BIT(4)
-#define TMC_FFCR_FON_TRIG_EVT	BIT(5)
-#define TMC_FFCR_FLUSHMAN	BIT(6)
-#define TMC_FFCR_TRIGON_TRIGIN	BIT(8)
-#define TMC_FFCR_STOP_ON_FLUSH	BIT(12)
-
-#define TMC_STS_TMCREADY_BIT	2
-#define TMC_FFCR_FLUSHMAN_BIT	6
-
-enum tmc_config_type {
-	TMC_CONFIG_TYPE_ETB,
-	TMC_CONFIG_TYPE_ETR,
-	TMC_CONFIG_TYPE_ETF,
-};
-
-enum tmc_mode {
-	TMC_MODE_CIRCULAR_BUFFER,
-	TMC_MODE_SOFTWARE_FIFO,
-	TMC_MODE_HARDWARE_FIFO,
-};
-
-enum tmc_mem_intf_width {
-	TMC_MEM_INTF_WIDTH_32BITS	= 0x2,
-	TMC_MEM_INTF_WIDTH_64BITS	= 0x3,
-	TMC_MEM_INTF_WIDTH_128BITS	= 0x4,
-	TMC_MEM_INTF_WIDTH_256BITS	= 0x5,
-};
-
-/**
- * struct tmc_drvdata - specifics associated to an TMC component
- * @base:	memory mapped base address for this component.
- * @dev:	the device entity associated to this component.
- * @csdev:	component vitals needed by the framework.
- * @miscdev:	specifics to handle "/dev/xyz.tmc" entry.
- * @spinlock:	only one at a time pls.
- * @read_count:	manages preparation of buffer for reading.
- * @buf:	area of memory where trace data get sent.
- * @paddr:	DMA start location in RAM.
- * @vaddr:	virtual representation of @paddr.
- * @size:	@buf size.
- * @enable:	this TMC is being used.
- * @config_type: TMC variant, must be of type @tmc_config_type.
- * @trigger_cntr: amount of words to store after a trigger.
- */
-struct tmc_drvdata {
-	void __iomem		*base;
-	struct device		*dev;
-	struct coresight_device	*csdev;
-	struct miscdevice	miscdev;
-	spinlock_t		spinlock;
-	int			read_count;
-	bool			reading;
-	char			*buf;
-	dma_addr_t		paddr;
-	void			*vaddr;
-	u32			size;
-	bool			enable;
-	enum tmc_config_type	config_type;
-	u32			trigger_cntr;
-};
+#include "coresight-tmc.h"
 
 static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
 {
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
new file mode 100644
index 000000000000..2d7d52747b4e
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -0,0 +1,122 @@
+/*
+ * 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 _CORESIGHT_TMC_H
+#define _CORESIGHT_TMC_H
+
+#define TMC_RSZ			0x004
+#define TMC_STS			0x00c
+#define TMC_RRD			0x010
+#define TMC_RRP			0x014
+#define TMC_RWP			0x018
+#define TMC_TRG			0x01c
+#define TMC_CTL			0x020
+#define TMC_RWD			0x024
+#define TMC_MODE		0x028
+#define TMC_LBUFLEVEL		0x02c
+#define TMC_CBUFLEVEL		0x030
+#define TMC_BUFWM		0x034
+#define TMC_RRPHI		0x038
+#define TMC_RWPHI		0x03c
+#define TMC_AXICTL		0x110
+#define TMC_DBALO		0x118
+#define TMC_DBAHI		0x11c
+#define TMC_FFSR		0x300
+#define TMC_FFCR		0x304
+#define TMC_PSCR		0x308
+#define TMC_ITMISCOP0		0xee0
+#define TMC_ITTRFLIN		0xee8
+#define TMC_ITATBDATA0		0xeec
+#define TMC_ITATBCTR2		0xef0
+#define TMC_ITATBCTR1		0xef4
+#define TMC_ITATBCTR0		0xef8
+
+/* register description */
+/* TMC_CTL - 0x020 */
+#define TMC_CTL_CAPT_EN		BIT(0)
+/* TMC_STS - 0x00C */
+#define TMC_STS_TRIGGERED	BIT(1)
+/* TMC_AXICTL - 0x110 */
+#define TMC_AXICTL_PROT_CTL_B0	BIT(0)
+#define TMC_AXICTL_PROT_CTL_B1	BIT(1)
+#define TMC_AXICTL_SCT_GAT_MODE	BIT(7)
+#define TMC_AXICTL_WR_BURST_LEN 0xF00
+/* TMC_FFCR - 0x304 */
+#define TMC_FFCR_EN_FMT		BIT(0)
+#define TMC_FFCR_EN_TI		BIT(1)
+#define TMC_FFCR_FON_FLIN	BIT(4)
+#define TMC_FFCR_FON_TRIG_EVT	BIT(5)
+#define TMC_FFCR_FLUSHMAN	BIT(6)
+#define TMC_FFCR_TRIGON_TRIGIN	BIT(8)
+#define TMC_FFCR_STOP_ON_FLUSH	BIT(12)
+
+#define TMC_STS_TMCREADY_BIT	2
+#define TMC_FFCR_FLUSHMAN_BIT	6
+
+enum tmc_config_type {
+	TMC_CONFIG_TYPE_ETB,
+	TMC_CONFIG_TYPE_ETR,
+	TMC_CONFIG_TYPE_ETF,
+};
+
+enum tmc_mode {
+	TMC_MODE_CIRCULAR_BUFFER,
+	TMC_MODE_SOFTWARE_FIFO,
+	TMC_MODE_HARDWARE_FIFO,
+};
+
+enum tmc_mem_intf_width {
+	TMC_MEM_INTF_WIDTH_32BITS	= 0x2,
+	TMC_MEM_INTF_WIDTH_64BITS	= 0x3,
+	TMC_MEM_INTF_WIDTH_128BITS	= 0x4,
+	TMC_MEM_INTF_WIDTH_256BITS	= 0x5,
+};
+
+/**
+ * struct tmc_drvdata - specifics associated to an TMC component
+ * @base:	memory mapped base address for this component.
+ * @dev:	the device entity associated to this component.
+ * @csdev:	component vitals needed by the framework.
+ * @miscdev:	specifics to handle "/dev/xyz.tmc" entry.
+ * @spinlock:	only one at a time pls.
+ * @read_count:	manages preparation of buffer for reading.
+ * @buf:	area of memory where trace data get sent.
+ * @paddr:	DMA start location in RAM.
+ * @vaddr:	virtual representation of @paddr.
+ * @size:	@buf size.
+ * @enable:	this TMC is being used.
+ * @config_type: TMC variant, must be of type @tmc_config_type.
+ * @trigger_cntr: amount of words to store after a trigger.
+ */
+struct tmc_drvdata {
+	void __iomem		*base;
+	struct device		*dev;
+	struct coresight_device	*csdev;
+	struct miscdevice	miscdev;
+	spinlock_t		spinlock;
+	int			read_count;
+	bool			reading;
+	char			*buf;
+	dma_addr_t		paddr;
+	void __iomem		*vaddr;
+	u32			size;
+	bool			enable;
+	enum tmc_config_type	config_type;
+	u32			trigger_cntr;
+};
+
+#endif
-- 
2.1.4

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

* [PATCH 04/14] coresight: tmc: introducing new header file
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

The amount of #define, enumeration and structure definition
is big enough to justify moving them to a new header file.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc.c | 102 +----------------------
 drivers/hwtracing/coresight/coresight-tmc.h | 122 ++++++++++++++++++++++++++++
 2 files changed, 123 insertions(+), 101 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-tmc.h

diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 208d47dd3083..64f744724d00 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -29,107 +29,7 @@
 #include <linux/amba/bus.h>
 
 #include "coresight-priv.h"
-
-#define TMC_RSZ			0x004
-#define TMC_STS			0x00c
-#define TMC_RRD			0x010
-#define TMC_RRP			0x014
-#define TMC_RWP			0x018
-#define TMC_TRG			0x01c
-#define TMC_CTL			0x020
-#define TMC_RWD			0x024
-#define TMC_MODE		0x028
-#define TMC_LBUFLEVEL		0x02c
-#define TMC_CBUFLEVEL		0x030
-#define TMC_BUFWM		0x034
-#define TMC_RRPHI		0x038
-#define TMC_RWPHI		0x03c
-#define TMC_AXICTL		0x110
-#define TMC_DBALO		0x118
-#define TMC_DBAHI		0x11c
-#define TMC_FFSR		0x300
-#define TMC_FFCR		0x304
-#define TMC_PSCR		0x308
-#define TMC_ITMISCOP0		0xee0
-#define TMC_ITTRFLIN		0xee8
-#define TMC_ITATBDATA0		0xeec
-#define TMC_ITATBCTR2		0xef0
-#define TMC_ITATBCTR1		0xef4
-#define TMC_ITATBCTR0		0xef8
-
-/* register description */
-/* TMC_CTL - 0x020 */
-#define TMC_CTL_CAPT_EN		BIT(0)
-/* TMC_STS - 0x00C */
-#define TMC_STS_TRIGGERED	BIT(1)
-/* TMC_AXICTL - 0x110 */
-#define TMC_AXICTL_PROT_CTL_B0	BIT(0)
-#define TMC_AXICTL_PROT_CTL_B1	BIT(1)
-#define TMC_AXICTL_SCT_GAT_MODE	BIT(7)
-#define TMC_AXICTL_WR_BURST_LEN 0xF00
-/* TMC_FFCR - 0x304 */
-#define TMC_FFCR_EN_FMT		BIT(0)
-#define TMC_FFCR_EN_TI		BIT(1)
-#define TMC_FFCR_FON_FLIN	BIT(4)
-#define TMC_FFCR_FON_TRIG_EVT	BIT(5)
-#define TMC_FFCR_FLUSHMAN	BIT(6)
-#define TMC_FFCR_TRIGON_TRIGIN	BIT(8)
-#define TMC_FFCR_STOP_ON_FLUSH	BIT(12)
-
-#define TMC_STS_TMCREADY_BIT	2
-#define TMC_FFCR_FLUSHMAN_BIT	6
-
-enum tmc_config_type {
-	TMC_CONFIG_TYPE_ETB,
-	TMC_CONFIG_TYPE_ETR,
-	TMC_CONFIG_TYPE_ETF,
-};
-
-enum tmc_mode {
-	TMC_MODE_CIRCULAR_BUFFER,
-	TMC_MODE_SOFTWARE_FIFO,
-	TMC_MODE_HARDWARE_FIFO,
-};
-
-enum tmc_mem_intf_width {
-	TMC_MEM_INTF_WIDTH_32BITS	= 0x2,
-	TMC_MEM_INTF_WIDTH_64BITS	= 0x3,
-	TMC_MEM_INTF_WIDTH_128BITS	= 0x4,
-	TMC_MEM_INTF_WIDTH_256BITS	= 0x5,
-};
-
-/**
- * struct tmc_drvdata - specifics associated to an TMC component
- * @base:	memory mapped base address for this component.
- * @dev:	the device entity associated to this component.
- * @csdev:	component vitals needed by the framework.
- * @miscdev:	specifics to handle "/dev/xyz.tmc" entry.
- * @spinlock:	only one at a time pls.
- * @read_count:	manages preparation of buffer for reading.
- * @buf:	area of memory where trace data get sent.
- * @paddr:	DMA start location in RAM.
- * @vaddr:	virtual representation of @paddr.
- * @size:	@buf size.
- * @enable:	this TMC is being used.
- * @config_type: TMC variant, must be of type @tmc_config_type.
- * @trigger_cntr: amount of words to store after a trigger.
- */
-struct tmc_drvdata {
-	void __iomem		*base;
-	struct device		*dev;
-	struct coresight_device	*csdev;
-	struct miscdevice	miscdev;
-	spinlock_t		spinlock;
-	int			read_count;
-	bool			reading;
-	char			*buf;
-	dma_addr_t		paddr;
-	void			*vaddr;
-	u32			size;
-	bool			enable;
-	enum tmc_config_type	config_type;
-	u32			trigger_cntr;
-};
+#include "coresight-tmc.h"
 
 static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
 {
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
new file mode 100644
index 000000000000..2d7d52747b4e
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -0,0 +1,122 @@
+/*
+ * 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 _CORESIGHT_TMC_H
+#define _CORESIGHT_TMC_H
+
+#define TMC_RSZ			0x004
+#define TMC_STS			0x00c
+#define TMC_RRD			0x010
+#define TMC_RRP			0x014
+#define TMC_RWP			0x018
+#define TMC_TRG			0x01c
+#define TMC_CTL			0x020
+#define TMC_RWD			0x024
+#define TMC_MODE		0x028
+#define TMC_LBUFLEVEL		0x02c
+#define TMC_CBUFLEVEL		0x030
+#define TMC_BUFWM		0x034
+#define TMC_RRPHI		0x038
+#define TMC_RWPHI		0x03c
+#define TMC_AXICTL		0x110
+#define TMC_DBALO		0x118
+#define TMC_DBAHI		0x11c
+#define TMC_FFSR		0x300
+#define TMC_FFCR		0x304
+#define TMC_PSCR		0x308
+#define TMC_ITMISCOP0		0xee0
+#define TMC_ITTRFLIN		0xee8
+#define TMC_ITATBDATA0		0xeec
+#define TMC_ITATBCTR2		0xef0
+#define TMC_ITATBCTR1		0xef4
+#define TMC_ITATBCTR0		0xef8
+
+/* register description */
+/* TMC_CTL - 0x020 */
+#define TMC_CTL_CAPT_EN		BIT(0)
+/* TMC_STS - 0x00C */
+#define TMC_STS_TRIGGERED	BIT(1)
+/* TMC_AXICTL - 0x110 */
+#define TMC_AXICTL_PROT_CTL_B0	BIT(0)
+#define TMC_AXICTL_PROT_CTL_B1	BIT(1)
+#define TMC_AXICTL_SCT_GAT_MODE	BIT(7)
+#define TMC_AXICTL_WR_BURST_LEN 0xF00
+/* TMC_FFCR - 0x304 */
+#define TMC_FFCR_EN_FMT		BIT(0)
+#define TMC_FFCR_EN_TI		BIT(1)
+#define TMC_FFCR_FON_FLIN	BIT(4)
+#define TMC_FFCR_FON_TRIG_EVT	BIT(5)
+#define TMC_FFCR_FLUSHMAN	BIT(6)
+#define TMC_FFCR_TRIGON_TRIGIN	BIT(8)
+#define TMC_FFCR_STOP_ON_FLUSH	BIT(12)
+
+#define TMC_STS_TMCREADY_BIT	2
+#define TMC_FFCR_FLUSHMAN_BIT	6
+
+enum tmc_config_type {
+	TMC_CONFIG_TYPE_ETB,
+	TMC_CONFIG_TYPE_ETR,
+	TMC_CONFIG_TYPE_ETF,
+};
+
+enum tmc_mode {
+	TMC_MODE_CIRCULAR_BUFFER,
+	TMC_MODE_SOFTWARE_FIFO,
+	TMC_MODE_HARDWARE_FIFO,
+};
+
+enum tmc_mem_intf_width {
+	TMC_MEM_INTF_WIDTH_32BITS	= 0x2,
+	TMC_MEM_INTF_WIDTH_64BITS	= 0x3,
+	TMC_MEM_INTF_WIDTH_128BITS	= 0x4,
+	TMC_MEM_INTF_WIDTH_256BITS	= 0x5,
+};
+
+/**
+ * struct tmc_drvdata - specifics associated to an TMC component
+ * @base:	memory mapped base address for this component.
+ * @dev:	the device entity associated to this component.
+ * @csdev:	component vitals needed by the framework.
+ * @miscdev:	specifics to handle "/dev/xyz.tmc" entry.
+ * @spinlock:	only one at a time pls.
+ * @read_count:	manages preparation of buffer for reading.
+ * @buf:	area of memory where trace data get sent.
+ * @paddr:	DMA start location in RAM.
+ * @vaddr:	virtual representation of @paddr.
+ * @size:	@buf size.
+ * @enable:	this TMC is being used.
+ * @config_type: TMC variant, must be of type @tmc_config_type.
+ * @trigger_cntr: amount of words to store after a trigger.
+ */
+struct tmc_drvdata {
+	void __iomem		*base;
+	struct device		*dev;
+	struct coresight_device	*csdev;
+	struct miscdevice	miscdev;
+	spinlock_t		spinlock;
+	int			read_count;
+	bool			reading;
+	char			*buf;
+	dma_addr_t		paddr;
+	void __iomem		*vaddr;
+	u32			size;
+	bool			enable;
+	enum tmc_config_type	config_type;
+	u32			trigger_cntr;
+};
+
+#endif
-- 
2.1.4

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

* [PATCH 05/14] coresight: tmc: splitting driver in ETB/ETF and ETR components
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

The TMC block can operate in 3 modes (ETB, ETF and ETR) and accessed
via two interfaces (sysFS and Perf).  That makes 6 mode to cover, which
is way too much coupling for a single file.

This patch splits the original TMC driver in 2 halves, one for ETB/ETF
and another one for ETR mode.  A common core is kept for functionality
common to all 3 modes.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/Makefile            |   4 +-
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 204 ++++++++++++++++++
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 128 ++++++++++++
 drivers/hwtracing/coresight/coresight-tmc.c     | 264 +-----------------------
 drivers/hwtracing/coresight/coresight-tmc.h     |  18 ++
 5 files changed, 357 insertions(+), 261 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-tmc-etf.c
 create mode 100644 drivers/hwtracing/coresight/coresight-tmc-etr.c

diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 1d0e32c7dbe4..0cf842ea3fe2 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -3,7 +3,9 @@
 #
 obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o
 obj-$(CONFIG_OF) += of_coresight.o
-obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o
+obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \
+					     coresight-tmc-etf.o \
+					     coresight-tmc-etr.o
 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 \
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
new file mode 100644
index 000000000000..467d19221f7b
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright(C) 2016 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 "coresight-priv.h"
+#include "coresight-tmc.h"
+
+void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
+{
+	/* Zero out the memory to help with debug */
+	memset(drvdata->buf, 0, drvdata->size);
+
+	CS_UNLOCK(drvdata->base);
+
+	/* Wait for TMCSReady bit to be set */
+	tmc_wait_for_tmcready(drvdata);
+
+	writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
+	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
+		       TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
+		       TMC_FFCR_TRIGON_TRIGIN,
+		       drvdata->base + TMC_FFCR);
+
+	writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
+	tmc_enable_hw(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
+static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
+{
+	enum tmc_mem_intf_width memwidth;
+	u8 memwords;
+	char *bufp;
+	u32 read_data;
+	int i;
+
+	memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10);
+	if (memwidth == TMC_MEM_INTF_WIDTH_32BITS)
+		memwords = 1;
+	else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS)
+		memwords = 2;
+	else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS)
+		memwords = 4;
+	else
+		memwords = 8;
+
+	bufp = drvdata->buf;
+	while (1) {
+		for (i = 0; i < memwords; i++) {
+			read_data = readl_relaxed(drvdata->base + TMC_RRD);
+			if (read_data == 0xFFFFFFFF)
+				return;
+			memcpy(bufp, &read_data, 4);
+			bufp += 4;
+		}
+	}
+}
+
+void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
+{
+	CS_UNLOCK(drvdata->base);
+
+	tmc_flush_and_stop(drvdata);
+	tmc_etb_dump_hw(drvdata);
+	tmc_disable_hw(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
+static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
+{
+	CS_UNLOCK(drvdata->base);
+
+	/* Wait for TMCSReady bit to be set */
+	tmc_wait_for_tmcready(drvdata);
+
+	writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE);
+	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI,
+		       drvdata->base + TMC_FFCR);
+	writel_relaxed(0x0, drvdata->base + TMC_BUFWM);
+	tmc_enable_hw(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
+static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
+{
+	CS_UNLOCK(drvdata->base);
+
+	tmc_flush_and_stop(drvdata);
+	tmc_disable_hw(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
+static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
+{
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	tmc_etb_enable_hw(drvdata);
+	drvdata->enable = true;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n");
+	return 0;
+}
+
+static void tmc_disable_etf_sink(struct coresight_device *csdev)
+{
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return;
+	}
+
+	tmc_etb_disable_hw(drvdata);
+	drvdata->enable = false;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "TMC-ETB/ETF disabled\n");
+}
+
+static int tmc_enable_etf_link(struct coresight_device *csdev,
+			       int inport, int outport)
+{
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	tmc_etf_enable_hw(drvdata);
+	drvdata->enable = true;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "TMC-ETF enabled\n");
+	return 0;
+}
+
+static void tmc_disable_etf_link(struct coresight_device *csdev,
+				 int inport, int outport)
+{
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return;
+	}
+
+	tmc_etf_disable_hw(drvdata);
+	drvdata->enable = false;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "TMC disabled\n");
+}
+
+static const struct coresight_ops_sink tmc_etf_sink_ops = {
+	.enable		= tmc_enable_etf_sink,
+	.disable	= tmc_disable_etf_sink,
+};
+
+static const struct coresight_ops_link tmc_etf_link_ops = {
+	.enable		= tmc_enable_etf_link,
+	.disable	= tmc_disable_etf_link,
+};
+
+const struct coresight_ops tmc_etb_cs_ops = {
+	.sink_ops	= &tmc_etf_sink_ops,
+};
+
+const struct coresight_ops tmc_etf_cs_ops = {
+	.sink_ops	= &tmc_etf_sink_ops,
+	.link_ops	= &tmc_etf_link_ops,
+};
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
new file mode 100644
index 000000000000..910d6f3b7d26
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright(C) 2016 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 "coresight-priv.h"
+#include "coresight-tmc.h"
+
+void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
+{
+	u32 axictl;
+
+	/* Zero out the memory to help with debug */
+	memset(drvdata->vaddr, 0, drvdata->size);
+
+	CS_UNLOCK(drvdata->base);
+
+	/* Wait for TMCSReady bit to be set */
+	tmc_wait_for_tmcready(drvdata);
+
+	writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ);
+	writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
+
+	axictl = readl_relaxed(drvdata->base + TMC_AXICTL);
+	axictl |= TMC_AXICTL_WR_BURST_LEN;
+	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
+	axictl &= ~TMC_AXICTL_SCT_GAT_MODE;
+	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
+	axictl = (axictl &
+		  ~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) |
+		  TMC_AXICTL_PROT_CTL_B1;
+	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
+
+	writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO);
+	writel_relaxed(0x0, drvdata->base + TMC_DBAHI);
+	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
+		       TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
+		       TMC_FFCR_TRIGON_TRIGIN,
+		       drvdata->base + TMC_FFCR);
+	writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
+	tmc_enable_hw(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
+static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
+{
+	u32 rwp, val;
+
+	rwp = readl_relaxed(drvdata->base + TMC_RWP);
+	val = readl_relaxed(drvdata->base + TMC_STS);
+
+	/* How much memory do we still have */
+	if (val & BIT(0))
+		drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
+	else
+		drvdata->buf = drvdata->vaddr;
+}
+
+void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
+{
+	CS_UNLOCK(drvdata->base);
+
+	tmc_flush_and_stop(drvdata);
+	tmc_etr_dump_hw(drvdata);
+	tmc_disable_hw(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
+static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
+{
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	tmc_etr_enable_hw(drvdata);
+	drvdata->enable = true;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "TMC-ETR enabled\n");
+	return 0;
+}
+
+static void tmc_disable_etr_sink(struct coresight_device *csdev)
+{
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return;
+	}
+
+	tmc_etr_disable_hw(drvdata);
+	drvdata->enable = false;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "TMC-ETR disabled\n");
+}
+
+static const struct coresight_ops_sink tmc_etr_sink_ops = {
+	.enable		= tmc_enable_etr_sink,
+	.disable	= tmc_disable_etr_sink,
+};
+
+const struct coresight_ops tmc_etr_cs_ops = {
+	.sink_ops	= &tmc_etr_sink_ops,
+};
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 64f744724d00..bc31131e4bd5 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -31,7 +31,7 @@
 #include "coresight-priv.h"
 #include "coresight-tmc.h"
 
-static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
+void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
 {
 	/* Ensure formatter, unformatter and hardware fifo are empty */
 	if (coresight_timeout(drvdata->base,
@@ -42,7 +42,7 @@ static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
 	}
 }
 
-static void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
+void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
 {
 	u32 ffcr;
 
@@ -62,272 +62,16 @@ static void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
 	tmc_wait_for_tmcready(drvdata);
 }
 
-static void tmc_enable_hw(struct tmc_drvdata *drvdata)
+void tmc_enable_hw(struct tmc_drvdata *drvdata)
 {
 	writel_relaxed(TMC_CTL_CAPT_EN, drvdata->base + TMC_CTL);
 }
 
-static void tmc_disable_hw(struct tmc_drvdata *drvdata)
+void tmc_disable_hw(struct tmc_drvdata *drvdata)
 {
 	writel_relaxed(0x0, drvdata->base + TMC_CTL);
 }
 
-static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
-{
-	/* Zero out the memory to help with debug */
-	memset(drvdata->buf, 0, drvdata->size);
-
-	CS_UNLOCK(drvdata->base);
-
-	/* Wait for TMCSReady bit to be set */
-	tmc_wait_for_tmcready(drvdata);
-
-	writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
-	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
-		       TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
-		       TMC_FFCR_TRIGON_TRIGIN,
-		       drvdata->base + TMC_FFCR);
-
-	writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
-	tmc_enable_hw(drvdata);
-
-	CS_LOCK(drvdata->base);
-}
-
-static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
-{
-	u32 axictl;
-
-	/* Zero out the memory to help with debug */
-	memset(drvdata->vaddr, 0, drvdata->size);
-
-	CS_UNLOCK(drvdata->base);
-
-	/* Wait for TMCSReady bit to be set */
-	tmc_wait_for_tmcready(drvdata);
-
-	writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ);
-	writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
-
-	axictl = readl_relaxed(drvdata->base + TMC_AXICTL);
-	axictl |= TMC_AXICTL_WR_BURST_LEN;
-	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
-	axictl &= ~TMC_AXICTL_SCT_GAT_MODE;
-	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
-	axictl = (axictl &
-		  ~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) |
-		  TMC_AXICTL_PROT_CTL_B1;
-	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
-
-	writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO);
-	writel_relaxed(0x0, drvdata->base + TMC_DBAHI);
-	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
-		       TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
-		       TMC_FFCR_TRIGON_TRIGIN,
-		       drvdata->base + TMC_FFCR);
-	writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
-	tmc_enable_hw(drvdata);
-
-	CS_LOCK(drvdata->base);
-}
-
-static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
-{
-	CS_UNLOCK(drvdata->base);
-
-	/* Wait for TMCSReady bit to be set */
-	tmc_wait_for_tmcready(drvdata);
-
-	writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE);
-	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI,
-		       drvdata->base + TMC_FFCR);
-	writel_relaxed(0x0, drvdata->base + TMC_BUFWM);
-	tmc_enable_hw(drvdata);
-
-	CS_LOCK(drvdata->base);
-}
-
-static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	if (drvdata->reading) {
-		spin_unlock_irqrestore(&drvdata->spinlock, flags);
-		return -EBUSY;
-	}
-
-	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
-		tmc_etb_enable_hw(drvdata);
-	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		tmc_etr_enable_hw(drvdata);
-	} else {
-		if (mode == TMC_MODE_CIRCULAR_BUFFER)
-			tmc_etb_enable_hw(drvdata);
-		else
-			tmc_etf_enable_hw(drvdata);
-	}
-	drvdata->enable = true;
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-
-	dev_info(drvdata->dev, "TMC enabled\n");
-	return 0;
-}
-
-static int tmc_enable_sink(struct coresight_device *csdev, u32 mode)
-{
-	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
-
-	return tmc_enable(drvdata, TMC_MODE_CIRCULAR_BUFFER);
-}
-
-static int tmc_enable_link(struct coresight_device *csdev, int inport,
-			   int outport)
-{
-	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
-
-	return tmc_enable(drvdata, TMC_MODE_HARDWARE_FIFO);
-}
-
-static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
-{
-	enum tmc_mem_intf_width memwidth;
-	u8 memwords;
-	char *bufp;
-	u32 read_data;
-	int i;
-
-	memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10);
-	if (memwidth == TMC_MEM_INTF_WIDTH_32BITS)
-		memwords = 1;
-	else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS)
-		memwords = 2;
-	else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS)
-		memwords = 4;
-	else
-		memwords = 8;
-
-	bufp = drvdata->buf;
-	while (1) {
-		for (i = 0; i < memwords; i++) {
-			read_data = readl_relaxed(drvdata->base + TMC_RRD);
-			if (read_data == 0xFFFFFFFF)
-				return;
-			memcpy(bufp, &read_data, 4);
-			bufp += 4;
-		}
-	}
-}
-
-static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
-{
-	CS_UNLOCK(drvdata->base);
-
-	tmc_flush_and_stop(drvdata);
-	tmc_etb_dump_hw(drvdata);
-	tmc_disable_hw(drvdata);
-
-	CS_LOCK(drvdata->base);
-}
-
-static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
-{
-	u32 rwp, val;
-
-	rwp = readl_relaxed(drvdata->base + TMC_RWP);
-	val = readl_relaxed(drvdata->base + TMC_STS);
-
-	/* How much memory do we still have */
-	if (val & BIT(0))
-		drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
-	else
-		drvdata->buf = drvdata->vaddr;
-}
-
-static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
-{
-	CS_UNLOCK(drvdata->base);
-
-	tmc_flush_and_stop(drvdata);
-	tmc_etr_dump_hw(drvdata);
-	tmc_disable_hw(drvdata);
-
-	CS_LOCK(drvdata->base);
-}
-
-static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
-{
-	CS_UNLOCK(drvdata->base);
-
-	tmc_flush_and_stop(drvdata);
-	tmc_disable_hw(drvdata);
-
-	CS_LOCK(drvdata->base);
-}
-
-static void tmc_disable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	if (drvdata->reading)
-		goto out;
-
-	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
-		tmc_etb_disable_hw(drvdata);
-	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		tmc_etr_disable_hw(drvdata);
-	} else {
-		if (mode == TMC_MODE_CIRCULAR_BUFFER)
-			tmc_etb_disable_hw(drvdata);
-		else
-			tmc_etf_disable_hw(drvdata);
-	}
-out:
-	drvdata->enable = false;
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-
-	dev_info(drvdata->dev, "TMC disabled\n");
-}
-
-static void tmc_disable_sink(struct coresight_device *csdev)
-{
-	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
-
-	tmc_disable(drvdata, TMC_MODE_CIRCULAR_BUFFER);
-}
-
-static void tmc_disable_link(struct coresight_device *csdev, int inport,
-			     int outport)
-{
-	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
-
-	tmc_disable(drvdata, TMC_MODE_HARDWARE_FIFO);
-}
-
-static const struct coresight_ops_sink tmc_sink_ops = {
-	.enable		= tmc_enable_sink,
-	.disable	= tmc_disable_sink,
-};
-
-static const struct coresight_ops_link tmc_link_ops = {
-	.enable		= tmc_enable_link,
-	.disable	= tmc_disable_link,
-};
-
-static const struct coresight_ops tmc_etb_cs_ops = {
-	.sink_ops	= &tmc_sink_ops,
-};
-
-static const struct coresight_ops tmc_etr_cs_ops = {
-	.sink_ops	= &tmc_sink_ops,
-};
-
-static const struct coresight_ops tmc_etf_cs_ops = {
-	.sink_ops	= &tmc_sink_ops,
-	.link_ops	= &tmc_link_ops,
-};
-
 static int tmc_read_prepare(struct tmc_drvdata *drvdata)
 {
 	int ret = 0;
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 2d7d52747b4e..b99d4dfc1d0b 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -18,6 +18,8 @@
 #ifndef _CORESIGHT_TMC_H
 #define _CORESIGHT_TMC_H
 
+#include <linux/miscdevice.h>
+
 #define TMC_RSZ			0x004
 #define TMC_STS			0x00c
 #define TMC_RRD			0x010
@@ -119,4 +121,20 @@ struct tmc_drvdata {
 	u32			trigger_cntr;
 };
 
+/* Generic functions */
+void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata);
+void tmc_flush_and_stop(struct tmc_drvdata *drvdata);
+void tmc_enable_hw(struct tmc_drvdata *drvdata);
+void tmc_disable_hw(struct tmc_drvdata *drvdata);
+
+/* ETB/ETF functions */
+void tmc_etb_enable_hw(struct tmc_drvdata *drvdata);
+void tmc_etb_disable_hw(struct tmc_drvdata *drvdata);
+extern const struct coresight_ops tmc_etb_cs_ops;
+extern const struct coresight_ops tmc_etf_cs_ops;
+
+/* ETR functions */
+void tmc_etr_enable_hw(struct tmc_drvdata *drvdata);
+void tmc_etr_disable_hw(struct tmc_drvdata *drvdata);
+extern const struct coresight_ops tmc_etr_cs_ops;
 #endif
-- 
2.1.4

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

* [PATCH 05/14] coresight: tmc: splitting driver in ETB/ETF and ETR components
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

The TMC block can operate in 3 modes (ETB, ETF and ETR) and accessed
via two interfaces (sysFS and Perf).  That makes 6 mode to cover, which
is way too much coupling for a single file.

This patch splits the original TMC driver in 2 halves, one for ETB/ETF
and another one for ETR mode.  A common core is kept for functionality
common to all 3 modes.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/Makefile            |   4 +-
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 204 ++++++++++++++++++
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 128 ++++++++++++
 drivers/hwtracing/coresight/coresight-tmc.c     | 264 +-----------------------
 drivers/hwtracing/coresight/coresight-tmc.h     |  18 ++
 5 files changed, 357 insertions(+), 261 deletions(-)
 create mode 100644 drivers/hwtracing/coresight/coresight-tmc-etf.c
 create mode 100644 drivers/hwtracing/coresight/coresight-tmc-etr.c

diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 1d0e32c7dbe4..0cf842ea3fe2 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -3,7 +3,9 @@
 #
 obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o
 obj-$(CONFIG_OF) += of_coresight.o
-obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o
+obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \
+					     coresight-tmc-etf.o \
+					     coresight-tmc-etr.o
 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 \
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
new file mode 100644
index 000000000000..467d19221f7b
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright(C) 2016 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 "coresight-priv.h"
+#include "coresight-tmc.h"
+
+void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
+{
+	/* Zero out the memory to help with debug */
+	memset(drvdata->buf, 0, drvdata->size);
+
+	CS_UNLOCK(drvdata->base);
+
+	/* Wait for TMCSReady bit to be set */
+	tmc_wait_for_tmcready(drvdata);
+
+	writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
+	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
+		       TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
+		       TMC_FFCR_TRIGON_TRIGIN,
+		       drvdata->base + TMC_FFCR);
+
+	writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
+	tmc_enable_hw(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
+static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
+{
+	enum tmc_mem_intf_width memwidth;
+	u8 memwords;
+	char *bufp;
+	u32 read_data;
+	int i;
+
+	memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10);
+	if (memwidth == TMC_MEM_INTF_WIDTH_32BITS)
+		memwords = 1;
+	else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS)
+		memwords = 2;
+	else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS)
+		memwords = 4;
+	else
+		memwords = 8;
+
+	bufp = drvdata->buf;
+	while (1) {
+		for (i = 0; i < memwords; i++) {
+			read_data = readl_relaxed(drvdata->base + TMC_RRD);
+			if (read_data == 0xFFFFFFFF)
+				return;
+			memcpy(bufp, &read_data, 4);
+			bufp += 4;
+		}
+	}
+}
+
+void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
+{
+	CS_UNLOCK(drvdata->base);
+
+	tmc_flush_and_stop(drvdata);
+	tmc_etb_dump_hw(drvdata);
+	tmc_disable_hw(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
+static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
+{
+	CS_UNLOCK(drvdata->base);
+
+	/* Wait for TMCSReady bit to be set */
+	tmc_wait_for_tmcready(drvdata);
+
+	writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE);
+	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI,
+		       drvdata->base + TMC_FFCR);
+	writel_relaxed(0x0, drvdata->base + TMC_BUFWM);
+	tmc_enable_hw(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
+static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
+{
+	CS_UNLOCK(drvdata->base);
+
+	tmc_flush_and_stop(drvdata);
+	tmc_disable_hw(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
+static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
+{
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	tmc_etb_enable_hw(drvdata);
+	drvdata->enable = true;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n");
+	return 0;
+}
+
+static void tmc_disable_etf_sink(struct coresight_device *csdev)
+{
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return;
+	}
+
+	tmc_etb_disable_hw(drvdata);
+	drvdata->enable = false;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "TMC-ETB/ETF disabled\n");
+}
+
+static int tmc_enable_etf_link(struct coresight_device *csdev,
+			       int inport, int outport)
+{
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	tmc_etf_enable_hw(drvdata);
+	drvdata->enable = true;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "TMC-ETF enabled\n");
+	return 0;
+}
+
+static void tmc_disable_etf_link(struct coresight_device *csdev,
+				 int inport, int outport)
+{
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return;
+	}
+
+	tmc_etf_disable_hw(drvdata);
+	drvdata->enable = false;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "TMC disabled\n");
+}
+
+static const struct coresight_ops_sink tmc_etf_sink_ops = {
+	.enable		= tmc_enable_etf_sink,
+	.disable	= tmc_disable_etf_sink,
+};
+
+static const struct coresight_ops_link tmc_etf_link_ops = {
+	.enable		= tmc_enable_etf_link,
+	.disable	= tmc_disable_etf_link,
+};
+
+const struct coresight_ops tmc_etb_cs_ops = {
+	.sink_ops	= &tmc_etf_sink_ops,
+};
+
+const struct coresight_ops tmc_etf_cs_ops = {
+	.sink_ops	= &tmc_etf_sink_ops,
+	.link_ops	= &tmc_etf_link_ops,
+};
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
new file mode 100644
index 000000000000..910d6f3b7d26
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright(C) 2016 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 "coresight-priv.h"
+#include "coresight-tmc.h"
+
+void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
+{
+	u32 axictl;
+
+	/* Zero out the memory to help with debug */
+	memset(drvdata->vaddr, 0, drvdata->size);
+
+	CS_UNLOCK(drvdata->base);
+
+	/* Wait for TMCSReady bit to be set */
+	tmc_wait_for_tmcready(drvdata);
+
+	writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ);
+	writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
+
+	axictl = readl_relaxed(drvdata->base + TMC_AXICTL);
+	axictl |= TMC_AXICTL_WR_BURST_LEN;
+	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
+	axictl &= ~TMC_AXICTL_SCT_GAT_MODE;
+	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
+	axictl = (axictl &
+		  ~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) |
+		  TMC_AXICTL_PROT_CTL_B1;
+	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
+
+	writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO);
+	writel_relaxed(0x0, drvdata->base + TMC_DBAHI);
+	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
+		       TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
+		       TMC_FFCR_TRIGON_TRIGIN,
+		       drvdata->base + TMC_FFCR);
+	writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
+	tmc_enable_hw(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
+static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
+{
+	u32 rwp, val;
+
+	rwp = readl_relaxed(drvdata->base + TMC_RWP);
+	val = readl_relaxed(drvdata->base + TMC_STS);
+
+	/* How much memory do we still have */
+	if (val & BIT(0))
+		drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
+	else
+		drvdata->buf = drvdata->vaddr;
+}
+
+void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
+{
+	CS_UNLOCK(drvdata->base);
+
+	tmc_flush_and_stop(drvdata);
+	tmc_etr_dump_hw(drvdata);
+	tmc_disable_hw(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
+static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
+{
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	tmc_etr_enable_hw(drvdata);
+	drvdata->enable = true;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "TMC-ETR enabled\n");
+	return 0;
+}
+
+static void tmc_disable_etr_sink(struct coresight_device *csdev)
+{
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return;
+	}
+
+	tmc_etr_disable_hw(drvdata);
+	drvdata->enable = false;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "TMC-ETR disabled\n");
+}
+
+static const struct coresight_ops_sink tmc_etr_sink_ops = {
+	.enable		= tmc_enable_etr_sink,
+	.disable	= tmc_disable_etr_sink,
+};
+
+const struct coresight_ops tmc_etr_cs_ops = {
+	.sink_ops	= &tmc_etr_sink_ops,
+};
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 64f744724d00..bc31131e4bd5 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -31,7 +31,7 @@
 #include "coresight-priv.h"
 #include "coresight-tmc.h"
 
-static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
+void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
 {
 	/* Ensure formatter, unformatter and hardware fifo are empty */
 	if (coresight_timeout(drvdata->base,
@@ -42,7 +42,7 @@ static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
 	}
 }
 
-static void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
+void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
 {
 	u32 ffcr;
 
@@ -62,272 +62,16 @@ static void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
 	tmc_wait_for_tmcready(drvdata);
 }
 
-static void tmc_enable_hw(struct tmc_drvdata *drvdata)
+void tmc_enable_hw(struct tmc_drvdata *drvdata)
 {
 	writel_relaxed(TMC_CTL_CAPT_EN, drvdata->base + TMC_CTL);
 }
 
-static void tmc_disable_hw(struct tmc_drvdata *drvdata)
+void tmc_disable_hw(struct tmc_drvdata *drvdata)
 {
 	writel_relaxed(0x0, drvdata->base + TMC_CTL);
 }
 
-static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
-{
-	/* Zero out the memory to help with debug */
-	memset(drvdata->buf, 0, drvdata->size);
-
-	CS_UNLOCK(drvdata->base);
-
-	/* Wait for TMCSReady bit to be set */
-	tmc_wait_for_tmcready(drvdata);
-
-	writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
-	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
-		       TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
-		       TMC_FFCR_TRIGON_TRIGIN,
-		       drvdata->base + TMC_FFCR);
-
-	writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
-	tmc_enable_hw(drvdata);
-
-	CS_LOCK(drvdata->base);
-}
-
-static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
-{
-	u32 axictl;
-
-	/* Zero out the memory to help with debug */
-	memset(drvdata->vaddr, 0, drvdata->size);
-
-	CS_UNLOCK(drvdata->base);
-
-	/* Wait for TMCSReady bit to be set */
-	tmc_wait_for_tmcready(drvdata);
-
-	writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ);
-	writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
-
-	axictl = readl_relaxed(drvdata->base + TMC_AXICTL);
-	axictl |= TMC_AXICTL_WR_BURST_LEN;
-	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
-	axictl &= ~TMC_AXICTL_SCT_GAT_MODE;
-	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
-	axictl = (axictl &
-		  ~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) |
-		  TMC_AXICTL_PROT_CTL_B1;
-	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
-
-	writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO);
-	writel_relaxed(0x0, drvdata->base + TMC_DBAHI);
-	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
-		       TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
-		       TMC_FFCR_TRIGON_TRIGIN,
-		       drvdata->base + TMC_FFCR);
-	writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
-	tmc_enable_hw(drvdata);
-
-	CS_LOCK(drvdata->base);
-}
-
-static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata)
-{
-	CS_UNLOCK(drvdata->base);
-
-	/* Wait for TMCSReady bit to be set */
-	tmc_wait_for_tmcready(drvdata);
-
-	writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE);
-	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI,
-		       drvdata->base + TMC_FFCR);
-	writel_relaxed(0x0, drvdata->base + TMC_BUFWM);
-	tmc_enable_hw(drvdata);
-
-	CS_LOCK(drvdata->base);
-}
-
-static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	if (drvdata->reading) {
-		spin_unlock_irqrestore(&drvdata->spinlock, flags);
-		return -EBUSY;
-	}
-
-	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
-		tmc_etb_enable_hw(drvdata);
-	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		tmc_etr_enable_hw(drvdata);
-	} else {
-		if (mode == TMC_MODE_CIRCULAR_BUFFER)
-			tmc_etb_enable_hw(drvdata);
-		else
-			tmc_etf_enable_hw(drvdata);
-	}
-	drvdata->enable = true;
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-
-	dev_info(drvdata->dev, "TMC enabled\n");
-	return 0;
-}
-
-static int tmc_enable_sink(struct coresight_device *csdev, u32 mode)
-{
-	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
-
-	return tmc_enable(drvdata, TMC_MODE_CIRCULAR_BUFFER);
-}
-
-static int tmc_enable_link(struct coresight_device *csdev, int inport,
-			   int outport)
-{
-	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
-
-	return tmc_enable(drvdata, TMC_MODE_HARDWARE_FIFO);
-}
-
-static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
-{
-	enum tmc_mem_intf_width memwidth;
-	u8 memwords;
-	char *bufp;
-	u32 read_data;
-	int i;
-
-	memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10);
-	if (memwidth == TMC_MEM_INTF_WIDTH_32BITS)
-		memwords = 1;
-	else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS)
-		memwords = 2;
-	else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS)
-		memwords = 4;
-	else
-		memwords = 8;
-
-	bufp = drvdata->buf;
-	while (1) {
-		for (i = 0; i < memwords; i++) {
-			read_data = readl_relaxed(drvdata->base + TMC_RRD);
-			if (read_data == 0xFFFFFFFF)
-				return;
-			memcpy(bufp, &read_data, 4);
-			bufp += 4;
-		}
-	}
-}
-
-static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
-{
-	CS_UNLOCK(drvdata->base);
-
-	tmc_flush_and_stop(drvdata);
-	tmc_etb_dump_hw(drvdata);
-	tmc_disable_hw(drvdata);
-
-	CS_LOCK(drvdata->base);
-}
-
-static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
-{
-	u32 rwp, val;
-
-	rwp = readl_relaxed(drvdata->base + TMC_RWP);
-	val = readl_relaxed(drvdata->base + TMC_STS);
-
-	/* How much memory do we still have */
-	if (val & BIT(0))
-		drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
-	else
-		drvdata->buf = drvdata->vaddr;
-}
-
-static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
-{
-	CS_UNLOCK(drvdata->base);
-
-	tmc_flush_and_stop(drvdata);
-	tmc_etr_dump_hw(drvdata);
-	tmc_disable_hw(drvdata);
-
-	CS_LOCK(drvdata->base);
-}
-
-static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
-{
-	CS_UNLOCK(drvdata->base);
-
-	tmc_flush_and_stop(drvdata);
-	tmc_disable_hw(drvdata);
-
-	CS_LOCK(drvdata->base);
-}
-
-static void tmc_disable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	if (drvdata->reading)
-		goto out;
-
-	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
-		tmc_etb_disable_hw(drvdata);
-	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		tmc_etr_disable_hw(drvdata);
-	} else {
-		if (mode == TMC_MODE_CIRCULAR_BUFFER)
-			tmc_etb_disable_hw(drvdata);
-		else
-			tmc_etf_disable_hw(drvdata);
-	}
-out:
-	drvdata->enable = false;
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-
-	dev_info(drvdata->dev, "TMC disabled\n");
-}
-
-static void tmc_disable_sink(struct coresight_device *csdev)
-{
-	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
-
-	tmc_disable(drvdata, TMC_MODE_CIRCULAR_BUFFER);
-}
-
-static void tmc_disable_link(struct coresight_device *csdev, int inport,
-			     int outport)
-{
-	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
-
-	tmc_disable(drvdata, TMC_MODE_HARDWARE_FIFO);
-}
-
-static const struct coresight_ops_sink tmc_sink_ops = {
-	.enable		= tmc_enable_sink,
-	.disable	= tmc_disable_sink,
-};
-
-static const struct coresight_ops_link tmc_link_ops = {
-	.enable		= tmc_enable_link,
-	.disable	= tmc_disable_link,
-};
-
-static const struct coresight_ops tmc_etb_cs_ops = {
-	.sink_ops	= &tmc_sink_ops,
-};
-
-static const struct coresight_ops tmc_etr_cs_ops = {
-	.sink_ops	= &tmc_sink_ops,
-};
-
-static const struct coresight_ops tmc_etf_cs_ops = {
-	.sink_ops	= &tmc_sink_ops,
-	.link_ops	= &tmc_link_ops,
-};
-
 static int tmc_read_prepare(struct tmc_drvdata *drvdata)
 {
 	int ret = 0;
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 2d7d52747b4e..b99d4dfc1d0b 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -18,6 +18,8 @@
 #ifndef _CORESIGHT_TMC_H
 #define _CORESIGHT_TMC_H
 
+#include <linux/miscdevice.h>
+
 #define TMC_RSZ			0x004
 #define TMC_STS			0x00c
 #define TMC_RRD			0x010
@@ -119,4 +121,20 @@ struct tmc_drvdata {
 	u32			trigger_cntr;
 };
 
+/* Generic functions */
+void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata);
+void tmc_flush_and_stop(struct tmc_drvdata *drvdata);
+void tmc_enable_hw(struct tmc_drvdata *drvdata);
+void tmc_disable_hw(struct tmc_drvdata *drvdata);
+
+/* ETB/ETF functions */
+void tmc_etb_enable_hw(struct tmc_drvdata *drvdata);
+void tmc_etb_disable_hw(struct tmc_drvdata *drvdata);
+extern const struct coresight_ops tmc_etb_cs_ops;
+extern const struct coresight_ops tmc_etf_cs_ops;
+
+/* ETR functions */
+void tmc_etr_enable_hw(struct tmc_drvdata *drvdata);
+void tmc_etr_disable_hw(struct tmc_drvdata *drvdata);
+extern const struct coresight_ops tmc_etr_cs_ops;
 #endif
-- 
2.1.4

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

* [PATCH 06/14] coresight: tmc: making prepare/unprepare functions generic
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

Dealing with HW related matters in tmc_read_prepare/unprepare
becomes convoluted when many cases need to be handled distinctively.

As such moving processing related to HW setup to individual driver
files and keep the core driver generic.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 68 ++++++++++++++++++++++++-
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 50 +++++++++++++++++-
 drivers/hwtracing/coresight/coresight-tmc.c     | 55 ++++----------------
 drivers/hwtracing/coresight/coresight-tmc.h     |  8 +--
 4 files changed, 131 insertions(+), 50 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index 467d19221f7b..789384be81b6 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -71,7 +71,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
 	}
 }
 
-void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
+static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
 {
 	CS_UNLOCK(drvdata->base);
 
@@ -202,3 +202,69 @@ const struct coresight_ops tmc_etf_cs_ops = {
 	.sink_ops	= &tmc_etf_sink_ops,
 	.link_ops	= &tmc_etf_link_ops,
 };
+
+int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)
+{
+	int ret = 0;
+	enum tmc_mode mode;
+	unsigned long flags;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	/* The TMC isn't enable, so there is no need to disable it */
+	if (!drvdata->enable)
+		goto out;
+
+	if (drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
+	    drvdata->config_type != TMC_CONFIG_TYPE_ETF) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* There is no point in reading a TMC in HW FIFO mode */
+	mode = readl_relaxed(drvdata->base + TMC_MODE);
+	if (mode != TMC_MODE_CIRCULAR_BUFFER) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	tmc_etb_disable_hw(drvdata);
+	drvdata->reading = true;
+
+out:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	return ret;
+}
+
+int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
+{
+	int ret = 0;
+	enum tmc_mode mode;
+	unsigned long flags;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	/* The TMC isn't enable, so there is no need to enable it */
+	if (!drvdata->enable)
+		goto out;
+
+	if (drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
+	    drvdata->config_type != TMC_CONFIG_TYPE_ETF) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* There is no point in reading a TMC in HW FIFO mode */
+	mode = readl_relaxed(drvdata->base + TMC_MODE);
+	if (mode != TMC_MODE_CIRCULAR_BUFFER) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	tmc_etb_enable_hw(drvdata);
+	drvdata->reading = false;
+
+out:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	return ret;
+}
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 910d6f3b7d26..e1b43e0bd1dd 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -70,7 +70,7 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
 		drvdata->buf = drvdata->vaddr;
 }
 
-void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
+static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 {
 	CS_UNLOCK(drvdata->base);
 
@@ -126,3 +126,51 @@ static const struct coresight_ops_sink tmc_etr_sink_ops = {
 const struct coresight_ops tmc_etr_cs_ops = {
 	.sink_ops	= &tmc_etr_sink_ops,
 };
+
+int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
+{
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	/* The TMC isn't enable, so there is no need to disable it */
+	if (!drvdata->enable)
+		goto out;
+
+	if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	tmc_etr_disable_hw(drvdata);
+	drvdata->reading = true;
+
+out:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	return ret;
+}
+
+int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
+{
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	/* The TMC isn't enable, so there is no need to enable it */
+	if (!drvdata->enable)
+		goto out;
+
+	if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	tmc_etr_enable_hw(drvdata);
+	drvdata->reading = false;
+
+out:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	return ret;
+}
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index bc31131e4bd5..26dbef305851 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -75,76 +75,43 @@ void tmc_disable_hw(struct tmc_drvdata *drvdata)
 static int tmc_read_prepare(struct tmc_drvdata *drvdata)
 {
 	int ret = 0;
-	unsigned long flags;
-	enum tmc_mode mode;
-
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	if (!drvdata->enable)
-		goto out;
 
 	switch (drvdata->config_type) {
 	case TMC_CONFIG_TYPE_ETB:
-		tmc_etb_disable_hw(drvdata);
-		break;
 	case TMC_CONFIG_TYPE_ETF:
-		/* There is no point in reading a TMC in HW FIFO mode */
-		mode = readl_relaxed(drvdata->base + TMC_MODE);
-		if (mode != TMC_MODE_CIRCULAR_BUFFER) {
-			ret = -EINVAL;
-			goto err;
-		}
-
-		tmc_etb_disable_hw(drvdata);
+		ret = tmc_read_prepare_etf(drvdata);
 		break;
 	case TMC_CONFIG_TYPE_ETR:
-		tmc_etr_disable_hw(drvdata);
+		ret = tmc_read_prepare_etr(drvdata);
 		break;
 	default:
 		ret = -EINVAL;
-		goto err;
 	}
 
-out:
-	drvdata->reading = true;
-	dev_info(drvdata->dev, "TMC read start\n");
-err:
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	if (!ret)
+		dev_info(drvdata->dev, "TMC read start\n");
+
 	return ret;
 }
 
 static void tmc_read_unprepare(struct tmc_drvdata *drvdata)
 {
-	unsigned long flags;
-	enum tmc_mode mode;
-
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	if (!drvdata->enable)
-		goto out;
+	int ret = 0;
 
 	switch (drvdata->config_type) {
 	case TMC_CONFIG_TYPE_ETB:
-		tmc_etb_enable_hw(drvdata);
-		break;
 	case TMC_CONFIG_TYPE_ETF:
-		/* Make sure we don't re-enable a TMC in HW FIFO mode */
-		mode = readl_relaxed(drvdata->base + TMC_MODE);
-		if (mode != TMC_MODE_CIRCULAR_BUFFER)
-			goto err;
-
-		tmc_etb_enable_hw(drvdata);
+		ret = tmc_read_unprepare_etf(drvdata);
 		break;
 	case TMC_CONFIG_TYPE_ETR:
-		tmc_etr_disable_hw(drvdata);
+		ret = tmc_read_unprepare_etr(drvdata);
 		break;
 	default:
-		goto err;
+		ret = -EINVAL;
 	}
 
-out:
-	drvdata->reading = false;
-	dev_info(drvdata->dev, "TMC read end\n");
-err:
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	if (!ret)
+		dev_info(drvdata->dev, "TMC read end\n");
 }
 
 static int tmc_open(struct inode *inode, struct file *file)
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index b99d4dfc1d0b..6b11caf77ad1 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -128,13 +128,13 @@ void tmc_enable_hw(struct tmc_drvdata *drvdata);
 void tmc_disable_hw(struct tmc_drvdata *drvdata);
 
 /* ETB/ETF functions */
-void tmc_etb_enable_hw(struct tmc_drvdata *drvdata);
-void tmc_etb_disable_hw(struct tmc_drvdata *drvdata);
+int tmc_read_prepare_etf(struct tmc_drvdata *drvdata);
+int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata);
 extern const struct coresight_ops tmc_etb_cs_ops;
 extern const struct coresight_ops tmc_etf_cs_ops;
 
 /* ETR functions */
-void tmc_etr_enable_hw(struct tmc_drvdata *drvdata);
-void tmc_etr_disable_hw(struct tmc_drvdata *drvdata);
+int tmc_read_prepare_etr(struct tmc_drvdata *drvdata);
+int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata);
 extern const struct coresight_ops tmc_etr_cs_ops;
 #endif
-- 
2.1.4

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

* [PATCH 06/14] coresight: tmc: making prepare/unprepare functions generic
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

Dealing with HW related matters in tmc_read_prepare/unprepare
becomes convoluted when many cases need to be handled distinctively.

As such moving processing related to HW setup to individual driver
files and keep the core driver generic.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 68 ++++++++++++++++++++++++-
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 50 +++++++++++++++++-
 drivers/hwtracing/coresight/coresight-tmc.c     | 55 ++++----------------
 drivers/hwtracing/coresight/coresight-tmc.h     |  8 +--
 4 files changed, 131 insertions(+), 50 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index 467d19221f7b..789384be81b6 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -71,7 +71,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
 	}
 }
 
-void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
+static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
 {
 	CS_UNLOCK(drvdata->base);
 
@@ -202,3 +202,69 @@ const struct coresight_ops tmc_etf_cs_ops = {
 	.sink_ops	= &tmc_etf_sink_ops,
 	.link_ops	= &tmc_etf_link_ops,
 };
+
+int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)
+{
+	int ret = 0;
+	enum tmc_mode mode;
+	unsigned long flags;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	/* The TMC isn't enable, so there is no need to disable it */
+	if (!drvdata->enable)
+		goto out;
+
+	if (drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
+	    drvdata->config_type != TMC_CONFIG_TYPE_ETF) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* There is no point in reading a TMC in HW FIFO mode */
+	mode = readl_relaxed(drvdata->base + TMC_MODE);
+	if (mode != TMC_MODE_CIRCULAR_BUFFER) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	tmc_etb_disable_hw(drvdata);
+	drvdata->reading = true;
+
+out:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	return ret;
+}
+
+int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
+{
+	int ret = 0;
+	enum tmc_mode mode;
+	unsigned long flags;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	/* The TMC isn't enable, so there is no need to enable it */
+	if (!drvdata->enable)
+		goto out;
+
+	if (drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
+	    drvdata->config_type != TMC_CONFIG_TYPE_ETF) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* There is no point in reading a TMC in HW FIFO mode */
+	mode = readl_relaxed(drvdata->base + TMC_MODE);
+	if (mode != TMC_MODE_CIRCULAR_BUFFER) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	tmc_etb_enable_hw(drvdata);
+	drvdata->reading = false;
+
+out:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	return ret;
+}
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 910d6f3b7d26..e1b43e0bd1dd 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -70,7 +70,7 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
 		drvdata->buf = drvdata->vaddr;
 }
 
-void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
+static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 {
 	CS_UNLOCK(drvdata->base);
 
@@ -126,3 +126,51 @@ static const struct coresight_ops_sink tmc_etr_sink_ops = {
 const struct coresight_ops tmc_etr_cs_ops = {
 	.sink_ops	= &tmc_etr_sink_ops,
 };
+
+int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
+{
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	/* The TMC isn't enable, so there is no need to disable it */
+	if (!drvdata->enable)
+		goto out;
+
+	if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	tmc_etr_disable_hw(drvdata);
+	drvdata->reading = true;
+
+out:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	return ret;
+}
+
+int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
+{
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+
+	/* The TMC isn't enable, so there is no need to enable it */
+	if (!drvdata->enable)
+		goto out;
+
+	if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	tmc_etr_enable_hw(drvdata);
+	drvdata->reading = false;
+
+out:
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	return ret;
+}
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index bc31131e4bd5..26dbef305851 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -75,76 +75,43 @@ void tmc_disable_hw(struct tmc_drvdata *drvdata)
 static int tmc_read_prepare(struct tmc_drvdata *drvdata)
 {
 	int ret = 0;
-	unsigned long flags;
-	enum tmc_mode mode;
-
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	if (!drvdata->enable)
-		goto out;
 
 	switch (drvdata->config_type) {
 	case TMC_CONFIG_TYPE_ETB:
-		tmc_etb_disable_hw(drvdata);
-		break;
 	case TMC_CONFIG_TYPE_ETF:
-		/* There is no point in reading a TMC in HW FIFO mode */
-		mode = readl_relaxed(drvdata->base + TMC_MODE);
-		if (mode != TMC_MODE_CIRCULAR_BUFFER) {
-			ret = -EINVAL;
-			goto err;
-		}
-
-		tmc_etb_disable_hw(drvdata);
+		ret = tmc_read_prepare_etf(drvdata);
 		break;
 	case TMC_CONFIG_TYPE_ETR:
-		tmc_etr_disable_hw(drvdata);
+		ret = tmc_read_prepare_etr(drvdata);
 		break;
 	default:
 		ret = -EINVAL;
-		goto err;
 	}
 
-out:
-	drvdata->reading = true;
-	dev_info(drvdata->dev, "TMC read start\n");
-err:
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	if (!ret)
+		dev_info(drvdata->dev, "TMC read start\n");
+
 	return ret;
 }
 
 static void tmc_read_unprepare(struct tmc_drvdata *drvdata)
 {
-	unsigned long flags;
-	enum tmc_mode mode;
-
-	spin_lock_irqsave(&drvdata->spinlock, flags);
-	if (!drvdata->enable)
-		goto out;
+	int ret = 0;
 
 	switch (drvdata->config_type) {
 	case TMC_CONFIG_TYPE_ETB:
-		tmc_etb_enable_hw(drvdata);
-		break;
 	case TMC_CONFIG_TYPE_ETF:
-		/* Make sure we don't re-enable a TMC in HW FIFO mode */
-		mode = readl_relaxed(drvdata->base + TMC_MODE);
-		if (mode != TMC_MODE_CIRCULAR_BUFFER)
-			goto err;
-
-		tmc_etb_enable_hw(drvdata);
+		ret = tmc_read_unprepare_etf(drvdata);
 		break;
 	case TMC_CONFIG_TYPE_ETR:
-		tmc_etr_disable_hw(drvdata);
+		ret = tmc_read_unprepare_etr(drvdata);
 		break;
 	default:
-		goto err;
+		ret = -EINVAL;
 	}
 
-out:
-	drvdata->reading = false;
-	dev_info(drvdata->dev, "TMC read end\n");
-err:
-	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	if (!ret)
+		dev_info(drvdata->dev, "TMC read end\n");
 }
 
 static int tmc_open(struct inode *inode, struct file *file)
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index b99d4dfc1d0b..6b11caf77ad1 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -128,13 +128,13 @@ void tmc_enable_hw(struct tmc_drvdata *drvdata);
 void tmc_disable_hw(struct tmc_drvdata *drvdata);
 
 /* ETB/ETF functions */
-void tmc_etb_enable_hw(struct tmc_drvdata *drvdata);
-void tmc_etb_disable_hw(struct tmc_drvdata *drvdata);
+int tmc_read_prepare_etf(struct tmc_drvdata *drvdata);
+int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata);
 extern const struct coresight_ops tmc_etb_cs_ops;
 extern const struct coresight_ops tmc_etf_cs_ops;
 
 /* ETR functions */
-void tmc_etr_enable_hw(struct tmc_drvdata *drvdata);
-void tmc_etr_disable_hw(struct tmc_drvdata *drvdata);
+int tmc_read_prepare_etr(struct tmc_drvdata *drvdata);
+int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata);
 extern const struct coresight_ops tmc_etr_cs_ops;
 #endif
-- 
2.1.4

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

* [PATCH 07/14] coresight: tmc: making disable function reusable
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

When disabling a TMC in any mode it may not be automatically
desirable to read the content of the trace buffer.  It is
the case when operating in Perf mode where the content of the
trace buffer is dealt with prior to the TMC being disabled.

As such moving the tmc_etb/etr_dump() functions out of the
disable_hw callback and into the current sysFS interface.

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

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index 789384be81b6..f9749924a055 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -76,7 +76,6 @@ static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
 	CS_UNLOCK(drvdata->base);
 
 	tmc_flush_and_stop(drvdata);
-	tmc_etb_dump_hw(drvdata);
 	tmc_disable_hw(drvdata);
 
 	CS_LOCK(drvdata->base);
@@ -139,6 +138,7 @@ static void tmc_disable_etf_sink(struct coresight_device *csdev)
 	}
 
 	tmc_etb_disable_hw(drvdata);
+	tmc_etb_dump_hw(drvdata);
 	drvdata->enable = false;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index e1b43e0bd1dd..67e7d5dd891f 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -75,7 +75,6 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 	CS_UNLOCK(drvdata->base);
 
 	tmc_flush_and_stop(drvdata);
-	tmc_etr_dump_hw(drvdata);
 	tmc_disable_hw(drvdata);
 
 	CS_LOCK(drvdata->base);
@@ -112,6 +111,7 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev)
 	}
 
 	tmc_etr_disable_hw(drvdata);
+	tmc_etr_dump_hw(drvdata);
 	drvdata->enable = false;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
-- 
2.1.4

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

* [PATCH 07/14] coresight: tmc: making disable function reusable
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

When disabling a TMC in any mode it may not be automatically
desirable to read the content of the trace buffer.  It is
the case when operating in Perf mode where the content of the
trace buffer is dealt with prior to the TMC being disabled.

As such moving the tmc_etb/etr_dump() functions out of the
disable_hw callback and into the current sysFS interface.

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

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index 789384be81b6..f9749924a055 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -76,7 +76,6 @@ static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
 	CS_UNLOCK(drvdata->base);
 
 	tmc_flush_and_stop(drvdata);
-	tmc_etb_dump_hw(drvdata);
 	tmc_disable_hw(drvdata);
 
 	CS_LOCK(drvdata->base);
@@ -139,6 +138,7 @@ static void tmc_disable_etf_sink(struct coresight_device *csdev)
 	}
 
 	tmc_etb_disable_hw(drvdata);
+	tmc_etb_dump_hw(drvdata);
 	drvdata->enable = false;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index e1b43e0bd1dd..67e7d5dd891f 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -75,7 +75,6 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 	CS_UNLOCK(drvdata->base);
 
 	tmc_flush_and_stop(drvdata);
-	tmc_etr_dump_hw(drvdata);
 	tmc_disable_hw(drvdata);
 
 	CS_LOCK(drvdata->base);
@@ -112,6 +111,7 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev)
 	}
 
 	tmc_etr_disable_hw(drvdata);
+	tmc_etr_dump_hw(drvdata);
 	drvdata->enable = false;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
-- 
2.1.4

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

* [PATCH 08/14] coresight: tmc: allocating memory when needed
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

In it's current form the TMC probe() function allocates
trace buffer memory at boot time, event if coresight isn't
used.  This is highly inefficient since trace buffers can
occupy a lot of memory that could be used otherwised.

This patch allocates trace buffers on the fly, when the
coresight subsytem is sollicited.  Allocated buffers are
released when traces are read using the device descriptors
under /dev.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 97 ++++++++++++++++++++-----
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 97 ++++++++++++++++++++++---
 drivers/hwtracing/coresight/coresight-tmc.c     | 14 ----
 3 files changed, 164 insertions(+), 44 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index f9749924a055..e5d67e01409c 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -16,14 +16,13 @@
  */
 
 #include <linux/coresight.h>
+#include <linux/slab.h>
+
 #include "coresight-priv.h"
 #include "coresight-tmc.h"
 
 void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
 {
-	/* Zero out the memory to help with debug */
-	memset(drvdata->buf, 0, drvdata->size);
-
 	CS_UNLOCK(drvdata->base);
 
 	/* Wait for TMCSReady bit to be set */
@@ -109,19 +108,46 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
 
 static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 {
+	bool allocated = false;
+	char *buf = NULL;
 	unsigned long flags;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
+	/* Allocating the memory here while outside of the spinlock */
+	buf = devm_kzalloc(drvdata->dev, drvdata->size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading) {
 		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		kfree(buf);
 		return -EBUSY;
 	}
 
+	/*
+	 * If drvdata::buf isn't NULL, memory was allocated for a previous
+	 * trace run but wasn't read.  If so simply zero-out the memory.
+	 *
+	 * The memory is freed when users read the buffer using the
+	 * /dev/xyz.{etf|etb} interface.  See tmc_read_unprepare_etf() for
+	 * details.
+	 */
+	if (!drvdata->buf) {
+		allocated = true;
+		drvdata->buf = buf;
+	} else {
+		memset(drvdata->buf, 0, drvdata->size);
+	}
+
 	tmc_etb_enable_hw(drvdata);
 	drvdata->enable = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
+	/* Free memory outside the spinlock if need be */
+	if (!allocated)
+		kfree(buf);
+
 	dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n");
 	return 0;
 }
@@ -205,52 +231,75 @@ const struct coresight_ops tmc_etf_cs_ops = {
 
 int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)
 {
-	int ret = 0;
 	enum tmc_mode mode;
 	unsigned long flags;
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	/* The TMC isn't enable, so there is no need to disable it */
-	if (!drvdata->enable)
+	/* The TMC isn't enabled, so there is no need to disable it */
+	if (!drvdata->enable) {
+		/*
+		 * The ETB/ETF is disabled already.  If drvdata::buf is NULL
+		 * trace data has been harvested.
+		 */
+		if (!drvdata->buf) {
+			spin_unlock_irqrestore(&drvdata->spinlock, flags);
+			return -EINVAL;
+		}
+
 		goto out;
+	}
 
 	if (drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
 	    drvdata->config_type != TMC_CONFIG_TYPE_ETF) {
-		ret = -EINVAL;
-		goto out;
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EINVAL;
 	}
 
 	/* There is no point in reading a TMC in HW FIFO mode */
 	mode = readl_relaxed(drvdata->base + TMC_MODE);
 	if (mode != TMC_MODE_CIRCULAR_BUFFER) {
-		ret = -EINVAL;
-		goto out;
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EINVAL;
 	}
 
 	tmc_etb_disable_hw(drvdata);
-	drvdata->reading = true;
+	tmc_etb_dump_hw(drvdata);
 
 out:
+	drvdata->reading = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	return ret;
+	return 0;
 }
 
 int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
 {
 	int ret = 0;
+	char *buf = NULL;
 	enum tmc_mode mode;
 	unsigned long flags;
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	/* The TMC isn't enable, so there is no need to enable it */
-	if (!drvdata->enable)
-		goto out;
-
 	if (drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
 	    drvdata->config_type != TMC_CONFIG_TYPE_ETF) {
 		ret = -EINVAL;
+		goto err;
+	}
+
+	/* The TMC isn't enabled, so there is no need to enable it */
+	if (!drvdata->enable) {
+		/*
+		 * The ETB/ETF is not tracing and the buffer was just read.
+		 * As such prepare to free the trace buffer.
+		 */
+		buf = drvdata->buf;
+
+		/*
+		 * drvdata::buf is switched on in tmc_read_prepare_etf() so
+		 * it is important to set it back to NULL.
+		 */
+		drvdata->buf = NULL;
 		goto out;
 	}
 
@@ -258,13 +307,25 @@ int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
 	mode = readl_relaxed(drvdata->base + TMC_MODE);
 	if (mode != TMC_MODE_CIRCULAR_BUFFER) {
 		ret = -EINVAL;
-		goto out;
+		goto err;
 	}
 
+	/*
+	 * The trace run will continue with the same allocated trace buffer.
+	 * As such zero-out the buffer so that we don't end up with stale
+	 * data.
+	 */
+	memset(drvdata->buf, 0, drvdata->size);
 	tmc_etb_enable_hw(drvdata);
-	drvdata->reading = false;
 
 out:
+	drvdata->reading = false;
+
+err:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	/* Free allocated memory outside of the spinlock */
+	kfree(buf);
+
 	return ret;
 }
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 67e7d5dd891f..c4962568276e 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -16,6 +16,8 @@
  */
 
 #include <linux/coresight.h>
+#include <linux/dma-mapping.h>
+
 #include "coresight-priv.h"
 #include "coresight-tmc.h"
 
@@ -82,19 +84,51 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 
 static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 {
+	bool allocated = false;
 	unsigned long flags;
+	void __iomem *vaddr;
+	dma_addr_t paddr;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
+	/*
+	 * Contiguous  memory can't be allocated while a spinlock is held.
+	 * As such allocate memory here and free it if a buffer has already
+	 * been allocated (from a previous session).
+	 */
+	vaddr = dma_alloc_coherent(drvdata->dev, drvdata->size,
+				   &paddr, GFP_KERNEL);
+	if (!vaddr)
+		return -ENOMEM;
+
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading) {
 		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
 		return -EBUSY;
 	}
 
+	/*
+	 * If drvdata::buf == NULL, use the memory allocated above.
+	 * Otherwise a buffer still exists from a previous session, so
+	 * simply use that.
+	 */
+	if (!drvdata->buf) {
+		allocated = true;
+		drvdata->vaddr = vaddr;
+		drvdata->paddr = paddr;
+		drvdata->buf = drvdata->vaddr;
+	}
+
+	memset(drvdata->vaddr, 0, drvdata->size);
+
 	tmc_etr_enable_hw(drvdata);
 	drvdata->enable = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
+	/* Free memory outside the spinlock if need be */
+	if (!allocated)
+		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
+
 	dev_info(drvdata->dev, "TMC-ETR enabled\n");
 	return 0;
 }
@@ -129,48 +163,87 @@ const struct coresight_ops tmc_etr_cs_ops = {
 
 int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
 {
-	int ret = 0;
 	unsigned long flags;
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	/* The TMC isn't enable, so there is no need to disable it */
-	if (!drvdata->enable)
+	/* The TMC isn't enabled, so there is no need to disable it */
+	if (!drvdata->enable) {
+		/*
+		 * The ETR is disabled already.  If drvdata::buf is NULL
+		 * trace data has been harvested.
+		 */
+		if (!drvdata->buf) {
+			spin_unlock_irqrestore(&drvdata->spinlock, flags);
+			return -EINVAL;
+		}
+
 		goto out;
+	}
 
 	if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
-		ret = -EINVAL;
-		goto out;
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EINVAL;
 	}
 
 	tmc_etr_disable_hw(drvdata);
-	drvdata->reading = true;
+	tmc_etr_dump_hw(drvdata);
 
 out:
+	drvdata->reading = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	return ret;
+	return 0;
 }
 
 int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
 {
 	int ret = 0;
 	unsigned long flags;
+	void __iomem *vaddr = NULL;
+	dma_addr_t paddr;
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	/* The TMC isn't enable, so there is no need to enable it */
-	if (!drvdata->enable)
-		goto out;
-
 	if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
 		ret = -EINVAL;
+		goto err;
+	}
+
+	/* The TMC isn't enabled, so there is no need to enable it */
+	if (!drvdata->enable) {
+		/*
+		 * The ETR is not tracing and trace data was just read. As
+		 * such prepare to free the trace buffer.
+		 */
+		vaddr = drvdata->vaddr;
+		paddr = drvdata->paddr;
+
+		/*
+		 * drvdata::buf is switched on in tmc_read_prepare_etr() and
+		 * tmc_enable_etr_sink so it is important to set it back to
+		 * NULL.
+		 */
+		drvdata->buf = NULL;
 		goto out;
 	}
 
+	/*
+	 * The trace run will continue with the same allocated trace buffer.
+	 * As such zero-out the buffer so that we don't end up with stale
+	 * data.
+	 */
+	memset(drvdata->buf, 0, drvdata->size);
 	tmc_etr_enable_hw(drvdata);
-	drvdata->reading = false;
 
 out:
+	drvdata->reading = false;
+
+err:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	/* Free allocated memory out side of the spinlock */
+	if (vaddr)
+		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
+
 	return ret;
 }
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 26dbef305851..c614fe160f8a 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -318,20 +318,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
 
 	pm_runtime_put(&adev->dev);
 
-	if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		drvdata->vaddr = dma_alloc_coherent(dev, drvdata->size,
-						&drvdata->paddr, GFP_KERNEL);
-		if (!drvdata->vaddr)
-			return -ENOMEM;
-
-		memset(drvdata->vaddr, 0, drvdata->size);
-		drvdata->buf = drvdata->vaddr;
-	} else {
-		drvdata->buf = devm_kzalloc(dev, drvdata->size, GFP_KERNEL);
-		if (!drvdata->buf)
-			return -ENOMEM;
-	}
-
 	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
 	if (!desc) {
 		ret = -ENOMEM;
-- 
2.1.4

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

* [PATCH 08/14] coresight: tmc: allocating memory when needed
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

In it's current form the TMC probe() function allocates
trace buffer memory at boot time, event if coresight isn't
used.  This is highly inefficient since trace buffers can
occupy a lot of memory that could be used otherwised.

This patch allocates trace buffers on the fly, when the
coresight subsytem is sollicited.  Allocated buffers are
released when traces are read using the device descriptors
under /dev.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 97 ++++++++++++++++++++-----
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 97 ++++++++++++++++++++++---
 drivers/hwtracing/coresight/coresight-tmc.c     | 14 ----
 3 files changed, 164 insertions(+), 44 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index f9749924a055..e5d67e01409c 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -16,14 +16,13 @@
  */
 
 #include <linux/coresight.h>
+#include <linux/slab.h>
+
 #include "coresight-priv.h"
 #include "coresight-tmc.h"
 
 void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
 {
-	/* Zero out the memory to help with debug */
-	memset(drvdata->buf, 0, drvdata->size);
-
 	CS_UNLOCK(drvdata->base);
 
 	/* Wait for TMCSReady bit to be set */
@@ -109,19 +108,46 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
 
 static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 {
+	bool allocated = false;
+	char *buf = NULL;
 	unsigned long flags;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
+	/* Allocating the memory here while outside of the spinlock */
+	buf = devm_kzalloc(drvdata->dev, drvdata->size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading) {
 		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		kfree(buf);
 		return -EBUSY;
 	}
 
+	/*
+	 * If drvdata::buf isn't NULL, memory was allocated for a previous
+	 * trace run but wasn't read.  If so simply zero-out the memory.
+	 *
+	 * The memory is freed when users read the buffer using the
+	 * /dev/xyz.{etf|etb} interface.  See tmc_read_unprepare_etf() for
+	 * details.
+	 */
+	if (!drvdata->buf) {
+		allocated = true;
+		drvdata->buf = buf;
+	} else {
+		memset(drvdata->buf, 0, drvdata->size);
+	}
+
 	tmc_etb_enable_hw(drvdata);
 	drvdata->enable = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
+	/* Free memory outside the spinlock if need be */
+	if (!allocated)
+		kfree(buf);
+
 	dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n");
 	return 0;
 }
@@ -205,52 +231,75 @@ const struct coresight_ops tmc_etf_cs_ops = {
 
 int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)
 {
-	int ret = 0;
 	enum tmc_mode mode;
 	unsigned long flags;
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	/* The TMC isn't enable, so there is no need to disable it */
-	if (!drvdata->enable)
+	/* The TMC isn't enabled, so there is no need to disable it */
+	if (!drvdata->enable) {
+		/*
+		 * The ETB/ETF is disabled already.  If drvdata::buf is NULL
+		 * trace data has been harvested.
+		 */
+		if (!drvdata->buf) {
+			spin_unlock_irqrestore(&drvdata->spinlock, flags);
+			return -EINVAL;
+		}
+
 		goto out;
+	}
 
 	if (drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
 	    drvdata->config_type != TMC_CONFIG_TYPE_ETF) {
-		ret = -EINVAL;
-		goto out;
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EINVAL;
 	}
 
 	/* There is no point in reading a TMC in HW FIFO mode */
 	mode = readl_relaxed(drvdata->base + TMC_MODE);
 	if (mode != TMC_MODE_CIRCULAR_BUFFER) {
-		ret = -EINVAL;
-		goto out;
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EINVAL;
 	}
 
 	tmc_etb_disable_hw(drvdata);
-	drvdata->reading = true;
+	tmc_etb_dump_hw(drvdata);
 
 out:
+	drvdata->reading = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	return ret;
+	return 0;
 }
 
 int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
 {
 	int ret = 0;
+	char *buf = NULL;
 	enum tmc_mode mode;
 	unsigned long flags;
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	/* The TMC isn't enable, so there is no need to enable it */
-	if (!drvdata->enable)
-		goto out;
-
 	if (drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
 	    drvdata->config_type != TMC_CONFIG_TYPE_ETF) {
 		ret = -EINVAL;
+		goto err;
+	}
+
+	/* The TMC isn't enabled, so there is no need to enable it */
+	if (!drvdata->enable) {
+		/*
+		 * The ETB/ETF is not tracing and the buffer was just read.
+		 * As such prepare to free the trace buffer.
+		 */
+		buf = drvdata->buf;
+
+		/*
+		 * drvdata::buf is switched on in tmc_read_prepare_etf() so
+		 * it is important to set it back to NULL.
+		 */
+		drvdata->buf = NULL;
 		goto out;
 	}
 
@@ -258,13 +307,25 @@ int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
 	mode = readl_relaxed(drvdata->base + TMC_MODE);
 	if (mode != TMC_MODE_CIRCULAR_BUFFER) {
 		ret = -EINVAL;
-		goto out;
+		goto err;
 	}
 
+	/*
+	 * The trace run will continue with the same allocated trace buffer.
+	 * As such zero-out the buffer so that we don't end up with stale
+	 * data.
+	 */
+	memset(drvdata->buf, 0, drvdata->size);
 	tmc_etb_enable_hw(drvdata);
-	drvdata->reading = false;
 
 out:
+	drvdata->reading = false;
+
+err:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	/* Free allocated memory outside of the spinlock */
+	kfree(buf);
+
 	return ret;
 }
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 67e7d5dd891f..c4962568276e 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -16,6 +16,8 @@
  */
 
 #include <linux/coresight.h>
+#include <linux/dma-mapping.h>
+
 #include "coresight-priv.h"
 #include "coresight-tmc.h"
 
@@ -82,19 +84,51 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 
 static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 {
+	bool allocated = false;
 	unsigned long flags;
+	void __iomem *vaddr;
+	dma_addr_t paddr;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
+	/*
+	 * Contiguous  memory can't be allocated while a spinlock is held.
+	 * As such allocate memory here and free it if a buffer has already
+	 * been allocated (from a previous session).
+	 */
+	vaddr = dma_alloc_coherent(drvdata->dev, drvdata->size,
+				   &paddr, GFP_KERNEL);
+	if (!vaddr)
+		return -ENOMEM;
+
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading) {
 		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
 		return -EBUSY;
 	}
 
+	/*
+	 * If drvdata::buf == NULL, use the memory allocated above.
+	 * Otherwise a buffer still exists from a previous session, so
+	 * simply use that.
+	 */
+	if (!drvdata->buf) {
+		allocated = true;
+		drvdata->vaddr = vaddr;
+		drvdata->paddr = paddr;
+		drvdata->buf = drvdata->vaddr;
+	}
+
+	memset(drvdata->vaddr, 0, drvdata->size);
+
 	tmc_etr_enable_hw(drvdata);
 	drvdata->enable = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
+	/* Free memory outside the spinlock if need be */
+	if (!allocated)
+		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
+
 	dev_info(drvdata->dev, "TMC-ETR enabled\n");
 	return 0;
 }
@@ -129,48 +163,87 @@ const struct coresight_ops tmc_etr_cs_ops = {
 
 int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
 {
-	int ret = 0;
 	unsigned long flags;
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	/* The TMC isn't enable, so there is no need to disable it */
-	if (!drvdata->enable)
+	/* The TMC isn't enabled, so there is no need to disable it */
+	if (!drvdata->enable) {
+		/*
+		 * The ETR is disabled already.  If drvdata::buf is NULL
+		 * trace data has been harvested.
+		 */
+		if (!drvdata->buf) {
+			spin_unlock_irqrestore(&drvdata->spinlock, flags);
+			return -EINVAL;
+		}
+
 		goto out;
+	}
 
 	if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
-		ret = -EINVAL;
-		goto out;
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EINVAL;
 	}
 
 	tmc_etr_disable_hw(drvdata);
-	drvdata->reading = true;
+	tmc_etr_dump_hw(drvdata);
 
 out:
+	drvdata->reading = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
-	return ret;
+	return 0;
 }
 
 int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
 {
 	int ret = 0;
 	unsigned long flags;
+	void __iomem *vaddr = NULL;
+	dma_addr_t paddr;
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	/* The TMC isn't enable, so there is no need to enable it */
-	if (!drvdata->enable)
-		goto out;
-
 	if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
 		ret = -EINVAL;
+		goto err;
+	}
+
+	/* The TMC isn't enabled, so there is no need to enable it */
+	if (!drvdata->enable) {
+		/*
+		 * The ETR is not tracing and trace data was just read. As
+		 * such prepare to free the trace buffer.
+		 */
+		vaddr = drvdata->vaddr;
+		paddr = drvdata->paddr;
+
+		/*
+		 * drvdata::buf is switched on in tmc_read_prepare_etr() and
+		 * tmc_enable_etr_sink so it is important to set it back to
+		 * NULL.
+		 */
+		drvdata->buf = NULL;
 		goto out;
 	}
 
+	/*
+	 * The trace run will continue with the same allocated trace buffer.
+	 * As such zero-out the buffer so that we don't end up with stale
+	 * data.
+	 */
+	memset(drvdata->buf, 0, drvdata->size);
 	tmc_etr_enable_hw(drvdata);
-	drvdata->reading = false;
 
 out:
+	drvdata->reading = false;
+
+err:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	/* Free allocated memory out side of the spinlock */
+	if (vaddr)
+		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
+
 	return ret;
 }
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 26dbef305851..c614fe160f8a 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -318,20 +318,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
 
 	pm_runtime_put(&adev->dev);
 
-	if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
-		drvdata->vaddr = dma_alloc_coherent(dev, drvdata->size,
-						&drvdata->paddr, GFP_KERNEL);
-		if (!drvdata->vaddr)
-			return -ENOMEM;
-
-		memset(drvdata->vaddr, 0, drvdata->size);
-		drvdata->buf = drvdata->vaddr;
-	} else {
-		drvdata->buf = devm_kzalloc(dev, drvdata->size, GFP_KERNEL);
-		if (!drvdata->buf)
-			return -ENOMEM;
-	}
-
 	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
 	if (!desc) {
 		ret = -ENOMEM;
-- 
2.1.4

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

* [PATCH 09/14] coresight: tmc: adding mode of operation for link/sinks
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

Moving tmc_drvdata::enable to a local_t mode.  That way the
sink interface is aware of it's orgin and the foundation for
mutual exclusion between the sysFS and Perf interface can be
laid out.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 29 ++++++++++++++++++++-----
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 25 +++++++++++++++++----
 drivers/hwtracing/coresight/coresight-tmc.h     |  5 +++--
 3 files changed, 47 insertions(+), 12 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index e5d67e01409c..a88c76d7f473 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -108,6 +108,7 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
 
 static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 {
+	u32 val;
 	bool allocated = false;
 	char *buf = NULL;
 	unsigned long flags;
@@ -125,6 +126,15 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 		return -EBUSY;
 	}
 
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+	/*
+	 * In sysFS mode we can have multiple writers per sink.  Since this
+	 * sink is already enabled no memory is needed and the HW need not be
+	 * touched.
+	 */
+	if (val == CS_MODE_SYSFS)
+		goto out;
+
 	/*
 	 * If drvdata::buf isn't NULL, memory was allocated for a previous
 	 * trace run but wasn't read.  If so simply zero-out the memory.
@@ -141,9 +151,9 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 	}
 
 	tmc_etb_enable_hw(drvdata);
-	drvdata->enable = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
+out:
 	/* Free memory outside the spinlock if need be */
 	if (!allocated)
 		kfree(buf);
@@ -154,6 +164,7 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 
 static void tmc_disable_etf_sink(struct coresight_device *csdev)
 {
+	u32 val;
 	unsigned long flags;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
@@ -163,9 +174,15 @@ static void tmc_disable_etf_sink(struct coresight_device *csdev)
 		return;
 	}
 
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_SYSFS, CS_MODE_DISABLED);
+	/* Nothing to do, the TMC was already disabled */
+	if (val == CS_MODE_DISABLED)
+		goto out;
+
 	tmc_etb_disable_hw(drvdata);
 	tmc_etb_dump_hw(drvdata);
-	drvdata->enable = false;
+
+out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
 	dev_info(drvdata->dev, "TMC-ETB/ETF disabled\n");
@@ -184,7 +201,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
 	}
 
 	tmc_etf_enable_hw(drvdata);
-	drvdata->enable = true;
+	local_set(&drvdata->mode, CS_MODE_SYSFS);
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
 	dev_info(drvdata->dev, "TMC-ETF enabled\n");
@@ -204,7 +221,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
 	}
 
 	tmc_etf_disable_hw(drvdata);
-	drvdata->enable = false;
+	local_set(&drvdata->mode, CS_MODE_DISABLED);
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
 	dev_info(drvdata->dev, "TMC disabled\n");
@@ -237,7 +254,7 @@ int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
 	/* The TMC isn't enabled, so there is no need to disable it */
-	if (!drvdata->enable) {
+	if (local_read(&drvdata->mode) == CS_MODE_DISABLED) {
 		/*
 		 * The ETB/ETF is disabled already.  If drvdata::buf is NULL
 		 * trace data has been harvested.
@@ -288,7 +305,7 @@ int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
 	}
 
 	/* The TMC isn't enabled, so there is no need to enable it */
-	if (!drvdata->enable) {
+	if (local_read(&drvdata->mode) == CS_MODE_DISABLED) {
 		/*
 		 * The ETB/ETF is not tracing and the buffer was just read.
 		 * As such prepare to free the trace buffer.
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index c4962568276e..540d0b96a958 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -84,6 +84,7 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 
 static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 {
+	u32 val;
 	bool allocated = false;
 	unsigned long flags;
 	void __iomem *vaddr;
@@ -107,6 +108,15 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 		return -EBUSY;
 	}
 
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+	/*
+	 * In sysFS mode we can have multiple writers per sink.  Since this
+	 * sink is already enabled no memory is needed and the HW need not be
+	 * touched.
+	 */
+	if (val == CS_MODE_SYSFS)
+		goto out;
+
 	/*
 	 * If drvdata::buf == NULL, use the memory allocated above.
 	 * Otherwise a buffer still exists from a previous session, so
@@ -122,9 +132,9 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 	memset(drvdata->vaddr, 0, drvdata->size);
 
 	tmc_etr_enable_hw(drvdata);
-	drvdata->enable = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
+out:
 	/* Free memory outside the spinlock if need be */
 	if (!allocated)
 		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
@@ -135,6 +145,7 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 
 static void tmc_disable_etr_sink(struct coresight_device *csdev)
 {
+	u32 val;
 	unsigned long flags;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
@@ -144,9 +155,15 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev)
 		return;
 	}
 
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_SYSFS, CS_MODE_DISABLED);
+	/* Nothing to do, the TMC was already disabled */
+	if (val == CS_MODE_DISABLED)
+		goto out;
+
 	tmc_etr_disable_hw(drvdata);
 	tmc_etr_dump_hw(drvdata);
-	drvdata->enable = false;
+
+out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
 	dev_info(drvdata->dev, "TMC-ETR disabled\n");
@@ -168,7 +185,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
 	/* The TMC isn't enabled, so there is no need to disable it */
-	if (!drvdata->enable) {
+	if (local_read(&drvdata->mode) == CS_MODE_DISABLED) {
 		/*
 		 * The ETR is disabled already.  If drvdata::buf is NULL
 		 * trace data has been harvested.
@@ -210,7 +227,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
 	}
 
 	/* The TMC isn't enabled, so there is no need to enable it */
-	if (!drvdata->enable) {
+	if (local_read(&drvdata->mode) == CS_MODE_DISABLED) {
 		/*
 		 * The ETR is not tracing and trace data was just read. As
 		 * such prepare to free the trace buffer.
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 6b11caf77ad1..6dbd70861b17 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -18,6 +18,7 @@
 #ifndef _CORESIGHT_TMC_H
 #define _CORESIGHT_TMC_H
 
+#include <linux/io.h>
 #include <linux/miscdevice.h>
 
 #define TMC_RSZ			0x004
@@ -100,7 +101,7 @@ enum tmc_mem_intf_width {
  * @paddr:	DMA start location in RAM.
  * @vaddr:	virtual representation of @paddr.
  * @size:	@buf size.
- * @enable:	this TMC is being used.
+ * @mode:	how this TMC is being used.
  * @config_type: TMC variant, must be of type @tmc_config_type.
  * @trigger_cntr: amount of words to store after a trigger.
  */
@@ -116,7 +117,7 @@ struct tmc_drvdata {
 	dma_addr_t		paddr;
 	void __iomem		*vaddr;
 	u32			size;
-	bool			enable;
+	local_t			mode;
 	enum tmc_config_type	config_type;
 	u32			trigger_cntr;
 };
-- 
2.1.4

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

* [PATCH 09/14] coresight: tmc: adding mode of operation for link/sinks
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

Moving tmc_drvdata::enable to a local_t mode.  That way the
sink interface is aware of it's orgin and the foundation for
mutual exclusion between the sysFS and Perf interface can be
laid out.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 29 ++++++++++++++++++++-----
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 25 +++++++++++++++++----
 drivers/hwtracing/coresight/coresight-tmc.h     |  5 +++--
 3 files changed, 47 insertions(+), 12 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index e5d67e01409c..a88c76d7f473 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -108,6 +108,7 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
 
 static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 {
+	u32 val;
 	bool allocated = false;
 	char *buf = NULL;
 	unsigned long flags;
@@ -125,6 +126,15 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 		return -EBUSY;
 	}
 
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+	/*
+	 * In sysFS mode we can have multiple writers per sink.  Since this
+	 * sink is already enabled no memory is needed and the HW need not be
+	 * touched.
+	 */
+	if (val == CS_MODE_SYSFS)
+		goto out;
+
 	/*
 	 * If drvdata::buf isn't NULL, memory was allocated for a previous
 	 * trace run but wasn't read.  If so simply zero-out the memory.
@@ -141,9 +151,9 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 	}
 
 	tmc_etb_enable_hw(drvdata);
-	drvdata->enable = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
+out:
 	/* Free memory outside the spinlock if need be */
 	if (!allocated)
 		kfree(buf);
@@ -154,6 +164,7 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 
 static void tmc_disable_etf_sink(struct coresight_device *csdev)
 {
+	u32 val;
 	unsigned long flags;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
@@ -163,9 +174,15 @@ static void tmc_disable_etf_sink(struct coresight_device *csdev)
 		return;
 	}
 
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_SYSFS, CS_MODE_DISABLED);
+	/* Nothing to do, the TMC was already disabled */
+	if (val == CS_MODE_DISABLED)
+		goto out;
+
 	tmc_etb_disable_hw(drvdata);
 	tmc_etb_dump_hw(drvdata);
-	drvdata->enable = false;
+
+out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
 	dev_info(drvdata->dev, "TMC-ETB/ETF disabled\n");
@@ -184,7 +201,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
 	}
 
 	tmc_etf_enable_hw(drvdata);
-	drvdata->enable = true;
+	local_set(&drvdata->mode, CS_MODE_SYSFS);
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
 	dev_info(drvdata->dev, "TMC-ETF enabled\n");
@@ -204,7 +221,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
 	}
 
 	tmc_etf_disable_hw(drvdata);
-	drvdata->enable = false;
+	local_set(&drvdata->mode, CS_MODE_DISABLED);
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
 	dev_info(drvdata->dev, "TMC disabled\n");
@@ -237,7 +254,7 @@ int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
 	/* The TMC isn't enabled, so there is no need to disable it */
-	if (!drvdata->enable) {
+	if (local_read(&drvdata->mode) == CS_MODE_DISABLED) {
 		/*
 		 * The ETB/ETF is disabled already.  If drvdata::buf is NULL
 		 * trace data has been harvested.
@@ -288,7 +305,7 @@ int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
 	}
 
 	/* The TMC isn't enabled, so there is no need to enable it */
-	if (!drvdata->enable) {
+	if (local_read(&drvdata->mode) == CS_MODE_DISABLED) {
 		/*
 		 * The ETB/ETF is not tracing and the buffer was just read.
 		 * As such prepare to free the trace buffer.
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index c4962568276e..540d0b96a958 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -84,6 +84,7 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 
 static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 {
+	u32 val;
 	bool allocated = false;
 	unsigned long flags;
 	void __iomem *vaddr;
@@ -107,6 +108,15 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 		return -EBUSY;
 	}
 
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+	/*
+	 * In sysFS mode we can have multiple writers per sink.  Since this
+	 * sink is already enabled no memory is needed and the HW need not be
+	 * touched.
+	 */
+	if (val == CS_MODE_SYSFS)
+		goto out;
+
 	/*
 	 * If drvdata::buf == NULL, use the memory allocated above.
 	 * Otherwise a buffer still exists from a previous session, so
@@ -122,9 +132,9 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 	memset(drvdata->vaddr, 0, drvdata->size);
 
 	tmc_etr_enable_hw(drvdata);
-	drvdata->enable = true;
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
+out:
 	/* Free memory outside the spinlock if need be */
 	if (!allocated)
 		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
@@ -135,6 +145,7 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 
 static void tmc_disable_etr_sink(struct coresight_device *csdev)
 {
+	u32 val;
 	unsigned long flags;
 	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
@@ -144,9 +155,15 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev)
 		return;
 	}
 
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_SYSFS, CS_MODE_DISABLED);
+	/* Nothing to do, the TMC was already disabled */
+	if (val == CS_MODE_DISABLED)
+		goto out;
+
 	tmc_etr_disable_hw(drvdata);
 	tmc_etr_dump_hw(drvdata);
-	drvdata->enable = false;
+
+out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
 	dev_info(drvdata->dev, "TMC-ETR disabled\n");
@@ -168,7 +185,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
 	/* The TMC isn't enabled, so there is no need to disable it */
-	if (!drvdata->enable) {
+	if (local_read(&drvdata->mode) == CS_MODE_DISABLED) {
 		/*
 		 * The ETR is disabled already.  If drvdata::buf is NULL
 		 * trace data has been harvested.
@@ -210,7 +227,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
 	}
 
 	/* The TMC isn't enabled, so there is no need to enable it */
-	if (!drvdata->enable) {
+	if (local_read(&drvdata->mode) == CS_MODE_DISABLED) {
 		/*
 		 * The ETR is not tracing and trace data was just read. As
 		 * such prepare to free the trace buffer.
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 6b11caf77ad1..6dbd70861b17 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -18,6 +18,7 @@
 #ifndef _CORESIGHT_TMC_H
 #define _CORESIGHT_TMC_H
 
+#include <linux/io.h>
 #include <linux/miscdevice.h>
 
 #define TMC_RSZ			0x004
@@ -100,7 +101,7 @@ enum tmc_mem_intf_width {
  * @paddr:	DMA start location in RAM.
  * @vaddr:	virtual representation of @paddr.
  * @size:	@buf size.
- * @enable:	this TMC is being used.
+ * @mode:	how this TMC is being used.
  * @config_type: TMC variant, must be of type @tmc_config_type.
  * @trigger_cntr: amount of words to store after a trigger.
  */
@@ -116,7 +117,7 @@ struct tmc_drvdata {
 	dma_addr_t		paddr;
 	void __iomem		*vaddr;
 	u32			size;
-	bool			enable;
+	local_t			mode;
 	enum tmc_config_type	config_type;
 	u32			trigger_cntr;
 };
-- 
2.1.4

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

* [PATCH 10/14] coresight: tmc: make sysFS and Perf mode mutually exclusive
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

The sysFS and Perf access methods can't be allowed to interfere
with one another.  As such introducing guards to access
functions that prevents moving forward if a TMC is already
being used.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 76 +++++++++++++++++++++---
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 77 ++++++++++++++++++++++---
 2 files changed, 139 insertions(+), 14 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index a88c76d7f473..c533b4494969 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -106,7 +106,7 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
 	CS_LOCK(drvdata->base);
 }
 
-static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
+static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode)
 {
 	u32 val;
 	bool allocated = false;
@@ -127,6 +127,12 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 	}
 
 	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+	/* No need to continue if already operated from Perf */
+	if (val == CS_MODE_PERF) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		kfree(buf);
+		return -EBUSY;
+	}
 	/*
 	 * In sysFS mode we can have multiple writers per sink.  Since this
 	 * sink is already enabled no memory is needed and the HW need not be
@@ -162,7 +168,7 @@ out:
 	return 0;
 }
 
-static void tmc_disable_etf_sink(struct coresight_device *csdev)
+static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, u32 mode)
 {
 	u32 val;
 	unsigned long flags;
@@ -171,16 +177,66 @@ static void tmc_disable_etf_sink(struct coresight_device *csdev)
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading) {
 		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+	/*
+	 * In Perf mode there can be only one writer per sink.  There
+	 * is also no need to continue if the ETB/ETR is already operated
+	 * from sysFS.
+	 */
+	if (val != CS_MODE_DISABLED) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	tmc_etb_enable_hw(drvdata);
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	return 0;
+}
+
+static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
+{
+	switch (mode) {
+	case CS_MODE_SYSFS:
+		return tmc_enable_etf_sink_sysfs(csdev, mode);
+	case CS_MODE_PERF:
+		return tmc_enable_etf_sink_perf(csdev, mode);
+	}
+
+	/* We shouldn't be here */
+	return -EINVAL;
+}
+
+static void tmc_disable_etf_sink(struct coresight_device *csdev)
+{
+	u32 mode;
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
 		return;
 	}
 
-	val = local_cmpxchg(&drvdata->mode, CS_MODE_SYSFS, CS_MODE_DISABLED);
-	/* Nothing to do, the TMC was already disabled */
-	if (val == CS_MODE_DISABLED)
+	mode = local_xchg(&drvdata->mode, CS_MODE_DISABLED);
+	/* Nothing to do, the ETB/ETF was already disabled */
+	if (mode == CS_MODE_DISABLED)
 		goto out;
 
+	/* The engine has to be stopped in both sysFS and Perf mode */
 	tmc_etb_disable_hw(drvdata);
-	tmc_etb_dump_hw(drvdata);
+
+	/*
+	 * If we operated from sysFS, dump the trace data for retrieval
+	 * via /dev/.  From Perf trace data is handled via the Perf ring
+	 * buffer.
+	 */
+	if (mode == CS_MODE_SYSFS)
+		tmc_etb_dump_hw(drvdata);
 
 out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
@@ -253,7 +309,13 @@ int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	/* The TMC isn't enabled, so there is no need to disable it */
+	/* Don't interfere if operated from Perf */
+	if (local_read(&drvdata->mode) == CS_MODE_PERF) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	/* The ETB/ETF isn't enabled, so there is no need to disable it */
 	if (local_read(&drvdata->mode) == CS_MODE_DISABLED) {
 		/*
 		 * The ETB/ETF is disabled already.  If drvdata::buf is NULL
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 540d0b96a958..50a2e0a83714 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -82,7 +82,7 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 	CS_LOCK(drvdata->base);
 }
 
-static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
+static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode)
 {
 	u32 val;
 	bool allocated = false;
@@ -109,6 +109,13 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 	}
 
 	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+	/* No need to continue if already operated from Perf */
+	if (val == CS_MODE_PERF) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
+		return -EBUSY;
+	}
+
 	/*
 	 * In sysFS mode we can have multiple writers per sink.  Since this
 	 * sink is already enabled no memory is needed and the HW need not be
@@ -143,7 +150,7 @@ out:
 	return 0;
 }
 
-static void tmc_disable_etr_sink(struct coresight_device *csdev)
+static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, u32 mode)
 {
 	u32 val;
 	unsigned long flags;
@@ -152,16 +159,66 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev)
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading) {
 		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+	/*
+	 * In Perf mode there can be only one writer per sink.  There
+	 * is also no need to continue if the ETR is already operated
+	 * from sysFS.
+	 */
+	if (val != CS_MODE_DISABLED) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	tmc_etr_enable_hw(drvdata);
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	return 0;
+}
+
+static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
+{
+	switch (mode) {
+	case CS_MODE_SYSFS:
+		return tmc_enable_etr_sink_sysfs(csdev, mode);
+	case CS_MODE_PERF:
+		return tmc_enable_etr_sink_perf(csdev, mode);
+	}
+
+	/* We shouldn't be here */
+	return -EINVAL;
+}
+
+static void tmc_disable_etr_sink(struct coresight_device *csdev)
+{
+	u32 mode;
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
 		return;
 	}
 
-	val = local_cmpxchg(&drvdata->mode, CS_MODE_SYSFS, CS_MODE_DISABLED);
-	/* Nothing to do, the TMC was already disabled */
-	if (val == CS_MODE_DISABLED)
+	mode = local_xchg(&drvdata->mode, CS_MODE_DISABLED);
+	/* Nothing to do, the ETR was already disabled */
+	if (mode == CS_MODE_DISABLED)
 		goto out;
 
+	/* The engine has to be stopped in both sysFS and Perf mode */
 	tmc_etr_disable_hw(drvdata);
-	tmc_etr_dump_hw(drvdata);
+
+	/*
+	 * If we operated from sysFS, dump the trace data for retrieval
+	 * via /dev/.  From Perf trace data is handled via the Perf ring
+	 * buffer.
+	 */
+	if (mode == CS_MODE_SYSFS)
+		tmc_etr_dump_hw(drvdata);
 
 out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
@@ -184,7 +241,13 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	/* The TMC isn't enabled, so there is no need to disable it */
+	/* Don't interfere if operated from Perf */
+	if (local_read(&drvdata->mode) == CS_MODE_PERF) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	/* The ETR isn't enabled, so there is no need to disable it */
 	if (local_read(&drvdata->mode) == CS_MODE_DISABLED) {
 		/*
 		 * The ETR is disabled already.  If drvdata::buf is NULL
-- 
2.1.4

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

* [PATCH 10/14] coresight: tmc: make sysFS and Perf mode mutually exclusive
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

The sysFS and Perf access methods can't be allowed to interfere
with one another.  As such introducing guards to access
functions that prevents moving forward if a TMC is already
being used.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 76 +++++++++++++++++++++---
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 77 ++++++++++++++++++++++---
 2 files changed, 139 insertions(+), 14 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index a88c76d7f473..c533b4494969 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -106,7 +106,7 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata)
 	CS_LOCK(drvdata->base);
 }
 
-static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
+static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode)
 {
 	u32 val;
 	bool allocated = false;
@@ -127,6 +127,12 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
 	}
 
 	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+	/* No need to continue if already operated from Perf */
+	if (val == CS_MODE_PERF) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		kfree(buf);
+		return -EBUSY;
+	}
 	/*
 	 * In sysFS mode we can have multiple writers per sink.  Since this
 	 * sink is already enabled no memory is needed and the HW need not be
@@ -162,7 +168,7 @@ out:
 	return 0;
 }
 
-static void tmc_disable_etf_sink(struct coresight_device *csdev)
+static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, u32 mode)
 {
 	u32 val;
 	unsigned long flags;
@@ -171,16 +177,66 @@ static void tmc_disable_etf_sink(struct coresight_device *csdev)
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading) {
 		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+	/*
+	 * In Perf mode there can be only one writer per sink.  There
+	 * is also no need to continue if the ETB/ETR is already operated
+	 * from sysFS.
+	 */
+	if (val != CS_MODE_DISABLED) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	tmc_etb_enable_hw(drvdata);
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	return 0;
+}
+
+static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
+{
+	switch (mode) {
+	case CS_MODE_SYSFS:
+		return tmc_enable_etf_sink_sysfs(csdev, mode);
+	case CS_MODE_PERF:
+		return tmc_enable_etf_sink_perf(csdev, mode);
+	}
+
+	/* We shouldn't be here */
+	return -EINVAL;
+}
+
+static void tmc_disable_etf_sink(struct coresight_device *csdev)
+{
+	u32 mode;
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
 		return;
 	}
 
-	val = local_cmpxchg(&drvdata->mode, CS_MODE_SYSFS, CS_MODE_DISABLED);
-	/* Nothing to do, the TMC was already disabled */
-	if (val == CS_MODE_DISABLED)
+	mode = local_xchg(&drvdata->mode, CS_MODE_DISABLED);
+	/* Nothing to do, the ETB/ETF was already disabled */
+	if (mode == CS_MODE_DISABLED)
 		goto out;
 
+	/* The engine has to be stopped in both sysFS and Perf mode */
 	tmc_etb_disable_hw(drvdata);
-	tmc_etb_dump_hw(drvdata);
+
+	/*
+	 * If we operated from sysFS, dump the trace data for retrieval
+	 * via /dev/.  From Perf trace data is handled via the Perf ring
+	 * buffer.
+	 */
+	if (mode == CS_MODE_SYSFS)
+		tmc_etb_dump_hw(drvdata);
 
 out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
@@ -253,7 +309,13 @@ int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	/* The TMC isn't enabled, so there is no need to disable it */
+	/* Don't interfere if operated from Perf */
+	if (local_read(&drvdata->mode) == CS_MODE_PERF) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	/* The ETB/ETF isn't enabled, so there is no need to disable it */
 	if (local_read(&drvdata->mode) == CS_MODE_DISABLED) {
 		/*
 		 * The ETB/ETF is disabled already.  If drvdata::buf is NULL
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 540d0b96a958..50a2e0a83714 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -82,7 +82,7 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 	CS_LOCK(drvdata->base);
 }
 
-static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
+static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode)
 {
 	u32 val;
 	bool allocated = false;
@@ -109,6 +109,13 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
 	}
 
 	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+	/* No need to continue if already operated from Perf */
+	if (val == CS_MODE_PERF) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
+		return -EBUSY;
+	}
+
 	/*
 	 * In sysFS mode we can have multiple writers per sink.  Since this
 	 * sink is already enabled no memory is needed and the HW need not be
@@ -143,7 +150,7 @@ out:
 	return 0;
 }
 
-static void tmc_disable_etr_sink(struct coresight_device *csdev)
+static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, u32 mode)
 {
 	u32 val;
 	unsigned long flags;
@@ -152,16 +159,66 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev)
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 	if (drvdata->reading) {
 		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+	/*
+	 * In Perf mode there can be only one writer per sink.  There
+	 * is also no need to continue if the ETR is already operated
+	 * from sysFS.
+	 */
+	if (val != CS_MODE_DISABLED) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	tmc_etr_enable_hw(drvdata);
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	return 0;
+}
+
+static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
+{
+	switch (mode) {
+	case CS_MODE_SYSFS:
+		return tmc_enable_etr_sink_sysfs(csdev, mode);
+	case CS_MODE_PERF:
+		return tmc_enable_etr_sink_perf(csdev, mode);
+	}
+
+	/* We shouldn't be here */
+	return -EINVAL;
+}
+
+static void tmc_disable_etr_sink(struct coresight_device *csdev)
+{
+	u32 mode;
+	unsigned long flags;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->reading) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
 		return;
 	}
 
-	val = local_cmpxchg(&drvdata->mode, CS_MODE_SYSFS, CS_MODE_DISABLED);
-	/* Nothing to do, the TMC was already disabled */
-	if (val == CS_MODE_DISABLED)
+	mode = local_xchg(&drvdata->mode, CS_MODE_DISABLED);
+	/* Nothing to do, the ETR was already disabled */
+	if (mode == CS_MODE_DISABLED)
 		goto out;
 
+	/* The engine has to be stopped in both sysFS and Perf mode */
 	tmc_etr_disable_hw(drvdata);
-	tmc_etr_dump_hw(drvdata);
+
+	/*
+	 * If we operated from sysFS, dump the trace data for retrieval
+	 * via /dev/.  From Perf trace data is handled via the Perf ring
+	 * buffer.
+	 */
+	if (mode == CS_MODE_SYSFS)
+		tmc_etr_dump_hw(drvdata);
 
 out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
@@ -184,7 +241,13 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
 
 	spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	/* The TMC isn't enabled, so there is no need to disable it */
+	/* Don't interfere if operated from Perf */
+	if (local_read(&drvdata->mode) == CS_MODE_PERF) {
+		spin_unlock_irqrestore(&drvdata->spinlock, flags);
+		return -EBUSY;
+	}
+
+	/* The ETR isn't enabled, so there is no need to disable it */
 	if (local_read(&drvdata->mode) == CS_MODE_DISABLED) {
 		/*
 		 * The ETR is disabled already.  If drvdata::buf is NULL
-- 
2.1.4

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

* [PATCH 11/14] coresight: tmc: keep track of memory width
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

Accessing the HW configuration register each time the memory
width is needed simply doesn't make sense.  It is much more
efficient to read the value once and keep a reference for
later use.

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

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index c533b4494969..2cad1aa1949f 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -42,18 +42,16 @@ void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
 
 static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
 {
-	enum tmc_mem_intf_width memwidth;
 	u8 memwords;
 	char *bufp;
 	u32 read_data;
 	int i;
 
-	memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10);
-	if (memwidth == TMC_MEM_INTF_WIDTH_32BITS)
+	if (drvdata->memwidth == TMC_MEM_INTF_WIDTH_32BITS)
 		memwords = 1;
-	else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS)
+	else if (drvdata->memwidth == TMC_MEM_INTF_WIDTH_64BITS)
 		memwords = 2;
-	else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS)
+	else if (drvdata->memwidth == TMC_MEM_INTF_WIDTH_128BITS)
 		memwords = 4;
 	else
 		memwords = 8;
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index c614fe160f8a..beeac81ff2fe 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -304,6 +304,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
 
 	devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
 	drvdata->config_type = BMVAL(devid, 6, 7);
+	drvdata->memwidth = BMVAL(devid, 8, 10);
 
 	if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
 		if (np)
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 6dbd70861b17..a6fb7a9fec5a 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -103,6 +103,7 @@ enum tmc_mem_intf_width {
  * @size:	@buf size.
  * @mode:	how this TMC is being used.
  * @config_type: TMC variant, must be of type @tmc_config_type.
+ * @memwidth:	width of the memory interface databus, powers of two.
  * @trigger_cntr: amount of words to store after a trigger.
  */
 struct tmc_drvdata {
@@ -119,6 +120,7 @@ struct tmc_drvdata {
 	u32			size;
 	local_t			mode;
 	enum tmc_config_type	config_type;
+	enum tmc_mem_intf_width	memwidth;
 	u32			trigger_cntr;
 };
 
-- 
2.1.4

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

* [PATCH 11/14] coresight: tmc: keep track of memory width
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

Accessing the HW configuration register each time the memory
width is needed simply doesn't make sense.  It is much more
efficient to read the value once and keep a reference for
later use.

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

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index c533b4494969..2cad1aa1949f 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -42,18 +42,16 @@ void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
 
 static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
 {
-	enum tmc_mem_intf_width memwidth;
 	u8 memwords;
 	char *bufp;
 	u32 read_data;
 	int i;
 
-	memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10);
-	if (memwidth == TMC_MEM_INTF_WIDTH_32BITS)
+	if (drvdata->memwidth == TMC_MEM_INTF_WIDTH_32BITS)
 		memwords = 1;
-	else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS)
+	else if (drvdata->memwidth == TMC_MEM_INTF_WIDTH_64BITS)
 		memwords = 2;
-	else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS)
+	else if (drvdata->memwidth == TMC_MEM_INTF_WIDTH_128BITS)
 		memwords = 4;
 	else
 		memwords = 8;
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index c614fe160f8a..beeac81ff2fe 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -304,6 +304,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
 
 	devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
 	drvdata->config_type = BMVAL(devid, 6, 7);
+	drvdata->memwidth = BMVAL(devid, 8, 10);
 
 	if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
 		if (np)
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 6dbd70861b17..a6fb7a9fec5a 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -103,6 +103,7 @@ enum tmc_mem_intf_width {
  * @size:	@buf size.
  * @mode:	how this TMC is being used.
  * @config_type: TMC variant, must be of type @tmc_config_type.
+ * @memwidth:	width of the memory interface databus, powers of two.
  * @trigger_cntr: amount of words to store after a trigger.
  */
 struct tmc_drvdata {
@@ -119,6 +120,7 @@ struct tmc_drvdata {
 	u32			size;
 	local_t			mode;
 	enum tmc_config_type	config_type;
+	enum tmc_mem_intf_width	memwidth;
 	u32			trigger_cntr;
 };
 
-- 
2.1.4

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

* [PATCH 12/14] coresight: tmc: implementing TMC-ETF AUX space API
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

This patch implement the AUX area interfaces required to
use the TMC (configured as an ETF) from the Perf sub-system.

The heuristic is heavily borrowed from the ETB10 implementation.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 198 ++++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-tmc.h     |  21 +++
 2 files changed, 219 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index 2cad1aa1949f..79762b2c6114 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -15,7 +15,9 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/circ_buf.h>
 #include <linux/coresight.h>
+#include <linux/perf_event.h>
 #include <linux/slab.h>
 
 #include "coresight-priv.h"
@@ -281,9 +283,205 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
 	dev_info(drvdata->dev, "TMC disabled\n");
 }
 
+static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu,
+				  void **pages, int nr_pages, bool overwrite)
+{
+	int node;
+	struct cs_tmc_buffers *buf;
+
+	if (cpu == -1)
+		cpu = smp_processor_id();
+	node = cpu_to_node(cpu);
+
+	/* Allocate memory structure for interaction with Perf */
+	buf = kzalloc_node(sizeof(struct cs_tmc_buffers), GFP_KERNEL, node);
+	if (!buf)
+		return NULL;
+
+	buf->snapshot = overwrite;
+	buf->nr_pages = nr_pages;
+	buf->data_pages = pages;
+
+	return buf;
+}
+
+static void tmc_free_etf_buffer(void *config)
+{
+	struct cs_tmc_buffers *buf = config;
+
+	kfree(buf);
+}
+
+static int tmc_set_etf_buffer(struct coresight_device *csdev,
+			      struct perf_output_handle *handle,
+			      void *sink_config)
+{
+	int ret = 0;
+	unsigned long head;
+	struct cs_tmc_buffers *buf = sink_config;
+
+	/* 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->data_size, 0);
+
+	return ret;
+}
+
+static unsigned long tmc_reset_etf_buffer(struct coresight_device *csdev,
+					  struct perf_output_handle *handle,
+					  void *sink_config, bool *lost)
+{
+	unsigned long size = 0;
+	struct cs_tmc_buffers *buf = sink_config;
+
+	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);
+		/*
+		 * Tell the tracer PMU how much we got in this run and if
+		 * something went wrong along the way.  Nobody else can use
+		 * this cs_tmc_buffers instance until we are done.  As such
+		 * resetting parameters here and squaring off with the ring
+		 * buffer API in the tracer PMU is fine.
+		 */
+		*lost = !!local_xchg(&buf->lost, 0);
+		size = local_xchg(&buf->data_size, 0);
+	}
+
+	return size;
+}
+
+static void tmc_update_etf_buffer(struct coresight_device *csdev,
+				  struct perf_output_handle *handle,
+				  void *sink_config)
+{
+	int i, cur;
+	u32 *buf_ptr;
+	u32 read_ptr, write_ptr;
+	u32 status, to_read;
+	unsigned long offset;
+	struct cs_tmc_buffers *buf = sink_config;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (!buf)
+		return;
+
+	/* This shouldn't happen */
+	if (WARN_ON_ONCE(local_read(&drvdata->mode) != CS_MODE_PERF))
+		return;
+
+	CS_UNLOCK(drvdata->base);
+
+	tmc_flush_and_stop(drvdata);
+
+	read_ptr = readl_relaxed(drvdata->base + TMC_RRP);
+	write_ptr = readl_relaxed(drvdata->base + TMC_RWP);
+
+	/*
+	 * Get a hold of the status register and see if a wrap around
+	 * has occurred.  If so adjust things accordingly.
+	 */
+	status = readl_relaxed(drvdata->base + TMC_STS);
+	if (status & TMC_STS_FULL) {
+		local_inc(&buf->lost);
+		to_read = drvdata->size;
+	} else {
+		to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->size);
+	}
+
+	/*
+	 * The TMC RAM buffer may be bigger than the space available in the
+	 * perf ring buffer (handle->size).  If so advance the RRP so that we
+	 * get the latest trace data.
+	 */
+	if (to_read > handle->size) {
+		u32 mask = 0;
+
+		/*
+		 * The value written to RRP must be byte-address aligned to
+		 * the width of the trace memory databus _and_ to a frame
+		 * boundary (16 byte), whichever is the biggest. For example,
+		 * for 32-bit, 64-bit and 128-bit wide trace memory, the four
+		 * LSBs must be 0s. For 256-bit wide trace memory, the five
+		 * LSBs must be 0s.
+		 */
+		switch (drvdata->memwidth) {
+		case TMC_MEM_INTF_WIDTH_32BITS:
+		case TMC_MEM_INTF_WIDTH_64BITS:
+		case TMC_MEM_INTF_WIDTH_128BITS:
+			mask = GENMASK(31, 5);
+			break;
+		case TMC_MEM_INTF_WIDTH_256BITS:
+			mask = GENMASK(31, 6);
+			break;
+		}
+
+		/*
+		 * Make sure the new size is aligned in accordance with the
+		 * requirement explained above.
+		 */
+		to_read -= handle->size & mask;
+		/* Move the RAM read pointer up */
+		read_ptr = (write_ptr + drvdata->size) - to_read;
+		/* Make sure we are still within our limits */
+		read_ptr &= ~(drvdata->size - 1);
+		/* Tell the HW */
+		writel_relaxed(read_ptr, drvdata->base + TMC_RRP);
+		local_inc(&buf->lost);
+	}
+
+	cur = buf->cur;
+	offset = buf->offset;
+
+	/* for every byte to read */
+	for (i = 0; i < to_read; i += 4) {
+		buf_ptr = buf->data_pages[cur] + offset;
+		*buf_ptr = readl_relaxed(drvdata->base + TMC_RRD);
+
+		offset += 4;
+		if (offset >= PAGE_SIZE) {
+			offset = 0;
+			cur++;
+			/* wrap around at the end of the buffer */
+			cur &= buf->nr_pages - 1;
+		}
+	}
+
+	/*
+	 * 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(to_read, &buf->data_size);
+
+	CS_LOCK(drvdata->base);
+}
+
 static const struct coresight_ops_sink tmc_etf_sink_ops = {
 	.enable		= tmc_enable_etf_sink,
 	.disable	= tmc_disable_etf_sink,
+	.alloc_buffer	= tmc_alloc_etf_buffer,
+	.free_buffer	= tmc_free_etf_buffer,
+	.set_buffer	= tmc_set_etf_buffer,
+	.reset_buffer	= tmc_reset_etf_buffer,
+	.update_buffer	= tmc_update_etf_buffer,
 };
 
 static const struct coresight_ops_link tmc_etf_link_ops = {
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index a6fb7a9fec5a..3de442f15be7 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -52,6 +52,7 @@
 /* TMC_CTL - 0x020 */
 #define TMC_CTL_CAPT_EN		BIT(0)
 /* TMC_STS - 0x00C */
+#define TMC_STS_FULL		BIT(0)
 #define TMC_STS_TRIGGERED	BIT(1)
 /* TMC_AXICTL - 0x110 */
 #define TMC_AXICTL_PROT_CTL_B0	BIT(0)
@@ -90,6 +91,26 @@ enum tmc_mem_intf_width {
 };
 
 /**
+ * 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
+ * @offset:	offset within the current buffer
+ * @data_size:	how much we collected in this run
+ * @lost:	other than zero if we had a HW buffer wrap around
+ * @snapshot:	is this run in snapshot mode
+ * @data_pages:	a handle the ring buffer
+ */
+struct cs_tmc_buffers {
+	unsigned int		cur;
+	unsigned int		nr_pages;
+	unsigned long		offset;
+	local_t			data_size;
+	local_t			lost;
+	bool			snapshot;
+	void			**data_pages;
+};
+
+/**
  * struct tmc_drvdata - specifics associated to an TMC component
  * @base:	memory mapped base address for this component.
  * @dev:	the device entity associated to this component.
-- 
2.1.4

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

* [PATCH 12/14] coresight: tmc: implementing TMC-ETF AUX space API
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

This patch implement the AUX area interfaces required to
use the TMC (configured as an ETF) from the Perf sub-system.

The heuristic is heavily borrowed from the ETB10 implementation.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 198 ++++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-tmc.h     |  21 +++
 2 files changed, 219 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index 2cad1aa1949f..79762b2c6114 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -15,7 +15,9 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/circ_buf.h>
 #include <linux/coresight.h>
+#include <linux/perf_event.h>
 #include <linux/slab.h>
 
 #include "coresight-priv.h"
@@ -281,9 +283,205 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
 	dev_info(drvdata->dev, "TMC disabled\n");
 }
 
+static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu,
+				  void **pages, int nr_pages, bool overwrite)
+{
+	int node;
+	struct cs_tmc_buffers *buf;
+
+	if (cpu == -1)
+		cpu = smp_processor_id();
+	node = cpu_to_node(cpu);
+
+	/* Allocate memory structure for interaction with Perf */
+	buf = kzalloc_node(sizeof(struct cs_tmc_buffers), GFP_KERNEL, node);
+	if (!buf)
+		return NULL;
+
+	buf->snapshot = overwrite;
+	buf->nr_pages = nr_pages;
+	buf->data_pages = pages;
+
+	return buf;
+}
+
+static void tmc_free_etf_buffer(void *config)
+{
+	struct cs_tmc_buffers *buf = config;
+
+	kfree(buf);
+}
+
+static int tmc_set_etf_buffer(struct coresight_device *csdev,
+			      struct perf_output_handle *handle,
+			      void *sink_config)
+{
+	int ret = 0;
+	unsigned long head;
+	struct cs_tmc_buffers *buf = sink_config;
+
+	/* 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->data_size, 0);
+
+	return ret;
+}
+
+static unsigned long tmc_reset_etf_buffer(struct coresight_device *csdev,
+					  struct perf_output_handle *handle,
+					  void *sink_config, bool *lost)
+{
+	unsigned long size = 0;
+	struct cs_tmc_buffers *buf = sink_config;
+
+	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);
+		/*
+		 * Tell the tracer PMU how much we got in this run and if
+		 * something went wrong along the way.  Nobody else can use
+		 * this cs_tmc_buffers instance until we are done.  As such
+		 * resetting parameters here and squaring off with the ring
+		 * buffer API in the tracer PMU is fine.
+		 */
+		*lost = !!local_xchg(&buf->lost, 0);
+		size = local_xchg(&buf->data_size, 0);
+	}
+
+	return size;
+}
+
+static void tmc_update_etf_buffer(struct coresight_device *csdev,
+				  struct perf_output_handle *handle,
+				  void *sink_config)
+{
+	int i, cur;
+	u32 *buf_ptr;
+	u32 read_ptr, write_ptr;
+	u32 status, to_read;
+	unsigned long offset;
+	struct cs_tmc_buffers *buf = sink_config;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (!buf)
+		return;
+
+	/* This shouldn't happen */
+	if (WARN_ON_ONCE(local_read(&drvdata->mode) != CS_MODE_PERF))
+		return;
+
+	CS_UNLOCK(drvdata->base);
+
+	tmc_flush_and_stop(drvdata);
+
+	read_ptr = readl_relaxed(drvdata->base + TMC_RRP);
+	write_ptr = readl_relaxed(drvdata->base + TMC_RWP);
+
+	/*
+	 * Get a hold of the status register and see if a wrap around
+	 * has occurred.  If so adjust things accordingly.
+	 */
+	status = readl_relaxed(drvdata->base + TMC_STS);
+	if (status & TMC_STS_FULL) {
+		local_inc(&buf->lost);
+		to_read = drvdata->size;
+	} else {
+		to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->size);
+	}
+
+	/*
+	 * The TMC RAM buffer may be bigger than the space available in the
+	 * perf ring buffer (handle->size).  If so advance the RRP so that we
+	 * get the latest trace data.
+	 */
+	if (to_read > handle->size) {
+		u32 mask = 0;
+
+		/*
+		 * The value written to RRP must be byte-address aligned to
+		 * the width of the trace memory databus _and_ to a frame
+		 * boundary (16 byte), whichever is the biggest. For example,
+		 * for 32-bit, 64-bit and 128-bit wide trace memory, the four
+		 * LSBs must be 0s. For 256-bit wide trace memory, the five
+		 * LSBs must be 0s.
+		 */
+		switch (drvdata->memwidth) {
+		case TMC_MEM_INTF_WIDTH_32BITS:
+		case TMC_MEM_INTF_WIDTH_64BITS:
+		case TMC_MEM_INTF_WIDTH_128BITS:
+			mask = GENMASK(31, 5);
+			break;
+		case TMC_MEM_INTF_WIDTH_256BITS:
+			mask = GENMASK(31, 6);
+			break;
+		}
+
+		/*
+		 * Make sure the new size is aligned in accordance with the
+		 * requirement explained above.
+		 */
+		to_read -= handle->size & mask;
+		/* Move the RAM read pointer up */
+		read_ptr = (write_ptr + drvdata->size) - to_read;
+		/* Make sure we are still within our limits */
+		read_ptr &= ~(drvdata->size - 1);
+		/* Tell the HW */
+		writel_relaxed(read_ptr, drvdata->base + TMC_RRP);
+		local_inc(&buf->lost);
+	}
+
+	cur = buf->cur;
+	offset = buf->offset;
+
+	/* for every byte to read */
+	for (i = 0; i < to_read; i += 4) {
+		buf_ptr = buf->data_pages[cur] + offset;
+		*buf_ptr = readl_relaxed(drvdata->base + TMC_RRD);
+
+		offset += 4;
+		if (offset >= PAGE_SIZE) {
+			offset = 0;
+			cur++;
+			/* wrap around at the end of the buffer */
+			cur &= buf->nr_pages - 1;
+		}
+	}
+
+	/*
+	 * 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(to_read, &buf->data_size);
+
+	CS_LOCK(drvdata->base);
+}
+
 static const struct coresight_ops_sink tmc_etf_sink_ops = {
 	.enable		= tmc_enable_etf_sink,
 	.disable	= tmc_disable_etf_sink,
+	.alloc_buffer	= tmc_alloc_etf_buffer,
+	.free_buffer	= tmc_free_etf_buffer,
+	.set_buffer	= tmc_set_etf_buffer,
+	.reset_buffer	= tmc_reset_etf_buffer,
+	.update_buffer	= tmc_update_etf_buffer,
 };
 
 static const struct coresight_ops_link tmc_etf_link_ops = {
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index a6fb7a9fec5a..3de442f15be7 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -52,6 +52,7 @@
 /* TMC_CTL - 0x020 */
 #define TMC_CTL_CAPT_EN		BIT(0)
 /* TMC_STS - 0x00C */
+#define TMC_STS_FULL		BIT(0)
 #define TMC_STS_TRIGGERED	BIT(1)
 /* TMC_AXICTL - 0x110 */
 #define TMC_AXICTL_PROT_CTL_B0	BIT(0)
@@ -90,6 +91,26 @@ enum tmc_mem_intf_width {
 };
 
 /**
+ * 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
+ * @offset:	offset within the current buffer
+ * @data_size:	how much we collected in this run
+ * @lost:	other than zero if we had a HW buffer wrap around
+ * @snapshot:	is this run in snapshot mode
+ * @data_pages:	a handle the ring buffer
+ */
+struct cs_tmc_buffers {
+	unsigned int		cur;
+	unsigned int		nr_pages;
+	unsigned long		offset;
+	local_t			data_size;
+	local_t			lost;
+	bool			snapshot;
+	void			**data_pages;
+};
+
+/**
  * struct tmc_drvdata - specifics associated to an TMC component
  * @base:	memory mapped base address for this component.
  * @dev:	the device entity associated to this component.
-- 
2.1.4

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

* [PATCH 13/14] coresight: tmc: implementing TMC-ETR AUX space API
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

This patch implement the AUX area interfaces required to
use the TMC (configured as an ETR) from the Perf sub-system.

The heuristic is heavily borrowed from the ETB10 and TMC-ETF
implementation.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etf.c |   6 +-
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 141 ++++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-tmc.h     |   3 +
 3 files changed, 147 insertions(+), 3 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index 79762b2c6114..64b4b4f8df12 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -364,9 +364,9 @@ static unsigned long tmc_reset_etf_buffer(struct coresight_device *csdev,
 	return size;
 }
 
-static void tmc_update_etf_buffer(struct coresight_device *csdev,
-				  struct perf_output_handle *handle,
-				  void *sink_config)
+void tmc_update_etf_buffer(struct coresight_device *csdev,
+			   struct perf_output_handle *handle,
+			   void *sink_config)
 {
 	int i, cur;
 	u32 *buf_ptr;
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 50a2e0a83714..6ba261277316 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -15,12 +15,30 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/circ_buf.h>
 #include <linux/coresight.h>
 #include <linux/dma-mapping.h>
+#include <linux/slab.h>
 
 #include "coresight-priv.h"
 #include "coresight-tmc.h"
 
+/**
+ * struct cs_etr_buffer - keep track of a recording session' specifics
+ * @tmc:	generic portion of the TMC buffers
+ * @paddr:	the physical address of a DMA'able contiguous memory area
+ * @vaddr:	the virtual address associated to @paddr
+ * @size:	how much memory we have, starting at @paddr
+ * @dev:	the device @vaddr has been tied to
+ */
+struct cs_etr_buffers {
+	struct cs_tmc_buffers	tmc;
+	dma_addr_t		paddr;
+	void __iomem		*vaddr;
+	u32			size;
+	struct device		*dev;
+};
+
 void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
 {
 	u32 axictl;
@@ -226,9 +244,132 @@ out:
 	dev_info(drvdata->dev, "TMC-ETR disabled\n");
 }
 
+static void *tmc_alloc_etr_buffer(struct coresight_device *csdev, int cpu,
+				  void **pages, int nr_pages, bool overwrite)
+{
+	int node;
+	struct cs_etr_buffers *buf;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (cpu == -1)
+		cpu = smp_processor_id();
+	node = cpu_to_node(cpu);
+
+	/* Allocate memory structure for interaction with Perf */
+	buf = kzalloc_node(sizeof(struct cs_etr_buffers), GFP_KERNEL, node);
+	if (!buf)
+		return NULL;
+
+	buf->dev = drvdata->dev;
+	buf->size = drvdata->size;
+	buf->vaddr = dma_alloc_coherent(buf->dev, buf->size,
+					&buf->paddr, GFP_KERNEL);
+	if (!buf->vaddr) {
+		kfree(buf);
+		return NULL;
+	}
+
+	buf->tmc.snapshot = overwrite;
+	buf->tmc.nr_pages = nr_pages;
+	buf->tmc.data_pages = pages;
+
+	return buf;
+}
+
+static void tmc_free_etr_buffer(void *config)
+{
+	struct cs_etr_buffers *buf = config;
+
+	dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->paddr);
+	kfree(buf);
+}
+
+static int tmc_set_etr_buffer(struct coresight_device *csdev,
+			      struct perf_output_handle *handle,
+			      void *sink_config)
+{
+	int ret = 0;
+	unsigned long head;
+	struct cs_etr_buffers *buf = sink_config;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	/* wrap head around to the amount of space we have */
+	head = handle->head & ((buf->tmc.nr_pages << PAGE_SHIFT) - 1);
+
+	/* find the page to write to */
+	buf->tmc.cur = head / PAGE_SIZE;
+
+	/* and offset within that page */
+	buf->tmc.offset = head % PAGE_SIZE;
+
+	local_set(&buf->tmc.data_size, 0);
+
+	/* Tell the HW where to put the trace data */
+	drvdata->vaddr = buf->vaddr;
+	drvdata->paddr = buf->paddr;
+	memset(drvdata->vaddr, 0, drvdata->size);
+
+	return ret;
+}
+
+static unsigned long tmc_reset_etr_buffer(struct coresight_device *csdev,
+					  struct perf_output_handle *handle,
+					  void *sink_config, bool *lost)
+{
+	unsigned long size = 0;
+	struct cs_etr_buffers *buf = sink_config;
+	struct tmc_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->tmc.snapshot) {
+			size = buf->tmc.nr_pages << PAGE_SHIFT;
+			handle->head = local_xchg(&buf->tmc.data_size, size);
+		}
+
+		/*
+		 * Tell the tracer PMU how much we got in this run and if
+		 * something went wrong along the way.  Nobody else can use
+		 * this cs_etr_buffers instance until we are done.  As such
+		 * resetting parameters here and squaring off with the ring
+		 * buffer API in the tracer PMU is fine.
+		 */
+		*lost = !!local_xchg(&buf->tmc.lost, 0);
+		size = local_xchg(&buf->tmc.data_size, 0);
+	}
+
+	/* Get ready for another run */
+	drvdata->vaddr = NULL;
+	drvdata->paddr = 0;
+
+	return size;
+}
+
+static void tmc_update_etr_buffer(struct coresight_device *csdev,
+				  struct perf_output_handle *handle,
+				  void *sink_config)
+{
+	struct cs_etr_buffers *buf = sink_config;
+
+	/*
+	 * An ETR configured to work in contiguous memory mode works the same
+	 * was as an ETB or ETF.
+	 */
+	tmc_update_etf_buffer(csdev, handle, &buf->tmc);
+}
+
 static const struct coresight_ops_sink tmc_etr_sink_ops = {
 	.enable		= tmc_enable_etr_sink,
 	.disable	= tmc_disable_etr_sink,
+	.alloc_buffer	= tmc_alloc_etr_buffer,
+	.free_buffer	= tmc_free_etr_buffer,
+	.set_buffer	= tmc_set_etr_buffer,
+	.reset_buffer	= tmc_reset_etr_buffer,
+	.update_buffer	= tmc_update_etr_buffer,
 };
 
 const struct coresight_ops tmc_etr_cs_ops = {
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 3de442f15be7..12f1bd8ccdc9 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -154,6 +154,9 @@ void tmc_disable_hw(struct tmc_drvdata *drvdata);
 /* ETB/ETF functions */
 int tmc_read_prepare_etf(struct tmc_drvdata *drvdata);
 int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata);
+void tmc_update_etf_buffer(struct coresight_device *csdev,
+			   struct perf_output_handle *handle,
+			   void *sink_config);
 extern const struct coresight_ops tmc_etb_cs_ops;
 extern const struct coresight_ops tmc_etf_cs_ops;
 
-- 
2.1.4

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

* [PATCH 13/14] coresight: tmc: implementing TMC-ETR AUX space API
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

This patch implement the AUX area interfaces required to
use the TMC (configured as an ETR) from the Perf sub-system.

The heuristic is heavily borrowed from the ETB10 and TMC-ETF
implementation.

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etf.c |   6 +-
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 141 ++++++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-tmc.h     |   3 +
 3 files changed, 147 insertions(+), 3 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index 79762b2c6114..64b4b4f8df12 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -364,9 +364,9 @@ static unsigned long tmc_reset_etf_buffer(struct coresight_device *csdev,
 	return size;
 }
 
-static void tmc_update_etf_buffer(struct coresight_device *csdev,
-				  struct perf_output_handle *handle,
-				  void *sink_config)
+void tmc_update_etf_buffer(struct coresight_device *csdev,
+			   struct perf_output_handle *handle,
+			   void *sink_config)
 {
 	int i, cur;
 	u32 *buf_ptr;
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 50a2e0a83714..6ba261277316 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -15,12 +15,30 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/circ_buf.h>
 #include <linux/coresight.h>
 #include <linux/dma-mapping.h>
+#include <linux/slab.h>
 
 #include "coresight-priv.h"
 #include "coresight-tmc.h"
 
+/**
+ * struct cs_etr_buffer - keep track of a recording session' specifics
+ * @tmc:	generic portion of the TMC buffers
+ * @paddr:	the physical address of a DMA'able contiguous memory area
+ * @vaddr:	the virtual address associated to @paddr
+ * @size:	how much memory we have, starting at @paddr
+ * @dev:	the device @vaddr has been tied to
+ */
+struct cs_etr_buffers {
+	struct cs_tmc_buffers	tmc;
+	dma_addr_t		paddr;
+	void __iomem		*vaddr;
+	u32			size;
+	struct device		*dev;
+};
+
 void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
 {
 	u32 axictl;
@@ -226,9 +244,132 @@ out:
 	dev_info(drvdata->dev, "TMC-ETR disabled\n");
 }
 
+static void *tmc_alloc_etr_buffer(struct coresight_device *csdev, int cpu,
+				  void **pages, int nr_pages, bool overwrite)
+{
+	int node;
+	struct cs_etr_buffers *buf;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (cpu == -1)
+		cpu = smp_processor_id();
+	node = cpu_to_node(cpu);
+
+	/* Allocate memory structure for interaction with Perf */
+	buf = kzalloc_node(sizeof(struct cs_etr_buffers), GFP_KERNEL, node);
+	if (!buf)
+		return NULL;
+
+	buf->dev = drvdata->dev;
+	buf->size = drvdata->size;
+	buf->vaddr = dma_alloc_coherent(buf->dev, buf->size,
+					&buf->paddr, GFP_KERNEL);
+	if (!buf->vaddr) {
+		kfree(buf);
+		return NULL;
+	}
+
+	buf->tmc.snapshot = overwrite;
+	buf->tmc.nr_pages = nr_pages;
+	buf->tmc.data_pages = pages;
+
+	return buf;
+}
+
+static void tmc_free_etr_buffer(void *config)
+{
+	struct cs_etr_buffers *buf = config;
+
+	dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->paddr);
+	kfree(buf);
+}
+
+static int tmc_set_etr_buffer(struct coresight_device *csdev,
+			      struct perf_output_handle *handle,
+			      void *sink_config)
+{
+	int ret = 0;
+	unsigned long head;
+	struct cs_etr_buffers *buf = sink_config;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	/* wrap head around to the amount of space we have */
+	head = handle->head & ((buf->tmc.nr_pages << PAGE_SHIFT) - 1);
+
+	/* find the page to write to */
+	buf->tmc.cur = head / PAGE_SIZE;
+
+	/* and offset within that page */
+	buf->tmc.offset = head % PAGE_SIZE;
+
+	local_set(&buf->tmc.data_size, 0);
+
+	/* Tell the HW where to put the trace data */
+	drvdata->vaddr = buf->vaddr;
+	drvdata->paddr = buf->paddr;
+	memset(drvdata->vaddr, 0, drvdata->size);
+
+	return ret;
+}
+
+static unsigned long tmc_reset_etr_buffer(struct coresight_device *csdev,
+					  struct perf_output_handle *handle,
+					  void *sink_config, bool *lost)
+{
+	unsigned long size = 0;
+	struct cs_etr_buffers *buf = sink_config;
+	struct tmc_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->tmc.snapshot) {
+			size = buf->tmc.nr_pages << PAGE_SHIFT;
+			handle->head = local_xchg(&buf->tmc.data_size, size);
+		}
+
+		/*
+		 * Tell the tracer PMU how much we got in this run and if
+		 * something went wrong along the way.  Nobody else can use
+		 * this cs_etr_buffers instance until we are done.  As such
+		 * resetting parameters here and squaring off with the ring
+		 * buffer API in the tracer PMU is fine.
+		 */
+		*lost = !!local_xchg(&buf->tmc.lost, 0);
+		size = local_xchg(&buf->tmc.data_size, 0);
+	}
+
+	/* Get ready for another run */
+	drvdata->vaddr = NULL;
+	drvdata->paddr = 0;
+
+	return size;
+}
+
+static void tmc_update_etr_buffer(struct coresight_device *csdev,
+				  struct perf_output_handle *handle,
+				  void *sink_config)
+{
+	struct cs_etr_buffers *buf = sink_config;
+
+	/*
+	 * An ETR configured to work in contiguous memory mode works the same
+	 * was as an ETB or ETF.
+	 */
+	tmc_update_etf_buffer(csdev, handle, &buf->tmc);
+}
+
 static const struct coresight_ops_sink tmc_etr_sink_ops = {
 	.enable		= tmc_enable_etr_sink,
 	.disable	= tmc_disable_etr_sink,
+	.alloc_buffer	= tmc_alloc_etr_buffer,
+	.free_buffer	= tmc_free_etr_buffer,
+	.set_buffer	= tmc_set_etr_buffer,
+	.reset_buffer	= tmc_reset_etr_buffer,
+	.update_buffer	= tmc_update_etr_buffer,
 };
 
 const struct coresight_ops tmc_etr_cs_ops = {
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 3de442f15be7..12f1bd8ccdc9 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -154,6 +154,9 @@ void tmc_disable_hw(struct tmc_drvdata *drvdata);
 /* ETB/ETF functions */
 int tmc_read_prepare_etf(struct tmc_drvdata *drvdata);
 int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata);
+void tmc_update_etf_buffer(struct coresight_device *csdev,
+			   struct perf_output_handle *handle,
+			   void *sink_config);
 extern const struct coresight_ops tmc_etb_cs_ops;
 extern const struct coresight_ops tmc_etf_cs_ops;
 
-- 
2.1.4

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

* [PATCH 14/14] coresight: configuring ETF in FIFO mode when acting as link
  2016-03-22 20:23 ` Mathieu Poirier
@ 2016-03-22 20:23   ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: linux-kernel

When part of a path but not identified as a sink, the EFT has to
be configured as a link and placed in HW FIFO mode.  As such when
enabling a path, call the right configuration function based on
the role the ETF if playing in this trace run.

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

diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 6ec2b66af9ee..fa7ecbd99901 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -287,15 +287,27 @@ int coresight_enable_path(struct list_head *path, u32 mode)
 {
 
 	int ret = 0;
+	u32 type;
 	struct coresight_node *nd;
 	struct coresight_device *csdev, *parent, *child;
 
 	list_for_each_entry_reverse(nd, path, link) {
 		csdev = nd->csdev;
+		type = csdev->type;
 
-		switch (csdev->type) {
+		/*
+		 * ETF devices are tricky... They can be a link or a sink,
+		 * depending on how they are configured.  If an ETF has been
+		 * "activated" it will be configured as a sink, otherwise
+		 * go ahead with the link configuration.
+		 */
+		if (type == CORESIGHT_DEV_TYPE_LINKSINK)
+			type = (csdev == coresight_get_sink(path)) ?
+						CORESIGHT_DEV_TYPE_SINK :
+						CORESIGHT_DEV_TYPE_LINK;
+
+		switch (type) {
 		case CORESIGHT_DEV_TYPE_SINK:
-		case CORESIGHT_DEV_TYPE_LINKSINK:
 			ret = coresight_enable_sink(csdev, mode);
 			if (ret)
 				goto err;
@@ -304,6 +316,7 @@ int coresight_enable_path(struct list_head *path, u32 mode)
 			/* sources are enabled from either sysFS or Perf */
 			break;
 		case CORESIGHT_DEV_TYPE_LINK:
+		case CORESIGHT_DEV_TYPE_LINKSINK:
 			parent = list_prev_entry(nd, link)->csdev;
 			child = list_next_entry(nd, link)->csdev;
 			ret = coresight_enable_link(csdev, parent, child);
-- 
2.1.4

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

* [PATCH 14/14] coresight: configuring ETF in FIFO mode when acting as link
@ 2016-03-22 20:23   ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-22 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

When part of a path but not identified as a sink, the EFT has to
be configured as a link and placed in HW FIFO mode.  As such when
enabling a path, call the right configuration function based on
the role the ETF if playing in this trace run.

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

diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 6ec2b66af9ee..fa7ecbd99901 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -287,15 +287,27 @@ int coresight_enable_path(struct list_head *path, u32 mode)
 {
 
 	int ret = 0;
+	u32 type;
 	struct coresight_node *nd;
 	struct coresight_device *csdev, *parent, *child;
 
 	list_for_each_entry_reverse(nd, path, link) {
 		csdev = nd->csdev;
+		type = csdev->type;
 
-		switch (csdev->type) {
+		/*
+		 * ETF devices are tricky... They can be a link or a sink,
+		 * depending on how they are configured.  If an ETF has been
+		 * "activated" it will be configured as a sink, otherwise
+		 * go ahead with the link configuration.
+		 */
+		if (type == CORESIGHT_DEV_TYPE_LINKSINK)
+			type = (csdev == coresight_get_sink(path)) ?
+						CORESIGHT_DEV_TYPE_SINK :
+						CORESIGHT_DEV_TYPE_LINK;
+
+		switch (type) {
 		case CORESIGHT_DEV_TYPE_SINK:
-		case CORESIGHT_DEV_TYPE_LINKSINK:
 			ret = coresight_enable_sink(csdev, mode);
 			if (ret)
 				goto err;
@@ -304,6 +316,7 @@ int coresight_enable_path(struct list_head *path, u32 mode)
 			/* sources are enabled from either sysFS or Perf */
 			break;
 		case CORESIGHT_DEV_TYPE_LINK:
+		case CORESIGHT_DEV_TYPE_LINKSINK:
 			parent = list_prev_entry(nd, link)->csdev;
 			child = list_next_entry(nd, link)->csdev;
 			ret = coresight_enable_link(csdev, parent, child);
-- 
2.1.4

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

* Re: [PATCH 03/14] coresight: tmc: re-implementing tmc_read_prepare/unprepare() functions
  2016-03-22 20:23   ` Mathieu Poirier
  (?)
@ 2016-03-23 10:37   ` Suzuki K. Poulose
  2016-03-24 16:38       ` Mathieu Poirier
  2016-03-24 19:15       ` Mathieu Poirier
  -1 siblings, 2 replies; 44+ messages in thread
From: Suzuki K. Poulose @ 2016-03-23 10:37 UTC (permalink / raw)
  To: Mathieu Poirier; +Cc: linux-arm-kernel, linux-kernel

On 22/03/16 20:23, Mathieu Poirier wrote:
> In their current implementation the tmc_read_prepare/unprepare()
> are a lump of if/else that is difficult to read.  This patch is
> alleviating that by using a switch statement.  The latter also
> allows for a better control on the error path.
>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>   drivers/hwtracing/coresight/coresight-tmc.c | 56 ++++++++++++++++++-----------
>   1 file changed, 36 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
> index f4ba837a0810..208d47dd3083 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc.c
> +++ b/drivers/hwtracing/coresight/coresight-tmc.c
> @@ -430,7 +430,7 @@ static const struct coresight_ops tmc_etf_cs_ops = {
>

> -	if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
> +	switch (drvdata->config_type) {
> +	case TMC_CONFIG_TYPE_ETB:
>   		tmc_etb_disable_hw(drvdata);
> -	} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
> -		tmc_etr_disable_hw(drvdata);
> -	} else {
> +		break;
> +	case TMC_CONFIG_TYPE_ETF:
> +		/* There is no point in reading a TMC in HW FIFO mode */
>   		mode = readl_relaxed(drvdata->base + TMC_MODE);
> -		if (mode == TMC_MODE_CIRCULAR_BUFFER) {
> -			tmc_etb_disable_hw(drvdata);
> -		} else {
> -			ret = -ENODEV;
> +		if (mode != TMC_MODE_CIRCULAR_BUFFER) {
> +			ret = -EINVAL;
>   			goto err;
>   		}
> +
> +		tmc_etb_disable_hw(drvdata);
> +		break;
> +	case TMC_CONFIG_TYPE_ETR:
> +		tmc_etr_disable_hw(drvdata);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		goto err;
>   	}

We seem to be doing this switch at different places in the code just for enable_hw/disable_hw.
e.g, tmc_enable, tmc_disable

Could we make this a bit more cleaner by introducing something like this ?

struct tmc_hw_ops {
	int (*enable_hw)(struct tmc_drvdata *drvdata, enum tmc_mode mode);
	int (*disable_hw)(struct tmc_drvdata *drvdata, enum tmc_mode mode);
};

struct tmc_hw_ops tmc_etf_ops = {
	tmc_etf_enable_hw,
	tmc_etf_disable_hw,
};

similiarly for etb and etr and then add struct tmc_hw_ops *hw_ops to tmc_drvdata, initialised
at probe time (while reading the config_type).


Suzuki

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

* Re: [PATCH 01/14] coresight: tmc: modifying naming convention
  2016-03-22 20:23   ` Mathieu Poirier
  (?)
@ 2016-03-23 10:38   ` Suzuki K. Poulose
  -1 siblings, 0 replies; 44+ messages in thread
From: Suzuki K. Poulose @ 2016-03-23 10:38 UTC (permalink / raw)
  To: Mathieu Poirier, linux-arm-kernel; +Cc: linux-kernel

On 22/03/16 20:23, Mathieu Poirier wrote:
> According to the TMC architectural state machine, the 'stopped'
> state is reached when bit 2 (TMCReady) of the TMC Status register
> turns to '1'.  The code is correct but the naming convention isn't.
>
> The 'Triggered' bit occupies position '1' of the TMC Status register
> and has nothing to do with the indication of the TMC entering the
> stopped state. As such renaming function "tmc_wait_for_triggered()"
> and changing the #define to reflect what the code is really doing.
>
> This patch has no effect other than clarifying the semantic.
>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>

> -	tmc_wait_for_ready(drvdata);
> +	tmc_wait_for_tmcready(drvdata);

Thanks for cleaning this up. The code was indeed confusing.

Suzuki

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

* Re: [PATCH 03/14] coresight: tmc: re-implementing tmc_read_prepare/unprepare() functions
  2016-03-23 10:37   ` Suzuki K. Poulose
@ 2016-03-24 16:38       ` Mathieu Poirier
  2016-03-24 19:15       ` Mathieu Poirier
  1 sibling, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-24 16:38 UTC (permalink / raw)
  To: Suzuki K. Poulose; +Cc: linux-arm-kernel, linux-kernel

On 23 March 2016 at 04:37, Suzuki K. Poulose <Suzuki.Poulose@arm.com> wrote:
> On 22/03/16 20:23, Mathieu Poirier wrote:
>>
>> In their current implementation the tmc_read_prepare/unprepare()
>> are a lump of if/else that is difficult to read.  This patch is
>> alleviating that by using a switch statement.  The latter also
>> allows for a better control on the error path.
>>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> ---
>>   drivers/hwtracing/coresight/coresight-tmc.c | 56
>> ++++++++++++++++++-----------
>>   1 file changed, 36 insertions(+), 20 deletions(-)
>>
>> diff --git a/drivers/hwtracing/coresight/coresight-tmc.c
>> b/drivers/hwtracing/coresight/coresight-tmc.c
>> index f4ba837a0810..208d47dd3083 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc.c
>> +++ b/drivers/hwtracing/coresight/coresight-tmc.c
>> @@ -430,7 +430,7 @@ static const struct coresight_ops tmc_etf_cs_ops = {
>>
>
>> -       if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
>> +       switch (drvdata->config_type) {
>> +       case TMC_CONFIG_TYPE_ETB:
>>                 tmc_etb_disable_hw(drvdata);
>> -       } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
>> -               tmc_etr_disable_hw(drvdata);
>> -       } else {
>> +               break;
>> +       case TMC_CONFIG_TYPE_ETF:
>> +               /* There is no point in reading a TMC in HW FIFO mode */
>>                 mode = readl_relaxed(drvdata->base + TMC_MODE);
>> -               if (mode == TMC_MODE_CIRCULAR_BUFFER) {
>> -                       tmc_etb_disable_hw(drvdata);
>> -               } else {
>> -                       ret = -ENODEV;
>> +               if (mode != TMC_MODE_CIRCULAR_BUFFER) {
>> +                       ret = -EINVAL;
>>                         goto err;
>>                 }
>> +
>> +               tmc_etb_disable_hw(drvdata);
>> +               break;
>> +       case TMC_CONFIG_TYPE_ETR:
>> +               tmc_etr_disable_hw(drvdata);
>> +               break;
>> +       default:
>> +               ret = -EINVAL;
>> +               goto err;
>>         }
>
>
> We seem to be doing this switch at different places in the code just for
> enable_hw/disable_hw.
> e.g, tmc_enable, tmc_disable
>
> Could we make this a bit more cleaner by introducing something like this ?
>
> struct tmc_hw_ops {
>         int (*enable_hw)(struct tmc_drvdata *drvdata, enum tmc_mode mode);
>         int (*disable_hw)(struct tmc_drvdata *drvdata, enum tmc_mode mode);
> };
>
> struct tmc_hw_ops tmc_etf_ops = {
>         tmc_etf_enable_hw,
>         tmc_etf_disable_hw,
> };
>
> similiarly for etb and etr and then add struct tmc_hw_ops *hw_ops to
> tmc_drvdata, initialised
> at probe time (while reading the config_type).

That is a good idea - let me experiment with it a little...

>
>
> Suzuki
>

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

* [PATCH 03/14] coresight: tmc: re-implementing tmc_read_prepare/unprepare() functions
@ 2016-03-24 16:38       ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-24 16:38 UTC (permalink / raw)
  To: linux-arm-kernel

On 23 March 2016 at 04:37, Suzuki K. Poulose <Suzuki.Poulose@arm.com> wrote:
> On 22/03/16 20:23, Mathieu Poirier wrote:
>>
>> In their current implementation the tmc_read_prepare/unprepare()
>> are a lump of if/else that is difficult to read.  This patch is
>> alleviating that by using a switch statement.  The latter also
>> allows for a better control on the error path.
>>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> ---
>>   drivers/hwtracing/coresight/coresight-tmc.c | 56
>> ++++++++++++++++++-----------
>>   1 file changed, 36 insertions(+), 20 deletions(-)
>>
>> diff --git a/drivers/hwtracing/coresight/coresight-tmc.c
>> b/drivers/hwtracing/coresight/coresight-tmc.c
>> index f4ba837a0810..208d47dd3083 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc.c
>> +++ b/drivers/hwtracing/coresight/coresight-tmc.c
>> @@ -430,7 +430,7 @@ static const struct coresight_ops tmc_etf_cs_ops = {
>>
>
>> -       if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
>> +       switch (drvdata->config_type) {
>> +       case TMC_CONFIG_TYPE_ETB:
>>                 tmc_etb_disable_hw(drvdata);
>> -       } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
>> -               tmc_etr_disable_hw(drvdata);
>> -       } else {
>> +               break;
>> +       case TMC_CONFIG_TYPE_ETF:
>> +               /* There is no point in reading a TMC in HW FIFO mode */
>>                 mode = readl_relaxed(drvdata->base + TMC_MODE);
>> -               if (mode == TMC_MODE_CIRCULAR_BUFFER) {
>> -                       tmc_etb_disable_hw(drvdata);
>> -               } else {
>> -                       ret = -ENODEV;
>> +               if (mode != TMC_MODE_CIRCULAR_BUFFER) {
>> +                       ret = -EINVAL;
>>                         goto err;
>>                 }
>> +
>> +               tmc_etb_disable_hw(drvdata);
>> +               break;
>> +       case TMC_CONFIG_TYPE_ETR:
>> +               tmc_etr_disable_hw(drvdata);
>> +               break;
>> +       default:
>> +               ret = -EINVAL;
>> +               goto err;
>>         }
>
>
> We seem to be doing this switch at different places in the code just for
> enable_hw/disable_hw.
> e.g, tmc_enable, tmc_disable
>
> Could we make this a bit more cleaner by introducing something like this ?
>
> struct tmc_hw_ops {
>         int (*enable_hw)(struct tmc_drvdata *drvdata, enum tmc_mode mode);
>         int (*disable_hw)(struct tmc_drvdata *drvdata, enum tmc_mode mode);
> };
>
> struct tmc_hw_ops tmc_etf_ops = {
>         tmc_etf_enable_hw,
>         tmc_etf_disable_hw,
> };
>
> similiarly for etb and etr and then add struct tmc_hw_ops *hw_ops to
> tmc_drvdata, initialised
> at probe time (while reading the config_type).

That is a good idea - let me experiment with it a little...

>
>
> Suzuki
>

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

* Re: [PATCH 03/14] coresight: tmc: re-implementing tmc_read_prepare/unprepare() functions
  2016-03-23 10:37   ` Suzuki K. Poulose
@ 2016-03-24 19:15       ` Mathieu Poirier
  2016-03-24 19:15       ` Mathieu Poirier
  1 sibling, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-24 19:15 UTC (permalink / raw)
  To: Suzuki K. Poulose; +Cc: linux-arm-kernel, linux-kernel

On 23 March 2016 at 04:37, Suzuki K. Poulose <Suzuki.Poulose@arm.com> wrote:
> On 22/03/16 20:23, Mathieu Poirier wrote:
>>
>> In their current implementation the tmc_read_prepare/unprepare()
>> are a lump of if/else that is difficult to read.  This patch is
>> alleviating that by using a switch statement.  The latter also
>> allows for a better control on the error path.
>>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> ---
>>   drivers/hwtracing/coresight/coresight-tmc.c | 56
>> ++++++++++++++++++-----------
>>   1 file changed, 36 insertions(+), 20 deletions(-)
>>
>> diff --git a/drivers/hwtracing/coresight/coresight-tmc.c
>> b/drivers/hwtracing/coresight/coresight-tmc.c
>> index f4ba837a0810..208d47dd3083 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc.c
>> +++ b/drivers/hwtracing/coresight/coresight-tmc.c
>> @@ -430,7 +430,7 @@ static const struct coresight_ops tmc_etf_cs_ops = {
>>
>
>> -       if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
>> +       switch (drvdata->config_type) {
>> +       case TMC_CONFIG_TYPE_ETB:
>>                 tmc_etb_disable_hw(drvdata);
>> -       } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
>> -               tmc_etr_disable_hw(drvdata);
>> -       } else {
>> +               break;
>> +       case TMC_CONFIG_TYPE_ETF:
>> +               /* There is no point in reading a TMC in HW FIFO mode */
>>                 mode = readl_relaxed(drvdata->base + TMC_MODE);
>> -               if (mode == TMC_MODE_CIRCULAR_BUFFER) {
>> -                       tmc_etb_disable_hw(drvdata);
>> -               } else {
>> -                       ret = -ENODEV;
>> +               if (mode != TMC_MODE_CIRCULAR_BUFFER) {
>> +                       ret = -EINVAL;
>>                         goto err;
>>                 }
>> +
>> +               tmc_etb_disable_hw(drvdata);
>> +               break;
>> +       case TMC_CONFIG_TYPE_ETR:
>> +               tmc_etr_disable_hw(drvdata);
>> +               break;
>> +       default:
>> +               ret = -EINVAL;
>> +               goto err;
>>         }
>
>
> We seem to be doing this switch at different places in the code just for
> enable_hw/disable_hw.
> e.g, tmc_enable, tmc_disable
>
> Could we make this a bit more cleaner by introducing something like this ?
>
> struct tmc_hw_ops {
>         int (*enable_hw)(struct tmc_drvdata *drvdata, enum tmc_mode mode);
>         int (*disable_hw)(struct tmc_drvdata *drvdata, enum tmc_mode mode);
> };
>
> struct tmc_hw_ops tmc_etf_ops = {
>         tmc_etf_enable_hw,
>         tmc_etf_disable_hw,
> };
>
> similiarly for etb and etr and then add struct tmc_hw_ops *hw_ops to
> tmc_drvdata, initialised
> at probe time (while reading the config_type).

So I started to look into implementing your proposition.  In this
patch adding a new hw_ops does make sense but the next few patches
keep cleaning up the code even further, with a very clean and neat end
result [1].  We could still optimise things further but not by a lot.
In the end would will likely end up with the same amount of code
(maybe a little more) without much gain.

Performance wise the hit taken by the case statement is negligible,
even more so since the code is only driven by user space.

As such I don't think there is much to gain by moving ahead with this
- get back to me if you feel otherwise.

Thanks for the review,
Mathieu


[1].  https://git.linaro.org/people/mathieu.poirier/coresight.git/blob/refs/heads/perf-opencsd-4.5:/drivers/hwtracing/coresight/coresight-tmc.c#l75


>
>
> Suzuki
>

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

* [PATCH 03/14] coresight: tmc: re-implementing tmc_read_prepare/unprepare() functions
@ 2016-03-24 19:15       ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-03-24 19:15 UTC (permalink / raw)
  To: linux-arm-kernel

On 23 March 2016 at 04:37, Suzuki K. Poulose <Suzuki.Poulose@arm.com> wrote:
> On 22/03/16 20:23, Mathieu Poirier wrote:
>>
>> In their current implementation the tmc_read_prepare/unprepare()
>> are a lump of if/else that is difficult to read.  This patch is
>> alleviating that by using a switch statement.  The latter also
>> allows for a better control on the error path.
>>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> ---
>>   drivers/hwtracing/coresight/coresight-tmc.c | 56
>> ++++++++++++++++++-----------
>>   1 file changed, 36 insertions(+), 20 deletions(-)
>>
>> diff --git a/drivers/hwtracing/coresight/coresight-tmc.c
>> b/drivers/hwtracing/coresight/coresight-tmc.c
>> index f4ba837a0810..208d47dd3083 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc.c
>> +++ b/drivers/hwtracing/coresight/coresight-tmc.c
>> @@ -430,7 +430,7 @@ static const struct coresight_ops tmc_etf_cs_ops = {
>>
>
>> -       if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
>> +       switch (drvdata->config_type) {
>> +       case TMC_CONFIG_TYPE_ETB:
>>                 tmc_etb_disable_hw(drvdata);
>> -       } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
>> -               tmc_etr_disable_hw(drvdata);
>> -       } else {
>> +               break;
>> +       case TMC_CONFIG_TYPE_ETF:
>> +               /* There is no point in reading a TMC in HW FIFO mode */
>>                 mode = readl_relaxed(drvdata->base + TMC_MODE);
>> -               if (mode == TMC_MODE_CIRCULAR_BUFFER) {
>> -                       tmc_etb_disable_hw(drvdata);
>> -               } else {
>> -                       ret = -ENODEV;
>> +               if (mode != TMC_MODE_CIRCULAR_BUFFER) {
>> +                       ret = -EINVAL;
>>                         goto err;
>>                 }
>> +
>> +               tmc_etb_disable_hw(drvdata);
>> +               break;
>> +       case TMC_CONFIG_TYPE_ETR:
>> +               tmc_etr_disable_hw(drvdata);
>> +               break;
>> +       default:
>> +               ret = -EINVAL;
>> +               goto err;
>>         }
>
>
> We seem to be doing this switch at different places in the code just for
> enable_hw/disable_hw.
> e.g, tmc_enable, tmc_disable
>
> Could we make this a bit more cleaner by introducing something like this ?
>
> struct tmc_hw_ops {
>         int (*enable_hw)(struct tmc_drvdata *drvdata, enum tmc_mode mode);
>         int (*disable_hw)(struct tmc_drvdata *drvdata, enum tmc_mode mode);
> };
>
> struct tmc_hw_ops tmc_etf_ops = {
>         tmc_etf_enable_hw,
>         tmc_etf_disable_hw,
> };
>
> similiarly for etb and etr and then add struct tmc_hw_ops *hw_ops to
> tmc_drvdata, initialised
> at probe time (while reading the config_type).

So I started to look into implementing your proposition.  In this
patch adding a new hw_ops does make sense but the next few patches
keep cleaning up the code even further, with a very clean and neat end
result [1].  We could still optimise things further but not by a lot.
In the end would will likely end up with the same amount of code
(maybe a little more) without much gain.

Performance wise the hit taken by the case statement is negligible,
even more so since the code is only driven by user space.

As such I don't think there is much to gain by moving ahead with this
- get back to me if you feel otherwise.

Thanks for the review,
Mathieu


[1].  https://git.linaro.org/people/mathieu.poirier/coresight.git/blob/refs/heads/perf-opencsd-4.5:/drivers/hwtracing/coresight/coresight-tmc.c#l75


>
>
> Suzuki
>

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

* Re: [PATCH 06/14] coresight: tmc: making prepare/unprepare functions generic
  2016-03-22 20:23   ` Mathieu Poirier
@ 2016-04-07 15:43     ` Suzuki K Poulose
  -1 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2016-04-07 15:43 UTC (permalink / raw)
  To: Mathieu Poirier, linux-arm-kernel; +Cc: linux-kernel

On 22/03/16 20:23, Mathieu Poirier wrote:
> Dealing with HW related matters in tmc_read_prepare/unprepare
> becomes convoluted when many cases need to be handled distinctively.
>
> As such moving processing related to HW setup to individual driver
> files and keep the core driver generic.
>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>   drivers/hwtracing/coresight/coresight-tmc-etf.c | 68 ++++++++++++++++++++++++-
>   drivers/hwtracing/coresight/coresight-tmc-etr.c | 50 +++++++++++++++++-
>   drivers/hwtracing/coresight/coresight-tmc.c     | 55 ++++----------------
>   drivers/hwtracing/coresight/coresight-tmc.h     |  8 +--
>   4 files changed, 131 insertions(+), 50 deletions(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
> index 467d19221f7b..789384be81b6 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
> +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
> @@ -71,7 +71,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
>   	}
>   }
>
> -void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
> +static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
>   {
>   	CS_UNLOCK(drvdata->base);
>
> @@ -202,3 +202,69 @@ const struct coresight_ops tmc_etf_cs_ops = {
>   	.sink_ops	= &tmc_etf_sink_ops,
>   	.link_ops	= &tmc_etf_link_ops,
>   };
> +

> +int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)

...

> +int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)


Ideally, we are operating ETF as if it was an ETB in the sink mode,
so,

tmc_read_{prepare/unprepare}_etb sounds more intuitive than _etf.

Nothing wrong with the current code.

Thanks
Suzuki

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

* [PATCH 06/14] coresight: tmc: making prepare/unprepare functions generic
@ 2016-04-07 15:43     ` Suzuki K Poulose
  0 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2016-04-07 15:43 UTC (permalink / raw)
  To: linux-arm-kernel

On 22/03/16 20:23, Mathieu Poirier wrote:
> Dealing with HW related matters in tmc_read_prepare/unprepare
> becomes convoluted when many cases need to be handled distinctively.
>
> As such moving processing related to HW setup to individual driver
> files and keep the core driver generic.
>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>   drivers/hwtracing/coresight/coresight-tmc-etf.c | 68 ++++++++++++++++++++++++-
>   drivers/hwtracing/coresight/coresight-tmc-etr.c | 50 +++++++++++++++++-
>   drivers/hwtracing/coresight/coresight-tmc.c     | 55 ++++----------------
>   drivers/hwtracing/coresight/coresight-tmc.h     |  8 +--
>   4 files changed, 131 insertions(+), 50 deletions(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
> index 467d19221f7b..789384be81b6 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
> +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
> @@ -71,7 +71,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
>   	}
>   }
>
> -void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
> +static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
>   {
>   	CS_UNLOCK(drvdata->base);
>
> @@ -202,3 +202,69 @@ const struct coresight_ops tmc_etf_cs_ops = {
>   	.sink_ops	= &tmc_etf_sink_ops,
>   	.link_ops	= &tmc_etf_link_ops,
>   };
> +

> +int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)

...

> +int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)


Ideally, we are operating ETF as if it was an ETB in the sink mode,
so,

tmc_read_{prepare/unprepare}_etb sounds more intuitive than _etf.

Nothing wrong with the current code.

Thanks
Suzuki

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

* Re: [PATCH 08/14] coresight: tmc: allocating memory when needed
  2016-03-22 20:23   ` Mathieu Poirier
@ 2016-04-07 16:50     ` Suzuki K Poulose
  -1 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2016-04-07 16:50 UTC (permalink / raw)
  To: Mathieu Poirier, linux-arm-kernel; +Cc: linux-kernel

On 22/03/16 20:23, Mathieu Poirier wrote:
> In it's current form the TMC probe() function allocates
> trace buffer memory at boot time, event if coresight isn't
> used.  This is highly inefficient since trace buffers can
> occupy a lot of memory that could be used otherwised.
>
> This patch allocates trace buffers on the fly, when the
> coresight subsytem is sollicited.  Allocated buffers are

nit: *subsystem* is *solicited*.

> released when traces are read using the device descriptors
> under /dev.
>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>   drivers/hwtracing/coresight/coresight-tmc-etf.c | 97 ++++++++++++++++++++-----
>   drivers/hwtracing/coresight/coresight-tmc-etr.c | 97 ++++++++++++++++++++++---
>   drivers/hwtracing/coresight/coresight-tmc.c     | 14 ----
>   3 files changed, 164 insertions(+), 44 deletions(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
> index f9749924a055..e5d67e01409c 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
> +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c


> @@ -205,52 +231,75 @@ const struct coresight_ops tmc_etf_cs_ops = {
>
>   int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)
>   {
> -	int ret = 0;
>   	enum tmc_mode mode;
>   	unsigned long flags;
>
>   	spin_lock_irqsave(&drvdata->spinlock, flags);
>
> -	/* The TMC isn't enable, so there is no need to disable it */
> -	if (!drvdata->enable)
> +	/* The TMC isn't enabled, so there is no need to disable it */
> +	if (!drvdata->enable) {
> +		/*
> +		 * The ETB/ETF is disabled already.  If drvdata::buf is NULL
> +		 * trace data has been harvested.
> +		 */
> +		if (!drvdata->buf) {
> +			spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +			return -EINVAL;
> +		}
> +
>   		goto out;
> +	}
>
>   	if (drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
>   	    drvdata->config_type != TMC_CONFIG_TYPE_ETF) {
> -		ret = -EINVAL;
> -		goto out;
> +		spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +		return -EINVAL;
>   	}
>
>   	/* There is no point in reading a TMC in HW FIFO mode */
>   	mode = readl_relaxed(drvdata->base + TMC_MODE);
>   	if (mode != TMC_MODE_CIRCULAR_BUFFER) {
> -		ret = -EINVAL;
> -		goto out;
> +		spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +		return -EINVAL;
>   	}

>
>   	tmc_etb_disable_hw(drvdata);
> -	drvdata->reading = true;
> +	tmc_etb_dump_hw(drvdata);

The addition of tmc_etb_dump_hw() should have been part of the previous patch,
which made disable() reusable.

>
>   out:
> +	drvdata->reading = true;

Instead of spilling the spin_unlock_irq_restore() and return from too many
different places in the function above, could we do :

done:
	drvdata->reading = true;

out:
>   	spin_unlock_irqrestore(&drvdata->spinlock, flags);
> -	return ret;
> +	return 0;


>   }
>
>   int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
>   {

...

> +	}
> +
> +	/* The TMC isn't enabled, so there is no need to enable it */
> +	if (!drvdata->enable) {
> +		/*
> +		 * The ETB/ETF is not tracing and the buffer was just read.
> +		 * As such prepare to free the trace buffer.
> +		 */
> +		buf = drvdata->buf;
> +
> +		/*
> +		 * drvdata::buf is switched on in tmc_read_prepare_etf() so
> +		 * it is important to set it back to NULL.
> +		 */
> +		drvdata->buf = NULL;
>   		goto out;
>   	}

This check should be brought down after the MODE check below for better error
check.

>
> @@ -258,13 +307,25 @@ int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
>   	mode = readl_relaxed(drvdata->base + TMC_MODE);
>   	if (mode != TMC_MODE_CIRCULAR_BUFFER) {
>   		ret = -EINVAL;
> -		goto out;
> +		goto err;
>   	}
>
> +	/*
> +	 * The trace run will continue with the same allocated trace buffer.
> +	 * As such zero-out the buffer so that we don't end up with stale
> +	 * data.
> +	 */

Could this drvdata->buf be NULL here ?

> +	memset(drvdata->buf, 0, drvdata->size);
>   	tmc_etb_enable_hw(drvdata);
> -	drvdata->reading = false;
>
>   out:
> +	drvdata->reading = false;
> +
> +err:
>   	spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +
> +	/* Free allocated memory outside of the spinlock */
> +	kfree(buf);
> +
>   	return ret;
>   }
> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
> index 67e7d5dd891f..c4962568276e 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
> +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
> @@ -16,6 +16,8 @@
>    */
>
>   #include <linux/coresight.h>
> +#include <linux/dma-mapping.h>
> +
>   #include "coresight-priv.h"
>   #include "coresight-tmc.h"
>
> @@ -82,19 +84,51 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
>
>   static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
>   {
> +	bool allocated = false;
>   	unsigned long flags;
> +	void __iomem *vaddr;
> +	dma_addr_t paddr;
>   	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
>
> +	/*
> +	 * Contiguous  memory can't be allocated while a spinlock is held.
> +	 * As such allocate memory here and free it if a buffer has already
> +	 * been allocated (from a previous session).
> +	 */
> +	vaddr = dma_alloc_coherent(drvdata->dev, drvdata->size,
> +				   &paddr, GFP_KERNEL);
> +	if (!vaddr)
> +		return -ENOMEM;
> +
>   	spin_lock_irqsave(&drvdata->spinlock, flags);
>   	if (drvdata->reading) {
>   		spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
>   		return -EBUSY;
>   	}

Both here and above in the etf/etb case, could we do a preliminary check
by taking the lock where we may not have to allocate the buffer and allocate
it if necessary and retake the lock ? I know it could make the code a bit more
complex, but could save us on unnecessary allocations.

Also it would be good to consolidate the return points here as well for the
error path.

>
> +	/*
> +	 * If drvdata::buf == NULL, use the memory allocated above.
> +	 * Otherwise a buffer still exists from a previous session, so
> +	 * simply use that.
> +	 */
> +	if (!drvdata->buf) {
> +		allocated = true;
> +		drvdata->vaddr = vaddr;
> +		drvdata->paddr = paddr;
> +		drvdata->buf = drvdata->vaddr;
> +	}
> +
> +	memset(drvdata->vaddr, 0, drvdata->size);
> +
>   	tmc_etr_enable_hw(drvdata);
>   	drvdata->enable = true;
>   	spin_unlock_irqrestore(&drvdata->spinlock, flags);
>
> +	/* Free memory outside the spinlock if need be */
> +	if (!allocated)
> +		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
> +
>   	dev_info(drvdata->dev, "TMC-ETR enabled\n");
>   	return 0;
>   }
> @@ -129,48 +163,87 @@ const struct coresight_ops tmc_etr_cs_ops = {
>
>   int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
>   {
> -	int ret = 0;
>   	unsigned long flags;
>
>   	spin_lock_irqsave(&drvdata->spinlock, flags);
>
> -	/* The TMC isn't enable, so there is no need to disable it */
> -	if (!drvdata->enable)
> +	/* The TMC isn't enabled, so there is no need to disable it */
> +	if (!drvdata->enable) {
> +		/*
> +		 * The ETR is disabled already.  If drvdata::buf is NULL
> +		 * trace data has been harvested.
> +		 */
> +		if (!drvdata->buf) {
> +			spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +			return -EINVAL;
> +		}
> +
>   		goto out;
> +	}
>
>   	if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
> -		ret = -EINVAL;
> -		goto out;
> +		spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +		return -EINVAL;
>   	}
>
>   	tmc_etr_disable_hw(drvdata);
> -	drvdata->reading = true;
> +	tmc_etr_dump_hw(drvdata);

Same as for etf/etb, shouldn't the above dump_hw be part of the previous patch ?

>
>   out:
> +	drvdata->reading = true;
>   	spin_unlock_irqrestore(&drvdata->spinlock, flags);
> -	return ret;
> +	return 0;
>   }
>
>   int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
>   {
>   	int ret = 0;
>   	unsigned long flags;
> +	void __iomem *vaddr = NULL;
> +	dma_addr_t paddr;
>
>   	spin_lock_irqsave(&drvdata->spinlock, flags);
>
> -	/* The TMC isn't enable, so there is no need to enable it */
> -	if (!drvdata->enable)
> -		goto out;
> -
>   	if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
>   		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	/* The TMC isn't enabled, so there is no need to enable it */
> +	if (!drvdata->enable) {
> +		/*
> +		 * The ETR is not tracing and trace data was just read. As
> +		 * such prepare to free the trace buffer.
> +		 */
> +		vaddr = drvdata->vaddr;
> +		paddr = drvdata->paddr;
> +
> +		/*
> +		 * drvdata::buf is switched on in tmc_read_prepare_etr() and
> +		 * tmc_enable_etr_sink so it is important to set it back to
> +		 * NULL.
> +		 */
> +		drvdata->buf = NULL;
>   		goto out;
>   	}
>
> +	/*
> +	 * The trace run will continue with the same allocated trace buffer.
> +	 * As such zero-out the buffer so that we don't end up with stale
> +	 * data.
> +	 */
> +	memset(drvdata->buf, 0, drvdata->size);

Are we sure drvdata->buf is not NULL here ?


Suzuki

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

* [PATCH 08/14] coresight: tmc: allocating memory when needed
@ 2016-04-07 16:50     ` Suzuki K Poulose
  0 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2016-04-07 16:50 UTC (permalink / raw)
  To: linux-arm-kernel

On 22/03/16 20:23, Mathieu Poirier wrote:
> In it's current form the TMC probe() function allocates
> trace buffer memory at boot time, event if coresight isn't
> used.  This is highly inefficient since trace buffers can
> occupy a lot of memory that could be used otherwised.
>
> This patch allocates trace buffers on the fly, when the
> coresight subsytem is sollicited.  Allocated buffers are

nit: *subsystem* is *solicited*.

> released when traces are read using the device descriptors
> under /dev.
>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> ---
>   drivers/hwtracing/coresight/coresight-tmc-etf.c | 97 ++++++++++++++++++++-----
>   drivers/hwtracing/coresight/coresight-tmc-etr.c | 97 ++++++++++++++++++++++---
>   drivers/hwtracing/coresight/coresight-tmc.c     | 14 ----
>   3 files changed, 164 insertions(+), 44 deletions(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
> index f9749924a055..e5d67e01409c 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
> +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c


> @@ -205,52 +231,75 @@ const struct coresight_ops tmc_etf_cs_ops = {
>
>   int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)
>   {
> -	int ret = 0;
>   	enum tmc_mode mode;
>   	unsigned long flags;
>
>   	spin_lock_irqsave(&drvdata->spinlock, flags);
>
> -	/* The TMC isn't enable, so there is no need to disable it */
> -	if (!drvdata->enable)
> +	/* The TMC isn't enabled, so there is no need to disable it */
> +	if (!drvdata->enable) {
> +		/*
> +		 * The ETB/ETF is disabled already.  If drvdata::buf is NULL
> +		 * trace data has been harvested.
> +		 */
> +		if (!drvdata->buf) {
> +			spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +			return -EINVAL;
> +		}
> +
>   		goto out;
> +	}
>
>   	if (drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
>   	    drvdata->config_type != TMC_CONFIG_TYPE_ETF) {
> -		ret = -EINVAL;
> -		goto out;
> +		spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +		return -EINVAL;
>   	}
>
>   	/* There is no point in reading a TMC in HW FIFO mode */
>   	mode = readl_relaxed(drvdata->base + TMC_MODE);
>   	if (mode != TMC_MODE_CIRCULAR_BUFFER) {
> -		ret = -EINVAL;
> -		goto out;
> +		spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +		return -EINVAL;
>   	}

>
>   	tmc_etb_disable_hw(drvdata);
> -	drvdata->reading = true;
> +	tmc_etb_dump_hw(drvdata);

The addition of tmc_etb_dump_hw() should have been part of the previous patch,
which made disable() reusable.

>
>   out:
> +	drvdata->reading = true;

Instead of spilling the spin_unlock_irq_restore() and return from too many
different places in the function above, could we do :

done:
	drvdata->reading = true;

out:
>   	spin_unlock_irqrestore(&drvdata->spinlock, flags);
> -	return ret;
> +	return 0;


>   }
>
>   int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
>   {

...

> +	}
> +
> +	/* The TMC isn't enabled, so there is no need to enable it */
> +	if (!drvdata->enable) {
> +		/*
> +		 * The ETB/ETF is not tracing and the buffer was just read.
> +		 * As such prepare to free the trace buffer.
> +		 */
> +		buf = drvdata->buf;
> +
> +		/*
> +		 * drvdata::buf is switched on in tmc_read_prepare_etf() so
> +		 * it is important to set it back to NULL.
> +		 */
> +		drvdata->buf = NULL;
>   		goto out;
>   	}

This check should be brought down after the MODE check below for better error
check.

>
> @@ -258,13 +307,25 @@ int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
>   	mode = readl_relaxed(drvdata->base + TMC_MODE);
>   	if (mode != TMC_MODE_CIRCULAR_BUFFER) {
>   		ret = -EINVAL;
> -		goto out;
> +		goto err;
>   	}
>
> +	/*
> +	 * The trace run will continue with the same allocated trace buffer.
> +	 * As such zero-out the buffer so that we don't end up with stale
> +	 * data.
> +	 */

Could this drvdata->buf be NULL here ?

> +	memset(drvdata->buf, 0, drvdata->size);
>   	tmc_etb_enable_hw(drvdata);
> -	drvdata->reading = false;
>
>   out:
> +	drvdata->reading = false;
> +
> +err:
>   	spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +
> +	/* Free allocated memory outside of the spinlock */
> +	kfree(buf);
> +
>   	return ret;
>   }
> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
> index 67e7d5dd891f..c4962568276e 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
> +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
> @@ -16,6 +16,8 @@
>    */
>
>   #include <linux/coresight.h>
> +#include <linux/dma-mapping.h>
> +
>   #include "coresight-priv.h"
>   #include "coresight-tmc.h"
>
> @@ -82,19 +84,51 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
>
>   static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
>   {
> +	bool allocated = false;
>   	unsigned long flags;
> +	void __iomem *vaddr;
> +	dma_addr_t paddr;
>   	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
>
> +	/*
> +	 * Contiguous  memory can't be allocated while a spinlock is held.
> +	 * As such allocate memory here and free it if a buffer has already
> +	 * been allocated (from a previous session).
> +	 */
> +	vaddr = dma_alloc_coherent(drvdata->dev, drvdata->size,
> +				   &paddr, GFP_KERNEL);
> +	if (!vaddr)
> +		return -ENOMEM;
> +
>   	spin_lock_irqsave(&drvdata->spinlock, flags);
>   	if (drvdata->reading) {
>   		spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
>   		return -EBUSY;
>   	}

Both here and above in the etf/etb case, could we do a preliminary check
by taking the lock where we may not have to allocate the buffer and allocate
it if necessary and retake the lock ? I know it could make the code a bit more
complex, but could save us on unnecessary allocations.

Also it would be good to consolidate the return points here as well for the
error path.

>
> +	/*
> +	 * If drvdata::buf == NULL, use the memory allocated above.
> +	 * Otherwise a buffer still exists from a previous session, so
> +	 * simply use that.
> +	 */
> +	if (!drvdata->buf) {
> +		allocated = true;
> +		drvdata->vaddr = vaddr;
> +		drvdata->paddr = paddr;
> +		drvdata->buf = drvdata->vaddr;
> +	}
> +
> +	memset(drvdata->vaddr, 0, drvdata->size);
> +
>   	tmc_etr_enable_hw(drvdata);
>   	drvdata->enable = true;
>   	spin_unlock_irqrestore(&drvdata->spinlock, flags);
>
> +	/* Free memory outside the spinlock if need be */
> +	if (!allocated)
> +		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
> +
>   	dev_info(drvdata->dev, "TMC-ETR enabled\n");
>   	return 0;
>   }
> @@ -129,48 +163,87 @@ const struct coresight_ops tmc_etr_cs_ops = {
>
>   int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
>   {
> -	int ret = 0;
>   	unsigned long flags;
>
>   	spin_lock_irqsave(&drvdata->spinlock, flags);
>
> -	/* The TMC isn't enable, so there is no need to disable it */
> -	if (!drvdata->enable)
> +	/* The TMC isn't enabled, so there is no need to disable it */
> +	if (!drvdata->enable) {
> +		/*
> +		 * The ETR is disabled already.  If drvdata::buf is NULL
> +		 * trace data has been harvested.
> +		 */
> +		if (!drvdata->buf) {
> +			spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +			return -EINVAL;
> +		}
> +
>   		goto out;
> +	}
>
>   	if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
> -		ret = -EINVAL;
> -		goto out;
> +		spin_unlock_irqrestore(&drvdata->spinlock, flags);
> +		return -EINVAL;
>   	}
>
>   	tmc_etr_disable_hw(drvdata);
> -	drvdata->reading = true;
> +	tmc_etr_dump_hw(drvdata);

Same as for etf/etb, shouldn't the above dump_hw be part of the previous patch ?

>
>   out:
> +	drvdata->reading = true;
>   	spin_unlock_irqrestore(&drvdata->spinlock, flags);
> -	return ret;
> +	return 0;
>   }
>
>   int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
>   {
>   	int ret = 0;
>   	unsigned long flags;
> +	void __iomem *vaddr = NULL;
> +	dma_addr_t paddr;
>
>   	spin_lock_irqsave(&drvdata->spinlock, flags);
>
> -	/* The TMC isn't enable, so there is no need to enable it */
> -	if (!drvdata->enable)
> -		goto out;
> -
>   	if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
>   		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	/* The TMC isn't enabled, so there is no need to enable it */
> +	if (!drvdata->enable) {
> +		/*
> +		 * The ETR is not tracing and trace data was just read. As
> +		 * such prepare to free the trace buffer.
> +		 */
> +		vaddr = drvdata->vaddr;
> +		paddr = drvdata->paddr;
> +
> +		/*
> +		 * drvdata::buf is switched on in tmc_read_prepare_etr() and
> +		 * tmc_enable_etr_sink so it is important to set it back to
> +		 * NULL.
> +		 */
> +		drvdata->buf = NULL;
>   		goto out;
>   	}
>
> +	/*
> +	 * The trace run will continue with the same allocated trace buffer.
> +	 * As such zero-out the buffer so that we don't end up with stale
> +	 * data.
> +	 */
> +	memset(drvdata->buf, 0, drvdata->size);

Are we sure drvdata->buf is not NULL here ?


Suzuki

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

* Re: [PATCH 09/14] coresight: tmc: adding mode of operation for link/sinks
  2016-03-22 20:23   ` Mathieu Poirier
@ 2016-04-07 17:19     ` Suzuki K Poulose
  -1 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2016-04-07 17:19 UTC (permalink / raw)
  To: Mathieu Poirier, linux-arm-kernel; +Cc: linux-kernel

On 22/03/16 20:23, Mathieu Poirier wrote:
> Moving tmc_drvdata::enable to a local_t mode.  That way the
> sink interface is aware of it's orgin and the foundation for
> mutual exclusion between the sysFS and Perf interface can be
> laid out.
>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>

>   static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
>   {
> +	u32 val;
>   	bool allocated = false;
>   	char *buf = NULL;
>   	unsigned long flags;
> @@ -125,6 +126,15 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
>   		return -EBUSY;
>   	}
>
> +	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);

Since we don't support PERF mode yet, should we check that before setting the mode ?

>
>   static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
>   {
> +	u32 val;
>   	bool allocated = false;
>   	unsigned long flags;
>   	void __iomem *vaddr;
> @@ -107,6 +108,15 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
>   		return -EBUSY;
>   	}
>
> +	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
> +	/*
> +	 * In sysFS mode we can have multiple writers per sink.  Since this
> +	 * sink is already enabled no memory is needed and the HW need not be
> +	 * touched.
> +	 */
> +	if (val == CS_MODE_SYSFS)
> +		goto out;
> +

Should we make sure 'mode' is not PERF ? Since we assume that the mode is
could be SYSFS in disable below ?


> diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
> index 6b11caf77ad1..6dbd70861b17 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc.h
> +++ b/drivers/hwtracing/coresight/coresight-tmc.h
> @@ -18,6 +18,7 @@
>   #ifndef _CORESIGHT_TMC_H
>   #define _CORESIGHT_TMC_H
>
> +#include <linux/io.h>

Why ?

Suzuki

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

* [PATCH 09/14] coresight: tmc: adding mode of operation for link/sinks
@ 2016-04-07 17:19     ` Suzuki K Poulose
  0 siblings, 0 replies; 44+ messages in thread
From: Suzuki K Poulose @ 2016-04-07 17:19 UTC (permalink / raw)
  To: linux-arm-kernel

On 22/03/16 20:23, Mathieu Poirier wrote:
> Moving tmc_drvdata::enable to a local_t mode.  That way the
> sink interface is aware of it's orgin and the foundation for
> mutual exclusion between the sysFS and Perf interface can be
> laid out.
>
> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>

>   static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
>   {
> +	u32 val;
>   	bool allocated = false;
>   	char *buf = NULL;
>   	unsigned long flags;
> @@ -125,6 +126,15 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
>   		return -EBUSY;
>   	}
>
> +	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);

Since we don't support PERF mode yet, should we check that before setting the mode ?

>
>   static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
>   {
> +	u32 val;
>   	bool allocated = false;
>   	unsigned long flags;
>   	void __iomem *vaddr;
> @@ -107,6 +108,15 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
>   		return -EBUSY;
>   	}
>
> +	val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
> +	/*
> +	 * In sysFS mode we can have multiple writers per sink.  Since this
> +	 * sink is already enabled no memory is needed and the HW need not be
> +	 * touched.
> +	 */
> +	if (val == CS_MODE_SYSFS)
> +		goto out;
> +

Should we make sure 'mode' is not PERF ? Since we assume that the mode is
could be SYSFS in disable below ?


> diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
> index 6b11caf77ad1..6dbd70861b17 100644
> --- a/drivers/hwtracing/coresight/coresight-tmc.h
> +++ b/drivers/hwtracing/coresight/coresight-tmc.h
> @@ -18,6 +18,7 @@
>   #ifndef _CORESIGHT_TMC_H
>   #define _CORESIGHT_TMC_H
>
> +#include <linux/io.h>

Why ?

Suzuki

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

* Re: [PATCH 08/14] coresight: tmc: allocating memory when needed
  2016-04-07 16:50     ` Suzuki K Poulose
@ 2016-04-08 15:23       ` Mathieu Poirier
  -1 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-04-08 15:23 UTC (permalink / raw)
  To: Suzuki K Poulose; +Cc: linux-arm-kernel, linux-kernel

On 7 April 2016 at 10:50, Suzuki K Poulose <Suzuki.Poulose@arm.com> wrote:
> On 22/03/16 20:23, Mathieu Poirier wrote:
>>
>> In it's current form the TMC probe() function allocates
>> trace buffer memory at boot time, event if coresight isn't
>> used.  This is highly inefficient since trace buffers can
>> occupy a lot of memory that could be used otherwised.
>>
>> This patch allocates trace buffers on the fly, when the
>> coresight subsytem is sollicited.  Allocated buffers are
>
>
> nit: *subsystem* is *solicited*.

Ack

>
>> released when traces are read using the device descriptors
>> under /dev.
>>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> ---
>>   drivers/hwtracing/coresight/coresight-tmc-etf.c | 97
>> ++++++++++++++++++++-----
>>   drivers/hwtracing/coresight/coresight-tmc-etr.c | 97
>> ++++++++++++++++++++++---
>>   drivers/hwtracing/coresight/coresight-tmc.c     | 14 ----
>>   3 files changed, 164 insertions(+), 44 deletions(-)
>>
>> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c
>> b/drivers/hwtracing/coresight/coresight-tmc-etf.c
>> index f9749924a055..e5d67e01409c 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
>> +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
>
>
>
>> @@ -205,52 +231,75 @@ const struct coresight_ops tmc_etf_cs_ops = {
>>
>>   int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)
>>   {
>> -       int ret = 0;
>>         enum tmc_mode mode;
>>         unsigned long flags;
>>
>>         spin_lock_irqsave(&drvdata->spinlock, flags);
>>
>> -       /* The TMC isn't enable, so there is no need to disable it */
>> -       if (!drvdata->enable)
>> +       /* The TMC isn't enabled, so there is no need to disable it */
>> +       if (!drvdata->enable) {
>> +               /*
>> +                * The ETB/ETF is disabled already.  If drvdata::buf is
>> NULL
>> +                * trace data has been harvested.
>> +                */
>> +               if (!drvdata->buf) {
>> +                       spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +                       return -EINVAL;
>> +               }
>> +
>>                 goto out;
>> +       }
>>
>>         if (drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
>>             drvdata->config_type != TMC_CONFIG_TYPE_ETF) {
>> -               ret = -EINVAL;
>> -               goto out;
>> +               spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +               return -EINVAL;
>>         }
>>
>>         /* There is no point in reading a TMC in HW FIFO mode */
>>         mode = readl_relaxed(drvdata->base + TMC_MODE);
>>         if (mode != TMC_MODE_CIRCULAR_BUFFER) {
>> -               ret = -EINVAL;
>> -               goto out;
>> +               spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +               return -EINVAL;
>>         }
>
>
>>
>>         tmc_etb_disable_hw(drvdata);
>> -       drvdata->reading = true;
>> +       tmc_etb_dump_hw(drvdata);
>
>
> The addition of tmc_etb_dump_hw() should have been part of the previous
> patch,
> which made disable() reusable.

Thanks for your comment on this patchset.

After Dan found an error with the memory allocation I started to look
at the driver again and found several areas worth improving.  I'm
currently re-writing this patchset and the result should be much
easier to follow.  I would appreciate if you can have a look when V2
is out.

Best regards,
Mathieu

[1]. http://www.spinics.net/lists/arm-kernel/msg494888.html

>
>>
>>   out:
>> +       drvdata->reading = true;
>
>
> Instead of spilling the spin_unlock_irq_restore() and return from too many
> different places in the function above, could we do :
>
> done:
>         drvdata->reading = true;
>
> out:
>>
>>         spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> -       return ret;
>> +       return 0;
>
>
>
>>   }
>>
>>   int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
>>   {
>
>
> ...
>
>> +       }
>> +
>> +       /* The TMC isn't enabled, so there is no need to enable it */
>> +       if (!drvdata->enable) {
>> +               /*
>> +                * The ETB/ETF is not tracing and the buffer was just
>> read.
>> +                * As such prepare to free the trace buffer.
>> +                */
>> +               buf = drvdata->buf;
>> +
>> +               /*
>> +                * drvdata::buf is switched on in tmc_read_prepare_etf()
>> so
>> +                * it is important to set it back to NULL.
>> +                */
>> +               drvdata->buf = NULL;
>>                 goto out;
>>         }
>
>
> This check should be brought down after the MODE check below for better
> error
> check.
>
>>
>> @@ -258,13 +307,25 @@ int tmc_read_unprepare_etf(struct tmc_drvdata
>> *drvdata)
>>         mode = readl_relaxed(drvdata->base + TMC_MODE);
>>         if (mode != TMC_MODE_CIRCULAR_BUFFER) {
>>                 ret = -EINVAL;
>> -               goto out;
>> +               goto err;
>>         }
>>
>> +       /*
>> +        * The trace run will continue with the same allocated trace
>> buffer.
>> +        * As such zero-out the buffer so that we don't end up with stale
>> +        * data.
>> +        */
>
>
> Could this drvdata->buf be NULL here ?
>
>> +       memset(drvdata->buf, 0, drvdata->size);
>>         tmc_etb_enable_hw(drvdata);
>> -       drvdata->reading = false;
>>
>>   out:
>> +       drvdata->reading = false;
>> +
>> +err:
>>         spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +
>> +       /* Free allocated memory outside of the spinlock */
>> +       kfree(buf);
>> +
>>         return ret;
>>   }
>> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> b/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> index 67e7d5dd891f..c4962568276e 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> @@ -16,6 +16,8 @@
>>    */
>>
>>   #include <linux/coresight.h>
>> +#include <linux/dma-mapping.h>
>> +
>>   #include "coresight-priv.h"
>>   #include "coresight-tmc.h"
>>
>> @@ -82,19 +84,51 @@ static void tmc_etr_disable_hw(struct tmc_drvdata
>> *drvdata)
>>
>>   static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
>>   {
>> +       bool allocated = false;
>>         unsigned long flags;
>> +       void __iomem *vaddr;
>> +       dma_addr_t paddr;
>>         struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
>>
>> +       /*
>> +        * Contiguous  memory can't be allocated while a spinlock is held.
>> +        * As such allocate memory here and free it if a buffer has
>> already
>> +        * been allocated (from a previous session).
>> +        */
>> +       vaddr = dma_alloc_coherent(drvdata->dev, drvdata->size,
>> +                                  &paddr, GFP_KERNEL);
>> +       if (!vaddr)
>> +               return -ENOMEM;
>> +
>>         spin_lock_irqsave(&drvdata->spinlock, flags);
>>         if (drvdata->reading) {
>>                 spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +               dma_free_coherent(drvdata->dev, drvdata->size, vaddr,
>> paddr);
>>                 return -EBUSY;
>>         }
>
>
> Both here and above in the etf/etb case, could we do a preliminary check
> by taking the lock where we may not have to allocate the buffer and allocate
> it if necessary and retake the lock ? I know it could make the code a bit
> more
> complex, but could save us on unnecessary allocations.
>
> Also it would be good to consolidate the return points here as well for the
> error path.
>
>>
>> +       /*
>> +        * If drvdata::buf == NULL, use the memory allocated above.
>> +        * Otherwise a buffer still exists from a previous session, so
>> +        * simply use that.
>> +        */
>> +       if (!drvdata->buf) {
>> +               allocated = true;
>> +               drvdata->vaddr = vaddr;
>> +               drvdata->paddr = paddr;
>> +               drvdata->buf = drvdata->vaddr;
>> +       }
>> +
>> +       memset(drvdata->vaddr, 0, drvdata->size);
>> +
>>         tmc_etr_enable_hw(drvdata);
>>         drvdata->enable = true;
>>         spin_unlock_irqrestore(&drvdata->spinlock, flags);
>>
>> +       /* Free memory outside the spinlock if need be */
>> +       if (!allocated)
>> +               dma_free_coherent(drvdata->dev, drvdata->size, vaddr,
>> paddr);
>> +
>>         dev_info(drvdata->dev, "TMC-ETR enabled\n");
>>         return 0;
>>   }
>> @@ -129,48 +163,87 @@ const struct coresight_ops tmc_etr_cs_ops = {
>>
>>   int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
>>   {
>> -       int ret = 0;
>>         unsigned long flags;
>>
>>         spin_lock_irqsave(&drvdata->spinlock, flags);
>>
>> -       /* The TMC isn't enable, so there is no need to disable it */
>> -       if (!drvdata->enable)
>> +       /* The TMC isn't enabled, so there is no need to disable it */
>> +       if (!drvdata->enable) {
>> +               /*
>> +                * The ETR is disabled already.  If drvdata::buf is NULL
>> +                * trace data has been harvested.
>> +                */
>> +               if (!drvdata->buf) {
>> +                       spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +                       return -EINVAL;
>> +               }
>> +
>>                 goto out;
>> +       }
>>
>>         if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
>> -               ret = -EINVAL;
>> -               goto out;
>> +               spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +               return -EINVAL;
>>         }
>>
>>         tmc_etr_disable_hw(drvdata);
>> -       drvdata->reading = true;
>> +       tmc_etr_dump_hw(drvdata);
>
>
> Same as for etf/etb, shouldn't the above dump_hw be part of the previous
> patch ?
>
>>
>>   out:
>> +       drvdata->reading = true;
>>         spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> -       return ret;
>> +       return 0;
>>   }
>>
>>   int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
>>   {
>>         int ret = 0;
>>         unsigned long flags;
>> +       void __iomem *vaddr = NULL;
>> +       dma_addr_t paddr;
>>
>>         spin_lock_irqsave(&drvdata->spinlock, flags);
>>
>> -       /* The TMC isn't enable, so there is no need to enable it */
>> -       if (!drvdata->enable)
>> -               goto out;
>> -
>>         if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
>>                 ret = -EINVAL;
>> +               goto err;
>> +       }
>> +
>> +       /* The TMC isn't enabled, so there is no need to enable it */
>> +       if (!drvdata->enable) {
>> +               /*
>> +                * The ETR is not tracing and trace data was just read. As
>> +                * such prepare to free the trace buffer.
>> +                */
>> +               vaddr = drvdata->vaddr;
>> +               paddr = drvdata->paddr;
>> +
>> +               /*
>> +                * drvdata::buf is switched on in tmc_read_prepare_etr()
>> and
>> +                * tmc_enable_etr_sink so it is important to set it back
>> to
>> +                * NULL.
>> +                */
>> +               drvdata->buf = NULL;
>>                 goto out;
>>         }
>>
>> +       /*
>> +        * The trace run will continue with the same allocated trace
>> buffer.
>> +        * As such zero-out the buffer so that we don't end up with stale
>> +        * data.
>> +        */
>> +       memset(drvdata->buf, 0, drvdata->size);
>
>
> Are we sure drvdata->buf is not NULL here ?
>
>
> Suzuki

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

* [PATCH 08/14] coresight: tmc: allocating memory when needed
@ 2016-04-08 15:23       ` Mathieu Poirier
  0 siblings, 0 replies; 44+ messages in thread
From: Mathieu Poirier @ 2016-04-08 15:23 UTC (permalink / raw)
  To: linux-arm-kernel

On 7 April 2016 at 10:50, Suzuki K Poulose <Suzuki.Poulose@arm.com> wrote:
> On 22/03/16 20:23, Mathieu Poirier wrote:
>>
>> In it's current form the TMC probe() function allocates
>> trace buffer memory at boot time, event if coresight isn't
>> used.  This is highly inefficient since trace buffers can
>> occupy a lot of memory that could be used otherwised.
>>
>> This patch allocates trace buffers on the fly, when the
>> coresight subsytem is sollicited.  Allocated buffers are
>
>
> nit: *subsystem* is *solicited*.

Ack

>
>> released when traces are read using the device descriptors
>> under /dev.
>>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> ---
>>   drivers/hwtracing/coresight/coresight-tmc-etf.c | 97
>> ++++++++++++++++++++-----
>>   drivers/hwtracing/coresight/coresight-tmc-etr.c | 97
>> ++++++++++++++++++++++---
>>   drivers/hwtracing/coresight/coresight-tmc.c     | 14 ----
>>   3 files changed, 164 insertions(+), 44 deletions(-)
>>
>> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c
>> b/drivers/hwtracing/coresight/coresight-tmc-etf.c
>> index f9749924a055..e5d67e01409c 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
>> +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
>
>
>
>> @@ -205,52 +231,75 @@ const struct coresight_ops tmc_etf_cs_ops = {
>>
>>   int tmc_read_prepare_etf(struct tmc_drvdata *drvdata)
>>   {
>> -       int ret = 0;
>>         enum tmc_mode mode;
>>         unsigned long flags;
>>
>>         spin_lock_irqsave(&drvdata->spinlock, flags);
>>
>> -       /* The TMC isn't enable, so there is no need to disable it */
>> -       if (!drvdata->enable)
>> +       /* The TMC isn't enabled, so there is no need to disable it */
>> +       if (!drvdata->enable) {
>> +               /*
>> +                * The ETB/ETF is disabled already.  If drvdata::buf is
>> NULL
>> +                * trace data has been harvested.
>> +                */
>> +               if (!drvdata->buf) {
>> +                       spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +                       return -EINVAL;
>> +               }
>> +
>>                 goto out;
>> +       }
>>
>>         if (drvdata->config_type != TMC_CONFIG_TYPE_ETB &&
>>             drvdata->config_type != TMC_CONFIG_TYPE_ETF) {
>> -               ret = -EINVAL;
>> -               goto out;
>> +               spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +               return -EINVAL;
>>         }
>>
>>         /* There is no point in reading a TMC in HW FIFO mode */
>>         mode = readl_relaxed(drvdata->base + TMC_MODE);
>>         if (mode != TMC_MODE_CIRCULAR_BUFFER) {
>> -               ret = -EINVAL;
>> -               goto out;
>> +               spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +               return -EINVAL;
>>         }
>
>
>>
>>         tmc_etb_disable_hw(drvdata);
>> -       drvdata->reading = true;
>> +       tmc_etb_dump_hw(drvdata);
>
>
> The addition of tmc_etb_dump_hw() should have been part of the previous
> patch,
> which made disable() reusable.

Thanks for your comment on this patchset.

After Dan found an error with the memory allocation I started to look
at the driver again and found several areas worth improving.  I'm
currently re-writing this patchset and the result should be much
easier to follow.  I would appreciate if you can have a look when V2
is out.

Best regards,
Mathieu

[1]. http://www.spinics.net/lists/arm-kernel/msg494888.html

>
>>
>>   out:
>> +       drvdata->reading = true;
>
>
> Instead of spilling the spin_unlock_irq_restore() and return from too many
> different places in the function above, could we do :
>
> done:
>         drvdata->reading = true;
>
> out:
>>
>>         spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> -       return ret;
>> +       return 0;
>
>
>
>>   }
>>
>>   int tmc_read_unprepare_etf(struct tmc_drvdata *drvdata)
>>   {
>
>
> ...
>
>> +       }
>> +
>> +       /* The TMC isn't enabled, so there is no need to enable it */
>> +       if (!drvdata->enable) {
>> +               /*
>> +                * The ETB/ETF is not tracing and the buffer was just
>> read.
>> +                * As such prepare to free the trace buffer.
>> +                */
>> +               buf = drvdata->buf;
>> +
>> +               /*
>> +                * drvdata::buf is switched on in tmc_read_prepare_etf()
>> so
>> +                * it is important to set it back to NULL.
>> +                */
>> +               drvdata->buf = NULL;
>>                 goto out;
>>         }
>
>
> This check should be brought down after the MODE check below for better
> error
> check.
>
>>
>> @@ -258,13 +307,25 @@ int tmc_read_unprepare_etf(struct tmc_drvdata
>> *drvdata)
>>         mode = readl_relaxed(drvdata->base + TMC_MODE);
>>         if (mode != TMC_MODE_CIRCULAR_BUFFER) {
>>                 ret = -EINVAL;
>> -               goto out;
>> +               goto err;
>>         }
>>
>> +       /*
>> +        * The trace run will continue with the same allocated trace
>> buffer.
>> +        * As such zero-out the buffer so that we don't end up with stale
>> +        * data.
>> +        */
>
>
> Could this drvdata->buf be NULL here ?
>
>> +       memset(drvdata->buf, 0, drvdata->size);
>>         tmc_etb_enable_hw(drvdata);
>> -       drvdata->reading = false;
>>
>>   out:
>> +       drvdata->reading = false;
>> +
>> +err:
>>         spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +
>> +       /* Free allocated memory outside of the spinlock */
>> +       kfree(buf);
>> +
>>         return ret;
>>   }
>> diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> b/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> index 67e7d5dd891f..c4962568276e 100644
>> --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
>> @@ -16,6 +16,8 @@
>>    */
>>
>>   #include <linux/coresight.h>
>> +#include <linux/dma-mapping.h>
>> +
>>   #include "coresight-priv.h"
>>   #include "coresight-tmc.h"
>>
>> @@ -82,19 +84,51 @@ static void tmc_etr_disable_hw(struct tmc_drvdata
>> *drvdata)
>>
>>   static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
>>   {
>> +       bool allocated = false;
>>         unsigned long flags;
>> +       void __iomem *vaddr;
>> +       dma_addr_t paddr;
>>         struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
>>
>> +       /*
>> +        * Contiguous  memory can't be allocated while a spinlock is held.
>> +        * As such allocate memory here and free it if a buffer has
>> already
>> +        * been allocated (from a previous session).
>> +        */
>> +       vaddr = dma_alloc_coherent(drvdata->dev, drvdata->size,
>> +                                  &paddr, GFP_KERNEL);
>> +       if (!vaddr)
>> +               return -ENOMEM;
>> +
>>         spin_lock_irqsave(&drvdata->spinlock, flags);
>>         if (drvdata->reading) {
>>                 spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +               dma_free_coherent(drvdata->dev, drvdata->size, vaddr,
>> paddr);
>>                 return -EBUSY;
>>         }
>
>
> Both here and above in the etf/etb case, could we do a preliminary check
> by taking the lock where we may not have to allocate the buffer and allocate
> it if necessary and retake the lock ? I know it could make the code a bit
> more
> complex, but could save us on unnecessary allocations.
>
> Also it would be good to consolidate the return points here as well for the
> error path.
>
>>
>> +       /*
>> +        * If drvdata::buf == NULL, use the memory allocated above.
>> +        * Otherwise a buffer still exists from a previous session, so
>> +        * simply use that.
>> +        */
>> +       if (!drvdata->buf) {
>> +               allocated = true;
>> +               drvdata->vaddr = vaddr;
>> +               drvdata->paddr = paddr;
>> +               drvdata->buf = drvdata->vaddr;
>> +       }
>> +
>> +       memset(drvdata->vaddr, 0, drvdata->size);
>> +
>>         tmc_etr_enable_hw(drvdata);
>>         drvdata->enable = true;
>>         spin_unlock_irqrestore(&drvdata->spinlock, flags);
>>
>> +       /* Free memory outside the spinlock if need be */
>> +       if (!allocated)
>> +               dma_free_coherent(drvdata->dev, drvdata->size, vaddr,
>> paddr);
>> +
>>         dev_info(drvdata->dev, "TMC-ETR enabled\n");
>>         return 0;
>>   }
>> @@ -129,48 +163,87 @@ const struct coresight_ops tmc_etr_cs_ops = {
>>
>>   int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
>>   {
>> -       int ret = 0;
>>         unsigned long flags;
>>
>>         spin_lock_irqsave(&drvdata->spinlock, flags);
>>
>> -       /* The TMC isn't enable, so there is no need to disable it */
>> -       if (!drvdata->enable)
>> +       /* The TMC isn't enabled, so there is no need to disable it */
>> +       if (!drvdata->enable) {
>> +               /*
>> +                * The ETR is disabled already.  If drvdata::buf is NULL
>> +                * trace data has been harvested.
>> +                */
>> +               if (!drvdata->buf) {
>> +                       spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +                       return -EINVAL;
>> +               }
>> +
>>                 goto out;
>> +       }
>>
>>         if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
>> -               ret = -EINVAL;
>> -               goto out;
>> +               spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> +               return -EINVAL;
>>         }
>>
>>         tmc_etr_disable_hw(drvdata);
>> -       drvdata->reading = true;
>> +       tmc_etr_dump_hw(drvdata);
>
>
> Same as for etf/etb, shouldn't the above dump_hw be part of the previous
> patch ?
>
>>
>>   out:
>> +       drvdata->reading = true;
>>         spin_unlock_irqrestore(&drvdata->spinlock, flags);
>> -       return ret;
>> +       return 0;
>>   }
>>
>>   int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
>>   {
>>         int ret = 0;
>>         unsigned long flags;
>> +       void __iomem *vaddr = NULL;
>> +       dma_addr_t paddr;
>>
>>         spin_lock_irqsave(&drvdata->spinlock, flags);
>>
>> -       /* The TMC isn't enable, so there is no need to enable it */
>> -       if (!drvdata->enable)
>> -               goto out;
>> -
>>         if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) {
>>                 ret = -EINVAL;
>> +               goto err;
>> +       }
>> +
>> +       /* The TMC isn't enabled, so there is no need to enable it */
>> +       if (!drvdata->enable) {
>> +               /*
>> +                * The ETR is not tracing and trace data was just read. As
>> +                * such prepare to free the trace buffer.
>> +                */
>> +               vaddr = drvdata->vaddr;
>> +               paddr = drvdata->paddr;
>> +
>> +               /*
>> +                * drvdata::buf is switched on in tmc_read_prepare_etr()
>> and
>> +                * tmc_enable_etr_sink so it is important to set it back
>> to
>> +                * NULL.
>> +                */
>> +               drvdata->buf = NULL;
>>                 goto out;
>>         }
>>
>> +       /*
>> +        * The trace run will continue with the same allocated trace
>> buffer.
>> +        * As such zero-out the buffer so that we don't end up with stale
>> +        * data.
>> +        */
>> +       memset(drvdata->buf, 0, drvdata->size);
>
>
> Are we sure drvdata->buf is not NULL here ?
>
>
> Suzuki

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

end of thread, other threads:[~2016-04-08 15:23 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-03-22 20:23 [PATCH 00/14] coresight: tmc: make driver usable by Perf Mathieu Poirier
2016-03-22 20:23 ` Mathieu Poirier
2016-03-22 20:23 ` [PATCH 01/14] coresight: tmc: modifying naming convention Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier
2016-03-23 10:38   ` Suzuki K. Poulose
2016-03-22 20:23 ` [PATCH 02/14] coresight: tmc: waiting for TMCReady bit before programming Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier
2016-03-22 20:23 ` [PATCH 03/14] coresight: tmc: re-implementing tmc_read_prepare/unprepare() functions Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier
2016-03-23 10:37   ` Suzuki K. Poulose
2016-03-24 16:38     ` Mathieu Poirier
2016-03-24 16:38       ` Mathieu Poirier
2016-03-24 19:15     ` Mathieu Poirier
2016-03-24 19:15       ` Mathieu Poirier
2016-03-22 20:23 ` [PATCH 04/14] coresight: tmc: introducing new header file Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier
2016-03-22 20:23 ` [PATCH 05/14] coresight: tmc: splitting driver in ETB/ETF and ETR components Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier
2016-03-22 20:23 ` [PATCH 06/14] coresight: tmc: making prepare/unprepare functions generic Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier
2016-04-07 15:43   ` Suzuki K Poulose
2016-04-07 15:43     ` Suzuki K Poulose
2016-03-22 20:23 ` [PATCH 07/14] coresight: tmc: making disable function reusable Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier
2016-03-22 20:23 ` [PATCH 08/14] coresight: tmc: allocating memory when needed Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier
2016-04-07 16:50   ` Suzuki K Poulose
2016-04-07 16:50     ` Suzuki K Poulose
2016-04-08 15:23     ` Mathieu Poirier
2016-04-08 15:23       ` Mathieu Poirier
2016-03-22 20:23 ` [PATCH 09/14] coresight: tmc: adding mode of operation for link/sinks Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier
2016-04-07 17:19   ` Suzuki K Poulose
2016-04-07 17:19     ` Suzuki K Poulose
2016-03-22 20:23 ` [PATCH 10/14] coresight: tmc: make sysFS and Perf mode mutually exclusive Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier
2016-03-22 20:23 ` [PATCH 11/14] coresight: tmc: keep track of memory width Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier
2016-03-22 20:23 ` [PATCH 12/14] coresight: tmc: implementing TMC-ETF AUX space API Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier
2016-03-22 20:23 ` [PATCH 13/14] coresight: tmc: implementing TMC-ETR " Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier
2016-03-22 20:23 ` [PATCH 14/14] coresight: configuring ETF in FIFO mode when acting as link Mathieu Poirier
2016-03-22 20:23   ` Mathieu Poirier

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.