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 }