Skip to content

Commit

Permalink
Cater for ECC failures in EFL wear-leveling. (qmk#19749)
Browse files Browse the repository at this point in the history
Co-authored-by: Sergey Vlasov <[email protected]>
  • Loading branch information
2 people authored and Nathan committed Feb 11, 2024
1 parent 22a9091 commit be4c367
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 2 deletions.
32 changes: 31 additions & 1 deletion platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ static flash_sector_t first_sector = UINT16_MAX;
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;

// "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) {
#if defined(WEAR_LEVELING_EFL_FLASH_SIZE)
Expand Down Expand Up @@ -131,11 +134,38 @@ bool backing_store_lock(void) {
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) {
uint32_t offset = (base_offset + address);
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 ");
wl_dump(offset, value, sizeof(backing_store_int_t));
return true;
}

bool backing_store_allow_ecc_errors(void) {
return is_issuing_read;
}

void backing_store_signal_ecc_error(void) {
ecc_error_occurred = true;
}
45 changes: 45 additions & 0 deletions platforms/chibios/interrupt_handlers.c
Original file line number Diff line number Diff line change
@@ -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
///////////////////////////////////////////////////////////////////////////////
3 changes: 2 additions & 1 deletion platforms/chibios/platform.mk
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@ PLATFORM_SRC = \
$(CHIBIOS)/os/various/syscalls.c \
$(PLATFORM_COMMON_DIR)/syscall-fallbacks.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.
QUANTUM_LIB_SRC += $(STARTUPASM) $(PORTASM) $(OSALASM) $(PLATFORMASM)
Expand Down

0 comments on commit be4c367

Please sign in to comment.