This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit a15117f0a0f876e1be59c3cfef1f307aa76e8f34 Author: Jorge Guzman <[email protected]> AuthorDate: Sun Oct 25 01:50:48 2026 -0300 arch/arm/gd32w51x: read the QSPI through the DMA Add CONFIG_GD32W51X_QSPI_DMA: the data phase of the indirect reads of CONFIG_GD32W51X_QSPI_DMATHRESHOLD bytes or more is moved by DMA1 channel 0, request 5 (QSPI_STAT DMAEN), and the thread sleeps until the end of the transfer instead of polling the FIFO. The user manual also lists DMA1 channel 1 for the QSPI requests, but that channel gets none on the GD32W515. The littlefs configuration of the GD-xD-W515-EVAL enables it: data read through the DMA matches the polled reads, and reading a 256 KB file from /flash takes 60 ms instead of 80 ms. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Jorge Guzman <[email protected]> --- .../arm/gd32w51x/boards/gd-xd-w515-eval/index.rst | 3 +- arch/arm/src/gd32w51x/Kconfig | 17 +++ arch/arm/src/gd32w51x/gd32w51x_qspi.c | 114 ++++++++++++++++++++- arch/arm/src/gd32w51x/hardware/gd32w51x_dma.h | 7 ++ arch/arm/src/gd32w51x/hardware/gd32w51x_qspi.h | 1 + .../gd-xd-w515-eval/configs/littlefs/defconfig | 2 + 6 files changed, 138 insertions(+), 6 deletions(-) diff --git a/Documentation/platforms/arm/gd32w51x/boards/gd-xd-w515-eval/index.rst b/Documentation/platforms/arm/gd32w51x/boards/gd-xd-w515-eval/index.rst index cadd028a6e7..338fd3bd06e 100644 --- a/Documentation/platforms/arm/gd32w51x/boards/gd-xd-w515-eval/index.rst +++ b/Documentation/platforms/arm/gd32w51x/boards/gd-xd-w515-eval/index.rst @@ -250,7 +250,8 @@ reboot: - ``/data``: the last 128 KB of the internal flash (``/dev/gd32flash``). - ``/flash``: the 16 MB QSPI NOR flash (``/dev/qspiflash``, GD25 driver in - QSPI mode). + QSPI mode). Reads of 64 bytes or more go through DMA1 channel 0 + (``CONFIG_GD32W51X_QSPI_DMA``): 256 KB in 60 ms instead of 80 ms. :: diff --git a/arch/arm/src/gd32w51x/Kconfig b/arch/arm/src/gd32w51x/Kconfig index 0614fbf3ed3..7f11e7707fc 100644 --- a/arch/arm/src/gd32w51x/Kconfig +++ b/arch/arm/src/gd32w51x/Kconfig @@ -123,6 +123,23 @@ config GD32W51X_QSPI is a secure peripheral: its pins are marked secure when it is initialized. +config GD32W51X_QSPI_DMA + bool "QSPI DMA reads" + default n + depends on GD32W51X_QSPI && GD32W51X_DMA + ---help--- + Move the data of the longer reads with DMA1 channel 0 instead of + polling the FIFO. The calling thread sleeps until the transfer + ends. + +config GD32W51X_QSPI_DMATHRESHOLD + int "QSPI DMA threshold (bytes)" + default 64 + depends on GD32W51X_QSPI_DMA + ---help--- + Reads shorter than this are done by polling: setting up DMA costs + more than moving a few bytes. + config GD32W51X_TSI bool "TSI" default n diff --git a/arch/arm/src/gd32w51x/gd32w51x_qspi.c b/arch/arm/src/gd32w51x/gd32w51x_qspi.c index 2e5f3c96502..7dfd034edd1 100644 --- a/arch/arm/src/gd32w51x/gd32w51x_qspi.c +++ b/arch/arm/src/gd32w51x/gd32w51x_qspi.c @@ -35,6 +35,7 @@ #include <nuttx/arch.h> #include <nuttx/kmalloc.h> #include <nuttx/mutex.h> +#include <nuttx/semaphore.h> #include <nuttx/spi/qspi.h> #include <arch/board/board.h> @@ -43,6 +44,7 @@ #include "chip.h" #include "gd32w51x.h" #include "gd32w51x_clockconfig.h" +#include "gd32w51x_dma.h" #include "gd32w51x_gpio.h" #include "gd32w51x_qspi.h" #include "hardware/gd32w51x_qspi.h" @@ -69,6 +71,10 @@ #define QSPI_TIMEOUT 0x00ffffff +/* Longest wait for a DMA read: 64 KB at the slowest useful clock */ + +#define QSPI_DMA_TIMEOUT MSEC2TICK(1000) + /**************************************************************************** * Private Types ****************************************************************************/ @@ -81,6 +87,11 @@ struct gd32_qspidev_s uint32_t actual; /* Actual clock frequency */ uint8_t mode; /* Mode 0 or 3 */ bool initialized; +#ifdef CONFIG_GD32W51X_QSPI_DMA + DMA_HANDLE rxdma; /* DMA channel of the reads */ + sem_t dmawait; /* Wait for the end of a DMA read */ + volatile uint16_t dmaresult; /* DMA status, non-zero when done */ +#endif }; /**************************************************************************** @@ -122,6 +133,9 @@ static struct gd32_qspidev_s g_qspi0dev = .ops = &g_qspi_ops, }, .lock = NXMUTEX_INITIALIZER, +#ifdef CONFIG_GD32W51X_QSPI_DMA + .dmawait = SEM_INITIALIZER(0), +#endif }; /**************************************************************************** @@ -183,6 +197,73 @@ static int qspi_wait_status(uint32_t flags) return OK; } +#ifdef CONFIG_GD32W51X_QSPI_DMA +/**************************************************************************** + * Name: qspi_dmacallback + ****************************************************************************/ + +static void qspi_dmacallback(DMA_HANDLE handle, uint16_t isr, void *arg) +{ + struct gd32_qspidev_s *priv = (struct gd32_qspidev_s *)arg; + + /* The status is OR'ed with 0x0080 to assure that it is non-zero */ + + priv->dmaresult = isr | 0x0080; + nxsem_post(&priv->dmawait); +} + +/**************************************************************************** + * Name: qspi_dmaread + * + * Description: + * Move the data phase of an indirect read with the DMA. TCFG has been + * written; the transfer starts when the address is written. + * + ****************************************************************************/ + +static int qspi_dmaread(struct gd32_qspidev_s *priv, uint32_t addr, + uint8_t *buffer, size_t buflen) +{ + struct gd32_dma_config_s cfg; + int ret; + + cfg.periph_addr = GD32W51X_QSPI_DATA; + cfg.memory0_addr = (uint32_t)buffer; + cfg.number = buflen; + cfg.periph_inc = DMA_PERIPH_INCREASE_DISABLE; + cfg.memory_inc = DMA_MEMORY_INCREASE_ENABLE; + cfg.periph_memory_width = DMA_WIDTH_8BITS_SELECT; + cfg.circular_mode = DMA_CIRCULAR_MODE_DISABLE; + cfg.direction = DMA_PERIPH_TO_MEMORY; + cfg.priority = DMA_PRIO_HIGH_SELECT; + + priv->dmaresult = 0; + gd32_dma_setup(priv->rxdma, &cfg, GD32_DMA_SINGLE_DATA_MODE); + gd32_dma_start(priv->rxdma, qspi_dmacallback, priv, + DMA_CHXCTL_FTFIE | DMA_CHXCTL_TAEIE); + + modifyreg32(GD32W51X_QSPI_STAT, 0, QSPI_STAT_DMAEN); + putreg32(addr, GD32W51X_QSPI_ADDR); + + do + { + ret = nxsem_tickwait_uninterruptible(&priv->dmawait, + QSPI_DMA_TIMEOUT); + } + while (ret == OK && priv->dmaresult == 0); + + modifyreg32(GD32W51X_QSPI_STAT, QSPI_STAT_DMAEN, 0); + gd32_dma_stop(priv->rxdma); + + if (ret == OK && (priv->dmaresult & DMA_INTF_TAEIF) != 0) + { + ret = -EIO; + } + + return ret; +} +#endif + /**************************************************************************** * Name: qspi_transfer * @@ -193,8 +274,9 @@ static int qspi_wait_status(uint32_t flags) * ****************************************************************************/ -static int qspi_transfer(uint32_t tcfg, bool hasaddr, uint32_t addr, - uint8_t *buffer, size_t buflen, bool read) +static int qspi_transfer(struct gd32_qspidev_s *priv, uint32_t tcfg, + bool hasaddr, uint32_t addr, uint8_t *buffer, + size_t buflen, bool read) { size_t i; int ret; @@ -217,6 +299,21 @@ static int qspi_transfer(uint32_t tcfg, bool hasaddr, uint32_t addr, QSPI_TCFG_FMOD_SHIFT; putreg32(tcfg, GD32W51X_QSPI_TCFG); +#ifdef CONFIG_GD32W51X_QSPI_DMA + if (read && hasaddr && buflen >= CONFIG_GD32W51X_QSPI_DMATHRESHOLD && + !up_interrupt_context()) + { + ret = qspi_dmaread(priv, addr, buffer, buflen); + if (ret < 0) + { + goto errout; + } + + hasaddr = false; + buflen = 0; + } +#endif + if (hasaddr) { putreg32(addr, GD32W51X_QSPI_ADDR); @@ -375,7 +472,8 @@ static int qspi_command(struct qspi_dev_s *dev, tcfg |= lines << QSPI_TCFG_DATAMOD_SHIFT; } - return qspi_transfer(tcfg, QSPICMD_ISADDRESS(cmdinfo->flags), + return qspi_transfer((struct gd32_qspidev_s *)dev, tcfg, + QSPICMD_ISADDRESS(cmdinfo->flags), cmdinfo->addr, cmdinfo->buffer, QSPICMD_ISDATA(cmdinfo->flags) ? cmdinfo->buflen : 0, !QSPICMD_ISWRITE(cmdinfo->flags)); @@ -425,8 +523,9 @@ static int qspi_memory(struct qspi_dev_s *dev, QSPI_TCFG_DUMYC_MASK) | (dlines << QSPI_TCFG_DATAMOD_SHIFT); - return qspi_transfer(tcfg, true, meminfo->addr, meminfo->buffer, - meminfo->buflen, QSPIMEM_ISREAD(meminfo->flags)); + return qspi_transfer((struct gd32_qspidev_s *)dev, tcfg, true, + meminfo->addr, meminfo->buffer, meminfo->buflen, + QSPIMEM_ISREAD(meminfo->flags)); } /**************************************************************************** @@ -505,6 +604,11 @@ struct qspi_dev_s *gd32_qspi_initialize(int intf) (QSPI_FMSZ << QSPI_DCFG_FMSZ_SHIFT), GD32W51X_QSPI_DCFG); modifyreg32(GD32W51X_QSPI_CTL, 0, QSPI_CTL_QSPIEN); +#ifdef CONFIG_GD32W51X_QSPI_DMA + priv->rxdma = gd32_dma_channel_alloc(DMA_REQ_QSPI); + DEBUGASSERT(priv->rxdma != NULL); +#endif + priv->frequency = 0; priv->mode = QSPIDEV_MODE0; priv->initialized = true; diff --git a/arch/arm/src/gd32w51x/hardware/gd32w51x_dma.h b/arch/arm/src/gd32w51x/hardware/gd32w51x_dma.h index 96ee112e158..4dca259dec1 100644 --- a/arch/arm/src/gd32w51x/hardware/gd32w51x_dma.h +++ b/arch/arm/src/gd32w51x/hardware/gd32w51x_dma.h @@ -428,4 +428,11 @@ #define DMA_REQ_SPI1_TX GD32_DMA_REQUEST(GD32_DMA_SUBPERI0, \ GD32_DMA_CH4) +/* The user manual also lists DMA1 channel 1 for the QSPI, but that channel + * gets no request on the GD32W515. + */ + +#define DMA_REQ_QSPI GD32_DMA_REQUEST(GD32_DMA_SUBPERI5, \ + GD32_DMA1_CH(0)) + #endif /* __ARCH_ARM_SRC_GD32W51X_HARDWARE_GD32W51X_DMA_H */ diff --git a/arch/arm/src/gd32w51x/hardware/gd32w51x_qspi.h b/arch/arm/src/gd32w51x/hardware/gd32w51x_qspi.h index 1cdc3382a48..fe9a0214892 100644 --- a/arch/arm/src/gd32w51x/hardware/gd32w51x_qspi.h +++ b/arch/arm/src/gd32w51x/hardware/gd32w51x_qspi.h @@ -86,6 +86,7 @@ #define QSPI_STAT_SM (1 << 3) /* Status match */ #define QSPI_STAT_TMOUT (1 << 4) /* Timeout */ #define QSPI_STAT_WS (1 << 5) /* Wrong start sequence */ +#define QSPI_STAT_DMAEN (1 << 16) /* DMA enable */ #define QSPI_STATC_TERRC (1 << 0) #define QSPI_STATC_TCC (1 << 1) diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/configs/littlefs/defconfig b/boards/arm/gd32w51x/gd-xd-w515-eval/configs/littlefs/defconfig index 6f179fa7cc1..a7657712596 100644 --- a/boards/arm/gd32w51x/gd-xd-w515-eval/configs/littlefs/defconfig +++ b/boards/arm/gd32w51x/gd-xd-w515-eval/configs/littlefs/defconfig @@ -24,8 +24,10 @@ CONFIG_FS_LITTLEFS=y CONFIG_FS_LITTLEFS_PROGRAM_SIZE_FACTOR=1 CONFIG_FS_PROCFS=y CONFIG_GD25_QSPI=y +CONFIG_GD32W51X_DMA=y CONFIG_GD32W51X_PROGMEM=y CONFIG_GD32W51X_QSPI=y +CONFIG_GD32W51X_QSPI_DMA=y CONFIG_GD32W51X_RNG=y CONFIG_GD32W51X_UART2=y CONFIG_IDLETHREAD_STACKSIZE=2048
