diff options
Diffstat (limited to 'platforms/chibios/drivers/wear_leveling')
6 files changed, 569 insertions, 0 deletions
diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c new file mode 100644 index 0000000000..4b5639ee4a --- /dev/null +++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c @@ -0,0 +1,140 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#include <stdbool.h> +#include <hal.h> +#include "timer.h" +#include "wear_leveling.h" +#include "wear_leveling_internal.h" + +static flash_offset_t base_offset = UINT32_MAX; + +#if defined(WEAR_LEVELING_EFL_FIRST_SECTOR) +static flash_sector_t first_sector = WEAR_LEVELING_EFL_FIRST_SECTOR; +#else // defined(WEAR_LEVELING_EFL_FIRST_SECTOR) +static flash_sector_t first_sector = UINT16_MAX; +#endif // defined(WEAR_LEVELING_EFL_FIRST_SECTOR) + +static flash_sector_t sector_count = UINT16_MAX; +static BaseFlash * flash; + +#ifndef WEAR_LEVELING_EFL_FIRST_SECTOR +// "Automatic" detection of the flash size -- ideally ChibiOS would have this already, but alas, it doesn't. +static inline uint32_t detect_flash_size(void) { +# if defined(WEAR_LEVELING_EFL_FLASH_SIZE) + return WEAR_LEVELING_EFL_FLASH_SIZE; +# elif defined(FLASH_BANK_SIZE) + return FLASH_BANK_SIZE; +# elif defined(FLASH_SIZE) + return FLASH_SIZE; +# elif defined(FLASHSIZE_BASE) +# if defined(QMK_MCU_SERIES_STM32F0XX) || defined(QMK_MCU_SERIES_STM32F1XX) || defined(QMK_MCU_SERIES_STM32F3XX) || defined(QMK_MCU_SERIES_STM32F4XX) || defined(QMK_MCU_SERIES_STM32G4XX) || defined(QMK_MCU_SERIES_STM32L0XX) || defined(QMK_MCU_SERIES_STM32L4XX) || defined(QMK_MCU_SERIES_GD32VF103) + return ((*(uint32_t *)FLASHSIZE_BASE) & 0xFFFFU) << 10U; // this register has the flash size in kB, so we convert it to bytes +# elif defined(QMK_MCU_SERIES_STM32L1XX) +# error This MCU family has an uncommon flash size register definition and has not been implemented. Perhaps try using the true EEPROM on the MCU instead? +# endif +# else +# error Unknown flash size definition. + return 0; +# endif +} +#endif // WEAR_LEVELING_EFL_FIRST_SECTOR + +bool backing_store_init(void) { + bs_dprintf("Init\n"); + flash = (BaseFlash *)&EFLD1; + + const flash_descriptor_t *desc = flashGetDescriptor(flash); + uint32_t counter = 0; + +#if defined(WEAR_LEVELING_EFL_FIRST_SECTOR) + + // Work out how many sectors we want to use, working forwards from the first sector specified + for (flash_sector_t i = 0; i < desc->sectors_count - first_sector; ++i) { + counter += flashGetSectorSize(flash, first_sector + i); + if (counter >= (WEAR_LEVELING_BACKING_SIZE)) { + sector_count = i + 1; + base_offset = flashGetSectorOffset(flash, first_sector); + break; + } + } + if (sector_count == UINT16_MAX || base_offset >= flash_size) { + // We didn't get the required number of sectors. Can't do anything here. Fault. + chSysHalt("Invalid sector count intended to be used with wear_leveling"); + } + +#else // defined(WEAR_LEVELING_EFL_FIRST_SECTOR) + + // Work out how many sectors we want to use, working backwards from the end of the flash + uint32_t flash_size = detect_flash_size(); + flash_sector_t last_sector = desc->sectors_count; + for (flash_sector_t i = 0; i < desc->sectors_count; ++i) { + first_sector = desc->sectors_count - i - 1; + if (flashGetSectorOffset(flash, first_sector) >= flash_size) { + last_sector = first_sector; + continue; + } + counter += flashGetSectorSize(flash, first_sector); + if (counter >= (WEAR_LEVELING_BACKING_SIZE)) { + sector_count = last_sector - first_sector; + base_offset = flashGetSectorOffset(flash, first_sector); + break; + } + } + +#endif // defined(WEAR_LEVELING_EFL_FIRST_SECTOR) + + return true; +} + +bool backing_store_unlock(void) { + bs_dprintf("Unlock\n"); + return eflStart(&EFLD1, NULL) == HAL_RET_SUCCESS; +} + +bool backing_store_erase(void) { +#ifdef WEAR_LEVELING_DEBUG_OUTPUT + uint32_t start = timer_read32(); +#endif + + bool ret = true; + flash_error_t status; + for (int i = 0; i < sector_count; ++i) { + // Kick off the sector erase + status = flashStartEraseSector(flash, first_sector + i); + if (status != FLASH_NO_ERROR && status != FLASH_BUSY_ERASING) { + ret = false; + } + + // Wait for the erase to complete + status = flashWaitErase(flash); + if (status != FLASH_NO_ERROR && status != FLASH_BUSY_ERASING) { + ret = false; + } + } + + bs_dprintf("Backing store erase took %ldms to complete\n", ((long)(timer_read32() - start))); + return ret; +} + +bool backing_store_write(uint32_t address, backing_store_int_t value) { + uint32_t offset = (base_offset + address); + bs_dprintf("Write "); + wl_dump(offset, &value, sizeof(value)); + value = ~value; + return flashProgram(flash, offset, sizeof(value), (const uint8_t *)&value) == FLASH_NO_ERROR; +} + +bool backing_store_lock(void) { + bs_dprintf("Lock \n"); + eflStop(&EFLD1); + return true; +} + +bool backing_store_read(uint32_t address, backing_store_int_t *value) { + uint32_t offset = (base_offset + address); + backing_store_int_t *loc = (backing_store_int_t *)offset; + *value = ~(*loc); + bs_dprintf("Read "); + wl_dump(offset, value, sizeof(backing_store_int_t)); + return true; +} diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_efl_config.h b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl_config.h new file mode 100644 index 0000000000..9e38c2965d --- /dev/null +++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl_config.h @@ -0,0 +1,50 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#ifndef __ASSEMBLER__ +# include <hal.h> +#endif + +// Work out how many bytes per write to internal flash +#ifndef BACKING_STORE_WRITE_SIZE +// These need to match EFL's XXXXXX_FLASH_LINE_SIZE, see associated code in `lib/chibios/os/hal/ports/**/hal_efl_lld.c`, +// or associated `stm32_registry.h` for the MCU in question (or equivalent for the family). +# if defined(QMK_MCU_SERIES_GD32VF103) +# define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c +# elif defined(QMK_MCU_FAMILY_NUC123) +# define BACKING_STORE_WRITE_SIZE 4 // from hal_efl_lld.c +# elif defined(QMK_MCU_FAMILY_STM32) +# if defined(STM32_FLASH_LINE_SIZE) // from some family's stm32_registry.h file +# define BACKING_STORE_WRITE_SIZE (STM32_FLASH_LINE_SIZE) +# else +# if defined(QMK_MCU_SERIES_STM32F1XX) +# define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c +# elif defined(QMK_MCU_SERIES_STM32F3XX) +# define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c +# elif defined(QMK_MCU_SERIES_STM32F4XX) +# define BACKING_STORE_WRITE_SIZE (1 << STM32_FLASH_PSIZE) // from hal_efl_lld.c +# elif defined(QMK_MCU_SERIES_STM32L4XX) +# define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c +# elif defined(QMK_MCU_SERIES_STM32G0XX) +# define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c +# elif defined(QMK_MCU_SERIES_STM32G4XX) +# define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c +# else +# error "ChibiOS hasn't defined STM32_FLASH_LINE_SIZE, and could not automatically determine BACKING_STORE_WRITE_SIZE" // normally defined in stm32_registry.h, should be set by STM32_FLASH_LINE_SIZE +# endif +# endif +# else +# error "Could not automatically determine BACKING_STORE_WRITE_SIZE" +# endif +#endif + +// 2kB backing space allocated +#ifndef WEAR_LEVELING_BACKING_SIZE +# define WEAR_LEVELING_BACKING_SIZE 2048 +#endif // WEAR_LEVELING_BACKING_SIZE + +// 1kB logical EEPROM +#ifndef WEAR_LEVELING_LOGICAL_SIZE +# define WEAR_LEVELING_LOGICAL_SIZE 1024 +#endif // WEAR_LEVELING_LOGICAL_SIZE diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy.c b/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy.c new file mode 100644 index 0000000000..87126c4467 --- /dev/null +++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy.c @@ -0,0 +1,59 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#include <stdbool.h> +#include <hal.h> +#include "timer.h" +#include "wear_leveling.h" +#include "wear_leveling_internal.h" +#include "flash_stm32.h" + +bool backing_store_init(void) { + bs_dprintf("Init\n"); + return true; +} + +bool backing_store_unlock(void) { + bs_dprintf("Unlock\n"); + FLASH_Unlock(); + return true; +} + +bool backing_store_erase(void) { +#ifdef WEAR_LEVELING_DEBUG_OUTPUT + uint32_t start = timer_read32(); +#endif + + bool ret = true; + FLASH_Status status; + for (int i = 0; i < (WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT); ++i) { + status = FLASH_ErasePage(WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS + (i * (WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE))); + if (status != FLASH_COMPLETE) { + ret = false; + } + } + + bs_dprintf("Backing store erase took %ldms to complete\n", ((long)(timer_read32() - start))); + return ret; +} + +bool backing_store_write(uint32_t address, backing_store_int_t value) { + uint32_t offset = ((WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS) + address); + bs_dprintf("Write "); + wl_dump(offset, &value, sizeof(backing_store_int_t)); + return FLASH_ProgramHalfWord(offset, ~value) == FLASH_COMPLETE; +} + +bool backing_store_lock(void) { + bs_dprintf("Lock \n"); + FLASH_Lock(); + return true; +} + +bool backing_store_read(uint32_t address, backing_store_int_t* value) { + uint32_t offset = ((WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS) + address); + backing_store_int_t* loc = (backing_store_int_t*)offset; + *value = ~(*loc); + bs_dprintf("Read "); + wl_dump(offset, loc, sizeof(backing_store_int_t)); + return true; +} diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy_config.h b/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy_config.h new file mode 100644 index 0000000000..1e4691a6c0 --- /dev/null +++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy_config.h @@ -0,0 +1,67 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +// Work out the page size to use +#ifndef WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE +# if defined(QMK_MCU_STM32F042) +# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE 1024 +# elif defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072) +# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE 2048 +# elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411) +# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE 16384 +# endif +#endif + +// Work out how much flash space we have +#ifndef WEAR_LEVELING_LEGACY_EMULATION_FLASH_SIZE +# define WEAR_LEVELING_LEGACY_EMULATION_FLASH_SIZE ((*(uint32_t *)FLASHSIZE_BASE) & 0xFFFFU) // in kB +#endif + +// The base location of program memory +#ifndef WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE +# define WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE 0x08000000 +#endif + +// The number of pages to use +#ifndef WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT +# if defined(QMK_MCU_STM32F042) +# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT 2 +# elif defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072) +# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT 1 +# elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411) +# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT 1 +# endif +#endif + +// The origin of the emulated eeprom +#ifndef WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS +# if defined(QMK_MCU_STM32F042) || defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072) +# define WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS ((uintptr_t)(WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE) + WEAR_LEVELING_LEGACY_EMULATION_FLASH_SIZE * 1024 - (WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT * WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE)) +# elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411) +# if defined(BOOTLOADER_STM32) +# define WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS (WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE + (1 * (WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE))) // +16k +# elif defined(BOOTLOADER_TINYUF2) +# define WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS (WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE + (3 * (WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE))) // +48k +# endif +# endif +#endif + +// 2-byte writes +#ifndef BACKING_STORE_WRITE_SIZE +# define BACKING_STORE_WRITE_SIZE 2 +#endif + +// The amount of space to use for the entire set of emulation +#ifndef WEAR_LEVELING_BACKING_SIZE +# if defined(QMK_MCU_STM32F042) || defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072) +# define WEAR_LEVELING_BACKING_SIZE 2048 +# elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411) +# define WEAR_LEVELING_BACKING_SIZE 16384 +# endif +#endif + +// The logical amount of eeprom available +#ifndef WEAR_LEVELING_LOGICAL_SIZE +# define WEAR_LEVELING_LOGICAL_SIZE 1024 +#endif diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c b/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c new file mode 100644 index 0000000000..640628e1e9 --- /dev/null +++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c @@ -0,0 +1,221 @@ +/** + * 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 + */ + +#include "pico/bootrom.h" +#include "hardware/flash.h" +#include "hardware/sync.h" +#include "hardware/structs/ssi.h" +#include "hardware/structs/ioqspi.h" + +#include <stdbool.h> +#include "timer.h" +#include "wear_leveling.h" +#include "wear_leveling_internal.h" + +#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 +check_hw_layout(ssi_hw_t, ssienr, SSI_SSIENR_OFFSET); +check_hw_layout(ssi_hw_t, spi_ctrlr0, SSI_SPI_CTRLR0_OFFSET); + +static void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void) { + ((void (*)(void))BOOT2_ROM_RAM + 1)(); +} + +// Bitbanging the chip select using IO overrides, in case RAM-resident IRQs +// are still running, and the FIFO bottoms out. (the bootrom does the same) +static void __no_inline_not_in_flash_func(flash_cs_force)(bool high) { + uint32_t field_val = high ? IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH : IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW; + hw_write_masked(&ioqspi_hw->io[1].ctrl, field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB, IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS); +} + +// Also allow any unbounded loops to check whether the above abort condition +// was asserted, and terminate early +static int __no_inline_not_in_flash_func(flash_was_aborted)(void) { + return *(io_rw_32 *)(IO_QSPI_BASE + IO_QSPI_GPIO_QSPI_SD1_CTRL_OFFSET) & IO_QSPI_GPIO_QSPI_SD1_CTRL_INOVER_BITS; +} + +// Put bytes from one buffer, and get bytes into another buffer. +// These can be the same buffer. +// If tx is NULL then send zeroes. +// If rx is NULL then all read data will be dropped. +// +// If rx_skip is nonzero, this many bytes will first be consumed from the FIFO, +// before reading a further count bytes into *rx. +// E.g. if you have written a command+address just before calling this function. +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) { + // Make sure there is never more data in flight than the depth of the RX + // FIFO. Otherwise, when we are interrupted for long periods, hardware + // will overflow the RX FIFO. + const uint max_in_flight = 16 - 2; // account for data internal to SSI + size_t tx_count = count; + size_t rx_count = count; + while (tx_count || rx_skip || rx_count) { + // NB order of reads, for pessimism rather than optimism + uint32_t tx_level = ssi_hw->txflr; + uint32_t rx_level = ssi_hw->rxflr; + bool did_something = false; // Expect this to be folded into control flow, not register + if (tx_count && tx_level + rx_level < max_in_flight) { + ssi->dr0 = (uint32_t)(tx ? *tx++ : 0); + --tx_count; + did_something = true; + } + if (rx_level) { + uint8_t rxbyte = ssi->dr0; + did_something = true; + if (rx_skip) { + --rx_skip; + } else { + if (rx) *rx++ = rxbyte; + --rx_count; + } + } + // APB load costs 4 cycles, so only do it on idle loops (our budget is + // 48 cyc/byte) + if (!did_something && __builtin_expect(flash_was_aborted(), 0)) break; + } + flash_cs_force(1); +} + +// Convenience wrapper for above +// (And it's hard for the debug host to get the tight timing between +// cmd DR0 write and the remaining data) +static void __no_inline_not_in_flash_func(_flash_do_cmd)(uint8_t cmd, const uint8_t *tx, uint8_t *rx, size_t count) { + flash_cs_force(0); + ssi->dr0 = cmd; + flash_put_get(tx, rx, count, 1); +} + +// Timing of this one is critical, so do not expose the symbol to debugger etc +static void __no_inline_not_in_flash_func(flash_put_cmd_addr)(uint8_t cmd, uint32_t addr) { + flash_cs_force(0); + addr |= cmd << 24; + for (int i = 0; i < 4; ++i) { + ssi->dr0 = addr >> 24; + addr <<= 8; + } +} + +// Poll the flash status register until the busy bit (LSB) clears +static void __no_inline_not_in_flash_func(flash_wait_ready)(void) { + uint8_t stat; + do { + _flash_do_cmd(FLASHCMD_READ_STATUS, NULL, &stat, 1); + } while (stat & 0x1 && !flash_was_aborted()); +} + +// Set the WEL bit (needed before any program/erase operation) +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_bulk)(uint32_t flash_address, backing_store_int_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 backing_store_int_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 (size_t 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(); + + flash_put_cmd_addr(FLASHCMD_PAGE_PROGRAM, flash_address); + flash_put_get((uint8_t *)bulk_write_buffer, NULL, batch_size * sizeof(backing_store_int_t), 4); + flash_wait_ready(); + flash_address += batch_size * sizeof(backing_store_int_t); + + flash_flush_cache(); + flash_enable_xip_via_boot2(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// QMK Wear-Leveling Backing Store implementation + +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; +} + +bool backing_store_unlock(void) { + bs_dprintf("Unlock\n"); + return true; +} + +bool backing_store_erase(void) { +#ifdef WEAR_LEVELING_DEBUG_OUTPUT + uint32_t start = timer_read32(); +#endif + + // Ensure the backing size can be cleanly subtracted from the flash size without alignment issues. + _Static_assert((WEAR_LEVELING_BACKING_SIZE) % (FLASH_SECTOR_SIZE) == 0, "Backing size must be a multiple of FLASH_SECTOR_SIZE"); + + interrupts = save_and_disable_interrupts(); + flash_range_erase((WEAR_LEVELING_RP2040_FLASH_BASE), (WEAR_LEVELING_BACKING_SIZE)); + restore_interrupts(interrupts); + + bs_dprintf("Backing store erase took %ldms to complete\n", ((long)(timer_read32() - start))); + return true; +} + +bool backing_store_write(uint32_t address, backing_store_int_t value) { + return backing_store_write_bulk(address, &value, 1); +} + +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 "); + 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; +} + +bool backing_store_lock(void) { + return true; +} + +bool backing_store_read(uint32_t address, backing_store_int_t *value) { + return backing_store_read_bulk(address, value, 1); +} + +bool backing_store_read_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) { + uint32_t offset = (WEAR_LEVELING_RP2040_FLASH_BASE) + address; + backing_store_int_t *loc = (backing_store_int_t *)((XIP_BASE) + offset); + for (size_t i = 0; i < item_count; ++i) { + values[i] = ~loc[i]; + } + bs_dprintf("Read "); + wl_dump(offset, values, item_count * sizeof(backing_store_int_t)); + return true; +} diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash_config.h b/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash_config.h new file mode 100644 index 0000000000..93a9aa0372 --- /dev/null +++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash_config.h @@ -0,0 +1,32 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#ifndef __ASSEMBLER__ +# include "hardware/flash.h" +#endif + +// 2-byte writes +#ifndef BACKING_STORE_WRITE_SIZE +# define BACKING_STORE_WRITE_SIZE 2 +#endif + +// 64kB backing space allocated +#ifndef WEAR_LEVELING_BACKING_SIZE +# define WEAR_LEVELING_BACKING_SIZE 8192 +#endif // WEAR_LEVELING_BACKING_SIZE + +// 32kB logical EEPROM +#ifndef WEAR_LEVELING_LOGICAL_SIZE +# define WEAR_LEVELING_LOGICAL_SIZE 4096 +#endif // WEAR_LEVELING_LOGICAL_SIZE + +// Define how much flash space we have (defaults to lib/pico-sdk/src/boards/include/boards/***) +#ifndef WEAR_LEVELING_RP2040_FLASH_SIZE +# define WEAR_LEVELING_RP2040_FLASH_SIZE (PICO_FLASH_SIZE_BYTES) +#endif + +// Define the location of emulated EEPROM +#ifndef WEAR_LEVELING_RP2040_FLASH_BASE +# define WEAR_LEVELING_RP2040_FLASH_BASE ((WEAR_LEVELING_RP2040_FLASH_SIZE) - (WEAR_LEVELING_BACKING_SIZE)) +#endif |