From 1d8898d7d61522feedc3c88bc38e84c29fdd1f0a Mon Sep 17 00:00:00 2001 From: Marco Casaroli Date: Wed, 7 Oct 2026 09:12:40 +0200 Subject: [PATCH] arch/arm/rp23xx: Restore XIP with the bootrom XIP setup function. After a flash operation the driver called flash_select_xip_read_mode() with a fixed EBh quad mode and clock divisor 4, and called flash_enter_cmd_xip() if it "failed". But that ROM function returns void, so the check read a random r0. The fixed mode and divisor can also be different from the ones the bootrom found at boot. The datasheet (5.2.7, 5.4.8.10) and the Pico SDK use a different method: after a flash boot the bootrom leaves an XIP setup function in the first 256 bytes of boot RAM. It restores the read mode and clock divisor found at boot. Boot RAM is not executable, so copy the function to SRAM once at initialization, and call the copy. If boot RAM is empty (no flash boot), use flash_enter_cmd_xip(), as RP23XX_FLASH_MTD_SAFE_XIP does. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli --- Documentation/platforms/arm/rp23xx/index.rst | 8 ++- arch/arm/src/rp23xx/Kconfig | 12 ++-- arch/arm/src/rp23xx/rp23xx_flash_mtd.c | 73 +++++++++++--------- 3 files changed, 50 insertions(+), 43 deletions(-) diff --git a/Documentation/platforms/arm/rp23xx/index.rst b/Documentation/platforms/arm/rp23xx/index.rst index 0e8c9df7754..69345e5af5e 100644 --- a/Documentation/platforms/arm/rp23xx/index.rst +++ b/Documentation/platforms/arm/rp23xx/index.rst @@ -328,11 +328,13 @@ Erase and program go through the bootrom flash routines. Because those operations stall instruction fetch from the same flash, the driver runs them from SRAM with interrupts disabled and, on SMP builds, the other core parked; expect interrupt latency to suffer for the duration of a write. Afterwards the -QSPI interface is put back into execute-in-place mode -- by default restoring -the fast read mode the bootrom set up at boot, or, with +QSPI interface is put back into execute-in-place mode -- by default with a +copy of the XIP setup function that the bootrom leaves in boot RAM, which +restores the read mode found at boot, or, with ``RP23XX_FLASH_MTD_SAFE_XIP``, always through the bootrom ``flash_enter_cmd_xip`` routine, which is slower to execute from but depends -only on the documented bootrom entry point. +only on the documented bootrom entry point. The driver also saves and +restores the QSPI pads and the PSRAM configuration on chip select 1. The driver answers the ``BIOC_XIPBASE`` ioctl with the memory-mapped address of the region, so a filesystem that supports execute in place can hand out real diff --git a/arch/arm/src/rp23xx/Kconfig b/arch/arm/src/rp23xx/Kconfig index 7c3e4180c53..dc3af5774ce 100644 --- a/arch/arm/src/rp23xx/Kconfig +++ b/arch/arm/src/rp23xx/Kconfig @@ -1247,14 +1247,14 @@ config RP23XX_FLASH_MTD_SAFE_XIP default n ---help--- After each erase or program the QSPI interface must be put back - into execute-in-place mode. By default the driver restores the - fast read mode the bootrom configured at boot, which is what code - executing in place from this flash needs. + into execute-in-place mode. By default the driver runs a copy of + the XIP setup function that the bootrom leaves in boot RAM, as the + Pico SDK does. It restores the read mode and clock divisor that + the bootrom found at boot. Enable this to always use the bootrom flash_enter_cmd_xip routine - instead. That is slower to execute from, but depends on nothing - beyond the documented bootrom entry point, so it is the safe - choice when bringing up a new board. + instead. That gives a slow 03h serial read mode, but depends on + nothing beyond the documented bootrom entry point. endif # RP23XX_FLASH_MTD diff --git a/arch/arm/src/rp23xx/rp23xx_flash_mtd.c b/arch/arm/src/rp23xx/rp23xx_flash_mtd.c index e4279b4f46c..d229dca87f1 100644 --- a/arch/arm/src/rp23xx/rp23xx_flash_mtd.c +++ b/arch/arm/src/rp23xx/rp23xx_flash_mtd.c @@ -114,15 +114,11 @@ #define RP23XX_FLASH_MAX_SIZE 0x04000000 -/* Bootrom XIP read modes, and the clock divisor to run them at. Quad is - * the fast one; the bootrom validates the part can do it. +/* Size of the XIP setup function the bootrom leaves at the start of boot + * RAM (datasheet 5.2.7). */ -#define RP23XX_XIP_MODE_03H_SERIAL 0 -#define RP23XX_XIP_MODE_0BH_SERIAL 1 -#define RP23XX_XIP_MODE_BBH_DUAL 2 -#define RP23XX_XIP_MODE_EBH_QUAD 3 -#define RP23XX_XIP_CLKDIV 4 +#define XIP_SETUP_WORDS 64 /* Smallest unit that can be programmed, and smallest that can be erased */ @@ -172,7 +168,7 @@ 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_enter_cmd_xip_f)(void); -typedef bool (*select_xip_read_mode_f)(uint32_t mode, uint8_t clkdiv); +typedef void (*xip_setup_f)(void); #ifdef CONFIG_SMP /* Locks coordinating "pause" and "resume" with the handler that blocks a @@ -245,15 +241,18 @@ static struct flash_flush_cache_f flash_flush_cache; flash_enter_cmd_xip_f flash_enter_cmd_xip; - /* Restores a fast XIP read mode. flash_enter_cmd_xip works everywhere - * but leaves the flash in a slow 03h serial mode, which costs roughly an - * order of magnitude of read bandwidth -- and the base firmware executes - * from this same flash, so it is not a cost confined to the filesystem. + /* SRAM copy of the bootrom XIP setup function. It restores the read + * mode and clock divisor found at boot. NULL if there is none: + * flash_enter_cmd_xip then gives a slow 03h serial mode. */ - select_xip_read_mode_f select_xip_read_mode; + xip_setup_f xip_setup; } g_rom; +#ifndef CONFIG_RP23XX_FLASH_MTD_SAFE_XIP +static uint32_t g_xip_setup[XIP_SETUP_WORDS]; +#endif + /* End of the NuttX image in flash, provided by the linker script. Declared * weak so that a RAM-only memory map, which does not define it, still * links. @@ -454,14 +453,11 @@ static void RAM_CODE(rp23xx_flash_end)(struct rp23xx_qspi_state_s *state) g_rom.flash_flush_cache(); - /* Ask the bootrom to put the flash back into a fast quad read mode. It - * reports whether it managed to, so a part that cannot do quad falls - * back rather than leaving the interface unusable. - */ - - if (g_rom.select_xip_read_mode == NULL || - !g_rom.select_xip_read_mode(RP23XX_XIP_MODE_EBH_QUAD, - RP23XX_XIP_CLKDIV)) + if (g_rom.xip_setup != NULL) + { + g_rom.xip_setup(); + } + else { g_rom.flash_enter_cmd_xip(); } @@ -766,6 +762,10 @@ static int rp23xx_flash_ioctl(struct mtd_dev_s *dev, int cmd, struct mtd_dev_s *rp23xx_flash_mtd_initialize(void) { +#ifndef CONFIG_RP23XX_FLASH_MTD_SAFE_XIP + int i; +#endif + if (g_initialized) { set_errno(EBUSY); @@ -816,24 +816,29 @@ struct mtd_dev_s *rp23xx_flash_mtd_initialize(void) return NULL; } - /* Resolve the fast XIP read mode selector. Unlike the bootrom's saved - * XIP setup pointer -- which is a data table entry whose semantics this - * driver got wrong, and calling it with flash torn down hangs the core - * unrecoverably -- this is an ordinary ROM function looked up exactly - * like the others above, and it returns a status. + /* Copy the bootrom XIP setup function out of boot RAM, which is not + * executable, as the Pico SDK does. Boot RAM is empty when the image + * was not started by a flash boot. */ #ifndef CONFIG_RP23XX_FLASH_MTD_SAFE_XIP - g_rom.select_xip_read_mode = - rom_func_lookup(ROM_FUNC_FLASH_SELECT_XIP_READ_MODE); - - if (g_rom.select_xip_read_mode == NULL) + for (i = 0; i < XIP_SETUP_WORDS; i++) { - fwarn("rp23xx_flash: no fast XIP selector; reads will be slow after " - "every flash operation\n"); + g_xip_setup[i] = getreg32(RP23XX_BOOTRAM_BASE + 4 * i); + } + + UP_DSB(); + UP_ISB(); + + if (g_xip_setup[0] != 0) + { + g_rom.xip_setup = (xip_setup_f)((uintptr_t)g_xip_setup | 1); + } + else + { + fwarn("rp23xx_flash: no XIP setup function in boot RAM; reads will " + "be slow after every flash operation\n"); } -#else - g_rom.select_xip_read_mode = NULL; #endif g_initialized = true;