diff options
Diffstat (limited to 'drivers/hwtracing/coresight/coresight-tmc-etr.c')
-rw-r--r-- | drivers/hwtracing/coresight/coresight-tmc-etr.c | 93 |
1 files changed, 88 insertions, 5 deletions
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 3483d139a4ac..ac37bf904fb7 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -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; } |