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 <marco.casaroli@gmail.com>
This commit is contained in:
Marco Casaroli 2026-10-07 09:12:40 +02:00 • committed by Alan C. Assis
parent ecc0036470
commit 1d8898d7d6
3 changed files with 50 additions and 43 deletions

View file

@ -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

View file

@ -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

View file

@ -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;