From d8c8805b538aee2ae6c1e2c383f091b978d5080f Mon Sep 17 00:00:00 2001 From: Ricard Rosson Date: Fri, 10 Jul 2026 10:02:43 +0100 Subject: [PATCH] arch/arm/rp23xx: add on-chip flash LittleFS (MTD) support Add an MTD driver over the RP2350's on-chip QSPI flash, registered as a whole-chip device (/dev/rpflash) so the NuttX image / OTA region stays addressable; a bounded data partition (/dev/rpdata) is carved out with mtd_partition() and a persistent read/write LittleFS is mounted on it via the common board bringup. The driver uses the RP2350 bootrom flash programming functions. The erase/program routine (do_flash_op) must not be fetched from flash while XIP is disabled, so it is placed in ".time_critical.flash_mtd", which the RP2350 board linker scripts relocate into RAM. It runs with interrupts disabled, one 4 KiB block per critical section, so a large erase (e.g. a LittleFS format) never holds interrupts off for long or faults on an XIP-cache miss. Reads go through the cached XIP window -- freshness is guaranteed because do_flash_op() calls the bootrom flash_flush_cache() after every operation. The per-operation critical section provides all the hardware atomicity the driver needs; serializing whole multi-block operations against concurrent callers is the consumer/filesystem's responsibility under the usual MTD contract, so the driver holds no mutex (reads are a plain XIP memcpy). Enabled with CONFIG_RP23XX_FLASH_MTD (off by default); the data partition offset/size and mount point are configurable. A blank or corrupt filesystem is a non-fatal, logged condition (format once from NSH with 'mount -t littlefs -o autoformat /dev/rpdata /data') so it never aborts the rest of board bringup. Developed with Claude Code (Anthropic's agentic coding tool) and validated on a Raspberry Pi Pico 2 W: format, read/write, persistence across an unmount/remount and a power-cycle, and loading and executing a code image from the LittleFS. Signed-off-by: Ricard Rosson Co-Authored-By: Claude Fable 5 --- arch/arm/src/rp23xx/CMakeLists.txt | 4 + arch/arm/src/rp23xx/Kconfig | 66 +++ arch/arm/src/rp23xx/Make.defs | 4 + arch/arm/src/rp23xx/rp23xx_flash_mtd.c | 430 ++++++++++++++++++ arch/arm/src/rp23xx/rp23xx_flash_mtd.h | 54 +++ .../rp23xx/common/src/rp23xx_common_bringup.c | 94 ++++ 6 files changed, 652 insertions(+) create mode 100644 arch/arm/src/rp23xx/rp23xx_flash_mtd.c create mode 100644 arch/arm/src/rp23xx/rp23xx_flash_mtd.h diff --git a/arch/arm/src/rp23xx/CMakeLists.txt b/arch/arm/src/rp23xx/CMakeLists.txt index b67f08039aad2..76f365e892589 100644 --- a/arch/arm/src/rp23xx/CMakeLists.txt +++ b/arch/arm/src/rp23xx/CMakeLists.txt @@ -89,4 +89,8 @@ if(CONFIG_CRYPTO_CRYPTODEV_HARDWARE) list(APPEND SRCS rp23xx_crypto.c) endif() +if(CONFIG_RP23XX_FLASH_MTD) + list(APPEND SRCS rp23xx_flash_mtd.c) +endif() + target_sources(arch PRIVATE ${SRCS}) diff --git a/arch/arm/src/rp23xx/Kconfig b/arch/arm/src/rp23xx/Kconfig index cbeba7bbaa7e0..bf53e907d6e49 100644 --- a/arch/arm/src/rp23xx/Kconfig +++ b/arch/arm/src/rp23xx/Kconfig @@ -1059,3 +1059,69 @@ config RP23XX_BOARD_HAS_WS2812 ---help--- See the Board Selection menu to configure the pins used by ws2812. + +##################################################################### +# On-chip Flash File System Configuration +##################################################################### + +config RP23XX_FLASH_MTD + bool "MTD device over the on-chip QSPI flash" + default n + select MTD + ---help--- + Create an MTD device spanning the entire on-chip QSPI flash, + using the RP2350 bootrom flash programming functions. It is + registered as /dev/rpflash. Because the MTD covers the whole + chip, the region the NuttX image lives in stays addressable (for + example, for a firmware/OTA update). Filesystem and OTA + partitions are layered on top with mtd_partition(). + +if RP23XX_FLASH_MTD + +config RP23XX_FLASH_SIZE + int "Total size of the on-chip flash (bytes)" + default 4194304 + ---help--- + Size of the board's QSPI flash chip in bytes. The MTD spans this + entire region. The Raspberry Pi Pico 2 / Pico 2 W have + 4 MiB = 4194304 bytes. + +config RP23XX_FLASH_MTD_DATA + bool "LittleFS data partition on the flash MTD" + default n + select MTD_PARTITION + select FS_LITTLEFS + ---help--- + Carve a partition out of the flash MTD (registered as /dev/rpdata) + and mount a LittleFS filesystem on it for persistent local + storage. Bounding LittleFS to this partition keeps it clear of + the NuttX image / OTA region. + +if RP23XX_FLASH_MTD_DATA + +config RP23XX_FLASH_MTD_DATA_OFFSET + int "Data partition offset from start of flash (bytes)" + default 1048576 + ---help--- + Offset of the LittleFS data partition from the start of flash. + Must be a multiple of 4096 and lie safely beyond the end of the + NuttX image. The default (1 MiB) leaves room for the image. + +config RP23XX_FLASH_MTD_DATA_SIZE + int "Data partition size (bytes)" + default 3145728 + ---help--- + Size of the LittleFS data partition in bytes. Must be a multiple + of 4096, and OFFSET + SIZE must not exceed RP23XX_FLASH_SIZE. + The default (3 MiB) fills the rest of a 4 MiB chip after a 1 MiB + image region. + +config RP23XX_FLASH_MTD_DATA_MOUNTPOINT + string "Mount point for the data partition" + default "/data" + ---help--- + Where to mount the LittleFS. Leave empty to register the + partition (/dev/rpdata) without auto-mounting. + +endif # RP23XX_FLASH_MTD_DATA +endif # RP23XX_FLASH_MTD diff --git a/arch/arm/src/rp23xx/Make.defs b/arch/arm/src/rp23xx/Make.defs index 0042124804909..a8b938821bc61 100644 --- a/arch/arm/src/rp23xx/Make.defs +++ b/arch/arm/src/rp23xx/Make.defs @@ -92,3 +92,7 @@ endif ifeq ($(CONFIG_CRYPTO_CRYPTODEV_HARDWARE),y) CHIP_CSRCS += rp23xx_crypto.c endif + +ifeq ($(CONFIG_RP23XX_FLASH_MTD),y) +CHIP_CSRCS += rp23xx_flash_mtd.c +endif diff --git a/arch/arm/src/rp23xx/rp23xx_flash_mtd.c b/arch/arm/src/rp23xx/rp23xx_flash_mtd.c new file mode 100644 index 0000000000000..321daa303bee2 --- /dev/null +++ b/arch/arm/src/rp23xx/rp23xx_flash_mtd.c @@ -0,0 +1,430 @@ +/**************************************************************************** + * arch/arm/src/rp23xx/rp23xx_flash_mtd.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 implements an MTD device spanning the entire on-chip QSPI flash of + * the RP2350. Callers layer partitions on top of it with mtd_partition() + * (e.g. a LittleFS data partition beyond the NuttX image, and/or a + * firmware/OTA slot); keeping the MTD whole-device means the region the + * NuttX image lives in stays addressable for updates. + * + * Because the RP2350 normally executes code directly from this same flash + * (execute-in-place, XIP), no code may be fetched from flash while a block + * is being erased or programmed. Therefore the erase/program routines run + * from RAM (the ".time_critical.*" section, copied into RAM with .data at + * startup) and run with interrupts disabled. + * + * The RP2350 flash-programming sequence follows the Raspberry Pi Pico SDK + * (hardware_flash/flash.c) and the RP2350 datasheet bootrom chapter: + * + * - the bootrom flash helpers are looked up via rom_func_lookup(); + * - after flash_flush_cache() the bootrom already leaves the flash in a + * basic (readable) XIP state, so XIP is re-enabled by calling the + * bootrom flash_enter_cmd_xip() helper (no copied BOOTRAM setup + * function needed); + * - the QMI window-1 (CS1) registers, which flash_exit_xip() disturbs on + * the RP2350, are saved before and restored after the operation. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include "rp23xx_rom.h" +#include "rp23xx_flash_mtd.h" +#include "hardware/rp23xx_memorymap.h" +#include "hardware/rp23xx_qmi.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Run this function from RAM (copied along with .data at startup). */ + +#define RAM_CODE __attribute__((noinline, section(".time_critical.flash_mtd"))) + +/* Read the filesystem through the normal cached XIP window (0x10000000) — + * the same alias the CPU executes from, so it is always functional. + * Freshness after a program/erase is guaranteed because do_flash_op() calls + * the bootrom flash_flush_cache() (which invalidates the XIP cache) at the + * end of every operation. (The uncached alias 0x14000000 was used + * previously, but reading through it depends on the uncached window being + * set up and is a needless fault risk during the very first mount-time + * read.) + */ + +#define RP23XX_XIP_READ_BASE 0x10000000 + +/* QMI window-1 registers clobbered by flash_exit_xip() on the RP2350. + * (Build the absolute addresses from the QMI base + register offsets; the + * convenience macros in rp23xx_qmi.h reference an undefined + * RP23XX_QMI_BASE.) + */ + +#define QMI_M1_TIMING_REG (RP23XX_XIP_QMI_BASE + RP23XX_QMI_M1_TIMING_OFFSET) +#define QMI_M1_RFMT_REG (RP23XX_XIP_QMI_BASE + RP23XX_QMI_M1_RFMT_OFFSET) +#define QMI_M1_RCMD_REG (RP23XX_XIP_QMI_BASE + RP23XX_QMI_M1_RCMD_OFFSET) + +/* Flash geometry */ + +#define FLASH_PAGE_SIZE 256 /* Smallest program unit */ +#define FLASH_BLOCK_SIZE 4096 /* Smallest erase unit */ +#define FLASH_BLOCK_ERASE_CMD 0xd8 /* 64KiB block erase (ROM falls */ +#define FLASH_LARGE_BLOCK 65536 /* back to 4KiB where unaligned) */ + +/* Total flash size. The MTD spans the whole chip; sub-regions (data + * partition, OTA slot, ...) are carved out by the board using + * mtd_partition(). + */ + +#define FLASH_TOTAL_SIZE CONFIG_RP23XX_FLASH_SIZE +#define FLASH_BLOCK_COUNT (FLASH_TOTAL_SIZE / FLASH_BLOCK_SIZE) +#define FLASH_PAGE_COUNT (FLASH_TOTAL_SIZE / FLASH_PAGE_SIZE) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef void (*rom_void_fn)(void); +typedef void (*rom_erase_fn)(uint32_t, size_t, uint32_t, uint8_t); +typedef void (*rom_program_fn)(uint32_t, const uint8_t *, size_t); + +struct rp23xx_flash_dev_s +{ + struct mtd_dev_s mtd; /* Embedded MTD interface (must be first) */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int rp23xx_flash_erase(struct mtd_dev_s *dev, off_t startblock, + size_t nblocks); +static ssize_t rp23xx_flash_bread(struct mtd_dev_s *dev, off_t startblock, + size_t nblocks, uint8_t *buffer); +static ssize_t rp23xx_flash_bwrite(struct mtd_dev_s *dev, off_t startblock, + size_t nblocks, const uint8_t *buffer); +static ssize_t rp23xx_flash_read(struct mtd_dev_s *dev, off_t offset, + size_t nbytes, uint8_t *buffer); +static int rp23xx_flash_ioctl(struct mtd_dev_s *dev, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The bootrom function pointers and the RAM copy of the XIP setup function. + * All of these live in RAM (.bss/.data) so they may be touched from the + * RAM-resident do_flash_op() while XIP is disabled. + */ + +static struct +{ + rom_void_fn connect_internal_flash; + rom_void_fn flash_exit_xip; + rom_erase_fn flash_range_erase; + rom_program_fn flash_range_program; + rom_void_fn flash_flush_cache; + rom_void_fn flash_enter_cmd_xip; +} g_rom; + +static struct rp23xx_flash_dev_s g_dev = +{ + .mtd = + { + .erase = rp23xx_flash_erase, + .bread = rp23xx_flash_bread, + .bwrite = rp23xx_flash_bwrite, + .read = rp23xx_flash_read, + .ioctl = rp23xx_flash_ioctl, + .name = "rp23xx_flash", + }, +}; + +static bool g_initialized = false; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: do_flash_op + * + * Description: + * Perform a single erase or program operation against the flash. This + * MUST run from RAM with interrupts disabled: while XIP is exited, the CPU + * cannot fetch instructions or read data from the flash. + * + * addr - byte offset from the start of flash (NOT an XIP address) + * data - source buffer for a program; ignored for an erase + * count - number of bytes to program/erase (block/page aligned) + * erase - true to erase, false to program + * + ****************************************************************************/ + +static void RAM_CODE do_flash_op(uint32_t addr, const uint8_t *data, + size_t count, bool erase) +{ + /* Save the QMI window-1 registers that flash_exit_xip() disturbs. */ + + uint32_t m1_timing = *(volatile uint32_t *)QMI_M1_TIMING_REG; + uint32_t m1_rfmt = *(volatile uint32_t *)QMI_M1_RFMT_REG; + uint32_t m1_rcmd = *(volatile uint32_t *)QMI_M1_RCMD_REG; + + /* No flash access is permitted past this barrier until XIP is re-enabled */ + + __asm__ volatile ("" : : : "memory"); + + g_rom.connect_internal_flash(); + g_rom.flash_exit_xip(); + + if (erase) + { + g_rom.flash_range_erase(addr, count, FLASH_LARGE_BLOCK, + FLASH_BLOCK_ERASE_CMD); + } + else + { + g_rom.flash_range_program(addr, data, count); + } + + /* flash_flush_cache() also drops the CSn IO force left by exit_xip(). */ + + g_rom.flash_flush_cache(); + + /* Re-enable XIP. Use the bootrom's flash_enter_cmd_xip helper, which is + * always present and restores a working (standard read) XIP mode without + * depending on a copied boot2/bootram setup function. + */ + + g_rom.flash_enter_cmd_xip(); + + /* Restore the QMI window-1 registers. */ + + *(volatile uint32_t *)QMI_M1_TIMING_REG = m1_timing; + *(volatile uint32_t *)QMI_M1_RFMT_REG = m1_rfmt; + *(volatile uint32_t *)QMI_M1_RCMD_REG = m1_rcmd; +} + +/**************************************************************************** + * Name: rp23xx_flash_erase + ****************************************************************************/ + +static int rp23xx_flash_erase(struct mtd_dev_s *dev, off_t startblock, + size_t nblocks) +{ + irqstate_t flags; + size_t i; + + if (startblock + nblocks > FLASH_BLOCK_COUNT) + { + return -EINVAL; + } + + finfo("erase block %ju count %zu\n", (uintmax_t)startblock, nblocks); + + /* Erase one block per critical section. do_flash_op() runs with + * interrupts disabled (XIP is exited during the operation); erasing the + * whole request in a single critical section would hold interrupts off for + * many seconds on a large (e.g. bulk) erase and stall the system. One + * 4 KiB block at a time keeps each interrupts-off window short. + */ + + for (i = 0; i < nblocks; i++) + { + flags = enter_critical_section(); + do_flash_op((startblock + i) * FLASH_BLOCK_SIZE, + NULL, FLASH_BLOCK_SIZE, true); + leave_critical_section(flags); + } + + return nblocks; +} + +/**************************************************************************** + * Name: rp23xx_flash_bread + ****************************************************************************/ + +static ssize_t rp23xx_flash_bread(struct mtd_dev_s *dev, off_t startblock, + size_t nblocks, uint8_t *buffer) +{ + if (startblock + nblocks > FLASH_PAGE_COUNT) + { + return -EINVAL; + } + + /* Read through the cached XIP window; do_flash_op() invalidates the XIP + * cache via flash_flush_cache() after every program/erase, so this never + * returns stale data. + */ + + memcpy(buffer, + (const void *)(RP23XX_XIP_READ_BASE + startblock * FLASH_PAGE_SIZE), + nblocks * FLASH_PAGE_SIZE); + + return nblocks; +} + +/**************************************************************************** + * Name: rp23xx_flash_bwrite + ****************************************************************************/ + +static ssize_t rp23xx_flash_bwrite(struct mtd_dev_s *dev, off_t startblock, + size_t nblocks, const uint8_t *buffer) +{ + irqstate_t flags; + + if (startblock + nblocks > FLASH_PAGE_COUNT) + { + return -EINVAL; + } + + finfo("write page %ju count %zu\n", (uintmax_t)startblock, nblocks); + + flags = enter_critical_section(); + do_flash_op(startblock * FLASH_PAGE_SIZE, buffer, + nblocks * FLASH_PAGE_SIZE, false); + leave_critical_section(flags); + + return nblocks; +} + +/**************************************************************************** + * Name: rp23xx_flash_read + ****************************************************************************/ + +static ssize_t rp23xx_flash_read(struct mtd_dev_s *dev, off_t offset, + size_t nbytes, uint8_t *buffer) +{ + if (offset + nbytes > FLASH_TOTAL_SIZE) + { + return -EINVAL; + } + + memcpy(buffer, + (const void *)(RP23XX_XIP_READ_BASE + offset), + nbytes); + + return nbytes; +} + +/**************************************************************************** + * Name: rp23xx_flash_ioctl + ****************************************************************************/ + +static int rp23xx_flash_ioctl(struct mtd_dev_s *dev, int cmd, + unsigned long arg) +{ + int ret = OK; + + switch (cmd) + { + case MTDIOC_GEOMETRY: + { + struct mtd_geometry_s *geo = (struct mtd_geometry_s *)arg; + + if (geo != NULL) + { + memset(geo, 0, sizeof(*geo)); + geo->blocksize = FLASH_PAGE_SIZE; + geo->erasesize = FLASH_BLOCK_SIZE; + geo->neraseblocks = FLASH_BLOCK_COUNT; + } + break; + } + + case MTDIOC_BULKERASE: + ret = rp23xx_flash_erase(dev, 0, FLASH_BLOCK_COUNT); + break; + + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rp23xx_flash_mtd_initialize + ****************************************************************************/ + +struct mtd_dev_s *rp23xx_flash_mtd_initialize(void) +{ + if (g_initialized) + { + errno = EBUSY; + return NULL; + } + + if (FLASH_BLOCK_COUNT < 4) + { + errno = EINVAL; + return NULL; + } + + /* Resolve the bootrom flash helper functions. */ + + g_rom.connect_internal_flash = + (rom_void_fn)rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH); + g_rom.flash_exit_xip = + (rom_void_fn)rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP); + g_rom.flash_range_erase = + (rom_erase_fn)rom_func_lookup(ROM_FUNC_FLASH_RANGE_ERASE); + g_rom.flash_range_program = + (rom_program_fn)rom_func_lookup(ROM_FUNC_FLASH_RANGE_PROGRAM); + g_rom.flash_flush_cache = + (rom_void_fn)rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE); + g_rom.flash_enter_cmd_xip = + (rom_void_fn)rom_func_lookup(ROM_FUNC_FLASH_ENTER_CMD_XIP); + + if (g_rom.connect_internal_flash == NULL || + g_rom.flash_exit_xip == NULL || + g_rom.flash_range_erase == NULL || + g_rom.flash_range_program == NULL || + g_rom.flash_flush_cache == NULL || + g_rom.flash_enter_cmd_xip == NULL) + { + errno = ENODEV; + return NULL; + } + + g_initialized = true; + + finfo("rp23xx flash MTD: length=0x%x blocks=%d\n", + (unsigned)FLASH_TOTAL_SIZE, (int)FLASH_BLOCK_COUNT); + + return &g_dev.mtd; +} diff --git a/arch/arm/src/rp23xx/rp23xx_flash_mtd.h b/arch/arm/src/rp23xx/rp23xx_flash_mtd.h new file mode 100644 index 0000000000000..b73e08ef101b3 --- /dev/null +++ b/arch/arm/src/rp23xx/rp23xx_flash_mtd.h @@ -0,0 +1,54 @@ +/**************************************************************************** + * arch/arm/src/rp23xx/rp23xx_flash_mtd.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 __ARCH_ARM_SRC_RP23XX_RP23XX_FLASH_MTD_H +#define __ARCH_ARM_SRC_RP23XX_RP23XX_FLASH_MTD_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: rp23xx_flash_mtd_initialize + * + * Description: + * Create and initialize an MTD device that uses the RP2350 bootrom flash + * programming functions for read/write access to the entire on-chip QSPI + * flash. Sub-regions (e.g. a data partition beyond the NuttX image, or a + * firmware/OTA slot) are carved out of it with mtd_partition(). + * + * Returned Value: + * A pointer to the whole-flash MTD device on success; NULL on failure + * (errno set). + * + ****************************************************************************/ + +struct mtd_dev_s *rp23xx_flash_mtd_initialize(void); + +#endif /* __ARCH_ARM_SRC_RP23XX_RP23XX_FLASH_MTD_H */ diff --git a/boards/arm/rp23xx/common/src/rp23xx_common_bringup.c b/boards/arm/rp23xx/common/src/rp23xx_common_bringup.c index 10b941ce3c650..27f7034d80af5 100644 --- a/boards/arm/rp23xx/common/src/rp23xx_common_bringup.c +++ b/boards/arm/rp23xx/common/src/rp23xx_common_bringup.c @@ -36,6 +36,10 @@ #include "rp23xx_common_pico.h" #include "rp23xx_common_bringup.h" +#ifdef CONFIG_RP23XX_FLASH_MTD +# include "rp23xx_flash_mtd.h" +#endif + #ifdef CONFIG_RP23XX_PWM #include "rp23xx_pwm.h" #include "rp23xx_pwmdev.h" @@ -73,6 +77,13 @@ int rp23xx_common_bringup(void) { int ret = 0; +#ifdef CONFIG_RP23XX_FLASH_MTD + FAR struct mtd_dev_s *mtd; +#endif +#ifdef CONFIG_RP23XX_FLASH_MTD_DATA + FAR struct mtd_dev_s *part; + struct mtd_geometry_s geo; +#endif #ifdef CONFIG_RP23XX_I2C_DRIVER #ifdef CONFIG_RP23XX_I2C0 @@ -405,6 +416,89 @@ int rp23xx_common_bringup(void) } #endif +#ifdef CONFIG_RP23XX_FLASH_MTD + /* Create a whole-flash MTD and register it as /dev/rpflash. Keeping the + * MTD over the entire chip leaves the NuttX image region addressable (e.g. + * for a firmware/OTA update); a data filesystem is layered on top as a + * bounded partition below. + */ + + mtd = rp23xx_flash_mtd_initialize(); + if (mtd == NULL) + { + serr("ERROR: rp23xx_flash_mtd_initialize failed\n"); + } + else + { + ret = register_mtddriver("/dev/rpflash", mtd, 0755, NULL); + if (ret < 0) + { + serr("ERROR: register_mtddriver(/dev/rpflash) failed: %d\n", ret); + } + +#ifdef CONFIG_RP23XX_FLASH_MTD_DATA + /* Carve a data partition out of the whole-flash MTD and mount a + * LittleFS on it. mtd_partition() takes its offset/size in blocksize + * (page) units, so convert from the configured byte values using the + * MTD's own geometry. Bounding LittleFS to this partition keeps it + * clear of the firmware / OTA region. + */ + + if (ret >= 0 && + MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)&geo) >= 0) + { + part = mtd_partition(mtd, + CONFIG_RP23XX_FLASH_MTD_DATA_OFFSET / geo.blocksize, + CONFIG_RP23XX_FLASH_MTD_DATA_SIZE / geo.blocksize); + if (part == NULL) + { + serr("ERROR: flash data mtd_partition failed\n"); + } + else + { + ret = register_mtddriver("/dev/rpdata", part, 0755, NULL); + if (ret < 0) + { + serr("ERROR: register_mtddriver(/dev/rpdata) failed: %d\n", + ret); + } + else if (CONFIG_RP23XX_FLASH_MTD_DATA_MOUNTPOINT[0] != '\0') + { + /* Mount an EXISTING LittleFS on the data partition. We + * deliberately do NOT pass "autoformat": formatting writes + * flash, and a flash erase/program this early in bringup + * (interrupts disabled, XIP exited, USB not up) can hang + * the boot. A blank or corrupt filesystem simply fails to + * mount and is logged; the board still boots. Format it + * once from NSH: + * mount -t littlefs -o autoformat /dev/rpdata /data + */ + + ret = nx_mount("/dev/rpdata", + CONFIG_RP23XX_FLASH_MTD_DATA_MOUNTPOINT, + "littlefs", 0, NULL); + if (ret < 0) + { + serr("flash: %s not mounted (format with 'mount -t " + "littlefs -o autoformat /dev/rpdata %s'): %d\n", + CONFIG_RP23XX_FLASH_MTD_DATA_MOUNTPOINT, + CONFIG_RP23XX_FLASH_MTD_DATA_MOUNTPOINT, ret); + + /* A blank/corrupt optional filesystem is non-fatal: + * log and continue bringup rather than returning the + * error (which would abort the rest of board bringup, + * e.g. networking). + */ + + ret = OK; + } + } + } + } +#endif + } +#endif + #ifdef CONFIG_RP23XX_I2S ret = board_i2sdev_initialize(0); if (ret < 0)