Skip to content

Commit 7b07b0b

Browse files
authored
Save/restore QMI window 1 registers across calls to flash_exit_xip(). (#2082)
* Save/restore QMI window 1 registers across calls to flash_exit_xip(). This works around the RP2350 ROM reinitialising window 1 even when it does not actually issue an XIP exit sequence to that QSPI device. If no exit sequence is issued, then the original configuration still applies. This is not valid in the case where the ROM *does* issue an XIP exit sequence to the second chip select, because the original mode is no longer valid once the device is in the serial command state. The ROM does this when FLASH_DEVINFO (loaded from OTP, stored in boot RAM) has a nonzero size for chip select 1. To distinguish the two cases, this patch adds getters/setters for FLASH_DEVINFO. If chip select 1 has a nonzero size in FLASH_DEVINFO then the ROM's register changes for window 1 are not reverted, and instead the hardware_flash code makes additional changes to ensure writes also work (as the ROM only reinitialises for reads). This means that, in the case you have opted into ROM support for the second chip select by setting a nonzero CS1 size in FLASH_DEVINFO, the device on chip select 1 will continue to function correctly, but full performance won't be restored until it is reinitialised. The best way to handle this is initialising both chip select devices in your XIP setup function (your boot2). * Fix whitespace. Fix handling of PICO_FLASH_SIZE_BYTES == 0.
1 parent 9762b00 commit 7b07b0b

File tree

2 files changed

+275
-4
lines changed

2 files changed

+275
-4
lines changed

src/rp2_common/hardware_flash/flash.c

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "hardware/structs/ssi.h"
1313
#else
1414
#include "hardware/structs/qmi.h"
15+
#include "hardware/regs/otp_data.h"
1516
#endif
1617
#include "hardware/xip_cache.h"
1718

@@ -70,6 +71,43 @@ static void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void) {
7071

7172
#endif
7273

74+
#if PICO_RP2350
75+
// This is specifically for saving/restoring the registers modified by RP2350
76+
// flash_exit_xip() ROM func, not the entirety of the QMI window state.
77+
typedef struct flash_rp2350_qmi_save_state {
78+
uint32_t timing;
79+
uint32_t rcmd;
80+
uint32_t rfmt;
81+
} flash_rp2350_qmi_save_state_t;
82+
83+
static void __no_inline_not_in_flash_func(flash_rp2350_save_qmi_cs1)(flash_rp2350_qmi_save_state_t *state) {
84+
state->timing = qmi_hw->m[1].timing;
85+
state->rcmd = qmi_hw->m[1].rcmd;
86+
state->rfmt = qmi_hw->m[1].rfmt;
87+
}
88+
89+
static void __no_inline_not_in_flash_func(flash_rp2350_restore_qmi_cs1)(const flash_rp2350_qmi_save_state_t *state) {
90+
if (flash_devinfo_get_cs_size(1) == FLASH_DEVINFO_SIZE_NONE) {
91+
// Case 1: The RP2350 ROM sets QMI to a clean (03h read) configuration
92+
// during flash_exit_xip(), even though when CS1 is not enabled via
93+
// FLASH_DEVINFO it does not issue an XIP exit sequence to CS1. In
94+
// this case, restore the original register config for CS1 as it is
95+
// still the correct config.
96+
qmi_hw->m[1].timing = state->timing;
97+
qmi_hw->m[1].rcmd = state->rcmd;
98+
qmi_hw->m[1].rfmt = state->rfmt;
99+
} else {
100+
// Case 2: If RAM is attached to CS1, and the ROM has issued an XIP
101+
// exit sequence to it, then the ROM re-initialisation of the QMI
102+
// registers has actually not gone far enough. The old XIP write mode
103+
// is no longer valid when the QSPI RAM is returned to a serial
104+
// command state. Restore the default 02h serial write command config.
105+
qmi_hw->m[1].wfmt = QMI_M1_WFMT_RESET;
106+
qmi_hw->m[1].wcmd = QMI_M1_WCMD_RESET;
107+
}
108+
}
109+
#endif
110+
73111
//-----------------------------------------------------------------------------
74112
// Actual flash programming shims (work whether or not PICO_NO_FLASH==1)
75113

@@ -87,6 +125,10 @@ void __no_inline_not_in_flash_func(flash_range_erase)(uint32_t flash_offs, size_
87125
flash_init_boot2_copyout();
88126
// Commit any pending writes to external RAM, to avoid losing them in the subsequent flush:
89127
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
90132

91133
// No flash accesses after this point
92134
__compiler_memory_barrier();
@@ -96,6 +138,9 @@ void __no_inline_not_in_flash_func(flash_range_erase)(uint32_t flash_offs, size_
96138
flash_range_erase_func(flash_offs, count, FLASH_BLOCK_SIZE, FLASH_BLOCK_ERASE_CMD);
97139
flash_flush_cache_func(); // Note this is needed to remove CSn IO force as well as cache flushing
98140
flash_enable_xip_via_boot2();
141+
#if PICO_RP2350
142+
flash_rp2350_restore_qmi_cs1(&qmi_save);
143+
#endif
99144
}
100145

101146
void __no_inline_not_in_flash_func(flash_flush_cache)(void) {
@@ -116,6 +161,10 @@ void __no_inline_not_in_flash_func(flash_range_program)(uint32_t flash_offs, con
116161
assert(connect_internal_flash_func && flash_exit_xip_func && flash_range_program_func && flash_flush_cache_func);
117162
flash_init_boot2_copyout();
118163
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
119168

120169
__compiler_memory_barrier();
121170

@@ -124,6 +173,9 @@ void __no_inline_not_in_flash_func(flash_range_program)(uint32_t flash_offs, con
124173
flash_range_program_func(flash_offs, data, count);
125174
flash_flush_cache_func(); // Note this is needed to remove CSn IO force as well as cache flushing
126175
flash_enable_xip_via_boot2();
176+
#if PICO_RP2350
177+
flash_rp2350_restore_qmi_cs1(&qmi_save);
178+
#endif
127179
}
128180

129181
//-----------------------------------------------------------------------------
@@ -157,6 +209,10 @@ void __no_inline_not_in_flash_func(flash_do_cmd)(const uint8_t *txbuf, uint8_t *
157209
assert(connect_internal_flash_func && flash_exit_xip_func && flash_flush_cache_func);
158210
flash_init_boot2_copyout();
159211
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
160216

161217
__compiler_memory_barrier();
162218
connect_internal_flash_func();
@@ -204,6 +260,9 @@ void __no_inline_not_in_flash_func(flash_do_cmd)(const uint8_t *txbuf, uint8_t *
204260

205261
flash_flush_cache_func();
206262
flash_enable_xip_via_boot2();
263+
#if PICO_RP2350
264+
flash_rp2350_restore_qmi_cs1(&qmi_save);
265+
#endif
207266
}
208267
#endif
209268

@@ -225,3 +284,89 @@ void flash_get_unique_id(uint8_t *id_out) {
225284
id_out[i] = rxbuf[i + 1 + FLASH_RUID_DUMMY_BYTES];
226285
#endif
227286
}
287+
288+
#if !PICO_RP2040
289+
// This is a static symbol because the layout of FLASH_DEVINFO is liable to change from device to
290+
// device, so fields must have getters/setters.
291+
static io_rw_16 * flash_devinfo_ptr(void) {
292+
// Note the lookup returns a pointer to a 32-bit pointer literal in the ROM
293+
io_rw_16 **p = (io_rw_16 **) rom_data_lookup(ROM_DATA_FLASH_DEVINFO16_PTR);
294+
assert(p);
295+
return *p;
296+
}
297+
298+
static void flash_devinfo_update_field(uint16_t wdata, uint16_t mask) {
299+
// Boot RAM does not support exclusives, but does support RWTYPE SET/CLR/XOR (with byte
300+
// strobes). Can't use hw_write_masked because it performs a 32-bit write.
301+
io_rw_16 *devinfo = flash_devinfo_ptr();
302+
*hw_xor_alias(devinfo) = (*devinfo ^ wdata) & mask;
303+
}
304+
305+
// This is a RAM function because may be called during flash programming to enable save/restore of
306+
// QMI window 1 registers on RP2350:
307+
flash_devinfo_size_t __no_inline_not_in_flash_func(flash_devinfo_get_cs_size)(uint cs) {
308+
invalid_params_if(HARDWARE_FLASH, cs > 1);
309+
io_ro_16 *devinfo = (io_ro_16 *) flash_devinfo_ptr();
310+
if (cs == 0u) {
311+
#ifdef PICO_FLASH_SIZE_BYTES
312+
// A flash size explicitly specified for the build (e.g. from the board header) takes
313+
// precedence over whatever was found in OTP. Not using flash_devinfo_bytes_to_size() as
314+
// the call could be outlined, and this code must be in RAM.
315+
if (PICO_FLASH_SIZE_BYTES == 0) {
316+
return FLASH_DEVINFO_SIZE_NONE;
317+
} else {
318+
return (flash_devinfo_size_t) (
319+
__builtin_ctz(PICO_FLASH_SIZE_BYTES / 8192u) + (uint)FLASH_DEVINFO_SIZE_8K
320+
);
321+
}
322+
#else
323+
return (flash_devinfo_size_t) (
324+
(*devinfo & OTP_DATA_FLASH_DEVINFO_CS0_SIZE_BITS) >> OTP_DATA_FLASH_DEVINFO_CS0_SIZE_LSB
325+
);
326+
#endif
327+
} else {
328+
return (flash_devinfo_size_t) (
329+
(*devinfo & OTP_DATA_FLASH_DEVINFO_CS1_SIZE_BITS) >> OTP_DATA_FLASH_DEVINFO_CS1_SIZE_LSB
330+
);
331+
}
332+
}
333+
334+
void flash_devinfo_set_cs_size(uint cs, flash_devinfo_size_t size) {
335+
invalid_params_if(HARDWARE_FLASH, cs > 1);
336+
invalid_params_if(HARDWARE_FLASH, (uint)size > (uint)FLASH_DEVINFO_SIZE_MAX);
337+
uint cs_shift = cs == 0u ? OTP_DATA_FLASH_DEVINFO_CS0_SIZE_LSB : OTP_DATA_FLASH_DEVINFO_CS1_SIZE_LSB;
338+
uint cs_mask = OTP_DATA_FLASH_DEVINFO_CS0_SIZE_BITS >> OTP_DATA_FLASH_DEVINFO_CS0_SIZE_LSB;
339+
flash_devinfo_update_field(
340+
(uint)size << cs_shift,
341+
cs_mask << cs_shift
342+
);
343+
}
344+
345+
bool flash_devinfo_get_d8h_erase_supported(void) {
346+
return *flash_devinfo_ptr() & OTP_DATA_FLASH_DEVINFO_D8H_ERASE_SUPPORTED_BITS;
347+
}
348+
349+
void flash_devinfo_set_d8h_erase_supported(bool supported) {
350+
flash_devinfo_update_field(
351+
(uint)supported << OTP_DATA_FLASH_DEVINFO_D8H_ERASE_SUPPORTED_LSB,
352+
OTP_DATA_FLASH_DEVINFO_D8H_ERASE_SUPPORTED_BITS
353+
);
354+
}
355+
356+
uint flash_devinfo_get_cs_gpio(uint cs) {
357+
invalid_params_if(HARDWARE_FLASH, cs != 1);
358+
(void)cs;
359+
return (*flash_devinfo_ptr() & OTP_DATA_FLASH_DEVINFO_CS1_GPIO_BITS) >> OTP_DATA_FLASH_DEVINFO_CS1_GPIO_LSB;
360+
}
361+
362+
void flash_devinfo_set_cs_gpio(uint cs, uint gpio) {
363+
invalid_params_if(HARDWARE_FLASH, cs != 1);
364+
invalid_params_if(HARDWARE_FLASH, gpio >= NUM_BANK0_GPIOS);
365+
(void)cs;
366+
flash_devinfo_update_field(
367+
gpio << OTP_DATA_FLASH_DEVINFO_CS1_GPIO_LSB,
368+
OTP_DATA_FLASH_DEVINFO_CS1_GPIO_BITS
369+
);
370+
}
371+
372+
#endif // !PICO_RP2040

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

Lines changed: 130 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
*
1515
* \brief Low level flash programming and erase API
1616
*
17-
* Note these functions are *unsafe* if you are using both cores, and the other
18-
* is executing from flash concurrently with the operation. In this could be the
19-
* case, you must perform your own synchronisation to make sure that no XIP
20-
* accesses take place during flash programming. One option is to use the
17+
* Note these functions are *unsafe* if you are using both cores, and the other
18+
* is executing from flash concurrently with the operation. In this case, you
19+
* must perform your own synchronisation to make sure that no XIP accesses take
20+
* place during flash programming. One option is to use the
2121
* \ref multicore_lockout functions.
2222
*
2323
* Likewise they are *unsafe* if you have interrupt handlers or an interrupt
@@ -121,6 +121,132 @@ void flash_do_cmd(const uint8_t *txbuf, uint8_t *rxbuf, size_t count);
121121

122122
void flash_flush_cache(void);
123123

124+
#if !PICO_RP2040
125+
typedef enum {
126+
FLASH_DEVINFO_SIZE_NONE = 0x0,
127+
FLASH_DEVINFO_SIZE_8K = 0x1,
128+
FLASH_DEVINFO_SIZE_16K = 0x2,
129+
FLASH_DEVINFO_SIZE_32K = 0x3,
130+
FLASH_DEVINFO_SIZE_64K = 0x4,
131+
FLASH_DEVINFO_SIZE_128K = 0x5,
132+
FLASH_DEVINFO_SIZE_256K = 0x6,
133+
FLASH_DEVINFO_SIZE_512K = 0x7,
134+
FLASH_DEVINFO_SIZE_1M = 0x8,
135+
FLASH_DEVINFO_SIZE_2M = 0x9,
136+
FLASH_DEVINFO_SIZE_4M = 0xa,
137+
FLASH_DEVINFO_SIZE_8M = 0xb,
138+
FLASH_DEVINFO_SIZE_16M = 0xc,
139+
FLASH_DEVINFO_SIZE_MAX = 0xc
140+
} flash_devinfo_size_t;
141+
142+
/*! \brief Convert a flash/PSRAM size enum to an integer size in bytes
143+
* \ingroup hardware_flash
144+
*/
145+
static inline uint32_t flash_devinfo_size_to_bytes(flash_devinfo_size_t size) {
146+
if (size == FLASH_DEVINFO_SIZE_NONE) {
147+
return 0;
148+
} else {
149+
return 4096u << (uint)size;
150+
}
151+
}
152+
153+
/*! \brief Convert an integer flash/PSRAM size in bytes to a size enum, as
154+
! stored in OTP and used by the ROM.
155+
* \ingroup hardware_flash
156+
*/
157+
static inline flash_devinfo_size_t flash_devinfo_bytes_to_size(uint32_t bytes) {
158+
// Must be zero or a power of two
159+
valid_params_if(HARDWARE_FLASH, (bytes & (bytes - 1)) == 0u);
160+
uint sectors = bytes / 4096u;
161+
if (sectors <= 1u) {
162+
return FLASH_DEVINFO_SIZE_NONE;
163+
} else {
164+
return (flash_devinfo_size_t) __builtin_ctz(sectors);
165+
}
166+
}
167+
168+
/*! \brief Get the size of the QSPI device attached to chip select cs, according to FLASH_DEVINFO
169+
* \ingroup hardware_flash
170+
*
171+
* \param cs Chip select index: 0 is QMI chip select 0 (QSPI CS pin), 1 is QMI chip select 1.
172+
*
173+
* The bootrom reads the FLASH_DEVINFO OTP data entry from OTP into boot RAM during startup. This
174+
* contains basic information about the flash device which can be queried without communicating
175+
* with the external device.(There are several methods to determine the size of a QSPI device over
176+
* QSPI, but none are universally supported.)
177+
*
178+
* Since the FLASH_DEVINFO information is stored in boot RAM at runtime, it can be updated. Updates
179+
* made in this way persist until the next reboot. The ROM uses this device information to control
180+
* some low-level flash API behaviour, such as issuing an XIP exit sequence to CS 1 if its size is
181+
* nonzero.
182+
*
183+
* If the macro PICO_FLASH_SIZE_BYTES is specified, this overrides the value for chip select 0. This
184+
* can be specified in a board header if a board is always equipped with the same size of flash.
185+
*/
186+
flash_devinfo_size_t flash_devinfo_get_cs_size(uint cs);
187+
188+
/*! \brief Update the size of the QSPI device attached to chip select cs in the runtime copy
189+
* of FLASH_DEVINFO.
190+
*
191+
* \ingroup hardware_flash
192+
*
193+
* \param cs Chip select index: 0 is QMI chip select 0 (QSPI CS pin), 1 is QMI chip select 1.
194+
*
195+
* \param size The size of the attached device, or FLASH_DEVINFO_SIZE_NONE if there is none on this
196+
* chip select.
197+
*
198+
* The bootrom maintains a copy in boot RAM of the FLASH_DEVINFO information read from OTP during
199+
* startup. This function updates that copy to reflect runtime information about the sizes of
200+
* attached QSPI devices.
201+
*
202+
* This controls the behaviour of some ROM flash APIs, such as bounds checking addresses for
203+
* erase/programming in the checked_flash_op() API, or issuing an XIP exit sequence to CS 1 in
204+
* flash_exit_xip() if the size is nonzero.
205+
*/
206+
void flash_devinfo_set_cs_size(uint cs, flash_devinfo_size_t size);
207+
208+
/*! \brief Check whether all attached devices support D8h block erase with 64k size, according to
209+
* FLASH_DEVINFO.
210+
*
211+
* \ingroup hardware_flash
212+
*
213+
* This controls whether checked_flash_op() ROM API uses D8h 64k block erase where possible, for
214+
* faster erase times. If not, this ROM API always uses 20h 4k sector erase.
215+
*
216+
* The bootrom loads this flag from the OTP FLASH_DEVINFO data entry during startup, and stores it
217+
* in boot RAM. You can update the boot RAM copy based on runtime knowledge of the attached QSPI
218+
* devices.
219+
*/
220+
bool flash_devinfo_get_d8h_erase_supported(void);
221+
222+
/*! \brief Specify whether all attached devices support D8h block erase with 64k size, in the
223+
* runtime copy of FLASH_DEVINFO
224+
*
225+
* \ingroup hardware_flash
226+
*
227+
* This function updates the boot RAM copy of OTP FLASH_DEVINFO. The flag passed here is visible to
228+
* ROM APIs, and is also returned in the next call to flash_devinfo_get_d8h_erase_supported()
229+
*/
230+
void flash_devinfo_set_d8h_erase_supported(bool supported);
231+
232+
/*! \brief Check the GPIO allocated for each chip select, according to FLASH_DEVINFO
233+
* \ingroup hardware_flash
234+
*
235+
* \param cs Chip select index (only the value 1 is supported on RP2350)
236+
*/
237+
uint flash_devinfo_get_cs_gpio(uint cs);
238+
239+
/*! \brief Update the GPIO allocated for each chip select in the runtime copy of FLASH_DEVINFO
240+
* \ingroup hardware_flash
241+
*
242+
* \param cs Chip select index (only the value 1 is supported on RP2350)
243+
*
244+
* \param gpio GPIO index (must be less than NUM_BANK0_GPIOS)
245+
*/
246+
void flash_devinfo_set_cs_gpio(uint cs, uint gpio);
247+
248+
#endif // !PICO_RP2040
249+
124250
#ifdef __cplusplus
125251
}
126252
#endif

0 commit comments

Comments
 (0)