Skip to content

Commit dfc26a0

Browse files
authored
hardware_flash: preserve QSPI pad state over flash access calls. (#2565)
* hardware_flash: preserve QSPI pad state over flash access calls. Add new function, flash_start_xip(), which explicitly performs a first-time XIP setup (including initialising pads). This is mostly useful for PICO_NO_FLASH=1 binaries where there is actually an attached external flash. Fixes #2333 * Address review comments
1 parent 175abe7 commit dfc26a0

File tree

2 files changed

+114
-26
lines changed

2 files changed

+114
-26
lines changed

src/rp2_common/hardware_flash/flash.c

Lines changed: 88 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "hardware/structs/qmi.h"
1515
#include "hardware/regs/otp_data.h"
1616
#endif
17+
#include "hardware/structs/pads_qspi.h"
1718
#include "hardware/xip_cache.h"
1819

1920
#define FLASH_BLOCK_ERASE_CMD 0xd8
@@ -71,7 +72,23 @@ static void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void) {
7172

7273
#endif
7374

74-
#if PICO_RP2350
75+
//-----------------------------------------------------------------------------
76+
// State save/restore
77+
78+
// Most functions save and restore the QSPI pad state over the call. (The main
79+
// exception is flash_start_xip() which is explicitly intended to initialise
80+
// them). The expectation is that by the time you do any flash operations,
81+
// you have either gone through a normal flash boot process or (in the case
82+
// of PICO_NO_FLASH=1) you have called flash_start_xip(). Any further
83+
// modifications to the pad state are therefore deliberate changes that we
84+
// should preserve.
85+
//
86+
// Additionally, on RP2350, we save and restore the window 1 QMI configuration
87+
// if the user has not opted into bootrom CS1 support via FLASH_DEVINFO OTP
88+
// flags. This avoids clobbering CS1 setup (e.g. PSRAM) performed by the
89+
// application.
90+
91+
#if !PICO_RP2040
7592
// This is specifically for saving/restoring the registers modified by RP2350
7693
// flash_exit_xip() ROM func, not the entirety of the QMI window state.
7794
typedef struct flash_rp2350_qmi_save_state {
@@ -108,9 +125,69 @@ static void __no_inline_not_in_flash_func(flash_rp2350_restore_qmi_cs1)(const fl
108125
}
109126
#endif
110127

128+
129+
typedef struct flash_hardware_save_state {
130+
#if !PICO_RP2040
131+
flash_rp2350_qmi_save_state_t qmi_save;
132+
#endif
133+
uint32_t qspi_pads[count_of(pads_qspi_hw->io)];
134+
} flash_hardware_save_state_t;
135+
136+
static void __no_inline_not_in_flash_func(flash_save_hardware_state)(flash_hardware_save_state_t *state) {
137+
// Commit any pending writes to external RAM, to avoid losing them in a subsequent flush:
138+
xip_cache_clean_all();
139+
for (size_t i = 0; i < count_of(pads_qspi_hw->io); ++i) {
140+
state->qspi_pads[i] = pads_qspi_hw->io[i];
141+
}
142+
#if !PICO_RP2040
143+
flash_rp2350_save_qmi_cs1(&state->qmi_save);
144+
#endif
145+
}
146+
147+
static void __no_inline_not_in_flash_func(flash_restore_hardware_state)(flash_hardware_save_state_t *state) {
148+
for (size_t i = 0; i < count_of(pads_qspi_hw->io); ++i) {
149+
pads_qspi_hw->io[i] = state->qspi_pads[i];
150+
}
151+
#if !PICO_RP2040
152+
// Tail call!
153+
flash_rp2350_restore_qmi_cs1(&state->qmi_save);
154+
#endif
155+
}
156+
111157
//-----------------------------------------------------------------------------
112158
// Actual flash programming shims (work whether or not PICO_NO_FLASH==1)
113159

160+
void __no_inline_not_in_flash_func(flash_start_xip)(void) {
161+
rom_connect_internal_flash_fn connect_internal_flash_func = (rom_connect_internal_flash_fn)rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH);
162+
rom_flash_exit_xip_fn flash_exit_xip_func = (rom_flash_exit_xip_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP);
163+
rom_flash_flush_cache_fn flash_flush_cache_func = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE);
164+
rom_flash_enter_cmd_xip_fn flash_enter_cmd_xip_func = (rom_flash_enter_cmd_xip_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_ENTER_CMD_XIP);
165+
assert(connect_internal_flash_func && flash_exit_xip_func && flash_flush_cache_func && flash_enter_cmd_xip_func);
166+
// Commit any pending writes to external RAM, to avoid losing them in the subsequent flush:
167+
xip_cache_clean_all();
168+
#if !PICO_RP2040
169+
flash_rp2350_qmi_save_state_t qmi_save;
170+
flash_rp2350_save_qmi_cs1(&qmi_save);
171+
#endif
172+
173+
// Use ROM calls to get from ~any state to a state where low-speed flash access works:
174+
connect_internal_flash_func();
175+
flash_exit_xip_func();
176+
flash_flush_cache_func();
177+
flash_enter_cmd_xip_func();
178+
179+
// If a boot2 is available then call it now. Slight limitation here is that if this is a
180+
// NO_FLASH binary which was loaded via bootrom LOAD_MAP, we should actually have a better
181+
// flash setup than this available via xip setup func stub left in boot RAM, but we can't
182+
// easily detect this case to take advantage of this.
183+
flash_init_boot2_copyout();
184+
flash_enable_xip_via_boot2();
185+
186+
#if !PICO_RP2040
187+
flash_rp2350_restore_qmi_cs1(&qmi_save);
188+
#endif
189+
}
190+
114191
void __no_inline_not_in_flash_func(flash_range_erase)(uint32_t flash_offs, size_t count) {
115192
#ifdef PICO_FLASH_SIZE_BYTES
116193
hard_assert(flash_offs + count <= PICO_FLASH_SIZE_BYTES);
@@ -123,12 +200,8 @@ void __no_inline_not_in_flash_func(flash_range_erase)(uint32_t flash_offs, size_
123200
rom_flash_flush_cache_fn flash_flush_cache_func = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE);
124201
assert(connect_internal_flash_func && flash_exit_xip_func && flash_range_erase_func && flash_flush_cache_func);
125202
flash_init_boot2_copyout();
126-
// Commit any pending writes to external RAM, to avoid losing them in the subsequent flush:
127-
xip_cache_clean_all();
128-
#if PICO_RP2350
129-
flash_rp2350_qmi_save_state_t qmi_save;
130-
flash_rp2350_save_qmi_cs1(&qmi_save);
131-
#endif
203+
flash_hardware_save_state_t state;
204+
flash_save_hardware_state(&state);
132205

133206
// No flash accesses after this point
134207
__compiler_memory_barrier();
@@ -138,9 +211,7 @@ void __no_inline_not_in_flash_func(flash_range_erase)(uint32_t flash_offs, size_
138211
flash_range_erase_func(flash_offs, count, FLASH_BLOCK_SIZE, FLASH_BLOCK_ERASE_CMD);
139212
flash_flush_cache_func(); // Note this is needed to remove CSn IO force as well as cache flushing
140213
flash_enable_xip_via_boot2();
141-
#if PICO_RP2350
142-
flash_rp2350_restore_qmi_cs1(&qmi_save);
143-
#endif
214+
flash_restore_hardware_state(&state);
144215
}
145216

146217
void __no_inline_not_in_flash_func(flash_flush_cache)(void) {
@@ -160,11 +231,8 @@ void __no_inline_not_in_flash_func(flash_range_program)(uint32_t flash_offs, con
160231
rom_flash_flush_cache_fn flash_flush_cache_func = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE);
161232
assert(connect_internal_flash_func && flash_exit_xip_func && flash_range_program_func && flash_flush_cache_func);
162233
flash_init_boot2_copyout();
163-
xip_cache_clean_all();
164-
#if PICO_RP2350
165-
flash_rp2350_qmi_save_state_t qmi_save;
166-
flash_rp2350_save_qmi_cs1(&qmi_save);
167-
#endif
234+
flash_hardware_save_state_t state;
235+
flash_save_hardware_state(&state);
168236

169237
__compiler_memory_barrier();
170238

@@ -173,9 +241,8 @@ void __no_inline_not_in_flash_func(flash_range_program)(uint32_t flash_offs, con
173241
flash_range_program_func(flash_offs, data, count);
174242
flash_flush_cache_func(); // Note this is needed to remove CSn IO force as well as cache flushing
175243
flash_enable_xip_via_boot2();
176-
#if PICO_RP2350
177-
flash_rp2350_restore_qmi_cs1(&qmi_save);
178-
#endif
244+
245+
flash_restore_hardware_state(&state);
179246
}
180247

181248
//-----------------------------------------------------------------------------
@@ -208,11 +275,8 @@ void __no_inline_not_in_flash_func(flash_do_cmd)(const uint8_t *txbuf, uint8_t *
208275
rom_flash_flush_cache_fn flash_flush_cache_func = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE);
209276
assert(connect_internal_flash_func && flash_exit_xip_func && flash_flush_cache_func);
210277
flash_init_boot2_copyout();
211-
xip_cache_clean_all();
212-
#if PICO_RP2350
213-
flash_rp2350_qmi_save_state_t qmi_save;
214-
flash_rp2350_save_qmi_cs1(&qmi_save);
215-
#endif
278+
flash_hardware_save_state_t state;
279+
flash_save_hardware_state(&state);
216280

217281
__compiler_memory_barrier();
218282
connect_internal_flash_func();
@@ -260,9 +324,7 @@ void __no_inline_not_in_flash_func(flash_do_cmd)(const uint8_t *txbuf, uint8_t *
260324

261325
flash_flush_cache_func();
262326
flash_enable_xip_via_boot2();
263-
#if PICO_RP2350
264-
flash_rp2350_restore_qmi_cs1(&qmi_save);
265-
#endif
327+
flash_restore_hardware_state(&state);
266328
}
267329
#endif
268330

src/rp2_common/hardware_flash/include/hardware/flash.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,32 @@
5656
extern "C" {
5757
#endif
5858

59+
/*! \brief Initialise QSPI interface and external QSPI devices for execute-in-place
60+
* \ingroup hardware_flash
61+
*
62+
* This function performs the same first-time flash setup that would normally occur over the course
63+
* of the bootrom locating a flash binary and booting it, and that flash binary executing the SDK
64+
* crt0. Specifically:
65+
*
66+
* * Initialise QSPI pads to their default states, and (non-RP2040) disable pad isolation latches
67+
* * Issue a hardcoded sequence to attached QSPI devices to return them to a serial command state
68+
* * Flush the XIP cache
69+
* * Configure the QSPI interface for low-speed 03h reads
70+
* * If this is not a PICO_NO_FLASH=1 binary:
71+
* * (RP2040) load a boot2 stage from the first 256 bytes of RAM and execute it
72+
* * (non-RP2040) execute an XIP setup function stored in boot RAM by either the bootrom or by crt0
73+
*
74+
* This is mostly useful for initialising flash on a PICO_NO_FLASH=1 binary. (In spite of the name,
75+
* this binary type really means "preloaded to RAM" and there may still be a flash device.)
76+
*
77+
* This function does not preserve the QSPI interface state or pad state. This is in contrast to
78+
* most other functions in this library, which preserve at least the QSPI pad state. However, on
79+
* RP2350 it does preserve the QMI window 1 configuration if you have not opted into bootrom CS1
80+
* support via FLASH_DEVINFO.
81+
*/
82+
void flash_start_xip(void);
83+
84+
5985
/*! \brief Erase areas of flash
6086
* \ingroup hardware_flash
6187
*

0 commit comments

Comments
 (0)