Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 6 commits into from
Nov 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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