diff --git a/docs/platformdev_rp2040.md b/docs/platformdev_rp2040.md index 9426efa29a..d690ebebf8 100644 --- a/docs/platformdev_rp2040.md +++ b/docs/platformdev_rp2040.md @@ -2,17 +2,17 @@ The following table shows the current driver status for peripherals on RP2040 MCUs: -| System | Support | -| ------------------------------------ | ---------------------------------------------- | -| [ADC driver](adc_driver.md) | Support planned (no ETA) | -| [Audio](audio_driver.md) | Support planned (no ETA) | -| [I2C driver](i2c_driver.md) | :heavy_check_mark: | -| [SPI driver](spi_driver.md) | :heavy_check_mark: | -| [WS2812 driver](ws2812_driver.md) | :heavy_check_mark: using `PIO` driver | -| [External EEPROMs](eeprom_driver.md) | :heavy_check_mark: using `I2C` or `SPI` driver | -| [EEPROM emulation](eeprom_driver.md) | Support planned (no ETA) | -| [serial driver](serial_driver.md) | :heavy_check_mark: using `SIO` or `PIO` driver | -| [UART driver](uart_driver.md) | Support planned (no ETA) | +| System | Support | +| ---------------------------------------------------------------- | ---------------------------------------------- | +| [ADC driver](adc_driver.md) | Support planned (no ETA) | +| [Audio](audio_driver.md) | Support planned (no ETA) | +| [I2C driver](i2c_driver.md) | :heavy_check_mark: | +| [SPI driver](spi_driver.md) | :heavy_check_mark: | +| [WS2812 driver](ws2812_driver.md) | :heavy_check_mark: using `PIO` driver | +| [External EEPROMs](eeprom_driver.md) | :heavy_check_mark: using `I2C` or `SPI` driver | +| [EEPROM emulation](eeprom_driver.md#wear_leveling-configuration) | :heavy_check_mark: | +| [serial driver](serial_driver.md) | :heavy_check_mark: using `SIO` or `PIO` driver | +| [UART driver](uart_driver.md) | Support planned (no ETA) | ## GPIO diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c b/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c index 5306179ecf..7bfa0e0b85 100644 --- a/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c +++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c @@ -1,6 +1,7 @@ /** * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * Copyright (c) 2022 Nick Brassel (@tzarc) + * Copyright (c) 2022 Stefan Kerkmann (@KarlK90) * * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,32 +12,25 @@ #include "hardware/structs/ssi.h" #include "hardware/structs/ioqspi.h" -#define BOOT2_SIZE_WORDS 64 +#ifndef WEAR_LEVELING_RP2040_FLASH_BULK_COUNT +# define WEAR_LEVELING_RP2040_FLASH_BULK_COUNT 64 +#endif // WEAR_LEVELING_RP2040_FLASH_BULK_COUNT + #define FLASHCMD_PAGE_PROGRAM 0x02 #define FLASHCMD_READ_STATUS 0x05 #define FLASHCMD_WRITE_ENABLE 0x06 +extern uint8_t BOOT2_ROM[256]; +static uint32_t BOOT2_ROM_RAM[64]; + static ssi_hw_t *const ssi = (ssi_hw_t *)XIP_SSI_BASE; // Sanity check -#undef static_assert -#define static_assert _Static_assert check_hw_layout(ssi_hw_t, ssienr, SSI_SSIENR_OFFSET); check_hw_layout(ssi_hw_t, spi_ctrlr0, SSI_SPI_CTRLR0_OFFSET); -static uint32_t boot2_copyout[BOOT2_SIZE_WORDS]; -static bool boot2_copyout_valid = false; - -static void __no_inline_not_in_flash_func(flash_init_boot2_copyout)(void) { - if (boot2_copyout_valid) return; - for (int i = 0; i < BOOT2_SIZE_WORDS; ++i) - boot2_copyout[i] = ((uint32_t *)XIP_BASE)[i]; - __compiler_memory_barrier(); - boot2_copyout_valid = true; -} - static void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void) { - ((void (*)(void))boot2_copyout + 1)(); + ((void (*)(void))BOOT2_ROM_RAM + 1)(); } // Bitbanging the chip select using IO overrides, in case RAM-resident IRQs @@ -126,13 +120,11 @@ static void __no_inline_not_in_flash_func(flash_enable_write)(void) { _flash_do_cmd(FLASHCMD_WRITE_ENABLE, NULL, NULL, 0); } -static void __no_inline_not_in_flash_func(pico_program_u16)(uint32_t flash_offs, uint16_t data) { +static void __no_inline_not_in_flash_func(pico_program_u16)(uint32_t flash_address, uint16_t value) { rom_connect_internal_flash_fn connect_internal_flash = (rom_connect_internal_flash_fn)rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH); rom_flash_exit_xip_fn flash_exit_xip = (rom_flash_exit_xip_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP); - rom_flash_range_program_fn flash_range_program = (rom_flash_range_program_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_RANGE_PROGRAM); rom_flash_flush_cache_fn flash_flush_cache = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE); - assert(connect_internal_flash && flash_exit_xip && flash_range_program && flash_flush_cache); - flash_init_boot2_copyout(); + assert(connect_internal_flash && flash_exit_xip && flash_flush_cache); __compiler_memory_barrier(); @@ -140,8 +132,8 @@ static void __no_inline_not_in_flash_func(pico_program_u16)(uint32_t flash_offs, flash_exit_xip(); flash_enable_write(); - flash_put_cmd_addr(FLASHCMD_PAGE_PROGRAM, flash_offs); - flash_put_get((uint8_t *)&data, NULL, 2, 4); + flash_put_cmd_addr(FLASHCMD_PAGE_PROGRAM, flash_address); + flash_put_get((uint8_t *)&value, NULL, 2, 4); flash_wait_ready(); flash_flush_cache(); // Note this is needed to remove CSn IO force as well @@ -149,6 +141,37 @@ static void __no_inline_not_in_flash_func(pico_program_u16)(uint32_t flash_offs, flash_enable_xip_via_boot2(); } +static void __no_inline_not_in_flash_func(pico_program_bulk)(uint32_t flash_address, uint16_t *values, size_t item_count) { + rom_connect_internal_flash_fn connect_internal_flash = (rom_connect_internal_flash_fn)rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH); + rom_flash_exit_xip_fn flash_exit_xip = (rom_flash_exit_xip_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP); + rom_flash_flush_cache_fn flash_flush_cache = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE); + assert(connect_internal_flash && flash_exit_xip && flash_flush_cache); + + static uint16_t bulk_write_buffer[WEAR_LEVELING_RP2040_FLASH_BULK_COUNT]; + + while (item_count) { + size_t batch_size = MIN(item_count, WEAR_LEVELING_RP2040_FLASH_BULK_COUNT); + for (int i = 0; i < batch_size; i++, values++, item_count--) { + bulk_write_buffer[i] = ~(*values); + } + __compiler_memory_barrier(); + + connect_internal_flash(); + flash_exit_xip(); + flash_enable_write(); + + for (int i = 0; i < batch_size; i++) { + flash_put_cmd_addr(FLASHCMD_PAGE_PROGRAM, flash_address); + flash_put_get((uint8_t *)&bulk_write_buffer[i], NULL, 2, 4); + flash_wait_ready(); + flash_address += BACKING_STORE_WRITE_SIZE; + } + + flash_flush_cache(); + flash_enable_xip_via_boot2(); + } +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // QMK Wear-Leveling Backing Store implementation @@ -157,10 +180,12 @@ static void __no_inline_not_in_flash_func(pico_program_u16)(uint32_t flash_offs, #include "wear_leveling.h" #include "wear_leveling_internal.h" -static int intr_stat; +static int interrupts; bool backing_store_init(void) { bs_dprintf("Init\n"); + memcpy(BOOT2_ROM_RAM, BOOT2_ROM, sizeof(BOOT2_ROM)); + __compiler_memory_barrier(); return true; } @@ -174,11 +199,11 @@ bool backing_store_erase(void) { uint32_t start = timer_read32(); #endif - static_assert(WEAR_LEVELING_BACKING_SIZE % 4096 == 0, "Backing size must be a multiple of 4096"); + _Static_assert(WEAR_LEVELING_BACKING_SIZE % 4096 == 0, "Backing size must be a multiple of 4096"); - intr_stat = save_and_disable_interrupts(); + interrupts = save_and_disable_interrupts(); flash_range_erase((WEAR_LEVELING_RP2040_FLASH_BASE), (WEAR_LEVELING_BACKING_SIZE)); - restore_interrupts(intr_stat); + restore_interrupts(interrupts); bs_dprintf("Backing store erase took %ldms to complete\n", ((long)(timer_read32() - start))); return true; @@ -188,9 +213,19 @@ bool backing_store_write(uint32_t address, backing_store_int_t value) { uint32_t offset = (WEAR_LEVELING_RP2040_FLASH_BASE) + address; bs_dprintf("Write "); wl_dump(offset, &value, sizeof(backing_store_int_t)); - intr_stat = save_and_disable_interrupts(); + interrupts = save_and_disable_interrupts(); pico_program_u16(offset, ~value); - restore_interrupts(intr_stat); + restore_interrupts(interrupts); + return true; +} + +bool backing_store_write_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) { + uint32_t offset = (WEAR_LEVELING_RP2040_FLASH_BASE) + address; + bs_dprintf("Write bulk"); + wl_dump(offset, values, sizeof(backing_store_int_t) * item_count); + interrupts = save_and_disable_interrupts(); + pico_program_bulk(offset, values, item_count); + restore_interrupts(interrupts); return true; } diff --git a/platforms/chibios/vendors/RP/stage2_bootloaders.c b/platforms/chibios/vendors/RP/stage2_bootloaders.c index f22112fca9..213dcb86fc 100644 --- a/platforms/chibios/vendors/RP/stage2_bootloaders.c +++ b/platforms/chibios/vendors/RP/stage2_bootloaders.c @@ -11,7 +11,7 @@ #if defined(RP2040_FLASH_AT25SF128A) -uint8_t BOOTLOADER_SECTION BOOT2_AT25SF128A[256] = { +uint8_t BOOTLOADER_SECTION BOOT2_ROM[256] = { 0x00, 0xb5, 0x31, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, 0x98, 0x60, 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2d, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x01, 0x21, 0xf0, 0x22, @@ -38,7 +38,7 @@ uint8_t BOOTLOADER_SECTION BOOT2_AT25SF128A[256] = { #elif defined(RP2040_FLASH_GD25Q64CS) -uint8_t BOOTLOADER_SECTION BOOT2_GD25Q64CS[256] = { +uint8_t BOOTLOADER_SECTION BOOT2_ROM[256] = { 0x00, 0xb5, 0x31, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, 0x98, 0x60, 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2d, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x01, 0x21, 0xf0, 0x22, @@ -65,7 +65,7 @@ uint8_t BOOTLOADER_SECTION BOOT2_GD25Q64CS[256] = { #elif defined(RP2040_FLASH_W25X10CL) -uint8_t BOOTLOADER_SECTION BOOT2_W25X10CL[256] = { +uint8_t BOOTLOADER_SECTION BOOT2_ROM[256] = { 0x00, 0xb5, 0x14, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x12, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60, 0x11, 0x49, 0x12, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xbb, 0x21, 0x19, 0x66, 0x02, 0x21, @@ -92,7 +92,7 @@ uint8_t BOOTLOADER_SECTION BOOT2_W25X10CL[256] = { #elif defined(RP2040_FLASH_IS25LP080) -uint8_t BOOTLOADER_SECTION BOOT2_IS25LP080[256] = { +uint8_t BOOTLOADER_SECTION BOOT2_ROOM[256] = { 0x00, 0xb5, 0x2b, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x29, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x28, 0x48, 0x00, 0xf0, 0x42, 0xf8, 0x28, 0x4a, 0x90, 0x42, 0x12, 0xd0, 0x06, 0x21, 0x19, 0x66, @@ -119,7 +119,7 @@ uint8_t BOOTLOADER_SECTION BOOT2_IS25LP080[256] = { #elif defined(RP2040_FLASH_GENERIC_03H) -uint8_t BOOTLOADER_SECTION BOOT2_GENERIC_03H[256] = { +uint8_t BOOTLOADER_SECTION BOOT2_ROM[256] = { 0x00, 0xb5, 0x0c, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x0a, 0x49, 0x19, 0x60, 0x0a, 0x49, 0x0b, 0x48, 0x01, 0x60, 0x00, 0x21, 0x59, 0x60, 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, @@ -146,7 +146,7 @@ uint8_t BOOTLOADER_SECTION BOOT2_GENERIC_03H[256] = { #else -uint8_t BOOTLOADER_SECTION BOOT2_W25Q080[256] = { +uint8_t BOOTLOADER_SECTION BOOT2_ROM[256] = { 0x00, 0xb5, 0x32, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, 0x98, 0x60, 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2e, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x01, 0x21, 0xf0, 0x22,