[PATCH v2 19/19] dmaengine: dw-edma: Add trace support

From: Koichiro Den

Date: Thu Jul 23 2026 - 04:50:03 EST


From: Frank Li <Frank.Li@xxxxxxx>

The circular LL ring has separate producer and consumer positions. Add
tracepoints for LL publication, descriptor append and completion,
IRQ-paired progress, and engine recovery. The common events cover eDMA
and HDMA.

The recovery event records configured LL channels before a direction
reset. When disabled, static keys leave only a branch at each trace site.

Signed-off-by: Frank Li <Frank.Li@xxxxxxx>
Co-developed-by: Koichiro Den <den@xxxxxxxxxxxxx>
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v2:
- Drop the obsolete tx_status event and ll_end fields.
- Rename the start event for append/refill and add stopped/request state
to the IRQ event.
- Add the engine recovery event.
- Match fields to their source types and simplify the Kbuild wiring.

drivers/dma/dw-edma/Makefile | 2 +
drivers/dma/dw-edma/dw-edma-core.c | 54 ++++++++
drivers/dma/dw-edma/dw-edma-core.h | 2 +
drivers/dma/dw-edma/dw-edma-trace.c | 4 +
drivers/dma/dw-edma/dw-edma-trace.h | 187 ++++++++++++++++++++++++++++
5 files changed, 249 insertions(+)
create mode 100644 drivers/dma/dw-edma/dw-edma-trace.c
create mode 100644 drivers/dma/dw-edma/dw-edma-trace.h

diff --git a/drivers/dma/dw-edma/Makefile b/drivers/dma/dw-edma/Makefile
index 83ab58f87760..5f395fa1ede4 100644
--- a/drivers/dma/dw-edma/Makefile
+++ b/drivers/dma/dw-edma/Makefile
@@ -1,8 +1,10 @@
# SPDX-License-Identifier: GPL-2.0

+CFLAGS_dw-edma-trace.o := -I$(src)
obj-$(CONFIG_DW_EDMA) += dw-edma.o
dw-edma-$(CONFIG_DEBUG_FS) := dw-edma-v0-debugfs.o \
dw-hdma-v0-debugfs.o
+dw-edma-$(CONFIG_TRACING) += dw-edma-trace.o
dw-edma-objs := dw-edma-core.o \
dw-edma-v0-core.o \
dw-hdma-v0-core.o $(dw-edma-y)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 1ccd3783181e..4b30fa5383d0 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -293,6 +293,12 @@ static void dw_edma_core_ll_start(struct dw_edma_desc *desc)
chan->ll_head, chan->cb,
dw_edma_core_enable_ll_irq(desc, i, free));

+ trace_edma_fill_ll(chan, chan->ll_head,
+ desc->vd.tx.cookie,
+ desc->burst[i].sar,
+ desc->burst[i].dar, desc->burst[i].sz,
+ chan->cb);
+
chan->ll_head++;

if (chan->ll_head == chan->ll_max) {
@@ -350,6 +356,7 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)
if (desc->start_burst == desc->nburst)
continue;

+ trace_edma_append_desc(desc);
dw_edma_core_start(desc);
ret = 1;
}
@@ -421,6 +428,7 @@ static void dw_edma_ll_clean_pending(struct dw_edma_chan *chan, u32 old_done)
/* Hardware has consumed this descriptor's LL entries. */
dw_hdma_set_callback_result(vd, DMA_TRANS_NOERROR);
list_del(&vd->node);
+ trace_edma_complete_desc(desc);
vchan_cookie_complete(vd);
}
}
@@ -647,6 +655,45 @@ dw_edma_engine_recovery_sync_irqs(struct dw_edma_engine_recovery *rec)
}
}

+static void
+dw_edma_trace_engine_recovery(struct dw_edma_engine_recovery *rec)
+{
+ struct dw_edma *dw = rec->dw;
+ u16 off = rec->dir == EDMA_DIR_WRITE ? 0 : dw->wr_ch_cnt;
+ u16 cnt = rec->dir == EDMA_DIR_WRITE ? dw->wr_ch_cnt : dw->rd_ch_cnt;
+ struct dw_edma_chan *chan;
+ u16 i;
+
+ if (!trace_edma_engine_recovery_enabled())
+ return;
+
+ for (i = 0; i < cnt; i++) {
+ enum dw_edma_request request;
+ enum dw_edma_status status;
+ bool configured_ll;
+ unsigned int pending;
+ u32 ll_head, ll_done;
+
+ chan = &dw->chan[off + i];
+ scoped_guard(spinlock_irqsave, &chan->vc.lock) {
+ configured_ll = chan->configured && !chan->non_ll;
+ if (configured_ll) {
+ request = chan->request;
+ status = chan->status;
+ ll_head = chan->ll_head;
+ ll_done = chan->ll_done;
+ pending = dw_edma_ll_pending(chan);
+ }
+ }
+ if (!configured_ll)
+ continue;
+
+ trace_edma_engine_recovery(chan, dw_edma_core_ch_status(chan),
+ request, status, ll_head, ll_done,
+ pending);
+ }
+}
+
static void dw_edma_engine_recovery_work(struct work_struct *work)
{
struct dw_edma_engine_recovery *rec =
@@ -747,6 +794,8 @@ static void dw_edma_engine_recovery_work(struct work_struct *work)
if (!dw_edma_engine_recovery_needed(rec))
goto out_release;

+ dw_edma_trace_engine_recovery(rec);
+
if (!dw->core->engine_reset(dw, rec->dir)) {
/*
* Keep channels gated and do not re-enable the engine unless
@@ -1322,6 +1371,9 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
bool active;

spin_lock_irqsave(&chan->vc.lock, flags);
+ if (!chan->non_ll)
+ trace_edma_irq(chan, chan->ll_irq_idx, false,
+ chan->ll_irq_stopped);
if (chan->status == EDMA_ST_PAUSE)
goto out;

@@ -1394,6 +1446,8 @@ static void dw_edma_progress_interrupt(struct dw_edma_chan *chan)

guard(spinlock_irqsave)(&chan->vc.lock);

+ trace_edma_irq(chan, chan->ll_irq_idx, true,
+ chan->ll_irq_stopped);
if (chan->request == EDMA_REQ_NONE && chan->status != EDMA_ST_PAUSE) {
if (dw_edma_ll_consume_progress(chan))
dw_edma_start_transfer(chan);
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index f67232110f3c..c174fa0d8447 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -179,6 +179,8 @@ struct dw_edma {
const struct dw_edma_core_ops *core;
};

+#include "dw-edma-trace.h"
+
typedef void (*dw_edma_handler_t)(struct dw_edma_chan *, bool);

struct dw_edma_core_ops {
diff --git a/drivers/dma/dw-edma/dw-edma-trace.c b/drivers/dma/dw-edma/dw-edma-trace.c
new file mode 100644
index 000000000000..2620ad61a943
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-trace.c
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define CREATE_TRACE_POINTS
+#include "dw-edma-core.h"
diff --git a/drivers/dma/dw-edma/dw-edma-trace.h b/drivers/dma/dw-edma/dw-edma-trace.h
new file mode 100644
index 000000000000..9d6e8fce6032
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-trace.h
@@ -0,0 +1,187 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2023 NXP.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM dw_edma
+
+#if !defined(__LINUX_DW_EDMA_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#define __LINUX_DW_EDMA_TRACE
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(edma_desc_info,
+ TP_PROTO(struct dw_edma_desc *desc),
+ TP_ARGS(desc),
+ TP_STRUCT__entry(
+ __field(size_t, nburst)
+ __field(size_t, start_burst)
+ __field(dma_cookie_t, cookie)
+ __field(u32, id)
+ __field(u8, dir)
+ ),
+ TP_fast_assign(
+ __entry->nburst = desc->nburst;
+ __entry->start_burst = desc->start_burst;
+ __entry->id = desc->chan->id;
+ __entry->dir = desc->chan->dir;
+ __entry->cookie = desc->vd.tx.cookie;
+ ),
+ TP_printk("chan %u%c desc %d nburst %zu start_burst %zu",
+ __entry->id,
+ __entry->dir ? 'R' : 'W',
+ __entry->cookie,
+ __entry->nburst,
+ __entry->start_burst)
+);
+
+DEFINE_EVENT(edma_desc_info, edma_append_desc,
+ TP_PROTO(struct dw_edma_desc *desc),
+ TP_ARGS(desc)
+);
+
+DEFINE_EVENT(edma_desc_info, edma_complete_desc,
+ TP_PROTO(struct dw_edma_desc *desc),
+ TP_ARGS(desc)
+);
+
+DECLARE_EVENT_CLASS(edma_ll_info,
+ TP_PROTO(struct dw_edma_chan *chan, int idx, bool progress,
+ bool stopped),
+ TP_ARGS(chan, idx, progress, stopped),
+ TP_STRUCT__entry(
+ __field(u32, head)
+ __field(u32, done)
+ __field(u32, total)
+ __field(int, index)
+ __field(dma_cookie_t, completed_cookie)
+ __field(dma_cookie_t, cookie)
+ __field(u32, id)
+ __field(u8, dir)
+ __field(bool, progress)
+ __field(bool, stopped)
+ __field(bool, requested)
+ ),
+ TP_fast_assign(
+ __entry->head = chan->ll_head;
+ __entry->done = chan->ll_done;
+ __entry->total = chan->ll_max;
+ __entry->index = idx;
+ __entry->completed_cookie = chan->vc.chan.completed_cookie;
+ __entry->cookie = chan->vc.chan.cookie;
+ __entry->id = chan->id;
+ __entry->dir = chan->dir;
+ __entry->progress = progress;
+ __entry->stopped = stopped;
+ __entry->requested = chan->ll_irq_requested;
+ ),
+ TP_printk("chan %u%c event %s stopped %u requested %u head %u done %u total %u index %d complete cookie %d cookie %d",
+ __entry->id,
+ __entry->dir ? 'R' : 'W',
+ __entry->progress ? "progress" : "done",
+ __entry->stopped,
+ __entry->requested,
+ __entry->head,
+ __entry->done,
+ __entry->total,
+ __entry->index,
+ __entry->completed_cookie,
+ __entry->cookie)
+);
+
+DEFINE_EVENT(edma_ll_info, edma_irq,
+ TP_PROTO(struct dw_edma_chan *chan, int idx, bool progress,
+ bool stopped),
+ TP_ARGS(chan, idx, progress, stopped)
+);
+
+TRACE_EVENT(edma_engine_recovery,
+ TP_PROTO(struct dw_edma_chan *chan, enum dma_status hw_status,
+ enum dw_edma_request request, enum dw_edma_status status,
+ u32 ll_head, u32 ll_done, u32 pending),
+ TP_ARGS(chan, hw_status, request, status, ll_head, ll_done, pending),
+ TP_STRUCT__entry(
+ __field(u32, id)
+ __field(u32, head)
+ __field(u32, done)
+ __field(u32, pending)
+ __field(int, hw_status)
+ __field(int, request)
+ __field(int, status)
+ __field(u8, dir)
+ ),
+ TP_fast_assign(
+ __entry->id = chan->id;
+ __entry->dir = chan->dir;
+ __entry->hw_status = hw_status;
+ __entry->request = request;
+ __entry->status = status;
+ __entry->head = ll_head;
+ __entry->done = ll_done;
+ __entry->pending = pending;
+ ),
+ TP_printk("chan %u%c hw %d request %d status %d head %u done %u pending %u",
+ __entry->id,
+ __entry->dir ? 'R' : 'W',
+ __entry->hw_status,
+ __entry->request,
+ __entry->status,
+ __entry->head,
+ __entry->done,
+ __entry->pending)
+);
+
+DECLARE_EVENT_CLASS(edma_log_ll,
+ TP_PROTO(struct dw_edma_chan *chan, u32 idx, dma_cookie_t cookie, u64 src,
+ u64 dest, u32 sz, bool flag),
+ TP_ARGS(chan, idx, cookie, src, dest, sz, flag),
+ TP_STRUCT__entry(
+ __field(u32, idx)
+ __field(u64, src)
+ __field(u64, dest)
+ __field(u32, sz)
+ __field(u32, id)
+ __field(dma_cookie_t, cookie)
+ __field(bool, flag)
+ __field(u8, dir)
+ ),
+ TP_fast_assign(
+ __entry->idx = idx;
+ __entry->src = src;
+ __entry->dest = dest;
+ __entry->sz = sz;
+ __entry->id = chan->id;
+ __entry->dir = chan->dir;
+ __entry->cookie = cookie;
+ __entry->flag = flag;
+ ),
+ TP_printk("chan %u%c %d [%u] %c src: %08llx dest: %08llx sz: %04x",
+ __entry->id,
+ __entry->dir ? 'R' : 'W',
+ __entry->cookie,
+ __entry->idx,
+ __entry->flag ? 'C' : 'c',
+ __entry->src,
+ __entry->dest,
+ __entry->sz)
+);
+
+DEFINE_EVENT(edma_log_ll, edma_fill_ll,
+ TP_PROTO(struct dw_edma_chan *chan, u32 idx, dma_cookie_t cookie, u64 src,
+ u64 dest, u32 sz, bool flag),
+ TP_ARGS(chan, idx, cookie, src, dest, sz, flag)
+);
+
+#endif
+
+/* This part must be outside the header guard. */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE dw-edma-trace
+
+#include <trace/define_trace.h>
--
2.51.0