On 8/12/2026 5:29 PM, Gagandeep Singh wrote: > Add the skeleton of a dmadev PMD for the NXP i.MX95 eDMA5 (Enhanced > Direct Memory Access Type 5) controller. The eDMA5 exposes 64 channels > with a 64-bit TCD (TCD64) layout.
It should be 64-byte accord `struct imx_edma5_hw_tcd64` define > > The controller is probed on the platform bus via the generic > vfio-platform kernel driver and claimed by its device-tree compatible > string fsl,imx95-edma5. This patch adds the register definitions, the > private data structures, probe and remove handling, device-tree > dma-channel-mask parsing to skip channels reserved for other bus > masters, global clock and arbitration setup, and the device information > query. Subsequent patches add configuration, the data path, and > statistics. > > Signed-off-by: Gagandeep Singh <[email protected]> > Signed-off-by: Prashant Gupta <[email protected]> > --- > MAINTAINERS | 5 + > doc/guides/dmadevs/imx_edma5.rst | 60 +++++++ > doc/guides/dmadevs/index.rst | 1 + > doc/guides/rel_notes/release_26_11.rst | 6 + > drivers/dma/imx_edma5/imx_edma5_dmadev.c | 219 ++++++++++++++++++++++ > drivers/dma/imx_edma5/imx_edma5_dmadev.h | 220 +++++++++++++++++++++++ > drivers/dma/imx_edma5/imx_edma5_hw.h | 143 +++++++++++++++ > drivers/dma/imx_edma5/imx_edma5_logs.h | 16 ++ > drivers/dma/imx_edma5/meson.build | 10 ++ > drivers/dma/meson.build | 1 + > 10 files changed, 681 insertions(+) > create mode 100644 doc/guides/dmadevs/imx_edma5.rst > create mode 100644 drivers/dma/imx_edma5/imx_edma5_dmadev.c > create mode 100644 drivers/dma/imx_edma5/imx_edma5_dmadev.h > create mode 100644 drivers/dma/imx_edma5/imx_edma5_hw.h > create mode 100644 drivers/dma/imx_edma5/imx_edma5_logs.h > create mode 100644 drivers/dma/imx_edma5/meson.build > > diff --git a/MAINTAINERS b/MAINTAINERS > index e99a65d197..b2645ebe2a 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -1422,6 +1422,11 @@ M: Hemant Agrawal <[email protected]> > F: drivers/dma/dpaa2/ > F: doc/guides/dmadevs/dpaa2.rst > > +NXP i.MX95 eDMA5 > +M: Gagandeep Singh <[email protected]> > +M: Prashant Gupta <[email protected]> > +F: drivers/dma/imx_edma5/ > +F: doc/guides/dmadevs/imx_edma5.rst > > RegEx Drivers > ------------- > diff --git a/doc/guides/dmadevs/imx_edma5.rst > b/doc/guides/dmadevs/imx_edma5.rst > new file mode 100644 > index 0000000000..f57881816e > --- /dev/null > +++ b/doc/guides/dmadevs/imx_edma5.rst > @@ -0,0 +1,60 @@ > +.. SPDX-License-Identifier: BSD-3-Clause > + Copyright 2026 NXP > + > +NXP i.MX95 eDMA5 DMA Driver > +=========================== > + > +The ``imx_edma5`` DMA driver is a poll-mode driver (PMD) for the NXP i.MX95 > +Enhanced Direct Memory Access controller version 5 (eDMA5). It exposes each > +eDMA5 controller instance as a DPDK dmadev device and can be used through the > +generic DMA device (dmadev) API. > + > +The i.MX95 SoC integrates multiple eDMA instances. The eDMA5 instances > provide > +64 hardware channels, 64-bit addressing and a 64-byte Transfer Control > +Descriptor (TCD64). This driver targets the eDMA5 instances only (device tree > +compatible ``fsl,imx95-edma5``). > + > +Supported Features > +------------------ > + > +- Memory-to-memory copy (``RTE_DMA_DIR_MEM_TO_MEM``). > +- Single-operation copy (``rte_dma_copy``). > +- Scatter-gather copy (``rte_dma_copy_sg``). > +- Per virtual channel statistics. > + > +Each configured virtual channel (vchan) is mapped one-to-one onto a hardware > +eDMA5 channel. Software-initiated (SWSTART) single-block transfers are > +programmed into the per-channel TCD and completion is detected by polling the > +TCD DONE status. > + > +Prerequisites > +------------- > + > +The eDMA5 register window is memory-mapped into the userspace process through > +the DPDK platform bus using the Linux ``vfio-platform`` mechanism. The device > +tree node targeted by this driver must be released from the kernel > ``fsl-edma`` > +driver (its status set to ``disabled`` or the node unbound) before it can be > +used by DPDK. > + > +Bind the platform device to ``vfio-platform``, for example:: > + > + echo vfio-platform > /sys/bus/platform/devices/<node>/driver_override > + echo <node> > /sys/bus/platform/drivers/vfio-platform/bind > + > +where ``<node>`` is the platform device name of the eDMA5 instance (for > +example ``42000000.dma-controller``). > + > +Compilation > +----------- > + > +The driver is built as part of the standard DPDK meson build on Linux > targets. > +No extra configuration option is required. > + > +Limitations > +----------- > + > +- Only the memory-to-memory transfer direction is supported. > +- The scatter-gather path iterates the source and destination segment lists > + independently and programs one hardware transfer per consumed segment; > + full TCD scatter-gather linking is not yet implemented. > +- The driver operates in poll mode only; completion interrupts are not used. > diff --git a/doc/guides/dmadevs/index.rst b/doc/guides/dmadevs/index.rst > index 56beb1733f..47c52a9220 100644 > --- a/doc/guides/dmadevs/index.rst > +++ b/doc/guides/dmadevs/index.rst > @@ -17,5 +17,6 @@ an application through DMA API. > hisi_acc > hisi_pciep > idxd > + imx_edma5 > ioat > odm > diff --git a/doc/guides/rel_notes/release_26_11.rst > b/doc/guides/rel_notes/release_26_11.rst > index a2c0267818..638e92ad8d 100644 > --- a/doc/guides/rel_notes/release_26_11.rst > +++ b/doc/guides/rel_notes/release_26_11.rst > @@ -64,6 +64,12 @@ New Features > kernel driver is the generic ``vfio-platform``, which carries no > device identity in its name. > > +* **Added NXP i.MX95 eDMA5 DMA driver.** > + > + Added the ``imx_edma5`` DMA driver for the NXP i.MX95 eDMA5 controller. > + The driver exposes each eDMA5 instance as a DPDK dmadev device and > + supports memory-to-memory copy and scatter-gather copy operations. > + > > Removed Items > ------------- > diff --git a/drivers/dma/imx_edma5/imx_edma5_dmadev.c > b/drivers/dma/imx_edma5/imx_edma5_dmadev.c > new file mode 100644 > index 0000000000..f1c65a474a > --- /dev/null > +++ b/drivers/dma/imx_edma5/imx_edma5_dmadev.c > @@ -0,0 +1,219 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright 2026 NXP > + */ > + > +/* > + * NXP i.MX95 eDMA5 dmadev driver. > + * > + * Exposes an eDMA5 controller instance as a DPDK dmadev. Each configured > + * virtual channel maps 1:1 onto a hardware eDMA5 channel. Memory-to-memory > + * copy and scatter-gather copy are supported using software-initiated > + * single-block transfers programmed through the per-channel 64-bit TCD. > + * > + * The device is bound to userspace through the platform bus (vfio-platform); > + * the device tree node must be released from the kernel fsl-edma driver > before > + * it can be used here. > + */ No need for above comments because it has repeat content with imx_edma5.rst > + > +#include <errno.h> > +#include <inttypes.h> > +#include <limits.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > + > +#include <bus_platform_driver.h> > +#include <rte_bitops.h> > +#include <rte_byteorder.h> > +#include <rte_common.h> > +#include <rte_cycles.h> > +#include <rte_dmadev_pmd.h> > +#include <rte_malloc.h> > + > +#include "imx_edma5_dmadev.h" > + > +#include "imx_edma5_logs.h" > + > +RTE_LOG_REGISTER_DEFAULT(imx_edma5_logtype, INFO); > + > +/* Compatible string for i.MX95 eDMA5 device tree nodes. */ > +#define IMX_EDMA5_COMPAT "fsl,imx95-edma5" > + > +/* sysfs base path for platform devices' device-tree nodes. */ > +#define IMX_EDMA5_SYSFS_DEVICES "/sys/bus/platform/devices" > + > +/* > + * Read the device-tree "dma-channel-mask" property and return the combined > + * 64-bit mask of reserved hardware channels. The property is a raw binary > file > + * of big-endian 32-bit cells: cell[0] covers channels 0-31, cell[1] channels > + * 32-63. A set bit marks a channel owned by another bus master (e.g. SCMI > + * firmware) that must not be accessed. Returns 0 if the property is absent. > + */ > +static uint64_t > +imx_edma5_read_channel_mask(const char *dev_name) > +{ > + char path[PATH_MAX]; > + uint32_t cells[2] = { 0, 0 }; > + uint64_t mask = 0; > + size_t n; > + FILE *f; > + > + snprintf(path, sizeof(path), > + IMX_EDMA5_SYSFS_DEVICES "/%s/of_node/dma-channel-mask", > + dev_name); > + > + f = fopen(path, "rb"); > + if (f == NULL) > + return 0; > + > + n = fread(cells, 1, sizeof(cells), f); > + fclose(f); > + > + /* Device-tree cells are big-endian regardless of CPU endianness. */ > + if (n >= sizeof(uint32_t)) > + mask |= rte_be_to_cpu_32(cells[0]); > + if (n >= 2 * sizeof(uint32_t)) > + mask |= (uint64_t)rte_be_to_cpu_32(cells[1]) << 32; > + > + return mask; > +} > + > +static int > +imx_edma5_info_get(const struct rte_dma_dev *dev, struct rte_dma_info > *dev_info, > + uint32_t info_sz) > +{ > + const struct imx_edma5_dev *ed = dev->data->dev_private; > + > + RTE_SET_USED(info_sz); > + > + dev_info->dev_capa = RTE_DMA_CAPA_MEM_TO_MEM | > + RTE_DMA_CAPA_OPS_COPY | > + RTE_DMA_CAPA_OPS_COPY_SG; > + dev_info->max_vchans = ed->max_vchans; > + dev_info->max_desc = IMX_EDMA5_MAX_DESC; > + dev_info->min_desc = IMX_EDMA5_MIN_DESC; > + dev_info->max_sges = IMX_EDMA5_MAX_SGES; > + > + return 0; > +} > + > +static const struct rte_dma_dev_ops imx_edma5_ops = { > + .dev_info_get = imx_edma5_info_get, > +}; > + > +static int > +imx_edma5_probe(struct rte_platform_device *pdev) > +{ > + struct rte_platform_resource *res; > + struct imx_edma5_dev *ed; > + struct rte_dma_dev *dev; > + const char *name; > + > + name = pdev->name; > + > + if (rte_eal_process_type() != RTE_PROC_PRIMARY) { > + IMX_EDMA5_LOG(ERR, "Secondary process not supported for %s", > + name); > + return -ENOTSUP; > + } > + > + if (pdev->num_resource < 1 || pdev->resource == NULL) { > + IMX_EDMA5_LOG(ERR, "No MMIO resource for %s", name); > + return -EINVAL; > + } > + res = &pdev->resource[0]; > + if (res->mem.addr == NULL) { > + IMX_EDMA5_LOG(ERR, "MMIO resource not mapped for %s", name); > + return -EINVAL; > + } > + > + dev = rte_dma_pmd_allocate(name, rte_socket_id(), > + sizeof(struct imx_edma5_dev)); > + if (dev == NULL) { > + IMX_EDMA5_LOG(ERR, "Failed to allocate dmadev for %s", name); > + return -ENOMEM; > + } > + > + dev->device = &pdev->device; > + dev->dev_ops = &imx_edma5_ops; > + > + ed = dev->data->dev_private; > + ed->reg_base = res->mem.addr; > + ed->reg_size = res->mem.len; > + ed->dev_id = dev->data->dev_id; > + > + /* > + * Build a map from usable vchan index to hardware channel index, > + * skipping channels reserved for other bus masters by the device-tree > + * "dma-channel-mask" (accessing them external-aborts). > + */ > + ed->masked_channels = imx_edma5_read_channel_mask(name); > + ed->nb_channels = 0; > + { > + uint16_t hw; > + > + for (hw = 0; hw < IMX_EDMA5_MAX_CHANNELS; hw++) { > + if (ed->masked_channels & (RTE_BIT64(hw))) > + continue; > + ed->chan_map[ed->nb_channels++] = hw; > + } > + } How about move `uint16_t hw` to the beginning of this function, so that eliminate {} sub-block > + ed->max_vchans = ed->nb_channels; > + > + if (ed->nb_channels == 0) { > + IMX_EDMA5_LOG(ERR, > + "No usable eDMA5 channels for %s (mask 0x%" > PRIx64 ")", > + name, ed->masked_channels); > + rte_dma_pmd_release(name); > + return -ENODEV; > + } > + > + dev->state = RTE_DMA_DEV_READY; > + > + /* > + * Set MP_CSR.GCLC (Global Clock Control) before any per-channel > register > + * is touched: the per-channel windows are individually clock-gated and > + * external-abort when accessed with GCLC clear. Also enable round-robin > + * arbitration (ERCA). Use read-modify-write to preserve reset defaults. > + */ > + { > + uint32_t mp_csr = imx_edma5_read32(ed->reg_base, > IMX_EDMA5_MP_CSR); > + > + mp_csr |= IMX_EDMA5_MP_CSR_GCLC | IMX_EDMA5_MP_CSR_ERCA; > + imx_edma5_write32(ed->reg_base, IMX_EDMA5_MP_CSR, mp_csr); > + } Suggest move mp_csr to the beginning of this function > + > + IMX_EDMA5_LOG(INFO, "Probed i.MX95 eDMA5 dmadev %s (%u channels)", > + name, ed->nb_channels); > + > + return 0; > +} > + > +static int > +imx_edma5_remove(struct rte_platform_device *pdev) > +{ > + const char *name = pdev->name; > + > + return rte_dma_pmd_release(name); > +} > + > +static struct rte_platform_driver imx_edma5_pmd_drv = { > + /* > + * Set the DT compatible string as the driver alias. The platform bus > + * match logic compares this against the device-tree "compatible" sysfs > + * strings of each platform device when the kernel driver name (always > + * "vfio-platform") does not uniquely identify the device. > + */ > + .driver = { > + .alias = IMX_EDMA5_COMPAT, > + }, > + .probe = imx_edma5_probe, > + .remove = imx_edma5_remove, > + /* > + * The eDMA5 is programmed with the IOVA of the buffers, so it works in > + * both IOVA=VA and IOVA=PA modes; no IOVA-as-VA requirement is forced. > + */ > + .drv_flags = 0, > +}; > + > +RTE_PMD_REGISTER_PLATFORM(dma_imx_edma5, imx_edma5_pmd_drv); > diff --git a/drivers/dma/imx_edma5/imx_edma5_dmadev.h > b/drivers/dma/imx_edma5/imx_edma5_dmadev.h > new file mode 100644 > index 0000000000..3da7957457 > --- /dev/null > +++ b/drivers/dma/imx_edma5/imx_edma5_dmadev.h > @@ -0,0 +1,220 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright 2026 NXP > + */ > + > +#ifndef IMX_EDMA5_DMADEV_H > +#define IMX_EDMA5_DMADEV_H > + > +#include <stdbool.h> > +#include <stddef.h> > +#include <stdint.h> > + > +#include <rte_byteorder.h> > +#include <rte_common.h> > +#include <rte_dmadev.h> > +#include <rte_io.h> > +#include <rte_memory.h> > + > +#include "imx_edma5_hw.h" > + > +/* > + * CPU data-cache maintenance for the non-coherent eDMA5 master: clean the > + * source before the transfer and clean+invalidate the destination after it. > + * DC IVAC (invalidate-only) is EL1-only and faults from userspace, so DC > CIVAC > + * is used. No-ops on non-arm64 builds. > + */ > +static inline void > +imx_edma5_dcbf(void *p) > +{ > +#ifdef RTE_ARCH_ARM64 > + asm volatile("dc cvac, %0" : : "r"(p) : "memory"); > +#else > + RTE_SET_USED(p); > +#endif > +} > + > +static inline void > +imx_edma5_dccivac(void *p) > +{ > +#ifdef RTE_ARCH_ARM64 > + asm volatile("dc civac, %0" : : "r"(p) : "memory"); > +#else > + RTE_SET_USED(p); > +#endif > +} > + > +/* Clean a VA range to the Point of Coherency. */ > +static inline void > +imx_edma5_cache_clean(void *addr, size_t len) > +{ > + uintptr_t p = (uintptr_t)addr & ~(uintptr_t)(RTE_CACHE_LINE_SIZE - 1); > + uintptr_t end = (uintptr_t)addr + len; > + > + for (; p < end; p += RTE_CACHE_LINE_SIZE) > + imx_edma5_dcbf((void *)p); > + /* > + * DSB ensures all DC CVAC instructions have completed to the Point of > + * Coherency before the eDMA5 is allowed to read the source data. > + */ > +#ifdef RTE_ARCH_ARM64 > + asm volatile("dsb sy" ::: "memory"); > +#endif > +} > + > +/* Clean+invalidate a VA range to the Point of Coherency. */ > +static inline void > +imx_edma5_cache_inval(void *addr, size_t len) > +{ > + uintptr_t p = (uintptr_t)addr & ~(uintptr_t)(RTE_CACHE_LINE_SIZE - 1); > + uintptr_t end = (uintptr_t)addr + len; > + > + for (; p < end; p += RTE_CACHE_LINE_SIZE) > + imx_edma5_dccivac((void *)p); > + /* > + * DSB ensures all DC CIVAC instructions have completed to the Point of > + * Coherency before the CPU reads the destination data written by eDMA5. > + */ > +#ifdef RTE_ARCH_ARM64 > + asm volatile("dsb sy" ::: "memory"); > +#endif > +} > + > +/* > + * NBYTES field maximum: bits 31:30 of TCD_NBYTES are SMLOE/DMLOE control > bits > + * when the minor loop offset feature is enabled. To avoid accidentally > setting > + * those bits, cap every single-block transfer at the 30-bit maximum count. > + */ > +#define IMX_EDMA5_MAX_NBYTES ((uint32_t)((1u << 30) - 1)) > + > +/* Maximum scatter-gather segments per copy_sg request. */ > +#define IMX_EDMA5_MAX_SGES 16 > + > +/* > + * In-memory TCD64 descriptors reserved per job slot for scatter-gather. > + * Splitting an asymmetric src/dst segment list at the union of both sets of > + * boundaries yields at most n_src + n_dst - 1 sub-transfers, so with up to > + * IMX_EDMA5_MAX_SGES segments per side the worst case fits in this bound. > + */ > +#define IMX_EDMA5_SG_TCD_PER_JOB (2 * IMX_EDMA5_MAX_SGES) > + > +/* Software job ring size per virtual channel (power of two). */ > +#define IMX_EDMA5_MAX_DESC 4096 > +#define IMX_EDMA5_MIN_DESC 32 > + > +/* Per in-flight job bookkeeping. */ > +struct imx_edma5_job { > + uint16_t ridx; /* ring index returned to application */ > + uint8_t submitted; /* job has been started on hardware */ > + uint8_t done; /* job completed */ > + uint8_t error; /* job completed with error */ > + /* > + * Destination VA and byte count of the copy, used to invalidate the > + * destination cache lines on completion (non-coherent eDMA master). > + * NULL if the VA could not be resolved. Unused for SG jobs (nb_sg > 0). > + */ > + void *dst_va; > + uint32_t len; > + /* > + * Source/destination IOVAs of a plain single-block copy, recorded at > + * enqueue time and used to program the TCD at submit time (the eDMA5 > has > + * a single register TCD shared by all jobs). Unused for SG jobs. > + */ > + rte_iova_t src_iova; > + rte_iova_t dst_iova; > + /* > + * Scatter-gather state. nb_sg is the segment count (0 for a plain > copy); > + * sg_tcd points at this job's slice of the vchan's in-memory TCD pool. > + */ > + uint16_t nb_sg; > + struct imx_edma5_hw_tcd64 *sg_tcd; > +}; > + > +/* A virtual channel maps 1:1 onto a single eDMA5 hardware channel. */ > +struct imx_edma5_vchan { > + uint8_t *ch_regs; /* channel register window base */ > + uint8_t *tcd_regs; /* channel TCD base (ch_regs + TCD_OFF) > */ > + uint32_t hw_chan; /* hardware channel index */ > + > + struct imx_edma5_job *jobs; /* software job ring */ > + uint16_t nb_desc; /* size of job ring (power of two) */ > + uint16_t desc_mask; /* nb_desc - 1 */ > + > + /* > + * Pool of in-memory TCD64 descriptors for scatter-gather, sized > + * nb_desc * IMX_EDMA5_SG_TCD_PER_JOB. Each job slot owns a contiguous > + * slice of IMX_EDMA5_SG_TCD_PER_JOB descriptors. sg_tcd_iova is the > + * pool base IOVA. > + */ > + struct imx_edma5_hw_tcd64 *sg_tcd_pool; > + rte_iova_t sg_tcd_iova; > + > + uint16_t head; /* next slot to enqueue */ > + uint16_t tail; /* next slot to reap */ > + uint16_t nb_enqueued; /* outstanding jobs in ring (unreaped) > */ > + uint16_t ridx; /* running ring index counter */ > + uint16_t last_idx; /* last completed ring index */ > + > + uint64_t submitted_count; > + uint64_t completed_count; > + uint64_t errors_count; > + > + bool configured; > +}; > + > +/* Per-device (per eDMA5 instance) private data. */ > +struct imx_edma5_dev { > + uint8_t *reg_base; /* mapped register window base */ > + uint64_t reg_size; /* mapped register window length */ > + > + uint16_t nb_channels; /* channels available on this instance > */ > + uint16_t max_vchans; /* channels usable as dmadev vchans */ > + > + /* > + * Bitmask of hardware channels reserved for other bus masters (from the > + * device-tree "dma-channel-mask"). A set bit marks a channel this > driver > + * must not touch; accessing it faults with a bus external abort. > + */ > + uint64_t masked_channels; > + /* Map of usable dmadev vchan index -> hardware channel index. */ > + uint16_t chan_map[IMX_EDMA5_MAX_CHANNELS]; > + > + struct imx_edma5_vchan *vchans; /* array of vchan states */ > + uint16_t nb_vchans; /* number of configured vchans */ > + > + int16_t dev_id; /* dmadev id */ > +}; > + > +/* MMIO helpers (little-endian device). */ > +static inline uint32_t > +imx_edma5_read32(const uint8_t *base, uint32_t off) > +{ > + return rte_le_to_cpu_32(rte_read32(base + off)); > +} > + > +static inline void > +imx_edma5_write32(uint8_t *base, uint32_t off, uint32_t val) > +{ > + rte_write32(rte_cpu_to_le_32(val), base + off); > +} > + > +static inline uint16_t > +imx_edma5_read16(const uint8_t *base, uint32_t off) > +{ > + return rte_le_to_cpu_16(rte_read16(base + off)); > +} > + > +static inline void > +imx_edma5_write16(uint8_t *base, uint32_t off, uint16_t val) > +{ > + rte_write16(rte_cpu_to_le_16(val), base + off); > +} > + > +static inline void > +imx_edma5_write64(uint8_t *base, uint32_t off, uint64_t val) > +{ > + /* Write the 64-bit field as two 32-bit accesses (order not > significant). */ > + imx_edma5_write32(base, off, (uint32_t)(val & 0xFFFFFFFFu)); > + imx_edma5_write32(base, off + 4, (uint32_t)(val >> 32)); > +} > + > +#endif /* IMX_EDMA5_DMADEV_H */ > diff --git a/drivers/dma/imx_edma5/imx_edma5_hw.h > b/drivers/dma/imx_edma5/imx_edma5_hw.h > new file mode 100644 > index 0000000000..07c941cd13 > --- /dev/null > +++ b/drivers/dma/imx_edma5/imx_edma5_hw.h > @@ -0,0 +1,143 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright 2026 NXP > + */ > + > +/* > + * Hardware register definitions for the NXP i.MX95 eDMA5 controller. > + * The block has a Management Page (MP) region followed by per-channel > register > + * windows (base offset 0x10000, stride 0x8000, 64 channels, 64-bit TCD64). 64-byte TCD64? > + * All register accesses are little-endian. > + */ > + > +#ifndef IMX_EDMA5_HW_H > +#define IMX_EDMA5_HW_H > + > +#include <stdint.h> > + > +/* Number of DMA channels implemented on i.MX95 eDMA5. */ > +#define IMX_EDMA5_MAX_CHANNELS 64 > + > +/* Per-channel register window geometry. */ > +#define IMX_EDMA5_CHAN_BASE_OFF 0x10000u > +#define IMX_EDMA5_CHAN_STRIDE 0x8000u > + > +/* > + * Management Page (MP) registers (offsets from register window base). > + * Only the fields used by this driver are documented here. > + */ > +#define IMX_EDMA5_MP_CSR 0x0000u /* Management control */ > +#define IMX_EDMA5_MP_ES 0x0004u /* Management error > status */ > +#define IMX_EDMA5_MP_INT_LOW 0x0008u /* Interrupt request (chan > 0-31) */ > +#define IMX_EDMA5_MP_INT_HIGH 0x000Cu /* Interrupt request > (chan 32-63) */ > +#define IMX_EDMA5_MP_HRS_LOW 0x0010u /* Hardware request status low > */ > +#define IMX_EDMA5_MP_HRS_HIGH 0x0014u /* Hardware request > status high */ > + > +/* MP_CSR bit fields. */ > +#define IMX_EDMA5_MP_CSR_EDBG (1u << 1) /* Enable debug */ > +#define IMX_EDMA5_MP_CSR_ERCA (1u << 2) /* Enable > round-robin arb */ > +#define IMX_EDMA5_MP_CSR_HAE (1u << 4) /* Halt after error */ > +#define IMX_EDMA5_MP_CSR_GCLC (1u << 6) /* Global clock > control */ > +#define IMX_EDMA5_MP_CSR_GMRC (1u << 7) /* Global master ID > replic */ > + > +/* MP_ES: valid bit indicates a logged error is present. */ > +#define IMX_EDMA5_MP_ES_VLD (1u << 31) > + > +/* > + * Per-channel control registers (offsets from a channel window base). > + * Layout matches the eDMA4/eDMA5 fsl_edma3_ch_reg structure. > + */ > +#define IMX_EDMA5_CH_CSR 0x00u /* Channel control/status */ > +#define IMX_EDMA5_CH_ES 0x04u /* Channel error status > */ > +#define IMX_EDMA5_CH_INT 0x08u /* Channel interrupt status */ > +#define IMX_EDMA5_CH_SBR 0x0Cu /* System bus register */ > +#define IMX_EDMA5_CH_PRI 0x10u /* Channel priority */ > +#define IMX_EDMA5_CH_MUX 0x14u /* Channel multiplexor (source) > */ > +#define IMX_EDMA5_CH_MATTR 0x18u /* Memory attributes */ > + > +/* Channel window offset of the TCD (Transfer Control Descriptor). */ > +#define IMX_EDMA5_CH_TCD_OFF 0x20u > + > +/* CH_CSR bit fields. */ > +#define IMX_EDMA5_CH_CSR_ERQ (1u << 0) /* Enable hardware request */ > +#define IMX_EDMA5_CH_CSR_EARQ (1u << 1) /* Enable async hw > request */ > +#define IMX_EDMA5_CH_CSR_EEI (1u << 2) /* Enable error interrupt */ > +#define IMX_EDMA5_CH_CSR_DONE (1u << 30) /* Channel done > (w1c) */ > +#define IMX_EDMA5_CH_CSR_ACTIVE (1u << 31) /* Channel active */ > + > +/* CH_ES: valid bit indicates a logged channel error. */ > +#define IMX_EDMA5_CH_ES_ERR (1u << 31) > + > +/* CH_INT: write 1 to clear the channel interrupt request. */ > +#define IMX_EDMA5_CH_INT_INT (1u << 0) > + > +/* CH_SBR: read/write privileged/secure attributes for bus mastering. */ > +#define IMX_EDMA5_CH_SBR_RD (1u << 22) > +#define IMX_EDMA5_CH_SBR_WR (1u << 21) > + > +/* > + * TCD64 field offsets, relative to the channel TCD base > + * (channel window base + IMX_EDMA5_CH_TCD_OFF). > + */ > +#define IMX_EDMA5_TCD_SADDR 0x00u > +#define IMX_EDMA5_TCD_SOFF 0x08u > +#define IMX_EDMA5_TCD_ATTR 0x0Au > +#define IMX_EDMA5_TCD_NBYTES 0x0Cu > +#define IMX_EDMA5_TCD_SLAST 0x10u > +#define IMX_EDMA5_TCD_DADDR 0x18u > +#define IMX_EDMA5_TCD_DLAST_SGA 0x20u > +#define IMX_EDMA5_TCD_DOFF 0x28u > +#define IMX_EDMA5_TCD_CITER 0x2Au > +#define IMX_EDMA5_TCD_CSR 0x2Cu > +#define IMX_EDMA5_TCD_BITER 0x2Eu > + > +/* TCD ATTR sub-fields: transfer size is encoded as log2(bytes). GET_* > extract. */ > +#define IMX_EDMA5_TCD_ATTR_DSIZE(x) (((x) & 0x7u)) > +#define IMX_EDMA5_TCD_ATTR_SSIZE(x) (((x) & 0x7u) << 8) > +#define IMX_EDMA5_TCD_ATTR_GET_DSIZE(x) ((x) & 0x7u) > +#define IMX_EDMA5_TCD_ATTR_GET_SSIZE(x) (((x) >> 8) & 0x7u) > + > +/* Transfer size encodings for ATTR SSIZE/DSIZE (log2 of bytes). */ > +#define IMX_EDMA5_TCD_SIZE_1B 0u > +#define IMX_EDMA5_TCD_SIZE_2B 1u > +#define IMX_EDMA5_TCD_SIZE_4B 2u > +#define IMX_EDMA5_TCD_SIZE_8B 3u > +#define IMX_EDMA5_TCD_SIZE_16B 4u > +#define IMX_EDMA5_TCD_SIZE_32B 5u > +#define IMX_EDMA5_TCD_SIZE_64B 6u > + > +/* Major iteration count field mask (15-bit CITER/BITER). */ > +#define IMX_EDMA5_TCD_ITER_MASK 0x7FFFu > + > +/* TCD CSR bit fields. */ > +#define IMX_EDMA5_TCD_CSR_START (1u << 0) /* Software start */ > +#define IMX_EDMA5_TCD_CSR_INT_MAJOR (1u << 1) /* Interrupt on major done */ > +#define IMX_EDMA5_TCD_CSR_INT_HALF (1u << 2) /* Interrupt on half done */ > +#define IMX_EDMA5_TCD_CSR_D_REQ (1u << 3) /* Disable request > on done */ > +#define IMX_EDMA5_TCD_CSR_E_SG (1u << 4) /* Enable > scatter-gather */ > +#define IMX_EDMA5_TCD_CSR_E_LINK (1u << 5) /* Enable channel linking */ > +#define IMX_EDMA5_TCD_CSR_ACTIVE (1u << 6) /* Channel active */ > +#define IMX_EDMA5_TCD_CSR_DONE (1u << 7) /* Channel done */ > + > +/* > + * In-memory 64-bit Transfer Control Descriptor. The field order and offsets > + * match the register TCD64 layout above; all fields are little-endian. The > + * descriptor must be 32-byte aligned and, on this non-coherent SoC, cleaned > + * from the CPU cache before the transfer is started. > + */ > +struct __rte_aligned(32) imx_edma5_hw_tcd64 { > + uint64_t saddr; /* 0x00 source address */ > + uint16_t soff; /* 0x08 source offset */ > + uint16_t attr; /* 0x0A transfer attributes */ > + uint32_t nbytes; /* 0x0C minor loop byte count */ > + uint64_t slast; /* 0x10 last source adjustment */ > + uint64_t daddr; /* 0x18 destination address */ > + uint64_t dlast_sga; /* 0x20 next TCD address (scatter-gather) */ > + uint16_t doff; /* 0x28 destination offset */ > + uint16_t citer; /* 0x2A current major iteration count */ > + uint16_t csr; /* 0x2C control and status */ > + uint16_t biter; /* 0x2E starting major iteration count */ > + /* Pad to 64 bytes total; the type is 32-byte aligned for TCD fetches. > */ > + uint8_t reserved[16]; > +}; > + > +#endif /* IMX_EDMA5_HW_H */ > diff --git a/drivers/dma/imx_edma5/imx_edma5_logs.h > b/drivers/dma/imx_edma5/imx_edma5_logs.h > new file mode 100644 > index 0000000000..e73e496bb4 > --- /dev/null > +++ b/drivers/dma/imx_edma5/imx_edma5_logs.h > @@ -0,0 +1,16 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright 2026 NXP > + */ > + > +#ifndef IMX_EDMA5_LOGS_H > +#define IMX_EDMA5_LOGS_H > + > +#include <rte_log.h> > + > +extern int imx_edma5_logtype; > +#define RTE_LOGTYPE_IMX_EDMA5 imx_edma5_logtype > + > +#define IMX_EDMA5_LOG(level, ...) \ > + RTE_LOG_LINE_PREFIX(level, IMX_EDMA5, "%s(): ", __func__, __VA_ARGS__) > + > +#endif /* IMX_EDMA5_LOGS_H */ > diff --git a/drivers/dma/imx_edma5/meson.build > b/drivers/dma/imx_edma5/meson.build > new file mode 100644 > index 0000000000..e4f5d4e0bb > --- /dev/null > +++ b/drivers/dma/imx_edma5/meson.build > @@ -0,0 +1,10 @@ > +# SPDX-License-Identifier: BSD-3-Clause > +# Copyright 2026 NXP > + > +if not is_linux > + build = false > + reason = 'only supported on linux' > +endif > + > +deps += ['dmadev', 'bus_platform'] Please include by alphabetical order > +sources = files('imx_edma5_dmadev.c') > diff --git a/drivers/dma/meson.build b/drivers/dma/meson.build > index e0d94db967..dd00b1dae9 100644 > --- a/drivers/dma/meson.build > +++ b/drivers/dma/meson.build > @@ -8,6 +8,7 @@ drivers = [ > 'hisi_acc', > 'hisi_pciep', > 'idxd', > + 'imx_edma5', > 'ioat', > 'odm', > 'skeleton', with above fixed, Acked-by: Chengwen Feng <[email protected]>

