diff --git a/docs/drivers/eeprom.md b/docs/drivers/eeprom.md index 82630c501d1..0ae258424f8 100644 --- a/docs/drivers/eeprom.md +++ b/docs/drivers/eeprom.md @@ -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. diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c index 3d6ed52e5c0..f49c4a45b0b 100644 --- a/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c +++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c @@ -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) { @@ -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; @@ -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; @@ -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; } @@ -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; }