You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ar...@apache.org on 2025/02/11 09:23:50 UTC

(nuttx) branch master updated (405287081d -> 34bde7b80a)

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

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


    from 405287081d boards/arm Add support to Seeed Xiao nRF52840 Board
     new ac019bbfa2 drivers/mtd: cfi-flash driver
     new 2c4285d9ac mtd/mtd_cfi: cfi flash bind to mtd interface
     new ef24233501 qemu/armv7-a: adjust flash size to free area to register cfi flash
     new 34bde7b80a qemu/armv7a: register cfi flash

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 Documentation/components/drivers/special/mtd.rst   |   20 +
 boards/arm/qemu/qemu-armv7a/configs/full/defconfig |    8 +-
 boards/arm/qemu/qemu-armv7a/src/qemu_bringup.c     |   18 +
 drivers/devicetree/CMakeLists.txt                  |    4 +
 drivers/devicetree/Make.defs                       |    4 +
 .../devicetree/{fdt_virtio_mmio.c => fdt_cfi.c}    |   72 +-
 drivers/mtd/CMakeLists.txt                         |    5 +
 drivers/mtd/Kconfig                                |    6 +
 drivers/mtd/Make.defs                              |    5 +
 drivers/mtd/cfi.c                                  | 1148 ++++++++++++++++++++
 drivers/mtd/cfi.h                                  |  131 +++
 drivers/mtd/mtd_cfi.c                              |  329 ++++++
 include/nuttx/fdt.h                                |   17 +
 include/nuttx/mtd/mtd.h                            |   20 +
 14 files changed, 1756 insertions(+), 31 deletions(-)
 copy drivers/devicetree/{fdt_virtio_mmio.c => fdt_cfi.c} (53%)
 create mode 100644 drivers/mtd/cfi.c
 create mode 100644 drivers/mtd/cfi.h
 create mode 100644 drivers/mtd/mtd_cfi.c


(nuttx) 04/04: qemu/armv7a: register cfi flash

Posted by ar...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 34bde7b80a9ae96f4a2482124fe9d79b63d83374
Author: dongjiuzhu1 <do...@xiaomi.com>
AuthorDate: Wed Feb 5 13:25:08 2025 +0800

    qemu/armv7a: register cfi flash
    
    search the device tree and register cfi flash
    
    test flowchart:
    mount -t littlefs -o autoformat /dev/cfi-flash1 /data
    fstest -m /data
    
    Signed-off-by: dongjiuzhu1 <do...@xiaomi.com>
---
 boards/arm/qemu/qemu-armv7a/configs/full/defconfig |   6 ++
 boards/arm/qemu/qemu-armv7a/src/qemu_bringup.c     |  18 ++++
 drivers/devicetree/CMakeLists.txt                  |   4 +
 drivers/devicetree/Make.defs                       |   4 +
 drivers/devicetree/fdt_cfi.c                       | 101 +++++++++++++++++++++
 include/nuttx/fdt.h                                |  17 ++++
 6 files changed, 150 insertions(+)

diff --git a/boards/arm/qemu/qemu-armv7a/configs/full/defconfig b/boards/arm/qemu/qemu-armv7a/configs/full/defconfig
index 1b5dc69a8f..dd26aa94ca 100644
--- a/boards/arm/qemu/qemu-armv7a/configs/full/defconfig
+++ b/boards/arm/qemu/qemu-armv7a/configs/full/defconfig
@@ -29,6 +29,8 @@ CONFIG_CXX_LOCALIZATION=y
 CONFIG_CXX_WCHAR=y
 CONFIG_DEBUG_ASSERTIONS=y
 CONFIG_DEBUG_FEATURES=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_FS_ERROR=y
 CONFIG_DEBUG_FULLOPT=y
 CONFIG_DEBUG_SCHED=y
 CONFIG_DEBUG_SCHED_ERROR=y
@@ -56,6 +58,7 @@ CONFIG_FLASH_SIZE=50331648
 CONFIG_FLASH_START=0x00600000
 CONFIG_FLASH_VSTART=0x00600000
 CONFIG_FS_LARGEFILE=y
+CONFIG_FS_LITTLEFS=y
 CONFIG_FS_LOCK_BUCKET_SIZE=8
 CONFIG_FS_PROCFS=y
 CONFIG_FS_ROMFS=y
@@ -88,6 +91,8 @@ CONFIG_LV_USE_LOG=y
 CONFIG_LV_USE_NUTTX=y
 CONFIG_LV_USE_NUTTX_TOUCHSCREEN=y
 CONFIG_LV_USE_QRCODE=y
+CONFIG_MTD=y
+CONFIG_MTD_CFI=y
 CONFIG_NET=y
 CONFIG_NETDEV_HPWORK_THREAD=y
 CONFIG_NETDEV_IFINDEX=y
@@ -133,6 +138,7 @@ CONFIG_SYSTEM_NXCODEC=y
 CONFIG_SYSTEM_VI=y
 CONFIG_SYSTEM_YMODEM=y
 CONFIG_SYSTEM_ZMODEM=y
+CONFIG_TESTING_FSTEST=y
 CONFIG_TESTING_GETPRIME=y
 CONFIG_TESTING_OSTEST=y
 CONFIG_TIMER_FD=y
diff --git a/boards/arm/qemu/qemu-armv7a/src/qemu_bringup.c b/boards/arm/qemu/qemu-armv7a/src/qemu_bringup.c
index 2e1f43e5e8..463f846de7 100644
--- a/boards/arm/qemu/qemu-armv7a/src/qemu_bringup.c
+++ b/boards/arm/qemu/qemu-armv7a/src/qemu_bringup.c
@@ -84,6 +84,14 @@ static void register_devices_from_fdt(void)
     }
 #endif
 
+#ifdef CONFIG_MTD_CFI
+  ret = fdt_cfi_register(fdt);
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "fdt_cfi_register failed, ret=%d\n", ret);
+    }
+#endif
+
   UNUSED(ret);
 }
 
@@ -130,6 +138,16 @@ int qemu_bringup(void)
   register_devices_from_fdt();
 #endif
 
+#if defined(CONFIG_FS_LITTLEFS) && defined(CONFIG_MTD_CFI)
+  /* Mount the procfs file system */
+
+  ret = nx_mount("/dev/cfi-flash1", "/data", "littlefs", 0, "autoformat");
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: Failed to mount littlefs at /data: %d\n", ret);
+    }
+#endif
+
   UNUSED(ret);
   return OK;
 }
diff --git a/drivers/devicetree/CMakeLists.txt b/drivers/devicetree/CMakeLists.txt
index 98ce039cb7..2f9c9391af 100644
--- a/drivers/devicetree/CMakeLists.txt
+++ b/drivers/devicetree/CMakeLists.txt
@@ -30,6 +30,10 @@ if(CONFIG_DEVICE_TREE)
     list(APPEND SRCS fdt_virtio_mmio.c)
   endif()
 
+  if(CONFIG_MTD_CFI)
+    list(APPEND SRCS fdt_cfi.c)
+  endif()
+
   target_include_directories(drivers
                              PRIVATE ${NUTTX_DIR}/libs/libc/fdt/dtc/libfdt)
   target_sources(drivers PRIVATE ${SRCS})
diff --git a/drivers/devicetree/Make.defs b/drivers/devicetree/Make.defs
index 23b5e6e403..b67aadd581 100644
--- a/drivers/devicetree/Make.defs
+++ b/drivers/devicetree/Make.defs
@@ -32,6 +32,10 @@ ifeq ($(CONFIG_DRIVERS_VIRTIO_MMIO),y)
   CSRCS += fdt_virtio_mmio.c
 endif
 
+ifeq ($(CONFIG_MTD_CFI),y)
+  CSRCS += fdt_cfi.c
+endif
+
 CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)libs$(DELIM)libc$(DELIM)fdt$(DELIM)dtc$(DELIM)libfdt
 
 DEPPATH += --dep-path devicetree
diff --git a/drivers/devicetree/fdt_cfi.c b/drivers/devicetree/fdt_cfi.c
new file mode 100644
index 0000000000..e544ef18c0
--- /dev/null
+++ b/drivers/devicetree/fdt_cfi.c
@@ -0,0 +1,101 @@
+/****************************************************************************
+ * drivers/devicetree/fdt_cfi.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 <errno.h>
+#include <debug.h>
+
+#include <nuttx/config.h>
+#include <nuttx/fdt.h>
+#include <nuttx/mtd/mtd.h>
+
+#include <libfdt.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fdt_cfi_register
+ *
+ * Description:
+ *   This function is used to register an fci flash from the device tree
+ *
+ * Input Parameters:
+ *   fdt - Device tree handle
+ *
+ * Returned Value:
+ *   Return 0 if success, nageative if failed
+ *
+ ****************************************************************************/
+
+int fdt_cfi_register(FAR const void *fdt)
+{
+  int ret = 0;
+  int offset = -1;
+
+  while ((offset = fdt_node_offset_by_compatible(fdt, offset, "cfi-flash"))
+         >= 0)
+    {
+      uintptr_t address_cell = fdt_get_parent_address_cells(fdt, offset);
+      uintptr_t size_cell    = fdt_get_parent_size_cells(fdt, offset);
+      uint32_t bankwidth     = fdt_get_bankwidth(fdt, offset);
+      uint32_t reg_cnt       = fdt_get_reg_count(fdt, offset);
+      size_t num;
+      size_t i;
+
+      /* get number of "address & size" cell pairs */
+
+      num = reg_cnt / (4 * (address_cell + size_cell));
+      for (i = 0; i < num; i++)
+        {
+          uintptr_t flash_base = fdt_get_reg_base_by_index(fdt, offset, i);
+          uintptr_t flash_end = fdt_get_reg_size_by_index(fdt, offset, i) +
+                                flash_base;
+
+          if ((CONFIG_FLASH_START > flash_end ||
+               CONFIG_FLASH_START + CONFIG_FLASH_SIZE < flash_base))
+            {
+              ret = register_cfi_driver(flash_base, flash_end,
+                                        bankwidth, i);
+              if (ret < 0)
+                {
+                  break;
+                }
+            }
+          else
+            {
+              ferr("cfi flash%d has beed used to store text:"
+                   "[%x,%x], flash:[%x,%x]\n", i, flash_base, flash_end,
+                   CONFIG_FLASH_START, CONFIG_FLASH_START +
+                   CONFIG_FLASH_SIZE);
+            }
+        }
+    }
+
+  return ret;
+}
diff --git a/include/nuttx/fdt.h b/include/nuttx/fdt.h
index 23b939bc45..af2fda728d 100644
--- a/include/nuttx/fdt.h
+++ b/include/nuttx/fdt.h
@@ -491,4 +491,21 @@ int fdt_pci_ecam_register(FAR const void *fdt);
 int fdt_virtio_mmio_devices_register(FAR const void *fdt, int irqbase);
 #endif
 
+/****************************************************************************
+ * Name: fdt_cfi_register
+ *
+ * Description:
+ *   This function is used to register an fci flash from the device tree
+ *
+ * Input Parameters:
+ *   fdt - Device tree handle
+ *
+ * Returned Value:
+ *   Return 0 if success, nageative if failed
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_CFI
+int fdt_cfi_register(FAR const void *fdt);
+#endif
 #endif /* __INCLUDE_NUTTX_FDT_H */


(nuttx) 01/04: drivers/mtd: cfi-flash driver

Posted by ar...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit ac019bbfa2707c7eeddcd8b3be2294c9a75b5c4e
Author: zhengyu9 <zh...@xiaomi.com>
AuthorDate: Fri Jul 26 15:38:00 2024 +0800

    drivers/mtd: cfi-flash driver
    
    Realization of cfi-flash driver.
    Add the interfaces of intel and amd command sets.
    
    Signed-off-by: zhengyu9 <zh...@xiaomi.com>
    Signed-off-by: dongjiuzhu1 <do...@xiaomi.com>
---
 Documentation/components/drivers/special/mtd.rst |   20 +
 drivers/mtd/cfi.c                                | 1148 ++++++++++++++++++++++
 drivers/mtd/cfi.h                                |  131 +++
 3 files changed, 1299 insertions(+)

diff --git a/Documentation/components/drivers/special/mtd.rst b/Documentation/components/drivers/special/mtd.rst
index fd9ccd0e8e..d1d6731afb 100644
--- a/Documentation/components/drivers/special/mtd.rst
+++ b/Documentation/components/drivers/special/mtd.rst
@@ -66,6 +66,26 @@ massive data storage. For these reasons, it may not be convenient to use
 the more complex MTD interface but instead use the simple character
 interface provided by the EEPROM drivers.  See drivers/eeprom.
 
+CFI FLASH
+=========
+
+CFI, the full name of which is Common Flash Interface, is a standard proposed
+by JEDEC (Joint Electron Device Engineering Council) in 2003. The role of CFI
+(Common Flash Interface) is to read the information of NOR Flash through a
+unified method.
+
+This standard defines a query interface for Flash that complies with CFI,
+allowing parameterization and platformization of read, write, erase, and other
+interfaces for Flash. It allows programmers to read electrical characteristics
+of Flash-related devices, including memory size, erase speed, special features,
+and more. In other words, it is equivalent to loading the data manual of Flash
+into the physical device of Flash, It can be read and used by relevant personnel
+when needed.
+
+CFI's instruction link:
+https://netwinder.osuosl.org/pub/netwinder/docs/nw/flash/29220403.pdf
+CFI supports intel and amd instruction sets, and currently device drivers are
+already supported
 
 NAND MEMORY
 ===========
diff --git a/drivers/mtd/cfi.c b/drivers/mtd/cfi.c
new file mode 100644
index 0000000000..ec0853bfa0
--- /dev/null
+++ b/drivers/mtd/cfi.c
@@ -0,0 +1,1148 @@
+/****************************************************************************
+ * drivers/mtd/cfi.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 <sys/types.h>
+#include <sys/param.h>
+#include <stdint.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/arch.h>
+#include <nuttx/nuttx.h>
+
+#include "cfi.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* type of port */
+
+#define CFI_PORT_8BIT                      0x01
+#define CFI_PORT_16BIT                     0x02
+#define CFI_PORT_32BIT                     0X04
+#define CFI_PORT_64BIT                     0x08
+
+/* Common CFI value */
+
+#define CFI_CMD                            0x98
+#define CFI_QRY_OFFSET                     0x10
+#define CFI_ERASE_BLOCK_REGION_INFO_OFFSET 0x2D
+
+/* CFI command set number */
+
+#define CFI_CMDSET_INTEL_EXTENDED          0x0001
+#define CFI_CMDSET_AMD_STANDARD            0x0002
+#define CFI_CMDSET_INTEL_STANDARD          0x0003
+#define CFI_CMDSET_AMD_EXTENDED            0x0004
+#define CFI_CMDSET_MITS_STANDARD           0x0100
+#define CFI_CMDSET_MITS_EXTENDED           0x0101
+
+/* Intel command value */
+
+#define CFI_INTEL_CMD_RESET                0xFF
+#define CFI_INTEL_CMD_CLEAR_STATUS         0x50
+#define CFI_INTEL_CMD_BLOCK_ERASE          0x20
+#define CFI_INTEL_CMD_ERASE_CONFIRM        0xD0
+#define CFI_INTEL_CMD_WRITE                0x40
+#define CFI_INTEL_CMD_WRITE_BUFFER         0xE8
+#define CFI_INTEL_CMD_WRITE_CONFIRM        0xD0
+
+/* Intel status value */
+
+#define CFI_INTEL_STATUS_DONE              0x80
+#define CFI_INTEL_STATUS_ESS               0x40 /* erase suspended */
+#define CFI_INTEL_STATUS_ECLBS             0x20 /* clear block lock bit
+                                                 * or erase error */
+#define CFI_INTEL_STATUS_PSLBS             0X10 /* write or block lock bit
+                                                 * set error */
+#define CFI_INTEL_STATUS_VPENS             0x08 /* voltage range error */
+#define CFI_INTEL_STATUS_PSS               0x04 /* write is suspended */
+#define CFI_INTEL_STATUS_DPS               0x02 /* device protected error */
+
+/* Amd command value */
+
+#define CFI_AMD_CMD_RESET                  0xF0
+#define CFI_AMD_CMD_UNLOCK1                0xAA
+#define CFI_AMD_CMD_UNLOCK2                0x55
+#define CFI_AMD_CMD_ERASE_START            0x80
+#define CFI_AMD_CMD_ERASE_SECTOR           0x30
+#define CFI_AMD_CMD_WRITE                  0xA0
+#define CFI_AMD_CMD_WRITE_BUFFER           0x25
+#define CFI_AMD_CMD_WRITE_CONFIRM          0x29
+
+/* Amd status value */
+
+#define CFI_AMD_STATUS_TOGGLE              0x40 /* DQ6 will toggle after a
+                                                 * reading cycle during
+                                                 * erasing */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Supports up to 64bits bank width */
+
+typedef uint64_t cfiword_t;
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* the offset of CFI query address is usually 0x55
+ * Spansion S29WS-N CFI query fix is to try 0x555 if 0x55 fails
+ */
+
+static const uint32_t g_cfi_query_address[2] =
+{
+  0x55, 0x555
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cfi_get_block_addr
+ *
+ * Description:
+ *   Get the start address of the block.
+ *
+ ****************************************************************************/
+
+static uintptr_t cfi_get_block_addr(FAR struct cfi_dev_s *cfi,
+                                    blkcnt_t block)
+{
+  uint8_t i;
+  uintptr_t base = cfi->base_addr;
+
+  for (i = 0; i < cfi->info.erase_region_num; i++)
+    {
+      blkcnt_t block_num = cfi_get_blocknum(cfi, i);
+      size_t block_size = cfi_get_blocksize(cfi, i);
+
+      if (block >= block_num)
+        {
+          block -= block_num;
+          base += block_num * block_size;
+          continue;
+        }
+
+      base += block * block_size;
+      break;
+    }
+
+  return base;
+}
+
+/****************************************************************************
+ * Name: cfi_addr_map
+ *
+ * Description:
+ *   Get the address according to block number and offset, and needs to
+ *   convert it according to bankwidth.
+ *
+ ****************************************************************************/
+
+static uintptr_t cfi_addr_map(FAR struct cfi_dev_s *cfi, blkcnt_t block,
+                              off_t offset)
+{
+  return cfi_get_block_addr(cfi, block) + cfi->bankwidth * offset;
+}
+
+/****************************************************************************
+ * Name: cfi_make_cmd
+ *
+ * Description:
+ *   Make command value according to bankwidth and device width. For example,
+ *   a command 0x98 should be 0x00980098 for a flash with bankwidth = 4 and
+ *   dev_width = 2.
+ *
+ ****************************************************************************/
+
+static cfiword_t cfi_make_cmd(FAR struct cfi_dev_s *cfi, uint32_t cmd)
+{
+  cfiword_t result = 0;
+  uint8_t i = cfi->bankwidth / cfi->dev_width;
+
+  while (i--)
+    {
+      result = (result << (8 * cfi->dev_width)) | cmd;
+    }
+
+  return result;
+}
+
+/****************************************************************************
+ * Name: cfi_add_byte
+ *
+ * Description:
+ *   Add a byte to cfiword, which will be written into cfi-flash later.
+ *
+ ****************************************************************************/
+
+static void cfi_add_byte(FAR struct cfi_dev_s *cfi, FAR cfiword_t *word,
+                         uint8_t c)
+{
+  switch (cfi->bankwidth)
+    {
+      case CFI_PORT_8BIT:
+        *word = c;
+        break;
+      case CFI_PORT_16BIT:
+        *word = (*word >> 8) | (uint16_t)c << 8;
+        break;
+      case CFI_PORT_32BIT:
+        *word = (*word >> 8) | (uint32_t)c << 24;
+        break;
+      default:
+        *word = (*word >> 8) | (uint64_t)c << 56;
+    }
+}
+
+/****************************************************************************
+ * Name: cfi_write_data
+ *
+ * Description:
+ *   Write a value to specified address.
+ *
+ ****************************************************************************/
+
+static inline void cfi_write_data(FAR struct cfi_dev_s *cfi, uintptr_t addr,
+                                  FAR const void *buffer)
+{
+  switch (cfi->bankwidth)
+    {
+      case CFI_PORT_8BIT:
+        *(FAR volatile uint8_t *)addr = *(FAR const uint8_t *)buffer;
+        break;
+      case CFI_PORT_16BIT:
+        *(FAR volatile uint16_t *)addr = *(FAR const uint16_t *)buffer;
+        break;
+      case CFI_PORT_32BIT:
+        *(FAR volatile uint32_t *)addr = *(FAR const uint32_t *)buffer;
+        break;
+      default:
+        *(FAR volatile uint64_t *)addr = *(FAR const uint64_t *)buffer;
+    }
+}
+
+/****************************************************************************
+ * Name: cfi_write_block_cmd
+ *
+ * Description:
+ *   Write a command about specified block.
+ *
+ ****************************************************************************/
+
+static void cfi_write_block_cmd(FAR struct cfi_dev_s *cfi, blkcnt_t block,
+                                uint32_t cmd)
+{
+  cfiword_t val = cfi_make_cmd(cfi, cmd);
+
+  cfi_write_data(cfi, cfi_addr_map(cfi, block, 0), &val);
+}
+
+/****************************************************************************
+ * Name: cfi_write_address_cmd
+ *
+ * Description:
+ *   Write a command to specified address.
+ *
+ ****************************************************************************/
+
+static void cfi_write_address_cmd(FAR struct cfi_dev_s *cfi, uintptr_t addr,
+                                  uint8_t cmd)
+{
+  cfiword_t val = cfi_make_cmd(cfi, cmd);
+
+  cfi_write_data(cfi, cfi_addr_map(cfi, 0, addr), &val);
+}
+
+/****************************************************************************
+ * Name: cfi_read_byte
+ *
+ * Description:
+ *   Read an 8-bit value from addr.
+ *
+ ****************************************************************************/
+
+static inline uint8_t cfi_read_byte(uintptr_t addr)
+{
+  return *(FAR volatile uint8_t *)addr;
+}
+
+/****************************************************************************
+ * Name: cfi_read_data
+ *
+ * Description:
+ *   Read a value to specified address.
+ *
+ ****************************************************************************/
+
+static inline cfiword_t cfi_read_data(FAR struct cfi_dev_s *cfi,
+                                      uintptr_t addr)
+{
+  switch (cfi->bankwidth)
+    {
+      case CFI_PORT_8BIT:
+        return cfi_read_byte(addr);
+      case CFI_PORT_16BIT:
+        return *(FAR const volatile uint16_t *)addr;
+      case CFI_PORT_32BIT:
+        return *(FAR const volatile uint32_t *)addr;
+      default:
+        return *(FAR const volatile uint64_t *)addr;
+    }
+}
+
+/****************************************************************************
+ * Name: cfi_is_equal
+ *
+ * Description:
+ *   Check whether the value corresponding to addr is equal to cmd.
+ *
+ ****************************************************************************/
+
+static bool cfi_is_equal(FAR struct cfi_dev_s *cfi, uintptr_t addr,
+                         uint8_t cmd)
+{
+  cfiword_t val = cfi_make_cmd(cfi, cmd);
+
+  addr = cfi_addr_map(cfi, 0, addr);
+  return cfi_read_data(cfi, addr) == val;
+}
+
+/****************************************************************************
+ * Name: cfi_is_set
+ *
+ * Description:
+ *   Check the status bit, which is set by cmd.
+ *
+ ****************************************************************************/
+
+static bool cfi_is_set(FAR struct cfi_dev_s *cfi, uint8_t cmd)
+{
+  cfiword_t val = cfi_make_cmd(cfi, cmd);
+  uintptr_t addr = cfi->base_addr;
+
+  return (cfi_read_data(cfi, addr) & val) == val;
+}
+
+/****************************************************************************
+ * Name: cfi_get_time_us
+ *
+ * Description:
+ *   Check whether the value corresponding to offset is equal to cmd.
+ *
+ ****************************************************************************/
+
+static inline uint32_t cfi_get_time_us(void)
+{
+  struct timespec ts;
+
+  clock_systime_timespec(&ts);
+  return 1000000 * ts.tv_sec + ts.tv_nsec / 1000;
+}
+
+/****************************************************************************
+ * Name: cfi_is_timeout
+ *
+ * Description:
+ *   Check whether the operation has timed out.
+ *
+ ****************************************************************************/
+
+static bool cfi_is_timeout(uint32_t start_us, uint32_t tout_us)
+{
+  return (int32_t)(cfi_get_time_us() - start_us) >= tout_us;
+}
+
+/****************************************************************************
+ * Name: cfi_intel_is_busy
+ *
+ * Description:
+ *   Function of intel cfi command set.
+ *   Check whether the cfi-flash is busy.
+ *
+ ****************************************************************************/
+
+static bool cfi_intel_is_busy(FAR struct cfi_dev_s *cfi, blkcnt_t block)
+{
+  return !cfi_is_set(cfi, CFI_INTEL_STATUS_DONE);
+}
+
+/****************************************************************************
+ * Name: cfi_intel_status_check
+ *
+ * Description:
+ *   Function of intel cfi command set.
+ *   Check the status register of the cfi-flash.
+ *
+ ****************************************************************************/
+
+static int cfi_intel_status_check(FAR struct cfi_dev_s *cfi,
+                                  blkcnt_t block, uint32_t tout)
+{
+  uint32_t start;
+
+  /* wait for command completion */
+
+  start = cfi_get_time_us();
+  while (cfi_intel_is_busy(cfi, block))
+    {
+      if (!cfi_is_timeout(start, tout))
+        {
+          up_udelay(1);
+          continue;
+        }
+
+      ferr("CFI flash operation has timed out.\n");
+
+      /* Check each status bit */
+
+      if (!cfi_is_equal(cfi, 0, CFI_INTEL_STATUS_DONE))
+        {
+          ferr("CFI flash operation failed.\n");
+          if (cfi_is_set(cfi, CFI_INTEL_STATUS_ECLBS |
+                         CFI_INTEL_STATUS_PSLBS))
+            {
+             ferr("CFI: command sequence error\n");
+            }
+          else if (cfi_is_set(cfi, CFI_INTEL_STATUS_ECLBS))
+            {
+             ferr("CFI: block erase error!\n");
+            }
+          else if (cfi_is_set(cfi, CFI_INTEL_STATUS_PSLBS))
+            {
+             ferr("CFI: locking error!\n");
+            }
+          else if (cfi_is_set(cfi, CFI_INTEL_STATUS_DPS))
+            {
+             ferr("CFI: block locked!\n");
+            }
+          else if (cfi_is_set(cfi, CFI_INTEL_STATUS_VPENS))
+            {
+             ferr("CFI: Vpp Low error!\n");
+            }
+        }
+
+      cfi_reset(cfi);
+      return -EAGAIN;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: cfi_intel_write_word
+ *
+ * Description:
+ *   Function of intel cfi command set.
+ *   Write data with the width of cfiword_t into cfi-flash.
+ *
+ ****************************************************************************/
+
+static int cfi_intel_write_word(FAR struct cfi_dev_s *cfi,
+                                uintptr_t addr, cfiword_t word)
+{
+  off_t offset = addr - cfi->base_addr;
+
+  cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_CLEAR_STATUS);
+  cfi_write_address_cmd(cfi, offset / cfi->bankwidth, CFI_INTEL_CMD_WRITE);
+  cfi_write_data(cfi, addr, &word);
+
+  return cfi_intel_status_check(cfi, cfi_find_block(cfi, offset),
+                                (1 << cfi->info.single_write_timeout_typ) *
+                                (1 << cfi->info.single_write_timeout_max));
+}
+
+/****************************************************************************
+ * Name: cfi_intel_write_buffer
+ *
+ * Description:
+ *   Function of intel cfi command set.
+ *   Write data from buffer into cfi-flash.
+ *
+ ****************************************************************************/
+
+static int cfi_intel_write_buffer(FAR struct cfi_dev_s *cfi,
+                                  uintptr_t addr, size_t nbytes,
+                                  FAR const uint8_t *buffer)
+{
+  int ret;
+  blkcnt_t block;
+  off_t offset = addr - cfi->base_addr;
+
+  block = cfi_find_block(cfi, offset);
+  cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_CLEAR_STATUS);
+  cfi_write_block_cmd(cfi, block, CFI_INTEL_CMD_WRITE_BUFFER);
+  ret = cfi_intel_status_check(cfi, block,
+                               (1 << cfi->info.buffer_write_timeout_typ) *
+                               (1 << cfi->info.buffer_write_timeout_max));
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  addr = cfi->base_addr + offset;
+  nbytes = nbytes / cfi->bankwidth;
+
+  cfi_write_block_cmd(cfi, block, nbytes - 1);
+  while (nbytes-- > 0)
+    {
+      cfi_write_data(cfi, addr, buffer);
+      addr += cfi->bankwidth;
+      buffer += cfi->bankwidth;
+    }
+
+  cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_WRITE_CONFIRM);
+
+  return cfi_intel_status_check(cfi, block,
+                               (1 << cfi->info.buffer_write_timeout_typ) *
+                               (1 << cfi->info.buffer_write_timeout_max));
+}
+
+/****************************************************************************
+ * Name: cfi_intel_erase
+ *
+ * Description:
+ *   Function of intel cfi command set.
+ *   Erase one block.
+ *
+ ****************************************************************************/
+
+static int cfi_intel_erase(FAR struct cfi_dev_s *cfi, blkcnt_t block)
+{
+  cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_CLEAR_STATUS);
+  cfi_write_block_cmd(cfi, block, CFI_INTEL_CMD_BLOCK_ERASE);
+  cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_ERASE_CONFIRM);
+  return cfi_intel_status_check(cfi, block, 1000 *
+                                (1 << cfi->info.block_erase_timeout_typ) *
+                                (1 << cfi->info.block_erase_timeout_max));
+}
+
+/****************************************************************************
+ * Name: cfi_amd_fix
+ *
+ * Description:
+ *   Set the variables specific to AMD.
+ *
+ ****************************************************************************/
+
+static void cfi_amd_fix(FAR struct cfi_dev_s *cfi)
+{
+  /* x8/x16 in x8 mode or x16/x32 in x16 mode */
+
+  if ((cfi->dev_width == 1 && cfi->info.interface_desc == 0x02) ||
+      (cfi->dev_width == 2 && cfi->info.interface_desc == 0x04))
+    {
+      cfi->unlock_addr1 = 0xaaa;
+      cfi->unlock_addr2 = 0x555;
+    }
+  else
+    {
+      cfi->unlock_addr1 = 0x555;
+      cfi->unlock_addr2 = 0x2aa;
+    }
+}
+
+/****************************************************************************
+ * Name: cfi_amd_unlock
+ *
+ * Description:
+ *   Flash unlock command sequence specific to AMD.
+ *
+ ****************************************************************************/
+
+static void cfi_amd_unlock(FAR struct cfi_dev_s *cfi)
+{
+  cfi_write_address_cmd(cfi, cfi->unlock_addr1, CFI_AMD_CMD_UNLOCK1);
+  cfi_write_address_cmd(cfi, cfi->unlock_addr2, CFI_AMD_CMD_UNLOCK2);
+}
+
+/****************************************************************************
+ * Name: cfi_amd_is_busy
+ *
+ * Description:
+ *   Function of amd cfi command set.
+ *   Check whether the cfi-flash is busy.
+ *
+ ****************************************************************************/
+
+static bool cfi_amd_is_busy(FAR struct cfi_dev_s *cfi, blkcnt_t block)
+{
+  uintptr_t addr;
+
+  addr = cfi_addr_map(cfi, block, 0);
+
+  /* status bit DQ6 will toggle after a reading cycle during erasing */
+
+  return ((cfi_read_byte(addr) ^ cfi_read_byte(addr)) &
+          CFI_AMD_STATUS_TOGGLE) != 0;
+}
+
+/****************************************************************************
+ * Name: cfi_amd_status_check
+ *
+ * Description:
+ *   Function of amd cfi command set.
+ *   Check the status register of the cfi-flash.
+ *
+ ****************************************************************************/
+
+static int cfi_amd_status_check(FAR struct cfi_dev_s *cfi,
+                                blkcnt_t block, uint32_t tout)
+{
+  uint32_t start;
+
+  /* wait for command completion */
+
+  start = cfi_get_time_us();
+  while (cfi_amd_is_busy(cfi, block))
+    {
+      if (cfi_is_timeout(start, tout))
+        {
+          ferr("CFI flash operation has timed out.\n");
+          cfi_reset(cfi);
+          return -EAGAIN;
+        }
+
+      up_udelay(1);
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: cfi_amd_write_word
+ *
+ * Description:
+ *   Function of amd cfi command set.
+ *   Write data with the width of cfiword_t into cfi-flash.
+ *
+ ****************************************************************************/
+
+static int cfi_amd_write_word(FAR struct cfi_dev_s *cfi,
+                              uintptr_t addr, cfiword_t word)
+{
+  off_t offset = addr - cfi->base_addr;
+
+  cfi_amd_unlock(cfi);
+  cfi_write_address_cmd(cfi, cfi->unlock_addr1, CFI_AMD_CMD_WRITE);
+  cfi_write_data(cfi, addr, &word);
+
+  return cfi_amd_status_check(cfi, cfi_find_block(cfi, offset),
+                              (1 << cfi->info.single_write_timeout_typ) *
+                              (1 << cfi->info.single_write_timeout_max));
+}
+
+/****************************************************************************
+ * Name: cfi_amd_write_buffer
+ *
+ * Description:
+ *   Function of amd cfi command set.
+ *   Write data from buffer into cfi-flash.
+ *
+ ****************************************************************************/
+
+static int cfi_amd_write_buffer(FAR struct cfi_dev_s *cfi, uintptr_t addr,
+                                size_t nbytes, FAR const uint8_t *buffer)
+{
+  blkcnt_t block;
+  off_t offset = addr - cfi->base_addr;
+
+  block = cfi_find_block(cfi, offset);
+  cfi_amd_unlock(cfi);
+  cfi_write_block_cmd(cfi, block, CFI_AMD_CMD_WRITE_BUFFER);
+
+  nbytes = nbytes / cfi->bankwidth;
+  cfi_write_block_cmd(cfi, block, nbytes - 1);
+
+  while (nbytes-- > 0)
+    {
+      cfi_write_data(cfi, addr, buffer);
+      addr += cfi->bankwidth;
+      buffer += cfi->bankwidth;
+    }
+
+  cfi_write_address_cmd(cfi, 0, CFI_AMD_CMD_WRITE_CONFIRM);
+  return cfi_amd_status_check(cfi, block,
+                              (1 << cfi->info.buffer_write_timeout_max) *
+                              (1 << cfi->info.buffer_write_timeout_max));
+}
+
+/****************************************************************************
+ * Name: cfi_amd_erase
+ *
+ * Description:
+ *   Function of amd cfi command set.
+ *   Erase one block.
+ *
+ ****************************************************************************/
+
+static int cfi_amd_erase(FAR struct cfi_dev_s *cfi, blkcnt_t block)
+{
+  cfi_amd_unlock(cfi);
+  cfi_write_address_cmd(cfi, cfi->unlock_addr1, CFI_AMD_CMD_ERASE_START);
+  cfi_amd_unlock(cfi);
+  cfi_write_block_cmd(cfi, block, CFI_AMD_CMD_ERASE_SECTOR);
+  return cfi_amd_status_check(cfi, block, 1000 *
+                              (1 << cfi->info.block_erase_timeout_typ) *
+                              (1 << cfi->info.block_erase_timeout_max));
+}
+
+/****************************************************************************
+ * Name: cfi_write_word
+ *
+ * Description:
+ *   Write data with the width of cfiword_t into cfi-flash.
+ *
+ ****************************************************************************/
+
+static int cfi_write_word(FAR struct cfi_dev_s *cfi, uintptr_t addr,
+                          cfiword_t word)
+{
+  switch (cfi->info.p_id)
+    {
+      case CFI_CMDSET_INTEL_EXTENDED:
+      case CFI_CMDSET_INTEL_STANDARD:
+        return cfi_intel_write_word(cfi, addr, word);
+      case CFI_CMDSET_AMD_STANDARD:
+      case CFI_CMDSET_AMD_EXTENDED:
+        return cfi_amd_write_word(cfi, addr, word);
+      case CFI_CMDSET_MITS_STANDARD:
+      case CFI_CMDSET_MITS_EXTENDED:
+      default:
+        return -EINVAL;
+    }
+}
+
+/****************************************************************************
+ * Name: cfi_write_buffer
+ *
+ * Description:
+ *   Write data from buffer into cfi-flash.
+ *
+ ****************************************************************************/
+
+static int cfi_write_buffer(FAR struct cfi_dev_s *cfi, uintptr_t addr,
+                            size_t nbytes, FAR const uint8_t *buffer)
+{
+  switch (cfi->info.p_id)
+    {
+      case CFI_CMDSET_INTEL_EXTENDED:
+      case CFI_CMDSET_INTEL_STANDARD:
+        return cfi_intel_write_buffer(cfi, addr, nbytes, buffer);
+      case CFI_CMDSET_AMD_STANDARD:
+      case CFI_CMDSET_AMD_EXTENDED:
+        return cfi_amd_write_buffer(cfi, addr, nbytes, buffer);
+      case CFI_CMDSET_MITS_STANDARD:
+      case CFI_CMDSET_MITS_EXTENDED:
+      default:
+        return -EINVAL;
+    }
+}
+
+/****************************************************************************
+ * Name: cfi_erase_one
+ *
+ * Description:
+ *   Erase one block.
+ *
+ ****************************************************************************/
+
+static int cfi_erase_one(FAR struct cfi_dev_s *cfi, blkcnt_t block)
+{
+  switch (cfi->info.p_id)
+    {
+      case CFI_CMDSET_INTEL_EXTENDED:
+      case CFI_CMDSET_INTEL_STANDARD:
+        return cfi_intel_erase(cfi, block);
+      case CFI_CMDSET_AMD_STANDARD:
+      case CFI_CMDSET_AMD_EXTENDED:
+        return cfi_amd_erase(cfi, block);
+      case CFI_CMDSET_MITS_STANDARD:
+      case CFI_CMDSET_MITS_EXTENDED:
+      default:
+        return -EINVAL;
+    }
+}
+
+static ssize_t cfi_write_unalign(FAR struct cfi_dev_s *cfi, off_t offset,
+                                 size_t nbytes, FAR const uint8_t *buffer)
+{
+  uint8_t i;
+  int ret;
+  size_t delta;
+  uintptr_t wp;
+  cfiword_t word = 0;
+
+  /* get lower aligned address */
+
+  wp = offset & ~(cfi->bankwidth - 1);
+
+  /* handle unaligned start */
+
+  if ((delta = offset - wp) == 0)
+    {
+      return 0;
+    }
+
+  for (i = 0; i < delta; i++)
+    {
+      cfi_add_byte(cfi, &word, cfi_read_byte(wp + i));
+    }
+
+  delta = 0;
+  for (; i < cfi->bankwidth && nbytes > 0; i++)
+    {
+      cfi_add_byte(cfi, &word, *buffer++);
+      nbytes--;
+      delta++;
+    }
+
+  for (; i < cfi->bankwidth; i++)
+    {
+      cfi_add_byte(cfi, &word, cfi_read_byte(wp + i));
+    }
+
+  ret = cfi_write_word(cfi, wp, word);
+  return ret < 0 ? ret : delta;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cfi_get_blocksize
+ *
+ * Description:
+ *   Get the size of block in the specified region.
+ *
+ ****************************************************************************/
+
+size_t cfi_get_blocksize(FAR struct cfi_dev_s *cfi, uint8_t region)
+{
+  return cfi->dev_num * (cfi->info.erase_region_info[region] >> 16) * 256;
+}
+
+/****************************************************************************
+ * Name: cfi_get_blocknum
+ *
+ * Description:
+ *   Get the number of blocks in the specified region.
+ *
+ ****************************************************************************/
+
+blkcnt_t cfi_get_blocknum(FAR struct cfi_dev_s *cfi, uint8_t region)
+{
+  return ((uint16_t)(cfi->info.erase_region_info[region])) + 1;
+}
+
+/****************************************************************************
+ * Name: cfi_find_block
+ *
+ * Description:
+ *   Get the block number corresponding to offset.
+ *
+ ****************************************************************************/
+
+blkcnt_t cfi_find_block(FAR struct cfi_dev_s *cfi, off_t offset)
+{
+  uint8_t i;
+  blkcnt_t ret = 0;
+
+  for (i = 0; i < cfi->info.erase_region_num; i++)
+    {
+      blkcnt_t block_num = cfi_get_blocknum(cfi, i);
+      size_t block_size = cfi_get_blocksize(cfi, i);
+
+      if (block_num * block_size <= offset)
+        {
+          offset -= block_num * block_size;
+          ret += block_num;
+        }
+      else
+        {
+          ret += offset / block_size;
+          break;
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: cfi_get_total_blocknum
+ *
+ * Description:
+ *   Get total number of erase blocks, assuming that all erase blocks are the
+ *   same size.
+ *
+ ****************************************************************************/
+
+blkcnt_t cfi_get_total_blocknum(FAR struct cfi_dev_s *cfi)
+{
+  uint8_t i;
+  blkcnt_t blocknum = 0;
+
+  for (i = 0; i < cfi->info.erase_region_num; i++)
+    {
+      blocknum += cfi_get_blocknum(cfi, i) * cfi_get_blocksize(cfi, i) /
+                  cfi_get_blocksize(cfi, 0);
+    }
+
+  return blocknum;
+}
+
+/****************************************************************************
+ * Name: cfi_check
+ *
+ * Description:
+ *   Check whether the device is a cfi device, if so, then get the cfi data.
+ *
+ ****************************************************************************/
+
+int cfi_check(FAR struct cfi_dev_s *cfi)
+{
+  uint8_t i;
+  uint8_t j;
+
+  /* try different single device width which we don't know now */
+
+  for (cfi->dev_width = 1; cfi->dev_width <= cfi->bankwidth;
+       cfi->dev_width <<= 1)
+    {
+      cfi_reset(cfi);
+      for (i = 0; i < nitems(g_cfi_query_address); i++)
+        {
+          cfi_write_address_cmd(cfi, g_cfi_query_address[i], CFI_CMD);
+
+          /* try to get into cfi mode and read cfi data */
+
+          if (cfi_is_equal(cfi, CFI_QRY_OFFSET, 'Q') &&
+              cfi_is_equal(cfi, CFI_QRY_OFFSET + 1, 'R') &&
+              cfi_is_equal(cfi, CFI_QRY_OFFSET + 2, 'Y'))
+            {
+              FAR struct cfi_info_s *info = &(cfi->info);
+              FAR uint8_t *ptr = (FAR uint8_t *)info;
+
+              for (j = CFI_QRY_OFFSET;
+                   j < CFI_ERASE_BLOCK_REGION_INFO_OFFSET; j++)
+                {
+                  *ptr++ = cfi_read_byte(cfi_addr_map(cfi, 0, j));
+                }
+
+              while (j < CFI_ERASE_BLOCK_REGION_INFO_OFFSET + 4 *
+                     info->erase_region_num)
+                {
+                  *ptr++ = cfi_read_byte(cfi_addr_map(cfi, 0, j++));
+                }
+
+              cfi->cfi_offset = g_cfi_query_address[i];
+              cfi->dev_num = cfi->bankwidth / cfi->dev_width;
+              cfi->page_size = (1 << info->max_write_bytes_num) *
+                               cfi->dev_num;
+
+              /* fix amd feature */
+
+              if (info->p_id == CFI_CMDSET_AMD_STANDARD ||
+                  info->p_id == CFI_CMDSET_AMD_EXTENDED)
+                {
+                  cfi_amd_fix(cfi);
+                }
+
+              /* exit cfi mode, enter into read array mode */
+
+              cfi_reset(cfi);
+              return 0;
+            }
+        }
+    }
+
+  /* cfi flash not found */
+
+  return -EINVAL;
+}
+
+/****************************************************************************
+ * Name: cfi_reset
+ *
+ * Description:
+ *   Get into read array mode.
+ *
+ ****************************************************************************/
+
+void cfi_reset(FAR struct cfi_dev_s *cfi)
+{
+  switch (cfi->info.p_id)
+    {
+      case CFI_CMDSET_INTEL_EXTENDED:
+      case CFI_CMDSET_INTEL_STANDARD:
+        cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_CLEAR_STATUS);
+        cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_RESET);
+        break;
+      case CFI_CMDSET_AMD_STANDARD:
+      case CFI_CMDSET_AMD_EXTENDED:
+        cfi_write_address_cmd(cfi, 0, CFI_AMD_CMD_RESET);
+    }
+}
+
+/****************************************************************************
+ * Name: cfi_erase
+ *
+ * Description:
+ *   Erase multi blocks.
+ *
+ ****************************************************************************/
+
+int cfi_erase(FAR struct cfi_dev_s *cfi, blkcnt_t startblock,
+              blkcnt_t blockcnt)
+{
+  blkcnt_t i;
+  int ret = 0;
+
+  for (i = startblock; i < startblock + blockcnt; i++)
+    {
+      ret = cfi_erase_one(cfi, i);
+      if (ret < 0)
+        {
+          break;
+        }
+    }
+
+  cfi_reset(cfi);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: cfi_read
+ *
+ * Description:
+ *   Read data from cfi-flash to buffer.
+ *
+ ****************************************************************************/
+
+int cfi_read(FAR struct cfi_dev_s *cfi, off_t offset, size_t nbytes,
+             FAR uint8_t *buffer)
+{
+  uintptr_t base = cfi->base_addr;
+
+  memcpy(buffer, (FAR const uint8_t *)base + offset, nbytes);
+  return 0;
+}
+
+/****************************************************************************
+ * Name: cfi_write
+ *
+ * Description:
+ *   Write data from buffer to cfi-flash.
+ *
+ ****************************************************************************/
+
+int cfi_write(FAR struct cfi_dev_s *cfi, off_t offset, size_t nbytes,
+              FAR const uint8_t *buffer)
+{
+  ssize_t ret;
+
+  offset = cfi->base_addr + offset;
+  ret = cfi_write_unalign(cfi, offset, nbytes, buffer);
+  if (ret < 0)
+    {
+      goto out;
+    }
+
+  offset = ALIGN_UP(offset, cfi->bankwidth);
+  nbytes -= ret;
+  buffer += ret;
+
+  /* handle the aligned part */
+
+  /* if the device support buffer write */
+
+  if (cfi->info.max_write_bytes_num != 0)
+    {
+      while (nbytes >= cfi->bankwidth)
+        {
+          size_t size = cfi->page_size - offset % cfi->page_size;
+
+          if (size > nbytes)
+            {
+              size = nbytes;
+            }
+
+          ret = cfi_write_buffer(cfi, offset, size, buffer);
+          if (ret < 0)
+            {
+              goto out;
+            }
+
+          offset += size;
+          buffer += size;
+          nbytes -= size;
+        }
+    }
+  else
+    {
+      while (nbytes >= cfi->bankwidth)
+        {
+          cfiword_t word = 0;
+          uint8_t i;
+
+          for (i = 0; i < cfi->bankwidth; i++)
+            {
+              cfi_add_byte(cfi, &word, *buffer++);
+            }
+
+          ret = cfi_write_word(cfi, offset, word);
+          if (ret < 0)
+            {
+              goto out;
+            }
+
+          offset += cfi->bankwidth;
+          nbytes -= cfi->bankwidth;
+        }
+    }
+
+  if (nbytes == 0)
+    {
+      goto out;
+    }
+
+  /* handle unaligned tail bytes */
+
+  ret = cfi_write_unalign(cfi, offset, nbytes, buffer);
+
+out:
+  cfi_reset(cfi);
+  return ret < 0 ? ret : 0;
+}
diff --git a/drivers/mtd/cfi.h b/drivers/mtd/cfi.h
new file mode 100644
index 0000000000..28cdecf380
--- /dev/null
+++ b/drivers/mtd/cfi.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+ * drivers/mtd/cfi.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_MTD_CFI_H
+#define __DRIVERS_MTD_CFI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/mtd/mtd.h>
+#include <nuttx/compiler.h>
+
+/****************************************************************************
+ * Pre-processor definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+begin_packed_struct struct cfi_info_s
+{
+  uint8_t qry[3];                   /* query-unique ASCII string 'QRY' */
+  uint16_t p_id;                    /* primary vendor command set and control
+                                     * interface id */
+  uint16_t p_addr;                  /* primary algorithm extended query table
+                                     * address */
+  uint16_t a_id;                    /* alternative vendor command set and
+                                     * control interface id */
+  uint16_t a_addr;                  /* alternative algorithm extended query
+                                     * table address */
+  uint8_t vccmin;                   /* Vcc logic supply minimum program/erase
+                                     * or write voltage */
+  uint8_t vccmax;                   /* Vcc logic supply maximum program/erase
+                                     * or write voltage */
+  uint8_t vppmin;                   /* Vpp logic supply minimum program/erase
+                                     * or write voltage */
+  uint8_t vppmax;                   /* Vpp logic supply maximum program/erase
+                                     * or write voltage */
+  uint8_t single_write_timeout_typ; /* typical timeout per single byte/word/
+                                     * D-word program, 2^N us */
+  uint8_t buffer_write_timeout_typ; /* typical timeout for maximum-size
+                                     * multi-byte program, 2^N us */
+  uint8_t block_erase_timeout_typ;  /* typical timeout per individual block
+                                     * erase, 2^N ms */
+  uint8_t chip_erase_timeout_typ;   /* typical timeout for full chip erase,
+                                     * 2^N ms */
+  uint8_t single_write_timeout_max; /* maximum timeout per single byte/word/
+                                     * D-word program, 2^N times typical */
+  uint8_t buffer_write_timeout_max; /* maximum timeout for multi-byte program,
+                                     * 2^N times typical */
+  uint8_t block_erase_timeout_max;  /* maximum timeout per individual block
+                                     * erase, 2^N times typical */
+  uint8_t chip_erase_timeout_max;   /* typical timeout for full chip erase,
+                                     * 2^N times typical */
+  uint8_t device_size;              /* device size = 2^N in number of bytes */
+  uint16_t interface_desc;          /* flash device interface code
+                                     * description */
+  uint16_t max_write_bytes_num;     /* maximum number of bytes in multi-byte
+                                     * program = 2^N */
+  uint8_t erase_region_num;         /* number of erase block regions */
+  uint32_t erase_region_info[4];    /* erase block region information
+                                     * bits 31-16 = z, where the erase blocks
+                                     * within this region are z times 256
+                                     * bytes.
+                                     * bits 15-0 = y, where y + 1 = number of
+                                     * erase blocks within the erase block
+                                     * region. */
+}end_packed_struct;
+
+/* This type represents the state of the MTD device.  The struct mtd_dev_s
+ * must appear at the beginning of the definition so that you can freely
+ * cast between pointers to struct mtd_dev_s and struct skel_dev_s.
+ */
+
+struct cfi_dev_s
+{
+  /* Publicly visible representation of the interface */
+
+  struct mtd_dev_s mtd;
+
+  /* Other implementation specific data may follow here */
+
+  uintptr_t base_addr;        /* base address of cfi flash */
+  uintptr_t end_addr;         /* end address of cfi flash */
+  size_t page_size;           /* page size for read and write */
+  uint8_t bankwidth;          /* port width of the whole device */
+  uint8_t dev_width;          /* port width of single chip */
+  uint8_t dev_num;            /* equals to bankwith divided by dev_width */
+  uint8_t cfi_offset;         /* cfi offset address, 0x55 or 0x555 */
+  uint32_t unlock_addr1;      /* unlock addr1 for amd */
+  uint32_t unlock_addr2;      /* unlock addr2 for amd */
+  struct cfi_info_s info;     /* struct of cfi information */
+};
+
+/****************************************************************************
+ * Public function prototypes
+ ****************************************************************************/
+
+size_t cfi_get_blocksize(FAR struct cfi_dev_s *cfi, uint8_t region);
+blkcnt_t cfi_get_blocknum(FAR struct cfi_dev_s *cfi, uint8_t region);
+blkcnt_t cfi_find_block(FAR struct cfi_dev_s *cfi, off_t offset);
+blkcnt_t cfi_get_total_blocknum(FAR struct cfi_dev_s *cfi);
+int cfi_check(FAR struct cfi_dev_s *cfi);
+void cfi_reset(FAR struct cfi_dev_s *cfi);
+int cfi_erase(FAR struct cfi_dev_s *cfi, blkcnt_t startblock,
+              blkcnt_t blockcnt);
+int cfi_read(FAR struct cfi_dev_s *cfi, off_t offset, size_t nbytes,
+             FAR uint8_t *buffer);
+int cfi_write(FAR struct cfi_dev_s *cfi, off_t offset, size_t nbytes,
+              FAR const uint8_t *buffer);
+
+#endif  /* __DRIVERS_MTD_CFI_H */


(nuttx) 03/04: qemu/armv7-a: adjust flash size to free area to register cfi flash

Posted by ar...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit ef24233501298d3a349a66a1a2f4a42978851dc7
Author: dongjiuzhu1 <do...@xiaomi.com>
AuthorDate: Sat Feb 8 12:11:46 2025 +0800

    qemu/armv7-a: adjust flash size to free area to register cfi flash
    
    122M->48M, free second cfi flash as filesystem
    
    Signed-off-by: dongjiuzhu1 <do...@xiaomi.com>
---
 boards/arm/qemu/qemu-armv7a/configs/full/defconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/boards/arm/qemu/qemu-armv7a/configs/full/defconfig b/boards/arm/qemu/qemu-armv7a/configs/full/defconfig
index 8e1d1e8c98..1b5dc69a8f 100644
--- a/boards/arm/qemu/qemu-armv7a/configs/full/defconfig
+++ b/boards/arm/qemu/qemu-armv7a/configs/full/defconfig
@@ -52,7 +52,7 @@ CONFIG_EXAMPLES_LUA_MODULE=y
 CONFIG_EXAMPLES_LVGLDEMO=y
 CONFIG_EXAMPLES_TOUCHSCREEN=y
 CONFIG_EXPERIMENTAL=y
-CONFIG_FLASH_SIZE=127926272
+CONFIG_FLASH_SIZE=50331648
 CONFIG_FLASH_START=0x00600000
 CONFIG_FLASH_VSTART=0x00600000
 CONFIG_FS_LARGEFILE=y


(nuttx) 02/04: mtd/mtd_cfi: cfi flash bind to mtd interface

Posted by ar...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 2c4285d9ac6f20aa30d928262bf8ec6d7235c7fb
Author: zhengyu9 <zh...@xiaomi.com>
AuthorDate: Mon Jul 29 16:11:26 2024 +0800

    mtd/mtd_cfi: cfi flash bind to mtd interface
    
    Bind cfi flash driver with the existing mtd interface
    
    Signed-off-by: zhengyu9 <zh...@xiaomi.com>
    Signed-off-by: dongjiuzhu1 <do...@xiaomi.com>
---
 drivers/mtd/CMakeLists.txt |   5 +
 drivers/mtd/Kconfig        |   6 +
 drivers/mtd/Make.defs      |   5 +
 drivers/mtd/mtd_cfi.c      | 329 +++++++++++++++++++++++++++++++++++++++++++++
 include/nuttx/mtd/mtd.h    |  20 +++
 5 files changed, 365 insertions(+)

diff --git a/drivers/mtd/CMakeLists.txt b/drivers/mtd/CMakeLists.txt
index 46965d40ca..c50988e663 100644
--- a/drivers/mtd/CMakeLists.txt
+++ b/drivers/mtd/CMakeLists.txt
@@ -181,5 +181,10 @@ if(CONFIG_MTD)
     target_include_directories(drivers PRIVATE ${CMAKE_CURRENT_LIST_DIR}/dhara)
   endif()
 
+  if(CONFIG_MTD_CFI)
+    list(APPEND SRCS mtd_cfi.c)
+    list(APPEND SRCS cfi.c)
+  endif()
+
   target_sources(drivers PRIVATE ${SRCS})
 endif()
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 0ec2c4d98c..38a364f58e 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -1459,4 +1459,10 @@ config DHARA_READ_NCACHES
 	default 4
 endif
 
+config MTD_CFI
+	bool "CFI(common flash interface) NOR FLASH"
+	default n
+	---help---
+		Support CFI(common flash interface) NOR FLASH.
+
 endif # MTD
diff --git a/drivers/mtd/Make.defs b/drivers/mtd/Make.defs
index 2a5a7a3153..e357e8c84c 100644
--- a/drivers/mtd/Make.defs
+++ b/drivers/mtd/Make.defs
@@ -208,6 +208,11 @@ CSRCS += mtd/dhara/dhara/journal.c
 CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)drivers$(DELIM)mtd$(DELIM)dhara
 endif
 
+ifeq ($(CONFIG_MTD_CFI),y)
+CSRCS += mtd_cfi.c
+CSRCS += cfi.c
+endif
+
 # Include MTD driver support
 
 DEPPATH += --dep-path mtd
diff --git a/drivers/mtd/mtd_cfi.c b/drivers/mtd/mtd_cfi.c
new file mode 100644
index 0000000000..96473e2f25
--- /dev/null
+++ b/drivers/mtd/mtd_cfi.c
@@ -0,0 +1,329 @@
+/****************************************************************************
+ * drivers/mtd/mtd_cfi.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 <sys/types.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <debug.h>
+
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/mtd/mtd.h>
+
+#include "cfi.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* MTD driver methods */
+
+static int     cfi_mtd_erase(FAR struct mtd_dev_s *dev, off_t startblock,
+                             size_t nblocks);
+static ssize_t cfi_mtd_bread(FAR struct mtd_dev_s *dev, off_t startblock,
+                             size_t nblocks, FAR uint8_t *buf);
+static ssize_t cfi_mtd_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
+                              size_t nblocks, FAR const uint8_t *buf);
+static ssize_t cfi_mtd_read(FAR struct mtd_dev_s *dev, off_t offset,
+                            size_t nbytes, FAR uint8_t *buffer);
+#ifdef CONFIG_MTD_BYTE_WRITE
+static ssize_t cfi_mtd_write(FAR struct mtd_dev_s *dev, off_t offset,
+                             size_t nbytes, FAR const uint8_t *buffer);
+#endif
+static int     cfi_mtd_ioctl(FAR struct mtd_dev_s *dev, int cmd,
+                             unsigned long arg);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cfi_mtd_erase
+ *
+ * Description:
+ *   Erase several blocks, each of the size previously reported.
+ *
+ ****************************************************************************/
+
+static int cfi_mtd_erase(FAR struct mtd_dev_s *dev, off_t startblock,
+                         size_t nblocks)
+{
+  FAR struct cfi_dev_s *priv = (FAR struct cfi_dev_s *)dev;
+  size_t endblock;
+
+  /* The interface definition assumes that all erase blocks are the
+   * same size. If that is not true for this particular device, then
+   * transform the start block and nblocks as necessary.
+   */
+
+  endblock = cfi_find_block(priv, (startblock + nblocks) *
+                            cfi_get_blocksize(priv, 0));
+  startblock = cfi_find_block(priv, startblock *
+                              cfi_get_blocksize(priv, 0));
+
+  /* Erase the specified blocks and return status (OK or a negated errno) */
+
+  return cfi_erase(priv, startblock, endblock - startblock);
+}
+
+/****************************************************************************
+ * Name: cfi_mtd_bread
+ *
+ * Description:
+ *   Read the specified number of blocks into the user provided buffer.
+ *
+ ****************************************************************************/
+
+static ssize_t cfi_mtd_bread(FAR struct mtd_dev_s *dev, off_t startpage,
+                             size_t npages, FAR uint8_t *buf)
+{
+  FAR struct cfi_dev_s *priv = (FAR struct cfi_dev_s *)dev;
+  off_t offset = startpage * priv->page_size;
+  int ret;
+
+  ret = cfi_read(priv, offset, npages * priv->page_size, buf);
+  return ret < 0 ? ret : npages;
+}
+
+/****************************************************************************
+ * Name: cfi_mtd_bwrite
+ *
+ * Description:
+ *   Write the specified number of blocks from the user provided buffer.
+ *
+ ****************************************************************************/
+
+static ssize_t cfi_mtd_bwrite(FAR struct mtd_dev_s *dev, off_t startpage,
+                              size_t npages, FAR const uint8_t *buf)
+{
+  FAR struct cfi_dev_s *priv = (FAR struct cfi_dev_s *)dev;
+  off_t offset = startpage * priv->page_size;
+  int ret;
+
+  ret = cfi_write(priv, offset, npages * priv->page_size, buf);
+  return ret < 0 ? ret : npages;
+}
+
+/****************************************************************************
+ * Name: cfi_mtd_read
+ *
+ * Description:
+ *   Read the specified number of bytes to the user provided buffer.
+ *
+ ****************************************************************************/
+
+static ssize_t cfi_mtd_read(FAR struct mtd_dev_s *dev, off_t offset,
+                            size_t nbytes, FAR uint8_t *buffer)
+{
+  FAR struct cfi_dev_s *priv = (FAR struct cfi_dev_s *)dev;
+  int ret;
+
+  ret = cfi_read(priv, offset, nbytes, buffer);
+  return ret < 0 ? ret : nbytes;
+}
+
+/****************************************************************************
+ * Name: cfi_mtd_write
+ *
+ * Description:
+ *   Some FLASH parts have the ability to write an arbitrary number of
+ *   bytes to an arbitrary offset on the device.  This method should be
+ *   implement only for devices that support such access.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_BYTE_WRITE
+static ssize_t cfi_mtd_write(FAR struct mtd_dev_s *dev, off_t offset,
+                             size_t nbytes, FAR const uint8_t *buffer)
+{
+  FAR struct cfi_dev_s *priv = (FAR struct cfi_dev_s *)dev;
+  int ret;
+
+  ret = cfi_write(priv, offset, nbytes, buffer);
+  return ret < 0 ? ret : nbytes;
+}
+#endif
+
+/****************************************************************************
+ * Name: cfi_mtd_ioctl
+ ****************************************************************************/
+
+static int cfi_mtd_ioctl(FAR struct mtd_dev_s *dev, int cmd,
+                         unsigned long arg)
+{
+  FAR struct cfi_dev_s *priv = (FAR struct cfi_dev_s *)dev;
+  int ret = OK;
+
+  switch (cmd)
+    {
+      case MTDIOC_GEOMETRY:
+        {
+          FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)arg;
+
+          DEBUGASSERT(geo != NULL);
+          memset(geo, 0, sizeof(*geo));
+          geo->blocksize    = priv->page_size;
+          geo->erasesize    = cfi_get_blocksize(priv, 0);
+          geo->neraseblocks = cfi_get_total_blocknum(priv);
+        }
+        break;
+
+      case BIOC_PARTINFO:
+        {
+          FAR struct partition_info_s *info =
+            (FAR struct partition_info_s *)arg;
+
+          DEBUGASSERT(info != NULL);
+          info->numsectors  = cfi_get_total_blocknum(priv);
+          info->sectorsize  = cfi_get_blocksize(priv, 0);
+          info->startsector = 0;
+          info->parent[0]   = '\0';
+        }
+        break;
+
+      case BIOC_XIPBASE:
+        {
+          FAR void **ppv = (FAR void**)arg;
+
+          DEBUGASSERT(ppv != NULL);
+          *ppv = (FAR void *)priv->base_addr;
+        }
+        break;
+
+      case MTDIOC_BULKERASE:
+        {
+          ret = cfi_mtd_erase(dev, 0, cfi_get_total_blocknum(priv));
+        }
+        break;
+
+      case MTDIOC_ERASESECTORS:
+        {
+          FAR struct mtd_erase_s *erase = (FAR struct mtd_erase_s *)arg;
+          ret = cfi_mtd_erase(dev, erase->startblock, erase->nblocks);
+        }
+        break;
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: cfi_initialize
+ *
+ * Description:
+ *   Create and initialize an MTD device instance.  MTD devices are not
+ *   registered in the file system, but are created as instances that can
+ *   be bound to other functions (such as a block or character driver front
+ *   end).
+ *
+ ****************************************************************************/
+
+static FAR struct mtd_dev_s *
+cfi_initialize(uintptr_t addr_base, uintptr_t addr_end, uint32_t bankwidth)
+{
+  FAR struct cfi_dev_s *cfi;
+
+  /* Create an instance of the CFI MTD device state structure  */
+
+  cfi = kmm_zalloc(sizeof(struct cfi_dev_s));
+  if (cfi == NULL)
+    {
+      ferr("ERROR: Failed to allocate the CFI MTD state structure\n");
+      return NULL;
+    }
+
+  cfi->mtd.erase = cfi_mtd_erase;
+  cfi->mtd.bread = cfi_mtd_bread;
+  cfi->mtd.bwrite = cfi_mtd_bwrite;
+  cfi->mtd.read = cfi_mtd_read;
+#ifdef CONFIG_MTD_BYTE_WRITE
+  cfi->mtd.write  = cfi_mtd_write;
+#endif
+  cfi->mtd.ioctl = cfi_mtd_ioctl;
+  cfi->mtd.name = "cfi-flash";
+
+  cfi->base_addr = addr_base;
+  cfi->end_addr = addr_end;
+  cfi->bankwidth = bankwidth;
+
+  /* Check the CFI, and set other parameters */
+
+  if (cfi_check(cfi) == OK)
+    {
+      return (FAR struct mtd_dev_s *)cfi;
+    }
+
+  ferr("ERROR: Not a CFI device!\n");
+  kmm_free(cfi);
+  return NULL;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: register_cfi_driver
+ *
+ * Description:
+ *   Initialize cfi MTD device instance and register it to vfs.
+ *
+ ****************************************************************************/
+
+int register_cfi_driver(uintptr_t addr_base, uintptr_t addr_end,
+                        uint32_t bankwidth, int id)
+{
+  int ret = -ENXIO;
+  FAR struct mtd_dev_s *mtd;
+
+  mtd = cfi_initialize(addr_base, addr_end, bankwidth);
+  if (mtd != NULL)
+    {
+      char devname[32];
+
+      snprintf(devname, sizeof(devname), "/dev/cfi-flash%d", id);
+      ret = register_mtddriver(devname, mtd, 0777, NULL);
+      if (ret < 0)
+        {
+          kmm_free(mtd);
+        }
+    }
+
+  return ret;
+}
diff --git a/include/nuttx/mtd/mtd.h b/include/nuttx/mtd/mtd.h
index 960560a6f8..af9ccd785a 100644
--- a/include/nuttx/mtd/mtd.h
+++ b/include/nuttx/mtd/mtd.h
@@ -805,6 +805,26 @@ int dhara_initialize_by_path(FAR const char *path,
                              FAR struct mtd_dev_s *mtd);
 #endif
 
+/****************************************************************************
+ * Name: register_cfi_driver
+ *
+ * Description:
+ *   Initialize and register a cfi nor flash.
+ *
+ * Input Parameters:
+ *   addr_base - The base(start) address of the device.
+ *   addr_end  - The end address of the device.
+ *   bankwidth - The bankwidth(port width) of the device.
+ *   id        - The device id used for register name.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_CFI
+int register_cfi_driver(volatile uintptr_t addr_base,
+                        volatile uintptr_t addr_end, uint32_t bankwidth,
+                        int id);
+#endif
+
 #undef EXTERN
 #ifdef __cplusplus
 }