This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch releases/13.0
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 961fd8f7caa5f47d734049f37b6f8789092fab90
Author: Sammy Tran <[email protected]>
AuthorDate: Fri Jun 19 16:42:25 2026 -0400

    drivers/mtd/gd25: add QSPI support
    
    Add QSPI mode to the GD25 MTD driver alongside the existing SPI path.
    When CONFIG_GD25_QSPI is selected, sector erase, chip erase, byte read,
    page write, and byte write all use the QSPI command/memory interfaces
    instead of SPI. Reads use the quad I/O fast-read command (1-4-4) and
    writes use the quad page-program command (1-1-4) via QSPIMEM_QUADDATA.
    A new Kconfig option enables the QE bit in SR2 at initialisation so the
    quad I/O pins are active.
    
    Signed-off-by: Sammy Tran <[email protected]>
---
 drivers/mtd/Kconfig     |  24 ++-
 drivers/mtd/gd25.c      | 444 +++++++++++++++++++++++++++++++++++++++++-------
 include/nuttx/mtd/mtd.h |   5 +
 3 files changed, 412 insertions(+), 61 deletions(-)

diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index b30e4f64fc6..f7ff6462984 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -1393,19 +1393,39 @@ config W25_DEBUG
 endif # MTD_W25
 
 config MTD_GD25
-       bool "SPI-based GD25 FLASH"
+       bool "SPI/QuadSPI-based GigaDevice GD25 FLASH"
        default n
-       select SPI
 
 if MTD_GD25
 
+config GD25_QSPI
+       bool "Use QuadSPI interface for GD25"
+       default n
+       select QSPI
+       ---help---
+               Enable to use the QuadSPI (QSPI) interface instead of SPI.
+               When enabled, gd25_initialize() accepts a qspi_dev_s pointer
+               and bool unprotect. When disabled (default), SPI is used.
+
 config GD25_SPIMODE
        int "GD25 SPI Mode"
        default 0
+       depends on !GD25_QSPI
 
 config GD25_SPIFREQUENCY
        int "GD25 SPI Frequency"
        default 20000000
+       depends on !GD25_QSPI
+
+config GD25_QSPIMODE
+       int "GD25 QuadSPI Mode"
+       default 0
+       depends on GD25_QSPI
+
+config GD25_QSPIFREQUENCY
+       int "GD25 QuadSPI Frequency"
+       default 20000000
+       depends on GD25_QSPI
 
 config GD25_READONLY
        bool "GD25 Read-Only FLASH"
diff --git a/drivers/mtd/gd25.c b/drivers/mtd/gd25.c
index a97a6bcd4a2..b449f481dab 100644
--- a/drivers/mtd/gd25.c
+++ b/drivers/mtd/gd25.c
@@ -41,7 +41,11 @@
 #include <nuttx/kmalloc.h>
 #include <nuttx/signal.h>
 #include <nuttx/fs/ioctl.h>
-#include <nuttx/spi/spi.h>
+#ifdef CONFIG_GD25_QSPI
+#  include <nuttx/spi/qspi.h>
+#else
+#  include <nuttx/spi/spi.h>
+#endif
 #include <nuttx/mtd/mtd.h>
 
 /***************************************************************************
@@ -56,6 +60,14 @@
 #  define CONFIG_GD25_SPIFREQUENCY 20000000
 #endif
 
+#ifndef CONFIG_GD25_QSPIMODE
+#  define CONFIG_GD25_QSPIMODE 0
+#endif
+
+#ifndef CONFIG_GD25_QSPIFREQUENCY
+#  define CONFIG_GD25_QSPIFREQUENCY 20000000
+#endif
+
 /***************************************************************************
  * GD25 Instructions
  ***************************************************************************/
@@ -65,20 +77,36 @@
 #define GD25_WREN                   0x06    /* Write enable               */
 #define GD25_WRDI                   0x04    /* Write Disable              */
 #define GD25_RDSR                   0x05    /* Read status register       */
-#define GD25_RDSR1                  0x35    /* Read status register-1     */
+#define GD25_RDSR1                  0x35    /* Read status register-2     */
 #define GD25_WRSR                   0x01    /* Write Status Register      */
 #define GD25_RDDATA                 0x03    /* Read data bytes            */
-#define GD25_FRD                    0x0b    /* Higher speed read          */
+#define GD25_FRD                    0x0b    /* Fast read (1-wire)         */
 #define GD25_FRDD                   0x3b    /* Fast read, dual output     */
-#define GD25_PP                     0x02    /* Program page               */
-#define GD25_SE                     0x20    /* Sector erase (4KB)         */
-#define GD25_BE                     0xd8    /* Block Erase (64KB)         */
+#define GD25_QOFRD                  0x6b    /* Quad output fast read      */
+#define GD25_QIOFRD                 0xeb    /* Quad I/O fast read, 3-byte */
+#define GD25_QIOFRD4B               0xec    /* Quad I/O fast read, 4-byte */
+#define GD25_PP                     0x02    /* Program page (1-1-1), 3-byte */
+#define GD25_PP4B                   0x12    /* Program page (1-1-1), 4-byte */
+#define GD25_QPP                    0x32    /* Quad page program (1-1-4), 
3-byte */
+#define GD25_QPP4B                  0x34    /* Quad page program (1-1-4), 
4-byte */
+#define GD25_SE                     0x20    /* Sector erase (4KB), 3-byte */
+#define GD25_SE4B                   0x21    /* Sector erase (4KB), 4-byte */
+#define GD25_BE                     0xd8    /* Block Erase (64KB), 3-byte */
+#define GD25_BE4B                   0xdc    /* Block Erase (64KB), 4-byte */
 #define GD25_CE                     0xc7    /* Chip erase                 */
 #define GD25_PD                     0xb9    /* Power down                 */
 #define GD25_PURDID                 0xab    /* Release PD, Device ID      */
 #define GD25_RDMFID                 0x90    /* Read Manufacturer / Device */
 #define GD25_JEDEC_ID               0x9f    /* JEDEC ID read              */
 #define GD25_4BEN                   0xb7    /* Enable 4-byte Mode         */
+#define GD25_4BEXT                  0xe9    /* Exit 4-byte Mode           */
+
+/* Dummy clock cycles for Quad I/O Fast Read (EBh/ECh).
+ * Mode byte (2 clocks on 4-wire) + 4 additional = 6 total, matching
+ * the MX25RXX driver's default and safe up to ~80MHz at VCC=3.3V.
+ */
+
+#define GD25_QIOFRD_DUMMIES         6
 
 /***************************************************************************
  * GD25 Registers
@@ -112,6 +140,11 @@
 #define GD25_SR1_EN4B               (1 << 3)  /* Bit 3: Enable 4byte address */
 #define GD25Q_SR1_EN4B              (1 << 0)  /* Bit 0: Enable 4byte address 
GD25Q memories */
 
+/* Status Register 2 (SR2) bit definitions */
+
+#define GD25_SR2_QE                 (1 << 1)  /* Bit 1 of SR2 (S9): Quad 
Enable, non-volatile */
+#define GD25_SR2_PRESERVE_MASK      0x78      /* Preserve SRP1 (bit6) and 
LB3-LB1 (bits5-3) */
+
 #define GD25_DUMMY                  0x00
 
 /***************************************************************************
@@ -138,13 +171,18 @@
 
 struct gd25_dev_s
 {
-  struct mtd_dev_s      mtd;         /* MTD interface */
-  FAR struct spi_dev_s *spi;         /* Saved SPI interface instance */
-  uint32_t              spi_devid;   /* Chip select inputs */
-  uint16_t              nsectors;    /* Number of erase sectors */
-  uint8_t               prev_instr;  /* Previous instruction given to GD25 
device */
-  bool                  addr_4byte;  /* True: Use Four-byte address */
-  uint8_t               memory;      /* memory type read from device */
+  struct mtd_dev_s       mtd;         /* MTD interface */
+#ifdef CONFIG_GD25_QSPI
+  FAR struct qspi_dev_s *qspi;        /* Saved QSPI interface instance */
+  FAR uint8_t           *cmdbuf;      /* DMA-safe command buffer */
+#else
+  FAR struct spi_dev_s  *spi;         /* Saved SPI interface instance */
+  uint32_t               spi_devid;   /* Chip select inputs */
+#endif
+  uint16_t               nsectors;    /* Number of erase sectors */
+  uint8_t                prev_instr;  /* Previous instruction given to GD25 
device */
+  bool                   addr_4byte;  /* True: Use Four-byte address */
+  uint8_t                memory;      /* memory type read from device */
 };
 
 /***************************************************************************
@@ -153,10 +191,13 @@ struct gd25_dev_s
 
 /* Helpers */
 
-static inline void gd25_purdid(FAR struct gd25_dev_s *priv);
-static inline void gd25_pd(FAR struct gd25_dev_s *priv);
+#ifdef CONFIG_GD25_QSPI
+static void gd25_lock(FAR struct qspi_dev_s *qspi);
+static inline void gd25_unlock(FAR struct qspi_dev_s *qspi);
+#else
 static void gd25_lock(FAR struct spi_dev_s *spi);
 static inline void gd25_unlock(FAR struct spi_dev_s *spi);
+#endif
 static inline int gd25_readid(FAR struct gd25_dev_s *priv);
 #ifndef CONFIG_GD25_READONLY
 static void gd25_unprotect(FAR struct gd25_dev_s *priv);
@@ -199,32 +240,23 @@ static ssize_t gd25_write(FAR struct mtd_dev_s *dev, 
off_t offset,
  ***************************************************************************/
 
 /***************************************************************************
- * Name: gd25_purdid
+ * Name: gd25_lock
  ***************************************************************************/
 
-static inline void gd25_purdid(FAR struct gd25_dev_s *priv)
+#ifdef CONFIG_GD25_QSPI
+static void gd25_lock(FAR struct qspi_dev_s *qspi)
 {
-  SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
-  SPI_SEND(priv->spi, GD25_PURDID);
-  SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
-  up_udelay(20);
+  QSPI_LOCK(qspi, true);
+  QSPI_SETMODE(qspi, CONFIG_GD25_QSPIMODE);
+  QSPI_SETBITS(qspi, 8);
+  QSPI_SETFREQUENCY(qspi, CONFIG_GD25_QSPIFREQUENCY);
 }
 
-/***************************************************************************
- * Name: gd25_pd
- ***************************************************************************/
-
-static inline void gd25_pd(FAR struct gd25_dev_s *priv)
+static inline void gd25_unlock(FAR struct qspi_dev_s *qspi)
 {
-  SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
-  SPI_SEND(priv->spi, GD25_PD);
-  SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
+  QSPI_LOCK(qspi, false);
 }
-
-/***************************************************************************
- * Name: gd25_lock
- ***************************************************************************/
-
+#else
 static void gd25_lock(FAR struct spi_dev_s *spi)
 {
   SPI_LOCK(spi, true);
@@ -239,14 +271,11 @@ static void gd25_lock(FAR struct spi_dev_s *spi)
 #endif
 }
 
-/***************************************************************************
- * Name: gd25_unlock
- ***************************************************************************/
-
 static inline void gd25_unlock(FAR struct spi_dev_s *spi)
 {
   SPI_LOCK(spi, false);
 }
+#endif /* CONFIG_GD25_QSPI */
 
 /***************************************************************************
  * Name: gd25_readid
@@ -259,10 +288,26 @@ static inline int gd25_readid(FAR struct gd25_dev_s *priv)
   uint16_t capacity;
   int ret = -ENODEV;
 
+#ifdef CONFIG_GD25_QSPI
+  struct qspi_cmdinfo_s cmdinfo;
+
+  gd25_lock(priv->qspi);
+
+  cmdinfo.flags   = QSPICMD_READDATA;
+  cmdinfo.addrlen = 0;
+  cmdinfo.cmd     = GD25_JEDEC_ID;
+  cmdinfo.buflen  = 3;
+  cmdinfo.addr    = 0;
+  cmdinfo.buffer  = priv->cmdbuf;
+  QSPI_COMMAND(priv->qspi, &cmdinfo);
+
+  manufacturer = priv->cmdbuf[0];
+  memory       = priv->cmdbuf[1];
+  capacity     = priv->cmdbuf[2];
+#else
   /* Lock and configure the SPI bus */
 
   gd25_lock(priv->spi);
-  gd25_purdid(priv);
 
   /* Select this FLASH part. */
 
@@ -278,6 +323,7 @@ static inline int gd25_readid(FAR struct gd25_dev_s *priv)
   /* Deselect the FLASH and unlock the bus */
 
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
+#endif /* CONFIG_GD25_QSPI */
 
   finfo("manufacturer: %02x memory: %02x capacity: %02x\n",
         manufacturer, memory, capacity);
@@ -344,8 +390,11 @@ out:
    * Or success.
    */
 
-  gd25_pd(priv);
+#ifdef CONFIG_GD25_QSPI
+  gd25_unlock(priv->qspi);
+#else
   gd25_unlock(priv->spi);
+#endif
   return ret;
 }
 
@@ -356,10 +405,39 @@ out:
 #ifndef CONFIG_GD25_READONLY
 static void gd25_unprotect(FAR struct gd25_dev_s *priv)
 {
+#ifdef CONFIG_GD25_QSPI
+  struct qspi_cmdinfo_s cmdinfo;
+  uint8_t sr2;
+
+  gd25_lock(priv->qspi);
+  gd25_waitwritecomplete(priv);
+
+  /* Read SR2 to preserve OTP/SRP bits before writing */
+
+  sr2 = gd25_rdsr(priv, 1);
+
+  gd25_wren(priv);
+
+  /* SR1 = 0 (clear all block-protect bits); SR2 = preserve non-OTP bits,
+   * set QE (S9 = bit1 of SR2) so quad I/O pins are active.
+   */
+
+  priv->cmdbuf[0] = 0;
+  priv->cmdbuf[1] = (sr2 & GD25_SR2_PRESERVE_MASK) | GD25_SR2_QE;
+  cmdinfo.flags   = QSPICMD_WRITEDATA;
+  cmdinfo.addrlen = 0;
+  cmdinfo.cmd     = GD25_WRSR;
+  cmdinfo.buflen  = 2;
+  cmdinfo.addr    = 0;
+  cmdinfo.buffer  = priv->cmdbuf;
+  QSPI_COMMAND(priv->qspi, &cmdinfo);
+
+  gd25_waitwritecomplete(priv);
+  gd25_unlock(priv->qspi);
+#else
   /* Lock and configure the SPI bus */
 
   gd25_lock(priv->spi);
-  gd25_purdid(priv);
 
   /* Wait for any preceding write or erase operation to complete. */
 
@@ -384,8 +462,8 @@ static void gd25_unprotect(FAR struct gd25_dev_s *priv)
 
   /* Unlock the SPI bus */
 
-  gd25_pd(priv);
   gd25_unlock(priv->spi);
+#endif /* CONFIG_GD25_QSPI */
 }
 #endif
 
@@ -400,11 +478,19 @@ static uint8_t gd25_waitwritecomplete(FAR struct 
gd25_dev_s *priv)
   do
     {
       status = gd25_rdsr(priv, 0);
-      if (priv->prev_instr != GD25_PP && (status & GD25_SR_WIP) != 0)
+      if (priv->prev_instr != GD25_PP && priv->prev_instr != GD25_PP4B &&
+          priv->prev_instr != GD25_QPP && priv->prev_instr != GD25_QPP4B &&
+          (status & GD25_SR_WIP) != 0)
         {
+#ifdef CONFIG_GD25_QSPI
+          gd25_unlock(priv->qspi);
+          nxsig_usleep(1000);
+          gd25_lock(priv->qspi);
+#else
           gd25_unlock(priv->spi);
-          nxsched_usleep(1000);
+          nxsig_usleep(1000);
           gd25_lock(priv->spi);
+#endif
         }
     }
   while ((status & GD25_SR_WIP) != 0);
@@ -418,18 +504,29 @@ static uint8_t gd25_waitwritecomplete(FAR struct 
gd25_dev_s *priv)
 
 static inline uint8_t gd25_rdsr(FAR struct gd25_dev_s *priv, uint32_t id)
 {
-  uint8_t status;
   uint8_t rdsr[2] =
   {
     GD25_RDSR, GD25_RDSR1
   };
 
+#ifdef CONFIG_GD25_QSPI
+  struct qspi_cmdinfo_s cmdinfo;
+  cmdinfo.flags   = QSPICMD_READDATA;
+  cmdinfo.addrlen = 0;
+  cmdinfo.cmd     = rdsr[id];
+  cmdinfo.buflen  = 1;
+  cmdinfo.addr    = 0;
+  cmdinfo.buffer  = priv->cmdbuf;
+  QSPI_COMMAND(priv->qspi, &cmdinfo);
+  return priv->cmdbuf[0];
+#else
+  uint8_t status;
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
   SPI_SEND(priv->spi, rdsr[id]);
   status = SPI_SEND(priv->spi, GD25_DUMMY);
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
-
   return status;
+#endif
 }
 
 /***************************************************************************
@@ -442,9 +539,20 @@ static inline uint8_t gd25_rdsr(FAR struct gd25_dev_s 
*priv, uint32_t id)
 
 static inline bool gd25_4ben(FAR struct gd25_dev_s *priv)
 {
+#ifdef CONFIG_GD25_QSPI
+  struct qspi_cmdinfo_s cmdinfo;
+  cmdinfo.flags   = 0;
+  cmdinfo.addrlen = 0;
+  cmdinfo.cmd     = GD25_4BEN;
+  cmdinfo.buflen  = 0;
+  cmdinfo.addr    = 0;
+  cmdinfo.buffer  = NULL;
+  QSPI_COMMAND(priv->qspi, &cmdinfo);
+#else
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
   SPI_SEND(priv->spi, GD25_4BEN);
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
+#endif
   if (priv->memory == GD25Q_JEDEC_MEMORY_TYPE)
     {
       return ((gd25_rdsr(priv, 1) & GD25Q_SR1_EN4B) == GD25Q_SR1_EN4B);
@@ -461,9 +569,20 @@ static inline bool gd25_4ben(FAR struct gd25_dev_s *priv)
 
 static inline void gd25_wren(FAR struct gd25_dev_s *priv)
 {
+#ifdef CONFIG_GD25_QSPI
+  struct qspi_cmdinfo_s cmdinfo;
+  cmdinfo.flags   = 0;
+  cmdinfo.addrlen = 0;
+  cmdinfo.cmd     = GD25_WREN;
+  cmdinfo.buflen  = 0;
+  cmdinfo.addr    = 0;
+  cmdinfo.buffer  = NULL;
+  QSPI_COMMAND(priv->qspi, &cmdinfo);
+#else
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
   SPI_SEND(priv->spi, GD25_WREN);
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
+#endif
 }
 
 /***************************************************************************
@@ -472,9 +591,20 @@ static inline void gd25_wren(FAR struct gd25_dev_s *priv)
 
 static inline void gd25_wrdi(FAR struct gd25_dev_s *priv)
 {
+#ifdef CONFIG_GD25_QSPI
+  struct qspi_cmdinfo_s cmdinfo;
+  cmdinfo.flags   = 0;
+  cmdinfo.addrlen = 0;
+  cmdinfo.cmd     = GD25_WRDI;
+  cmdinfo.buflen  = 0;
+  cmdinfo.addr    = 0;
+  cmdinfo.buffer  = NULL;
+  QSPI_COMMAND(priv->qspi, &cmdinfo);
+#else
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
   SPI_SEND(priv->spi, GD25_WRDI);
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
+#endif
 }
 
 /***************************************************************************
@@ -526,6 +656,9 @@ static bool gd25_is_erased(FAR struct gd25_dev_s *priv, 
off_t address,
 static void gd25_sectorerase(FAR struct gd25_dev_s *priv, off_t sector)
 {
   off_t address = sector << GD25_SECTOR_SHIFT;
+#ifdef CONFIG_GD25_QSPI
+  struct qspi_cmdinfo_s cmdinfo;
+#endif
 
   finfo("sector: %08lx\n", (long)sector);
 
@@ -546,6 +679,16 @@ static void gd25_sectorerase(FAR struct gd25_dev_s *priv, 
off_t sector)
 
   gd25_wren(priv);
 
+#ifdef CONFIG_GD25_QSPI
+  cmdinfo.flags   = QSPICMD_ADDRESS;
+  cmdinfo.addrlen = priv->addr_4byte ? 4 : 3;
+  cmdinfo.cmd     = priv->addr_4byte ? GD25_SE4B : GD25_SE;
+  cmdinfo.buflen  = 0;
+  cmdinfo.addr    = address;
+  cmdinfo.buffer  = NULL;
+  QSPI_COMMAND(priv->qspi, &cmdinfo);
+  priv->prev_instr = cmdinfo.cmd;
+#else
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
 
   /* Send the "Sector Erase (SE)" instruction */
@@ -567,6 +710,7 @@ static void gd25_sectorerase(FAR struct gd25_dev_s *priv, 
off_t sector)
   SPI_SEND(priv->spi, address & 0xff);
 
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
+#endif /* CONFIG_GD25_QSPI */
 }
 
 /***************************************************************************
@@ -575,6 +719,10 @@ static void gd25_sectorerase(FAR struct gd25_dev_s *priv, 
off_t sector)
 
 static inline int gd25_chiperase(FAR struct gd25_dev_s *priv)
 {
+#ifdef CONFIG_GD25_QSPI
+  struct qspi_cmdinfo_s cmdinfo;
+#endif
+
   /* Wait for any preceding write or erase operation to complete. */
 
   gd25_waitwritecomplete(priv);
@@ -583,6 +731,16 @@ static inline int gd25_chiperase(FAR struct gd25_dev_s 
*priv)
 
   gd25_wren(priv);
 
+#ifdef CONFIG_GD25_QSPI
+  cmdinfo.flags   = 0;
+  cmdinfo.addrlen = 0;
+  cmdinfo.cmd     = GD25_CE;
+  cmdinfo.buflen  = 0;
+  cmdinfo.addr    = 0;
+  cmdinfo.buffer  = NULL;
+  QSPI_COMMAND(priv->qspi, &cmdinfo);
+  priv->prev_instr = GD25_CE;
+#else
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
 
   /* Send the "Chip Erase (CE)" instruction */
@@ -591,6 +749,7 @@ static inline int gd25_chiperase(FAR struct gd25_dev_s 
*priv)
   priv->prev_instr = GD25_CE;
 
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
+#endif /* CONFIG_GD25_QSPI */
   return OK;
 }
 
@@ -601,6 +760,10 @@ static inline int gd25_chiperase(FAR struct gd25_dev_s 
*priv)
 static void gd25_byteread(FAR struct gd25_dev_s *priv, FAR uint8_t *buffer,
                           off_t address, size_t nbytes)
 {
+#ifdef CONFIG_GD25_QSPI
+  struct qspi_meminfo_s meminfo;
+#endif
+
   finfo("address: %08lx nbytes: %d\n", (long)address, (int)nbytes);
 
   /* Wait for any preceding write or erase operation to complete. */
@@ -611,6 +774,17 @@ static void gd25_byteread(FAR struct gd25_dev_s *priv, FAR 
uint8_t *buffer,
 
   gd25_wrdi(priv);
 
+#ifdef CONFIG_GD25_QSPI
+  meminfo.flags   = QSPIMEM_READ | QSPIMEM_QUADIO;
+  meminfo.addrlen = priv->addr_4byte ? 4 : 3;
+  meminfo.dummies = GD25_QIOFRD_DUMMIES;
+  meminfo.buflen  = nbytes;
+  meminfo.cmd     = priv->addr_4byte ? GD25_QIOFRD4B : GD25_QIOFRD;
+  meminfo.addr    = address;
+  meminfo.buffer  = buffer;
+  QSPI_MEMORY(priv->qspi, &meminfo);
+  priv->prev_instr = meminfo.cmd;
+#else
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
 
   /* Send "Read from Memory " instruction */
@@ -645,6 +819,7 @@ static void gd25_byteread(FAR struct gd25_dev_s *priv, FAR 
uint8_t *buffer,
   SPI_RECVBLOCK(priv->spi, buffer, nbytes);
 
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
+#endif /* CONFIG_GD25_QSPI */
 }
 
 /***************************************************************************
@@ -656,6 +831,17 @@ static void gd25_pagewrite(FAR struct gd25_dev_s *priv,
                            FAR const uint8_t *buffer, off_t address,
                            size_t nbytes)
 {
+#ifdef CONFIG_GD25_QSPI
+  /* GD25 Quad Page Program (0x32/0x34) is a 1-1-4 transfer: the address
+   * is clocked on a single line and only the data is quad.
+   * QSPIMEM_QUADIO cannot express this (it forces the address quad too),
+   * so use QSPIMEM_QUADDATA, which sets the data phase quad while leaving
+   * the address phase single-line.
+   */
+
+  struct qspi_meminfo_s meminfo;
+#endif
+
   finfo("address: %08lx nwords: %d\n", (long)address, (int)nbytes);
   DEBUGASSERT(priv && buffer && (address & 0xff) == 0 &&
               (nbytes & 0xff) == 0);
@@ -670,6 +856,17 @@ static void gd25_pagewrite(FAR struct gd25_dev_s *priv,
 
       gd25_wren(priv);
 
+#ifdef CONFIG_GD25_QSPI
+      meminfo.flags   = QSPIMEM_WRITE | QSPIMEM_QUADDATA;
+      meminfo.cmd     = priv->addr_4byte ? GD25_QPP4B : GD25_QPP;
+      meminfo.addrlen = priv->addr_4byte ? 4 : 3;
+      meminfo.buflen  = GD25_PAGE_SIZE;
+      meminfo.dummies = 0;
+      meminfo.addr    = address;
+      meminfo.buffer  = (FAR void *)buffer;
+      QSPI_MEMORY(priv->qspi, &meminfo);
+      priv->prev_instr = meminfo.cmd;
+#else
       SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
 
       /* Send the "Page Program (GD25_PP)" Command */
@@ -693,6 +890,7 @@ static void gd25_pagewrite(FAR struct gd25_dev_s *priv,
       SPI_SNDBLOCK(priv->spi, buffer, GD25_PAGE_SIZE);
 
       SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
+#endif /* CONFIG_GD25_QSPI */
 
       /* Update addresses */
 
@@ -711,6 +909,14 @@ static inline void gd25_bytewrite(FAR struct gd25_dev_s 
*priv,
                                   FAR const uint8_t *buffer, off_t offset,
                                   uint16_t count)
 {
+#ifdef CONFIG_GD25_QSPI
+  /* See gd25_pagewrite: GD25 Quad Page Program (0x32/0x34) is 1-1-4.
+   * QSPIMEM_QUADDATA selects quad data with a single-line address.
+   */
+
+  struct qspi_meminfo_s meminfo;
+#endif
+
   finfo("offset: %08lx  count:%d\n", (long)offset, count);
 
   /* Wait for any preceding write to complete.  We could simplify things by
@@ -725,6 +931,17 @@ static inline void gd25_bytewrite(FAR struct gd25_dev_s 
*priv,
 
   gd25_wren(priv);
 
+#ifdef CONFIG_GD25_QSPI
+  meminfo.flags   = QSPIMEM_WRITE | QSPIMEM_QUADDATA;
+  meminfo.cmd     = priv->addr_4byte ? GD25_QPP4B : GD25_QPP;
+  meminfo.addrlen = priv->addr_4byte ? 4 : 3;
+  meminfo.buflen  = count;
+  meminfo.dummies = 0;
+  meminfo.addr    = offset;
+  meminfo.buffer  = (FAR void *)buffer;
+  QSPI_MEMORY(priv->qspi, &meminfo);
+  priv->prev_instr = meminfo.cmd;
+#else
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
 
   /* Send "Page Program (PP)" command */
@@ -748,6 +965,7 @@ static inline void gd25_bytewrite(FAR struct gd25_dev_s 
*priv,
   SPI_SNDBLOCK(priv->spi, buffer, count);
 
   SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
+#endif /* CONFIG_GD25_QSPI */
   finfo("Written\n");
 }
 #endif /* defined(CONFIG_MTD_BYTE_WRITE) && !defined(CONFIG_GD25_READONLY) */
@@ -767,10 +985,13 @@ static int gd25_erase(FAR struct mtd_dev_s *dev, off_t 
startblock,
 
   finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
 
-  /* Lock access to the SPI bus until we complete the erase */
+  /* Lock access to the bus until we complete the erase */
 
+#ifdef CONFIG_GD25_QSPI
+  gd25_lock(priv->qspi);
+#else
   gd25_lock(priv->spi);
-  gd25_purdid(priv);
+#endif
 
   while (blocksleft-- > 0)
     {
@@ -780,8 +1001,11 @@ static int gd25_erase(FAR struct mtd_dev_s *dev, off_t 
startblock,
       startblock++;
     }
 
-  gd25_pd(priv);
+#ifdef CONFIG_GD25_QSPI
+  gd25_unlock(priv->qspi);
+#else
   gd25_unlock(priv->spi);
+#endif
   return (int)nblocks;
 #endif
 }
@@ -821,14 +1045,20 @@ static ssize_t gd25_bwrite(FAR struct mtd_dev_s *dev, 
off_t startblock,
 
   finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
 
-  /* Lock the SPI bus and write all of the pages to FLASH */
+  /* Lock the bus and write all of the pages to FLASH */
 
+#ifdef CONFIG_GD25_QSPI
+  gd25_lock(priv->qspi);
+#else
   gd25_lock(priv->spi);
-  gd25_purdid(priv);
+#endif
   gd25_pagewrite(priv, buffer, startblock << GD25_PAGE_SHIFT,
                  nblocks << GD25_PAGE_SHIFT);
-  gd25_pd(priv);
+#ifdef CONFIG_GD25_QSPI
+  gd25_unlock(priv->qspi);
+#else
   gd25_unlock(priv->spi);
+#endif
 
   return nblocks;
 #endif
@@ -845,13 +1075,19 @@ static ssize_t gd25_read(FAR struct mtd_dev_s *dev, 
off_t offset,
 
   finfo("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes);
 
-  /* Lock the SPI bus and select this FLASH part */
+  /* Lock the bus and select this FLASH part */
 
+#ifdef CONFIG_GD25_QSPI
+  gd25_lock(priv->qspi);
+#else
   gd25_lock(priv->spi);
-  gd25_purdid(priv);
+#endif
   gd25_byteread(priv, buffer, offset, nbytes);
-  gd25_pd(priv);
+#ifdef CONFIG_GD25_QSPI
+  gd25_unlock(priv->qspi);
+#else
   gd25_unlock(priv->spi);
+#endif
 
   finfo("return nbytes: %d,%x,%x\n", (int)nbytes, buffer[0], buffer[1]);
   return nbytes;
@@ -885,8 +1121,11 @@ static ssize_t gd25_write(FAR struct mtd_dev_s *dev, 
off_t offset,
   startpage = offset / GD25_PAGE_SIZE;
   endpage = (offset + nbytes) / GD25_PAGE_SIZE;
 
+#ifdef CONFIG_GD25_QSPI
+  gd25_lock(priv->qspi);
+#else
   gd25_lock(priv->spi);
-  gd25_purdid(priv);
+#endif
   if (startpage == endpage)
     {
       /* All bytes within one programmable page.  Just do the write. */
@@ -928,8 +1167,11 @@ static ssize_t gd25_write(FAR struct mtd_dev_s *dev, 
off_t offset,
         }
     }
 
-  gd25_pd(priv);
+#ifdef CONFIG_GD25_QSPI
+  gd25_unlock(priv->qspi);
+#else
   gd25_unlock(priv->spi);
+#endif
   return nbytes;
 #endif
 }
@@ -988,11 +1230,17 @@ static int gd25_ioctl(FAR struct mtd_dev_s *dev, int 
cmd, unsigned long arg)
         {
           /* Erase the entire device */
 
+#ifdef CONFIG_GD25_QSPI
+          gd25_lock(priv->qspi);
+#else
           gd25_lock(priv->spi);
-          gd25_purdid(priv);
+#endif
           ret = gd25_chiperase(priv);
-          gd25_pd(priv);
+#ifdef CONFIG_GD25_QSPI
+          gd25_unlock(priv->qspi);
+#else
           gd25_unlock(priv->spi);
+#endif
         }
         break;
 
@@ -1028,6 +1276,83 @@ static int gd25_ioctl(FAR struct mtd_dev_s *dev, int 
cmd, unsigned long arg)
  *
  ***************************************************************************/
 
+#ifdef CONFIG_GD25_QSPI
+FAR struct mtd_dev_s *gd25_initialize(FAR struct qspi_dev_s *qspi,
+                                      bool unprotect)
+{
+  FAR struct gd25_dev_s *priv;
+  int ret;
+
+  priv = (FAR struct gd25_dev_s *)kmm_zalloc(sizeof(struct gd25_dev_s));
+  if (priv)
+    {
+      priv->mtd.erase  = gd25_erase;
+      priv->mtd.bread  = gd25_bread;
+      priv->mtd.bwrite = gd25_bwrite;
+      priv->mtd.read   = gd25_read;
+      priv->mtd.ioctl  = gd25_ioctl;
+#ifdef CONFIG_MTD_BYTE_WRITE
+      priv->mtd.write  = gd25_write;
+#endif
+      priv->mtd.name   = "gd25";
+      priv->qspi       = qspi;
+
+      priv->cmdbuf = (FAR uint8_t *)QSPI_ALLOC(qspi, 4);
+      if (!priv->cmdbuf)
+        {
+          kmm_free(priv);
+          return NULL;
+        }
+
+      ret = gd25_readid(priv);
+      if (ret != OK)
+        {
+          ferr("ERROR: Unrecognized\n");
+          QSPI_FREE(qspi, priv->cmdbuf);
+          kmm_free(priv);
+          return NULL;
+        }
+
+      /* QE (Quad Enable) must be set before any quad I/O command is issued.
+       * It is non-volatile, so only write if currently clear to avoid
+       * unnecessary write cycles.
+       */
+
+      if (!(gd25_rdsr(priv, 1) & GD25_SR2_QE))
+        {
+          uint8_t sr2 = gd25_rdsr(priv, 1);
+
+          gd25_lock(priv->qspi);
+          gd25_waitwritecomplete(priv);
+          gd25_wren(priv);
+
+          priv->cmdbuf[0] = 0;
+          priv->cmdbuf[1] = (sr2 & GD25_SR2_PRESERVE_MASK) | GD25_SR2_QE;
+
+          struct qspi_cmdinfo_s cmdinfo;
+          cmdinfo.flags   = QSPICMD_WRITEDATA;
+          cmdinfo.addrlen = 0;
+          cmdinfo.cmd     = GD25_WRSR;
+          cmdinfo.buflen  = 2;
+          cmdinfo.addr    = 0;
+          cmdinfo.buffer  = priv->cmdbuf;
+          QSPI_COMMAND(priv->qspi, &cmdinfo);
+
+          gd25_waitwritecomplete(priv);
+          gd25_unlock(priv->qspi);
+        }
+
+#ifndef CONFIG_GD25_READONLY
+      if (unprotect)
+        {
+          gd25_unprotect(priv);
+        }
+#endif
+    }
+
+  return (FAR struct mtd_dev_s *)priv;
+}
+#else
 FAR struct mtd_dev_s *gd25_initialize(FAR struct spi_dev_s *spi,
                                       uint32_t spi_devid)
 {
@@ -1082,3 +1407,4 @@ FAR struct mtd_dev_s *gd25_initialize(FAR struct 
spi_dev_s *spi,
 
   return (FAR struct mtd_dev_s *)priv;
 }
+#endif /* CONFIG_GD25_QSPI */
diff --git a/include/nuttx/mtd/mtd.h b/include/nuttx/mtd/mtd.h
index 961ffd2ddd9..eebb310cb31 100644
--- a/include/nuttx/mtd/mtd.h
+++ b/include/nuttx/mtd/mtd.h
@@ -649,8 +649,13 @@ FAR struct mtd_dev_s *w25_initialize(FAR struct spi_dev_s 
*dev);
  *
  ****************************************************************************/
 
+#ifdef CONFIG_GD25_QSPI
+FAR struct mtd_dev_s *gd25_initialize(FAR struct qspi_dev_s *qspi,
+                                      bool unprotect);
+#else
 FAR struct mtd_dev_s *gd25_initialize(FAR struct spi_dev_s *dev,
                                       uint32_t spi_devid);
+#endif
 
 /****************************************************************************
  * Name: gd55_initialize

Reply via email to