Skip to content

Commit 5a981c7

Browse files
authored
Merge pull request #11 from hanan619/zephyr
hal_rpi_pico: flash: Move partial write support for RP2040 and add support for RP2350
2 parents 7b57b24 + 5d7744c commit 5a981c7

File tree

2 files changed

+202
-1
lines changed

2 files changed

+202
-1
lines changed

src/rp2_common/hardware_flash/flash.c

Lines changed: 189 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
#include "hardware/flash.h"
88
#include "pico/bootrom.h"
99

10-
#if PICO_RP2040
1110
#include "hardware/structs/io_qspi.h"
11+
#if PICO_RP2040
1212
#include "hardware/structs/ssi.h"
1313
#else
1414
#include "hardware/structs/qmi.h"
@@ -24,6 +24,8 @@
2424
#define FLASH_RUID_DATA_BYTES FLASH_UNIQUE_ID_SIZE_BYTES
2525
#define FLASH_RUID_TOTAL_BYTES (1 + FLASH_RUID_DUMMY_BYTES + FLASH_RUID_DATA_BYTES)
2626

27+
void __no_inline_not_in_flash_func(flash_write_partial_internal)(uint32_t addr, const uint8_t *data, size_t size);
28+
2729
//-----------------------------------------------------------------------------
2830
// Infrastructure for reentering XIP mode after exiting for programming (take
2931
// a copy of boot2 before XIP exit). Calling boot2 as a function works because
@@ -32,8 +34,18 @@
3234

3335
#if !PICO_NO_FLASH
3436

37+
#define FLASHCMD_PAGE_PROGRAM 0x02
38+
#define FLASHCMD_READ_STATUS 0x05
39+
#define FLASHCMD_WRITE_ENABLE 0x06
3540
#define BOOT2_SIZE_WORDS 64
3641

42+
enum outover {
43+
OUTOVER_NORMAL = 0,
44+
OUTOVER_INVERT,
45+
OUTOVER_LOW,
46+
OUTOVER_HIGH
47+
};
48+
3749
static uint32_t boot2_copyout[BOOT2_SIZE_WORDS];
3850
static bool boot2_copyout_valid = false;
3951

@@ -178,6 +190,30 @@ void __no_inline_not_in_flash_func(flash_range_program)(uint32_t flash_offs, con
178190
#endif
179191
}
180192

193+
void __no_inline_not_in_flash_func(flash_write_partial)(uint32_t flash_offs, const uint8_t *data, size_t count) {
194+
rom_connect_internal_flash_fn connect_internal_flash_func = (rom_connect_internal_flash_fn)rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH);
195+
rom_flash_exit_xip_fn flash_exit_xip_func = (rom_flash_exit_xip_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP);
196+
rom_flash_flush_cache_fn flash_flush_cache_func = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE);
197+
assert(connect_internal_flash_func && flash_exit_xip_func && flash_flush_cache_func);
198+
flash_init_boot2_copyout();
199+
xip_cache_clean_all();
200+
#if PICO_RP2350
201+
flash_rp2350_qmi_save_state_t qmi_save;
202+
flash_rp2350_save_qmi_cs1(&qmi_save);
203+
#endif
204+
205+
__compiler_memory_barrier();
206+
207+
connect_internal_flash_func();
208+
flash_exit_xip_func();
209+
flash_write_partial_internal(flash_offs, data, count);
210+
flash_flush_cache_func(); // Note this is needed to remove CSn IO force as well as cache flushing
211+
flash_enable_xip_via_boot2();
212+
#if PICO_RP2350
213+
flash_rp2350_restore_qmi_cs1(&qmi_save);
214+
#endif
215+
}
216+
181217
//-----------------------------------------------------------------------------
182218
// Lower-level flash access functions
183219

@@ -370,3 +406,155 @@ void flash_devinfo_set_cs_gpio(uint cs, uint gpio) {
370406
}
371407

372408
#endif // !PICO_RP2040
409+
//-----------------------------------------------------------------------------
410+
/**
411+
* Low level flash functions are based on:
412+
* github.com/raspberrypi/pico-bootrom-rp2040/blob/master/bootrom/program_flash_generic.c,
413+
* github.com/raspberrypi/pico-bootrom-rp2350/blob/master/src/main/arm/varm_generic_flash.c
414+
*/
415+
static int __no_inline_not_in_flash_func(flash_was_aborted)()
416+
{
417+
return *(io_rw_32 *)(IO_QSPI_BASE + IO_QSPI_GPIO_QSPI_SD1_CTRL_OFFSET) &
418+
IO_QSPI_GPIO_QSPI_SD1_CTRL_INOVER_BITS;
419+
}
420+
421+
#if PICO_RP2350
422+
static uint __no_inline_not_in_flash_func(flash_put_get)(uint cs, const uint8_t *tx, uint8_t *rx, size_t count)
423+
{
424+
/* Assert chip select, and enable direct mode. Anything queued in TX FIFO will start now. */
425+
uint32_t csr_toggle_mask = (QMI_DIRECT_CSR_ASSERT_CS0N_BITS << cs) | QMI_DIRECT_CSR_EN_BITS;
426+
427+
hw_xor_bits(&qmi_hw->direct_csr, csr_toggle_mask);
428+
429+
size_t tx_count = count;
430+
size_t rx_count = count;
431+
432+
while (tx_count || rx_count) {
433+
uint32_t status = qmi_hw->direct_csr;
434+
435+
if (tx_count && !(status & QMI_DIRECT_CSR_TXFULL_BITS)) {
436+
qmi_hw->direct_tx = (uint32_t)(tx ? *tx++ : 0);
437+
--tx_count;
438+
}
439+
if (rx_count && !(status & QMI_DIRECT_CSR_RXEMPTY_BITS)) {
440+
uint8_t rxbyte = (uint8_t)qmi_hw->direct_rx;
441+
442+
if (rx) {
443+
*rx++ = rxbyte;
444+
}
445+
--rx_count;
446+
}
447+
}
448+
449+
/* Wait for BUSY as there may be no RX data at all, e.g. for single-byte SPI commands */
450+
while (qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) {
451+
}
452+
453+
/* Disable direct-mode interface and deassert chip select */
454+
hw_xor_bits(&qmi_hw->direct_csr, csr_toggle_mask);
455+
return cs;
456+
}
457+
#else
458+
static void __no_inline_not_in_flash_func(flash_put_get)(const uint8_t *tx, uint8_t *rx, size_t count, size_t rx_skip)
459+
{
460+
const uint max_in_flight = 16 - 2;
461+
size_t tx_count = count;
462+
size_t rx_count = count;
463+
bool did_something;
464+
uint32_t tx_level;
465+
uint32_t rx_level;
466+
uint8_t rxbyte;
467+
468+
while (tx_count || rx_skip || rx_count) {
469+
tx_level = ssi_hw->txflr;
470+
rx_level = ssi_hw->rxflr;
471+
did_something = false;
472+
if (tx_count && tx_level + rx_level < max_in_flight) {
473+
ssi_hw->dr0 = (uint32_t)(tx ? *tx++ : 0);
474+
--tx_count;
475+
did_something = true;
476+
}
477+
if (rx_level) {
478+
rxbyte = ssi_hw->dr0;
479+
did_something = true;
480+
if (rx_skip) {
481+
--rx_skip;
482+
} else {
483+
if (rx) {
484+
*rx++ = rxbyte;
485+
}
486+
--rx_count;
487+
}
488+
}
489+
490+
if (!did_something && __builtin_expect(flash_was_aborted(), 0)) {
491+
break;
492+
}
493+
}
494+
flash_cs_force(OUTOVER_HIGH);
495+
}
496+
#endif
497+
498+
static inline void flash_wait_ready(uint cs)
499+
{
500+
uint8_t status_reg;
501+
#if PICO_RP2040
502+
__unused uint ignore = cs;
503+
#endif
504+
505+
do {
506+
#if PICO_RP2350
507+
qmi_hw->direct_tx = FLASHCMD_READ_STATUS | QMI_DIRECT_TX_NOPUSH_BITS;
508+
cs = flash_put_get(cs, NULL, &status_reg, 1);
509+
#else
510+
flash_cs_force(OUTOVER_LOW);
511+
ssi_hw->dr0 = FLASHCMD_READ_STATUS;
512+
flash_put_get(NULL, &status_reg, 1, 1);
513+
#endif
514+
} while (status_reg & 0x1 && !flash_was_aborted());
515+
}
516+
517+
static inline void flash_enable_write(uint cs)
518+
{
519+
#if PICO_RP2350
520+
qmi_hw->direct_tx = FLASHCMD_WRITE_ENABLE | QMI_DIRECT_TX_NOPUSH_BITS;
521+
flash_put_get(cs, NULL, NULL, 0);
522+
#else
523+
__unused uint ignore = cs;
524+
flash_cs_force(OUTOVER_LOW);
525+
ssi_hw->dr0 = FLASHCMD_WRITE_ENABLE;
526+
flash_put_get(NULL, NULL, 0, 1);
527+
#endif
528+
}
529+
530+
static inline void flash_put_cmd_addr(uint8_t cmd, uint32_t addr)
531+
{
532+
#if PICO_RP2350
533+
addr = __builtin_bswap32(addr & ((1u << 24) - 1));
534+
addr |= cmd;
535+
qmi_hw->direct_tx =
536+
((addr << 16) >> 16) | QMI_DIRECT_TX_NOPUSH_BITS | QMI_DIRECT_TX_DWIDTH_BITS;
537+
qmi_hw->direct_tx = (addr >> 16) | QMI_DIRECT_TX_NOPUSH_BITS | QMI_DIRECT_TX_DWIDTH_BITS;
538+
#else
539+
flash_cs_force(OUTOVER_LOW);
540+
addr |= cmd << 24;
541+
for (int i = 0; i < 4; ++i) {
542+
ssi_hw->dr0 = addr >> 24;
543+
addr <<= 8;
544+
}
545+
#endif
546+
}
547+
548+
void __no_inline_not_in_flash_func(flash_write_partial_internal)(uint32_t addr, const uint8_t *data, size_t size)
549+
{
550+
uint cs = (addr >> 24) & 0x1u;
551+
552+
flash_enable_write(cs);
553+
flash_put_cmd_addr(FLASHCMD_PAGE_PROGRAM, addr);
554+
#if PICO_RP2350
555+
flash_put_get(cs, data, NULL, size);
556+
#else
557+
flash_put_get(data, NULL, size, 4);
558+
#endif
559+
flash_wait_ready(cs);
560+
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,19 @@ void flash_range_erase(uint32_t flash_offs, size_t count);
8282

8383
void flash_range_program(uint32_t flash_offs, const uint8_t *data, size_t count);
8484

85+
/*! \brief Write partial flash data
86+
* \ingroup hardware_flash
87+
*
88+
* \param flash_offs Flash address of the first byte to be written. Can be any address within a flash page.
89+
* \param data Pointer to the data to write into flash
90+
* \param count Number of bytes to write. Can be less than a full flash page, but must not exceed page size.
91+
*
92+
* @note This function allows writing a portion of a flash page.
93+
* If the write crosses a page boundary, behavior is undefined.
94+
* Caller must ensure the range stays within a single flash page.
95+
*/
96+
void flash_write_partial(uint32_t flash_offs, const uint8_t *data, size_t count);
97+
8598
/*! \brief Get flash unique 64 bit identifier
8699
* \ingroup hardware_flash
87100
*

0 commit comments

Comments
 (0)