|
@@ -16,6 +16,7 @@
|
|
|
*/
|
|
|
|
|
|
#include <linux/coresight.h>
|
|
|
+#include <linux/dma-mapping.h>
|
|
|
#include "coresight-priv.h"
|
|
|
#include "coresight-tmc.h"
|
|
|
|
|
@@ -83,21 +84,71 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
|
|
|
|
|
|
static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
|
|
|
{
|
|
|
+ int ret = 0;
|
|
|
+ bool used = false;
|
|
|
unsigned long flags;
|
|
|
+ void __iomem *vaddr = NULL;
|
|
|
+ dma_addr_t paddr;
|
|
|
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
|
|
|
|
|
+ /* This shouldn't be happening */
|
|
|
+ if (WARN_ON(mode != CS_MODE_SYSFS))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we don't have a buffer release the lock and allocate memory.
|
|
|
+ * Otherwise keep the lock and move along.
|
|
|
+ */
|
|
|
spin_lock_irqsave(&drvdata->spinlock, flags);
|
|
|
- if (drvdata->reading) {
|
|
|
+ if (!drvdata->vaddr) {
|
|
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
|
|
- return -EBUSY;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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;
|
|
|
+
|
|
|
+ /* Let's try again */
|
|
|
+ spin_lock_irqsave(&drvdata->spinlock, flags);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (drvdata->reading) {
|
|
|
+ ret = -EBUSY;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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 == NULL) {
|
|
|
+ used = 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;
|
|
|
+out:
|
|
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
|
|
|
|
|
- dev_info(drvdata->dev, "TMC-ETR enabled\n");
|
|
|
- return 0;
|
|
|
+ /* Free memory outside the spinlock if need be */
|
|
|
+ if (!used && vaddr)
|
|
|
+ dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);
|
|
|
+
|
|
|
+ if (!ret)
|
|
|
+ dev_info(drvdata->dev, "TMC-ETR enabled\n");
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static void tmc_disable_etr_sink(struct coresight_device *csdev)
|
|
@@ -129,6 +180,7 @@ const struct coresight_ops tmc_etr_cs_ops = {
|
|
|
|
|
|
int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
|
|
|
{
|
|
|
+ int ret = 0;
|
|
|
unsigned long flags;
|
|
|
|
|
|
/* config types are set a boot time and never change */
|
|
@@ -137,11 +189,18 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
|
|
|
|
|
|
spin_lock_irqsave(&drvdata->spinlock, flags);
|
|
|
|
|
|
+ /* If drvdata::buf is NULL the trace data has been read already */
|
|
|
+ if (drvdata->buf == NULL) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
/* Disable the TMC if need be */
|
|
|
if (drvdata->enable)
|
|
|
tmc_etr_disable_hw(drvdata);
|
|
|
|
|
|
drvdata->reading = true;
|
|
|
+out:
|
|
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
|
|
|
|
|
return 0;
|
|
@@ -150,6 +209,8 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
|
|
|
int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
+ dma_addr_t paddr;
|
|
|
+ void __iomem *vaddr = NULL;
|
|
|
|
|
|
/* config types are set a boot time and never change */
|
|
|
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
|
|
@@ -158,11 +219,33 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
|
|
|
spin_lock_irqsave(&drvdata->spinlock, flags);
|
|
|
|
|
|
/* RE-enable the TMC if need be */
|
|
|
- if (drvdata->enable)
|
|
|
+ if (drvdata->enable) {
|
|
|
+ /*
|
|
|
+ * 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.
|
|
|
+ *
|
|
|
+ * Since the tracer is still enabled drvdata::buf
|
|
|
+ * can't be NULL.
|
|
|
+ */
|
|
|
+ memset(drvdata->buf, 0, drvdata->size);
|
|
|
tmc_etr_enable_hw(drvdata);
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * The ETR is not tracing and the buffer was just read.
|
|
|
+ * As such prepare to free the trace buffer.
|
|
|
+ */
|
|
|
+ vaddr = drvdata->vaddr;
|
|
|
+ paddr = drvdata->paddr;
|
|
|
+ drvdata->buf = NULL;
|
|
|
+ }
|
|
|
|
|
|
drvdata->reading = false;
|
|
|
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 0;
|
|
|
}
|