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

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit f2713d9511caae8fd8f59217c7de0e4a962798ce
Author: Daniel P. Carvalho <[email protected]>
AuthorDate: Fri Oct 2 17:12:19 2026 -0300

    drivers/mtd: Add 4-byte addressing support for W25Q256 and W25Q512.
    
    This commit adds 4-byte address mode support to the Winbond W25 SPI
    NOR flash driver (drivers/mtd/w25.c):
    - Detect W25_JEDEC_CAPACITY_256MBIT (0x19) and W25_JEDEC_CAPACITY_512MBIT 
(0x20).
    - Send W25_EN4B (0xB7) command to enter 4-byte address mode on 
initialization
      when chip capacity >= 256Mbit.
    - Expand w25_dev_s nsectors to uint32_t to support chips > 128Mbit.
    - Add w25_sendaddr() helper supporting both 3-byte and 4-byte addressing
      for sector erase, byte read, page write, and byte write.
    - Verified on hardware with Winbond W25Q256JV (256Mbit / 32MB):
      both raw MTD block access at >20MB (>16MB boundary) and SmartFS
      mounting and file reading are verified working.
    
    Assisted-by: gemini-3.8-flash
    Signed-off-by: Daniel P. Carvalho <[email protected]>
---
 drivers/mtd/w25.c | 126 +++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 102 insertions(+), 24 deletions(-)

diff --git a/drivers/mtd/w25.c b/drivers/mtd/w25.c
index 15eec908d0e..12fb2b643bf 100644
--- a/drivers/mtd/w25.c
+++ b/drivers/mtd/w25.c
@@ -96,6 +96,8 @@
 #define W25_BE                     0xd8    /* Block Erase (64KB)             */
 #define W25_SE                     0x20    /* Sector erase (4KB)             */
 #define W25_CE                     0xc7    /* Chip erase                     */
+#define W25_EN4B                   0xb7    /* Enter 4-byte address mode      */
+#define W25_EX4B                   0xe9    /* Exit 4-byte address mode       */
 #define W25_PD                     0xb9    /* Power down                     */
 #define W25_PURDID                 0xab    /* Release PD, Device ID          */
 #define W25_RDMFID                 0x90    /* Read Manufacturer / Device     */
@@ -127,6 +129,8 @@
 #define W25_JEDEC_CAPACITY_32MBIT  0x16  /* 1024x4096 = 32Mbit memory capacity 
*/
 #define W25_JEDEC_CAPACITY_64MBIT  0x17  /* 2048x4096 = 64Mbit memory capacity 
*/
 #define W25_JEDEC_CAPACITY_128MBIT 0x18  /* 4096x4096 = 128Mbit memory 
capacity */
+#define W25_JEDEC_CAPACITY_256MBIT 0x19  /* 8192x4096 = 256Mbit memory 
capacity */
+#define W25_JEDEC_CAPACITY_512MBIT 0x20  /* 16384x4096 = 512Mbit memory 
capacity */
 
 #define NSECTORS_2MBIT             64    /* 64 sectors x 4096 bytes/sector = 
256Kb */
 #define NSECTORS_8MBIT             256   /* 256 sectors x 4096 bytes/sector = 
1Mb */
@@ -134,6 +138,8 @@
 #define NSECTORS_32MBIT            1024  /* 1024 sectors x 4096 bytes/sector = 
4Mb */
 #define NSECTORS_64MBIT            2048  /* 2048 sectors x 4096 bytes/sector = 
8Mb */
 #define NSECTORS_128MBIT           4096  /* 4096 sectors x 4096 bytes/sector = 
16Mb */
+#define NSECTORS_256MBIT           8192  /* 8192 sectors x 4096 bytes/sector = 
32Mb */
+#define NSECTORS_512MBIT           16384 /* 16384 sectors x 4096 bytes/sector 
= 64Mb */
 
 /* Status register bit definitions */
 
@@ -239,8 +245,9 @@ struct w25_dev_s
 {
   struct mtd_dev_s      mtd;         /* MTD interface */
   FAR struct spi_dev_s *spi;         /* Saved SPI interface instance */
-  uint16_t              nsectors;    /* Number of erase sectors */
+  uint32_t              nsectors;    /* Number of erase sectors */
   uint8_t               prev_instr;  /* Previous instruction given to W25 
device */
+  uint8_t               addresslen;  /* Address length in bytes: 3 or 4 */
 
 #if defined(CONFIG_W25_SECTOR512) && !defined(CONFIG_W25_READONLY)
   uint8_t               flags;       /* Buffered sector flags */
@@ -259,12 +266,14 @@ static inline void w25_purdid(FAR struct w25_dev_s *priv);
 static void w25_lock(FAR struct spi_dev_s *spi);
 static inline void w25_unlock(FAR struct spi_dev_s *spi);
 static inline int w25_readid(FAR struct w25_dev_s *priv);
+static void w25_enter4b(FAR struct w25_dev_s *priv);
 #ifndef CONFIG_W25_READONLY
 static void w25_unprotect(FAR struct w25_dev_s *priv);
 #endif
 static uint8_t w25_waitwritecomplete(FAR struct w25_dev_s *priv);
 static inline void w25_wren(FAR struct w25_dev_s *priv);
 static inline void w25_wrdi(FAR struct w25_dev_s *priv);
+static inline void w25_sendaddr(FAR struct w25_dev_s *priv, off_t address);
 static bool w25_is_erased(struct w25_dev_s *priv,
                           off_t address,
                           off_t size);
@@ -436,6 +445,10 @@ static inline int w25_readid(struct w25_dev_s *priv)
        memory == W25Q_JEDEC_MEMORY_TYPE_C ||
        memory == W25Q_JEDEC_MEMORY_TYPE_D))
     {
+      /* Default to 3-byte address mode */
+
+      priv->addresslen = 3;
+
       /* Okay.. is it a FLASH capacity that we understand? If so, save
        * the FLASH capacity.
        */
@@ -447,7 +460,7 @@ static inline int w25_readid(struct w25_dev_s *priv)
 
       if (capacity == W25_JEDEC_CAPACITY_2MBIT)
         {
-           priv->nsectors = NSECTORS_2MBIT;
+          priv->nsectors = NSECTORS_2MBIT;
         }
 
       /* 8M-bit / 1M-byte
@@ -457,7 +470,7 @@ static inline int w25_readid(struct w25_dev_s *priv)
 
       else if (capacity == W25_JEDEC_CAPACITY_8MBIT)
         {
-           priv->nsectors = NSECTORS_8MBIT;
+          priv->nsectors = NSECTORS_8MBIT;
         }
 
       /* 16M-bit / 2M-byte (2,097,152)
@@ -467,7 +480,7 @@ static inline int w25_readid(struct w25_dev_s *priv)
 
       else if (capacity == W25_JEDEC_CAPACITY_16MBIT)
         {
-           priv->nsectors = NSECTORS_16MBIT;
+          priv->nsectors = NSECTORS_16MBIT;
         }
 
       /* 32M-bit / 4M-byte (4,194,304)
@@ -477,7 +490,7 @@ static inline int w25_readid(struct w25_dev_s *priv)
 
       else if (capacity == W25_JEDEC_CAPACITY_32MBIT)
         {
-           priv->nsectors = NSECTORS_32MBIT;
+          priv->nsectors = NSECTORS_32MBIT;
         }
 
       /* 64M-bit / 8M-byte (8,388,608)
@@ -487,7 +500,7 @@ static inline int w25_readid(struct w25_dev_s *priv)
 
       else if (capacity == W25_JEDEC_CAPACITY_64MBIT)
         {
-           priv->nsectors = NSECTORS_64MBIT;
+          priv->nsectors = NSECTORS_64MBIT;
         }
 
       /* 128M-bit / 16M-byte (16,777,216)
@@ -497,7 +510,29 @@ static inline int w25_readid(struct w25_dev_s *priv)
 
       else if (capacity == W25_JEDEC_CAPACITY_128MBIT)
         {
-           priv->nsectors = NSECTORS_128MBIT;
+          priv->nsectors = NSECTORS_128MBIT;
+        }
+
+      /* 256M-bit / 32M-byte (33,554,432)
+       *
+       * W25Q256JV
+       */
+
+      else if (capacity == W25_JEDEC_CAPACITY_256MBIT)
+        {
+          priv->nsectors   = NSECTORS_256MBIT;
+          priv->addresslen = 4;
+        }
+
+      /* 512M-bit / 64M-byte (67,108,864)
+       *
+       * W25Q512JV, W25Q512NW
+       */
+
+      else if (capacity == W25_JEDEC_CAPACITY_512MBIT)
+        {
+          priv->nsectors   = NSECTORS_512MBIT;
+          priv->addresslen = 4;
         }
       else
         {
@@ -517,6 +552,30 @@ static inline int w25_readid(struct w25_dev_s *priv)
   return -ENODEV;
 }
 
+/****************************************************************************
+ * Name: w25_enter4b
+ ****************************************************************************/
+
+static void w25_enter4b(FAR struct w25_dev_s *priv)
+{
+  /* Lock and configure the SPI bus */
+
+  w25_lock(priv->spi);
+
+  /* Select this FLASH part */
+
+  SPI_SELECT(priv->spi, SPIDEV_FLASH(0), true);
+
+  /* Send the "Enter 4-byte Address Mode (EN4B)" instruction */
+
+  SPI_SEND(priv->spi, W25_EN4B);
+
+  /* Deselect the FLASH and unlock the bus */
+
+  SPI_SELECT(priv->spi, SPIDEV_FLASH(0), false);
+  w25_unlock(priv->spi);
+}
+
 /****************************************************************************
  * Name: w25_unprotect
  ****************************************************************************/
@@ -697,6 +756,22 @@ static bool w25_is_erased(struct w25_dev_s *priv, off_t 
address, off_t size)
   return true;
 }
 
+/****************************************************************************
+ * Name: w25_sendaddr
+ ****************************************************************************/
+
+static inline void w25_sendaddr(FAR struct w25_dev_s *priv, off_t address)
+{
+  if (priv->addresslen == 4)
+    {
+      SPI_SEND(priv->spi, (address >> 24) & 0xff);
+    }
+
+  SPI_SEND(priv->spi, (address >> 16) & 0xff);
+  SPI_SEND(priv->spi, (address >> 8) & 0xff);
+  SPI_SEND(priv->spi, address & 0xff);
+}
+
 /****************************************************************************
  * Name:  w25_sectorerase
  ****************************************************************************/
@@ -737,9 +812,7 @@ static void w25_sectorerase(struct w25_dev_s *priv, off_t 
sector)
    * (those corresponding to the sector) have any meaning.
    */
 
-  SPI_SEND(priv->spi, (address >> 16) & 0xff);
-  SPI_SEND(priv->spi, (address >> 8) & 0xff);
-  SPI_SEND(priv->spi, address & 0xff);
+  w25_sendaddr(priv, address);
 
   /* Deselect the FLASH */
 
@@ -814,9 +887,7 @@ static void w25_byteread(FAR struct w25_dev_s *priv, FAR 
uint8_t *buffer,
 
   /* Send the address high byte first. */
 
-  SPI_SEND(priv->spi, (address >> 16) & 0xff);
-  SPI_SEND(priv->spi, (address >> 8) & 0xff);
-  SPI_SEND(priv->spi, address & 0xff);
+  w25_sendaddr(priv, address);
 
   /* Send a dummy byte */
 
@@ -869,9 +940,7 @@ static void w25_pagewrite(struct w25_dev_s *priv, FAR const 
uint8_t *buffer,
 
       /* Send the address high byte first. */
 
-      SPI_SEND(priv->spi, (address >> 16) & 0xff);
-      SPI_SEND(priv->spi, (address >> 8) & 0xff);
-      SPI_SEND(priv->spi, address & 0xff);
+      w25_sendaddr(priv, address);
 
       /* Then send the page of data */
 
@@ -928,9 +997,7 @@ static inline void w25_bytewrite(struct w25_dev_s *priv,
 
   /* Send the page offset high byte first. */
 
-  SPI_SEND(priv->spi, (offset >> 16) & 0xff);
-  SPI_SEND(priv->spi, (offset >> 8) & 0xff);
-  SPI_SEND(priv->spi, offset & 0xff);
+  w25_sendaddr(priv, offset);
 
   /* Then write the specified number of bytes */
 
@@ -1049,6 +1116,7 @@ static void w25_cacheerase(struct w25_dev_s *priv, off_t 
sector)
   if (!IS_ERASED(priv))
     {
       off_t esectno  = sector >> (W25_SECTOR_SHIFT - W25_SECTOR512_SHIFT);
+
       w25_finfo("sector: %ld esectno: %d\n", sector, esectno);
 
       w25_sectorerase(priv, esectno);
@@ -1094,6 +1162,7 @@ static void w25_cachewrite(FAR struct w25_dev_s *priv,
         {
           off_t esectno  = sector >>
                            (W25_SECTOR_SHIFT - W25_SECTOR512_SHIFT);
+
           w25_finfo("sector: %ld esectno: %d\n", sector, esectno);
 
           w25_sectorerase(priv, esectno);
@@ -1344,6 +1413,7 @@ static int w25_ioctl(FAR struct mtd_dev_s *dev, int cmd, 
unsigned long arg)
         {
           FAR struct mtd_geometry_s *geo =
             (FAR struct mtd_geometry_s *)((uintptr_t)arg);
+
           if (geo)
             {
               memset(geo, 0, sizeof(*geo));
@@ -1381,6 +1451,7 @@ static int w25_ioctl(FAR struct mtd_dev_s *dev, int cmd, 
unsigned long arg)
         {
           FAR struct partition_info_s *info =
             (FAR struct partition_info_s *)arg;
+
           if (info != NULL)
             {
 #ifdef CONFIG_W25_SECTOR512
@@ -1401,19 +1472,19 @@ static int w25_ioctl(FAR struct mtd_dev_s *dev, int 
cmd, unsigned long arg)
 
       case MTDIOC_BULKERASE:
         {
-            /* Erase the entire device */
+          /* Erase the entire device */
 
-            w25_lock(priv->spi);
-            ret = w25_chiperase(priv);
-            w25_unlock(priv->spi);
+          w25_lock(priv->spi);
+          ret = w25_chiperase(priv);
+          w25_unlock(priv->spi);
         }
         break;
 
       case MTDIOC_ERASESTATE:
         {
           FAR uint8_t *result = (FAR uint8_t *)arg;
-          *result = W25_ERASED_STATE;
 
+          *result = W25_ERASED_STATE;
           ret = OK;
         }
         break;
@@ -1492,6 +1563,13 @@ FAR struct mtd_dev_s *w25_initialize(FAR struct 
spi_dev_s *spi)
         }
       else
         {
+          /* Enter 4-byte address mode if required */
+
+          if (priv->addresslen == 4)
+            {
+              w25_enter4b(priv);
+            }
+
           /* Make sure that the FLASH is unprotected so that we can write
            * into it.
            */

Reply via email to