You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2022/09/05 13:39:01 UTC

[incubator-nuttx] branch master updated: Added SMART flash filesystem to RP2040

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 9ad75fd95d Added SMART flash filesystem to RP2040
9ad75fd95d is described below

commit 9ad75fd95dfbe91a0f4a65dd8c0a532c04fee16b
Author: curuvar <58...@users.noreply.github.com>
AuthorDate: Sun Sep 4 19:02:24 2022 -0400

    Added SMART flash filesystem to RP2040
---
 arch/arm/src/rp2040/Kconfig                        |  26 +
 arch/arm/src/rp2040/Make.defs                      |  14 +-
 arch/arm/src/rp2040/rp2040_flash_initialize.S      | 195 +++++++
 arch/arm/src/rp2040/rp2040_flash_mtd.c             | 569 +++++++++++++++++++++
 arch/arm/src/rp2040/rp2040_flash_mtd.h             |  35 ++
 arch/arm/src/rp2040/rp2040_rom.h                   |  63 +++
 .../arm/rp2040/adafruit-feather-rp2040/README.txt  |   3 +
 .../configs/nsh-flash/defconfig                    |  54 ++
 .../scripts/adafruit-feather-rp2040-flash.ld       |   6 +
 .../scripts/adafruit-feather-rp2040-sram.ld        |   7 +
 boards/arm/rp2040/adafruit-kb2040/README.txt       |   3 +
 .../adafruit-kb2040/configs/nsh-flash/defconfig    |  54 ++
 .../scripts/adafruit-kb2040-flash.ld               |   6 +
 .../scripts/adafruit-kb2040-sram.ld                |   7 +
 boards/arm/rp2040/adafruit-qt-py-rp2040/README.txt |   3 +
 .../configs/nsh-flash/defconfig                    |  57 +++
 .../scripts/adafruit-qt-py-rp2040-flash.ld         |   6 +
 .../scripts/adafruit-qt-py-rp2040-sram.ld          |   7 +
 boards/arm/rp2040/common/Kconfig                   |  15 +-
 .../arm/rp2040/common/src/rp2040_common_bringup.c  |  85 +++
 boards/arm/rp2040/pimoroni-tiny2040/README.txt     |   3 +
 .../pimoroni-tiny2040/configs/nsh-flash/defconfig  |  53 ++
 .../scripts/pimoroni-tiny2040-flash.ld             |   6 +
 .../scripts/pimoroni-tiny2040-sram.ld              |   7 +
 boards/arm/rp2040/raspberrypi-pico-w/README.txt    |   3 +
 .../raspberrypi-pico-w/configs/nsh-flash/defconfig |  50 ++
 .../scripts/raspberrypi-pico-flash.ld              |   6 +
 .../scripts/raspberrypi-pico-sram.ld               |   7 +
 boards/arm/rp2040/raspberrypi-pico/README.txt      |   3 +
 .../raspberrypi-pico/configs/nsh-flash/defconfig   |  50 ++
 .../scripts/raspberrypi-pico-flash.ld              |   6 +
 .../scripts/raspberrypi-pico-sram.ld               |   7 +
 drivers/mtd/smart.c                                |   6 +-
 fs/smartfs/smartfs.h                               |  14 +-
 fs/smartfs/smartfs_smart.c                         |  62 ++-
 fs/smartfs/smartfs_utils.c                         |  36 +-
 tools/rp2040/make_flash_fs.c                       | 480 +++++++++++++++++
 37 files changed, 1980 insertions(+), 34 deletions(-)

diff --git a/arch/arm/src/rp2040/Kconfig b/arch/arm/src/rp2040/Kconfig
index 8e19f13aa0..0b2ecde902 100644
--- a/arch/arm/src/rp2040/Kconfig
+++ b/arch/arm/src/rp2040/Kconfig
@@ -689,3 +689,29 @@ config RP2040_BOARD_HAS_WS2812
 	---help---
 		See the Board Selection menu to configure the pins used
 		by ws2812.
+
+#####################################################################
+#  Flash File System Configuration
+#####################################################################
+
+config RP2040_FLASH_FILE_SYSTEM
+	bool "Configure a read/write filesystem on unused flash memory"
+	default n
+	select MTD
+	select MTD_SMART
+	select FS_SMARTFS
+	---help---
+		See the Board Selection menu to configure the size of the
+		flash chip for a particular board.
+
+if RP2040_FLASH_FILE_SYSTEM
+
+	config RP2040_FLASH_MOUNT_POINT
+		string "mount point for flash file system"
+		default "/flash"
+		---help---
+			This is the mount point where the flash file system will
+			be mounted.  Leave this string empty to prevent automatic
+			mounting of the filesystem.
+
+endif # RP2040_FLASH_FILE_SYSTEM
diff --git a/arch/arm/src/rp2040/Make.defs b/arch/arm/src/rp2040/Make.defs
index e950476ed3..d2dce8f239 100644
--- a/arch/arm/src/rp2040/Make.defs
+++ b/arch/arm/src/rp2040/Make.defs
@@ -78,13 +78,17 @@ ifeq ($(CONFIG_ADC),y)
 CHIP_CSRCS += rp2040_adc.c
 endif
 
-ifeq ($(CONFIG_RP2040_FLASH_BOOT),y)
-ifneq ($(PICO_SDK_PATH),)
-include chip/boot2/Make.defs
-endif
-
 ifeq ($(CONFIG_IEEE80211_INFINEON_CYW43439),y)
 CHIP_CSRCS += rp2040_cyw43439.c
 endif
 
+ifeq ($(CONFIG_RP2040_FLASH_FILE_SYSTEM),y)
+CHIP_CSRCS += rp2040_flash_mtd.c
+CHIP_ASRCS += rp2040_flash_initialize.S
+endif
+
+ifeq ($(CONFIG_RP2040_FLASH_BOOT),y)
+ifneq ($(PICO_SDK_PATH),)
+include chip/boot2/Make.defs
+endif
 endif
diff --git a/arch/arm/src/rp2040/rp2040_flash_initialize.S b/arch/arm/src/rp2040/rp2040_flash_initialize.S
new file mode 100644
index 0000000000..77477623f6
--- /dev/null
+++ b/arch/arm/src/rp2040/rp2040_flash_initialize.S
@@ -0,0 +1,195 @@
+/****************************************************************************
+ * arch/arm/src/rp2040/rp2040_flash_initialize.S
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Description:
+ *   The low-level format of the smart filesystem for the rp2040 consists of
+ *   a series of 4096-byte blocks each of which contain four 1024 byte
+ *   logical sectors.  Due to the way flash works the filesystem can only
+ *   erase (set to all 1 bits) a whole block.  It can however, write smaller
+ *   amounts.  On the rp2040 the smallest write is 256 bytes.  The write can
+ *   only flip 1 bits to 0 -- it cannot change a 0 bit to 1.
+ *
+ *   Each logical sector starts with a five byte sector header.  Most sectors
+ *   follow this with a five byte filesystem header. Sector zero does not
+ *   follow this rule; it contains the volume label.
+ *
+ *   The volume will have root directory located in logical sector three. It
+ *   may have additional root directories (indicated by a count in the volume
+ *   label). This will be in sequential sectors starting with sector four.
+ *
+ *   Actual files and other subdirectories start in logical sector twelve.
+ *
+ *   == SECTOR HEADER ==
+ *
+ *       Logical sector number:  2 bytes
+ *       Sequence number:        2 bytes
+ *       Flags:                  1 byte
+ *
+ *     Flag bits are:  C R X S S S V V
+ *                C - Set to zero if block committed  0
+ *                R - Set to zero if block erased     1
+ *                X - CRC status                      1
+ *                S - Size Code                       010
+ *                V - Version code                    01
+ *
+ *   The logical sector number does not have to match the actual position
+ *   of a sector.  The volume in scanned when mounted and the mapping of
+ *   sector number to position in volume is maintained in ram.  If a sector
+ *   is written to it will be re-written to a new location.
+ *
+ *   == VOLUME LABEL ==
+ *
+ *   Sector zero is the volume label.  It currently consists of the
+ *   following fields.
+ *
+ *       Smart Volume ID:        4 bytes    "SMRT"
+ *       Smart version:          1 byte      0x01
+ *       Sector size flag:       1 byte      0x10
+ *       Extra Root Directories: 1 byte      0x00
+ *
+ *   == FILE SYSTEM HEADER ==
+ *
+ *   File and directory sectors have a filesystem header directly following
+ *   the sector header.  This filesystem header consists of:
+ *
+ *       Entry type:             1 byte     0x01 = Directory, 0x02 = File.
+ *       Next logical sector:    2 bytes    0xff no more sectors
+ *       Number of bytes used    2 bytes    0xff empty sector
+ *
+ *   == DIRECTORY ENTRIES ==
+ *
+ *       Directory Flags:        2 bytes
+ *
+ *   == FILE ENTRIES ==
+ *
+ *       File data starts following the filesystem header and extends
+ *       for a number of bytes as specified in the filesystem header.
+ *
+ *       Files with a length of greater that 1019 bytes will span multiple
+ *       sectors, linked with the next logical sector flag.
+ ****************************************************************************/
+
+dir=          1
+file=         2
+name_length= 16
+
+/****************************************************************************
+ * Name: sector
+ *
+ * Description:
+ *    This macro defines a sector header.  It actually sets both the actual
+ *    sector header and the filesystem header values.
+ *
+ * Parameters:
+ *    num  - The logical sector number.  Each sector must be unique.
+ *    type - The sector type.  Should be 'dir' or 'file'
+ *    used - The length of the data in this sector.
+ *    next - The logical sector number of the next sector in a chain.
+ ****************************************************************************/
+
+    .macro      sector      num, type, used=0xffff, next=0xffff
+    .balign     1024, 0xff
+    .hword      \num, 0
+    .byte       0b01101001, \type
+    .hword      \next, \used
+    .endm
+
+/****************************************************************************
+ * Name: dir_entry
+ *
+ * Description:
+ *    This macro defines a directory entry.
+ *
+ * Parameters:
+ *    perm - Permission bits for this directory entry
+ *    addr - Logical sector number of named entry
+ *    time - entry creation time stamp
+ *    name - name of this entry
+ ****************************************************************************/
+
+    .macro      dir_entry   perm, addr, time, name
+    .hword      \perm | 0x7e00, \addr
+    .word       \time
+0:
+    .ascii      "\name"
+.=      0b + name_length
+    .endm
+
+/****************************************************************************
+ * Name: file_entry
+ *
+ * Description:
+ *    This macro defines a directory entry.
+ *
+ * Parameters:
+ *    perm - Permission bits for this directory entry
+ *    addr - Logical sector number of named entry
+ *    time - entry creation time stamp
+ *    name - name of this entry
+ ****************************************************************************/
+
+    .macro      file_entry   perm, addr, time, name
+    .hword      \perm | 0x5e00, \addr
+    .word       \time
+0:
+    .ascii      "\name"
+.=      0b + name_length
+    .endm
+
+/****************************************************************************
+ * Global name of the initial filesystem data
+ ****************************************************************************/
+
+ 	.cpu 		cortex-m0plus
+ 	.thumb
+
+    .section    .flash.init, "ax"
+    .balign     4096
+    .global     rp2040_smart_flash_start
+rp2040_smart_flash_start:
+
+/****************************************************************************
+ * Volume Label
+ ****************************************************************************/
+
+    .ascii      "2040"       /* magic tag for flash initialization */
+    .byte       0b01101001
+    .ascii      "SMRT"
+    .byte       0x01, 0x10, 0
+
+    .balign     4096, 0xff
+
+/****************************************************************************
+ * Root directory and initial files in the filesystem
+ ****************************************************************************/
+
+    sector      3, dir
+    file_entry  0777, 4, 0, "test"
+
+    sector      4, file, used=14
+    .ascii      "Hello, world!\n"
+
+
+    .balign     4096, 0xff
+    .global     rp2040_smart_flash_end
+rp2040_smart_flash_end:
+
+    .end
diff --git a/arch/arm/src/rp2040/rp2040_flash_mtd.c b/arch/arm/src/rp2040/rp2040_flash_mtd.c
new file mode 100644
index 0000000000..3dc6ea1b98
--- /dev/null
+++ b/arch/arm/src/rp2040/rp2040_flash_mtd.c
@@ -0,0 +1,569 @@
+/****************************************************************************
+ * arch/arm/src/rp2040/rp2040_flash_mtd.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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * This code implements a Sector Mapped Allocation for Really Tiny (SMART)
+ * filesystem in the RP2040 flash memory chip.  It uses the space not
+ * otherwise used by the NuttX binary and supports both read and write
+ * access.
+ *
+ * There initial contents of this filesystem may be configured when a NuttX
+ * binary is built (using tools/rp2040/make_flash_fs.c), but any changes
+ * subsequently written to the filesystem will persist over re-boots of the
+ * RP2040.
+ *
+ * Note: Although read access to any data stored in this filesystem is very
+ *       rapid; because of how the RP2040's flash access routines work with
+ *       the normal execute-in-place (XIP) access to that chip, no code can
+ *       access flash in the normal manner when this filesystem is erasing
+ *       or writing blocks.   This means that interrupts must be disabled
+ *       during any such access.  The main issue with this is that any
+ *       application which requires precise timing or rapid response may
+ *       be negatively impacted by such operations.
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/semaphore.h>
+
+#include "rp2040_flash_mtd.h"
+#include "rp2040_rom.h"
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define XIP_BASE                 0x10000000
+#define XIP_NOCACHE_NOALLOC_BASE 0x13000000
+#define FLASH_BLOCK_ERASE_CMD    0x20
+#define BOOT_2_SIZE              256
+
+#define FLASH_START_OFFSET (rp2040_smart_flash_start - (uint8_t *) XIP_BASE)
+#define FLASH_END_OFFSET   (rp2040_smart_flash_end   - (uint8_t *) XIP_BASE)
+#define FLASH_START_READ   (rp2040_smart_flash_start + 0x03000000)
+
+/* Note: There is some ambiguity in terminology when it comes to flash.
+ *       Some call the chunk that can be erased a sector where others
+ *       call that a block.
+ *
+ *       Some call the chunk that can be written a sector where others
+ *       call that a page.
+ */
+
+/* Blocks are the smallest unit that can be erased */
+
+#define FLASH_BLOCK_SIZE   (4 * 1024)
+#define FLASH_BLOCK_COUNT  (CONFIG_RP2040_FLASH_LENGTH - FLASH_START_OFFSET)\
+                           / FLASH_BLOCK_SIZE
+
+/* Sectors are the smallest unit that can be written */
+
+#define FLASH_SECTOR_SIZE  256
+#define FLASH_SECTOR_COUNT (CONFIG_RP2040_FLASH_LENGTH - FLASH_START_OFFSET)\
+                           / FLASH_SECTOR_SIZE
+
+#ifdef CONFIG_SMP
+#  define OTHER_CPU (up_cpu_index() == 0 ? 1 : 0)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+typedef struct rp2040_flash_dev_s
+{
+  struct mtd_dev_s mtd_dev;                 /* Embedded mdt_dev structure */
+  sem_t            sem;                     /* file access serialization  */
+  uint32_t         boot_2[BOOT_2_SIZE / 4]; /* RAM copy of boot_2         */
+} rp2040_flash_dev_t;
+
+typedef void (*connect_internal_flash_f)(void);
+typedef void (*flash_exit_xip_f)(void);
+typedef void (*flash_range_erase_f)(uint32_t, size_t, uint32_t, uint8_t);
+typedef void (*flash_range_program_f)(uint32_t, const uint8_t *, size_t);
+typedef void (*flash_flush_cache_f)(void);
+typedef void (*flash_enable_xip_f)(void);
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int     rp2040_flash_erase      (struct mtd_dev_s *dev,
+                                        off_t             startblock,
+                                        size_t            nblocks);
+
+static ssize_t rp2040_flash_block_read (struct mtd_dev_s *dev,
+                                        off_t             startblock,
+                                        size_t            nblocks,
+                                        uint8_t          *buffer);
+
+static ssize_t rp2040_flash_block_write(struct mtd_dev_s *dev,
+                                        off_t             startblock,
+                                        size_t            nblocks,
+                                        const uint8_t    *buffer);
+
+static ssize_t rp2040_flash_byte_read  (struct mtd_dev_s *dev,
+                                        off_t             offset,
+                                        size_t            nbytes,
+                                        uint8_t          *buffer);
+
+static int     rp2040_flash_ioctl      (struct mtd_dev_s *dev,
+                                        int              cmd,
+                                        unsigned long    arg);
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+extern const uint8_t rp2040_smart_flash_start[256];
+extern const uint8_t rp2040_smart_flash_end[0];
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct rp2040_flash_dev_s my_dev =
+{
+  .mtd_dev =
+    {
+      rp2040_flash_erase,
+      rp2040_flash_block_read,
+      rp2040_flash_block_write,
+      rp2040_flash_byte_read,
+#ifdef CONFIG_MTD_BYTE_WRITE
+      NULL,
+#endif
+      rp2040_flash_ioctl,
+      "rp_flash"
+    }
+};
+
+static bool initialized = false;
+
+static struct
+{
+  connect_internal_flash_f connect_internal_flash;
+  flash_exit_xip_f         flash_exit_xip;
+  flash_range_erase_f      flash_range_erase;
+  flash_range_program_f    flash_range_program;
+  flash_flush_cache_f      flash_flush_cache;
+  flash_enable_xip_f       flash_enable_xip;
+} rom_functions;
+
+void *boot_2_copy = NULL;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: do_erase
+ ****************************************************************************/
+
+void RAM_CODE(do_erase)(uint32_t addr, size_t count)
+{
+  /* Note: While we would prefer not to flush the cache, the
+   * flash_flush_cache call is needed to remove  CSn IO force.
+   */
+
+  __asm__ volatile ("" : : : "memory");
+
+  rom_functions.connect_internal_flash();
+
+  rom_functions.flash_exit_xip();
+
+  /* The range erase will try to erase 65536-byte blocks with the 0xd8 flash
+   * command. If it cannot, because either the addr or count are not
+   * multiple of 65536, it will fall back to erasing 4096-byte blocks as
+   * needed.
+   */
+
+  rom_functions.flash_range_erase(addr, count, 65536, 0xd8);
+
+  rom_functions.flash_flush_cache();
+
+  rom_functions.flash_enable_xip();
+}
+
+/****************************************************************************
+ * Name: do_write
+ ****************************************************************************/
+
+void RAM_CODE(do_write)(uint32_t addr, const uint8_t *data, size_t count)
+{
+  /* Note: While we would prefer not to flush the cache, the
+   * flash_flush_cache call is needed to remove  CSn IO force.
+   */
+
+  __asm__ volatile ("" : : : "memory");
+
+  rom_functions.connect_internal_flash();
+
+  rom_functions.flash_exit_xip();
+
+  rom_functions.flash_range_program(addr, (uint8_t *)data, count);
+
+  rom_functions.flash_flush_cache();
+
+  rom_functions.flash_enable_xip();
+}
+
+/****************************************************************************
+ * Name: rp2040_flash_erase
+ ****************************************************************************/
+
+static int     rp2040_flash_erase(struct mtd_dev_s *dev,
+                                  off_t             startblock,
+                                  size_t            nblocks)
+{
+  rp2040_flash_dev_t *rp_dev = (rp2040_flash_dev_t *) dev;
+  irqstate_t          flags;
+  int                 ret    = OK;
+
+  finfo("FLASH: erase block:  %8u (0x%08x) count:%5u (0x%08X)\n",
+         (unsigned)(startblock),
+         (unsigned)(FLASH_BLOCK_SIZE * startblock + FLASH_START_OFFSET),
+         nblocks,
+         FLASH_BLOCK_SIZE * nblocks);
+
+  usleep(500000);
+
+  ret = nxsem_wait(&(rp_dev->sem));
+
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  flags = enter_critical_section();
+
+#ifdef CONFIG_SMP
+  up_cpu_pause(OTHER_CPU);
+#endif
+
+  do_erase(FLASH_BLOCK_SIZE * startblock + FLASH_START_OFFSET,
+            FLASH_BLOCK_SIZE * nblocks);
+
+#ifdef CONFIG_SMP
+  up_cpu_resume(OTHER_CPU);
+#endif
+
+  leave_critical_section(flags);
+
+  ret = nblocks;
+
+  nxsem_post(&(rp_dev->sem));
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: rp2040_flash_block_read
+ ****************************************************************************/
+
+static ssize_t rp2040_flash_block_read(struct mtd_dev_s *dev,
+                                       off_t             startblock,
+                                       size_t            nblocks,
+                                       uint8_t          *buffer)
+{
+  rp2040_flash_dev_t *rp_dev = (rp2040_flash_dev_t *) dev;
+  int                 start;
+  int                 length;
+  int                 ret   = OK;
+
+  ret = nxsem_wait(&(rp_dev->sem));
+
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  finfo("FLASH: read sector:  %8u (0x%08x) count:%5u\n",
+         (unsigned)(startblock),
+         (unsigned)(FLASH_SECTOR_SIZE * startblock + FLASH_START_OFFSET),
+         nblocks);
+
+  start  = FLASH_SECTOR_SIZE * startblock;
+  length = FLASH_SECTOR_SIZE * nblocks;
+
+  /* This reads starting at XIP_NOCACHE_NOALLOC_BASE to bypass the
+   * XIP cache.  This is done because flash programming does not update
+   * the cache and we don't want to read stale data.  Since we expect
+   * access to the flash filesystem to be rather infrequent this isn't
+   * really much of a burden.
+   */
+
+  memcpy(buffer, FLASH_START_READ + start, length);
+
+  /* Update the file position */
+
+  nxsem_post(&(rp_dev->sem));
+
+  return nblocks;
+}
+
+/****************************************************************************
+ * Name: rp2040_flash_write
+ ****************************************************************************/
+
+static ssize_t rp2040_flash_block_write(struct mtd_dev_s *dev,
+                                        off_t             startblock,
+                                        size_t            nblocks,
+                                        const uint8_t    *buffer)
+{
+  rp2040_flash_dev_t *rp_dev = (rp2040_flash_dev_t *) dev;
+  irqstate_t          flags;
+  int                 ret   = OK;
+
+  ret = nxsem_wait(&(rp_dev->sem));
+
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  flags = enter_critical_section();
+
+#ifdef CONFIG_SMP
+  up_cpu_pause(OTHER_CPU);
+#endif
+
+  do_write(FLASH_SECTOR_SIZE * startblock + FLASH_START_OFFSET,
+           buffer,
+           FLASH_SECTOR_SIZE * nblocks);
+
+#ifdef CONFIG_SMP
+  up_cpu_resume(OTHER_CPU);
+#endif
+
+  leave_critical_section(flags);
+
+  finfo("FLASH: write sector: %8u (0x%08x) count:%5u\n",
+         (unsigned)(startblock),
+         (unsigned)(FLASH_SECTOR_SIZE * startblock + FLASH_START_OFFSET),
+         nblocks);
+
+#ifdef CONFIG_DEBUG_FS_INFO
+  for (int i = 0; i < FLASH_SECTOR_SIZE * nblocks; i += 16)
+    {
+      for (int j = 0; j < 16; ++j)
+        {
+          printf("%02x, ", buffer[i + j]);
+        }
+
+        printf("\n");
+    }
+#endif
+
+  ret = nblocks;
+
+  nxsem_post(&(rp_dev->sem));
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: rp2040_flash_byte_read
+ ****************************************************************************/
+
+static ssize_t rp2040_flash_byte_read  (struct mtd_dev_s *dev,
+                                        off_t             offset,
+                                        size_t            nbytes,
+                                        uint8_t          *buffer)
+{
+  rp2040_flash_dev_t *rp_dev = (rp2040_flash_dev_t *) dev;
+  int                 length;
+  int                 ret   = OK;
+
+  ret = nxsem_wait(&(rp_dev->sem));
+
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  length = nbytes;
+
+  finfo("FLASH: read bytes:   %8u (0x%08x) count:%5u\n",
+         (unsigned)(offset),
+         (unsigned)(offset + FLASH_START_OFFSET),
+         nbytes);
+
+  /* This reads starting at XIP_NOCACHE_NOALLOC_BASE to bypass the
+   * XIP cache.  This is done because flash programming does not update
+   * the cache and we don't want to read stale data.  Since we expect
+   * access to the flash filesystem to be rather infrequent this isn't
+   * really much of a burden.
+   */
+
+  memcpy(buffer, FLASH_START_READ + offset, length);
+
+#ifdef CONFIG_DEBUG_FS_INFO
+  for (int j = 0; j < 16 && j < nbytes; ++j)
+    {
+      printf("%02x, ", buffer[j]);
+    }
+
+    printf("\n");
+#endif
+
+  /* Update the file position */
+
+  nxsem_post(&(rp_dev->sem));
+
+  return length;
+}
+
+/****************************************************************************
+ * Name: rp2040_flash_ioctl
+ ****************************************************************************/
+
+static int rp2040_flash_ioctl(struct mtd_dev_s *dev,
+                              int               cmd,
+                              unsigned long     arg)
+{
+  rp2040_flash_dev_t *rp_dev = (rp2040_flash_dev_t *) dev;
+  int                 ret    = OK;
+
+  UNUSED(rp_dev);
+
+  switch (cmd)
+    {
+      case MTDIOC_GEOMETRY:
+        {
+          struct mtd_geometry_s *geo = (struct mtd_geometry_s *) arg;
+
+          if (geo != NULL)
+            {
+              geo->blocksize    = FLASH_SECTOR_SIZE;
+              geo->erasesize    = FLASH_BLOCK_SIZE;
+              geo->neraseblocks = FLASH_BLOCK_COUNT;
+            }
+
+          break;
+        }
+
+      case MTDIOC_BULKERASE:
+        {
+          /* Erase all the filesystem blocks for the device.  Remember that
+           * we share this device with XIP memory so we cannot erase entire
+           * device.
+           */
+
+          ret = rp2040_flash_erase(dev, 0, FLASH_BLOCK_COUNT);
+
+          break;
+        }
+
+      default:
+        ret = -ENOTTY;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rp2040_flash_initialize
+ *
+ * Description: Bind a block mode driver that uses the built-in rp2040
+ * flash programming commands for read/write access to unused flash.
+ ****************************************************************************/
+
+struct mtd_dev_s *rp2040_flash_mtd_initialize(void)
+{
+  if (initialized)
+    {
+      errno = EBUSY;
+      return NULL;
+    }
+
+  initialized = true;
+
+  nxsem_init(&(my_dev.sem), 0, 1);
+
+  if (FLASH_BLOCK_COUNT < 4)
+    {
+      errno = ENOMEM;
+      return NULL;
+    }
+
+  rom_functions.connect_internal_flash = ROM_LOOKUP(ROM_FLASH_CONNECT);
+  rom_functions.flash_exit_xip         = ROM_LOOKUP(ROM_FLASH_EXIT_XIP);
+  rom_functions.flash_range_erase      = ROM_LOOKUP(ROM_FLASH_ERASE);
+  rom_functions.flash_range_program    = ROM_LOOKUP(ROM_FLASH_PROGRAM);
+  rom_functions.flash_flush_cache      = ROM_LOOKUP(ROM_FLASH_FLUSH_CACHE);
+
+  /* Instead of using the rom_function for flash_enable_xip, we use the one
+   * from boot stage 2 loaded at the beginning of the XIP rom. We do this
+   * because the boot_rom version can result in slower access to the the
+   * XIP memory.
+   *
+   * We need to make our own copy of this code in ram since we cannot use
+   * the rom until after this call completes.
+   */
+
+  memcpy(my_dev.boot_2, (void *) XIP_BASE, BOOT_2_SIZE);
+  rom_functions.flash_enable_xip = (flash_enable_xip_f)my_dev.boot_2 + 1;
+
+  /* Do we need to initialize the flash? */
+
+  if (memcmp(rp2040_smart_flash_start, "2040", 4) == 0)
+    {
+      uint8_t    buffer[FLASH_SECTOR_SIZE];
+      irqstate_t flags = enter_critical_section();
+
+      /* OK, we found the "magic" tag... */
+
+      /* Erase all flash beyond what was loaded from NuttX binary. */
+
+      do_erase(FLASH_END_OFFSET,
+                CONFIG_RP2040_FLASH_LENGTH - FLASH_END_OFFSET);
+
+      /* Erase the "magic" flag, setting the first two bytes to zero. */
+
+      memcpy(buffer, rp2040_smart_flash_start, FLASH_SECTOR_SIZE);
+
+      buffer[0] = 0;
+      buffer[1] = 0;
+
+      do_write(FLASH_START_OFFSET, buffer, FLASH_SECTOR_SIZE);
+
+      leave_critical_section(flags);
+    }
+
+  return &(my_dev.mtd_dev);
+}
diff --git a/arch/arm/src/rp2040/rp2040_flash_mtd.h b/arch/arm/src/rp2040/rp2040_flash_mtd.h
new file mode 100644
index 0000000000..b83b451014
--- /dev/null
+++ b/arch/arm/src/rp2040/rp2040_flash_mtd.h
@@ -0,0 +1,35 @@
+/****************************************************************************
+ * arch/arm/src/rp2040/rp2040_flash_mtd.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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/mtd/mtd.h>
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rp2040_flash_mtd_initialize
+ ****************************************************************************/
+
+struct mtd_dev_s *rp2040_flash_mtd_initialize(void);
diff --git a/arch/arm/src/rp2040/rp2040_rom.h b/arch/arm/src/rp2040/rp2040_rom.h
new file mode 100644
index 0000000000..b12f85d400
--- /dev/null
+++ b/arch/arm/src/rp2040/rp2040_rom.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+ * arch/arm/src/rp2040/rp2040_rom.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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ROM_HWORD_AS_PTR(a) ((void *)(uintptr_t) (*(uint16_t *)(uintptr_t)a))
+
+#define ROM_CODE(a,b) ((b << 8) | a)
+
+#define ROM_POPCOUNT32             ROM_CODE('P', '3')
+#define ROM_REVERSE32              ROM_CODE('R', '3')
+#define ROM_CLZ32                  ROM_CODE('L', '3')
+#define ROM_CTZ32                  ROM_CODE('T', '3')
+#define ROM_MEMSET                 ROM_CODE('M', 'S')
+#define ROM_MEMSET4                ROM_CODE('S', '4')
+#define ROM_MEMCPY                 ROM_CODE('M', 'C')
+#define ROM_MEMCPY44               ROM_CODE('C', '4')
+#define ROM_RESET_USB_BOOT         ROM_CODE('U', 'B')
+#define ROM_FLASH_CONNECT          ROM_CODE('I', 'F')
+#define ROM_FLASH_EXIT_XIP         ROM_CODE('E', 'X')
+#define ROM_FLASH_ERASE            ROM_CODE('R', 'E')
+#define ROM_FLASH_PROGRAM          ROM_CODE('R', 'P')
+#define ROM_FLASH_FLUSH_CACHE      ROM_CODE('F', 'C')
+#define ROM_FLASH_ENABLE_XIP       ROM_CODE('C', 'X')
+
+#define ROM_LOOKUP(x) \
+          ((rom_table_lookup_fn)ROM_HWORD_AS_PTR(0x18)) \
+          (ROM_HWORD_AS_PTR(0x14),x)
+
+#define STR(s)           #s
+#define RAM_CODE_ATTR(f) __attribute__((noinline, section(".ram_code." f)))
+#define RAM_CODE(f)      RAM_CODE_ATTR(STR(f)) f
+
+/****************************************************************************
+ * Public Type Definitions
+ ****************************************************************************/
+
+typedef void *(*rom_table_lookup_fn)(uint16_t *table, uint32_t code);
diff --git a/boards/arm/rp2040/adafruit-feather-rp2040/README.txt b/boards/arm/rp2040/adafruit-feather-rp2040/README.txt
index c289a627f2..80f2a72af0 100644
--- a/boards/arm/rp2040/adafruit-feather-rp2040/README.txt
+++ b/boards/arm/rp2040/adafruit-feather-rp2040/README.txt
@@ -71,6 +71,9 @@ Defconfigs
 - nsh
     Minimum configuration with NuttShell
 
+- nsh_flash
+    NuttX shell with SMART flash filesystem.
+
 - nshsram
     Load NuttX binary to SRAM
   
diff --git a/boards/arm/rp2040/adafruit-feather-rp2040/configs/nsh-flash/defconfig b/boards/arm/rp2040/adafruit-feather-rp2040/configs/nsh-flash/defconfig
new file mode 100644
index 0000000000..4ca63ed1df
--- /dev/null
+++ b/boards/arm/rp2040/adafruit-feather-rp2040/configs/nsh-flash/defconfig
@@ -0,0 +1,54 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed .config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that includes your
+# modifications.
+#
+# CONFIG_FS_PROCFS_EXCLUDE_ENVIRON is not set
+# CONFIG_LIBC_LONG_LONG is not set
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+# CONFIG_NSH_DISABLE_DATE is not set
+# CONFIG_STANDARD_SERIAL is not set
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="adafruit-feather-rp2040"
+CONFIG_ARCH_BOARD_ADAFRUIT_FEATHER_RP2040=y
+CONFIG_ARCH_CHIP="rp2040"
+CONFIG_ARCH_CHIP_RP2040=y
+CONFIG_ARCH_RAMVECTORS=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_BOARDCTL_RESET=y
+CONFIG_BOARD_LOOPSPERMSEC=10450
+CONFIG_BUILTIN=y
+CONFIG_DEBUG_FULLOPT=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_DISABLE_POSIX_TIMERS=y
+CONFIG_EXAMPLES_HELLO=y
+CONFIG_FS_PROCFS=y
+CONFIG_FS_PROCFS_REGISTER=y
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_READLINE=y
+CONFIG_RAM_SIZE=270336
+CONFIG_RAM_START=0x20000000
+CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RP2040_BOARD_HAS_WS2812=y
+CONFIG_RP2040_FLASH_FILE_SYSTEM=y
+CONFIG_RP2040_FLASH_LENGTH=8388608
+CONFIG_RP2040_WS2812_GPIO_PIN=16
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_WAITPID=y
+CONFIG_SMARTFS_ALIGNED_ACCESS=y
+CONFIG_START_DAY=9
+CONFIG_START_MONTH=2
+CONFIG_START_YEAR=2021
+CONFIG_SYSLOG_CONSOLE=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_TESTING_GETPRIME=y
+CONFIG_TESTING_OSTEST=y
+CONFIG_TESTING_SMART_TEST=y
+CONFIG_UART0_SERIAL_CONSOLE=y
+CONFIG_WS2812=y
diff --git a/boards/arm/rp2040/adafruit-feather-rp2040/scripts/adafruit-feather-rp2040-flash.ld b/boards/arm/rp2040/adafruit-feather-rp2040/scripts/adafruit-feather-rp2040-flash.ld
index 28c31bacc2..e7adceaf6b 100644
--- a/boards/arm/rp2040/adafruit-feather-rp2040/scripts/adafruit-feather-rp2040-flash.ld
+++ b/boards/arm/rp2040/adafruit-feather-rp2040/scripts/adafruit-feather-rp2040-flash.ld
@@ -82,11 +82,17 @@ SECTIONS
         _sdata = ABSOLUTE(.);
         *(.data .data.*)
         *(.gnu.linkonce.d.*)
+        *(.ram_code.*)
         CONSTRUCTORS
         . = ALIGN(4);
         _edata = ABSOLUTE(.);
     } > sram AT > flash
 
+    .flash_section : {
+        . = ALIGN(4*1024);
+        *(.flash.*)
+    } > flash
+
     .bss : {
         _sbss = ABSOLUTE(.);
         *(.bss .bss.*)
diff --git a/boards/arm/rp2040/adafruit-feather-rp2040/scripts/adafruit-feather-rp2040-sram.ld b/boards/arm/rp2040/adafruit-feather-rp2040/scripts/adafruit-feather-rp2040-sram.ld
index ba1b19945c..f607b392cc 100644
--- a/boards/arm/rp2040/adafruit-feather-rp2040/scripts/adafruit-feather-rp2040-sram.ld
+++ b/boards/arm/rp2040/adafruit-feather-rp2040/scripts/adafruit-feather-rp2040-sram.ld
@@ -20,6 +20,7 @@
 
 MEMORY
 {
+  flash (rx) : ORIGIN = 0x10000000, LENGTH = 8192K
   sram (rwx) : ORIGIN = 0x20000000, LENGTH = 264K
 }
 
@@ -67,11 +68,17 @@ SECTIONS
         _sdata = ABSOLUTE(.);
         *(.data .data.*)
         *(.gnu.linkonce.d.*)
+        *(.ram_code.*)
         CONSTRUCTORS
         . = ALIGN(4);
         _edata = ABSOLUTE(.);
     } > sram
 
+    .flash_section : {
+        . = ALIGN(4*1024);
+        *(.flash.*)
+    } > flash
+
     .bss : {
         _sbss = ABSOLUTE(.);
         *(.bss .bss.*)
diff --git a/boards/arm/rp2040/adafruit-kb2040/README.txt b/boards/arm/rp2040/adafruit-kb2040/README.txt
index 7451e2cfd3..4c725d2166 100644
--- a/boards/arm/rp2040/adafruit-kb2040/README.txt
+++ b/boards/arm/rp2040/adafruit-kb2040/README.txt
@@ -70,6 +70,9 @@ Defconfigs
 - nsh
     Minimum configuration with NuttShell
 
+- nsh_flash
+    NuttX shell with SMART flash filesystem.
+
 - nshsram
     Load NuttX binary to SRAM
   
diff --git a/boards/arm/rp2040/adafruit-kb2040/configs/nsh-flash/defconfig b/boards/arm/rp2040/adafruit-kb2040/configs/nsh-flash/defconfig
new file mode 100644
index 0000000000..322969f593
--- /dev/null
+++ b/boards/arm/rp2040/adafruit-kb2040/configs/nsh-flash/defconfig
@@ -0,0 +1,54 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed .config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that includes your
+# modifications.
+#
+# CONFIG_FS_PROCFS_EXCLUDE_ENVIRON is not set
+# CONFIG_LIBC_LONG_LONG is not set
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+# CONFIG_NSH_DISABLE_DATE is not set
+# CONFIG_STANDARD_SERIAL is not set
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="adafruit-kb2040"
+CONFIG_ARCH_BOARD_ADAFRUIT_KB2040=y
+CONFIG_ARCH_CHIP="rp2040"
+CONFIG_ARCH_CHIP_RP2040=y
+CONFIG_ARCH_RAMVECTORS=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_BOARDCTL_RESET=y
+CONFIG_BOARD_LOOPSPERMSEC=10450
+CONFIG_BUILTIN=y
+CONFIG_DEBUG_FULLOPT=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_DISABLE_POSIX_TIMERS=y
+CONFIG_EXAMPLES_HELLO=y
+CONFIG_FS_PROCFS=y
+CONFIG_FS_PROCFS_REGISTER=y
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_READLINE=y
+CONFIG_RAM_SIZE=270336
+CONFIG_RAM_START=0x20000000
+CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RP2040_BOARD_HAS_WS2812=y
+CONFIG_RP2040_FLASH_FILE_SYSTEM=y
+CONFIG_RP2040_FLASH_LENGTH=8388608
+CONFIG_RP2040_WS2812_GPIO_PIN=17
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_WAITPID=y
+CONFIG_SMARTFS_ALIGNED_ACCESS=y
+CONFIG_START_DAY=9
+CONFIG_START_MONTH=2
+CONFIG_START_YEAR=2021
+CONFIG_SYSLOG_CONSOLE=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_TESTING_GETPRIME=y
+CONFIG_TESTING_OSTEST=y
+CONFIG_TESTING_SMART_TEST=y
+CONFIG_UART0_SERIAL_CONSOLE=y
+CONFIG_WS2812=y
diff --git a/boards/arm/rp2040/adafruit-kb2040/scripts/adafruit-kb2040-flash.ld b/boards/arm/rp2040/adafruit-kb2040/scripts/adafruit-kb2040-flash.ld
index f30276ace0..1a8103198b 100644
--- a/boards/arm/rp2040/adafruit-kb2040/scripts/adafruit-kb2040-flash.ld
+++ b/boards/arm/rp2040/adafruit-kb2040/scripts/adafruit-kb2040-flash.ld
@@ -82,11 +82,17 @@ SECTIONS
         _sdata = ABSOLUTE(.);
         *(.data .data.*)
         *(.gnu.linkonce.d.*)
+        *(.ram_code.*)
         CONSTRUCTORS
         . = ALIGN(4);
         _edata = ABSOLUTE(.);
     } > sram AT > flash
 
+    .flash_section : {
+        . = ALIGN(4*1024);
+        *(.flash.*)
+    } > flash
+
     .bss : {
         _sbss = ABSOLUTE(.);
         *(.bss .bss.*)
diff --git a/boards/arm/rp2040/adafruit-kb2040/scripts/adafruit-kb2040-sram.ld b/boards/arm/rp2040/adafruit-kb2040/scripts/adafruit-kb2040-sram.ld
index b350bfb5f1..a1d8631350 100644
--- a/boards/arm/rp2040/adafruit-kb2040/scripts/adafruit-kb2040-sram.ld
+++ b/boards/arm/rp2040/adafruit-kb2040/scripts/adafruit-kb2040-sram.ld
@@ -20,6 +20,7 @@
 
 MEMORY
 {
+  flash (rx) : ORIGIN = 0x10000000, LENGTH = 8192K
   sram (rwx) : ORIGIN = 0x20000000, LENGTH = 264K
 }
 
@@ -67,11 +68,17 @@ SECTIONS
         _sdata = ABSOLUTE(.);
         *(.data .data.*)
         *(.gnu.linkonce.d.*)
+        *(.ram_code.*)
         CONSTRUCTORS
         . = ALIGN(4);
         _edata = ABSOLUTE(.);
     } > sram
 
+    .flash_section : {
+        . = ALIGN(4*1024);
+        *(.flash.*)
+    } > flash
+
     .bss : {
         _sbss = ABSOLUTE(.);
         *(.bss .bss.*)
diff --git a/boards/arm/rp2040/adafruit-qt-py-rp2040/README.txt b/boards/arm/rp2040/adafruit-qt-py-rp2040/README.txt
index d6add66f0d..039eb52964 100644
--- a/boards/arm/rp2040/adafruit-qt-py-rp2040/README.txt
+++ b/boards/arm/rp2040/adafruit-qt-py-rp2040/README.txt
@@ -65,6 +65,9 @@ Defconfigs
 - nsh
     Minimum configuration with NuttShell
 
+- nsh_flash
+    NuttX shell with SMART flash filesystem.
+
 - nshsram
     Load NuttX binary to SRAM
   
diff --git a/boards/arm/rp2040/adafruit-qt-py-rp2040/configs/nsh-flash/defconfig b/boards/arm/rp2040/adafruit-qt-py-rp2040/configs/nsh-flash/defconfig
new file mode 100644
index 0000000000..b4523bbc37
--- /dev/null
+++ b/boards/arm/rp2040/adafruit-qt-py-rp2040/configs/nsh-flash/defconfig
@@ -0,0 +1,57 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed .config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that includes your
+# modifications.
+#
+# CONFIG_FS_PROCFS_EXCLUDE_ENVIRON is not set
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+# CONFIG_NSH_DISABLE_DATE is not set
+# CONFIG_RP2040_UART0 is not set
+# CONFIG_STANDARD_SERIAL is not set
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="adafruit-qt-py-rp2040"
+CONFIG_ARCH_BOARD_ADAFRUIT_QT_PY_RP2040=y
+CONFIG_ARCH_CHIP="rp2040"
+CONFIG_ARCH_CHIP_RP2040=y
+CONFIG_ARCH_RAMVECTORS=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_BOARDCTL_RESET=y
+CONFIG_BOARD_LOOPSPERMSEC=10450
+CONFIG_BUILTIN=y
+CONFIG_DEBUG_FULLOPT=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_DISABLE_POSIX_TIMERS=y
+CONFIG_EXAMPLES_HELLO=y
+CONFIG_FS_PROCFS=y
+CONFIG_FS_PROCFS_REGISTER=y
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_READLINE=y
+CONFIG_RAM_SIZE=270336
+CONFIG_RAM_START=0x20000000
+CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RP2040_BOARD_HAS_WS2812=y
+CONFIG_RP2040_FLASH_FILE_SYSTEM=y
+CONFIG_RP2040_FLASH_LENGTH=8388608
+CONFIG_RP2040_UART1=y
+CONFIG_RP2040_UART1_RX_GPIO=5
+CONFIG_RP2040_WS2812_GPIO_PIN=12
+CONFIG_RP2040_WS2812_PWR_GPIO=11
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_WAITPID=y
+CONFIG_SMARTFS_ALIGNED_ACCESS=y
+CONFIG_START_DAY=9
+CONFIG_START_MONTH=2
+CONFIG_START_YEAR=2021
+CONFIG_SYSLOG_CONSOLE=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_TESTING_GETPRIME=y
+CONFIG_TESTING_OSTEST=y
+CONFIG_TESTING_SMART_TEST=y
+CONFIG_UART1_SERIAL_CONSOLE=y
+CONFIG_WS2812=y
diff --git a/boards/arm/rp2040/adafruit-qt-py-rp2040/scripts/adafruit-qt-py-rp2040-flash.ld b/boards/arm/rp2040/adafruit-qt-py-rp2040/scripts/adafruit-qt-py-rp2040-flash.ld
index 9fc8ee22a4..5c65d96751 100644
--- a/boards/arm/rp2040/adafruit-qt-py-rp2040/scripts/adafruit-qt-py-rp2040-flash.ld
+++ b/boards/arm/rp2040/adafruit-qt-py-rp2040/scripts/adafruit-qt-py-rp2040-flash.ld
@@ -82,11 +82,17 @@ SECTIONS
         _sdata = ABSOLUTE(.);
         *(.data .data.*)
         *(.gnu.linkonce.d.*)
+        *(.ram_code.*)
         CONSTRUCTORS
         . = ALIGN(4);
         _edata = ABSOLUTE(.);
     } > sram AT > flash
 
+    .flash_section : {
+        . = ALIGN(4*1024);
+        *(.flash.*)
+    } > flash
+
     .bss : {
         _sbss = ABSOLUTE(.);
         *(.bss .bss.*)
diff --git a/boards/arm/rp2040/adafruit-qt-py-rp2040/scripts/adafruit-qt-py-rp2040-sram.ld b/boards/arm/rp2040/adafruit-qt-py-rp2040/scripts/adafruit-qt-py-rp2040-sram.ld
index 7b559bf00c..b7ffd7b3c9 100644
--- a/boards/arm/rp2040/adafruit-qt-py-rp2040/scripts/adafruit-qt-py-rp2040-sram.ld
+++ b/boards/arm/rp2040/adafruit-qt-py-rp2040/scripts/adafruit-qt-py-rp2040-sram.ld
@@ -20,6 +20,7 @@
 
 MEMORY
 {
+  flash (rx) : ORIGIN = 0x10000000, LENGTH = 8192K
   sram (rwx) : ORIGIN = 0x20000000, LENGTH = 264K
 }
 
@@ -67,11 +68,17 @@ SECTIONS
         _sdata = ABSOLUTE(.);
         *(.data .data.*)
         *(.gnu.linkonce.d.*)
+        *(.ram_code.*)
         CONSTRUCTORS
         . = ALIGN(4);
         _edata = ABSOLUTE(.);
     } > sram
 
+    .flash_section : {
+        . = ALIGN(4*1024);
+        *(.flash.*)
+    } > flash
+
     .bss : {
         _sbss = ABSOLUTE(.);
         *(.bss .bss.*)
diff --git a/boards/arm/rp2040/common/Kconfig b/boards/arm/rp2040/common/Kconfig
index 0c95ae9b8f..aa69c90c1a 100644
--- a/boards/arm/rp2040/common/Kconfig
+++ b/boards/arm/rp2040/common/Kconfig
@@ -475,7 +475,6 @@ if RP2040_I2S
 
 endif # RP2040_I2S
 
-		
 #####################################################################
 #  WS2812 Configuration
 #####################################################################
@@ -501,3 +500,17 @@ if RP2040_BOARD_HAS_WS2812
 			such a pin.
 
 endif # RP2040_BOARD_HAS_WS2812
+
+#####################################################################
+#  FLASH File System Configuration
+#####################################################################
+
+if RP2040_FLASH_FILE_SYSTEM
+
+	config RP2040_FLASH_LENGTH
+		int "Size of flash memory in bytes."
+		default 2097152
+		---help---
+			This is the overall amount of flash memory on the board.
+
+endif # RP2040_FLASH_FILE_SYSTEM
diff --git a/boards/arm/rp2040/common/src/rp2040_common_bringup.c b/boards/arm/rp2040/common/src/rp2040_common_bringup.c
index 15d40a0f78..bfdbea5256 100644
--- a/boards/arm/rp2040/common/src/rp2040_common_bringup.c
+++ b/boards/arm/rp2040/common/src/rp2040_common_bringup.c
@@ -27,6 +27,8 @@
 #include <debug.h>
 #include <errno.h>
 #include <stddef.h>
+#include <string.h>
+#include <sys/stat.h>
 
 #include <nuttx/fs/fs.h>
 
@@ -74,6 +76,14 @@
 #include "rp2040_ws2812.h"
 #endif
 
+#if defined(CONFIG_RP2040_ROMFS_ROMDISK_DEVNAME)
+#  include <rp2040_romfsimg.h>
+#endif
+
+#ifdef CONFIG_RP2040_FLASH_FILE_SYSTEM
+#  include "rp2040_flash_mtd.h"
+#endif
+
 #ifdef CONFIG_WS2812_HAS_WHITE
 #define HAS_WHITE true
 #else /* CONFIG_WS2812_HAS_WHITE */
@@ -92,6 +102,10 @@ int rp2040_common_bringup(void)
 {
   int ret = 0;
 
+#ifdef CONFIG_RP2040_FLASH_FILE_SYSTEM
+  struct mtd_dev_s *mtd_dev;
+#endif
+
 #ifdef CONFIG_RP2040_I2C_DRIVER
   #ifdef CONFIG_RP2040_I2C0
   ret = board_i2cdev_initialize(0);
@@ -570,5 +584,76 @@ int rp2040_common_bringup(void)
     }
 #endif
 
+#ifdef CONFIG_RP2040_FLASH_FILE_SYSTEM
+
+  mtd_dev = rp2040_flash_mtd_initialize();
+
+  if (mtd_dev == NULL)
+    {
+      syslog(LOG_ERR, "ERROR: flash_mtd_initialize failed: %d\n", errno);
+    }
+  else
+    {
+      ret = smart_initialize(0, mtd_dev, NULL);
+
+      if (ret < 0)
+        {
+          syslog(LOG_ERR, "ERROR: smart_initialize failed: %d\n", -ret);
+        }
+      else if (strlen(CONFIG_RP2040_FLASH_MOUNT_POINT) > 0)
+        {
+          mkdir(CONFIG_RP2040_FLASH_MOUNT_POINT, 0777);
+
+          /* Mount the file system */
+
+          ret = nx_mount("/dev/smart0",
+                        CONFIG_RP2040_FLASH_MOUNT_POINT,
+                        "smartfs",
+                        0,
+                        NULL);
+          if (ret < 0)
+            {
+              syslog(LOG_ERR,
+                    "ERROR: nx_mount(\"/dev/smart0\", \"%s\", \"smartfs\","
+                    " 0, NULL) failed: %d\n",
+                    CONFIG_RP2040_FLASH_MOUNT_POINT,
+                    ret);
+            }
+        }
+    }
+
+#endif
+
+#if defined(CONFIG_RP2040_ROMFS_ROMDISK_DEVNAME)
+  /* Register the ROM disk */
+
+  ret = romdisk_register(CONFIG_RP2040_ROMFS_ROMDISK_MINOR,
+                         rp2040_romfs_img,
+                         NSECTORS(rp2040_romfs_img_len),
+                         CONFIG_RP2040_ROMFS_ROMDISK_SECTSIZE);
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: romdisk_register failed: %d\n", -ret);
+    }
+  else
+    {
+      /* Mount the file system */
+
+      ret = nx_mount(CONFIG_RP2040_ROMFS_ROMDISK_DEVNAME,
+                     CONFIG_RP2040_ROMFS_MOUNT_MOUNTPOINT,
+                     "romfs",
+                     MS_RDONLY,
+                     NULL);
+      if (ret < 0)
+        {
+          syslog(LOG_ERR,
+                 "ERROR: nx_mount(%s,%s,romfs) failed: %d\n",
+                 CONFIG_RP2040_ROMFS_ROMDISK_DEVNAME,
+                 CONFIG_RP2040_ROMFS_MOUNT_MOUNTPOINT,
+                 ret);
+        }
+    }
+
+#endif
   return OK;
 }
diff --git a/boards/arm/rp2040/pimoroni-tiny2040/README.txt b/boards/arm/rp2040/pimoroni-tiny2040/README.txt
index 12af598c3d..4eb1ab821c 100644
--- a/boards/arm/rp2040/pimoroni-tiny2040/README.txt
+++ b/boards/arm/rp2040/pimoroni-tiny2040/README.txt
@@ -69,6 +69,9 @@ Defconfigs
 - nsh
     Minimum configuration with NuttShell
 
+- nsh_flash
+    NuttX shell with SMART flash filesystem.
+
 - nshsram
     Load NuttX binary to SRAM
   
diff --git a/boards/arm/rp2040/pimoroni-tiny2040/configs/nsh-flash/defconfig b/boards/arm/rp2040/pimoroni-tiny2040/configs/nsh-flash/defconfig
new file mode 100644
index 0000000000..03d8ae784e
--- /dev/null
+++ b/boards/arm/rp2040/pimoroni-tiny2040/configs/nsh-flash/defconfig
@@ -0,0 +1,53 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed .config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that includes your
+# modifications.
+#
+# CONFIG_FS_PROCFS_EXCLUDE_ENVIRON is not set
+# CONFIG_LIBC_LONG_LONG is not set
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+# CONFIG_NSH_DISABLE_DATE is not set
+# CONFIG_STANDARD_SERIAL is not set
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="pimoroni-tiny2040"
+CONFIG_ARCH_BOARD_PIMORONI_TINY2040=y
+CONFIG_ARCH_CHIP="rp2040"
+CONFIG_ARCH_CHIP_RP2040=y
+CONFIG_ARCH_COVERAGE=y
+CONFIG_ARCH_RAMVECTORS=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_BOARDCTL_RESET=y
+CONFIG_BOARD_LOOPSPERMSEC=10450
+CONFIG_BUILTIN=y
+CONFIG_DEBUG_FULLOPT=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_DISABLE_POSIX_TIMERS=y
+CONFIG_EXAMPLES_HELLO=y
+CONFIG_FS_PROCFS=y
+CONFIG_FS_PROCFS_REGISTER=y
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_READLINE=y
+CONFIG_RAM_SIZE=270336
+CONFIG_RAM_START=0x20000000
+CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RP2040_FLASH_FILE_SYSTEM=y
+CONFIG_RP2040_FLASH_LENGTH=8388608
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_WAITPID=y
+CONFIG_SMARTFS_ALIGNED_ACCESS=y
+CONFIG_START_DAY=11
+CONFIG_START_MONTH=12
+CONFIG_START_YEAR=2021
+CONFIG_SYSLOG_CONSOLE=y
+CONFIG_SYSTEM_GCOV=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_TESTING_GETPRIME=y
+CONFIG_TESTING_OSTEST=y
+CONFIG_TESTING_SMART_TEST=y
+CONFIG_UART0_SERIAL_CONSOLE=y
diff --git a/boards/arm/rp2040/pimoroni-tiny2040/scripts/pimoroni-tiny2040-flash.ld b/boards/arm/rp2040/pimoroni-tiny2040/scripts/pimoroni-tiny2040-flash.ld
index 278777d5de..51b985467c 100644
--- a/boards/arm/rp2040/pimoroni-tiny2040/scripts/pimoroni-tiny2040-flash.ld
+++ b/boards/arm/rp2040/pimoroni-tiny2040/scripts/pimoroni-tiny2040-flash.ld
@@ -82,11 +82,17 @@ SECTIONS
         _sdata = ABSOLUTE(.);
         *(.data .data.*)
         *(.gnu.linkonce.d.*)
+        *(.ram_code.*)
         CONSTRUCTORS
         . = ALIGN(4);
         _edata = ABSOLUTE(.);
     } > sram AT > flash
 
+    .flash_section : {
+        . = ALIGN(4*1024);
+        *(.flash.*)
+    } > flash
+
     .bss : {
         _sbss = ABSOLUTE(.);
         *(.bss .bss.*)
diff --git a/boards/arm/rp2040/pimoroni-tiny2040/scripts/pimoroni-tiny2040-sram.ld b/boards/arm/rp2040/pimoroni-tiny2040/scripts/pimoroni-tiny2040-sram.ld
index bd69cdfe23..6864dee749 100644
--- a/boards/arm/rp2040/pimoroni-tiny2040/scripts/pimoroni-tiny2040-sram.ld
+++ b/boards/arm/rp2040/pimoroni-tiny2040/scripts/pimoroni-tiny2040-sram.ld
@@ -20,6 +20,7 @@
 
 MEMORY
 {
+  flash (rx) : ORIGIN = 0x10000000, LENGTH = 8192K
   sram (rwx) : ORIGIN = 0x20000000, LENGTH = 264K
 }
 
@@ -67,11 +68,17 @@ SECTIONS
         _sdata = ABSOLUTE(.);
         *(.data .data.*)
         *(.gnu.linkonce.d.*)
+        *(.ram_code.*)
         CONSTRUCTORS
         . = ALIGN(4);
         _edata = ABSOLUTE(.);
     } > sram
 
+    .flash_section : {
+        . = ALIGN(4*1024);
+        *(.flash.*)
+    } > flash
+
     .bss : {
         _sbss = ABSOLUTE(.);
         *(.bss .bss.*)
diff --git a/boards/arm/rp2040/raspberrypi-pico-w/README.txt b/boards/arm/rp2040/raspberrypi-pico-w/README.txt
index b0a96277ee..1757187c4d 100644
--- a/boards/arm/rp2040/raspberrypi-pico-w/README.txt
+++ b/boards/arm/rp2040/raspberrypi-pico-w/README.txt
@@ -72,6 +72,9 @@ Defconfigs
 - nsh
     Minimum configuration with NuttShell
 
+- nsh_flash
+    NuttX shell with SMART flash filesystem.
+
 - nshsram
     Load NuttX binary to SRAM
   
diff --git a/boards/arm/rp2040/raspberrypi-pico-w/configs/nsh-flash/defconfig b/boards/arm/rp2040/raspberrypi-pico-w/configs/nsh-flash/defconfig
new file mode 100644
index 0000000000..5441ec96cb
--- /dev/null
+++ b/boards/arm/rp2040/raspberrypi-pico-w/configs/nsh-flash/defconfig
@@ -0,0 +1,50 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed .config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that includes your
+# modifications.
+#
+# CONFIG_FS_PROCFS_EXCLUDE_ENVIRON is not set
+# CONFIG_LIBC_LONG_LONG is not set
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+# CONFIG_NSH_DISABLE_DATE is not set
+# CONFIG_STANDARD_SERIAL is not set
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="raspberrypi-pico-w"
+CONFIG_ARCH_BOARD_RASPBERRYPI_PICO_W=y
+CONFIG_ARCH_CHIP="rp2040"
+CONFIG_ARCH_CHIP_RP2040=y
+CONFIG_ARCH_RAMVECTORS=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_BOARDCTL_RESET=y
+CONFIG_BOARD_LOOPSPERMSEC=10450
+CONFIG_BUILTIN=y
+CONFIG_DEBUG_FULLOPT=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_DISABLE_POSIX_TIMERS=y
+CONFIG_EXAMPLES_HELLO=y
+CONFIG_FS_PROCFS=y
+CONFIG_FS_PROCFS_REGISTER=y
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_READLINE=y
+CONFIG_RAM_SIZE=270336
+CONFIG_RAM_START=0x20000000
+CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RP2040_FLASH_FILE_SYSTEM=y
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_WAITPID=y
+CONFIG_SMARTFS_ALIGNED_ACCESS=y
+CONFIG_START_DAY=9
+CONFIG_START_MONTH=2
+CONFIG_START_YEAR=2021
+CONFIG_SYSLOG_CONSOLE=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_TESTING_GETPRIME=y
+CONFIG_TESTING_OSTEST=y
+CONFIG_TESTING_SMART_TEST=y
+CONFIG_UART0_SERIAL_CONSOLE=y
diff --git a/boards/arm/rp2040/raspberrypi-pico-w/scripts/raspberrypi-pico-flash.ld b/boards/arm/rp2040/raspberrypi-pico-w/scripts/raspberrypi-pico-flash.ld
index f370cca326..82de4b749a 100644
--- a/boards/arm/rp2040/raspberrypi-pico-w/scripts/raspberrypi-pico-flash.ld
+++ b/boards/arm/rp2040/raspberrypi-pico-w/scripts/raspberrypi-pico-flash.ld
@@ -82,11 +82,17 @@ SECTIONS
         _sdata = ABSOLUTE(.);
         *(.data .data.*)
         *(.gnu.linkonce.d.*)
+        *(.ram_code.*)
         CONSTRUCTORS
         . = ALIGN(4);
         _edata = ABSOLUTE(.);
     } > sram AT > flash
 
+    .flash_section : {
+        . = ALIGN(4*1024);
+        *(.flash.*)
+    } > flash
+
     .bss : {
         _sbss = ABSOLUTE(.);
         *(.bss .bss.*)
diff --git a/boards/arm/rp2040/raspberrypi-pico-w/scripts/raspberrypi-pico-sram.ld b/boards/arm/rp2040/raspberrypi-pico-w/scripts/raspberrypi-pico-sram.ld
index d9278950cf..1227117022 100644
--- a/boards/arm/rp2040/raspberrypi-pico-w/scripts/raspberrypi-pico-sram.ld
+++ b/boards/arm/rp2040/raspberrypi-pico-w/scripts/raspberrypi-pico-sram.ld
@@ -20,6 +20,7 @@
 
 MEMORY
 {
+  flash (rx) : ORIGIN = 0x10000000, LENGTH = 2048K
   sram (rwx) : ORIGIN = 0x20000000, LENGTH = 264K
 }
 
@@ -67,11 +68,17 @@ SECTIONS
         _sdata = ABSOLUTE(.);
         *(.data .data.*)
         *(.gnu.linkonce.d.*)
+        *(.ram_code.*)
         CONSTRUCTORS
         . = ALIGN(4);
         _edata = ABSOLUTE(.);
     } > sram
 
+    .flash_section : {
+        . = ALIGN(4*1024);
+        *(.flash.*)
+    } > flash
+
     .bss : {
         _sbss = ABSOLUTE(.);
         *(.bss .bss.*)
diff --git a/boards/arm/rp2040/raspberrypi-pico/README.txt b/boards/arm/rp2040/raspberrypi-pico/README.txt
index 4bd892f45c..86d60167e7 100644
--- a/boards/arm/rp2040/raspberrypi-pico/README.txt
+++ b/boards/arm/rp2040/raspberrypi-pico/README.txt
@@ -71,6 +71,9 @@ Defconfigs
 - nsh
     Minimum configuration with NuttShell
 
+- nsh_flash
+    NuttX shell with SMART flash filesystem.
+
 - nshsram
     Load NuttX binary to SRAM
   
diff --git a/boards/arm/rp2040/raspberrypi-pico/configs/nsh-flash/defconfig b/boards/arm/rp2040/raspberrypi-pico/configs/nsh-flash/defconfig
new file mode 100644
index 0000000000..c07697f60f
--- /dev/null
+++ b/boards/arm/rp2040/raspberrypi-pico/configs/nsh-flash/defconfig
@@ -0,0 +1,50 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed .config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that includes your
+# modifications.
+#
+# CONFIG_FS_PROCFS_EXCLUDE_ENVIRON is not set
+# CONFIG_LIBC_LONG_LONG is not set
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+# CONFIG_NSH_DISABLE_DATE is not set
+# CONFIG_STANDARD_SERIAL is not set
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="raspberrypi-pico"
+CONFIG_ARCH_BOARD_RASPBERRYPI_PICO=y
+CONFIG_ARCH_CHIP="rp2040"
+CONFIG_ARCH_CHIP_RP2040=y
+CONFIG_ARCH_RAMVECTORS=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_BOARDCTL_RESET=y
+CONFIG_BOARD_LOOPSPERMSEC=10450
+CONFIG_BUILTIN=y
+CONFIG_DEBUG_FULLOPT=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_DISABLE_POSIX_TIMERS=y
+CONFIG_EXAMPLES_HELLO=y
+CONFIG_FS_PROCFS=y
+CONFIG_FS_PROCFS_REGISTER=y
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_READLINE=y
+CONFIG_RAM_SIZE=270336
+CONFIG_RAM_START=0x20000000
+CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RP2040_FLASH_FILE_SYSTEM=y
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_WAITPID=y
+CONFIG_SMARTFS_ALIGNED_ACCESS=y
+CONFIG_START_DAY=9
+CONFIG_START_MONTH=2
+CONFIG_START_YEAR=2021
+CONFIG_SYSLOG_CONSOLE=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_TESTING_GETPRIME=y
+CONFIG_TESTING_OSTEST=y
+CONFIG_TESTING_SMART_TEST=y
+CONFIG_UART0_SERIAL_CONSOLE=y
diff --git a/boards/arm/rp2040/raspberrypi-pico/scripts/raspberrypi-pico-flash.ld b/boards/arm/rp2040/raspberrypi-pico/scripts/raspberrypi-pico-flash.ld
index f370cca326..82de4b749a 100644
--- a/boards/arm/rp2040/raspberrypi-pico/scripts/raspberrypi-pico-flash.ld
+++ b/boards/arm/rp2040/raspberrypi-pico/scripts/raspberrypi-pico-flash.ld
@@ -82,11 +82,17 @@ SECTIONS
         _sdata = ABSOLUTE(.);
         *(.data .data.*)
         *(.gnu.linkonce.d.*)
+        *(.ram_code.*)
         CONSTRUCTORS
         . = ALIGN(4);
         _edata = ABSOLUTE(.);
     } > sram AT > flash
 
+    .flash_section : {
+        . = ALIGN(4*1024);
+        *(.flash.*)
+    } > flash
+
     .bss : {
         _sbss = ABSOLUTE(.);
         *(.bss .bss.*)
diff --git a/boards/arm/rp2040/raspberrypi-pico/scripts/raspberrypi-pico-sram.ld b/boards/arm/rp2040/raspberrypi-pico/scripts/raspberrypi-pico-sram.ld
index d9278950cf..1227117022 100644
--- a/boards/arm/rp2040/raspberrypi-pico/scripts/raspberrypi-pico-sram.ld
+++ b/boards/arm/rp2040/raspberrypi-pico/scripts/raspberrypi-pico-sram.ld
@@ -20,6 +20,7 @@
 
 MEMORY
 {
+  flash (rx) : ORIGIN = 0x10000000, LENGTH = 2048K
   sram (rwx) : ORIGIN = 0x20000000, LENGTH = 264K
 }
 
@@ -67,11 +68,17 @@ SECTIONS
         _sdata = ABSOLUTE(.);
         *(.data .data.*)
         *(.gnu.linkonce.d.*)
+        *(.ram_code.*)
         CONSTRUCTORS
         . = ALIGN(4);
         _edata = ABSOLUTE(.);
     } > sram
 
+    .flash_section : {
+        . = ALIGN(4*1024);
+        *(.flash.*)
+    } > flash
+
     .bss : {
         _sbss = ABSOLUTE(.);
         *(.bss .bss.*)
diff --git a/drivers/mtd/smart.c b/drivers/mtd/smart.c
index 4b84e408d0..91db48d69b 100644
--- a/drivers/mtd/smart.c
+++ b/drivers/mtd/smart.c
@@ -5751,7 +5751,7 @@ static int smart_fsck_file(FAR struct smart_struct_s *dev,
 
       /* next logical sector */
 
-      logsector = *(uint16_t *)chain->nextsector;
+      logsector = SMARTFS_NEXTSECTOR(chain);
     }
   while (logsector != 0xffff);
 
@@ -5879,7 +5879,7 @@ static int smart_fsck_directory(FAR struct smart_struct_s *dev,
 
   /* Check next sector recursively */
 
-  nextsector = *(uint16_t *)chain->nextsector;
+  nextsector = SMARTFS_NEXTSECTOR(chain);
 
   if (nextsector != 0xffff)
     {
@@ -5893,7 +5893,7 @@ static int smart_fsck_directory(FAR struct smart_struct_s *dev,
 
           ferr("Invalidate next log sector %d\n", nextsector);
 
-          *(uint16_t *)chain->nextsector = 0xffff;
+          SMARTFS_SET_NEXTSECTOR(chain, 0xffff);
 
           /* Set flag to relocate later */
 
diff --git a/fs/smartfs/smartfs.h b/fs/smartfs/smartfs.h
index 017049fd96..d00d836bf6 100644
--- a/fs/smartfs/smartfs.h
+++ b/fs/smartfs/smartfs.h
@@ -197,8 +197,18 @@
 #  define offsetof(type, member) ((size_t) & (((type *)0)->member))
 #endif
 
-#define SMARTFS_NEXTSECTOR(h)    (*((uint16_t *)h->nextsector))
-#define SMARTFS_USED(h)          (*((uint16_t *)h->used))
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+#  define SMARTFS_NEXTSECTOR(h)        (smartfs_rdle16(h->nextsector))
+#  define SMARTFS_SET_NEXTSECTOR(h, v) smartfs_wrle16(h->nextsector, v)
+#  define SMARTFS_USED(h)              (smartfs_rdle16(h->used))
+#  define SMARTFS_SET_USED(h, v)       smartfs_wrle16(h->used, v)
+
+#else
+#  define SMARTFS_NEXTSECTOR(h)        (*((uint16_t *)h->nextsector))
+#  define SMARTFS_SET_NEXTSECTOR(h, v) ((*((uint16_t *)h->nextsector)) = v)
+#  define SMARTFS_USED(h)              (*((uint16_t *)h->used))
+#  define SMARTFS_SET_USED(h, v)       ((*((uint16_t *)h->used)) = v)
+#endif
 
 #ifdef CONFIG_MTD_SMART_ENABLE_CRC
 #define CONFIG_SMARTFS_USE_SECTOR_BUFFER
diff --git a/fs/smartfs/smartfs_smart.c b/fs/smartfs/smartfs_smart.c
index 11b146bef5..9cebba5c99 100644
--- a/fs/smartfs/smartfs_smart.c
+++ b/fs/smartfs/smartfs_smart.c
@@ -45,6 +45,10 @@
 
 #include "smartfs.h"
 
+#ifdef CONFIG_DEBUG_FS_INFO
+#  include <stdio.h>
+#endif
+
 /****************************************************************************
  * Private Type
  ****************************************************************************/
@@ -528,6 +532,8 @@ static ssize_t smartfs_read(FAR struct file *filep, char *buffer,
 
   DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
 
+  finfo("Reading %u bytes\n", (unsigned) buflen);
+
   /* Recover our private data from the struct file instance */
 
   sf    = filep->f_priv;
@@ -578,7 +584,7 @@ static ssize_t smartfs_read(FAR struct file *filep, char *buffer,
 
       /* Get number of used bytes in this sector */
 
-      bytesinsector = *((uint16_t *) header->used);
+      bytesinsector = SMARTFS_USED(header);
       if (bytesinsector == SMARTFS_ERASEDSTATE_16BIT)
         {
           /* No bytes to read from this sector */
@@ -633,6 +639,18 @@ static ssize_t smartfs_read(FAR struct file *filep, char *buffer,
 
   /* Return the number of bytes we read */
 
+#ifdef CONFIG_DEBUG_FS_INFO
+  finfo("Read %lu bytes:", bytesread);
+  for (uint32_t i = 0; i < bytesread; ++i)
+    {
+      if ((i & 0x0f) == 0) printf("\n    ");
+
+      printf("%02x, ", buffer[i]);
+    }
+
+  printf("\n");
+#endif
+
   ret = bytesread;
 
 errout_with_semaphore:
@@ -657,6 +675,18 @@ static ssize_t smartfs_write(FAR struct file *filep, const char *buffer,
 
   DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
 
+#ifdef CONFIG_DEBUG_FS_INFO
+  finfo("Writing %lu bytes:", (uint32_t)buflen);
+  for (uint32_t i = 0; i < buflen; ++i)
+    {
+      if ((i & 0x0f) == 0) printf("\n    ");
+
+      printf("%02x, ", buffer[i]);
+    }
+
+  printf("\n");
+#endif
+
   /* Recover our private data from the struct file instance */
 
   sf    = filep->f_priv;
@@ -852,7 +882,7 @@ static ssize_t smartfs_write(FAR struct file *filep, const char *buffer,
           /* Copy the new sector to the old one and chain it */
 
           header = (struct smartfs_chain_header_s *) sf->buffer;
-          *((uint16_t *) header->nextsector) = (uint16_t) ret;
+          SMARTFS_SET_NEXTSECTOR(header, (uint16_t) ret);
 
           /* Now sync the file to write this sector out */
 
@@ -908,7 +938,7 @@ static ssize_t smartfs_write(FAR struct file *filep, const char *buffer,
               /* Copy the new sector to the old one and chain it */
 
               header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
-              *((uint16_t *) header->nextsector) = (uint16_t) ret;
+              SMARTFS_SET_NEXTSECTOR(header, (uint16_t) ret);
               readwrite.offset = offsetof(struct smartfs_chain_header_s,
                 nextsector);
               readwrite.buffer = (uint8_t *) header->nextsector;
@@ -941,6 +971,8 @@ static ssize_t smartfs_write(FAR struct file *filep, const char *buffer,
 
   ret = byteswritten;
 
+  finfo("Wrote %u bytes\n", (unsigned) byteswritten);
+
 errout_with_semaphore:
   smartfs_semgive(fs);
   return ret;
@@ -1325,6 +1357,7 @@ static int smartfs_readdir(FAR struct inode *mountpt,
 
   entrysize = sizeof(struct smartfs_entry_header_s) +
     fs->fs_llformat.namesize;
+
   while (sdir->fs_currsector != SMARTFS_ERASEDSTATE_16BIT)
     {
       /* Read the logical sector */
@@ -1341,7 +1374,11 @@ static int smartfs_readdir(FAR struct inode *mountpt,
 
       /* Now search for entries, starting at curroffset */
 
-      while (sdir->fs_curroffset < ret)
+      /* Note: directories don't use the header's used field
+       *       so we search all possilble directory entries.
+       */
+
+      while (sdir->fs_curroffset + entrysize < ret)
         {
           /* Point to next entry */
 
@@ -1350,24 +1387,35 @@ static int smartfs_readdir(FAR struct inode *mountpt,
 
           /* Test if this entry is valid and active */
 
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+          if (((smartfs_rdle16(&entry->flags)
+                & SMARTFS_DIRENT_EMPTY) ==
+              (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) ||
+              ((smartfs_rdle16(&entry->flags)
+                & SMARTFS_DIRENT_ACTIVE) !=
+              (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
+#else
           if (((entry->flags & SMARTFS_DIRENT_EMPTY) ==
               (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) ||
               ((entry->flags & SMARTFS_DIRENT_ACTIVE) !=
               (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
+#endif
             {
               /* This entry isn't valid, skip it */
 
               sdir->fs_curroffset += entrysize;
-              entry = (struct smartfs_entry_header_s *)
-                &fs->fs_rwbuffer[sdir->fs_curroffset];
-
               continue;
             }
 
           /* Entry found!  Report it */
 
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+          if ((smartfs_rdle16(&entry->flags) & SMARTFS_DIRENT_TYPE) ==
+              SMARTFS_DIRENT_TYPE_DIR)
+#else
           if ((entry->flags & SMARTFS_DIRENT_TYPE) ==
               SMARTFS_DIRENT_TYPE_DIR)
+#endif
             {
               dentry->d_type = DTYPE_DIRECTORY;
             }
diff --git a/fs/smartfs/smartfs_utils.c b/fs/smartfs/smartfs_utils.c
index 22d9cc1595..ef2d019c3a 100644
--- a/fs/smartfs/smartfs_utils.c
+++ b/fs/smartfs/smartfs_utils.c
@@ -714,11 +714,11 @@ int smartfs_finddirentry(struct smartfs_mountpt_s *fs,
                                    * to next sector
                                    */
 
-                                  if (*((FAR uint16_t *)header->used) !=
+                                  if (SMARTFS_USED(header) !=
                                       SMARTFS_ERASEDSTATE_16BIT)
                                     {
                                       direntry->datlen +=
-                                        *((uint16_t *)header->used);
+                                        SMARTFS_USED(header);
                                     }
 
                                   dirsector = SMARTFS_NEXTSECTOR(header);
@@ -1293,7 +1293,7 @@ int smartfs_deleteentry(struct smartfs_mountpt_s *fs,
                 {
                   /* We found ourselves in the chain.  Update the chain. */
 
-                  SMARTFS_NEXTSECTOR(header) = nextsector;
+                  SMARTFS_SET_NEXTSECTOR(header, nextsector);
                   readwrite.offset = offsetof(struct smartfs_chain_header_s,
                                               nextsector);
                   readwrite.count  = sizeof(uint16_t);
@@ -1449,13 +1449,15 @@ int smartfs_sync_internal(FAR struct smartfs_mountpt_s *fs,
       /* Update the header with the number of bytes written */
 
       header = (struct smartfs_chain_header_s *)sf->buffer;
-      if (*((uint16_t *)header->used) == SMARTFS_ERASEDSTATE_16BIT)
+
+      if (SMARTFS_USED(header) == SMARTFS_ERASEDSTATE_16BIT)
         {
-          *((uint16_t *)header->used) = sf->byteswritten;
+          SMARTFS_SET_USED(header, sf->byteswritten);
         }
       else
         {
-          *((uint16_t *)header->used) += sf->byteswritten;
+          SMARTFS_SET_USED(header, SMARTFS_USED(header)
+                                   + sf->byteswritten);
         }
 
       /* Write the entire sector to FLASH */
@@ -1504,13 +1506,15 @@ int smartfs_sync_internal(FAR struct smartfs_mountpt_s *fs,
       /* Add new byteswritten to existing value */
 
       header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
-      if (*((uint16_t *) header->used) == SMARTFS_ERASEDSTATE_16BIT)
+
+      if (SMARTFS_USED(header) == SMARTFS_ERASEDSTATE_16BIT)
         {
-          *((uint16_t *) header->used) = sf->byteswritten;
+          SMARTFS_SET_USED(header, sf->byteswritten);
         }
       else
         {
-          *((uint16_t *) header->used) += sf->byteswritten;
+          SMARTFS_SET_USED(header, SMARTFS_USED(header)
+                                   + sf->byteswritten);
         }
 
       readwrite.offset = offsetof(struct smartfs_chain_header_s, used);
@@ -1813,9 +1817,8 @@ int smartfs_shrinkfile(FAR struct smartfs_mountpt_s *fs,
               dest       = (FAR uint8_t *)&fs->fs_rwbuffer[offset];
               destsize   = fs->fs_llformat.availbytes - offset;
 
-              *((uint16_t *)header->used)       = remaining;
-              *((uint16_t *)header->nextsector) = SMARTFS_ERASEDSTATE_16BIT;
-
+              SMARTFS_SET_USED(header, remaining);
+              SMARTFS_SET_NEXTSECTOR(header, SMARTFS_ERASEDSTATE_16BIT);
               remaining  = 0;
             }
 
@@ -1879,8 +1882,9 @@ int smartfs_shrinkfile(FAR struct smartfs_mountpt_s *fs,
           destsize   = fs->fs_llformat.availbytes - offset;
 
           header     = (struct smartfs_chain_header_s *)sf->buffer;
-          *((uint16_t *)header->used)       = length;
-          *((uint16_t *)header->nextsector) = SMARTFS_ERASEDSTATE_16BIT;
+
+          SMARTFS_SET_USED(header, length);
+          SMARTFS_SET_NEXTSECTOR(header, SMARTFS_ERASEDSTATE_16BIT);
         }
 
       memset(dest, CONFIG_SMARTFS_ERASEDSTATE, destsize);
@@ -2027,7 +2031,7 @@ int smartfs_extendfile(FAR struct smartfs_mountpt_s *fs,
           /* Copy the new sector to the old one and chain it */
 
           header = (struct smartfs_chain_header_s *) sf->buffer;
-          *((uint16_t *)header->nextsector) = (uint16_t)ret;
+          SMARTFS_SET_NEXTSECTOR(header, (uint16_t)ret);
 
           /* Now sync the file to write this sector out */
 
@@ -2083,7 +2087,7 @@ int smartfs_extendfile(FAR struct smartfs_mountpt_s *fs,
               /* Copy the new sector to the old one and chain it */
 
               header = (struct smartfs_chain_header_s *)fs->fs_rwbuffer;
-              *((FAR uint16_t *)header->nextsector) = (uint16_t)ret;
+              SMARTFS_SET_NEXTSECTOR(header, (uint16_t)ret);
 
               readwrite.offset = offsetof(struct smartfs_chain_header_s,
                                           nextsector);
diff --git a/tools/rp2040/make_flash_fs.c b/tools/rp2040/make_flash_fs.c
new file mode 100644
index 0000000000..3607f21397
--- /dev/null
+++ b/tools/rp2040/make_flash_fs.c
@@ -0,0 +1,480 @@
+/****************************************************************************
+ * tools/rp2040/make_flash_fs.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 <stdio.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <sys/dir.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+
+#define MAX_NAME_LEN     16
+#define MAX_SECTOR_DATA  (1024 - 10)
+#define MAX_DIR_COUNT    (MAX_SECTOR_DATA / 24)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+typedef struct dir_item_s
+{
+  struct dir_item_s *prior;
+  bool               is_directory;
+  int                init_sector;
+  int                permissions;
+  char               name[0];
+} dir_item_t;
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+int         max_name_len   = MAX_NAME_LEN;
+int         root_sector    = 3;
+char        path[4096];
+uint8_t    *buffer;
+
+const char *preamble =
+  "\n     .macro      sector        num, type, used=0xffff, next=0xffff"
+  "\n     .balign     1024, 0xff"
+  "\n     .hword      \\num, 0"
+  "\n     .byte       0b01101001, \\type"
+  "\n 1:"
+  "\n     .hword      \\next, \\used"
+  "\n     .endm"
+  "\n"
+  "\n     .macro      dir_entry     perm, addr, time, name"
+  "\n     .hword      \\perm | 0x7e00, \\addr"
+  "\n     .word       \\time"
+  "\n 0:"
+  "\n     .ascii      \"\\name\""
+  "\n .=      0b + name_length"
+  "\n     .endm"
+  "\n"
+  "\n     .macro      file_entry     perm, addr, time, name"
+  "\n     .hword      \\perm | 0x5e00, \\addr"
+  "\n     .word       \\time"
+  "\n 0:"
+  "\n     .ascii      \"\\name\""
+  "\n .=      0b + name_length"
+  "\n     .endm"
+  "\n"
+  "\n     .cpu        cortex-m0plus"
+  "\n     .thumb"
+  "\n"
+  "\n     .section    .flash.init, \"ax\""
+  "\n     .balign     4096"
+  "\n     .global     rp2040_smart_flash_start"
+  "\n rp2040_smart_flash_start:"
+  "\n     .ascii      \"2040\""
+  "\n     .byte       0b01101001"
+  "\n     .ascii      \"SMRT\""
+  "\n     .byte       0x01, 0x10, 0"
+  "\n"
+  "\n     .balign     4096, 0xff";
+
+const char *postamble =
+  "\n"
+  "\n     .balign     4096, 0xff"
+  "\n     .global     rp2040_smart_flash_end"
+  "\n rp2040_smart_flash_end:"
+  "\n"
+  "\n      .end";
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+void put_name(const char * cp)
+{
+  putchar('"');
+
+  for (; *cp != 0; ++cp)
+    {
+      switch (*cp)
+        {
+          case '\"': printf("\\\""); break;
+          case '\'': printf("\\\'"); break;
+          case '\\': printf("\\\\"); break;
+          case '\a': printf("\\a");  break;
+          case '\b': printf("\\b");  break;
+          case '\n': printf("\\n");  break;
+          case '\t': printf("\\t");  break;
+          default:
+            if (iscntrl(*cp))
+              {
+                printf("\\%03o", *cp);
+              }
+            else
+              {
+                putchar(*cp);
+              }
+        }
+    }
+
+  putchar('"');
+}
+
+/****************************************************************************
+ * Name: copy_file
+ ****************************************************************************/
+
+int copy_file(int in_sector, const char * in_name)
+{
+  FILE         *data_file = fopen(path, "r");
+  struct stat   file_stat;
+  int           sector_len;
+  uint8_t      *bp;
+
+  if (data_file == NULL) return in_sector;
+
+  lstat(path, &file_stat);
+
+  while (file_stat.st_size > 0)
+    {
+      bp         = buffer;
+      sector_len = fread(buffer, 1, MAX_SECTOR_DATA, data_file);
+
+      file_stat.st_size -= sector_len;
+
+      if (file_stat.st_size > 0)
+        {
+          printf("\n     sector   %4d, dir, used=%d, next=%d",
+                 in_sector,
+                 sector_len,
+                 in_sector + 1);
+        }
+      else
+        {
+          printf("\n     sector   %4d, file, used=%d",
+                in_sector,
+                sector_len);
+        }
+
+      in_sector += 1;
+
+      for (; sector_len > 0; sector_len -= 8)
+        {
+          printf("\n     .byte      ");
+
+          for (int i = 0; i < 8  &&  i < sector_len; ++i)
+            {
+              if (i != 0) printf(", ");
+
+              printf("0x%02x", *bp++);
+            }
+        }
+
+      printf("\n");
+    }
+
+  return in_sector;
+}
+
+/****************************************************************************
+ * Name: dir_entry
+ *
+ * On entry:
+ *  The global "path" will be path to the prototype directory.
+ ****************************************************************************/
+
+int scan_dir(int in_sector)
+{
+  dir_item_t    *an_item    = NULL;
+  dir_item_t    *prior_item = NULL;
+  int            path_len   = strlen(path);
+  int            item_count = 0;
+  int            sector     = in_sector + 1;
+  DIR           *input_dir;
+  struct dirent *a_dirent;
+  struct stat    stat;
+  int            name_len;
+
+  if (name_len > max_name_len)
+    {
+      fprintf(stderr, "directory name to big. skipped. (%s)\n", path);
+      return -1;
+    }
+
+  input_dir = opendir(path);
+
+  if (input_dir == NULL)
+    {
+      fprintf(stderr,
+              "could not open directory %s  %s\n",
+              path,
+              strerror(errno));
+
+      return -1;
+    }
+
+  /* scan directory to create directory entries */
+
+  while ((a_dirent = readdir(input_dir)) != NULL)
+    {
+      if (strcmp(a_dirent->d_name, ".")  == 0) continue;
+      if (strcmp(a_dirent->d_name, "..") == 0) continue;
+
+      path[path_len] = '/';
+
+      strncpy(&path[path_len + 1], a_dirent->d_name, 4094 - path_len);
+
+      if (a_dirent->d_type == DT_DIR  ||  a_dirent->d_type == DT_REG)
+        {
+          name_len = strlen(a_dirent->d_name);
+
+          if (name_len > max_name_len)
+            {
+              fprintf(stderr, "Skipped item. Name too long: %s\n", path);
+              continue;
+            }
+
+          item_count += 1;
+
+          an_item = malloc(sizeof(dir_item_t) + name_len + 1);
+
+          an_item->prior = prior_item;
+          prior_item     = an_item;
+
+          strncpy(an_item->name, a_dirent->d_name, name_len);
+
+          an_item->init_sector  = sector;
+
+          if (a_dirent->d_type == DT_DIR)
+            {
+              an_item->is_directory = true;
+              an_item->permissions  = 0777;
+
+              sector = scan_dir(sector);
+            }
+          else
+            {
+              an_item->is_directory = false;
+              an_item->permissions  = 0666;
+
+              sector = copy_file(sector, an_item->name);
+            }
+        }
+      else
+        {
+          fprintf(stderr, "Skipped unusable item: %s\n", path);
+        }
+    }
+
+  path[path_len] = 0;
+
+  closedir(input_dir);
+
+  /* Generate the directory sector for this directory. */
+
+  if (item_count > MAX_DIR_COUNT)
+    {
+      printf("\n     sector   %4d, dir, next=%d", in_sector, sector);
+    }
+  else
+    {
+      printf("\n     sector   %4d, dir", in_sector);
+    }
+
+  while (an_item != NULL)
+    {
+      for (int i = 0; i < MAX_DIR_COUNT && an_item != NULL; ++i)
+        {
+          if (an_item->is_directory)
+            {
+              printf("\n     dir_entry   0%03o, 0x%04x, 0, ",
+                    an_item->permissions,
+                    an_item->init_sector);
+
+              put_name(an_item->name);
+            }
+          else
+            {
+              printf("\n     file_entry  0%03o, 0x%04x, 0, ",
+                    an_item->permissions,
+                    an_item->init_sector);
+
+              put_name(an_item->name);
+            }
+
+          prior_item = an_item->prior;
+          free(an_item);
+          an_item = prior_item;
+          item_count -= 1;
+        }
+
+      if (an_item != NULL)
+        {
+          printf("\n     sector   %4d, dir, used=%d",
+                sector,
+                (item_count > MAX_DIR_COUNT) ? (sector + 1) : 0xffff);
+
+          sector += 1;
+        }
+    }
+
+  printf("\n");
+
+  return sector;
+}
+
+/****************************************************************************
+ * Name: print_help
+ ****************************************************************************/
+
+void print_help(const char * name)
+{
+  fprintf(stderr, "\nusage: %s [-h][-n name-len] source-path\n\n", name);
+
+  fprintf(stderr, "-h  : print this help and exit.\n\n");
+
+  fprintf(stderr, "-n  : name length is the length of file names in\n");
+  fprintf(stderr, "      the smart filesystem.  It must match the\n");
+  fprintf(stderr, "      CONFIG_SMARTFS_MAXNAMLEN with which NuttX was\n");
+  fprintf(stderr, "      built.  The default is %d.\n\n", max_name_len);
+
+  fprintf(stderr, "source-path is the path to a prototype directory tree\n");
+  fprintf(stderr, "      that will be used to build the initial smart\n");
+  fprintf(stderr, "      filesystem.  Any items that are not normal\n");
+  fprintf(stderr, "      files or directories will be ignored.\n\n");
+
+  fprintf(stderr, "Output is to stdout.\n\n");
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int main(int argc, char *argv[])
+{
+  FILE         *output_file;
+  const char   *input_path   = NULL;
+
+  /* parse arguments */
+
+  for (int i = 1; i < argc; ++i)
+    {
+      if (argv[i][0] == '-')
+        {
+          if (argv[i][1] == 'h')
+            {
+              print_help(argv[0]);
+              return 0;
+            }
+
+          if (argv[i][1] == 'n')
+            {
+              if (argv[i][2] == 0  &&  argc > i + 1)
+                {
+                  i += 1;
+
+                  if (!isdigit(argv[i][0]))
+                    {
+                      fprintf(stderr, "invalid name length.\n");
+                      print_help(argv[0]);
+                      return 1;
+                    }
+
+                  max_name_len = atoi(argv[i]);
+                }
+              else if (isdigit(argv[i][2]))
+                {
+                  max_name_len = atoi(&(argv[i][2]));
+                }
+              else
+                {
+                  fprintf(stderr, "invalid name length.\n");
+                  print_help(argv[0]);
+                  return 1;
+                }
+            }
+          else
+            {
+              fprintf(stderr, "unknown argument: %c\n", argv[i][1]);
+              print_help(argv[0]);
+              return 1;
+            }
+        }
+      else if (input_path == NULL)
+        {
+          input_path = argv[i];
+        }
+    }
+
+  if (input_path == NULL)
+    {
+      print_help(argv[0]);
+      return 1;
+    }
+
+  buffer = malloc(MAX_SECTOR_DATA);
+
+  if (buffer == NULL)
+    {
+      fprintf(stderr, "could not allocate file buffer.\n");
+      return 1;
+    }
+
+  strncpy(path, input_path, 4096);
+
+  /* remove any stray trailing slash characters. */
+
+  for (int c = strlen(path) - 1; c >= 0; --c)
+    {
+      if (path[c] != '/') break;
+      path[c] = 0;
+    }
+
+  /* Output the defined constants */
+
+  printf("dir=          1\n"
+         "file=         2\n"
+         "name_length= %d\n",
+         max_name_len);
+
+  /* Output the macro definitions and block zero. */
+
+  puts(preamble);
+
+  /* Scan the prototype directory tree. */
+
+  scan_dir(root_sector);
+
+  /* Wrap things up. */
+
+  puts(postamble);
+
+  return 0;
+}