Cater for ECC failures in EFL wear-leveling. (#19749)

Co-authored-by: Sergey Vlasov <sigprof@gmail.com>
This commit is contained in:
Nick Brassel 2023-11-26 22:59:38 +11:00 committed by GitHub
parent 3ef06aa732
commit f96a7bbd63
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 78 additions and 2 deletions

View file

@ -17,6 +17,9 @@ static flash_sector_t first_sector = UINT16_MAX;
static flash_sector_t sector_count = UINT16_MAX; static flash_sector_t sector_count = UINT16_MAX;
static BaseFlash * flash; static BaseFlash * flash;
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. // "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) { static inline uint32_t detect_flash_size(void) {
#if defined(WEAR_LEVELING_EFL_FLASH_SIZE) #if defined(WEAR_LEVELING_EFL_FLASH_SIZE)
@ -131,11 +134,38 @@ bool backing_store_lock(void) {
return true; return true;
} }
static backing_store_int_t backing_store_safe_read_from_location(backing_store_int_t *loc) {
backing_store_int_t value;
is_issuing_read = true;
ecc_error_occurred = false;
value = ~(*loc);
is_issuing_read = false;
return value;
}
bool backing_store_read(uint32_t address, backing_store_int_t *value) { bool backing_store_read(uint32_t address, backing_store_int_t *value) {
uint32_t offset = (base_offset + address); uint32_t offset = (base_offset + address);
backing_store_int_t *loc = (backing_store_int_t *)flashGetOffsetAddress(flash, offset); backing_store_int_t *loc = (backing_store_int_t *)flashGetOffsetAddress(flash, offset);
*value = ~(*loc); backing_store_int_t tmp = backing_store_safe_read_from_location(loc);
if (ecc_error_occurred) {
bs_dprintf("Failed to read from backing store, ECC error detected\n");
ecc_error_occurred = false;
*value = 0;
return false;
}
*value = tmp;
bs_dprintf("Read "); bs_dprintf("Read ");
wl_dump(offset, value, sizeof(backing_store_int_t)); wl_dump(offset, value, sizeof(backing_store_int_t));
return true; return true;
} }
bool backing_store_allow_ecc_errors(void) {
return is_issuing_read;
}
void backing_store_signal_ecc_error(void) {
ecc_error_occurred = true;
}

View file

@ -0,0 +1,45 @@
// Copyright 2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
///////////////////////////////////////////////////////////////////////////////
// BEGIN: STM32 EFL Wear-leveling ECC fault handling
//
// Some STM32s have ECC checks for all flash memory access. Whenever there's an
// ECC failure, the MCU raises the NMI interrupt. Whenever we receive such an
// interrupt whilst reading the wear-leveling EEPROM area, we gracefully cater
// for it, signalling the wear-leveling code that a failure has occurred.
///////////////////////////////////////////////////////////////////////////////
#include <ch.h>
#include <chcore.h>
#ifdef WEAR_LEVELING_EMBEDDED_FLASH
# ifdef QMK_MCU_SERIES_STM32L4XX
# define ECC_ERRORS_TRIGGER_NMI_INTERRUPT
# define ECC_CHECK_REGISTER FLASH->ECCR
# define ECC_CHECK_FLAG FLASH_ECCR_ECCD
# endif // QMK_MCU_SERIES_STM32L4XX
#endif // WEAR_LEVELING_EMBEDDED_FLASH
#ifdef ECC_ERRORS_TRIGGER_NMI_INTERRUPT
extern bool backing_store_allow_ecc_errors(void);
extern void backing_store_signal_ecc_error(void);
void NMI_Handler(void) {
if ((ECC_CHECK_REGISTER) & (ECC_CHECK_FLAG)) {
if (backing_store_allow_ecc_errors()) {
(ECC_CHECK_REGISTER) = (ECC_CHECK_FLAG);
backing_store_signal_ecc_error();
return;
}
}
chSysHalt("NMI");
}
#endif // ECC_ERRORS_TRIGGER_NMI_INTERRUPT
///////////////////////////////////////////////////////////////////////////////
// END: STM32 EFL Wear-leveling ECC fault handling
///////////////////////////////////////////////////////////////////////////////

View file

@ -277,7 +277,8 @@ PLATFORM_SRC = \
$(CHIBIOS)/os/various/syscalls.c \ $(CHIBIOS)/os/various/syscalls.c \
$(PLATFORM_COMMON_DIR)/syscall-fallbacks.c \ $(PLATFORM_COMMON_DIR)/syscall-fallbacks.c \
$(PLATFORM_COMMON_DIR)/wait.c \ $(PLATFORM_COMMON_DIR)/wait.c \
$(PLATFORM_COMMON_DIR)/synchronization_util.c $(PLATFORM_COMMON_DIR)/synchronization_util.c \
$(PLATFORM_COMMON_DIR)/interrupt_handlers.c
# Ensure the ASM files are not subjected to LTO -- it'll strip out interrupt handlers otherwise. # Ensure the ASM files are not subjected to LTO -- it'll strip out interrupt handlers otherwise.
QUANTUM_LIB_SRC += $(STARTUPASM) $(PORTASM) $(OSALASM) $(PLATFORMASM) QUANTUM_LIB_SRC += $(STARTUPASM) $(PORTASM) $(OSALASM) $(PLATFORMASM)