diff options
| author | Nick Brassel <nick@tzarc.org> | 2023-11-26 22:59:38 +1100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-26 22:59:38 +1100 |
| commit | f96a7bbd6304410e15fa6fc744a9b0fa660f1eeb (patch) | |
| tree | 09672f15b55d62540b80cd3fa52fd0d89b6e5407 /platforms | |
| parent | 3ef06aa732ce8063e11b2f983592529883f7ecbc (diff) | |
Cater for ECC failures in EFL wear-leveling. (#19749)
Co-authored-by: Sergey Vlasov <sigprof@gmail.com>
Diffstat (limited to 'platforms')
| -rw-r--r-- | platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c | 32 | ||||
| -rw-r--r-- | platforms/chibios/interrupt_handlers.c | 45 | ||||
| -rw-r--r-- | platforms/chibios/platform.mk | 3 |
3 files changed, 78 insertions, 2 deletions
diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c index 3e4f5ffb89..3d6ed52e5c 100644 --- a/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c +++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c | |||
| @@ -17,6 +17,9 @@ static flash_sector_t first_sector = UINT16_MAX; | |||
| 17 | static flash_sector_t sector_count = UINT16_MAX; | 17 | static flash_sector_t sector_count = UINT16_MAX; |
| 18 | static BaseFlash * flash; | 18 | static BaseFlash * flash; |
| 19 | 19 | ||
| 20 | static volatile bool is_issuing_read = false; | ||
| 21 | static volatile bool ecc_error_occurred = false; | ||
| 22 | |||
| 20 | // "Automatic" detection of the flash size -- ideally ChibiOS would have this already, but alas, it doesn't. | 23 | // "Automatic" detection of the flash size -- ideally ChibiOS would have this already, but alas, it doesn't. |
| 21 | static inline uint32_t detect_flash_size(void) { | 24 | static inline uint32_t detect_flash_size(void) { |
| 22 | #if defined(WEAR_LEVELING_EFL_FLASH_SIZE) | 25 | #if defined(WEAR_LEVELING_EFL_FLASH_SIZE) |
| @@ -131,11 +134,38 @@ bool backing_store_lock(void) { | |||
| 131 | return true; | 134 | return true; |
| 132 | } | 135 | } |
| 133 | 136 | ||
| 137 | static backing_store_int_t backing_store_safe_read_from_location(backing_store_int_t *loc) { | ||
| 138 | backing_store_int_t value; | ||
| 139 | is_issuing_read = true; | ||
| 140 | ecc_error_occurred = false; | ||
| 141 | value = ~(*loc); | ||
| 142 | is_issuing_read = false; | ||
| 143 | return value; | ||
| 144 | } | ||
| 145 | |||
| 134 | bool backing_store_read(uint32_t address, backing_store_int_t *value) { | 146 | bool backing_store_read(uint32_t address, backing_store_int_t *value) { |
| 135 | uint32_t offset = (base_offset + address); | 147 | uint32_t offset = (base_offset + address); |
| 136 | backing_store_int_t *loc = (backing_store_int_t *)flashGetOffsetAddress(flash, offset); | 148 | backing_store_int_t *loc = (backing_store_int_t *)flashGetOffsetAddress(flash, offset); |
| 137 | *value = ~(*loc); | 149 | backing_store_int_t tmp = backing_store_safe_read_from_location(loc); |
| 150 | |||
| 151 | if (ecc_error_occurred) { | ||
| 152 | bs_dprintf("Failed to read from backing store, ECC error detected\n"); | ||
| 153 | ecc_error_occurred = false; | ||
| 154 | *value = 0; | ||
| 155 | return false; | ||
| 156 | } | ||
| 157 | |||
| 158 | *value = tmp; | ||
| 159 | |||
| 138 | bs_dprintf("Read "); | 160 | bs_dprintf("Read "); |
| 139 | wl_dump(offset, value, sizeof(backing_store_int_t)); | 161 | wl_dump(offset, value, sizeof(backing_store_int_t)); |
| 140 | return true; | 162 | return true; |
| 141 | } | 163 | } |
| 164 | |||
| 165 | bool backing_store_allow_ecc_errors(void) { | ||
| 166 | return is_issuing_read; | ||
| 167 | } | ||
| 168 | |||
| 169 | void backing_store_signal_ecc_error(void) { | ||
| 170 | ecc_error_occurred = true; | ||
| 171 | } | ||
diff --git a/platforms/chibios/interrupt_handlers.c b/platforms/chibios/interrupt_handlers.c new file mode 100644 index 0000000000..4ba32d58e4 --- /dev/null +++ b/platforms/chibios/interrupt_handlers.c | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | // Copyright 2023 Nick Brassel (@tzarc) | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | /////////////////////////////////////////////////////////////////////////////// | ||
| 5 | // BEGIN: STM32 EFL Wear-leveling ECC fault handling | ||
| 6 | // | ||
| 7 | // Some STM32s have ECC checks for all flash memory access. Whenever there's an | ||
| 8 | // ECC failure, the MCU raises the NMI interrupt. Whenever we receive such an | ||
| 9 | // interrupt whilst reading the wear-leveling EEPROM area, we gracefully cater | ||
| 10 | // for it, signalling the wear-leveling code that a failure has occurred. | ||
| 11 | /////////////////////////////////////////////////////////////////////////////// | ||
| 12 | |||
| 13 | #include <ch.h> | ||
| 14 | #include <chcore.h> | ||
| 15 | |||
| 16 | #ifdef WEAR_LEVELING_EMBEDDED_FLASH | ||
| 17 | # ifdef QMK_MCU_SERIES_STM32L4XX | ||
| 18 | # define ECC_ERRORS_TRIGGER_NMI_INTERRUPT | ||
| 19 | # define ECC_CHECK_REGISTER FLASH->ECCR | ||
| 20 | # define ECC_CHECK_FLAG FLASH_ECCR_ECCD | ||
| 21 | # endif // QMK_MCU_SERIES_STM32L4XX | ||
| 22 | #endif // WEAR_LEVELING_EMBEDDED_FLASH | ||
| 23 | |||
| 24 | #ifdef ECC_ERRORS_TRIGGER_NMI_INTERRUPT | ||
| 25 | |||
| 26 | extern bool backing_store_allow_ecc_errors(void); | ||
| 27 | extern void backing_store_signal_ecc_error(void); | ||
| 28 | |||
| 29 | void NMI_Handler(void) { | ||
| 30 | if ((ECC_CHECK_REGISTER) & (ECC_CHECK_FLAG)) { | ||
| 31 | if (backing_store_allow_ecc_errors()) { | ||
| 32 | (ECC_CHECK_REGISTER) = (ECC_CHECK_FLAG); | ||
| 33 | backing_store_signal_ecc_error(); | ||
| 34 | return; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | chSysHalt("NMI"); | ||
| 39 | } | ||
| 40 | |||
| 41 | #endif // ECC_ERRORS_TRIGGER_NMI_INTERRUPT | ||
| 42 | |||
| 43 | /////////////////////////////////////////////////////////////////////////////// | ||
| 44 | // END: STM32 EFL Wear-leveling ECC fault handling | ||
| 45 | /////////////////////////////////////////////////////////////////////////////// | ||
diff --git a/platforms/chibios/platform.mk b/platforms/chibios/platform.mk index e42ecebdc5..f38a888012 100644 --- a/platforms/chibios/platform.mk +++ b/platforms/chibios/platform.mk | |||
| @@ -277,7 +277,8 @@ PLATFORM_SRC = \ | |||
| 277 | $(CHIBIOS)/os/various/syscalls.c \ | 277 | $(CHIBIOS)/os/various/syscalls.c \ |
| 278 | $(PLATFORM_COMMON_DIR)/syscall-fallbacks.c \ | 278 | $(PLATFORM_COMMON_DIR)/syscall-fallbacks.c \ |
| 279 | $(PLATFORM_COMMON_DIR)/wait.c \ | 279 | $(PLATFORM_COMMON_DIR)/wait.c \ |
| 280 | $(PLATFORM_COMMON_DIR)/synchronization_util.c | 280 | $(PLATFORM_COMMON_DIR)/synchronization_util.c \ |
| 281 | $(PLATFORM_COMMON_DIR)/interrupt_handlers.c | ||
| 281 | 282 | ||
| 282 | # Ensure the ASM files are not subjected to LTO -- it'll strip out interrupt handlers otherwise. | 283 | # Ensure the ASM files are not subjected to LTO -- it'll strip out interrupt handlers otherwise. |
| 283 | QUANTUM_LIB_SRC += $(STARTUPASM) $(PORTASM) $(OSALASM) $(PLATFORMASM) | 284 | QUANTUM_LIB_SRC += $(STARTUPASM) $(PORTASM) $(OSALASM) $(PLATFORMASM) |
