2022-06-29 23:42:23 +02:00
// Copyright 2022 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
# include <stdbool.h>
# include <hal.h>
# include "timer.h"
# include "wear_leveling.h"
# include "wear_leveling_internal.h"
static flash_offset_t base_offset = UINT32_MAX ;
# if defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
static flash_sector_t first_sector = WEAR_LEVELING_EFL_FIRST_SECTOR ;
# else // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
static flash_sector_t first_sector = UINT16_MAX ;
# endif // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
static flash_sector_t sector_count = UINT16_MAX ;
static BaseFlash * flash ;
// "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 ) {
2022-12-02 11:55:28 +01:00
# if defined(WEAR_LEVELING_EFL_FLASH_SIZE)
2022-06-29 23:42:23 +02:00
return WEAR_LEVELING_EFL_FLASH_SIZE ;
2022-12-02 11:55:28 +01:00
# elif defined(FLASH_BANK_SIZE)
2022-06-29 23:42:23 +02:00
return FLASH_BANK_SIZE ;
2022-12-02 11:55:28 +01:00
# elif defined(FLASH_SIZE)
2022-06-29 23:42:23 +02:00
return FLASH_SIZE ;
2022-12-02 11:55:28 +01:00
# elif defined(FLASHSIZE_BASE)
# if defined(QMK_MCU_SERIES_STM32F0XX) || defined(QMK_MCU_SERIES_STM32F1XX) || defined(QMK_MCU_SERIES_STM32F3XX) || defined(QMK_MCU_SERIES_STM32F4XX) || defined(QMK_MCU_SERIES_STM32G4XX) || defined(QMK_MCU_SERIES_STM32L0XX) || defined(QMK_MCU_SERIES_STM32L4XX) || defined(QMK_MCU_SERIES_GD32VF103)
2022-06-29 23:42:23 +02:00
return ( ( * ( uint32_t * ) FLASHSIZE_BASE ) & 0xFFFFU ) < < 10U ; // this register has the flash size in kB, so we convert it to bytes
2022-12-02 11:55:28 +01:00
# elif defined(QMK_MCU_SERIES_STM32L1XX)
# error This MCU family has an uncommon flash size register definition and has not been implemented. Perhaps try using the true EEPROM on the MCU instead?
2022-06-29 23:42:23 +02:00
# endif
2022-12-02 11:55:28 +01:00
# else
# error Unknown flash size definition.
return 0 ;
# endif
2022-06-29 23:42:23 +02:00
}
bool backing_store_init ( void ) {
bs_dprintf ( " Init \n " ) ;
flash = ( BaseFlash * ) & EFLD1 ;
2022-07-13 16:41:08 +02:00
// Need to re-lock the EFL, as if we've just had the bootloader executing it'll already be unlocked.
backing_store_lock ( ) ;
2022-12-02 11:55:28 +01:00
const flash_descriptor_t * desc = flashGetDescriptor ( flash ) ;
uint32_t counter = 0 ;
uint32_t flash_size = detect_flash_size ( ) ;
2022-06-29 23:42:23 +02:00
# if defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
// Work out how many sectors we want to use, working forwards from the first sector specified
for ( flash_sector_t i = 0 ; i < desc - > sectors_count - first_sector ; + + i ) {
counter + = flashGetSectorSize ( flash , first_sector + i ) ;
if ( counter > = ( WEAR_LEVELING_BACKING_SIZE ) ) {
sector_count = i + 1 ;
base_offset = flashGetSectorOffset ( flash , first_sector ) ;
break ;
}
}
if ( sector_count = = UINT16_MAX | | base_offset > = flash_size ) {
// We didn't get the required number of sectors. Can't do anything here. Fault.
chSysHalt ( " Invalid sector count intended to be used with wear_leveling " ) ;
}
# else // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
// Work out how many sectors we want to use, working backwards from the end of the flash
flash_sector_t last_sector = desc - > sectors_count ;
for ( flash_sector_t i = 0 ; i < desc - > sectors_count ; + + i ) {
first_sector = desc - > sectors_count - i - 1 ;
if ( flashGetSectorOffset ( flash , first_sector ) > = flash_size ) {
last_sector = first_sector ;
continue ;
}
counter + = flashGetSectorSize ( flash , first_sector ) ;
if ( counter > = ( WEAR_LEVELING_BACKING_SIZE ) ) {
sector_count = last_sector - first_sector ;
base_offset = flashGetSectorOffset ( flash , first_sector ) ;
break ;
}
}
# endif // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
return true ;
}
bool backing_store_unlock ( void ) {
bs_dprintf ( " Unlock \n " ) ;
return eflStart ( & EFLD1 , NULL ) = = HAL_RET_SUCCESS ;
}
bool backing_store_erase ( void ) {
# ifdef WEAR_LEVELING_DEBUG_OUTPUT
uint32_t start = timer_read32 ( ) ;
# endif
bool ret = true ;
flash_error_t status ;
for ( int i = 0 ; i < sector_count ; + + i ) {
// Kick off the sector erase
status = flashStartEraseSector ( flash , first_sector + i ) ;
if ( status ! = FLASH_NO_ERROR & & status ! = FLASH_BUSY_ERASING ) {
ret = false ;
}
// Wait for the erase to complete
status = flashWaitErase ( flash ) ;
if ( status ! = FLASH_NO_ERROR & & status ! = FLASH_BUSY_ERASING ) {
ret = false ;
}
}
bs_dprintf ( " Backing store erase took %ldms to complete \n " , ( ( long ) ( timer_read32 ( ) - start ) ) ) ;
return ret ;
}
bool backing_store_write ( uint32_t address , backing_store_int_t value ) {
uint32_t offset = ( base_offset + address ) ;
bs_dprintf ( " Write " ) ;
wl_dump ( offset , & value , sizeof ( value ) ) ;
value = ~ value ;
return flashProgram ( flash , offset , sizeof ( value ) , ( const uint8_t * ) & value ) = = FLASH_NO_ERROR ;
}
bool backing_store_lock ( void ) {
bs_dprintf ( " Lock \n " ) ;
eflStop ( & EFLD1 ) ;
return true ;
}
bool backing_store_read ( uint32_t address , backing_store_int_t * value ) {
uint32_t offset = ( base_offset + address ) ;
2022-07-13 16:41:08 +02:00
backing_store_int_t * loc = ( backing_store_int_t * ) flashGetOffsetAddress ( flash , offset ) ;
2022-06-29 23:42:23 +02:00
* value = ~ ( * loc ) ;
bs_dprintf ( " Read " ) ;
wl_dump ( offset , value , sizeof ( backing_store_int_t ) ) ;
return true ;
}