Skip to content

Commit

Permalink
[wear_leveling] efl updates (qmk#22489)
Browse files Browse the repository at this point in the history
Co-authored-by: Nick Brassel <nick@tzarc.org>
  • Loading branch information
2 people authored and TheShige committed Jul 26, 2024
1 parent 6a743b7 commit 9498b8e
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 16 deletions.
15 changes: 8 additions & 7 deletions docs/drivers/eeprom.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,14 @@ This driver performs writes to the embedded flash storage embedded in the MCU. I

Configurable options in your keyboard's `config.h`:

`config.h` override | Default | Description
-----------------------------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
`#define WEAR_LEVELING_EFL_FIRST_SECTOR` | _unset_ | The first sector on the MCU to use. By default this is not defined and calculated at runtime based on the MCU. However, different flash sizes on MCUs may require custom configuration.
`#define WEAR_LEVELING_EFL_FLASH_SIZE` | _unset_ | Allows overriding the flash size available for use for wear-leveling. Under normal circumstances this is automatically calculated and should not need to be overridden. Specifying a size larger than the amount actually available in flash will usually prevent the MCU from booting.
`#define WEAR_LEVELING_LOGICAL_SIZE` | `(backing_size/2)` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM.
`#define WEAR_LEVELING_BACKING_SIZE` | `2048` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size.
`#define BACKING_STORE_WRITE_SIZE` | _automatic_ | The byte width of the underlying write used on the MCU, and is usually automatically determined from the selected MCU family. If an error occurs in the auto-detection, you'll need to consult the MCU's datasheet and determine this value, specifying it directly.
`config.h` override | Default | Description
---------------------------------------------------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
`#define WEAR_LEVELING_EFL_FIRST_SECTOR` | _unset_ | The first sector on the MCU to use. By default this is not defined and calculated at runtime based on the MCU. However, different flash sizes on MCUs may require custom configuration.
`#define WEAR_LEVELING_EFL_FLASH_SIZE` | _unset_ | Allows overriding the flash size available for use for wear-leveling. Under normal circumstances this is automatically calculated and should not need to be overridden. Specifying a size larger than the amount actually available in flash will usually prevent the MCU from booting.
`#define WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT` | `0` | Number of sectors to omit at the end of the flash. These sectors will not be allocated to the driver and the usable flash block will be offset, but keeping the set flash size. Useful on devices with bootloaders requiring a check flag at the end of flash to be present in order to confirm a valid, bootable firmware.
`#define WEAR_LEVELING_LOGICAL_SIZE` | `(backing_size/2)` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM.
`#define WEAR_LEVELING_BACKING_SIZE` | `2048` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size.
`#define BACKING_STORE_WRITE_SIZE` | _automatic_ | The byte width of the underlying write used on the MCU, and is usually automatically determined from the selected MCU family. If an error occurs in the auto-detection, you'll need to consult the MCU's datasheet and determine this value, specifying it directly.

::: warning
If your MCU does not boot after swapping to the EFL wear-leveling driver, it's likely that the flash size is incorrectly detected, usually as an MCU with larger flash and may require overriding.
Expand Down
33 changes: 24 additions & 9 deletions platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ static flash_sector_t first_sector = WEAR_LEVELING_EFL_FIRST_SECTOR;
static flash_sector_t first_sector = UINT16_MAX;
#endif // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)

#if !defined(WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT)
# define WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT 0
#endif // WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT

static flash_sector_t sector_count = UINT16_MAX;
static BaseFlash * flash;

static volatile bool is_issuing_read = false;
static volatile bool ecc_error_occurred = false;
static bool flash_erased_is_one;
static volatile bool is_issuing_read = false;
static volatile bool ecc_error_occurred = false;

// "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) {
Expand Down Expand Up @@ -51,10 +55,19 @@ bool backing_store_init(void) {
uint32_t counter = 0;
uint32_t flash_size = detect_flash_size();

// Check if the hardware erase is logic 1
flash_erased_is_one = (desc->attributes & FLASH_ATTR_ERASED_IS_ONE) ? true : false;

if (WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT >= desc->sectors_count) {
// Last sector defined is greater than available number of sectors. Can't do anything here. Fault.
chSysHalt("Last sector intended to be used with wear_leveling is beyond available flash descriptor range");
}

#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) {
flash_sector_t last_sector = desc->sectors_count - WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT;
for (flash_sector_t i = 0; i < last_sector - first_sector; ++i) {
counter += flashGetSectorSize(flash, first_sector + i);
if (counter >= (WEAR_LEVELING_BACKING_SIZE)) {
sector_count = i + 1;
Expand All @@ -70,9 +83,9 @@ bool backing_store_init(void) {
#else // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)

// Work out how many sectors we want to use, working backwards from the end of the flash
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;
flash_sector_t last_sector = desc->sectors_count - WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT;
for (flash_sector_t i = 0; i < last_sector; ++i) {
first_sector = last_sector - i - 1;
if (flashGetSectorOffset(flash, first_sector) >= flash_size) {
last_sector = first_sector;
continue;
Expand Down Expand Up @@ -124,7 +137,9 @@ 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;
if (flash_erased_is_one) {
value = ~value;
}
return flashProgram(flash, offset, sizeof(value), (const uint8_t *)&value) == FLASH_NO_ERROR;
}

Expand All @@ -138,7 +153,7 @@ static backing_store_int_t backing_store_safe_read_from_location(backing_store_i
backing_store_int_t value;
is_issuing_read = true;
ecc_error_occurred = false;
value = ~(*loc);
value = flash_erased_is_one ? ~(*loc) : (*loc);
is_issuing_read = false;
return value;
}
Expand Down

0 comments on commit 9498b8e

Please sign in to comment.