[PATCH 1/1] coresight: tmc-etr: Sync the trace buffer for the device
From: NoNine
Date: Tue Sep 15 2026 - 10:46:02 EST
From: Min Chen <min.chen@xxxxxxxxxxxx>
The flat ETR buffer comes from dma_alloc_noncoherent(), which zeroes it
with CPU stores. The DMA API requires the caller to sync the buffer for
the device before the device writes into it, but the TMC driver only
ever syncs for the CPU afterwards. On a non-coherent sink the zero fill
is therefore still dirty in cache when the ETR starts writing, and its
write-back lands on top of the trace data.
Add a sync_for_device() buffer operation and call it from
__tmc_etr_enable_hw() just before the TMC is enabled. Only the flat
buffer implements it. The ETR_SG and CATU data pages are synced by
dma_map_page() when they are allocated; their remaining corner case, a
barrier packet followed by a live-drain re-arm, is left for a separate
change.
Tested on an AD1000 EVB: five first windows on freshly allocated
buffers, including the first capture of a boot, all without the
previous all-zero-formatter-frame runs.
Signed-off-by: Min Chen <min.chen@xxxxxxxxxxxx>
---
.../hwtracing/coresight/coresight-tmc-etr.c | 29 +++++++++++++++++++
drivers/hwtracing/coresight/coresight-tmc.h | 1 +
2 files changed, 30 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 76a8cb2..bf1d6c6 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -689,10 +689,29 @@ static ssize_t tmc_etr_get_data_flat_buf(struct etr_buf *etr_buf,
return len;
}
+/*
+ * tmc_etr_sync_flat_buf_for_device: Drop any CPU cache lines over the trace
+ * buffer before the ETR is allowed to write into it. The buffer is allocated
+ * with dma_alloc_noncoherent(), which zeroes it with CPU stores, and the DMA
+ * API requires a sync for the device before the device writes into the
+ * memory. Without it a non-coherent sink writes into memory while the zero
+ * fill is still dirty in cache, and the write-back lands on top of the trace
+ * data.
+ */
+static void tmc_etr_sync_flat_buf_for_device(struct etr_buf *etr_buf)
+{
+ struct etr_flat_buf *flat_buf = etr_buf->private;
+ struct device *real_dev = flat_buf->dev->parent;
+
+ dma_sync_single_for_device(real_dev, flat_buf->daddr, etr_buf->size,
+ DMA_FROM_DEVICE);
+}
+
static const struct etr_buf_operations etr_flat_buf_ops = {
.alloc = tmc_etr_alloc_flat_buf,
.free = tmc_etr_free_flat_buf,
.sync = tmc_etr_sync_flat_buf,
+ .sync_for_device = tmc_etr_sync_flat_buf_for_device,
.get_data = tmc_etr_get_data_flat_buf,
};
@@ -1113,6 +1132,16 @@ static int __tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
writel_relaxed(ffcr, drvdata->base + TMC_FFCR);
writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
+
+ /*
+ * Hand the buffer over in a state the device can write into: drop
+ * any dirty CPU cache lines first, or they get written back over
+ * the trace data the ETR produces. Only the flat buffer needs
+ * this; the ETR_SG and CATU data pages are synced by
+ * dma_map_page() when they are allocated.
+ */
+ if (etr_buf->ops->sync_for_device)
+ etr_buf->ops->sync_for_device(etr_buf);
tmc_enable_hw(drvdata);
CS_LOCK(drvdata->base);
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 6541a27..3dd17da 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -277,6 +277,7 @@ struct etr_buf_operations {
int (*alloc)(struct tmc_drvdata *drvdata, struct etr_buf *etr_buf,
int node, void **pages);
void (*sync)(struct etr_buf *etr_buf, u64 rrp, u64 rwp);
+ void (*sync_for_device)(struct etr_buf *etr_buf);
ssize_t (*get_data)(struct etr_buf *etr_buf, u64 offset, size_t len,
char **bufpp);
void (*free)(struct etr_buf *etr_buf);
--
2.34.1