qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

wear_leveling_efl.c (6958B)


      1 // Copyright 2022 Nick Brassel (@tzarc)
      2 // SPDX-License-Identifier: GPL-2.0-or-later
      3 #include <stdbool.h>
      4 #include <hal.h>
      5 #include "timer.h"
      6 #include "wear_leveling.h"
      7 #include "wear_leveling_efl_config.h"
      8 #include "wear_leveling_internal.h"
      9 
     10 static flash_offset_t base_offset = UINT32_MAX;
     11 
     12 #if defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
     13 static flash_sector_t first_sector = WEAR_LEVELING_EFL_FIRST_SECTOR;
     14 #else  // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
     15 static flash_sector_t first_sector = UINT16_MAX;
     16 #endif // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
     17 
     18 #if !defined(WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT)
     19 #    define WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT 0
     20 #endif // WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT
     21 
     22 static flash_sector_t sector_count = UINT16_MAX;
     23 static BaseFlash     *flash;
     24 static bool           flash_erased_is_one;
     25 static volatile bool  is_issuing_read    = false;
     26 static volatile bool  ecc_error_occurred = false;
     27 
     28 // "Automatic" detection of the flash size -- ideally ChibiOS would have this already, but alas, it doesn't.
     29 static inline uint32_t detect_flash_size(void) {
     30 #if defined(WEAR_LEVELING_EFL_FLASH_SIZE)
     31     return WEAR_LEVELING_EFL_FLASH_SIZE;
     32 #elif defined(FLASH_BANK_SIZE)
     33     return FLASH_BANK_SIZE;
     34 #elif defined(FLASH_SIZE)
     35     return FLASH_SIZE;
     36 #elif defined(FLASHSIZE_BASE)
     37 #    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_AT32F415) || defined(QMK_MCU_SERIES_GD32VF103)
     38     return ((*(uint32_t *)FLASHSIZE_BASE) & 0xFFFFU) << 10U; // this register has the flash size in kB, so we convert it to bytes
     39 #    elif defined(QMK_MCU_SERIES_STM32L1XX)
     40 #        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?
     41 #    endif
     42 #else
     43 #    error Unknown flash size definition.
     44     return 0;
     45 #endif
     46 }
     47 
     48 bool backing_store_init(void) {
     49     bs_dprintf("Init\n");
     50     flash = (BaseFlash *)&EFLD1;
     51 
     52     // Need to re-lock the EFL, as if we've just had the bootloader executing it'll already be unlocked.
     53     backing_store_lock();
     54 
     55     const flash_descriptor_t *desc       = flashGetDescriptor(flash);
     56     uint32_t                  counter    = 0;
     57     uint32_t                  flash_size = detect_flash_size();
     58 
     59     // Check if the hardware erase is logic 1
     60     flash_erased_is_one = (desc->attributes & FLASH_ATTR_ERASED_IS_ONE) ? true : false;
     61 
     62     if (WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT >= desc->sectors_count) {
     63         // Last sector defined is greater than available number of sectors. Can't do anything here. Fault.
     64         chSysHalt("Last sector intended to be used with wear_leveling is beyond available flash descriptor range");
     65     }
     66 
     67 #if defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
     68 
     69     // Work out how many sectors we want to use, working forwards from the first sector specified
     70     flash_sector_t last_sector = desc->sectors_count - WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT;
     71     for (flash_sector_t i = 0; i < last_sector - first_sector; ++i) {
     72         counter += flashGetSectorSize(flash, first_sector + i);
     73         if (counter >= (WEAR_LEVELING_BACKING_SIZE)) {
     74             sector_count = i + 1;
     75             base_offset  = flashGetSectorOffset(flash, first_sector);
     76             break;
     77         }
     78     }
     79     if (sector_count == UINT16_MAX || base_offset >= flash_size) {
     80         // We didn't get the required number of sectors. Can't do anything here. Fault.
     81         chSysHalt("Invalid sector count intended to be used with wear_leveling");
     82     }
     83 
     84 #else // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
     85 
     86     // Work out how many sectors we want to use, working backwards from the end of the flash
     87     flash_sector_t last_sector = desc->sectors_count - WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT;
     88     for (flash_sector_t i = 0; i < last_sector; ++i) {
     89         first_sector = last_sector - i - 1;
     90         if (flashGetSectorOffset(flash, first_sector) >= flash_size) {
     91             last_sector = first_sector;
     92             continue;
     93         }
     94         counter += flashGetSectorSize(flash, first_sector);
     95         if (counter >= (WEAR_LEVELING_BACKING_SIZE)) {
     96             sector_count = last_sector - first_sector;
     97             base_offset  = flashGetSectorOffset(flash, first_sector);
     98             break;
     99         }
    100     }
    101 
    102 #endif // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
    103 
    104     return true;
    105 }
    106 
    107 bool backing_store_unlock(void) {
    108     bs_dprintf("Unlock\n");
    109     return eflStart(&EFLD1, NULL) == HAL_RET_SUCCESS;
    110 }
    111 
    112 bool backing_store_erase(void) {
    113 #ifdef WEAR_LEVELING_DEBUG_OUTPUT
    114     uint32_t start = timer_read32();
    115 #endif
    116 
    117     bool          ret = true;
    118     flash_error_t status;
    119     for (int i = 0; i < sector_count; ++i) {
    120         // Kick off the sector erase
    121         status = flashStartEraseSector(flash, first_sector + i);
    122         if (status != FLASH_NO_ERROR && status != FLASH_BUSY_ERASING) {
    123             ret = false;
    124         }
    125 
    126         // Wait for the erase to complete
    127         status = flashWaitErase(flash);
    128         if (status != FLASH_NO_ERROR && status != FLASH_BUSY_ERASING) {
    129             ret = false;
    130         }
    131     }
    132 
    133     bs_dprintf("Backing store erase took %ldms to complete\n", ((long)(timer_read32() - start)));
    134     return ret;
    135 }
    136 
    137 bool backing_store_write(uint32_t address, backing_store_int_t value) {
    138     uint32_t offset = (base_offset + address);
    139     bs_dprintf("Write ");
    140     wl_dump(offset, &value, sizeof(value));
    141     if (flash_erased_is_one) {
    142         value = ~value;
    143     }
    144     return flashProgram(flash, offset, sizeof(value), (const uint8_t *)&value) == FLASH_NO_ERROR;
    145 }
    146 
    147 bool backing_store_lock(void) {
    148     bs_dprintf("Lock  \n");
    149     eflStop(&EFLD1);
    150     return true;
    151 }
    152 
    153 static backing_store_int_t backing_store_safe_read_from_location(backing_store_int_t *loc) {
    154     backing_store_int_t value;
    155     is_issuing_read    = true;
    156     ecc_error_occurred = false;
    157     value              = flash_erased_is_one ? ~(*loc) : (*loc);
    158     is_issuing_read    = false;
    159     return value;
    160 }
    161 
    162 bool backing_store_read(uint32_t address, backing_store_int_t *value) {
    163     uint32_t             offset = (base_offset + address);
    164     backing_store_int_t *loc    = (backing_store_int_t *)flashGetOffsetAddress(flash, offset);
    165     backing_store_int_t  tmp    = backing_store_safe_read_from_location(loc);
    166 
    167     if (ecc_error_occurred) {
    168         bs_dprintf("Failed to read from backing store, ECC error detected\n");
    169         ecc_error_occurred = false;
    170         *value             = 0;
    171         return false;
    172     }
    173 
    174     *value = tmp;
    175 
    176     bs_dprintf("Read  ");
    177     wl_dump(offset, value, sizeof(backing_store_int_t));
    178     return true;
    179 }
    180 
    181 bool backing_store_allow_ecc_errors(void) {
    182     return is_issuing_read;
    183 }
    184 
    185 void backing_store_signal_ecc_error(void) {
    186     ecc_error_occurred = true;
    187 }