forked from mirrors/qmk_userspace
		
	Cater for ECC failures in EFL wear-leveling. (#19749)
Co-authored-by: Sergey Vlasov <sigprof@gmail.com>
This commit is contained in:
		
					parent
					
						
							
								3ef06aa732
							
						
					
				
			
			
				commit
				
					
						f96a7bbd63
					
				
			
		
					 3 changed files with 78 additions and 2 deletions
				
			
		| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										45
									
								
								platforms/chibios/interrupt_handlers.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								platforms/chibios/interrupt_handlers.c
									
										
									
									
									
										Normal 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
 | 
				
			||||||
 | 
					///////////////////////////////////////////////////////////////////////////////
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue