Donny9 commented on code in PR #15523:
URL: https://github.com/apache/nuttx/pull/15523#discussion_r1916029117


##########
drivers/mtd/gd55.c:
##########
@@ -0,0 +1,2082 @@
+/****************************************************************************
+ * drivers/mtd/gd55.c
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef CONFIG_MTD_GD55_SECTOR512
+#  include <stdlib.h>
+#  include <string.h>
+#endif
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/spi/qspi.h>
+#include <nuttx/mtd/mtd.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* 4 byte addressing is needed for addresses needing more than a 3 byte
+ * address, i.e. 16Mbyte
+ */
+
+#define MODE_3BYTE_LIMIT        ((16 * 1024 * 1024))
+
+/* GD55 Commands                                                            */
+
+#define GD55_QREAD              0x6b  /* Quad output fast read              */
+#define GD55_QREAD_DUMMIES      8
+#define GD55_QC_READ            0xeb  /* Quad output continuous fast read   */
+#define GD55_QC_READ_DUMMIES    6
+#define GD55_EQPP               0xc2  /* Extended quad page program         */
+#define GD55_EQPP_DUMMIES       0     /* No dummy clocks                    */
+#define GD55_SE                 0x20  /* 4Kb Sector erase                   */
+#define GD55_BE32               0x52  /* 32Kbit block Erase                 */
+#define GD55_BE64               0xd8  /* 64Kbit block Erase                 */
+#define GD55_CE                 0x60  /* Chip erase (alternate)             */
+#define GD55_WREN               0x06  /* Write Enable                       */
+#define GD55_WRDI               0x04  /* Write Disable                      */
+#define GD55_RDSR1              0x05  /* Read status register 1             */
+#define GD55_EN4B               0xb7  /* Enable 4 byte Addressing Mode      */
+#define GD55_DIS4B              0xe9  /* Disable 4 byte Addressing Mode     */
+#define GD55_IBSL               0x36  /* Individual block/sector lock       */
+#define GD55_IBSUL              0x39  /* Individual block/sector unlock     */
+#define GD55_RIBSL              0x3d  /* Read individual block/sector lock  */
+#define GD55_RDNVCR             0xb5  /* Read Non-Volatile config register  */
+#define GD55_RD_NVCR_DUMMIES    8
+#define GD55_RDSR2              0x35  /* Read status register 2             */
+#define GD55_WRSR1              0x01  /* Write status register 1            */
+#define GD55_SE_ALT             0x21  /* Alternate 4Kb Sector erase         */
+#define GD55_QC_READ_ALT        0xec  /* Quad output continuous fast read   */
+#define GD55_4B_QDTR_READ       0xed  /* Quad I/O DTR read                  */
+#define GD55_4B_QDTR_READ_ALT   0xee  /* Alternate quad I/O DTR read        */
+#define GD55_PP                 0x02  /* Page program (SPI, not used)       */
+#define GD55_PP_ALT             0x12  /* Aternate page program (SPI)        */
+#define GD55_BE32_ALT           0x5c  /* Alternate 32Kbit block Erase       */
+#define GD55_BE64_ALT           0xd8  /* ALternate 64Kbit block Erase       */
+#define GD55_CE_ALT             0xc7  /* Alternate chip erase               */
+#define GD55_QPP                0x32  /* Quad page program                  */
+#define GD55_QPP_ALT            0x34  /* ALternate quad page program        */
+#define GD55_QPP_DUMMIES        0     /* No dummy clocks                    */
+#define GD55_QPIEN              0x38  /* Enable QPI Operation               */
+#define GD55_QPIDIS             0xff  /* Disable QPI Operation              */
+#define GD55_DP                 0xb9  /* Deep power down                    */
+#define GD55_RDP                0xab  /* Release deep power down            */
+#define GD55_RUID               0x4b  /* Read Unique ID                     */
+#define GD55_RDID               0x9e  /* Read identification                */
+#define GD55_RDID_ALT           0x9f  /* Read identification (alternate)    */
+#define GD55_PE_SUSPEND         0x75  /* Suspends program/erase             */
+#define GD55_PE_RESUME          0x7a  /* Resume program                     */
+#define GD55_RDVCR              0x85  /* Read Volatile config register      */
+#define GD55_RD_VCR_DUMMIES     1
+#define GD55_WRSR2              0x31  /* Write status register 2            */
+#define GD55_WRNVCR             0xb1  /* Write Non-Volatile config register */
+#define GD55_WRENVSC            0x50  /* Write en. Volatile config register */
+#define GD55_WRVCR              0x91  /* Write Volatile config register     */
+#define GD55_WREAR              0xc5  /* Write Extended address register    */
+#define GD55_EARR               0xc8  /* Read extended address register     */
+#define GD55_RSFDP              0x5a  /* Read SFDP                          */
+#define GD55_RDSCUR             0x48  /* Read security register             */
+#define GD55_WRSCUR             0x42  /* Write security register            */
+#define GD55_ERSCUR             0x44  /* Erase security register            */
+#define GD55_RSTEN              0x66  /* Reset Enable                       */
+#define GD55_RST                0x99  /* Reset Memory                       */
+#define GD55_GBSL               0x7e  /* Global block/sector lock           */
+#define GD55_GBSUL              0x98  /* Global block/sector unlock         */
+
+/* Read ID (RDID) register values                                           */
+
+#define GD55_MANUFACTURER       0xc8  /* GigaSevice manufacturer ID         */
+
+/* JEDEC Read ID register values                                            */
+
+#define GD55_JEDEC_MANUFACTURER 0xc8 /* GigaDevice manufacturer ID          */
+
+#define GD55B_JEDEC_MEMORY_TYPE 0x47 /* GD55B memory type, 3V               */
+#define GD55L_JEDEC_MEMORY_TYPE 0x67 /* GD55L memory type, 1.8V             */
+#define GD55_JEDEC_1G_CAPACITY  0x1b /* 1Gbit memory capacity               */
+#define GD55_JEDEC_2G_CAPACITY  0x1c /* 2Gbit memory capacity               */
+
+/* GD55 devices all have identical sector sizes:
+ * block protection size: 64KiB
+ * sector size:           4KiB
+ * page size:             256B
+ */
+
+#define GD55_SECTOR_SHIFT       (12)
+#define GD55_SECTOR_SIZE        (1 << GD55_SECTOR_SHIFT)           /* 4KiB  */
+#define GD55_PAGE_SHIFT         (8)                                /* 256B  */
+#define GD55_PAGE_SIZE          (1 << GD55_PAGE_SHIFT)
+#define GD55_BP_SHIFT           (16)
+#define GD55_BP_SIZE            (1 << GD55_BP_SHIFT)               /* 64KiB */
+#define GD55_MIN_BP_BLKS        (GD55_BP_SIZE  >> GD55_PAGE_SHIFT)
+#define GD55_SECTORS_PER_BP_BLK (GD55_BP_SIZE / GD55_SECTOR_SIZE)
+
+/* GD55B01xx (128 MiB) memory capacity                                      */
+
+#define GD55_NSECTORS_1GBIT     (32768)
+
+/* GD55B02xx (256 MiB) memory capacity                                      */
+
+#define GD55_NSECTORS_2GBIT     (65536)
+
+/* 512 byte sector support **************************************************/
+
+#define GD55_SECTOR512_SHIFT    (9)
+#define GD55_SECTOR512_SIZE     (1 << GD55_SECTOR512_SHIFT)
+
+/* Status register 1 bit definitions                                        */
+
+#define GD55_SR_WIP             (1 << 0)  /* Bit 0: Write in progress       */
+#define GD55_SR_WEL             (1 << 1)  /* Bit 1: Write enable latch      */
+#define GD55_SR_BP_SHIFT        (2)       /* Bits 2-6: Block protect bits   */
+#define GD55_SR_BP_MASK         (31 << GD55_SR_BP_SHIFT)
+#define GD55_STATUS_BP_NONE     (0 << GD55_SR_BP_SHIFT)
+#define GD55_STATUS_BP_ALL      (7 << GD55_SR_BP_SHIFT)
+#define GD55_STATUS_TB_MASK     (1 << 6)  /* BP4 Top/Bottom Protect         */
+#define GD55_STATUS_TB_TOP      (0 << 6)  /*   = 0, BP3..0 protect Top down */
+#define GD55_STATUS_TB_BOTTOM   (1 << 6)  /*   = 1, BP3..0   "   Bottom up  */
+#define GD55_SR_BP_TOP(b)       (((b + 1) << GD55_SR_BP_SHIFT) | \
+                                 GD55_STATUS_TB_TOP)
+#define GD55_SR_BP_BOTTOM(b)    (((b + 1) << GD55_SR_BP_SHIFT) | \
+                                 GD55_STATUS_TB_BOTTOM)
+#define GD55_BP_ALL             (14 << GD55_SR_BP_SHIFT)
+                                          /* GD55B01 needs BP bits = 0xx11xx
+                                           * GD55B02 needs BP bits = 0xx111x
+                                           */
+#define GD55_SR_SRP0            (1 << 7)  /* Bit 7: SR protect bit 0        */
+
+/* Status register 2 bit definitions                                        */
+
+#define GD55_SR_ADS             (1 << 0)  /* Bit 0: Current Address Mode    */
+                                          /* Bit 1 - reserved               */
+#define GD55_SR_SUS2            (1 << 2)  /* Bit 2: Program suspend bit 2   */
+#define GD55_SR_LB              (1 << 3)  /* Bit 3: Security Register Lock  */
+#define GD55_SR_PE              (1 << 4)  /* Bit 4: Program Error Bit       */
+#define GD55_SR_EE              (1 << 5)  /* Bit 5: Erase Error Bit         */
+#define GD55_SR_SRP1            (1 << 6)  /* Bit 6: SR protection bit 1     */
+#define GD55_SR_SUS1            (1 << 7)  /* Bit 7: Program suspend bit 1   */
+
+/* Non-volatile and volatile config register addresses and bits             */
+
+#define GD55_DUMMY_CYCLES_REG   1         /* Dummy Cycle Configuration      */
+#define GD55_ODT_DS_REG         3         /* On-die termination and driver
+                                           * strength configuration
+                                           */
+#define GD55_DLP_PROT_REG       4         /* Data Learning and protect mode */
+#define GD55_PROT_MODE_MASK     (1 << 2)  /* Bit 2, BP or WPS mode          */
+#define GD55_PROT_MODE_WPS      (0 << 2)  /* 0 = Sector Protect mode        */
+#define GD55_PROT_MODE_BP       (1 << 2)  /* 1 = Block Protect mode (def.)  */
+#define GD55_4BYTE_MODE_REG     5         /* 3 pr 4-byte address mode       */
+#define GD55_XIP_MODE_REG       6         /* XIP (continuous read) mode     */
+#define GD55_WRAP_CONFIG_REG    7         /* Wrap mode (none/64/32/16 byte) */
+
+/* Block protection bit */
+
+#define GD55_BLK_PROTECTED      (1 << 0)  /* lsb set means block is locked  */
+
+/* Cache flags **************************************************************/
+
+#define GD55_CACHE_VALID        (1 << 0)  /* 1=Cache has valid data         */
+#define GD55_CACHE_DIRTY        (1 << 1)  /* 1=Cache is dirty               */
+#define GD55_CACHE_ERASED       (1 << 2)  /* 1=Backing FLASH is erased      */
+
+#define IS_VALID(p)             ((((p)->flags) & GD55_CACHE_VALID) != 0)
+#define IS_DIRTY(p)             ((((p)->flags) & GD55_CACHE_DIRTY) != 0)
+#define IS_ERASED(p)            ((((p)->flags) & GD55_CACHE_ERASED) != 0)
+
+#define SET_VALID(p)  do { (p)->flags |= GD55_CACHE_VALID; } while (0)

Review Comment:
   align



##########
drivers/mtd/gd55.c:
##########
@@ -0,0 +1,2082 @@
+/****************************************************************************
+ * drivers/mtd/gd55.c
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef CONFIG_MTD_GD55_SECTOR512
+#  include <stdlib.h>
+#  include <string.h>
+#endif
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/spi/qspi.h>
+#include <nuttx/mtd/mtd.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* 4 byte addressing is needed for addresses needing more than a 3 byte
+ * address, i.e. 16Mbyte
+ */
+
+#define MODE_3BYTE_LIMIT        ((16 * 1024 * 1024))
+
+/* GD55 Commands                                                            */
+
+#define GD55_QREAD              0x6b  /* Quad output fast read              */
+#define GD55_QREAD_DUMMIES      8
+#define GD55_QC_READ            0xeb  /* Quad output continuous fast read   */
+#define GD55_QC_READ_DUMMIES    6
+#define GD55_EQPP               0xc2  /* Extended quad page program         */
+#define GD55_EQPP_DUMMIES       0     /* No dummy clocks                    */
+#define GD55_SE                 0x20  /* 4Kb Sector erase                   */
+#define GD55_BE32               0x52  /* 32Kbit block Erase                 */
+#define GD55_BE64               0xd8  /* 64Kbit block Erase                 */
+#define GD55_CE                 0x60  /* Chip erase (alternate)             */
+#define GD55_WREN               0x06  /* Write Enable                       */
+#define GD55_WRDI               0x04  /* Write Disable                      */
+#define GD55_RDSR1              0x05  /* Read status register 1             */
+#define GD55_EN4B               0xb7  /* Enable 4 byte Addressing Mode      */
+#define GD55_DIS4B              0xe9  /* Disable 4 byte Addressing Mode     */
+#define GD55_IBSL               0x36  /* Individual block/sector lock       */
+#define GD55_IBSUL              0x39  /* Individual block/sector unlock     */
+#define GD55_RIBSL              0x3d  /* Read individual block/sector lock  */
+#define GD55_RDNVCR             0xb5  /* Read Non-Volatile config register  */
+#define GD55_RD_NVCR_DUMMIES    8
+#define GD55_RDSR2              0x35  /* Read status register 2             */
+#define GD55_WRSR1              0x01  /* Write status register 1            */
+#define GD55_SE_ALT             0x21  /* Alternate 4Kb Sector erase         */
+#define GD55_QC_READ_ALT        0xec  /* Quad output continuous fast read   */
+#define GD55_4B_QDTR_READ       0xed  /* Quad I/O DTR read                  */
+#define GD55_4B_QDTR_READ_ALT   0xee  /* Alternate quad I/O DTR read        */
+#define GD55_PP                 0x02  /* Page program (SPI, not used)       */
+#define GD55_PP_ALT             0x12  /* Aternate page program (SPI)        */
+#define GD55_BE32_ALT           0x5c  /* Alternate 32Kbit block Erase       */
+#define GD55_BE64_ALT           0xd8  /* ALternate 64Kbit block Erase       */
+#define GD55_CE_ALT             0xc7  /* Alternate chip erase               */
+#define GD55_QPP                0x32  /* Quad page program                  */
+#define GD55_QPP_ALT            0x34  /* ALternate quad page program        */
+#define GD55_QPP_DUMMIES        0     /* No dummy clocks                    */
+#define GD55_QPIEN              0x38  /* Enable QPI Operation               */
+#define GD55_QPIDIS             0xff  /* Disable QPI Operation              */
+#define GD55_DP                 0xb9  /* Deep power down                    */
+#define GD55_RDP                0xab  /* Release deep power down            */
+#define GD55_RUID               0x4b  /* Read Unique ID                     */
+#define GD55_RDID               0x9e  /* Read identification                */
+#define GD55_RDID_ALT           0x9f  /* Read identification (alternate)    */
+#define GD55_PE_SUSPEND         0x75  /* Suspends program/erase             */
+#define GD55_PE_RESUME          0x7a  /* Resume program                     */
+#define GD55_RDVCR              0x85  /* Read Volatile config register      */
+#define GD55_RD_VCR_DUMMIES     1
+#define GD55_WRSR2              0x31  /* Write status register 2            */
+#define GD55_WRNVCR             0xb1  /* Write Non-Volatile config register */
+#define GD55_WRENVSC            0x50  /* Write en. Volatile config register */
+#define GD55_WRVCR              0x91  /* Write Volatile config register     */
+#define GD55_WREAR              0xc5  /* Write Extended address register    */
+#define GD55_EARR               0xc8  /* Read extended address register     */
+#define GD55_RSFDP              0x5a  /* Read SFDP                          */
+#define GD55_RDSCUR             0x48  /* Read security register             */
+#define GD55_WRSCUR             0x42  /* Write security register            */
+#define GD55_ERSCUR             0x44  /* Erase security register            */
+#define GD55_RSTEN              0x66  /* Reset Enable                       */
+#define GD55_RST                0x99  /* Reset Memory                       */
+#define GD55_GBSL               0x7e  /* Global block/sector lock           */
+#define GD55_GBSUL              0x98  /* Global block/sector unlock         */
+
+/* Read ID (RDID) register values                                           */
+
+#define GD55_MANUFACTURER       0xc8  /* GigaSevice manufacturer ID         */
+
+/* JEDEC Read ID register values                                            */
+
+#define GD55_JEDEC_MANUFACTURER 0xc8 /* GigaDevice manufacturer ID          */
+
+#define GD55B_JEDEC_MEMORY_TYPE 0x47 /* GD55B memory type, 3V               */
+#define GD55L_JEDEC_MEMORY_TYPE 0x67 /* GD55L memory type, 1.8V             */
+#define GD55_JEDEC_1G_CAPACITY  0x1b /* 1Gbit memory capacity               */
+#define GD55_JEDEC_2G_CAPACITY  0x1c /* 2Gbit memory capacity               */
+
+/* GD55 devices all have identical sector sizes:
+ * block protection size: 64KiB
+ * sector size:           4KiB
+ * page size:             256B
+ */
+
+#define GD55_SECTOR_SHIFT       (12)
+#define GD55_SECTOR_SIZE        (1 << GD55_SECTOR_SHIFT)           /* 4KiB  */
+#define GD55_PAGE_SHIFT         (8)                                /* 256B  */
+#define GD55_PAGE_SIZE          (1 << GD55_PAGE_SHIFT)
+#define GD55_BP_SHIFT           (16)
+#define GD55_BP_SIZE            (1 << GD55_BP_SHIFT)               /* 64KiB */
+#define GD55_MIN_BP_BLKS        (GD55_BP_SIZE  >> GD55_PAGE_SHIFT)
+#define GD55_SECTORS_PER_BP_BLK (GD55_BP_SIZE / GD55_SECTOR_SIZE)
+
+/* GD55B01xx (128 MiB) memory capacity                                      */
+
+#define GD55_NSECTORS_1GBIT     (32768)
+
+/* GD55B02xx (256 MiB) memory capacity                                      */
+
+#define GD55_NSECTORS_2GBIT     (65536)
+
+/* 512 byte sector support **************************************************/
+
+#define GD55_SECTOR512_SHIFT    (9)
+#define GD55_SECTOR512_SIZE     (1 << GD55_SECTOR512_SHIFT)
+
+/* Status register 1 bit definitions                                        */
+
+#define GD55_SR_WIP             (1 << 0)  /* Bit 0: Write in progress       */
+#define GD55_SR_WEL             (1 << 1)  /* Bit 1: Write enable latch      */
+#define GD55_SR_BP_SHIFT        (2)       /* Bits 2-6: Block protect bits   */
+#define GD55_SR_BP_MASK         (31 << GD55_SR_BP_SHIFT)
+#define GD55_STATUS_BP_NONE     (0 << GD55_SR_BP_SHIFT)
+#define GD55_STATUS_BP_ALL      (7 << GD55_SR_BP_SHIFT)
+#define GD55_STATUS_TB_MASK     (1 << 6)  /* BP4 Top/Bottom Protect         */
+#define GD55_STATUS_TB_TOP      (0 << 6)  /*   = 0, BP3..0 protect Top down */
+#define GD55_STATUS_TB_BOTTOM   (1 << 6)  /*   = 1, BP3..0   "   Bottom up  */
+#define GD55_SR_BP_TOP(b)       (((b + 1) << GD55_SR_BP_SHIFT) | \
+                                 GD55_STATUS_TB_TOP)
+#define GD55_SR_BP_BOTTOM(b)    (((b + 1) << GD55_SR_BP_SHIFT) | \
+                                 GD55_STATUS_TB_BOTTOM)
+#define GD55_BP_ALL             (14 << GD55_SR_BP_SHIFT)
+                                          /* GD55B01 needs BP bits = 0xx11xx
+                                           * GD55B02 needs BP bits = 0xx111x
+                                           */
+#define GD55_SR_SRP0            (1 << 7)  /* Bit 7: SR protect bit 0        */
+
+/* Status register 2 bit definitions                                        */
+
+#define GD55_SR_ADS             (1 << 0)  /* Bit 0: Current Address Mode    */
+                                          /* Bit 1 - reserved               */
+#define GD55_SR_SUS2            (1 << 2)  /* Bit 2: Program suspend bit 2   */
+#define GD55_SR_LB              (1 << 3)  /* Bit 3: Security Register Lock  */
+#define GD55_SR_PE              (1 << 4)  /* Bit 4: Program Error Bit       */
+#define GD55_SR_EE              (1 << 5)  /* Bit 5: Erase Error Bit         */
+#define GD55_SR_SRP1            (1 << 6)  /* Bit 6: SR protection bit 1     */
+#define GD55_SR_SUS1            (1 << 7)  /* Bit 7: Program suspend bit 1   */
+
+/* Non-volatile and volatile config register addresses and bits             */
+
+#define GD55_DUMMY_CYCLES_REG   1         /* Dummy Cycle Configuration      */
+#define GD55_ODT_DS_REG         3         /* On-die termination and driver
+                                           * strength configuration
+                                           */
+#define GD55_DLP_PROT_REG       4         /* Data Learning and protect mode */
+#define GD55_PROT_MODE_MASK     (1 << 2)  /* Bit 2, BP or WPS mode          */
+#define GD55_PROT_MODE_WPS      (0 << 2)  /* 0 = Sector Protect mode        */
+#define GD55_PROT_MODE_BP       (1 << 2)  /* 1 = Block Protect mode (def.)  */
+#define GD55_4BYTE_MODE_REG     5         /* 3 pr 4-byte address mode       */
+#define GD55_XIP_MODE_REG       6         /* XIP (continuous read) mode     */
+#define GD55_WRAP_CONFIG_REG    7         /* Wrap mode (none/64/32/16 byte) */
+
+/* Block protection bit */
+
+#define GD55_BLK_PROTECTED      (1 << 0)  /* lsb set means block is locked  */
+
+/* Cache flags **************************************************************/
+
+#define GD55_CACHE_VALID        (1 << 0)  /* 1=Cache has valid data         */
+#define GD55_CACHE_DIRTY        (1 << 1)  /* 1=Cache is dirty               */
+#define GD55_CACHE_ERASED       (1 << 2)  /* 1=Backing FLASH is erased      */
+
+#define IS_VALID(p)             ((((p)->flags) & GD55_CACHE_VALID) != 0)
+#define IS_DIRTY(p)             ((((p)->flags) & GD55_CACHE_DIRTY) != 0)
+#define IS_ERASED(p)            ((((p)->flags) & GD55_CACHE_ERASED) != 0)
+
+#define SET_VALID(p)  do { (p)->flags |= GD55_CACHE_VALID; } while (0)
+#define SET_DIRTY(p)  do { (p)->flags |= GD55_CACHE_DIRTY; } while (0)
+#define SET_ERASED(p) do { (p)->flags |= GD55_CACHE_ERASED; } while (0)
+
+#define CLR_VALID(p)  do { (p)->flags &= ~GD55_CACHE_VALID; } while (0)
+#define CLR_DIRTY(p)  do { (p)->flags &= ~GD55_CACHE_DIRTY; } while (0)
+#define CLR_ERASED(p) do { (p)->flags &= ~GD55_CACHE_ERASED; } while (0)
+
+#define GD55_ERASED_STATE       0xff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Internal state of the MTD device */
+
+struct gd55_dev_s
+{
+  struct mtd_dev_s       mtd;         /* MTD interface                      */
+  FAR struct qspi_dev_s *qspi;        /* QuadSPI interface                  */
+  FAR uint8_t           *cmdbuf;      /* Allocated command buffer           */
+  FAR uint8_t           *readbuf;     /* Allocated status read buffer       */
+  uint32_t               nsectors;    /* Number of erase sectors            */
+#ifdef CONFIG_MTD_GD55_SECTOR512
+  uint8_t                flags;       /* Buffered sector flags              */
+  uint16_t               esectno;     /* Erase sector number in the cache   */
+  FAR uint8_t           *sector;      /* Allocated sector data              */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD driver methods */
+
+static int     gd55_erase(FAR struct mtd_dev_s *dev, off_t startblock,
+                          size_t nblocks);
+static ssize_t gd55_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+                          size_t nblocks, FAR uint8_t *buf);
+static ssize_t gd55_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+                           size_t nblocks, FAR const uint8_t *buf);
+static ssize_t gd55_read(FAR struct mtd_dev_s *dev, off_t offset,
+                         size_t nbytes, FAR uint8_t *buffer);
+static int     gd55_ioctl(FAR struct mtd_dev_s *dev, int cmd,
+                          unsigned long arg);
+
+/* Internal driver methods */
+
+static void     gd55_lock(FAR struct gd55_dev_s *priv);
+static void     gd55_unlock(FAR struct gd55_dev_s *priv);
+static int      gd55_command_read(FAR struct gd55_dev_s *priv, uint8_t cmd,
+                                  FAR void *buffer, size_t buflen);
+static int      gd55_command(FAR struct gd55_dev_s *priv, uint8_t cmd);
+static int      gd55_command_address(FAR struct gd55_dev_s *priv,
+                                     uint8_t cmd, off_t addr,
+                                     uint8_t addrlen);
+static int      gd55_readid(FAR struct gd55_dev_s *priv);
+static int      gd55_protect(FAR struct gd55_dev_s *priv, off_t startblock,
+                             size_t nblocks);
+static int      gd55_unprotect(FAR struct gd55_dev_s *priv, off_t startblock,
+                               size_t nblocks);
+static bool     gd55_isprotected(FAR struct gd55_dev_s *priv, off_t addr,
+                                 uint8_t status);
+static int      gd55_read_bytes(FAR struct gd55_dev_s *priv,
+                                FAR uint8_t *buffer, off_t address,
+                                size_t buflen);
+static uint8_t  gd55_read_status1(FAR struct gd55_dev_s *priv);
+static uint8_t  gd55_read_status2(FAR struct gd55_dev_s *priv);
+static void     gd55_write_status1(FAR struct gd55_dev_s *priv);
+static int      gd55_command_write(FAR struct gd55_dev_s *priv, uint8_t cmd,
+                                   FAR const void *buffer, size_t buflen);
+static void     gd55_write_enable(FAR struct gd55_dev_s *priv);
+static int      gd55_write_page(FAR struct gd55_dev_s *priv,
+                                FAR const uint8_t *buffer, off_t address,
+                                size_t buflen);
+static int      gd55_erase_sector(FAR struct gd55_dev_s *priv, off_t sector);
+
+static int      gd55_erase_chip(FAR struct gd55_dev_s *priv);
+#ifdef CONFIG_MTD_GD55_SECTOR512
+static int          gd55_flush_cache(FAR struct gd55_dev_s *priv);
+static FAR uint8_t *gd55_read_cache(FAR struct gd55_dev_s *priv,
+                                    off_t sector);
+static void         gd55_erase_cache(FAR struct gd55_dev_s *priv,
+                                     off_t sector);
+static int          gd55_write_cache(FAR struct gd55_dev_s *priv,
+                                     FAR const uint8_t *buffer, off_t sector,
+                                     size_t nsectors);
+#else
+static int          gd55_erase_64kblock(FAR struct gd55_dev_s *priv,
+                                        off_t sector);
+static int          gd55_erase_32kblock(FAR struct gd55_dev_s *priv,
+                                        off_t sector);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: gd55_lock
+ *
+ * Description:
+ * On QSPI buses where there are multiple devices, it will be necessary to
+ * lock QSPI to have exclusive access to the bus for a sequence of
+ * transfers.  The bus should be locked before the chip is selected.
+ *
+ * This is a blocking call and will not return until we have exclusive
+ * access to the SPI bus. We will retain that exclusive access until the
+ * bus is unlocked.
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *
+ * Returned Value:
+ *   none
+ *
+ ****************************************************************************/
+
+static void gd55_lock(FAR struct gd55_dev_s *priv)
+{
+  QSPI_LOCK(priv->qspi, true);
+
+  /* After locking the QSPI bus, the we also need call the setfrequency,
+   * setbits and setmode methods to make sure that the QSPI is properly
+   * configured for the device.  If the QSPI bus is being shared, then it
+   * may have been left in an incompatible state.
+   */
+
+  QSPI_SETMODE(priv->qspi, CONFIG_MTD_GD55_QSPIMODE);
+  QSPI_SETBITS(priv->qspi, 8);
+  QSPI_SETFREQUENCY(priv->qspi, CONFIG_MTD_GD55_FREQUENCY);
+}
+
+/****************************************************************************
+ * Name: gd55_unlock
+ *
+ * Description:
+ * On QSPI buses where there are multiple devices, it will have been
+ * necessary to lock QSSPI to have exclusive access to the bus for a sequence
+ * of transfers.  The bus must be unlocked after the transfers to relinquish
+ * the exclusive access from the call to LOCK the bus.
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *
+ * Returned Value:
+ *   none
+ *
+ ****************************************************************************/
+
+static void gd55_unlock(FAR struct gd55_dev_s *priv)
+{
+  QSPI_LOCK(priv->qspi, false);
+}
+
+/****************************************************************************
+ * Name: gd55_command_read
+ *
+ * Description:
+ *   Read data from the device.
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   cmd          - the read command to be used
+ *   buffer       - pointer to variable to store the read data
+ *   buflen       - the number of bytes to be read into the buffer
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_command_read(FAR struct gd55_dev_s *priv, uint8_t cmd,
+                      FAR void *buffer, size_t buflen)
+{
+  struct qspi_cmdinfo_s cmdinfo;
+
+  finfo("CMD: %02x buflen: %lu\n", cmd, (unsigned long)buflen);
+
+  cmdinfo.flags   = QSPICMD_READDATA;
+  cmdinfo.addrlen = 0;
+  cmdinfo.cmd     = cmd;
+  cmdinfo.buflen  = buflen;
+  cmdinfo.addr    = 0;
+  cmdinfo.buffer  = buffer;
+
+  return QSPI_COMMAND(priv->qspi, &cmdinfo);
+}
+
+/****************************************************************************
+ * Name: gd55_command
+ *
+ * Description:
+ *   Send a command to the device.
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   cmd          - the command to be sent
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_command(FAR struct gd55_dev_s *priv, uint8_t cmd)
+{
+  struct qspi_cmdinfo_s cmdinfo;
+
+  finfo("CMD: %02x\n", cmd);
+
+  cmdinfo.flags   = 0;
+  cmdinfo.addrlen = 0;
+  cmdinfo.cmd     = cmd;
+  cmdinfo.buflen  = 0;
+  cmdinfo.addr    = 0;
+  cmdinfo.buffer  = NULL;
+
+  return QSPI_COMMAND(priv->qspi, &cmdinfo);
+}
+
+/****************************************************************************
+ * Name: gd55_command_write
+ *
+ * Description:
+ *   Send a command to the device with data
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   cmd          - the command to be sent
+ *   buffer       - pointer to variable with the data to write
+ *   buflen       - the number of data bytes to be written
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_command_write(FAR struct gd55_dev_s *priv, uint8_t cmd,
+                              FAR const void *buffer, size_t buflen)
+{
+  struct qspi_cmdinfo_s cmdinfo;
+
+  finfo("CMD: %02x buflen: %lu\n", cmd, (unsigned long)buflen);
+
+  cmdinfo.flags   = QSPICMD_WRITEDATA;
+  cmdinfo.addrlen = 0;
+  cmdinfo.cmd     = cmd;
+  cmdinfo.buflen  = buflen;
+  cmdinfo.addr    = 0;
+  cmdinfo.buffer  = (FAR void *)buffer;
+
+  return QSPI_COMMAND(priv->qspi, &cmdinfo);
+}
+
+/****************************************************************************
+ * Name: gd55_command_address
+ *
+ * Description:
+ *   Send a command with an associated address to the device
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   cmd          - the command to be sent
+ *   addr         - address to send
+ *   addrlen      - address length
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_command_address(FAR struct gd55_dev_s *priv, uint8_t cmd,
+                         off_t addr, uint8_t addrlen)
+{
+  struct qspi_cmdinfo_s cmdinfo;
+
+  finfo("CMD: %02x Address: %04lx addrlen=%d\n",
+        cmd, (unsigned long)addr, addrlen);
+
+  cmdinfo.flags   = QSPICMD_ADDRESS;
+  cmdinfo.addrlen = addrlen;
+  cmdinfo.cmd     = cmd;
+  cmdinfo.buflen  = 0;
+  cmdinfo.addr    = addr;
+  cmdinfo.buffer  = NULL;
+
+  return QSPI_COMMAND(priv->qspi, &cmdinfo);
+}
+
+/****************************************************************************
+ * Name: gd55_read_bytes
+ *
+ * Description:
+ *   Read data from the device
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   buffer       - pointer to buffer to read the data to
+ *   address      - address to read from
+ *   buflen       - number of bytes to read (buffer length)
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_read_bytes(FAR struct gd55_dev_s *priv, FAR uint8_t *buffer,
+                    off_t address, size_t buflen)
+{
+  bool                  mode_4byte_addr;
+  int                   ret;
+  struct qspi_meminfo_s meminfo;
+
+  /* Check if any address exceeds range of 3 byte addressing */
+
+  if ((address + buflen) >= MODE_3BYTE_LIMIT)
+    {
+      gd55_command(priv, GD55_EN4B);
+      mode_4byte_addr = true;
+    }
+
+  finfo("4byte mode: %s\taddress: %08lx\tnbytes: %d\n",
+         mode_4byte_addr ? "true" : "false", (long)address, (int)buflen);
+
+  meminfo.flags   = QSPIMEM_READ | QSPIMEM_QUADIO;
+  meminfo.dummies = GD55_QC_READ_DUMMIES;
+  meminfo.buflen  = buflen;
+  meminfo.cmd     = GD55_QC_READ;
+  meminfo.addr    = address;
+  meminfo.addrlen = mode_4byte_addr ? 4 : 3;
+  meminfo.buffer  = buffer;
+
+  ret = QSPI_MEMORY(priv->qspi, &meminfo);
+  if (mode_4byte_addr)
+    {
+      gd55_command(priv, GD55_DIS4B);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: gd55_write_page
+ *
+ * Description:
+ *   Write a page of data to the device
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   buffer       - pointer to the buffer with the data to write
+ *   address      - address to write to
+ *   buflen       - number of bytes to write (buffer length)
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_write_page(FAR struct gd55_dev_s *priv,
+                    FAR const uint8_t *buffer,
+                    off_t address, size_t buflen)

Review Comment:
   align



##########
drivers/mtd/gd55.c:
##########
@@ -0,0 +1,2082 @@
+/****************************************************************************
+ * drivers/mtd/gd55.c
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef CONFIG_MTD_GD55_SECTOR512
+#  include <stdlib.h>
+#  include <string.h>
+#endif
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/spi/qspi.h>
+#include <nuttx/mtd/mtd.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* 4 byte addressing is needed for addresses needing more than a 3 byte
+ * address, i.e. 16Mbyte
+ */
+
+#define MODE_3BYTE_LIMIT        ((16 * 1024 * 1024))
+
+/* GD55 Commands                                                            */
+
+#define GD55_QREAD              0x6b  /* Quad output fast read              */
+#define GD55_QREAD_DUMMIES      8
+#define GD55_QC_READ            0xeb  /* Quad output continuous fast read   */
+#define GD55_QC_READ_DUMMIES    6
+#define GD55_EQPP               0xc2  /* Extended quad page program         */
+#define GD55_EQPP_DUMMIES       0     /* No dummy clocks                    */
+#define GD55_SE                 0x20  /* 4Kb Sector erase                   */
+#define GD55_BE32               0x52  /* 32Kbit block Erase                 */
+#define GD55_BE64               0xd8  /* 64Kbit block Erase                 */
+#define GD55_CE                 0x60  /* Chip erase (alternate)             */
+#define GD55_WREN               0x06  /* Write Enable                       */
+#define GD55_WRDI               0x04  /* Write Disable                      */
+#define GD55_RDSR1              0x05  /* Read status register 1             */
+#define GD55_EN4B               0xb7  /* Enable 4 byte Addressing Mode      */
+#define GD55_DIS4B              0xe9  /* Disable 4 byte Addressing Mode     */
+#define GD55_IBSL               0x36  /* Individual block/sector lock       */
+#define GD55_IBSUL              0x39  /* Individual block/sector unlock     */
+#define GD55_RIBSL              0x3d  /* Read individual block/sector lock  */
+#define GD55_RDNVCR             0xb5  /* Read Non-Volatile config register  */
+#define GD55_RD_NVCR_DUMMIES    8
+#define GD55_RDSR2              0x35  /* Read status register 2             */
+#define GD55_WRSR1              0x01  /* Write status register 1            */
+#define GD55_SE_ALT             0x21  /* Alternate 4Kb Sector erase         */
+#define GD55_QC_READ_ALT        0xec  /* Quad output continuous fast read   */
+#define GD55_4B_QDTR_READ       0xed  /* Quad I/O DTR read                  */
+#define GD55_4B_QDTR_READ_ALT   0xee  /* Alternate quad I/O DTR read        */
+#define GD55_PP                 0x02  /* Page program (SPI, not used)       */
+#define GD55_PP_ALT             0x12  /* Aternate page program (SPI)        */
+#define GD55_BE32_ALT           0x5c  /* Alternate 32Kbit block Erase       */
+#define GD55_BE64_ALT           0xd8  /* ALternate 64Kbit block Erase       */
+#define GD55_CE_ALT             0xc7  /* Alternate chip erase               */
+#define GD55_QPP                0x32  /* Quad page program                  */
+#define GD55_QPP_ALT            0x34  /* ALternate quad page program        */
+#define GD55_QPP_DUMMIES        0     /* No dummy clocks                    */
+#define GD55_QPIEN              0x38  /* Enable QPI Operation               */
+#define GD55_QPIDIS             0xff  /* Disable QPI Operation              */
+#define GD55_DP                 0xb9  /* Deep power down                    */
+#define GD55_RDP                0xab  /* Release deep power down            */
+#define GD55_RUID               0x4b  /* Read Unique ID                     */
+#define GD55_RDID               0x9e  /* Read identification                */
+#define GD55_RDID_ALT           0x9f  /* Read identification (alternate)    */
+#define GD55_PE_SUSPEND         0x75  /* Suspends program/erase             */
+#define GD55_PE_RESUME          0x7a  /* Resume program                     */
+#define GD55_RDVCR              0x85  /* Read Volatile config register      */
+#define GD55_RD_VCR_DUMMIES     1
+#define GD55_WRSR2              0x31  /* Write status register 2            */
+#define GD55_WRNVCR             0xb1  /* Write Non-Volatile config register */
+#define GD55_WRENVSC            0x50  /* Write en. Volatile config register */
+#define GD55_WRVCR              0x91  /* Write Volatile config register     */
+#define GD55_WREAR              0xc5  /* Write Extended address register    */
+#define GD55_EARR               0xc8  /* Read extended address register     */
+#define GD55_RSFDP              0x5a  /* Read SFDP                          */
+#define GD55_RDSCUR             0x48  /* Read security register             */
+#define GD55_WRSCUR             0x42  /* Write security register            */
+#define GD55_ERSCUR             0x44  /* Erase security register            */
+#define GD55_RSTEN              0x66  /* Reset Enable                       */
+#define GD55_RST                0x99  /* Reset Memory                       */
+#define GD55_GBSL               0x7e  /* Global block/sector lock           */
+#define GD55_GBSUL              0x98  /* Global block/sector unlock         */
+
+/* Read ID (RDID) register values                                           */
+
+#define GD55_MANUFACTURER       0xc8  /* GigaSevice manufacturer ID         */
+
+/* JEDEC Read ID register values                                            */
+
+#define GD55_JEDEC_MANUFACTURER 0xc8 /* GigaDevice manufacturer ID          */
+
+#define GD55B_JEDEC_MEMORY_TYPE 0x47 /* GD55B memory type, 3V               */
+#define GD55L_JEDEC_MEMORY_TYPE 0x67 /* GD55L memory type, 1.8V             */
+#define GD55_JEDEC_1G_CAPACITY  0x1b /* 1Gbit memory capacity               */
+#define GD55_JEDEC_2G_CAPACITY  0x1c /* 2Gbit memory capacity               */
+
+/* GD55 devices all have identical sector sizes:
+ * block protection size: 64KiB
+ * sector size:           4KiB
+ * page size:             256B
+ */
+
+#define GD55_SECTOR_SHIFT       (12)
+#define GD55_SECTOR_SIZE        (1 << GD55_SECTOR_SHIFT)           /* 4KiB  */
+#define GD55_PAGE_SHIFT         (8)                                /* 256B  */
+#define GD55_PAGE_SIZE          (1 << GD55_PAGE_SHIFT)
+#define GD55_BP_SHIFT           (16)
+#define GD55_BP_SIZE            (1 << GD55_BP_SHIFT)               /* 64KiB */
+#define GD55_MIN_BP_BLKS        (GD55_BP_SIZE  >> GD55_PAGE_SHIFT)
+#define GD55_SECTORS_PER_BP_BLK (GD55_BP_SIZE / GD55_SECTOR_SIZE)
+
+/* GD55B01xx (128 MiB) memory capacity                                      */
+
+#define GD55_NSECTORS_1GBIT     (32768)
+
+/* GD55B02xx (256 MiB) memory capacity                                      */
+
+#define GD55_NSECTORS_2GBIT     (65536)
+
+/* 512 byte sector support **************************************************/
+
+#define GD55_SECTOR512_SHIFT    (9)
+#define GD55_SECTOR512_SIZE     (1 << GD55_SECTOR512_SHIFT)
+
+/* Status register 1 bit definitions                                        */
+
+#define GD55_SR_WIP             (1 << 0)  /* Bit 0: Write in progress       */
+#define GD55_SR_WEL             (1 << 1)  /* Bit 1: Write enable latch      */
+#define GD55_SR_BP_SHIFT        (2)       /* Bits 2-6: Block protect bits   */
+#define GD55_SR_BP_MASK         (31 << GD55_SR_BP_SHIFT)
+#define GD55_STATUS_BP_NONE     (0 << GD55_SR_BP_SHIFT)
+#define GD55_STATUS_BP_ALL      (7 << GD55_SR_BP_SHIFT)
+#define GD55_STATUS_TB_MASK     (1 << 6)  /* BP4 Top/Bottom Protect         */
+#define GD55_STATUS_TB_TOP      (0 << 6)  /*   = 0, BP3..0 protect Top down */
+#define GD55_STATUS_TB_BOTTOM   (1 << 6)  /*   = 1, BP3..0   "   Bottom up  */
+#define GD55_SR_BP_TOP(b)       (((b + 1) << GD55_SR_BP_SHIFT) | \
+                                 GD55_STATUS_TB_TOP)
+#define GD55_SR_BP_BOTTOM(b)    (((b + 1) << GD55_SR_BP_SHIFT) | \
+                                 GD55_STATUS_TB_BOTTOM)
+#define GD55_BP_ALL             (14 << GD55_SR_BP_SHIFT)
+                                          /* GD55B01 needs BP bits = 0xx11xx
+                                           * GD55B02 needs BP bits = 0xx111x
+                                           */
+#define GD55_SR_SRP0            (1 << 7)  /* Bit 7: SR protect bit 0        */
+
+/* Status register 2 bit definitions                                        */
+
+#define GD55_SR_ADS             (1 << 0)  /* Bit 0: Current Address Mode    */
+                                          /* Bit 1 - reserved               */
+#define GD55_SR_SUS2            (1 << 2)  /* Bit 2: Program suspend bit 2   */
+#define GD55_SR_LB              (1 << 3)  /* Bit 3: Security Register Lock  */
+#define GD55_SR_PE              (1 << 4)  /* Bit 4: Program Error Bit       */
+#define GD55_SR_EE              (1 << 5)  /* Bit 5: Erase Error Bit         */
+#define GD55_SR_SRP1            (1 << 6)  /* Bit 6: SR protection bit 1     */
+#define GD55_SR_SUS1            (1 << 7)  /* Bit 7: Program suspend bit 1   */
+
+/* Non-volatile and volatile config register addresses and bits             */
+
+#define GD55_DUMMY_CYCLES_REG   1         /* Dummy Cycle Configuration      */
+#define GD55_ODT_DS_REG         3         /* On-die termination and driver
+                                           * strength configuration
+                                           */
+#define GD55_DLP_PROT_REG       4         /* Data Learning and protect mode */
+#define GD55_PROT_MODE_MASK     (1 << 2)  /* Bit 2, BP or WPS mode          */
+#define GD55_PROT_MODE_WPS      (0 << 2)  /* 0 = Sector Protect mode        */
+#define GD55_PROT_MODE_BP       (1 << 2)  /* 1 = Block Protect mode (def.)  */
+#define GD55_4BYTE_MODE_REG     5         /* 3 pr 4-byte address mode       */
+#define GD55_XIP_MODE_REG       6         /* XIP (continuous read) mode     */
+#define GD55_WRAP_CONFIG_REG    7         /* Wrap mode (none/64/32/16 byte) */
+
+/* Block protection bit */
+
+#define GD55_BLK_PROTECTED      (1 << 0)  /* lsb set means block is locked  */
+
+/* Cache flags **************************************************************/
+
+#define GD55_CACHE_VALID        (1 << 0)  /* 1=Cache has valid data         */
+#define GD55_CACHE_DIRTY        (1 << 1)  /* 1=Cache is dirty               */
+#define GD55_CACHE_ERASED       (1 << 2)  /* 1=Backing FLASH is erased      */
+
+#define IS_VALID(p)             ((((p)->flags) & GD55_CACHE_VALID) != 0)
+#define IS_DIRTY(p)             ((((p)->flags) & GD55_CACHE_DIRTY) != 0)
+#define IS_ERASED(p)            ((((p)->flags) & GD55_CACHE_ERASED) != 0)
+
+#define SET_VALID(p)  do { (p)->flags |= GD55_CACHE_VALID; } while (0)
+#define SET_DIRTY(p)  do { (p)->flags |= GD55_CACHE_DIRTY; } while (0)
+#define SET_ERASED(p) do { (p)->flags |= GD55_CACHE_ERASED; } while (0)
+
+#define CLR_VALID(p)  do { (p)->flags &= ~GD55_CACHE_VALID; } while (0)
+#define CLR_DIRTY(p)  do { (p)->flags &= ~GD55_CACHE_DIRTY; } while (0)
+#define CLR_ERASED(p) do { (p)->flags &= ~GD55_CACHE_ERASED; } while (0)
+
+#define GD55_ERASED_STATE       0xff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Internal state of the MTD device */
+
+struct gd55_dev_s
+{
+  struct mtd_dev_s       mtd;         /* MTD interface                      */
+  FAR struct qspi_dev_s *qspi;        /* QuadSPI interface                  */
+  FAR uint8_t           *cmdbuf;      /* Allocated command buffer           */
+  FAR uint8_t           *readbuf;     /* Allocated status read buffer       */
+  uint32_t               nsectors;    /* Number of erase sectors            */
+#ifdef CONFIG_MTD_GD55_SECTOR512
+  uint8_t                flags;       /* Buffered sector flags              */
+  uint16_t               esectno;     /* Erase sector number in the cache   */
+  FAR uint8_t           *sector;      /* Allocated sector data              */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD driver methods */
+
+static int     gd55_erase(FAR struct mtd_dev_s *dev, off_t startblock,
+                          size_t nblocks);
+static ssize_t gd55_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+                          size_t nblocks, FAR uint8_t *buf);
+static ssize_t gd55_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+                           size_t nblocks, FAR const uint8_t *buf);
+static ssize_t gd55_read(FAR struct mtd_dev_s *dev, off_t offset,
+                         size_t nbytes, FAR uint8_t *buffer);
+static int     gd55_ioctl(FAR struct mtd_dev_s *dev, int cmd,
+                          unsigned long arg);
+
+/* Internal driver methods */
+
+static void     gd55_lock(FAR struct gd55_dev_s *priv);
+static void     gd55_unlock(FAR struct gd55_dev_s *priv);
+static int      gd55_command_read(FAR struct gd55_dev_s *priv, uint8_t cmd,
+                                  FAR void *buffer, size_t buflen);
+static int      gd55_command(FAR struct gd55_dev_s *priv, uint8_t cmd);
+static int      gd55_command_address(FAR struct gd55_dev_s *priv,
+                                     uint8_t cmd, off_t addr,
+                                     uint8_t addrlen);
+static int      gd55_readid(FAR struct gd55_dev_s *priv);
+static int      gd55_protect(FAR struct gd55_dev_s *priv, off_t startblock,
+                             size_t nblocks);
+static int      gd55_unprotect(FAR struct gd55_dev_s *priv, off_t startblock,
+                               size_t nblocks);
+static bool     gd55_isprotected(FAR struct gd55_dev_s *priv, off_t addr,
+                                 uint8_t status);
+static int      gd55_read_bytes(FAR struct gd55_dev_s *priv,
+                                FAR uint8_t *buffer, off_t address,
+                                size_t buflen);
+static uint8_t  gd55_read_status1(FAR struct gd55_dev_s *priv);
+static uint8_t  gd55_read_status2(FAR struct gd55_dev_s *priv);
+static void     gd55_write_status1(FAR struct gd55_dev_s *priv);
+static int      gd55_command_write(FAR struct gd55_dev_s *priv, uint8_t cmd,
+                                   FAR const void *buffer, size_t buflen);
+static void     gd55_write_enable(FAR struct gd55_dev_s *priv);
+static int      gd55_write_page(FAR struct gd55_dev_s *priv,
+                                FAR const uint8_t *buffer, off_t address,
+                                size_t buflen);
+static int      gd55_erase_sector(FAR struct gd55_dev_s *priv, off_t sector);
+
+static int      gd55_erase_chip(FAR struct gd55_dev_s *priv);
+#ifdef CONFIG_MTD_GD55_SECTOR512
+static int          gd55_flush_cache(FAR struct gd55_dev_s *priv);
+static FAR uint8_t *gd55_read_cache(FAR struct gd55_dev_s *priv,
+                                    off_t sector);
+static void         gd55_erase_cache(FAR struct gd55_dev_s *priv,
+                                     off_t sector);
+static int          gd55_write_cache(FAR struct gd55_dev_s *priv,
+                                     FAR const uint8_t *buffer, off_t sector,
+                                     size_t nsectors);
+#else
+static int          gd55_erase_64kblock(FAR struct gd55_dev_s *priv,
+                                        off_t sector);
+static int          gd55_erase_32kblock(FAR struct gd55_dev_s *priv,
+                                        off_t sector);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: gd55_lock
+ *
+ * Description:
+ * On QSPI buses where there are multiple devices, it will be necessary to
+ * lock QSPI to have exclusive access to the bus for a sequence of
+ * transfers.  The bus should be locked before the chip is selected.
+ *
+ * This is a blocking call and will not return until we have exclusive
+ * access to the SPI bus. We will retain that exclusive access until the
+ * bus is unlocked.
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *
+ * Returned Value:
+ *   none
+ *
+ ****************************************************************************/
+
+static void gd55_lock(FAR struct gd55_dev_s *priv)
+{
+  QSPI_LOCK(priv->qspi, true);
+
+  /* After locking the QSPI bus, the we also need call the setfrequency,
+   * setbits and setmode methods to make sure that the QSPI is properly
+   * configured for the device.  If the QSPI bus is being shared, then it
+   * may have been left in an incompatible state.
+   */
+
+  QSPI_SETMODE(priv->qspi, CONFIG_MTD_GD55_QSPIMODE);
+  QSPI_SETBITS(priv->qspi, 8);
+  QSPI_SETFREQUENCY(priv->qspi, CONFIG_MTD_GD55_FREQUENCY);
+}
+
+/****************************************************************************
+ * Name: gd55_unlock
+ *
+ * Description:
+ * On QSPI buses where there are multiple devices, it will have been
+ * necessary to lock QSSPI to have exclusive access to the bus for a sequence
+ * of transfers.  The bus must be unlocked after the transfers to relinquish
+ * the exclusive access from the call to LOCK the bus.
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *
+ * Returned Value:
+ *   none
+ *
+ ****************************************************************************/
+
+static void gd55_unlock(FAR struct gd55_dev_s *priv)
+{
+  QSPI_LOCK(priv->qspi, false);
+}
+
+/****************************************************************************
+ * Name: gd55_command_read
+ *
+ * Description:
+ *   Read data from the device.
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   cmd          - the read command to be used
+ *   buffer       - pointer to variable to store the read data
+ *   buflen       - the number of bytes to be read into the buffer
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_command_read(FAR struct gd55_dev_s *priv, uint8_t cmd,
+                      FAR void *buffer, size_t buflen)
+{
+  struct qspi_cmdinfo_s cmdinfo;
+
+  finfo("CMD: %02x buflen: %lu\n", cmd, (unsigned long)buflen);
+
+  cmdinfo.flags   = QSPICMD_READDATA;
+  cmdinfo.addrlen = 0;
+  cmdinfo.cmd     = cmd;
+  cmdinfo.buflen  = buflen;
+  cmdinfo.addr    = 0;
+  cmdinfo.buffer  = buffer;
+
+  return QSPI_COMMAND(priv->qspi, &cmdinfo);
+}
+
+/****************************************************************************
+ * Name: gd55_command
+ *
+ * Description:
+ *   Send a command to the device.
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   cmd          - the command to be sent
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_command(FAR struct gd55_dev_s *priv, uint8_t cmd)
+{
+  struct qspi_cmdinfo_s cmdinfo;
+
+  finfo("CMD: %02x\n", cmd);
+
+  cmdinfo.flags   = 0;
+  cmdinfo.addrlen = 0;
+  cmdinfo.cmd     = cmd;
+  cmdinfo.buflen  = 0;
+  cmdinfo.addr    = 0;
+  cmdinfo.buffer  = NULL;
+
+  return QSPI_COMMAND(priv->qspi, &cmdinfo);
+}
+
+/****************************************************************************
+ * Name: gd55_command_write
+ *
+ * Description:
+ *   Send a command to the device with data
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   cmd          - the command to be sent
+ *   buffer       - pointer to variable with the data to write
+ *   buflen       - the number of data bytes to be written
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_command_write(FAR struct gd55_dev_s *priv, uint8_t cmd,
+                              FAR const void *buffer, size_t buflen)
+{
+  struct qspi_cmdinfo_s cmdinfo;
+
+  finfo("CMD: %02x buflen: %lu\n", cmd, (unsigned long)buflen);
+
+  cmdinfo.flags   = QSPICMD_WRITEDATA;
+  cmdinfo.addrlen = 0;
+  cmdinfo.cmd     = cmd;
+  cmdinfo.buflen  = buflen;
+  cmdinfo.addr    = 0;
+  cmdinfo.buffer  = (FAR void *)buffer;
+
+  return QSPI_COMMAND(priv->qspi, &cmdinfo);
+}
+
+/****************************************************************************
+ * Name: gd55_command_address
+ *
+ * Description:
+ *   Send a command with an associated address to the device
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   cmd          - the command to be sent
+ *   addr         - address to send
+ *   addrlen      - address length
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_command_address(FAR struct gd55_dev_s *priv, uint8_t cmd,
+                         off_t addr, uint8_t addrlen)
+{
+  struct qspi_cmdinfo_s cmdinfo;
+
+  finfo("CMD: %02x Address: %04lx addrlen=%d\n",
+        cmd, (unsigned long)addr, addrlen);
+
+  cmdinfo.flags   = QSPICMD_ADDRESS;
+  cmdinfo.addrlen = addrlen;
+  cmdinfo.cmd     = cmd;
+  cmdinfo.buflen  = 0;
+  cmdinfo.addr    = addr;
+  cmdinfo.buffer  = NULL;
+
+  return QSPI_COMMAND(priv->qspi, &cmdinfo);
+}
+
+/****************************************************************************
+ * Name: gd55_read_bytes
+ *
+ * Description:
+ *   Read data from the device
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   buffer       - pointer to buffer to read the data to
+ *   address      - address to read from
+ *   buflen       - number of bytes to read (buffer length)
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_read_bytes(FAR struct gd55_dev_s *priv, FAR uint8_t *buffer,
+                    off_t address, size_t buflen)
+{
+  bool                  mode_4byte_addr;
+  int                   ret;
+  struct qspi_meminfo_s meminfo;
+
+  /* Check if any address exceeds range of 3 byte addressing */
+
+  if ((address + buflen) >= MODE_3BYTE_LIMIT)
+    {
+      gd55_command(priv, GD55_EN4B);
+      mode_4byte_addr = true;
+    }
+
+  finfo("4byte mode: %s\taddress: %08lx\tnbytes: %d\n",
+         mode_4byte_addr ? "true" : "false", (long)address, (int)buflen);
+
+  meminfo.flags   = QSPIMEM_READ | QSPIMEM_QUADIO;
+  meminfo.dummies = GD55_QC_READ_DUMMIES;
+  meminfo.buflen  = buflen;
+  meminfo.cmd     = GD55_QC_READ;
+  meminfo.addr    = address;
+  meminfo.addrlen = mode_4byte_addr ? 4 : 3;
+  meminfo.buffer  = buffer;
+
+  ret = QSPI_MEMORY(priv->qspi, &meminfo);
+  if (mode_4byte_addr)
+    {
+      gd55_command(priv, GD55_DIS4B);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: gd55_write_page
+ *
+ * Description:
+ *   Write a page of data to the device
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   buffer       - pointer to the buffer with the data to write
+ *   address      - address to write to
+ *   buflen       - number of bytes to write (buffer length)
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_write_page(FAR struct gd55_dev_s *priv,
+                    FAR const uint8_t *buffer,
+                    off_t address, size_t buflen)
+{
+  struct qspi_meminfo_s meminfo;
+  uint8_t               status;
+  unsigned int          npages;
+  int                   ret;
+  int                   i;
+
+  npages = (buflen >> GD55_PAGE_SHIFT);
+
+  /* Check if address exceeds range of 3 byte addressing */
+
+  if ((address + buflen) >= MODE_3BYTE_LIMIT)
+    {
+      gd55_command(priv, GD55_EN4B);
+      meminfo.addrlen = 4;
+    }
+  else
+    {
+      gd55_command(priv, GD55_DIS4B);
+      meminfo.addrlen = 3;
+    }
+
+  finfo("4byte mode: %s\taddress: %08lx\tbuflen: %u\n",
+        (meminfo.addrlen == 4) ? "true" : "false", (unsigned long)address,
+        (unsigned)buflen);
+
+  /* Set up non-varying parts of transfer description */
+
+  meminfo.flags   = QSPIMEM_WRITE | QSPIMEM_QUADIO;
+  meminfo.cmd     = GD55_EQPP;
+  meminfo.buflen  = GD55_PAGE_SIZE;
+  meminfo.dummies = GD55_EQPP_DUMMIES;
+
+  /* Then write each page */
+
+  for (i = 0; i < npages; i++)
+    {
+      /* Set up varying parts of the transfer description */
+
+      meminfo.addr   = address;
+      meminfo.buffer = (FAR void *)buffer;
+
+      /* Write one page */
+
+      gd55_write_enable(priv);
+      ret = QSPI_MEMORY(priv->qspi, &meminfo);
+
+      if (ret < 0)
+        {
+          ferr("ERROR: QSPI_MEMORY failed writing address=%06jx\n",
+               (intmax_t)address);
+          return ret;
+        }
+
+      /* Update for the next time through the loop */
+
+      buffer  += GD55_PAGE_SIZE;
+      address += GD55_PAGE_SIZE;
+
+      /* Wait for write operation to finish */
+
+      do
+        {
+          status = gd55_read_status1(priv);
+        }
+      while ((status & GD55_SR_WIP) != 0);
+    }
+
+  if (meminfo.addrlen == 4)
+    {
+      gd55_command(priv, GD55_DIS4B);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: gd55_erase_sector
+ *
+ * Description:
+ *   Erase a single sector of th device
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   sector       - the sector to erase
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_erase_sector(FAR struct gd55_dev_s *priv, off_t sector)
+{
+  uint8_t status;
+  bool    mode_4byte_addr = false;
+  off_t   addr = sector << GD55_SECTOR_SHIFT;
+
+  finfo("4byte mode: %s\tsector: %08lx\n", mode_4byte_addr ?
+                                           "true" : "false",
+                                           (unsigned long)sector);
+
+  /* Check that the flash is ready and unprotected */
+
+  status = gd55_read_status1(priv);
+  if ((status & GD55_SR_WIP) == GD55_SR_WIP)
+    {
+      ferr("ERROR: Flash busy: %02x", status);
+      return -EBUSY;
+    }
+
+  if (gd55_isprotected(priv, addr, status))
+    {
+      ferr("ERROR: Flash protected at addr: %02" PRIx32, addr);
+      return -EACCES;
+    }
+
+  /* Check if address exceeds range of 3 byte addressing */
+
+  if (addr >= MODE_3BYTE_LIMIT)
+    {
+      gd55_command(priv, GD55_EN4B);
+      mode_4byte_addr = true;
+    }
+
+  /* Send the sector erase command */
+
+  gd55_write_enable(priv);
+  gd55_command_address(priv, GD55_SE, addr, mode_4byte_addr ? 4 : 3);
+
+  /* Wait for erasure to finish */
+
+  do
+    {
+      nxsig_usleep(10 * 1000); /* Typical sector erase time is 30ms */
+      status = gd55_read_status1(priv);
+    }
+  while ((status & GD55_SR_WIP) != 0);
+
+  if (mode_4byte_addr)
+    {
+      gd55_command(priv, GD55_DIS4B);
+    }
+
+  return OK;
+}
+
+#ifndef CONFIG_MTD_GD55_SECTOR512
+/****************************************************************************
+ * Name: gd55_erase_64kblock
+ *
+ * Description:
+ *   Erase a 64k block of the device
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   sector       - an address of a sector within the 64k block to erase
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_erase_64kblock(FAR struct gd55_dev_s *priv, off_t sector)
+{
+  off_t   addr = sector << GD55_SECTOR_SHIFT;
+  uint8_t status;
+  bool    mode_4byte_addr = false;
+
+  finfo("4byte mode: %s\tsector: %08lx\n", mode_4byte_addr ?
+                                           "true" : "false",
+                                           (unsigned long)sector);
+
+  /* Check that the flash is ready and unprotected */
+
+  status = gd55_read_status1(priv);
+  if ((status & GD55_SR_WIP) == GD55_SR_WIP)
+    {
+      ferr("ERROR: Flash busy: %02x", status);
+      return -EBUSY;
+    }
+
+  if (gd55_isprotected(priv, addr, status))
+    {
+      ferr("ERROR: Flash protected at addr: %02" PRIx32, addr);
+      return -EACCES;
+    }
+
+  if (addr >= MODE_3BYTE_LIMIT)
+    {
+      gd55_command(priv, GD55_EN4B);
+      mode_4byte_addr = true;
+    }
+
+  /* Send the 64k block erase command */
+
+  gd55_write_enable(priv);
+  gd55_command_address(priv, GD55_BE64, addr, mode_4byte_addr ?
+                                                       4 : 3);
+
+  /* Wait for erasure to finish */
+
+  do
+    {
+      nxsig_usleep(50 * 1000); /* typical 64k erase time is 220ms */
+      status = gd55_read_status1(priv);
+    }
+  while ((status & GD55_SR_WIP) != 0);
+
+  if (mode_4byte_addr)
+    {
+      gd55_command(priv, GD55_DIS4B);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: gd55_erase_32kblock
+ *
+ * Description:
+ *   Erase a 32k block of the device
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   sector       - an address of a sector within the 32k block to erase
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_erase_32kblock(FAR struct gd55_dev_s *priv, off_t sector)
+{
+  off_t   addr = sector << GD55_SECTOR_SHIFT;
+  uint8_t status;
+  bool    mode_4byte_addr = false;
+
+  finfo("4byte mode: %s\tsector: %08lx\n", mode_4byte_addr ?
+                                           "true" : "false",
+                                           (unsigned long)sector);
+
+  /* Check that the flash is ready and unprotected */
+
+  status = gd55_read_status1(priv);
+  if ((status & GD55_SR_WIP) == GD55_SR_WIP)
+    {
+      ferr("ERROR: Flash busy: %02x", status);
+      return -EBUSY;
+    }
+
+  if (gd55_isprotected(priv, addr, status))
+    {
+      ferr("ERROR: Flash protected at addr: %02" PRIx32, addr);
+      return -EACCES;
+    }
+
+  if (addr >= MODE_3BYTE_LIMIT)
+    {
+      gd55_command(priv, GD55_EN4B);
+      mode_4byte_addr = true;
+    }
+
+  /* Send the 32k block erase command */
+
+  gd55_write_enable(priv);
+  gd55_command_address(priv, GD55_BE32, addr, mode_4byte_addr ? 4 : 3);
+
+  /* Wait for erasure to finish */
+
+  do
+    {
+      nxsig_usleep(50 * 1000); /* typical 32k erase time is 150ms */
+      status = gd55_read_status1(priv);
+    }
+  while ((status & GD55_SR_WIP) != 0);
+
+  if (mode_4byte_addr)
+    {
+      gd55_command(priv, GD55_DIS4B);
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: gd55_erase_chip
+ *
+ * Description:
+ *   Erase entire chip
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_erase_chip(FAR struct gd55_dev_s *priv)
+{
+  uint8_t status;
+
+  /* Check if the FLASH is protected */
+
+  status = gd55_read_status1(priv);
+  if ((status & GD55_SR_BP_MASK) != 0)
+    {
+      ferr("ERROR: FLASH is Protected: %02x", status);
+      return -EACCES;
+    }
+
+  /* Erase the whole chip */
+
+  gd55_write_enable(priv);
+  gd55_command(priv, GD55_CE);
+
+  /* Wait for the erasure to complete */
+
+  status = gd55_read_status1(priv);
+
+  while ((status & GD55_SR_WIP) != 0)
+    {
+      nxsig_sleep(2);
+      status = gd55_read_status1(priv);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: gd55_write_enable
+ *
+ * Description:
+ *   Enable the device for writing by setting the wriet enable latch bit
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void gd55_write_enable(FAR struct gd55_dev_s *priv)
+{
+  uint8_t status;
+
+  gd55_command(priv, GD55_WREN);
+  do
+    {
+      status = gd55_read_status1(priv);
+    }
+  while ((status & GD55_SR_WEL) != GD55_SR_WEL);
+}
+
+/****************************************************************************
+ * Name: gd55_read_status1
+ *
+ * Description:
+ *   Read status register 1
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *
+ * Returned Value:
+ *   The status register data
+ *
+ ****************************************************************************/
+
+static uint8_t gd55_read_status1(FAR struct gd55_dev_s *priv)
+{
+  uint8_t status;
+
+  gd55_command_read(priv, GD55_RDSR1, &status, 1);
+  return status;
+}
+
+/****************************************************************************
+ * Name: gd55_read_status2
+ *
+ * Description:
+ *   Read status register 2
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *
+ * Returned Value:
+ *   The status register data
+ *
+ ****************************************************************************/
+
+static uint8_t gd55_read_status2(FAR struct gd55_dev_s *priv)
+{
+  uint8_t status;
+
+  gd55_command_read(priv, GD55_RDSR2, &status, 1);
+  return status;
+}
+
+/****************************************************************************
+ * Name: gd55_write_status1
+ *
+ * Description:
+ *   Write data to status register 1
+ *   The data to be written must have been written to the device structures
+ *   command buffer (cmdbuf)
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void gd55_write_status1(FAR struct gd55_dev_s *priv)
+{
+  uint8_t status;
+
+  gd55_write_enable(priv);
+
+  /* take care to mask of the SRP bit; it is one-time-programmable */
+
+  priv->cmdbuf[0] &= ~GD55_SR_SRP0;
+
+  gd55_command_write(priv, GD55_WRSR1,
+                     (FAR const void *)priv->cmdbuf, 1);
+
+  /* Wait for write operation to finish */
+
+  do
+    {
+      status = gd55_read_status1(priv);
+    }
+  while ((status & GD55_SR_WIP) != 0);
+}
+
+/****************************************************************************
+ * Name: gd55_erase
+ *
+ * Description:
+ *   Erase a number of blocks of data.
+ *
+ * Input Parameters:
+ *   dev        - a reference to the device structure
+ *   startblock - start block of the erase
+ *   nblocks    - nblocks to erase
+ *
+ * Returned Value:
+ *   Success (OK) or fail (negated error code)
+ *
+ ****************************************************************************/
+
+static int gd55_erase(FAR struct mtd_dev_s *dev, off_t startblock,
+                      size_t nblocks)
+{
+  FAR struct gd55_dev_s *priv = (FAR struct gd55_dev_s *)dev;
+  size_t                blocksleft = nblocks;
+  int                   ret;
+#ifndef CONFIG_MTD_GD55_SECTOR512
+  const size_t         sectorsper64kblock = (64 * 1024) >> GD55_SECTOR_SHIFT;
+  const size_t         sectorsper32kblock = (32 * 1024) >> GD55_SECTOR_SHIFT;
+#endif
+
+  finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+  /* Lock access to the SPI bus until we complete the erase */
+
+  gd55_lock(priv);
+
+#ifdef CONFIG_MTD_GD55_SECTOR512
+  while (blocksleft-- > 0)
+    {
+      /* Erase each sector */
+
+      gd55_erase_cache(priv, startblock);
+      startblock++;
+    }
+
+  /* Flush the last erase block left in the cache */
+
+  ret = gd55_flush_cache(priv);
+  if (ret < 0)
+    {
+      nblocks = ret;
+    }
+#else
+  while (blocksleft > 0)
+    {
+      /* Check if block is aligned on 64k or 32k block for faster erase */
+
+      if (((startblock & (sectorsper64kblock - 1)) == 0) &&
+          (blocksleft >= sectorsper64kblock))
+        {
+          /* Erase 64k block */
+
+          ret = gd55_erase_64kblock(priv, startblock);
+          if (ret < 0)
+            {
+              goto unlock;
+            }
+
+          startblock += sectorsper64kblock;
+          blocksleft -= sectorsper64kblock;
+          finfo("Erased 64kbytes at address 0x%08" PRIx32 "\n",
+                  startblock << GD55_SECTOR_SHIFT);
+        }
+      else if (((startblock & (sectorsper32kblock - 1)) == 0) &&
+          (blocksleft >= sectorsper32kblock))
+        {
+          /* Erase 32k block */
+
+          ret = gd55_erase_32kblock(priv, startblock);
+          if (ret < 0)
+            {
+              goto unlock;
+            }
+
+          startblock += sectorsper32kblock;
+          blocksleft -= sectorsper32kblock;
+          finfo("Erased 32kbytes at address 0x%08" PRIx32 "\n",
+                  startblock << GD55_SECTOR_SHIFT);
+        }
+      else
+        {
+          /* Erase each sector */
+
+          ret = gd55_erase_sector(priv, startblock);
+          if (ret < 0)
+            {
+              goto unlock;
+            }
+
+          startblock++;
+          blocksleft--;
+          finfo("Erased 4kbytes at address 0x%08" PRIx32 "\n",
+                  startblock << GD55_SECTOR_SHIFT);
+        }
+    }
+#endif
+
+  ret = (int)nblocks;
+unlock:
+  gd55_unlock(priv);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: gd55_bread
+ *
+ * Description:
+ *   Read a number of blocks of data.
+ *
+ * Input Parameters:
+ *   dev        - a reference to the device structure
+ *   startblock - start block of the memory to read
+ *   nblocks    - nblocks to read
+ *   buf        - pointer to the buffer to store the read data
+ *
+ * Returned Value:
+ *   Size of the data read
+ *
+ ****************************************************************************/
+
+static ssize_t gd55_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+                   size_t nblocks, FAR uint8_t *buf)
+{
+  ssize_t nbytes;
+
+  finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+  /* On this device, we can handle the block read just like the
+   * byte-oriented read
+   */
+
+#ifdef CONFIG_MTD_GD55_SECTOR512
+  nbytes = gd55_read(dev, startblock << GD55_SECTOR512_SHIFT,
+                     nblocks << GD55_SECTOR512_SHIFT, buf);
+  if (nbytes > 0)
+    {
+      nbytes >>= GD55_SECTOR512_SHIFT;
+    }
+#else
+  nbytes = gd55_read(dev, startblock << GD55_PAGE_SHIFT,
+                       nblocks << GD55_PAGE_SHIFT, buf);
+  if (nbytes > 0)
+    {
+      nbytes >>= GD55_PAGE_SHIFT;
+    }
+#endif
+
+  return nbytes;
+}
+
+/****************************************************************************
+ * Name: gd55_bwrite
+ *
+ * Description:
+ *   Write a number of blocks of data.
+ *
+ * Input Parameters:
+ *   dev        - a reference to the device structure
+ *   startblock - start block of the memory to write
+ *   nblocks    - nblocks to write
+ *   buf        - pointer to the buffer with the data to write
+ *
+ * Returned Value:
+ *   Size of the data written
+ *
+ ****************************************************************************/
+
+static ssize_t gd55_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+                    size_t nblocks, FAR const uint8_t *buf)
+{
+  FAR struct gd55_dev_s *priv = (FAR struct gd55_dev_s *)dev;
+  int                   ret;
+
+  finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
+
+  /* Lock the QuadSPI bus and write all of the pages to FLASH */
+
+  gd55_lock(priv);
+
+#if defined(CONFIG_MTD_GD55_SECTOR512)
+  ret = gd55_write_cache(priv, buf, startblock, nblocks);
+  if (ret < 0)
+    {
+      ferr("ERROR: gd55_write_cache failed: %d\n", ret);
+    }
+#else
+  ret = gd55_write_page(priv, buf, startblock << GD55_PAGE_SHIFT,
+                          nblocks << GD55_PAGE_SHIFT);
+  if (ret < 0)
+    {
+      ferr("ERROR: gd55_write_page failed: %d\n", ret);
+    }
+#endif
+
+  gd55_unlock(priv);
+
+  return ret < 0 ? ret : nblocks;
+}
+
+/****************************************************************************
+ * Name: gd55_read
+ *
+ * Description:
+ *   Read a number of bytes of data.
+ *
+ * Input Parameters:
+ *   dev        - a reference to the device structure
+ *   offset     - starting address of the memory to read
+ *   nbytes     - nbytes to read
+ *   buf        - pointer to the buffer to store the read data
+ *
+ * Returned Value:
+ *   Size of the data read
+ *
+ ****************************************************************************/
+
+static ssize_t gd55_read(FAR struct mtd_dev_s *dev, off_t offset,
+                         size_t nbytes, FAR uint8_t *buffer)
+{
+  int                   ret;
+  FAR struct gd55_dev_s *priv = (FAR struct gd55_dev_s *)dev;
+
+  finfo("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes);
+
+  /* Lock the QuadSPI bus and select this FLASH part */
+
+  gd55_lock(priv);
+  ret = gd55_read_bytes(priv, buffer, offset, nbytes);
+  gd55_unlock(priv);
+
+  if (ret < 0)
+    {
+      ferr("ERROR: gd55_read_bytes returned: %d\n", ret);
+      return (ssize_t)ret;
+    }
+
+  finfo("return nbytes: %d\n", (int)nbytes);
+  return (ssize_t)nbytes;
+}
+
+/****************************************************************************
+ * Name: gd55_ioctl
+ *
+ * Description:
+ *   IOCTLS relating to the GD55 mtd device
+ *
+ * Input Parameters:
+ *   dev        - a reference to the device structure
+ *   cmd        - ioctl command
+ *   arg        - ioctl argument
+ *
+ * Returned Value:
+ *   Success (OK) or fail (negated error code)
+ ****************************************************************************/
+
+static int gd55_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
+{
+  FAR struct gd55_dev_s *priv = (FAR struct gd55_dev_s *)dev;
+  int                    ret  = -EINVAL;
+
+  finfo("cmd: %d\n", cmd);
+
+  switch (cmd)
+    {
+      case MTDIOC_GEOMETRY:
+        {
+          FAR struct mtd_geometry_s *geo =
+            (FAR struct mtd_geometry_s *)((uintptr_t)arg);
+
+          if (geo)
+            {
+              memset(geo, 0, sizeof(*geo));
+
+              /* Populate the geometry structure with information need to
+               * know the capacity and how to access the device.
+               *
+               * NOTE:
+               * that the device is treated as though it where just an
+               * array of fixed size blocks.  That is most likely not true,
+               * but the client will expect the device logic to do whatever
+               * is necessary to make it appear so.
+               */
+
+#ifdef CONFIG_MTD_GD55_SECTOR512
+              geo->blocksize    = GD55_SECTOR512_SIZE;
+              geo->erasesize    = GD55_SECTOR512_SIZE;
+              geo->neraseblocks = priv->nsectors <<
+                                  (GD55_SECTOR_SHIFT -
+                                   GD55_SECTOR512_SHIFT);
+#else
+              geo->blocksize    = GD55_PAGE_SIZE;
+              geo->erasesize    = GD55_SECTOR_SIZE;
+              geo->neraseblocks = priv->nsectors;
+#endif
+              ret               = OK;
+
+              finfo("blocksize: %" PRId32
+                    " erasesize: %" PRId32
+                    " neraseblocks: %" PRId32 "\n",
+                    geo->blocksize, geo->erasesize, geo->neraseblocks);
+            }
+        }
+        break;
+
+      case BIOC_PARTINFO:
+        {
+          FAR struct partition_info_s *info =
+              (FAR struct partition_info_s *)arg;
+
+          if (info != NULL)
+            {
+#ifdef CONFIG_MTD_GD55_SECTOR512
+              info->numsectors  = priv->nsectors <<
+                               (GD55_SECTOR_SHIFT - GD55_SECTOR512_SHIFT);
+              info->sectorsize  = GD55_SECTOR512_SIZE;
+#else
+              info->numsectors  = priv->nsectors <<
+                                  (GD55_SECTOR_SHIFT - GD55_PAGE_SHIFT);
+              info->sectorsize  = GD55_PAGE_SIZE;
+#endif
+              info->startsector = 0;
+              info->parent[0]   = '\0';
+              ret               = OK;
+            }
+        }
+        break;
+
+      case MTDIOC_PROTECT:
+        {
+          FAR const struct mtd_protect_s *prot =
+            (FAR const struct mtd_protect_s *)((uintptr_t)arg);
+
+          DEBUGASSERT(prot);
+          ret = gd55_protect(priv, prot->startblock, prot->nblocks);
+        }
+        break;
+
+      case MTDIOC_UNPROTECT:
+        {
+          FAR const struct mtd_protect_s *prot =
+            (FAR const struct mtd_protect_s *)((uintptr_t)arg);
+
+          DEBUGASSERT(prot);
+          ret = gd55_unprotect(priv, prot->startblock, prot->nblocks);
+        }
+        break;
+
+      case MTDIOC_BULKERASE:
+        {
+          /* Erase the entire device */
+
+          gd55_lock(priv);
+          ret = gd55_erase_chip(priv);
+          gd55_unlock(priv);
+        }
+        break;
+
+      case MTDIOC_ERASESTATE:
+        {
+          FAR uint8_t *result = (FAR uint8_t *)arg;
+          *result = GD55_ERASED_STATE;
+
+          ret = OK;
+        }
+        break;
+
+      default:
+        ret = -ENOTTY; /* Bad/unsupported command */
+        break;
+    }
+
+  finfo("return %d\n", ret);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: gd55_readid
+ *
+ * Description:
+ *   Read the device ID.
+ *   - the read ID is stored in the cmdbuf variable of the device structure
+ *
+ * Input Parameters:
+ *   priv       - a reference to the device structure
+ *
+ * Returned Value:
+ *   Success (OK) or fail (negated error code)
+ *
+ ****************************************************************************/
+
+static int gd55_readid(FAR struct gd55_dev_s *priv)
+{
+  /* Lock the QuadSPI bus and configure the bus. */
+
+  gd55_lock(priv);
+
+  /* Read the JEDEC ID */
+
+  gd55_command_read(priv, GD55_RDID, priv->cmdbuf, 4);
+
+  /* Unlock the bus */
+
+  gd55_unlock(priv);
+
+  finfo("Manufacturer: %02x Device Type %02x, Capacity: %02x\n",
+        priv->cmdbuf[0], priv->cmdbuf[1], priv->cmdbuf[2]);
+
+  /* Check for GigaDevices GD55 chip */
+
+  if (priv->cmdbuf[0] != GD55_JEDEC_MANUFACTURER &&
+      (priv->cmdbuf[1] != GD55L_JEDEC_MEMORY_TYPE ||
+       priv->cmdbuf[1] != GD55B_JEDEC_MEMORY_TYPE))
+    {
+      ferr("ERROR: Unrecognized device type: 0x%02x 0x%02x\n",
+           priv->cmdbuf[0], priv->cmdbuf[1]);
+      return -ENODEV;
+    }
+
+  /* Check for a supported capacity */
+
+  switch (priv->cmdbuf[2])
+    {
+      case GD55_JEDEC_1G_CAPACITY:
+        priv->nsectors    = GD55_NSECTORS_1GBIT;
+        break;
+
+      case GD55_JEDEC_2G_CAPACITY:
+        priv->nsectors    = GD55_NSECTORS_2GBIT;
+        break;
+
+      default:
+        ferr("ERROR: Unsupported memory capacity: %02x\n", priv->cmdbuf[2]);
+        return -ENODEV;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: gd55_protect
+ *
+ * Description:
+ *   The GD55 flash supports sector protection either by individual 64KiB
+ *   blocks, or in a (64KiB * n^2) block from the bottom of the device memory
+ *   OR from the top of the device memory.
+ *
+ * Input Parameters:
+ *   priv       - a reference to the device structure
+ *   startblock - first block to protect
+ *   nblocks    - nblocks to protect
+ *
+ * Returned Value:
+ *   Success (OK) or fail (negated error code)
+ *
+ ****************************************************************************/
+
+static int gd55_protect(FAR struct gd55_dev_s *priv, off_t startblock,
+                        size_t nblocks)
+{
+  uint8_t status[2];
+  int     blkmask;
+
+  if (nblocks < GD55_MIN_BP_BLKS)
+    {
+      return -EINVAL; /* Too few blocks to protect */
+    }
+
+  /* Check if sector protection registers are locked */
+
+  status[0] = gd55_read_status1(priv);
+  status[1] = gd55_read_status2(priv);
+  if (status[1] & GD55_SR_SRP1)
+    {
+      /* Status register cannot be written to as device is in
+       * power supply lockdown or is set for OTP.
+       * If the external HW WP# pin is asserted we won't know until we
+       * attempt to unlock sectors though, regardless of state of SRP0 bit
+       * in status register 0.
+       */
+
+      return -EACCES;
+    }
+
+  if (nblocks == (priv->nsectors * GD55_SECTORS_PER_BP_BLK))
+    {
+      if (startblock == 0)
+        {
+          blkmask = GD55_BP_ALL; /* protect every block */
+        }
+      else
+        {
+          return -EINVAL;        /* Invalid size and startblock */
+        }
+    }
+  else
+    {
+      /* We can only protect in certain increments of size */
+
+      blkmask = 0;
+      while (nblocks > (GD55_MIN_BP_BLKS << blkmask))
+        {
+          if ((startblock % (GD55_MIN_BP_BLKS << blkmask)) ||
+              (nblocks % (GD55_MIN_BP_BLKS << blkmask)))
+            {
+              return -EINVAL; /* Not a size we can protect */
+            }
+
+          blkmask++;
+        }
+
+        blkmask = (startblock == 0) ? GD55_SR_BP_BOTTOM(blkmask) :
+                                      GD55_SR_BP_TOP(blkmask);
+    }
+
+  /* startblock must be first block, or (memory top - nblocks) */
+
+  if ((startblock != 0) &&
+      (startblock != (((priv->nsectors << GD55_SECTOR_SHIFT) /
+                        GD55_MIN_BP_BLKS) - nblocks)))
+    {
+      return -EINVAL;
+    }
+
+  /* Clear the relevant status register bits for the new mask */
+
+  priv->cmdbuf[0] = status[0] & ~GD55_SR_BP_MASK;
+
+  /* Now set them */
+
+  priv->cmdbuf[0] |= blkmask;
+
+  if ((priv->cmdbuf[0] & GD55_SR_BP_MASK) == (status[0] & GD55_SR_BP_MASK))
+    {
+      return OK; /* this protection is already set */
+    }
+
+  gd55_write_status1(priv);
+  status[0] = gd55_read_status1(priv);
+  if ((status[0] & GD55_SR_BP_MASK) != (priv->cmdbuf[0] & GD55_SR_BP_MASK))
+    {
+      return -EACCES; /* Likely that the external HW WP# pin is asserted */
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: gd55_unprotect
+ *
+ * Description:
+ *   The GD55 flash supports sector protection either by individual 64KiB
+ *   blocks, or in a (64KiB * n^2) block from the bottom of the device memory
+ *   OR from the top of the device memory.
+ *
+ *   This function removes protection from all blocks
+ *
+ *   REVISIT - there may be benefit from trying to only unprotect a range of
+ *   sectors but this means complex checking of the request range against the
+ *   current range of blocks that are currently protected so is non-trivial
+ *
+ * Input Parameters:
+ *   priv       - a reference to the device structure
+ *   startblock - first block to unprotect (ignored for now)
+ *   nblocks    - nblocks to unprotect (ignored for now)
+ *
+ * Returned Value:
+ *   Success (OK) or fail (negated error code)
+ *
+ ****************************************************************************/
+
+static int gd55_unprotect(FAR struct gd55_dev_s *priv, off_t startblock,
+                          size_t nblocks)
+{
+  uint8_t status[2];
+
+  /* Check if sector protection registers are locked */
+
+  status[0] = gd55_read_status1(priv);
+  status[1] = gd55_read_status2(priv);
+  if (status[1] & GD55_SR_SRP1)
+    {
+      /* Status register cannot be written to as device is in
+       * power supply lockdown or is set for OTP.
+       * If the external HW WP# pin is asserted we won't know until we
+       * attempt to unlock sectors though, regardless of state of SRP0 bit
+       * in status register 0.
+       */
+
+      return -EACCES;
+    }
+
+  if (!(status[0] & GD55_SR_BP_MASK))
+    {
+      return OK; /* all blocks are already unprotected */
+    }
+
+  /* Clear the all status register BP bits */
+
+  priv->cmdbuf[0] = status[0] & ~GD55_SR_BP_MASK;
+
+  gd55_write_status1(priv);
+  status[0] = gd55_read_status1(priv);
+  if ((status[0] & GD55_SR_BP_MASK) != (priv->cmdbuf[0] & GD55_SR_BP_MASK))
+    {
+      return -EACCES; /* Likely that the external HW WP# pin is asserted */
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: gd55_isprotected
+ *
+ * Description:
+ *   Check if an address has been write protected
+ *
+ * Input Parameters:
+ *   priv       - a reference to the device structure
+ *   addr       - address to check
+ *   status     - the previously read status register value
+ *
+ * Returned Value:
+ *   Protected (true) or unprotected (false)
+ *
+ ****************************************************************************/
+
+static bool gd55_isprotected(FAR struct gd55_dev_s *priv, off_t addr,
+                             uint8_t status)
+{
+  off_t        protstart;
+  off_t        protend;
+  off_t        protsize;
+  unsigned int bp;
+
+  /* the BP field is essentially the power-of-two of the number of 64k
+   * sectors that are protected, saturated to the device size.
+   * The msb determines if protection is:
+   *   - top down  (msb not set)
+   *   - bottom up (msb set)
+   */
+
+  bp = (status & GD55_SR_BP_MASK);
+  bp &= ~GD55_STATUS_TB_MASK; /* Ignore top/bottom for now */
+  bp >>= GD55_SR_BP_SHIFT;
+
+  if (bp == 0)
+    {
+      return false;
+    }
+
+  protsize = GD55_BP_SIZE;
+  protsize <<= (bp - 1);
+  protend = GD55_SECTOR_SIZE * priv->nsectors;
+  if (protsize > protend)
+    {
+      protsize = protend;
+    }
+
+  /* The final protection range then depends on if the protection region is
+   * configured top-down or bottom up.
+   */
+
+  if ((status & GD55_STATUS_TB_BOTTOM))
+    {
+      protstart = 0;
+      protend   = protstart + protsize;
+    }
+  else
+    {
+      protstart = protend - protsize;
+
+      /* protend already computed above */
+    }
+
+  return (addr >= protstart && addr < protend);
+}
+
+#ifdef CONFIG_MTD_GD55_SECTOR512
+/****************************************************************************
+ * Name: gd55_flush_cache
+ *
+ * Description:
+ *   If the cache is dirty (meaning that it no longer matches the old FLASH
+ *   contents) or was erased (with the cache containing the correct FLASH
+ *   contents), then write the cached erase block to FLASH.
+ *
+ * Input Parameters:
+ *   priv       - a reference to the device structure
+ *
+ * Returned Value:
+ *   Success (OK) or fail (negated error code)
+ *
+ ****************************************************************************/
+
+static int gd55_flush_cache(FAR struct gd55_dev_s *priv)
+{
+  int ret = OK;
+
+  if (IS_DIRTY(priv) || IS_ERASED(priv))
+    {
+      off_t address;
+
+      /* Convert the erase sector number into a FLASH address */
+
+      address = (off_t)priv->esectno << GD55_SECTOR_SHIFT;
+
+      /* Write entire erase block to FLASH */
+
+      ret = gd55_write_page(priv, priv->sector, address, GD55_SECTOR_SIZE);
+      if (ret < 0)
+        {
+          ferr("ERROR: gd55_write_page failed: %d\n", ret);
+        }
+
+      /* The cache is no long dirty and the FLASH is no longer erased */
+
+      CLR_DIRTY(priv);
+      CLR_ERASED(priv);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: gd55_read_cache
+ *
+ * Description:
+ *   Read cached data
+ *
+ * Input Parameters:
+ *   priv       - a reference to the device structure
+ *   sector     = sector to read
+ *
+ * Returned Value:
+ *   Success (OK) or fail (negated error code)
+ *
+ ****************************************************************************/
+
+static FAR uint8_t *gd55_read_cache(FAR struct gd55_dev_s *priv,
+                                       off_t sector)

Review Comment:
   align



##########
drivers/mtd/gd55.c:
##########
@@ -0,0 +1,2082 @@
+/****************************************************************************
+ * drivers/mtd/gd55.c
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef CONFIG_MTD_GD55_SECTOR512
+#  include <stdlib.h>
+#  include <string.h>
+#endif
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/spi/qspi.h>
+#include <nuttx/mtd/mtd.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* 4 byte addressing is needed for addresses needing more than a 3 byte
+ * address, i.e. 16Mbyte
+ */
+
+#define MODE_3BYTE_LIMIT        ((16 * 1024 * 1024))
+
+/* GD55 Commands                                                            */
+
+#define GD55_QREAD              0x6b  /* Quad output fast read              */
+#define GD55_QREAD_DUMMIES      8
+#define GD55_QC_READ            0xeb  /* Quad output continuous fast read   */
+#define GD55_QC_READ_DUMMIES    6
+#define GD55_EQPP               0xc2  /* Extended quad page program         */
+#define GD55_EQPP_DUMMIES       0     /* No dummy clocks                    */
+#define GD55_SE                 0x20  /* 4Kb Sector erase                   */
+#define GD55_BE32               0x52  /* 32Kbit block Erase                 */
+#define GD55_BE64               0xd8  /* 64Kbit block Erase                 */
+#define GD55_CE                 0x60  /* Chip erase (alternate)             */
+#define GD55_WREN               0x06  /* Write Enable                       */
+#define GD55_WRDI               0x04  /* Write Disable                      */
+#define GD55_RDSR1              0x05  /* Read status register 1             */
+#define GD55_EN4B               0xb7  /* Enable 4 byte Addressing Mode      */
+#define GD55_DIS4B              0xe9  /* Disable 4 byte Addressing Mode     */
+#define GD55_IBSL               0x36  /* Individual block/sector lock       */
+#define GD55_IBSUL              0x39  /* Individual block/sector unlock     */
+#define GD55_RIBSL              0x3d  /* Read individual block/sector lock  */
+#define GD55_RDNVCR             0xb5  /* Read Non-Volatile config register  */
+#define GD55_RD_NVCR_DUMMIES    8
+#define GD55_RDSR2              0x35  /* Read status register 2             */
+#define GD55_WRSR1              0x01  /* Write status register 1            */
+#define GD55_SE_ALT             0x21  /* Alternate 4Kb Sector erase         */
+#define GD55_QC_READ_ALT        0xec  /* Quad output continuous fast read   */
+#define GD55_4B_QDTR_READ       0xed  /* Quad I/O DTR read                  */
+#define GD55_4B_QDTR_READ_ALT   0xee  /* Alternate quad I/O DTR read        */
+#define GD55_PP                 0x02  /* Page program (SPI, not used)       */
+#define GD55_PP_ALT             0x12  /* Aternate page program (SPI)        */
+#define GD55_BE32_ALT           0x5c  /* Alternate 32Kbit block Erase       */
+#define GD55_BE64_ALT           0xd8  /* ALternate 64Kbit block Erase       */
+#define GD55_CE_ALT             0xc7  /* Alternate chip erase               */
+#define GD55_QPP                0x32  /* Quad page program                  */
+#define GD55_QPP_ALT            0x34  /* ALternate quad page program        */
+#define GD55_QPP_DUMMIES        0     /* No dummy clocks                    */
+#define GD55_QPIEN              0x38  /* Enable QPI Operation               */
+#define GD55_QPIDIS             0xff  /* Disable QPI Operation              */
+#define GD55_DP                 0xb9  /* Deep power down                    */
+#define GD55_RDP                0xab  /* Release deep power down            */
+#define GD55_RUID               0x4b  /* Read Unique ID                     */
+#define GD55_RDID               0x9e  /* Read identification                */
+#define GD55_RDID_ALT           0x9f  /* Read identification (alternate)    */
+#define GD55_PE_SUSPEND         0x75  /* Suspends program/erase             */
+#define GD55_PE_RESUME          0x7a  /* Resume program                     */
+#define GD55_RDVCR              0x85  /* Read Volatile config register      */
+#define GD55_RD_VCR_DUMMIES     1
+#define GD55_WRSR2              0x31  /* Write status register 2            */
+#define GD55_WRNVCR             0xb1  /* Write Non-Volatile config register */
+#define GD55_WRENVSC            0x50  /* Write en. Volatile config register */
+#define GD55_WRVCR              0x91  /* Write Volatile config register     */
+#define GD55_WREAR              0xc5  /* Write Extended address register    */
+#define GD55_EARR               0xc8  /* Read extended address register     */
+#define GD55_RSFDP              0x5a  /* Read SFDP                          */
+#define GD55_RDSCUR             0x48  /* Read security register             */
+#define GD55_WRSCUR             0x42  /* Write security register            */
+#define GD55_ERSCUR             0x44  /* Erase security register            */
+#define GD55_RSTEN              0x66  /* Reset Enable                       */
+#define GD55_RST                0x99  /* Reset Memory                       */
+#define GD55_GBSL               0x7e  /* Global block/sector lock           */
+#define GD55_GBSUL              0x98  /* Global block/sector unlock         */
+
+/* Read ID (RDID) register values                                           */
+
+#define GD55_MANUFACTURER       0xc8  /* GigaSevice manufacturer ID         */
+
+/* JEDEC Read ID register values                                            */
+
+#define GD55_JEDEC_MANUFACTURER 0xc8 /* GigaDevice manufacturer ID          */
+
+#define GD55B_JEDEC_MEMORY_TYPE 0x47 /* GD55B memory type, 3V               */
+#define GD55L_JEDEC_MEMORY_TYPE 0x67 /* GD55L memory type, 1.8V             */
+#define GD55_JEDEC_1G_CAPACITY  0x1b /* 1Gbit memory capacity               */
+#define GD55_JEDEC_2G_CAPACITY  0x1c /* 2Gbit memory capacity               */
+
+/* GD55 devices all have identical sector sizes:
+ * block protection size: 64KiB
+ * sector size:           4KiB
+ * page size:             256B
+ */
+
+#define GD55_SECTOR_SHIFT       (12)
+#define GD55_SECTOR_SIZE        (1 << GD55_SECTOR_SHIFT)           /* 4KiB  */
+#define GD55_PAGE_SHIFT         (8)                                /* 256B  */
+#define GD55_PAGE_SIZE          (1 << GD55_PAGE_SHIFT)
+#define GD55_BP_SHIFT           (16)
+#define GD55_BP_SIZE            (1 << GD55_BP_SHIFT)               /* 64KiB */
+#define GD55_MIN_BP_BLKS        (GD55_BP_SIZE  >> GD55_PAGE_SHIFT)
+#define GD55_SECTORS_PER_BP_BLK (GD55_BP_SIZE / GD55_SECTOR_SIZE)
+
+/* GD55B01xx (128 MiB) memory capacity                                      */
+
+#define GD55_NSECTORS_1GBIT     (32768)
+
+/* GD55B02xx (256 MiB) memory capacity                                      */
+
+#define GD55_NSECTORS_2GBIT     (65536)
+
+/* 512 byte sector support **************************************************/
+
+#define GD55_SECTOR512_SHIFT    (9)
+#define GD55_SECTOR512_SIZE     (1 << GD55_SECTOR512_SHIFT)
+
+/* Status register 1 bit definitions                                        */
+
+#define GD55_SR_WIP             (1 << 0)  /* Bit 0: Write in progress       */
+#define GD55_SR_WEL             (1 << 1)  /* Bit 1: Write enable latch      */
+#define GD55_SR_BP_SHIFT        (2)       /* Bits 2-6: Block protect bits   */
+#define GD55_SR_BP_MASK         (31 << GD55_SR_BP_SHIFT)
+#define GD55_STATUS_BP_NONE     (0 << GD55_SR_BP_SHIFT)
+#define GD55_STATUS_BP_ALL      (7 << GD55_SR_BP_SHIFT)
+#define GD55_STATUS_TB_MASK     (1 << 6)  /* BP4 Top/Bottom Protect         */
+#define GD55_STATUS_TB_TOP      (0 << 6)  /*   = 0, BP3..0 protect Top down */
+#define GD55_STATUS_TB_BOTTOM   (1 << 6)  /*   = 1, BP3..0   "   Bottom up  */
+#define GD55_SR_BP_TOP(b)       (((b + 1) << GD55_SR_BP_SHIFT) | \
+                                 GD55_STATUS_TB_TOP)
+#define GD55_SR_BP_BOTTOM(b)    (((b + 1) << GD55_SR_BP_SHIFT) | \
+                                 GD55_STATUS_TB_BOTTOM)
+#define GD55_BP_ALL             (14 << GD55_SR_BP_SHIFT)
+                                          /* GD55B01 needs BP bits = 0xx11xx
+                                           * GD55B02 needs BP bits = 0xx111x
+                                           */
+#define GD55_SR_SRP0            (1 << 7)  /* Bit 7: SR protect bit 0        */
+
+/* Status register 2 bit definitions                                        */
+
+#define GD55_SR_ADS             (1 << 0)  /* Bit 0: Current Address Mode    */
+                                          /* Bit 1 - reserved               */
+#define GD55_SR_SUS2            (1 << 2)  /* Bit 2: Program suspend bit 2   */
+#define GD55_SR_LB              (1 << 3)  /* Bit 3: Security Register Lock  */
+#define GD55_SR_PE              (1 << 4)  /* Bit 4: Program Error Bit       */
+#define GD55_SR_EE              (1 << 5)  /* Bit 5: Erase Error Bit         */
+#define GD55_SR_SRP1            (1 << 6)  /* Bit 6: SR protection bit 1     */
+#define GD55_SR_SUS1            (1 << 7)  /* Bit 7: Program suspend bit 1   */
+
+/* Non-volatile and volatile config register addresses and bits             */
+
+#define GD55_DUMMY_CYCLES_REG   1         /* Dummy Cycle Configuration      */
+#define GD55_ODT_DS_REG         3         /* On-die termination and driver
+                                           * strength configuration
+                                           */
+#define GD55_DLP_PROT_REG       4         /* Data Learning and protect mode */
+#define GD55_PROT_MODE_MASK     (1 << 2)  /* Bit 2, BP or WPS mode          */
+#define GD55_PROT_MODE_WPS      (0 << 2)  /* 0 = Sector Protect mode        */
+#define GD55_PROT_MODE_BP       (1 << 2)  /* 1 = Block Protect mode (def.)  */
+#define GD55_4BYTE_MODE_REG     5         /* 3 pr 4-byte address mode       */
+#define GD55_XIP_MODE_REG       6         /* XIP (continuous read) mode     */
+#define GD55_WRAP_CONFIG_REG    7         /* Wrap mode (none/64/32/16 byte) */
+
+/* Block protection bit */
+
+#define GD55_BLK_PROTECTED      (1 << 0)  /* lsb set means block is locked  */
+
+/* Cache flags **************************************************************/
+
+#define GD55_CACHE_VALID        (1 << 0)  /* 1=Cache has valid data         */
+#define GD55_CACHE_DIRTY        (1 << 1)  /* 1=Cache is dirty               */
+#define GD55_CACHE_ERASED       (1 << 2)  /* 1=Backing FLASH is erased      */
+
+#define IS_VALID(p)             ((((p)->flags) & GD55_CACHE_VALID) != 0)
+#define IS_DIRTY(p)             ((((p)->flags) & GD55_CACHE_DIRTY) != 0)
+#define IS_ERASED(p)            ((((p)->flags) & GD55_CACHE_ERASED) != 0)
+
+#define SET_VALID(p)  do { (p)->flags |= GD55_CACHE_VALID; } while (0)
+#define SET_DIRTY(p)  do { (p)->flags |= GD55_CACHE_DIRTY; } while (0)
+#define SET_ERASED(p) do { (p)->flags |= GD55_CACHE_ERASED; } while (0)
+
+#define CLR_VALID(p)  do { (p)->flags &= ~GD55_CACHE_VALID; } while (0)
+#define CLR_DIRTY(p)  do { (p)->flags &= ~GD55_CACHE_DIRTY; } while (0)
+#define CLR_ERASED(p) do { (p)->flags &= ~GD55_CACHE_ERASED; } while (0)
+
+#define GD55_ERASED_STATE       0xff
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Internal state of the MTD device */
+
+struct gd55_dev_s
+{
+  struct mtd_dev_s       mtd;         /* MTD interface                      */
+  FAR struct qspi_dev_s *qspi;        /* QuadSPI interface                  */
+  FAR uint8_t           *cmdbuf;      /* Allocated command buffer           */
+  FAR uint8_t           *readbuf;     /* Allocated status read buffer       */
+  uint32_t               nsectors;    /* Number of erase sectors            */
+#ifdef CONFIG_MTD_GD55_SECTOR512
+  uint8_t                flags;       /* Buffered sector flags              */
+  uint16_t               esectno;     /* Erase sector number in the cache   */
+  FAR uint8_t           *sector;      /* Allocated sector data              */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD driver methods */
+
+static int     gd55_erase(FAR struct mtd_dev_s *dev, off_t startblock,
+                          size_t nblocks);
+static ssize_t gd55_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+                          size_t nblocks, FAR uint8_t *buf);
+static ssize_t gd55_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+                           size_t nblocks, FAR const uint8_t *buf);
+static ssize_t gd55_read(FAR struct mtd_dev_s *dev, off_t offset,
+                         size_t nbytes, FAR uint8_t *buffer);
+static int     gd55_ioctl(FAR struct mtd_dev_s *dev, int cmd,
+                          unsigned long arg);
+
+/* Internal driver methods */
+
+static void     gd55_lock(FAR struct gd55_dev_s *priv);
+static void     gd55_unlock(FAR struct gd55_dev_s *priv);
+static int      gd55_command_read(FAR struct gd55_dev_s *priv, uint8_t cmd,
+                                  FAR void *buffer, size_t buflen);
+static int      gd55_command(FAR struct gd55_dev_s *priv, uint8_t cmd);
+static int      gd55_command_address(FAR struct gd55_dev_s *priv,
+                                     uint8_t cmd, off_t addr,
+                                     uint8_t addrlen);
+static int      gd55_readid(FAR struct gd55_dev_s *priv);
+static int      gd55_protect(FAR struct gd55_dev_s *priv, off_t startblock,
+                             size_t nblocks);
+static int      gd55_unprotect(FAR struct gd55_dev_s *priv, off_t startblock,
+                               size_t nblocks);
+static bool     gd55_isprotected(FAR struct gd55_dev_s *priv, off_t addr,
+                                 uint8_t status);
+static int      gd55_read_bytes(FAR struct gd55_dev_s *priv,
+                                FAR uint8_t *buffer, off_t address,
+                                size_t buflen);
+static uint8_t  gd55_read_status1(FAR struct gd55_dev_s *priv);
+static uint8_t  gd55_read_status2(FAR struct gd55_dev_s *priv);
+static void     gd55_write_status1(FAR struct gd55_dev_s *priv);
+static int      gd55_command_write(FAR struct gd55_dev_s *priv, uint8_t cmd,
+                                   FAR const void *buffer, size_t buflen);
+static void     gd55_write_enable(FAR struct gd55_dev_s *priv);
+static int      gd55_write_page(FAR struct gd55_dev_s *priv,
+                                FAR const uint8_t *buffer, off_t address,
+                                size_t buflen);
+static int      gd55_erase_sector(FAR struct gd55_dev_s *priv, off_t sector);
+
+static int      gd55_erase_chip(FAR struct gd55_dev_s *priv);
+#ifdef CONFIG_MTD_GD55_SECTOR512
+static int          gd55_flush_cache(FAR struct gd55_dev_s *priv);
+static FAR uint8_t *gd55_read_cache(FAR struct gd55_dev_s *priv,
+                                    off_t sector);
+static void         gd55_erase_cache(FAR struct gd55_dev_s *priv,
+                                     off_t sector);
+static int          gd55_write_cache(FAR struct gd55_dev_s *priv,
+                                     FAR const uint8_t *buffer, off_t sector,
+                                     size_t nsectors);
+#else
+static int          gd55_erase_64kblock(FAR struct gd55_dev_s *priv,
+                                        off_t sector);
+static int          gd55_erase_32kblock(FAR struct gd55_dev_s *priv,
+                                        off_t sector);
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: gd55_lock
+ *
+ * Description:
+ * On QSPI buses where there are multiple devices, it will be necessary to
+ * lock QSPI to have exclusive access to the bus for a sequence of
+ * transfers.  The bus should be locked before the chip is selected.
+ *
+ * This is a blocking call and will not return until we have exclusive
+ * access to the SPI bus. We will retain that exclusive access until the
+ * bus is unlocked.
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *
+ * Returned Value:
+ *   none
+ *
+ ****************************************************************************/
+
+static void gd55_lock(FAR struct gd55_dev_s *priv)
+{
+  QSPI_LOCK(priv->qspi, true);
+
+  /* After locking the QSPI bus, the we also need call the setfrequency,
+   * setbits and setmode methods to make sure that the QSPI is properly
+   * configured for the device.  If the QSPI bus is being shared, then it
+   * may have been left in an incompatible state.
+   */
+
+  QSPI_SETMODE(priv->qspi, CONFIG_MTD_GD55_QSPIMODE);
+  QSPI_SETBITS(priv->qspi, 8);
+  QSPI_SETFREQUENCY(priv->qspi, CONFIG_MTD_GD55_FREQUENCY);
+}
+
+/****************************************************************************
+ * Name: gd55_unlock
+ *
+ * Description:
+ * On QSPI buses where there are multiple devices, it will have been
+ * necessary to lock QSSPI to have exclusive access to the bus for a sequence
+ * of transfers.  The bus must be unlocked after the transfers to relinquish
+ * the exclusive access from the call to LOCK the bus.
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *
+ * Returned Value:
+ *   none
+ *
+ ****************************************************************************/
+
+static void gd55_unlock(FAR struct gd55_dev_s *priv)
+{
+  QSPI_LOCK(priv->qspi, false);
+}
+
+/****************************************************************************
+ * Name: gd55_command_read
+ *
+ * Description:
+ *   Read data from the device.
+ *
+ * Input Parameters:
+ *   priv         - a reference to the device structure
+ *   cmd          - the read command to be used
+ *   buffer       - pointer to variable to store the read data
+ *   buflen       - the number of bytes to be read into the buffer
+ *
+ * Returned Value:
+ *   Zero (OK) on SUCCESS, a negated errno on value of failure
+ *
+ ****************************************************************************/
+
+static int gd55_command_read(FAR struct gd55_dev_s *priv, uint8_t cmd,
+                      FAR void *buffer, size_t buflen)

Review Comment:
   align,ditto



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to