|
7 | 7 | #include "hardware/flash.h"
|
8 | 8 | #include "pico/bootrom.h"
|
9 | 9 |
|
10 |
| -#if PICO_RP2040 |
11 | 10 | #include "hardware/structs/io_qspi.h"
|
| 11 | +#if PICO_RP2040 |
12 | 12 | #include "hardware/structs/ssi.h"
|
13 | 13 | #else
|
14 | 14 | #include "hardware/structs/qmi.h"
|
|
24 | 24 | #define FLASH_RUID_DATA_BYTES FLASH_UNIQUE_ID_SIZE_BYTES
|
25 | 25 | #define FLASH_RUID_TOTAL_BYTES (1 + FLASH_RUID_DUMMY_BYTES + FLASH_RUID_DATA_BYTES)
|
26 | 26 |
|
| 27 | +void __no_inline_not_in_flash_func(flash_write_partial_internal)(uint32_t addr, const uint8_t *data, size_t size); |
| 28 | + |
27 | 29 | //-----------------------------------------------------------------------------
|
28 | 30 | // Infrastructure for reentering XIP mode after exiting for programming (take
|
29 | 31 | // a copy of boot2 before XIP exit). Calling boot2 as a function works because
|
|
32 | 34 |
|
33 | 35 | #if !PICO_NO_FLASH
|
34 | 36 |
|
| 37 | +#define FLASHCMD_PAGE_PROGRAM 0x02 |
| 38 | +#define FLASHCMD_READ_STATUS 0x05 |
| 39 | +#define FLASHCMD_WRITE_ENABLE 0x06 |
35 | 40 | #define BOOT2_SIZE_WORDS 64
|
36 | 41 |
|
| 42 | +enum outover { |
| 43 | + OUTOVER_NORMAL = 0, |
| 44 | + OUTOVER_INVERT, |
| 45 | + OUTOVER_LOW, |
| 46 | + OUTOVER_HIGH |
| 47 | +}; |
| 48 | + |
37 | 49 | static uint32_t boot2_copyout[BOOT2_SIZE_WORDS];
|
38 | 50 | static bool boot2_copyout_valid = false;
|
39 | 51 |
|
@@ -178,6 +190,30 @@ void __no_inline_not_in_flash_func(flash_range_program)(uint32_t flash_offs, con
|
178 | 190 | #endif
|
179 | 191 | }
|
180 | 192 |
|
| 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 | + |
181 | 217 | //-----------------------------------------------------------------------------
|
182 | 218 | // Lower-level flash access functions
|
183 | 219 |
|
@@ -370,3 +406,155 @@ void flash_devinfo_set_cs_gpio(uint cs, uint gpio) {
|
370 | 406 | }
|
371 | 407 |
|
372 | 408 | #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 | +} |
0 commit comments