DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Gagandeep Singh <g.singh@nxp.com>
To: dev@dpdk.org, Prashant Gupta <prashant.gupta_3@nxp.com>
Cc: hemant.agrawal@nxp.com
Subject: [PATCH 3/4] dma/imx_edma5: add data path
Date: Thu,  6 Aug 2026 14:12:44 +0530	[thread overview]
Message-ID: <20260806084245.561305-4-g.singh@nxp.com> (raw)
In-Reply-To: <20260806084245.561305-1-g.singh@nxp.com>

Add the memory-to-memory data path for the eDMA5 dmadev.

The eDMA5 exposes a single transfer control descriptor and a single
completion flag per hardware channel, so jobs are serialised in software.
Each enqueued job is played out synchronously as one or more single-block
transfers: the per-channel TCD is programmed, the transfer is
software-started and the driver busy-waits for the DONE flag with a
wall-clock bounded timeout. Scatter-gather copies walk the source and
destination segment lists as two cursors, emitting one single-block
sub-transfer per iteration that fits both current segments.

As the eDMA5 is a non-coherent bus master, source and destination buffers
are cleaned from the CPU cache before a transfer and the destination is
invalidated after completion so the application observes the DMA result.

This adds copy, copy_sg, submit, completed, completed_status and
burst_capacity, wired through the device fast-path object.

Signed-off-by: Gagandeep Singh <g.singh@nxp.com>
Signed-off-by: Prashant Gupta <prashant.gupta_3@nxp.com>
---
 drivers/dma/imx_edma5/imx_edma5_dmadev.c | 517 +++++++++++++++++++++++
 1 file changed, 517 insertions(+)

diff --git a/drivers/dma/imx_edma5/imx_edma5_dmadev.c b/drivers/dma/imx_edma5/imx_edma5_dmadev.c
index 0b2671e926..0b6a56112e 100644
--- a/drivers/dma/imx_edma5/imx_edma5_dmadev.c
+++ b/drivers/dma/imx_edma5/imx_edma5_dmadev.c
@@ -338,6 +338,516 @@ imx_edma5_close(struct rte_dma_dev *dev)
 	return 0;
 }
 
+/*
+ * Encode the largest natural transfer size (SSIZE/DSIZE) usable for a given
+ * source, destination and length. The address must be aligned to the transfer
+ * size and the byte count must be a multiple of it.
+ */
+static uint16_t
+imx_edma5_calc_attr(uint64_t src, uint64_t dst, uint32_t len)
+{
+	uint32_t sz = IMX_EDMA5_TCD_SIZE_1B;
+
+	if (((src | dst | len) & 0x1F) == 0)
+		sz = IMX_EDMA5_TCD_SIZE_32B;
+	else if (((src | dst | len) & 0xF) == 0)
+		sz = IMX_EDMA5_TCD_SIZE_16B;
+	else if (((src | dst | len) & 0x7) == 0)
+		sz = IMX_EDMA5_TCD_SIZE_8B;
+	else if (((src | dst | len) & 0x3) == 0)
+		sz = IMX_EDMA5_TCD_SIZE_4B;
+	else if (((src | dst | len) & 0x1) == 0)
+		sz = IMX_EDMA5_TCD_SIZE_2B;
+
+	return IMX_EDMA5_TCD_ATTR_SSIZE(sz) | IMX_EDMA5_TCD_ATTR_DSIZE(sz);
+}
+
+/*
+ * Program the channel TCD for a single-block copy: one minor loop of "len"
+ * bytes with a major count of 1. Completion is polled via CH_CSR.DONE.
+ */
+static inline void
+imx_edma5_program_copy(struct imx_edma5_vchan *vc, uint64_t src, uint64_t dst,
+		       uint32_t len)
+{
+	uint8_t *tcd = vc->tcd_regs;
+	uint16_t attr = imx_edma5_calc_attr(src, dst, len);
+
+	imx_edma5_write64(tcd, IMX_EDMA5_TCD_SADDR, src);
+	imx_edma5_write64(tcd, IMX_EDMA5_TCD_DADDR, dst);
+	imx_edma5_write16(tcd, IMX_EDMA5_TCD_ATTR, attr);
+	imx_edma5_write16(tcd, IMX_EDMA5_TCD_SOFF,
+			  (uint16_t)(1u << IMX_EDMA5_TCD_ATTR_GET_SSIZE(attr)));
+	imx_edma5_write16(tcd, IMX_EDMA5_TCD_DOFF,
+			  (uint16_t)(1u << IMX_EDMA5_TCD_ATTR_GET_DSIZE(attr)));
+	imx_edma5_write32(tcd, IMX_EDMA5_TCD_NBYTES, len);
+	imx_edma5_write64(tcd, IMX_EDMA5_TCD_SLAST, 0);
+	imx_edma5_write64(tcd, IMX_EDMA5_TCD_DLAST_SGA, 0);
+	imx_edma5_write16(tcd, IMX_EDMA5_TCD_CITER, 1);
+	imx_edma5_write16(tcd, IMX_EDMA5_TCD_BITER, 1);
+	/* Clear CSR so no scatter-gather link is followed. */
+	imx_edma5_write16(tcd, IMX_EDMA5_TCD_CSR, 0);
+}
+
+/* Fill an in-memory hardware TCD64 descriptor for one copy segment. */
+static inline void
+imx_edma5_fill_tcd(struct imx_edma5_hw_tcd64 *t, uint64_t src, uint64_t dst,
+		   uint32_t len)
+{
+	uint16_t attr = imx_edma5_calc_attr(src, dst, len);
+	uint16_t soff = (uint16_t)(1u << IMX_EDMA5_TCD_ATTR_GET_SSIZE(attr));
+	uint16_t doff = (uint16_t)(1u << IMX_EDMA5_TCD_ATTR_GET_DSIZE(attr));
+
+	t->saddr = rte_cpu_to_le_64(src);
+	t->soff = rte_cpu_to_le_16(soff);
+	t->attr = rte_cpu_to_le_16(attr);
+	t->nbytes = rte_cpu_to_le_32(len);
+	t->slast = 0;
+	t->daddr = rte_cpu_to_le_64(dst);
+	t->dlast_sga = 0;
+	t->doff = rte_cpu_to_le_16(doff);
+	t->citer = rte_cpu_to_le_16(1);
+	t->csr = 0;
+	t->biter = rte_cpu_to_le_16(1);
+}
+
+static inline void
+imx_edma5_hw_start(struct imx_edma5_vchan *vc)
+{
+	uint16_t csr = imx_edma5_read16(vc->tcd_regs, IMX_EDMA5_TCD_CSR);
+
+	csr |= IMX_EDMA5_TCD_CSR_START;
+	imx_edma5_write16(vc->tcd_regs, IMX_EDMA5_TCD_CSR, csr);
+}
+
+/*
+ * Invalidate the CPU cache lines covering a completed job's destination(s) so
+ * the application reads the DMA result rather than stale cache. The lines were
+ * cleaned at enqueue time, so this clean+invalidate behaves as a pure
+ * invalidate.
+ */
+static inline void
+imx_edma5_job_invalidate_dst(struct imx_edma5_job *job)
+{
+	if (job->nb_sg > 0) {
+		uint16_t s;
+
+		for (s = 0; s < job->nb_sg; s++) {
+			rte_iova_t da = rte_le_to_cpu_64(job->sg_tcd[s].daddr);
+			uint32_t len = rte_le_to_cpu_32(job->sg_tcd[s].nbytes);
+			void *va = rte_mem_iova2virt(da);
+
+			if (va != NULL)
+				imx_edma5_cache_inval(va, len);
+		}
+	} else if (job->dst_va != NULL) {
+		imx_edma5_cache_inval(job->dst_va, job->len);
+	}
+}
+
+/*
+ * Upper bound on how long to poll for a single-block transfer to complete.
+ * A wall-clock deadline is used rather than a raw spin count so the bound is
+ * independent of CPU speed and transfer size: even the largest single-block
+ * copy this driver issues completes in well under a millisecond at eDMA5 bus
+ * rates, so 1 ms is comfortably above the worst case while still bounding a
+ * wedged channel instead of hanging the CPU.
+ */
+#define IMX_EDMA5_WAIT_TIMEOUT_MS	1
+
+/*
+ * Wait for the single register TCD transfer to finish and clear its latched
+ * status. Returns true on success, false on a logged channel error (CH_ES.ERR)
+ * or timeout. The per-transfer completion flag CH_CSR.DONE and the CH_ES error
+ * bit are both write-1-to-clear.
+ */
+static inline bool
+imx_edma5_wait_done(struct imx_edma5_vchan *vc)
+{
+	uint64_t deadline = rte_get_timer_cycles() +
+		(rte_get_timer_hz() * IMX_EDMA5_WAIT_TIMEOUT_MS) / 1000;
+
+	do {
+		uint32_t ch_es = imx_edma5_read32(vc->ch_regs, IMX_EDMA5_CH_ES);
+		uint32_t ch_csr;
+
+		if (ch_es & IMX_EDMA5_CH_ES_ERR) {
+			imx_edma5_write32(vc->ch_regs, IMX_EDMA5_CH_ES,
+					  IMX_EDMA5_CH_ES_ERR);
+			/* Reset the errored channel before the next job reuses it. */
+			imx_edma5_reset_hw_chan(vc);
+			return false;
+		}
+
+		ch_csr = imx_edma5_read32(vc->ch_regs, IMX_EDMA5_CH_CSR);
+		if (ch_csr & IMX_EDMA5_CH_CSR_DONE) {
+			imx_edma5_write32(vc->ch_regs, IMX_EDMA5_CH_CSR,
+					  IMX_EDMA5_CH_CSR_DONE);
+			return true;
+		}
+	} while (rte_get_timer_cycles() < deadline);
+
+	IMX_EDMA5_LOG(ERR,
+		      "channel %u timed out waiting for DONE (CH_CSR=0x%08x "
+		      "CH_ES=0x%08x)",
+		      vc->hw_chan,
+		      imx_edma5_read32(vc->ch_regs, IMX_EDMA5_CH_CSR),
+		      imx_edma5_read32(vc->ch_regs, IMX_EDMA5_CH_ES));
+
+	/* Reset the possibly-still-active channel before the next job reuses it. */
+	imx_edma5_reset_hw_chan(vc);
+	return false;
+}
+
+/*
+ * Execute one job to completion on the channel's single register TCD.
+ *
+ * The eDMA5 exposes a single TCD and a single completion flag per channel, so
+ * jobs are serialised in software: a job is run synchronously here (program the
+ * TCD, software-start, busy-wait for DONE) and its completion recorded in
+ * job->done for the completion API to reap. Scatter-gather segments are played
+ * out one at a time as single-block transfers, since the eDMA5 does not
+ * auto-advance a hardware TCD chain for software-started mem-to-mem transfers.
+ * On success the destination cache lines are invalidated (non-coherent master).
+ */
+static inline void
+imx_edma5_run_job(struct imx_edma5_vchan *vc, struct imx_edma5_job *job)
+{
+	bool ok = true;
+
+	if (job->nb_sg > 0) {
+		uint16_t s;
+
+		for (s = 0; s < job->nb_sg; s++) {
+			uint64_t src = rte_le_to_cpu_64(job->sg_tcd[s].saddr);
+			uint64_t dst = rte_le_to_cpu_64(job->sg_tcd[s].daddr);
+			uint32_t len = rte_le_to_cpu_32(job->sg_tcd[s].nbytes);
+
+			imx_edma5_program_copy(vc, src, dst, len);
+			imx_edma5_hw_start(vc);
+			if (!imx_edma5_wait_done(vc)) {
+				ok = false;
+				break;
+			}
+		}
+	} else {
+		imx_edma5_program_copy(vc, job->src_iova, job->dst_iova,
+				       job->len);
+		imx_edma5_hw_start(vc);
+		ok = imx_edma5_wait_done(vc);
+	}
+
+	if (ok)
+		imx_edma5_job_invalidate_dst(job);
+
+	job->error = ok ? 0 : 1;
+	job->done = 1;
+	vc->submitted_count++;
+}
+
+static int
+imx_edma5_copy(void *dev_private, uint16_t vchan, rte_iova_t src,
+	       rte_iova_t dst, uint32_t length, uint64_t flags)
+{
+	struct imx_edma5_dev *ed = dev_private;
+	struct imx_edma5_vchan *vc = &ed->vchans[vchan];
+	struct imx_edma5_job *job;
+	uint16_t slot;
+
+	/* NBYTES = 0 is undefined on the eDMA5 and can wedge the channel. */
+	if (length == 0)
+		return -EINVAL;
+
+	/* Ring full? one slot is kept free to distinguish full from empty. */
+	if (vc->nb_enqueued >= (uint16_t)(vc->nb_desc - 1))
+		return -ENOSPC;
+
+	slot = vc->head;
+	job = &vc->jobs[slot];
+	job->ridx = vc->ridx;
+	job->submitted = 0;
+	job->done = 0;
+	job->error = 0;
+	job->nb_sg = 0;
+	job->len = length;
+	job->src_iova = src;
+	job->dst_iova = dst;
+	job->dst_va = rte_mem_iova2virt(dst);
+
+	/*
+	 * Non-cache-coherent master: clean the source so the device reads the
+	 * CPU's latest writes, and clean the destination so a prior dirty line
+	 * cannot be written back over the DMA result (the destination is
+	 * invalidated after completion).
+	 */
+	{
+		void *src_va = rte_mem_iova2virt(src);
+
+		if (src_va != NULL)
+			imx_edma5_cache_clean(src_va, length);
+		if (job->dst_va != NULL)
+			imx_edma5_cache_clean(job->dst_va, length);
+	}
+
+	/* Run now if SUBMIT is set, else later by imx_edma5_submit(). */
+	if (flags & RTE_DMA_OP_FLAG_SUBMIT) {
+		imx_edma5_run_job(vc, job);
+		job->submitted = 1;
+	}
+
+	vc->head = (vc->head + 1) & vc->desc_mask;
+	vc->nb_enqueued++;
+
+	return vc->ridx++;
+}
+
+static int
+imx_edma5_copy_sg(void *dev_private, uint16_t vchan,
+		  const struct rte_dma_sge *src, const struct rte_dma_sge *dst,
+		  uint16_t nb_src, uint16_t nb_dst, uint64_t flags)
+{
+	struct imx_edma5_dev *ed = dev_private;
+	struct imx_edma5_vchan *vc = &ed->vchans[vchan];
+	struct imx_edma5_hw_tcd64 *tcd;
+	struct imx_edma5_job *job;
+	uint16_t slot;
+	uint16_t s;
+	uint16_t si = 0, di = 0;	/* current source/destination seg index */
+	uint32_t s_off = 0, d_off = 0;	/* byte offset within current segment */
+	uint16_t nsg = 0;		/* sub-transfers produced so far */
+	uint64_t src_total = 0, dst_total = 0;
+
+	/*
+	 * The scatter-gather contract is a byte stream: the source and
+	 * destination lists may be segmented independently but their
+	 * concatenations are equal. The lists are walked as two cursors,
+	 * emitting one single-block sub-transfer per run that fits in both the
+	 * current source and destination segments. This yields at most
+	 * nb_src + nb_dst - 1 sub-transfers, which fit in IMX_EDMA5_SG_TCD_PER_JOB.
+	 */
+	if (nb_src == 0 || nb_dst == 0 ||
+	    nb_src > IMX_EDMA5_MAX_SGES || nb_dst > IMX_EDMA5_MAX_SGES) {
+		IMX_EDMA5_LOG(ERR, "Unsupported SG shape src=%u dst=%u",
+			      nb_src, nb_dst);
+		return -EINVAL;
+	}
+
+	/* Ring full? one slot is kept free to distinguish full from empty. */
+	if (vc->nb_enqueued >= (uint16_t)(vc->nb_desc - 1))
+		return -ENOSPC;
+
+	slot = vc->head;
+	job = &vc->jobs[slot];
+	job->ridx = vc->ridx;
+	job->submitted = 0;
+	job->done = 0;
+	job->error = 0;
+
+	/* This job's dedicated slice of the in-memory TCD pool. */
+	job->sg_tcd = &vc->sg_tcd_pool[(size_t)slot * IMX_EDMA5_SG_TCD_PER_JOB];
+	tcd = job->sg_tcd;
+
+	/*
+	 * Clean every source and destination segment up front (non-coherent
+	 * master): the device must read current source data, and dirty
+	 * destination lines must be flushed before the transfer.
+	 */
+	for (s = 0; s < nb_src; s++) {
+		void *va = rte_mem_iova2virt(src[s].addr);
+
+		src_total += src[s].length;
+		if (va != NULL)
+			imx_edma5_cache_clean(va, src[s].length);
+	}
+	for (s = 0; s < nb_dst; s++) {
+		void *va = rte_mem_iova2virt(dst[s].addr);
+
+		dst_total += dst[s].length;
+		if (va != NULL)
+			imx_edma5_cache_clean(va, dst[s].length);
+	}
+
+	/* copy_sg requires equal total bytes on both lists; reject misuse. */
+	if (src_total != dst_total) {
+		IMX_EDMA5_LOG(ERR,
+			      "SG byte count mismatch src=%" PRIu64
+			      " dst=%" PRIu64, src_total, dst_total);
+		return -EINVAL;
+	}
+
+	while (si < nb_src && di < nb_dst) {
+		uint32_t s_rem = src[si].length - s_off;
+		uint32_t d_rem = dst[di].length - d_off;
+		uint32_t len = RTE_MIN(s_rem, d_rem);
+
+		/* Skip zero-length segments without emitting a descriptor. */
+		if (len == 0) {
+			if (s_rem == 0) {
+				si++;
+				s_off = 0;
+			}
+			if (d_rem == 0) {
+				di++;
+				d_off = 0;
+			}
+			continue;
+		}
+
+		if (nsg >= IMX_EDMA5_SG_TCD_PER_JOB) {
+			IMX_EDMA5_LOG(ERR,
+				      "SG produced too many sub-transfers "
+				      "(src=%u dst=%u)", nb_src, nb_dst);
+			/* Defensive: clear nb_sg so the abandoned slot is not reused. */
+			job->nb_sg = 0;
+			return -EINVAL;
+		}
+
+		imx_edma5_fill_tcd(&tcd[nsg], src[si].addr + s_off,
+				   dst[di].addr + d_off, len);
+		nsg++;
+
+		s_off += len;
+		d_off += len;
+		if (s_off == src[si].length) {
+			si++;
+			s_off = 0;
+		}
+		if (d_off == dst[di].length) {
+			di++;
+			d_off = 0;
+		}
+	}
+
+	/* No sub-transfer (all segments zero-length): NBYTES = 0 wedges eDMA5. */
+	if (nsg == 0) {
+		IMX_EDMA5_LOG(ERR, "SG produced zero sub-transfers");
+		return -EINVAL;
+	}
+
+	job->nb_sg = nsg;
+
+	/* Run now if SUBMIT is set, else later by imx_edma5_submit(). */
+	if (flags & RTE_DMA_OP_FLAG_SUBMIT) {
+		imx_edma5_run_job(vc, job);
+		job->submitted = 1;
+	}
+
+	vc->head = (vc->head + 1) & vc->desc_mask;
+	vc->nb_enqueued++;
+
+	return vc->ridx++;
+}
+
+static int
+imx_edma5_submit(void *dev_private, uint16_t vchan)
+{
+	struct imx_edma5_dev *ed = dev_private;
+	struct imx_edma5_vchan *vc = &ed->vchans[vchan];
+	uint16_t idx = vc->tail;
+
+	/* Run every enqueued-but-not-yet-submitted job to completion (FIFO). */
+	while (idx != vc->head) {
+		struct imx_edma5_job *job = &vc->jobs[idx];
+
+		if (!job->submitted) {
+			imx_edma5_run_job(vc, job);
+			job->submitted = 1;
+		}
+		idx = (idx + 1) & vc->desc_mask;
+	}
+
+	return 0;
+}
+
+/*
+ * Reap completed jobs from the software ring in FIFO order. Jobs run
+ * synchronously, so a submitted job's result is already in job->done/error.
+ * An unsubmitted job stops the walk.
+ */
+static uint16_t
+imx_edma5_completed(void *dev_private, uint16_t vchan, const uint16_t nb_cpls,
+		    uint16_t *last_idx, bool *has_error)
+{
+	struct imx_edma5_dev *ed = dev_private;
+	struct imx_edma5_vchan *vc = &ed->vchans[vchan];
+	uint16_t count = 0;
+
+	*has_error = false;
+
+	while (count < nb_cpls && vc->tail != vc->head) {
+		struct imx_edma5_job *job = &vc->jobs[vc->tail];
+
+		if (!job->submitted || !job->done)
+			break;
+
+		/*
+		 * Stop before an errored job: it is left in the ring for
+		 * rte_dma_completed_status(), and last_idx stays at the last
+		 * successful transfer.
+		 */
+		if (job->error) {
+			*has_error = true;
+			break;
+		}
+
+		vc->last_idx = job->ridx;
+		vc->completed_count++;
+
+		vc->tail = (vc->tail + 1) & vc->desc_mask;
+		vc->nb_enqueued--;
+		count++;
+	}
+
+	*last_idx = vc->last_idx;
+
+	return count;
+}
+
+static uint16_t
+imx_edma5_completed_status(void *dev_private, uint16_t vchan,
+			   const uint16_t nb_cpls, uint16_t *last_idx,
+			   enum rte_dma_status_code *status)
+{
+	struct imx_edma5_dev *ed = dev_private;
+	struct imx_edma5_vchan *vc = &ed->vchans[vchan];
+	uint16_t count = 0;
+
+	while (count < nb_cpls && vc->tail != vc->head) {
+		struct imx_edma5_job *job = &vc->jobs[vc->tail];
+
+		if (!job->submitted || !job->done)
+			break;
+
+		if (job->error) {
+			status[count] = RTE_DMA_STATUS_BUS_ERROR;
+			vc->errors_count++;
+		} else {
+			status[count] = RTE_DMA_STATUS_SUCCESSFUL;
+		}
+
+		vc->last_idx = job->ridx;
+		vc->completed_count++;
+
+		vc->tail = (vc->tail + 1) & vc->desc_mask;
+		vc->nb_enqueued--;
+		count++;
+	}
+
+	*last_idx = vc->last_idx;
+
+	return count;
+}
+
+static uint16_t
+imx_edma5_burst_capacity(const void *dev_private, uint16_t vchan)
+{
+	const struct imx_edma5_dev *ed = dev_private;
+	const struct imx_edma5_vchan *vc = &ed->vchans[vchan];
+
+	/* One slot is reserved to distinguish full from empty. */
+	return vc->nb_desc - 1 - vc->nb_enqueued;
+}
+
 static const struct rte_dma_dev_ops imx_edma5_ops = {
 	.dev_info_get	= imx_edma5_info_get,
 	.dev_configure	= imx_edma5_configure,
@@ -383,6 +893,13 @@ imx_edma5_probe(struct rte_platform_device *pdev)
 
 	dev->device = &pdev->device;
 	dev->dev_ops = &imx_edma5_ops;
+	dev->fp_obj->dev_private = dev->data->dev_private;
+	dev->fp_obj->copy = imx_edma5_copy;
+	dev->fp_obj->copy_sg = imx_edma5_copy_sg;
+	dev->fp_obj->submit = imx_edma5_submit;
+	dev->fp_obj->completed = imx_edma5_completed;
+	dev->fp_obj->completed_status = imx_edma5_completed_status;
+	dev->fp_obj->burst_capacity = imx_edma5_burst_capacity;
 
 	ed = dev->data->dev_private;
 	ed->reg_base = res->mem.addr;
-- 
2.25.1


  parent reply	other threads:[~2026-08-06  8:43 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-06  8:42 [PATCH 0/4] dma/imx_edma5: introduce NXP i.MX95 eDMA5 driver Gagandeep Singh
2026-08-06  8:42 ` [PATCH 1/4] dma/imx_edma5: introduce eDMA5 dmadev skeleton Gagandeep Singh
2026-08-06  8:42 ` [PATCH 2/4] dma/imx_edma5: add device configuration Gagandeep Singh
2026-08-06  8:42 ` Gagandeep Singh [this message]
2026-08-06  8:42 ` [PATCH 4/4] dma/imx_edma5: add statistics and dump Gagandeep Singh
2026-08-06 16:21   ` Stephen Hemminger
2026-08-07  7:05     ` Gagandeep Singh
2026-08-06 17:11 ` [PATCH 0/4] dma/imx_edma5: introduce NXP i.MX95 eDMA5 driver Stephen Hemminger
2026-08-07  6:54   ` Gagandeep Singh
2026-08-07  6:48 ` [PATCH v2 0/5] " Gagandeep Singh
2026-08-07  6:48   ` [PATCH v2 1/5] bus/platform: match device by devicetree compatible string Gagandeep Singh
2026-08-07  6:48   ` [PATCH v2 2/5] dma/imx_edma5: introduce eDMA5 dmadev skeleton Gagandeep Singh
2026-08-07  6:48   ` [PATCH v2 3/5] dma/imx_edma5: add device configuration Gagandeep Singh
2026-08-07  6:48   ` [PATCH v2 4/5] dma/imx_edma5: add data path Gagandeep Singh
2026-08-07  6:48   ` [PATCH v2 5/5] dma/imx_edma5: add statistics and dump Gagandeep Singh
2026-08-10 15:37   ` [PATCH v2 0/5] dma/imx_edma5: introduce NXP i.MX95 eDMA5 driver Stephen Hemminger
2026-08-11 10:49   ` [PATCH v3 " Gagandeep Singh
2026-08-11 10:49     ` [PATCH v3 1/5] bus/platform: match device by devicetree compatible string Gagandeep Singh
2026-08-11 10:49     ` [PATCH v3 2/5] dma/imx_edma5: introduce eDMA5 dmadev skeleton Gagandeep Singh
2026-08-11 10:49     ` [PATCH v3 3/5] dma/imx_edma5: add device configuration Gagandeep Singh
2026-08-11 10:49     ` [PATCH v3 4/5] dma/imx_edma5: add data path Gagandeep Singh
2026-08-11 10:49     ` [PATCH v3 5/5] dma/imx_edma5: add statistics and dump Gagandeep Singh
2026-08-11 17:00     ` [PATCH v3 0/5] dma/imx_edma5: introduce NXP i.MX95 eDMA5 driver Stephen Hemminger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260806084245.561305-4-g.singh@nxp.com \
    --to=g.singh@nxp.com \
    --cc=dev@dpdk.org \
    --cc=hemant.agrawal@nxp.com \
    --cc=prashant.gupta_3@nxp.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox