Skip to content

Commit b1e177f

Browse files
committed
drivers: flash: rpi_pico: add support for rp2350 flash controller
The Raspberry Pi Pico 2 uses a QMI flash controller, which differs from the SSI controller used in the original Pico. Zephyr already has support for the SSI controller, but lacked support for QMI. This change adds the QMI flash controller implementation in the flash_rpi_pico.c driver file. Additionally, the RP2350 SoC devicetree file (rp2350.dtsi) has been updated to enable and describe the flash controller for Pico 2. Signed-off-by: Hanan Arshad <[email protected]>
1 parent ca182ed commit b1e177f

File tree

3 files changed

+160
-17
lines changed

3 files changed

+160
-17
lines changed

boards/raspberrypi/rpi_pico2/rpi_pico2.dtsi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
chosen {
1717
zephyr,sram = &sram0;
1818
zephyr,flash = &flash0;
19+
zephyr,flash-controller = &qmi;
1920
zephyr,console = &uart0;
2021
zephyr,shell-uart = &uart0;
2122
zephyr,code-partition = &code_partition;

drivers/flash/flash_rpi_pico.c

Lines changed: 158 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@
1818
#include <hardware/flash.h>
1919
#include <hardware/regs/io_qspi.h>
2020
#include <hardware/regs/pads_qspi.h>
21+
#if PICO_RP2350
22+
#include <hardware/structs/qmi.h>
23+
#else
2124
#include <hardware/structs/ssi.h>
25+
#endif
2226
#include <hardware/structs/xip_ctrl.h>
2327
#include <hardware/resets.h>
2428
#include <pico/bootrom.h>
@@ -31,8 +35,6 @@ LOG_MODULE_REGISTER(flash_rpi_pico, CONFIG_FLASH_LOG_LEVEL);
3135
#define SECTOR_SIZE DT_PROP(DT_CHOSEN(zephyr_flash), erase_block_size)
3236
#define ERASE_VALUE 0xff
3337
#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))
3638

3739
static const struct flash_parameters flash_rpi_parameters = {
3840
.write_block_size = 1,
@@ -58,7 +60,6 @@ enum outover {
5860
OUTOVER_HIGH
5961
};
6062

61-
static ssi_hw_t *const ssi = (ssi_hw_t *)SSI_BASE_ADDRESS;
6263
static uint32_t boot2_copyout[BOOT2_SIZE_WORDS];
6364
static bool boot2_copyout_valid;
6465
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)
6869
if (boot2_copyout_valid) {
6970
return;
7071
}
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
7177
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];
7379
}
7480
__compiler_memory_barrier();
7581
boot2_copyout_valid = true;
@@ -80,20 +86,95 @@ static void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void)
8086
((void (*)(void))((uint32_t)boot2_copyout+1))();
8187
}
8288

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
83128
void __no_inline_not_in_flash_func(flash_cs_force)(enum outover over)
84129
{
85130
io_rw_32 *reg = (io_rw_32 *) (IO_QSPI_BASE + IO_QSPI_GPIO_QSPI_SS_CTRL_OFFSET);
86131
*reg = (*reg & ~IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS)
87132
| (over << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB);
88133
(void) *reg;
89134
}
135+
#endif
90136

91137
int __no_inline_not_in_flash_func(flash_was_aborted)()
92138
{
93139
return *(io_rw_32 *) (IO_QSPI_BASE + IO_QSPI_GPIO_QSPI_SD1_CTRL_OFFSET)
94140
& IO_QSPI_GPIO_QSPI_SD1_CTRL_INOVER_BITS;
95141
}
96142

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
97178
void __no_inline_not_in_flash_func(flash_put_get)(const uint8_t *tx, uint8_t *rx, size_t count,
98179
size_t rx_skip)
99180
{
@@ -110,12 +191,12 @@ void __no_inline_not_in_flash_func(flash_put_get)(const uint8_t *tx, uint8_t *rx
110191
rx_level = ssi_hw->rxflr;
111192
did_something = false;
112193
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);
114195
--tx_count;
115196
did_something = true;
116197
}
117198
if (rx_level) {
118-
rxbyte = ssi->dr0;
199+
rxbyte = ssi_hw->dr0;
119200
did_something = true;
120201
if (rx_skip) {
121202
--rx_skip;
@@ -133,37 +214,92 @@ void __no_inline_not_in_flash_func(flash_put_get)(const uint8_t *tx, uint8_t *rx
133214
}
134215
flash_cs_force(OUTOVER_HIGH);
135216
}
217+
#endif
136218

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
137227
void __no_inline_not_in_flash_func(flash_put_get_wrapper)(uint8_t cmd, const uint8_t *tx,
138228
uint8_t *rx, size_t count)
139229
{
140230
flash_cs_force(OUTOVER_LOW);
141-
ssi->dr0 = cmd;
231+
ssi_hw->dr0 = cmd;
142232
flash_put_get(tx, rx, count, 1);
143233
}
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
144269

145270
static ALWAYS_INLINE void flash_put_cmd_addr(uint8_t cmd, uint32_t addr)
146271
{
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
147279
flash_cs_force(OUTOVER_LOW);
148280
addr |= cmd << 24;
149281
for (int i = 0; i < 4; ++i) {
150-
ssi->dr0 = addr >> 24;
282+
ssi_hw->dr0 = addr >> 24;
151283
addr <<= 8;
152284
}
285+
#endif
153286
}
154287

155288
void __no_inline_not_in_flash_func(flash_write_partial_internal)(uint32_t addr, const uint8_t *data,
156289
size_t size)
157290
{
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();
161299
flash_put_cmd_addr(FLASHCMD_PAGE_PROGRAM, addr);
162300
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
167303
}
168304

169305
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
177313
rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE);
178314

179315
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
180320

181321
__compiler_memory_barrier();
182322

@@ -185,6 +325,9 @@ void __no_inline_not_in_flash_func(flash_write_partial)(uint32_t flash_offs, con
185325
flash_write_partial_internal(flash_offs, data, count);
186326
flush_cache();
187327
flash_enable_xip_via_boot2();
328+
#if PICO_RP2350
329+
flash_rp2350_restore_qmi_cs1(&qmi_save);
330+
#endif
188331
}
189332

190333
static bool is_valid_range(off_t offset, uint32_t size)

dts/arm/raspberrypi/rpi_pico/rp2350.dtsi

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@
192192

193193
qmi: flash-controller@400d0000 {
194194
compatible = "raspberrypi,pico-flash-controller";
195-
reg = <0x400d0000 0xfc>;
195+
reg = <0x400d0000 0x54>;
196196

197197
#address-cells = <1>;
198198
#size-cells = <1>;
@@ -202,7 +202,6 @@
202202
write-block-size = <1>;
203203
erase-block-size = <DT_SIZE_K(4)>;
204204
};
205-
status = "disabled";
206205
};
207206

208207
reset: reset-controller@40020000 {

0 commit comments

Comments
 (0)