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 b9d1d6a83480d9d4d12135df52db86717633bff0
Author: Jorge Guzman <[email protected]>
AuthorDate: Sun Oct 25 22:23:12 2026 -0300

    arch/arm/gd32w51x: map the QSPI flash in memory and run code from it
    
    - qspi: add gd32_qspi_enter_memorymapped() and
      gd32_qspi_exit_memorymapped(), as the STM32 QSPI drivers do: in the
      memory-mapped mode the memory is read in place through the 0x90000000
      window with the given read transfer, optionally releasing CS after a
      timeout.  The QSPI command and memory operations fail with -EBUSY
      while the mode is on, and the clock and the SPI mode cannot change.
    - boards/gd-xd-w515-eval: with CONFIG_GD_XD_W515_EVAL_QSPI_MEMMAP the
      QSPI flash is set up by the GD25 driver, then mapped read-only at
      0x90000000 instead of being registered as /dev/qspiflash.
    - boards/gd-xd-w515-eval: with CONFIG_GD_XD_W515_EVAL_QSPI_XIP,
      functions and data marked locate_code(".qspixip") and
      locate_data(".qspixip") go to the .qspixip section, used in place
      from the last MiB of the QSPI flash.
    
    Code run from the QSPI window hangs the bus as soon as it also reads
    the window, its literal pool or its data, while through the remap of
    the instruction cache (16 MiB at 0x10000000 to the window, master port
    of the external memories, incrementing bursts) code and data both work.
    So the section is linked at 0x10f00000, and stored after the image in
    the internal flash: at boot the board programs it to the QSPI flash
    when it differs, maps the flash in memory, remaps the window in the
    instruction cache and runs a self-test from it.
    
    Tested on the GD-xD-W515-EVAL: "xd 0x90000000" shows the LittleFS
    superblock of the flash, and a CRC-32 function run at 0x10f00000 over
    a string read in place gives the CRC of the copy in the internal flash,
    after programming and after a reboot.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Jorge Guzman <[email protected]>
---
 .../arm/gd32w51x/boards/gd-xd-w515-eval/index.rst  |  31 +++
 arch/arm/src/gd32w51x/gd32w51x_qspi.c              | 135 +++++++++-
 arch/arm/src/gd32w51x/gd32w51x_qspi.h              |  41 +++
 arch/arm/src/gd32w51x/hardware/gd32w51x_qspi.h     |   4 +
 boards/arm/gd32w51x/gd-xd-w515-eval/Kconfig        |  27 ++
 .../arm/gd32w51x/gd-xd-w515-eval/scripts/ld.script |  28 +-
 .../gd32w51x/gd-xd-w515-eval/src/CMakeLists.txt    |   4 +
 boards/arm/gd32w51x/gd-xd-w515-eval/src/Makefile   |   4 +
 .../gd32w51x/gd-xd-w515-eval/src/gd-xd-w515-eval.h |  28 ++
 .../gd32w51x/gd-xd-w515-eval/src/gd32_bringup.c    |  40 ++-
 .../gd32w51x/gd-xd-w515-eval/src/gd32_qspixip.c    | 283 +++++++++++++++++++++
 11 files changed, 615 insertions(+), 10 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 82d48b06b2f..829c4fdad94 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
@@ -263,6 +263,37 @@ reboot:
     nsh> cat /flash/hello.txt
     hello
 
+With ``CONFIG_GD_XD_W515_EVAL_QSPI_MEMMAP`` the QSPI flash is not registered:
+it is mapped read-only at 0x90000000 (memory-mapped mode,
+``gd32_qspi_enter_memorymapped()``), so that constant data stored in it can
+be read in place.
+
+::
+
+    nsh> xd 0x90000000 16
+    Hex dump:
+    0000: cc 00 00 00 f0 0f ff f7 6c 69 74 74 6c 65 66 73 ........littlefs
+
+``CONFIG_GD_XD_W515_EVAL_QSPI_XIP`` adds code and constant data run in place
+from the last MiB of the QSPI flash: functions and data marked
+``locate_code(".qspixip")`` and ``locate_data(".qspixip")`` go to the
+``.qspixip`` section.  It is linked at 0x10f00000, which the instruction
+cache remaps to the window (0x90f00000), and stored after the image in the
+internal flash: the board programs it to the QSPI flash at boot when it
+differs, so ``nuttx.bin`` is still programmed as usual.  A self-test runs a
+CRC-32 function from the QSPI flash over a string read in place::
+
+    QSPI XIP section programmed (176 bytes)
+    QSPI XIP: code at 0x10f00001, data at 0x10f0002c: crc 21821ab6 OK
+
+.. note::
+   Code run from the window at 0x90000000 itself hangs the bus as soon as
+   it also reads the window (its literal pool, its data); through the
+   instruction cache remap both work.  Nothing may access the window
+   directly while code runs from the QSPI flash.  After such a hang the
+   debugger only connects under reset
+   (``reset_config srst_only srst_nogate connect_assert_srst``).
+
 pm
 --
 
diff --git a/arch/arm/src/gd32w51x/gd32w51x_qspi.c 
b/arch/arm/src/gd32w51x/gd32w51x_qspi.c
index 7dfd034edd1..650db3759d4 100644
--- a/arch/arm/src/gd32w51x/gd32w51x_qspi.c
+++ b/arch/arm/src/gd32w51x/gd32w51x_qspi.c
@@ -87,6 +87,7 @@ struct gd32_qspidev_s
   uint32_t actual;              /* Actual clock frequency */
   uint8_t mode;                 /* Mode 0 or 3 */
   bool initialized;
+  bool memmap;                  /* In memory-mapped mode */
 #ifdef CONFIG_GD32W51X_QSPI_DMA
   DMA_HANDLE rxdma;             /* DMA channel of the reads */
   sem_t dmawait;                /* Wait for the end of a DMA read */
@@ -385,6 +386,13 @@ static uint32_t qspi_setfrequency(struct qspi_dev_s *dev,
       return 0;
     }
 
+  /* The clock cannot change in the memory-mapped mode */
+
+  if (priv->memmap)
+    {
+      return priv->actual;
+    }
+
   if (frequency == priv->frequency)
     {
       return priv->actual;
@@ -425,7 +433,7 @@ static void qspi_setmode(struct qspi_dev_s *dev, enum 
qspi_mode_e mode)
    * level while idle is selected, the sampling edge is fixed.
    */
 
-  if (mode != priv->mode && qspi_wait_idle() == OK)
+  if (mode != priv->mode && !priv->memmap && qspi_wait_idle() == OK)
     {
       modifyreg32(GD32W51X_QSPI_DCFG, QSPI_DCFG_CKMOD,
                   mode == QSPIDEV_MODE3 ? QSPI_DCFG_CKMOD : 0);
@@ -454,6 +462,15 @@ static int qspi_command(struct qspi_dev_s *dev,
   uint32_t tcfg;
   uint32_t lines;
 
+  /* Reject commands issued while in memory-mapped mode, which they would
+   * cancel.  The memory-mapped mode must be left first.
+   */
+
+  if (((struct gd32_qspidev_s *)dev)->memmap)
+    {
+      return -EBUSY;
+    }
+
   lines = QSPICMD_ISIQUAD(cmdinfo->flags) ? QSPI_LINES_4 :
           QSPICMD_ISIDUAL(cmdinfo->flags) ? QSPI_LINES_2 : QSPI_LINES_1;
 
@@ -480,16 +497,19 @@ static int qspi_command(struct qspi_dev_s *dev,
 }
 
 /****************************************************************************
- * Name: qspi_memory
+ * Name: qspi_memtcfg
+ *
+ * Description:
+ *   Return the transfer configuration of a memory read or write, without
+ *   the functional mode.
+ *
  ****************************************************************************/
 
-static int qspi_memory(struct qspi_dev_s *dev,
-                       struct qspi_meminfo_s *meminfo)
+static uint32_t qspi_memtcfg(const struct qspi_meminfo_s *meminfo)
 {
   uint32_t ilines;
   uint32_t alines;
   uint32_t dlines;
-  uint32_t tcfg;
 
   ilines = (meminfo->flags & QSPIMEM_IQUAD) != 0 ? QSPI_LINES_4 :
            (meminfo->flags & QSPIMEM_IDUAL) != 0 ? QSPI_LINES_2 :
@@ -513,15 +533,37 @@ static int qspi_memory(struct qspi_dev_s *dev,
     }
 
   DEBUGASSERT(meminfo->addrlen >= 1 && meminfo->addrlen <= 4);
-  DEBUGASSERT(meminfo->buffer != NULL && meminfo->buflen > 0);
 
-  tcfg = (meminfo->cmd & 0xff) |
+  return (meminfo->cmd & 0xff) |
          (ilines << QSPI_TCFG_IMOD_SHIFT) |
          (alines << QSPI_TCFG_ADDRMOD_SHIFT) |
          ((meminfo->addrlen - 1) << QSPI_TCFG_ADDRSZ_SHIFT) |
          ((meminfo->dummies << QSPI_TCFG_DUMYC_SHIFT) &
           QSPI_TCFG_DUMYC_MASK) |
          (dlines << QSPI_TCFG_DATAMOD_SHIFT);
+}
+
+/****************************************************************************
+ * Name: qspi_memory
+ ****************************************************************************/
+
+static int qspi_memory(struct qspi_dev_s *dev,
+                       struct qspi_meminfo_s *meminfo)
+{
+  uint32_t tcfg;
+
+  /* Reject transfers issued while in memory-mapped mode, which they would
+   * cancel.  The memory-mapped mode must be left first.
+   */
+
+  if (((struct gd32_qspidev_s *)dev)->memmap)
+    {
+      return -EBUSY;
+    }
+
+  DEBUGASSERT(meminfo->buffer != NULL && meminfo->buflen > 0);
+
+  tcfg = qspi_memtcfg(meminfo);
 
   return qspi_transfer((struct gd32_qspidev_s *)dev, tcfg, true,
                        meminfo->addr, meminfo->buffer, meminfo->buflen,
@@ -617,4 +659,83 @@ struct qspi_dev_s *gd32_qspi_initialize(int intf)
   return &priv->qspi;
 }
 
+/****************************************************************************
+ * Name: gd32_qspi_enter_memorymapped
+ *
+ * Description:
+ *   Put the QSPI controller into memory-mapped mode: the memory is read in
+ *   place through the GD32W51X_QSPIMEM_BASE window.  The QSPI command and
+ *   memory operations fail with -EBUSY until gd32_qspi_exit_memorymapped().
+ *
+ * Input Parameters:
+ *   dev     - QSPI device
+ *   meminfo - The read transfer used to fetch the memory
+ *   lpto    - Number of SCK cycles without access after which CS is
+ *             released, or 0 to keep it asserted
+ *
+ ****************************************************************************/
+
+void gd32_qspi_enter_memorymapped(struct qspi_dev_s *dev,
+                                  const struct qspi_meminfo_s *meminfo,
+                                  uint32_t lpto)
+{
+  struct gd32_qspidev_s *priv = (struct gd32_qspidev_s *)dev;
+
+  qspi_lock(dev, true);
+
+  if (!priv->memmap && qspi_wait_idle() == OK)
+    {
+      putreg32(QSPI_STATC_TERRC | QSPI_STATC_TCC | QSPI_STATC_SMC |
+               QSPI_STATC_TMOUTC | QSPI_STATC_WSC, GD32W51X_QSPI_STATC);
+
+      if (lpto > 0)
+        {
+          putreg32(lpto, GD32W51X_QSPI_TMOUT);
+          modifyreg32(GD32W51X_QSPI_CTL, 0, QSPI_CTL_TMOUTEN);
+        }
+      else
+        {
+          modifyreg32(GD32W51X_QSPI_CTL, QSPI_CTL_TMOUTEN, 0);
+        }
+
+      putreg32(qspi_memtcfg(meminfo) |
+               (QSPI_FMOD_MEMORY_MAPPED << QSPI_TCFG_FMOD_SHIFT),
+               GD32W51X_QSPI_TCFG);
+      priv->memmap = true;
+    }
+
+  qspi_lock(dev, false);
+}
+
+/****************************************************************************
+ * Name: gd32_qspi_exit_memorymapped
+ *
+ * Description:
+ *   Take the QSPI controller out of memory-mapped mode.
+ *
+ * Input Parameters:
+ *   dev - QSPI device
+ *
+ ****************************************************************************/
+
+void gd32_qspi_exit_memorymapped(struct qspi_dev_s *dev)
+{
+  struct gd32_qspidev_s *priv = (struct gd32_qspidev_s *)dev;
+
+  qspi_lock(dev, true);
+
+  if (priv->memmap)
+    {
+      /* The controller keeps CS asserted to prefetch: abort it */
+
+      modifyreg32(GD32W51X_QSPI_CTL, 0, QSPI_CTL_ABORT);
+      qspi_wait_idle();
+      putreg32(QSPI_STATC_TERRC | QSPI_STATC_TCC | QSPI_STATC_SMC |
+               QSPI_STATC_TMOUTC | QSPI_STATC_WSC, GD32W51X_QSPI_STATC);
+      priv->memmap = false;
+    }
+
+  qspi_lock(dev, false);
+}
+
 #endif /* CONFIG_GD32W51X_QSPI */
diff --git a/arch/arm/src/gd32w51x/gd32w51x_qspi.h 
b/arch/arm/src/gd32w51x/gd32w51x_qspi.h
index e78f6925f06..5fb7ef56835 100644
--- a/arch/arm/src/gd32w51x/gd32w51x_qspi.h
+++ b/arch/arm/src/gd32w51x/gd32w51x_qspi.h
@@ -55,5 +55,46 @@
 
 struct qspi_dev_s *gd32_qspi_initialize(int intf);
 
+/****************************************************************************
+ * Name: gd32_qspi_enter_memorymapped
+ *
+ * Description:
+ *   Put the QSPI controller into memory-mapped mode: the memory is read in
+ *   place through the GD32W51X_QSPIMEM_BASE window (0x90000000).  The QSPI
+ *   command and memory operations fail with -EBUSY until
+ *   gd32_qspi_exit_memorymapped().  Only data can be used in place: code
+ *   run from the window hangs the bus.
+ *
+ * Input Parameters:
+ *   dev     - QSPI device
+ *   meminfo - The read transfer used to fetch the memory
+ *   lpto    - Number of SCK cycles without access after which CS is
+ *             released, or 0 to keep it asserted
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void gd32_qspi_enter_memorymapped(struct qspi_dev_s *dev,
+                                  const struct qspi_meminfo_s *meminfo,
+                                  uint32_t lpto);
+
+/****************************************************************************
+ * Name: gd32_qspi_exit_memorymapped
+ *
+ * Description:
+ *   Take the QSPI controller out of memory-mapped mode.
+ *
+ * Input Parameters:
+ *   dev - QSPI device
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void gd32_qspi_exit_memorymapped(struct qspi_dev_s *dev);
+
 #endif /* __ASSEMBLY__ */
 #endif /* __ARCH_ARM_SRC_GD32W51X_GD32W51X_QSPI_H */
diff --git a/arch/arm/src/gd32w51x/hardware/gd32w51x_qspi.h 
b/arch/arm/src/gd32w51x/hardware/gd32w51x_qspi.h
index fe9a0214892..683800da25d 100644
--- a/arch/arm/src/gd32w51x/hardware/gd32w51x_qspi.h
+++ b/arch/arm/src/gd32w51x/hardware/gd32w51x_qspi.h
@@ -44,6 +44,7 @@
 #define GD32W51X_QSPI_ADDR_OFFSET    0x0018  /* Address */
 #define GD32W51X_QSPI_ALTE_OFFSET    0x001c  /* Alternate bytes */
 #define GD32W51X_QSPI_DATA_OFFSET    0x0020  /* Data */
+#define GD32W51X_QSPI_TMOUT_OFFSET   0x0030  /* Memory-mapped timeout */
 
 /* Register addresses */
 
@@ -57,11 +58,14 @@
 #define GD32W51X_QSPI_TCFG    (GD32W51X_QSPI_BASE + GD32W51X_QSPI_TCFG_OFFSET)
 #define GD32W51X_QSPI_ADDR    (GD32W51X_QSPI_BASE + GD32W51X_QSPI_ADDR_OFFSET)
 #define GD32W51X_QSPI_DATA    (GD32W51X_QSPI_BASE + GD32W51X_QSPI_DATA_OFFSET)
+#define GD32W51X_QSPI_TMOUT \
+  (GD32W51X_QSPI_BASE + GD32W51X_QSPI_TMOUT_OFFSET)
 
 /* QSPI_CTL */
 
 #define QSPI_CTL_QSPIEN              (1 << 0)   /* Enable */
 #define QSPI_CTL_ABORT               (1 << 1)   /* Abort request */
+#define QSPI_CTL_TMOUTEN             (1 << 3)   /* Memory-mapped timeout */
 #define QSPI_CTL_SSAMPLE_SHIFT       (4)        /* Sample shift */
 #define QSPI_CTL_SSAMPLE_MASK        (3 << QSPI_CTL_SSAMPLE_SHIFT)
 #define QSPI_CTL_FTL_SHIFT           (8)        /* FIFO threshold (n - 1) */
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/Kconfig 
b/boards/arm/gd32w51x/gd-xd-w515-eval/Kconfig
index d9a8d7f9028..e7697bfe720 100644
--- a/boards/arm/gd32w51x/gd-xd-w515-eval/Kconfig
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/Kconfig
@@ -21,4 +21,31 @@
 # 
##############################################################################
 
 if ARCH_BOARD_GD_XD_W515_EVAL
+
+config GD_XD_W515_EVAL_QSPI_MEMMAP
+       bool "QSPI flash in memory-mapped mode"
+       default n
+       depends on GD32W51X_QSPI && MTD_GD25 && GD25_QSPI
+       ---help---
+               Map the QSPI NOR flash read-only at 0x90000000 (memory-mapped
+               mode) instead of registering it as /dev/qspiflash: constant data
+               stored in the flash can then be read in place.
+
+config GD_XD_W515_EVAL_QSPI_XIP
+       bool "Code and data in place from the QSPI flash"
+       default n
+       depends on GD_XD_W515_EVAL_QSPI_MEMMAP
+       ---help---
+               Link the .qspixip section (locate_code(".qspixip") and
+               locate_data(".qspixip")) in the last MiB of the QSPI flash,
+               used in place through the instruction cache remap of the QSPI
+               window at 0x10000000.  The section is stored after the image in
+               the internal flash and programmed to the QSPI flash at boot when
+               it differs; a self-test runs code from it.
+
+               Code run from the window at 0x90000000 hangs the bus as soon as
+               it also reads the window (its literals, its data), while through
+               the remap both work: nothing may access the window directly
+               while code runs from the QSPI flash.
+
 endif
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/scripts/ld.script 
b/boards/arm/gd32w51x/gd-xd-w515-eval/scripts/ld.script
index c3e6df01a84..28390388f36 100644
--- a/boards/arm/gd32w51x/gd-xd-w515-eval/scripts/ld.script
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/scripts/ld.script
@@ -20,18 +20,29 @@
  *
  ****************************************************************************/
 
+#include <nuttx/config.h>
+
 /* The GD32W515PI has 2 MB of SIP flash and 448 KB of SRAM (SRAM0..SRAM3,
  * contiguous from 0x20000000).
  *
  * The image is linked at the non-secure alias of the flash (0x08000000).
  * With the TZEN option bit set the boot ROM starts it through the secure
  * alias (0x0c000000); see gd32w51x_start.c.
+ *
+ * With CONFIG_GD_XD_W515_EVAL_QSPI_XIP the .qspixip section holds code and
+ * constant data used in place from the last MiB of the QSPI flash, through
+ * the instruction cache remap of the QSPI window (0x90f00000) at
+ * 0x10f00000.  It is stored after the image in the internal flash, and the
+ * board programs it to the QSPI flash at boot; see gd32_qspixip.c.
  */
 
 MEMORY
 {
-  flash (rx) : ORIGIN = 0x08000000, LENGTH = 2048K
-  sram (rwx) : ORIGIN = 0x20000000, LENGTH = 448K
+  flash (rx)   : ORIGIN = 0x08000000, LENGTH = 2048K
+  sram (rwx)   : ORIGIN = 0x20000000, LENGTH = 448K
+#ifdef CONFIG_GD_XD_W515_EVAL_QSPI_XIP
+  qspixip (rx) : ORIGIN = 0x10f00000, LENGTH = 1024K
+#endif
 }
 
 OUTPUT_ARCH(arm)
@@ -89,6 +100,19 @@ SECTIONS
       _edata = ABSOLUTE(.);
     } > sram AT > flash
 
+#ifdef CONFIG_GD_XD_W515_EVAL_QSPI_XIP
+  .qspixip :
+    {
+      . = ALIGN(4);
+      _sqspixip = ABSOLUTE(.);
+      KEEP(*(.qspixip .qspixip.*))
+      . = ALIGN(4);
+      _eqspixip = ABSOLUTE(.);
+    } > qspixip AT > flash
+
+  _lqspixip = LOADADDR(.qspixip);
+#endif
+
   .bss :
     {
       _sbss = ABSOLUTE(.);
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/src/CMakeLists.txt 
b/boards/arm/gd32w51x/gd-xd-w515-eval/src/CMakeLists.txt
index 8770eeeb3e3..aee936676f7 100644
--- a/boards/arm/gd32w51x/gd-xd-w515-eval/src/CMakeLists.txt
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/src/CMakeLists.txt
@@ -50,6 +50,10 @@ if(CONFIG_BOARDCTL_RESET)
   list(APPEND SRCS gd32_reset.c)
 endif()
 
+if(CONFIG_GD_XD_W515_EVAL_QSPI_XIP)
+  list(APPEND SRCS gd32_qspixip.c)
+endif()
+
 target_sources(board PRIVATE ${SRCS})
 
 set_property(GLOBAL PROPERTY LD_SCRIPT "${NUTTX_BOARD_DIR}/scripts/ld.script")
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/src/Makefile 
b/boards/arm/gd32w51x/gd-xd-w515-eval/src/Makefile
index f7e47d24d57..e84b5078a4f 100644
--- a/boards/arm/gd32w51x/gd-xd-w515-eval/src/Makefile
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/src/Makefile
@@ -52,4 +52,8 @@ ifeq ($(CONFIG_BOARDCTL_RESET),y)
 CSRCS += gd32_reset.c
 endif
 
+ifeq ($(CONFIG_GD_XD_W515_EVAL_QSPI_XIP),y)
+CSRCS += gd32_qspixip.c
+endif
+
 include $(TOPDIR)/boards/Board.mk
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd-xd-w515-eval.h 
b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd-xd-w515-eval.h
index 1c17b32c8e1..b6ec64076cf 100644
--- a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd-xd-w515-eval.h
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd-xd-w515-eval.h
@@ -81,5 +81,33 @@ void gd32_spidev_initialize(void);
 int gd32_gpio_initialize(void);
 #endif
 
+#ifdef CONFIG_GD_XD_W515_EVAL_QSPI_XIP
+struct mtd_dev_s;
+
+/****************************************************************************
+ * Name: gd32_qspixip_install
+ *
+ * Description:
+ *   Program the .qspixip section to the last MiB of the QSPI flash, from
+ *   its copy in the internal flash, when it differs.  Called before the
+ *   flash is mapped in memory.
+ *
+ ****************************************************************************/
+
+int gd32_qspixip_install(struct mtd_dev_s *mtd);
+
+/****************************************************************************
+ * Name: gd32_qspixip_initialize
+ *
+ * Description:
+ *   Once the flash is mapped in memory, remap the QSPI window in the
+ *   instruction cache, where the .qspixip section is linked, and check
+ *   that code runs from it.
+ *
+ ****************************************************************************/
+
+int gd32_qspixip_initialize(void);
+#endif
+
 #endif /* __ASSEMBLY__ */
 #endif /* __BOARDS_ARM_GD32W51X_GD_XD_W515_EVAL_SRC_GD_XD_W515_EVAL_H */
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_bringup.c 
b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_bringup.c
index 7e17fea5d16..58ae7bf443f 100644
--- a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_bringup.c
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_bringup.c
@@ -339,7 +339,8 @@ int gd32_bringup(void)
 
 #if defined(CONFIG_GD32W51X_QSPI) && defined(CONFIG_MTD_GD25)
   /* The 16 MB QSPI NOR flash as /dev/qspiflash, mounted as LittleFS on
-   * /flash (formatted on the first boot).
+   * /flash (formatted on the first boot), or mapped at 0x90000000 with
+   * CONFIG_GD_XD_W515_EVAL_QSPI_MEMMAP.
    */
 
   qspi = gd32_qspi_initialize(0);
@@ -348,6 +349,42 @@ int gd32_bringup(void)
     {
       ferr("ERROR: failed to initialize the QSPI flash\n");
     }
+#ifdef CONFIG_GD_XD_W515_EVAL_QSPI_MEMMAP
+  else
+    {
+      /* Read the flash in place at 0x90000000 with the quad I/O read of
+       * the GD25 driver, which has set the memory up.
+       */
+
+      struct qspi_meminfo_s meminfo =
+      {
+        .flags   = QSPIMEM_READ | QSPIMEM_QUADIO,
+        .addrlen = 3,
+        .dummies = 6,
+        .cmd     = 0xeb,
+      };
+
+#ifdef CONFIG_GD_XD_W515_EVAL_QSPI_XIP
+      /* The code and data used in place, in the last MiB of the flash */
+
+      ret = gd32_qspixip_install(qmtd);
+      if (ret < 0)
+        {
+          ferr("ERROR: failed to program the QSPI XIP section: %d\n", ret);
+        }
+#endif
+
+      gd32_qspi_enter_memorymapped(qspi, &meminfo, 0);
+      finfo("QSPI flash mapped at %08x\n", GD32W51X_QSPIMEM_BASE);
+
+#ifdef CONFIG_GD_XD_W515_EVAL_QSPI_XIP
+      if (ret >= 0)
+        {
+          gd32_qspixip_initialize();
+        }
+#endif
+    }
+#else
   else
     {
       ret = register_mtddriver("/dev/qspiflash", qmtd, 0755, NULL);
@@ -367,6 +404,7 @@ int gd32_bringup(void)
         }
 #endif
     }
+#endif /* CONFIG_GD_XD_W515_EVAL_QSPI_MEMMAP */
 #endif
 
   UNUSED(ret);
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_qspixip.c 
b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_qspixip.c
new file mode 100644
index 00000000000..2352bee08c4
--- /dev/null
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_qspixip.c
@@ -0,0 +1,283 @@
+/****************************************************************************
+ * boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_qspixip.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* Code and constant data used in place from the QSPI flash.
+ *
+ * The .qspixip section is linked at 0x10f00000, which the instruction cache
+ * remaps to the QSPI window (0x90f00000, the last MiB of the flash), and
+ * stored after the image in the internal flash.  At boot it is programmed
+ * to the QSPI flash when it differs, then the window is remapped.
+ *
+ * Code run from the window itself hangs the bus as soon as it also reads
+ * the window (its literals, its data); through the remap, code and data
+ * both work.  Nothing may access the window directly while code runs from
+ * the QSPI flash.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/param.h>
+
+#include <nuttx/compiler.h>
+#include <nuttx/crc32.h>
+#include <nuttx/mtd/mtd.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "gd-xd-w515-eval.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Instruction cache: region 0 remaps 16 MiB at 0x10000000 to the QSPI
+ * window at 0x90000000, through the master port of the external memories
+ * (MSEL), with incrementing bursts (OBT).
+ */
+
+#define ICACHE_CTL              (GD32W51X_ICACHE_BASE + 0x0000)
+#define ICACHE_STAT             (GD32W51X_ICACHE_BASE + 0x0004)
+#define ICACHE_FC               (GD32W51X_ICACHE_BASE + 0x000c)
+#define ICACHE_CFG0             (GD32W51X_ICACHE_BASE + 0x0020)
+
+#define ICACHE_CTL_EN           (1 << 0)
+#define ICACHE_CTL_INVAL        (1 << 1)
+#define ICACHE_STAT_BUSY        (1 << 0)
+#define ICACHE_STAT_END         (1 << 1)
+#define ICACHE_FC_ENDC          (1 << 1)
+
+#define ICACHE_CFG_BADDR(a)     (((a) >> 21) & 0xff)
+#define ICACHE_CFG_SIZE_16M     (4 << 9)
+#define ICACHE_CFG_EN           (1 << 15)
+#define ICACHE_CFG_RADDR(a)     ((((a) >> 21) & 0x7ff) << 16)
+#define ICACHE_CFG_MSEL         (1 << 28)
+#define ICACHE_CFG_OBT          (1 << 31)
+
+#define QSPIXIP_ALIAS           0x10000000
+#define QSPIXIP_REMAP           (ICACHE_CFG_OBT | ICACHE_CFG_MSEL | \
+                                 ICACHE_CFG_RADDR(GD32W51X_QSPIMEM_BASE) | \
+                                 ICACHE_CFG_EN | ICACHE_CFG_SIZE_16M | \
+                                 ICACHE_CFG_BADDR(QSPIXIP_ALIAS))
+
+/* The section lives in the last MiB of the 16 MiB flash */
+
+#define QSPIXIP_SIZE            (1024 * 1024)
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Linker symbols: the section where it is used, and its copy */
+
+extern const uint8_t _sqspixip[];
+extern const uint8_t _eqspixip[];
+extern const uint8_t _lqspixip[];
+
+/* Data of the self-test, read in place */
+
+locate_data(".qspixip.rodata")
+static const char g_qspixip_text[] =
+  "NuttX: code and data run in place from the GD25Q128E QSPI NOR flash "
+  "of the GD-xD-W515-EVAL, through the instruction cache remap.";
+
+static uint8_t g_qspixip_block[256];
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: qspixip_crc32
+ *
+ * Description:
+ *   Run from the QSPI flash: the CRC-32 of crc32(), without its table.
+ *
+ ****************************************************************************/
+
+locate_code(".qspixip.text") noinline_function
+static uint32_t qspixip_crc32(const uint8_t *src, size_t len)
+{
+  uint32_t crc = 0;
+  int i;
+
+  while (len-- > 0)
+    {
+      crc ^= *src++;
+      for (i = 0; i < 8; i++)
+        {
+          crc = (crc >> 1) ^ (0xedb88320 & -(crc & 1));
+        }
+    }
+
+  return crc;
+}
+
+/****************************************************************************
+ * Name: qspixip_cache
+ *
+ * Description:
+ *   With the instruction cache disabled, set the remap region up (when
+ *   'remap'), invalidate the cache, then enable it again.
+ *
+ ****************************************************************************/
+
+static void qspixip_cache(bool remap)
+{
+  modifyreg32(ICACHE_CTL, ICACHE_CTL_EN, 0);
+  while ((getreg32(ICACHE_STAT) & ICACHE_STAT_BUSY) != 0)
+    {
+    }
+
+  if (remap)
+    {
+      putreg32(QSPIXIP_REMAP, ICACHE_CFG0);
+    }
+
+  putreg32(ICACHE_FC_ENDC, ICACHE_FC);
+  modifyreg32(ICACHE_CTL, 0, ICACHE_CTL_INVAL);
+  while ((getreg32(ICACHE_STAT) & ICACHE_STAT_END) == 0)
+    {
+    }
+
+  modifyreg32(ICACHE_CTL, 0, ICACHE_CTL_EN);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: gd32_qspixip_install
+ *
+ * Description:
+ *   Program the .qspixip section to the last MiB of the QSPI flash, from
+ *   its copy in the internal flash, when it differs.  Called before the
+ *   flash is mapped in memory.
+ *
+ ****************************************************************************/
+
+int gd32_qspixip_install(struct mtd_dev_s *mtd)
+{
+  struct mtd_geometry_s geo;
+  size_t size = _eqspixip - _sqspixip;
+  off_t first;
+  size_t nblocks;
+  size_t i;
+  size_t n;
+  bool same = true;
+  int ret;
+
+  ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)&geo);
+  if (ret < 0 || geo.blocksize > sizeof(g_qspixip_block) ||
+      size > QSPIXIP_SIZE)
+    {
+      return ret < 0 ? ret : -EINVAL;
+    }
+
+  first   = ((off_t)geo.neraseblocks * geo.erasesize - QSPIXIP_SIZE) /
+            geo.blocksize;
+  nblocks = (size + geo.blocksize - 1) / geo.blocksize;
+
+  /* Compare, one read block at a time */
+
+  for (i = 0; i < nblocks && same; i++)
+    {
+      n = MIN(geo.blocksize, size - i * geo.blocksize);
+      ret = MTD_BREAD(mtd, first + i, 1, g_qspixip_block);
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      same = memcmp(g_qspixip_block, &_lqspixip[i * geo.blocksize], n) == 0;
+    }
+
+  if (same)
+    {
+      finfo("QSPI XIP section up to date (%zu bytes)\n", size);
+      return OK;
+    }
+
+  ret = MTD_ERASE(mtd, first * geo.blocksize / geo.erasesize,
+                  (size + geo.erasesize - 1) / geo.erasesize);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  for (i = 0; i < nblocks; i++)
+    {
+      n = MIN(geo.blocksize, size - i * geo.blocksize);
+      memset(g_qspixip_block, 0xff, geo.blocksize);
+      memcpy(g_qspixip_block, &_lqspixip[i * geo.blocksize], n);
+
+      ret = MTD_BWRITE(mtd, first + i, 1, g_qspixip_block);
+      if (ret < 0)
+        {
+          return ret;
+        }
+    }
+
+  syslog(LOG_INFO, "QSPI XIP section programmed (%zu bytes)\n", size);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: gd32_qspixip_initialize
+ *
+ * Description:
+ *   Once the flash is mapped in memory, remap the QSPI window in the
+ *   instruction cache, where the .qspixip section is linked, and check
+ *   that code runs from it.
+ *
+ ****************************************************************************/
+
+int gd32_qspixip_initialize(void)
+{
+  const uint8_t *copy = &_lqspixip[(const uint8_t *)g_qspixip_text -
+                                   _sqspixip];
+  uint32_t expected;
+  uint32_t crc;
+
+  qspixip_cache(true);
+
+  expected = crc32(copy, sizeof(g_qspixip_text));
+  crc      = qspixip_crc32((const uint8_t *)g_qspixip_text,
+                           sizeof(g_qspixip_text));
+
+  syslog(LOG_INFO, "QSPI XIP: code at %p, data at %p: crc %08" PRIx32
+         " %s\n", qspixip_crc32, g_qspixip_text, crc,
+         crc == expected ? "OK" : "FAILED");
+
+  return crc == expected ? OK : -EIO;
+}

Reply via email to