~ruther/qmk_firmware

f96a7bbd6304410e15fa6fc744a9b0fa660f1eeb — Nick Brassel 1 year, 5 months ago 3ef06aa
Cater for ECC failures in EFL wear-leveling. (#19749)

Co-authored-by: Sergey Vlasov <sigprof@gmail.com>
M platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c => platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c +31 -1
@@ 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)


@@ 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;
}

A platforms/chibios/interrupt_handlers.c => platforms/chibios/interrupt_handlers.c +45 -0
@@ 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
///////////////////////////////////////////////////////////////////////////////

M platforms/chibios/platform.mk => platforms/chibios/platform.mk +2 -1
@@ 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)

Do not follow this link