18
18
#include <hardware/flash.h>
19
19
#include <hardware/regs/io_qspi.h>
20
20
#include <hardware/regs/pads_qspi.h>
21
+ #if PICO_RP2350
22
+ #include <hardware/structs/qmi.h>
23
+ #else
21
24
#include <hardware/structs/ssi.h>
25
+ #endif
22
26
#include <hardware/structs/xip_ctrl.h>
23
27
#include <hardware/resets.h>
24
28
#include <pico/bootrom.h>
@@ -31,8 +35,6 @@ LOG_MODULE_REGISTER(flash_rpi_pico, CONFIG_FLASH_LOG_LEVEL);
31
35
#define SECTOR_SIZE DT_PROP(DT_CHOSEN(zephyr_flash), erase_block_size)
32
36
#define ERASE_VALUE 0xff
33
37
#define FLASH_SIZE KB(CONFIG_FLASH_SIZE)
34
- #define FLASH_BASE CONFIG_FLASH_BASE_ADDRESS
35
- #define SSI_BASE_ADDRESS DT_REG_ADDR(DT_CHOSEN(zephyr_flash_controller))
36
38
37
39
static const struct flash_parameters flash_rpi_parameters = {
38
40
.write_block_size = 1 ,
@@ -58,7 +60,6 @@ enum outover {
58
60
OUTOVER_HIGH
59
61
};
60
62
61
- static ssi_hw_t * const ssi = (ssi_hw_t * )SSI_BASE_ADDRESS ;
62
63
static uint32_t boot2_copyout [BOOT2_SIZE_WORDS ];
63
64
static bool boot2_copyout_valid ;
64
65
static uint8_t flash_ram_buffer [PAGE_SIZE ];
@@ -68,8 +69,13 @@ static void __no_inline_not_in_flash_func(flash_init_boot2_copyout)(void)
68
69
if (boot2_copyout_valid ) {
69
70
return ;
70
71
}
72
+ #if PICO_RP2040
73
+ const volatile uint32_t * copy_from = (uint32_t * )XIP_BASE ;
74
+ #else
75
+ const volatile uint32_t * copy_from = (uint32_t * )BOOTRAM_BASE ;
76
+ #endif
71
77
for (int i = 0 ; i < BOOT2_SIZE_WORDS ; ++ i ) {
72
- boot2_copyout [i ] = (( uint32_t * ) FLASH_BASE ) [i ];
78
+ boot2_copyout [i ] = copy_from [i ];
73
79
}
74
80
__compiler_memory_barrier ();
75
81
boot2_copyout_valid = true;
@@ -80,20 +86,95 @@ static void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void)
80
86
((void (* )(void ))((uint32_t )boot2_copyout + 1 ))();
81
87
}
82
88
89
+ #if PICO_RP2350
90
+ typedef struct flash_rp2350_qmi_save_state {
91
+ uint32_t timing ;
92
+ uint32_t rcmd ;
93
+ uint32_t rfmt ;
94
+ } flash_rp2350_qmi_save_state_t ;
95
+
96
+ static void
97
+ __no_inline_not_in_flash_func (flash_rp2350_save_qmi_cs1 )(flash_rp2350_qmi_save_state_t * state )
98
+ {
99
+ state -> timing = qmi_hw -> m [1 ].timing ;
100
+ state -> rcmd = qmi_hw -> m [1 ].rcmd ;
101
+ state -> rfmt = qmi_hw -> m [1 ].rfmt ;
102
+ }
103
+
104
+ static void __no_inline_not_in_flash_func (flash_rp2350_restore_qmi_cs1 )(
105
+ const flash_rp2350_qmi_save_state_t * state )
106
+ {
107
+ if (flash_devinfo_get_cs_size (1 ) == FLASH_DEVINFO_SIZE_NONE ) {
108
+ /* Case 1: The RP2350 ROM sets QMI to a clean (03h read) configuration
109
+ during flash_exit_xip(), even though when CS1 is not enabled via
110
+ FLASH_DEVINFO it does not issue an XIP exit sequence to CS1. In
111
+ this case, restore the original register config for CS1 as it is
112
+ still the correct config. */
113
+ qmi_hw -> m [1 ].timing = state -> timing ;
114
+ qmi_hw -> m [1 ].rcmd = state -> rcmd ;
115
+ qmi_hw -> m [1 ].rfmt = state -> rfmt ;
116
+ } else {
117
+ /* Case 2: If RAM is attached to CS1, and the ROM has issued an XIP
118
+ exit sequence to it, then the ROM re-initialisation of the QMI
119
+ registers has actually not gone far enough. The old XIP write mode
120
+ is no longer valid when the QSPI RAM is returned to a serial
121
+ command state. Restore the default 02h serial write command config.
122
+ */
123
+ qmi_hw -> m [1 ].wfmt = QMI_M1_WFMT_RESET ;
124
+ qmi_hw -> m [1 ].wcmd = QMI_M1_WCMD_RESET ;
125
+ }
126
+ }
127
+ #else
83
128
void __no_inline_not_in_flash_func (flash_cs_force )(enum outover over )
84
129
{
85
130
io_rw_32 * reg = (io_rw_32 * ) (IO_QSPI_BASE + IO_QSPI_GPIO_QSPI_SS_CTRL_OFFSET );
86
131
* reg = (* reg & ~IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS )
87
132
| (over << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB );
88
133
(void ) * reg ;
89
134
}
135
+ #endif
90
136
91
137
int __no_inline_not_in_flash_func (flash_was_aborted )()
92
138
{
93
139
return * (io_rw_32 * ) (IO_QSPI_BASE + IO_QSPI_GPIO_QSPI_SD1_CTRL_OFFSET )
94
140
& IO_QSPI_GPIO_QSPI_SD1_CTRL_INOVER_BITS ;
95
141
}
96
142
143
+ #if PICO_RP2350
144
+ uint __no_inline_not_in_flash_func (flash_put_get )(uint cs , const uint8_t * tx , uint8_t * rx ,
145
+ size_t count )
146
+ {
147
+ /* Assert chip select, and enable direct mode. Anything queued in TX FIFO will start now. */
148
+ uint32_t csr_toggle_mask = (QMI_DIRECT_CSR_ASSERT_CS0N_BITS << cs ) | QMI_DIRECT_CSR_EN_BITS ;
149
+
150
+ hw_xor_bits (& qmi_hw -> direct_csr , csr_toggle_mask );
151
+
152
+ size_t tx_count = count ;
153
+ size_t rx_count = count ;
154
+ while (tx_count || rx_count ) {
155
+ uint32_t status = qmi_hw -> direct_csr ;
156
+ if (tx_count && !(status & QMI_DIRECT_CSR_TXFULL_BITS )) {
157
+ qmi_hw -> direct_tx = (uint32_t )(tx ? * tx ++ : 0 );
158
+ -- tx_count ;
159
+ }
160
+ if (rx_count && !(status & QMI_DIRECT_CSR_RXEMPTY_BITS )) {
161
+ uint8_t rxbyte = (uint8_t )qmi_hw -> direct_rx ;
162
+ if (rx ) {
163
+ * rx ++ = rxbyte ;
164
+ }
165
+ -- rx_count ;
166
+ }
167
+ }
168
+
169
+ /* Wait for BUSY as there may be no RX data at all, e.g. for single-byte SPI commands */
170
+ while (qmi_hw -> direct_csr & QMI_DIRECT_CSR_BUSY_BITS )
171
+ ;
172
+
173
+ /* Disable direct-mode interface and deassert chip select */
174
+ hw_xor_bits (& qmi_hw -> direct_csr , csr_toggle_mask );
175
+ return cs ;
176
+ }
177
+ #else
97
178
void __no_inline_not_in_flash_func (flash_put_get )(const uint8_t * tx , uint8_t * rx , size_t count ,
98
179
size_t rx_skip )
99
180
{
@@ -110,12 +191,12 @@ void __no_inline_not_in_flash_func(flash_put_get)(const uint8_t *tx, uint8_t *rx
110
191
rx_level = ssi_hw -> rxflr ;
111
192
did_something = false;
112
193
if (tx_count && tx_level + rx_level < max_in_flight ) {
113
- ssi -> dr0 = (uint32_t ) (tx ? * tx ++ : 0 );
194
+ ssi_hw -> dr0 = (uint32_t )(tx ? * tx ++ : 0 );
114
195
-- tx_count ;
115
196
did_something = true;
116
197
}
117
198
if (rx_level ) {
118
- rxbyte = ssi -> dr0 ;
199
+ rxbyte = ssi_hw -> dr0 ;
119
200
did_something = true;
120
201
if (rx_skip ) {
121
202
-- rx_skip ;
@@ -133,37 +214,92 @@ void __no_inline_not_in_flash_func(flash_put_get)(const uint8_t *tx, uint8_t *rx
133
214
}
134
215
flash_cs_force (OUTOVER_HIGH );
135
216
}
217
+ #endif
136
218
219
+ #if PICO_RP2350
220
+ uint __no_inline_not_in_flash_func (flash_put_get_wrapper )(uint cs , uint8_t cmd , const uint8_t * tx ,
221
+ uint8_t * rx , size_t count )
222
+ {
223
+ qmi_hw -> direct_tx = cmd | QMI_DIRECT_TX_NOPUSH_BITS ;
224
+ return flash_put_get (cs , tx , rx , count );
225
+ }
226
+ #else
137
227
void __no_inline_not_in_flash_func (flash_put_get_wrapper )(uint8_t cmd , const uint8_t * tx ,
138
228
uint8_t * rx , size_t count )
139
229
{
140
230
flash_cs_force (OUTOVER_LOW );
141
- ssi -> dr0 = cmd ;
231
+ ssi_hw -> dr0 = cmd ;
142
232
flash_put_get (tx , rx , count , 1 );
143
233
}
234
+ #endif
235
+
236
+ #if PICO_RP2350
237
+ static ALWAYS_INLINE uint flash_wait_ready (uint cs )
238
+ {
239
+ uint8_t status_reg ;
240
+
241
+ do {
242
+ cs = flash_put_get_wrapper (cs , FLASHCMD_READ_STATUS , NULL , & status_reg , 1 );
243
+ } while (status_reg & 0x1 && !flash_was_aborted ());
244
+ return cs ;
245
+ }
246
+ #else
247
+ static ALWAYS_INLINE void flash_wait_ready ()
248
+ {
249
+ uint8_t status_reg ;
250
+
251
+ do {
252
+ flash_put_get_wrapper (FLASHCMD_READ_STATUS , NULL , & status_reg , 1 );
253
+ } while (status_reg & 0x1 && !flash_was_aborted ());
254
+ }
255
+ #endif
256
+
257
+ #if PICO_RP2350
258
+ static ALWAYS_INLINE uint flash_enable_write (uint cs )
259
+ {
260
+ qmi_hw -> direct_tx = FLASHCMD_WRITE_ENABLE | QMI_DIRECT_TX_NOPUSH_BITS ;
261
+ return flash_put_get (cs , NULL , NULL , 0 );
262
+ }
263
+ #else
264
+ static ALWAYS_INLINE void flash_enable_write ()
265
+ {
266
+ flash_put_get_wrapper (FLASHCMD_WRITE_ENABLE , NULL , NULL , 0 );
267
+ }
268
+ #endif
144
269
145
270
static ALWAYS_INLINE void flash_put_cmd_addr (uint8_t cmd , uint32_t addr )
146
271
{
272
+ #if PICO_RP2350
273
+ addr = __builtin_bswap32 (addr & ((1u << 24 ) - 1 ));
274
+ addr |= cmd ;
275
+ qmi_hw -> direct_tx =
276
+ ((addr << 16 ) >> 16 ) | QMI_DIRECT_TX_NOPUSH_BITS | QMI_DIRECT_TX_DWIDTH_BITS ;
277
+ qmi_hw -> direct_tx = (addr >> 16 ) | QMI_DIRECT_TX_NOPUSH_BITS | QMI_DIRECT_TX_DWIDTH_BITS ;
278
+ #else
147
279
flash_cs_force (OUTOVER_LOW );
148
280
addr |= cmd << 24 ;
149
281
for (int i = 0 ; i < 4 ; ++ i ) {
150
- ssi -> dr0 = addr >> 24 ;
282
+ ssi_hw -> dr0 = addr >> 24 ;
151
283
addr <<= 8 ;
152
284
}
285
+ #endif
153
286
}
154
287
155
288
void __no_inline_not_in_flash_func (flash_write_partial_internal )(uint32_t addr , const uint8_t * data ,
156
289
size_t size )
157
290
{
158
- uint8_t status_reg ;
159
-
160
- flash_put_get_wrapper (FLASHCMD_WRITE_ENABLE , NULL , NULL , 0 );
291
+ #if PICO_RP2350
292
+ uint cs = (addr >> 24 ) & 0x1u ;
293
+ cs = flash_enable_write (cs );
294
+ flash_put_cmd_addr (FLASHCMD_PAGE_PROGRAM , addr );
295
+ flash_put_get (cs , data , NULL , size );
296
+ (void )flash_wait_ready (cs );
297
+ #else
298
+ flash_enable_write ();
161
299
flash_put_cmd_addr (FLASHCMD_PAGE_PROGRAM , addr );
162
300
flash_put_get (data , NULL , size , 4 );
163
-
164
- do {
165
- flash_put_get_wrapper (FLASHCMD_READ_STATUS , NULL , & status_reg , 1 );
166
- } while (status_reg & 0x1 && !flash_was_aborted ());
301
+ flash_wait_ready ();
302
+ #endif
167
303
}
168
304
169
305
void __no_inline_not_in_flash_func (flash_write_partial )(uint32_t flash_offs , const uint8_t * data ,
@@ -177,6 +313,10 @@ void __no_inline_not_in_flash_func(flash_write_partial)(uint32_t flash_offs, con
177
313
rom_func_lookup_inline (ROM_FUNC_FLASH_FLUSH_CACHE );
178
314
179
315
flash_init_boot2_copyout ();
316
+ #if PICO_RP2350
317
+ flash_rp2350_qmi_save_state_t qmi_save ;
318
+ flash_rp2350_save_qmi_cs1 (& qmi_save );
319
+ #endif
180
320
181
321
__compiler_memory_barrier ();
182
322
@@ -185,6 +325,9 @@ void __no_inline_not_in_flash_func(flash_write_partial)(uint32_t flash_offs, con
185
325
flash_write_partial_internal (flash_offs , data , count );
186
326
flush_cache ();
187
327
flash_enable_xip_via_boot2 ();
328
+ #if PICO_RP2350
329
+ flash_rp2350_restore_qmi_cs1 (& qmi_save );
330
+ #endif
188
331
}
189
332
190
333
static bool is_valid_range (off_t offset , uint32_t size )
0 commit comments