From: Gagandeep Singh <g.singh@nxp.com>
To: dev@dpdk.org
Cc: hemant.agrawal@nxp.com, Gagandeep Singh <g.singh@nxp.com>,
Prashant Gupta <prashant.gupta_3@nxp.com>
Subject: [PATCH v3 3/5] dma/imx_edma5: add device configuration
Date: Tue, 11 Aug 2026 16:19:31 +0530 [thread overview]
Message-ID: <20260811104933.3843522-4-g.singh@nxp.com> (raw)
In-Reply-To: <20260811104933.3843522-1-g.singh@nxp.com>
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 <g.singh@nxp.com>
Signed-off-by: Prashant Gupta <prashant.gupta_3@nxp.com>
---
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;
+ }
+
+ 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;
+ }
+
+ if (conf->direction != RTE_DMA_DIR_MEM_TO_MEM) {
+ IMX_EDMA5_LOG(ERR, "Only mem-to-mem direction supported");
+ return -EINVAL;
+ }
+
+ 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;
+ }
+
+ 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);
+ 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
--
2.25.1
next prev parent reply other threads:[~2026-08-11 10:50 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 ` [PATCH 3/4] dma/imx_edma5: add data path Gagandeep Singh
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 ` Gagandeep Singh [this message]
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=20260811104933.3843522-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.