On 8/12/2026 5:29 PM, Gagandeep Singh wrote:
> Add the configuration and lifecycle operations for the i.MX95 eDMA5
> dmadev: device configure, virtual channel setup, start, stop and close.
> 
> Each virtual channel maps 1:1 onto a usable hardware channel and owns a
> software job ring plus a pool of in-memory TCD64 descriptors for
> scatter-gather. Channel setup validates the requested direction and ring
> size, allocates these rings, and resets the hardware channel to a known
> idle state. Start re-arms global clock and arbitration and clears the
> per-channel bookkeeping; stop resets every configured channel; close
> frees the per-channel resources.
> 
> Signed-off-by: Gagandeep Singh <[email protected]>
> Signed-off-by: Prashant Gupta <[email protected]>
> ---
>  drivers/dma/imx_edma5/imx_edma5_dmadev.c | 246 +++++++++++++++++++++++
>  1 file changed, 246 insertions(+)
> 
> diff --git a/drivers/dma/imx_edma5/imx_edma5_dmadev.c 
> b/drivers/dma/imx_edma5/imx_edma5_dmadev.c
> index f1c65a474a..fe5539b612 100644
> --- a/drivers/dma/imx_edma5/imx_edma5_dmadev.c
> +++ b/drivers/dma/imx_edma5/imx_edma5_dmadev.c
> @@ -78,6 +78,14 @@ imx_edma5_read_channel_mask(const char *dev_name)
>       return mask;
>  }
>  
> +/* Return the register base of hardware channel n. */
> +static inline uint8_t *
> +imx_edma5_chan_base(struct imx_edma5_dev *ed, uint32_t chan)
> +{
> +     return ed->reg_base + IMX_EDMA5_CHAN_BASE_OFF +
> +            (size_t)chan * IMX_EDMA5_CHAN_STRIDE;
> +}
> +
>  static int
>  imx_edma5_info_get(const struct rte_dma_dev *dev, struct rte_dma_info 
> *dev_info,
>                  uint32_t info_sz)
> @@ -97,8 +105,246 @@ imx_edma5_info_get(const struct rte_dma_dev *dev, struct 
> rte_dma_info *dev_info,
>       return 0;
>  }
>  
> +static void imx_edma5_reset_hw_chan(struct imx_edma5_vchan *vc);
> +
> +static int
> +imx_edma5_configure(struct rte_dma_dev *dev, const struct rte_dma_conf *conf,
> +                 uint32_t conf_sz)
> +{
> +     struct imx_edma5_dev *ed = dev->data->dev_private;
> +
> +     RTE_SET_USED(conf_sz);
> +
> +     if (conf->nb_vchans == 0 || conf->nb_vchans > ed->max_vchans) {
> +             IMX_EDMA5_LOG(ERR, "Invalid nb_vchans %u (max %u)",
> +                           conf->nb_vchans, ed->max_vchans);
> +             return -EINVAL;
> +     }

No need add such verify because the rte_dma_configure already done

> +
> +     if (ed->vchans == NULL) {
> +             ed->vchans = rte_zmalloc_socket("imx_edma5_vchans",
> +                             ed->max_vchans * sizeof(struct imx_edma5_vchan),
> +                             RTE_CACHE_LINE_SIZE, dev->data->numa_node);
> +             if (ed->vchans == NULL) {
> +                     IMX_EDMA5_LOG(ERR, "Failed to alloc vchan array");
> +                     return -ENOMEM;
> +             }
> +     } else {
> +             /* Reconfigure: reset and free every previously configured 
> channel. */
> +             uint16_t i;
> +
> +             for (i = 0; i < ed->nb_vchans; i++) {
> +                     struct imx_edma5_vchan *vc = &ed->vchans[i];
> +
> +                     if (!vc->configured)
> +                             continue;
> +                     imx_edma5_reset_hw_chan(vc);
> +                     rte_free(vc->jobs);
> +                     rte_free(vc->sg_tcd_pool);
> +                     memset(vc, 0, sizeof(*vc));
> +             }
> +     }
> +
> +     ed->nb_vchans = conf->nb_vchans;
> +
> +     return 0;
> +}
> +
> +/* Reset a hardware channel to a known idle state. */
> +static void
> +imx_edma5_reset_hw_chan(struct imx_edma5_vchan *vc)
> +{
> +     uint8_t *ch = vc->ch_regs;
> +     uint8_t *tcd = vc->tcd_regs;
> +     uint32_t sbr;
> +
> +     /*
> +      * Disable hardware request and clear latched completion state.
> +      * CH_CSR.DONE is write-1-to-clear, so write the DONE bit to clear any
> +      * stale completion (e.g. left by the bootloader/kernel driver) while
> +      * leaving all other control bits disabled.
> +      */
> +     imx_edma5_write32(ch, IMX_EDMA5_CH_CSR, IMX_EDMA5_CH_CSR_DONE);
> +     imx_edma5_write32(ch, IMX_EDMA5_CH_ES, IMX_EDMA5_CH_ES_ERR);
> +     imx_edma5_write32(ch, IMX_EDMA5_CH_INT, IMX_EDMA5_CH_INT_INT);
> +
> +     /*
> +      * Enable the read/write attribute bits in the System Bus Register with 
> a
> +      * read-modify-write. The security/privilege attribute bits carried here
> +      * come up with a valid reset default that the bus fabric (XRDC) checks
> +      * and that must be preserved; a blind write of just RD|WR would clear
> +      * them and make the fabric reject the eDMA master transaction.
> +      */
> +     sbr = imx_edma5_read32(ch, IMX_EDMA5_CH_SBR);
> +     sbr |= IMX_EDMA5_CH_SBR_RD | IMX_EDMA5_CH_SBR_WR;
> +     imx_edma5_write32(ch, IMX_EDMA5_CH_SBR, sbr);
> +
> +     /*
> +      * Leave CH_MATTR at its power-on reset value. The eDMA5 is a
> +      * non-coherent bus master; cache coherency is maintained by the driver
> +      * via explicit DC CVAC/CIVAC cache maintenance, not by AXI snooping.
> +      */
> +
> +     /* Clear the TCD control/status so the channel is idle. */
> +     imx_edma5_write16(tcd, IMX_EDMA5_TCD_CSR, 0);
> +     imx_edma5_write16(tcd, IMX_EDMA5_TCD_CITER, 0);
> +     imx_edma5_write16(tcd, IMX_EDMA5_TCD_BITER, 0);
> +}
> +
> +static int
> +imx_edma5_vchan_setup(struct rte_dma_dev *dev, uint16_t vchan,
> +                   const struct rte_dma_vchan_conf *conf,
> +                   uint32_t conf_sz)
> +{
> +     struct imx_edma5_dev *ed = dev->data->dev_private;
> +     struct imx_edma5_vchan *vc;
> +
> +     RTE_SET_USED(conf_sz);
> +
> +     if (vchan >= ed->nb_vchans) {
> +             IMX_EDMA5_LOG(ERR, "vchan %u out of range", vchan);
> +             return -EINVAL;
> +     }

rte_dma_vchan_setup() already done this verify

> +
> +     if (conf->direction != RTE_DMA_DIR_MEM_TO_MEM) {
> +             IMX_EDMA5_LOG(ERR, "Only mem-to-mem direction supported");
> +             return -EINVAL;
> +     }

rte_dma_vchan_setup() already done this verify

> +
> +     if (!rte_is_power_of_2(conf->nb_desc) ||
> +         conf->nb_desc < IMX_EDMA5_MIN_DESC ||
> +         conf->nb_desc > IMX_EDMA5_MAX_DESC) {
> +             IMX_EDMA5_LOG(ERR, "nb_desc must be power of 2 in [%u..%u]",
> +                           IMX_EDMA5_MIN_DESC, IMX_EDMA5_MAX_DESC);
> +             return -EINVAL;
> +     }

            conf->nb_desc < IMX_EDMA5_MIN_DESC ||
            conf->nb_desc > IMX_EDMA5_MAX_DESC)
The min-max verify already done in rte_dma_vchan_setup()

> +
> +     vc = &ed->vchans[vchan];
> +
> +     /* Free previous rings if this vchan is being reconfigured. */
> +     rte_free(vc->jobs);
> +     rte_free(vc->sg_tcd_pool);
> +     memset(vc, 0, sizeof(*vc));
> +
> +     /*
> +      * Map this vchan onto a usable hardware channel. chan_map[] skips
> +      * channels reserved by "dma-channel-mask" (channels 0 and 1 on i.MX95).
> +      */
> +     vc->hw_chan = ed->chan_map[vchan];
> +     vc->ch_regs = imx_edma5_chan_base(ed, vc->hw_chan);
> +     vc->tcd_regs = vc->ch_regs + IMX_EDMA5_CH_TCD_OFF;
> +     vc->nb_desc = conf->nb_desc;
> +     vc->desc_mask = conf->nb_desc - 1;
> +
> +     vc->jobs = rte_zmalloc_socket("imx_edma5_jobs",
> +                     vc->nb_desc * sizeof(struct imx_edma5_job),
> +                     RTE_CACHE_LINE_SIZE, dev->data->numa_node);
> +     if (vc->jobs == NULL) {
> +             IMX_EDMA5_LOG(ERR, "Failed to alloc job ring for vchan %u",
> +                           vchan);
> +             return -ENOMEM;
> +     }
> +
> +     /* One IMX_EDMA5_SG_TCD_PER_JOB descriptor slice per job ring slot. */
> +     vc->sg_tcd_pool = rte_zmalloc_socket("imx_edma5_sgtcd",
> +                     (size_t)vc->nb_desc * IMX_EDMA5_SG_TCD_PER_JOB *
> +                             sizeof(struct imx_edma5_hw_tcd64),
> +                     RTE_CACHE_LINE_SIZE, dev->data->numa_node);
> +     if (vc->sg_tcd_pool == NULL) {
> +             IMX_EDMA5_LOG(ERR, "Failed to alloc SG TCD pool for vchan %u",
> +                           vchan);

No need wrap because DPDK allow max 100 characters per line (only suggest)

> +             rte_free(vc->jobs);
> +             vc->jobs = NULL;
> +             return -ENOMEM;
> +     }
> +     vc->sg_tcd_iova = rte_malloc_virt2iova(vc->sg_tcd_pool);
> +
> +     imx_edma5_reset_hw_chan(vc);
> +     vc->configured = true;
> +
> +     return 0;
> +}
> +
> +static int
> +imx_edma5_start(struct rte_dma_dev *dev)
> +{
> +     struct imx_edma5_dev *ed = dev->data->dev_private;
> +     uint32_t mp_csr;
> +     uint16_t i;
> +
> +     /*
> +      * Enable round-robin arbitration with a read-modify-write so GCLC (set
> +      * in probe) is preserved; clearing GCLC would re-gate the per-channel
> +      * clocks and external-abort any subsequent channel access.
> +      */
> +     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);
> +
> +     for (i = 0; i < ed->nb_vchans; i++) {
> +             struct imx_edma5_vchan *vc = &ed->vchans[i];
> +
> +             if (!vc->configured)
> +                     continue;
> +             imx_edma5_reset_hw_chan(vc);
> +             vc->head = 0;
> +             vc->tail = 0;
> +             vc->nb_enqueued = 0;
> +             vc->ridx = 0;
> +             /* Seed last_idx one step before the first cookie (0). */
> +             vc->last_idx = UINT16_MAX;
> +             vc->submitted_count = 0;
> +             vc->completed_count = 0;
> +             vc->errors_count = 0;
> +     }
> +
> +     return 0;
> +}
> +
> +static int
> +imx_edma5_stop(struct rte_dma_dev *dev)
> +{
> +     struct imx_edma5_dev *ed = dev->data->dev_private;
> +     uint16_t i;
> +
> +     for (i = 0; i < ed->nb_vchans; i++) {
> +             struct imx_edma5_vchan *vc = &ed->vchans[i];
> +
> +             if (vc->configured)
> +                     imx_edma5_reset_hw_chan(vc);
> +     }
> +
> +     return 0;
> +}
> +
> +static int
> +imx_edma5_close(struct rte_dma_dev *dev)
> +{
> +     struct imx_edma5_dev *ed = dev->data->dev_private;
> +     uint16_t i;
> +
> +     if (ed->vchans != NULL) {
> +             for (i = 0; i < ed->max_vchans; i++) {
> +                     rte_free(ed->vchans[i].jobs);
> +                     rte_free(ed->vchans[i].sg_tcd_pool);
> +             }
> +             rte_free(ed->vchans);
> +             ed->vchans = NULL;
> +     }
> +
> +     ed->nb_vchans = 0;
> +
> +     return 0;
> +}
> +
>  static const struct rte_dma_dev_ops imx_edma5_ops = {
>       .dev_info_get   = imx_edma5_info_get,
> +     .dev_configure  = imx_edma5_configure,
> +     .dev_start      = imx_edma5_start,
> +     .dev_stop       = imx_edma5_stop,
> +     .dev_close      = imx_edma5_close,
> +
> +     .vchan_setup    = imx_edma5_vchan_setup,
>  };
>  
>  static int

with above fixed,
Acked-by: Chengwen Feng <[email protected]>


Reply via email to