diff --git a/common_features.mk b/common_features.mk index 7ff19e808425..4efde67dfdf2 100644 --- a/common_features.mk +++ b/common_features.mk @@ -197,6 +197,12 @@ else COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/eeprom SRC += eeprom_driver.c SRC += eeprom_stm32_L0_L1.c + else ifneq ($(filter $(MCU_SERIES),STM32L4xx),) + OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_STM32_FLASH_EMULATED + COMMON_VPATH += $(DRIVER_PATH)/eeprom + SRC += eeprom_driver.c + SRC += $(PLATFORM_COMMON_DIR)/eeprom_stm32_l4.c + SRC += $(PLATFORM_COMMON_DIR)/flash_stm32.c else ifneq ($(filter $(MCU_SERIES),KL2x K20x),) # Teensy EEPROM implementations OPT_DEFS += -DEEPROM_TEENSY diff --git a/platforms/chibios/eeprom_stm32_defs.h b/platforms/chibios/eeprom_stm32_defs.h index 581434eb5eed..1589ef28fdc0 100644 --- a/platforms/chibios/eeprom_stm32_defs.h +++ b/platforms/chibios/eeprom_stm32_defs.h @@ -25,7 +25,7 @@ # ifndef FEE_PAGE_COUNT # define FEE_PAGE_COUNT 2 // How many pages are used # endif -# elif defined(STM32F103xE) || defined(STM32F303xC) || defined(STM32F072xB) || defined(STM32F070xB) +# elif defined(STM32F103xE) || defined(STM32F303xC) || defined(STM32F072xB) || defined(STM32F070xB) || defined(STM32L432xx) # ifndef FEE_PAGE_SIZE # define FEE_PAGE_SIZE 0x800 // Page size = 2KByte # endif @@ -47,7 +47,7 @@ # define FEE_MCU_FLASH_SIZE 32 // Size in Kb # elif defined(GD32VF103C8) # define FEE_MCU_FLASH_SIZE 64 // Size in Kb -# elif defined(STM32F103xB) || defined(STM32F072xB) || defined(STM32F070xB) || defined(GD32VF103CB) +# elif defined(STM32F103xB) || defined(STM32F072xB) || defined(STM32F070xB) || defined(GD32VF103CB) || defined(STM32L432xx) # define FEE_MCU_FLASH_SIZE 128 // Size in Kb # elif defined(STM32F303xC) || defined(STM32F401xC) # define FEE_MCU_FLASH_SIZE 256 // Size in Kb diff --git a/platforms/chibios/eeprom_stm32_l4.c b/platforms/chibios/eeprom_stm32_l4.c new file mode 100644 index 000000000000..8f33881749bb --- /dev/null +++ b/platforms/chibios/eeprom_stm32_l4.c @@ -0,0 +1,546 @@ +/* + * This software is experimental and a work in progress. + * Under no circumstances should these files be used in relation to any critical system(s). + * Use of these files is at your own risk. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by + * Artur F. + * + * Modifications for QMK and STM32L432 by lalalademaxiya1 & lokher + * + * TODO: Add ECC correction interrupt handler. + */ + +#include +#include +#include +#include "flash_stm32.h" +#include "eeprom_stm32_l4.h" +#include "print.h" +/* + * We emulate eeprom by writing a snapshot compacted view of eeprom contents, + * followed by a write log of any change since that snapshot: + * + * === SIMULATED EEPROM CONTENTS === + * + * ┌─ Compacted─┬─ Write Log ──┐ + * │............│[DWord][DWord]│ + * │FFFF....FFFF│[DWord][DWord]│ + * │FFFFFFFFFFFF│[DWord][DWord]│ + * │....FFFFFFFF│[DWord][DWord]│ + * ├────────────┼──────────────┤ + * └──PAGE_BASE │ │ + * PAGE_LAST─┴─WRITE_BASE │ + * WRITE_LAST ──┘ + * + * Compacted contents are the 1's complement of the actual EEPROM contents. + * e.g. An 'FFFF' represents a '0000' value. + * + * The size of the 'compacted' area is equal to the size of the 'emulated' eeprom. + * The size of the compacted-area and write log are configurable, and the combined + * size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent. + * Simulated Eeprom contents are located at the end of available flash space. + * + * The following configuration defines can be set: + * + * FEE_PAGE_COUNT # Total number of pages to use for eeprom simulation (Compact + Write log) + * FEE_DENSITY_BYTES # Size of simulated eeprom. (Defaults to one pages of FEE_PAGE_COUNT) + * NOTE: The current implementation does not include page swapping, + * and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents. + * + * The maximum size of FEE_DENSITY_BYTES is currently 8192. The write log size equals + * FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES. + * The larger the write log, the less frequently the compacted area needs to be rewritten. + * + * + * *** General Algorithm *** + * + * During initialization: + * The contents of the Compacted-flash area are loaded and the 1's complement value + * is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache). + * Write log entries are processed until a 0xFFFF is reached. + * Each log entry updates 1/2/4 byte(s) in the cache. + * + * During reads: + * EEPROM contents are given back directly from the cache in memory. + * + * During writes: + * The contents of the cache is updated first. + * If the Compacted-flash area corresponding to the write address is unprogrammed, the 1's complement of the value is written directly into Compacted-flash + * Otherwise: + * If the write log is full, erase both the Compacted-flash area and the Write log, then write cached contents to the Compacted-flash area. + * Otherwise a Write log entry is constructed and appended to the next free position in the Write log. + * + * + * *** Write Log Structure *** + * + * Each log entry compose of double word (2 x 32-bit) due to the minimum program size of STM32L432 flash. + * + * === WRITE LOG ENTRY FORMATS === + * + * ╔══════════ Byte-Entry ═════════╗ + * ║ 00 01 XX XX ║ FF FF FF YY ║ + * ║ └─┬─┘ └─┬─┘ ║ └┘ ║ + * ║ Len Address ║ ~Value ║ + * ╚═══════════════╩═══════════════╝ + * + * ╔══════════ Word-Entry ═════════╗ + * ║ 00 02 XX XX ║ FF FF YY YY ║ + * ║ └─┬─┘ └─┬─┘ ║ └─┬─┘ ║ + * ║ Len Address ║ ~Value ║ + * ╚═══════════════╩═══════════════╝ + * + * ╔══════════ DWord-Entry ═══════╗ + * ║ 00 04 XX XX ║ FF FF FF FF ║ + * ║ └─┬─┘ └─┬─┘ ║ └───┬────┘ ║ + * ║ Len Address ║ ~Value ║ + * ╚═══════════════╩═══════════════╝ + * + */ + +#include "eeprom_stm32_defs.h" +#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) || !defined(FEE_MCU_FLASH_SIZE) || !defined(FEE_PAGE_BASE_ADDRESS) +# error "not implemented." +#endif + +/* These bits indicate that the length of data which was wrote to log space */ +#define FEE_BYTE_FLAG 0x00010000 +#define FEE_WORD_FLAG 0x00020000 +#define FEE_DWORD_FLAG 0x00040000 + +/* Flash byte value after erase */ +#define FEE_EMPTY_BYTE ((uint8_t)0xFF) +/* Flash double byte value after erase */ +#define FEE_EMPTY_DBYTE ((uint16_t)0xFFFF) +/* Flash word value after erase */ +#define FEE_EMPTY_WORD ((uint32_t)0xFFFFFFFF) +/* Flash double word value after erase */ +#define FEE_EMPTY_DWORD ((uint64_t)0xFFFFFFFFFFFFFFFF) + +/* Size of combined compacted eeprom and write log pages */ +#define FEE_DENSITY_MAX_SIZE (FEE_PAGE_COUNT * FEE_PAGE_SIZE) + +#ifndef FEE_MCU_FLASH_SIZE_IGNORE_CHECK /* *TODO: Get rid of this check */ +# if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024) +# pragma message STR(FEE_DENSITY_MAX_SIZE) " > " STR(FEE_MCU_FLASH_SIZE * 1024) +# error emulated eeprom: FEE_DENSITY_MAX_SIZE is greater than available flash size +# endif +#endif + +/* Size of emulated eeprom */ +#ifdef FEE_DENSITY_BYTES +# if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE) +# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE) +# error emulated eeprom: FEE_DENSITY_BYTES exceeds FEE_DENSITY_MAX_SIZE +# endif +# if (FEE_DENSITY_BYTES == FEE_DENSITY_MAX_SIZE) +# pragma message STR(FEE_DENSITY_BYTES) " == " STR(FEE_DENSITY_MAX_SIZE) +# warning emulated eeprom: FEE_DENSITY_BYTES leaves no room for a write log. This will greatly increase the flash wear rate! +# endif +# if FEE_DENSITY_BYTES > FEE_ADDRESS_MAX_SIZE +# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_ADDRESS_MAX_SIZE) +# error emulated eeprom: FEE_DENSITY_BYTES is greater than FEE_ADDRESS_MAX_SIZE allows +# endif +# if ((FEE_DENSITY_BYTES) % 8) != 0 +# error emulated eeprom: FEE_DENSITY_BYTES must be a multiple of 8 +# endif +#else +/* Default to one page of allocated space used for emulated eeprom, 3 pages for write log */ +# define FEE_DENSITY_BYTES FEE_PAGE_SIZE +#endif + +/* Size of write log */ +#ifdef FEE_WRITE_LOG_BYTES +# if ((FEE_DENSITY_BYTES + FEE_WRITE_LOG_BYTES) > FEE_DENSITY_MAX_SIZE) +# pragma message STR(FEE_DENSITY_BYTES) " + " STR(FEE_WRITE_LOG_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE) +# error emulated eeprom: FEE_WRITE_LOG_BYTES exceeds remaining FEE_DENSITY_MAX_SIZE +# endif +# if ((FEE_WRITE_LOG_BYTES) % 8) != 0 +# error emulated eeprom: FEE_WRITE_LOG_BYTES must be a multiple of 8 +# endif +#else +/* Default to use all remaining space */ +# define FEE_WRITE_LOG_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES) +#endif + +/* In-memory contents of emulated eeprom for faster access */ +/* *TODO: Implement page swapping */ +static uint64_t DWordBuf[FEE_DENSITY_BYTES / 8]; +static uint8_t *DataBuf = (uint8_t *)DWordBuf; + +/* Pointer to the first available slot within the write log */ +static uint32_t *empty_slot; + +/* Start of the emulated eeprom compacted flash area */ +#define FEE_COMPACTED_BASE_ADDRESS FEE_PAGE_BASE_ADDRESS +/* End of the emulated eeprom compacted flash area */ +#define FEE_COMPACTED_LAST_ADDRESS (FEE_COMPACTED_BASE_ADDRESS + FEE_DENSITY_BYTES) +/* Start of the emulated eeprom write log */ +#define FEE_WRITE_LOG_BASE_ADDRESS FEE_COMPACTED_LAST_ADDRESS +/* End of the emulated eeprom write log */ +#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES) + +uint16_t EEPROM_Init(void) { + /* Load emulated eeprom contents from compacted flash into memory */ + uint32_t *src = (uint32_t *)FEE_COMPACTED_BASE_ADDRESS; + uint32_t *dest = (uint32_t *)DataBuf; + for (; src < (uint32_t *)FEE_COMPACTED_LAST_ADDRESS; ++src, ++dest) { + *dest = ~*src; + } + + /* Replay write log */ + uint32_t *log_addr; + for (log_addr = (uint32_t *)FEE_WRITE_LOG_BASE_ADDRESS; log_addr < (uint32_t *)FEE_WRITE_LOG_LAST_ADDRESS; log_addr += 2) { + uint32_t address = *log_addr; + uint32_t data = ~*(log_addr + 1); + if (address == FEE_EMPTY_WORD) { + break; + } + /* Check if value is in bytes */ + else if ((address & FEE_BYTE_FLAG) == FEE_BYTE_FLAG) { + uint8_t value = (uint8_t)(data & 0xFF); + uint16_t addr = (uint16_t)address; + DataBuf[addr] = value; + } + /* Check if value is in words */ + else if ((address & FEE_WORD_FLAG) == FEE_WORD_FLAG) { + uint16_t value = (uint16_t)(data & 0xFFFF); + uint16_t addr = (uint16_t)address; + *(uint16_t *)(&DataBuf[addr]) = value; + } + /* Check if value is in double words */ + else if ((address & FEE_DWORD_FLAG) == FEE_DWORD_FLAG) { + uint32_t value = data; + uint16_t addr = (uint16_t)address; + *(uint32_t *)(&DataBuf[addr]) = value; + } + } + + empty_slot = log_addr; + + return FEE_DENSITY_BYTES; +} + +/* Clear flash contents (doesn't touch in-memory DataBuf) */ +static void eeprom_clear(void) { + FLASH_Unlock(); + + for (uint16_t page_num = 0; page_num < FEE_PAGE_COUNT; ++page_num) { + FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)); + } + + FLASH_Lock(); + + empty_slot = (uint32_t *)FEE_WRITE_LOG_BASE_ADDRESS; +} + +/* Erase emulated eeprom */ +void EEPROM_Erase(void) { + /* Erase compacted pages and write log */ + eeprom_clear(); + /* re-initialize to reset DataBuf */ + EEPROM_Init(); +} + +/* Compact write log */ +static uint8_t eeprom_compact(void) { + /* Erase compacted pages and write log */ + eeprom_clear(); + + FLASH_Unlock(); + + FLASH_Status final_status = FLASH_COMPLETE; + + /* Write emulated eeprom contents from memory to compacted flash */ + uint64_t *src = (uint64_t *)DataBuf; + uint32_t dest = FEE_COMPACTED_BASE_ADDRESS; + uint64_t value; + for (; dest < FEE_COMPACTED_LAST_ADDRESS; ++src, dest += 8) { + value = *src; + if (value) { + FLASH_Status status = FLASH_ProgramDoubleWord(dest, ~value); + if (status != FLASH_COMPLETE) final_status = status; + } + } + + FLASH_Lock(); + + return final_status; +} + +static uint8_t eeprom_write_direct_entry(uint16_t Address) { + /* Check if we can just write this directly to the compacted flash area */ + uint32_t directAddress = FEE_COMPACTED_BASE_ADDRESS + (Address & 0xFFF8); + + /* Write the value directly to the compacted area without a log entry */ + if (*(uint64_t *)directAddress == FEE_EMPTY_DWORD) { + /* Write the value directly to the compacted area without a log entry */ + uint64_t value = ~*(uint64_t *)(&DataBuf[Address & 0xFFF8]); + + /* Early exit if a write isn't needed */ + if (value == FEE_EMPTY_DWORD) return FLASH_COMPLETE; + + FLASH_Unlock(); + + /* write to flash */ + FLASH_Status status = FLASH_ProgramDoubleWord(directAddress, value); + + FLASH_Lock(); + + return status; + } + return 0; +} + +static uint8_t eeprom_write_log_byte_entry(uint16_t Address) { + /* if we can't find an empty spot, we must compact emulated eeprom */ + if (empty_slot >= (uint32_t *)FEE_WRITE_LOG_LAST_ADDRESS) { + /* compact the write log into the compacted flash area */ + return eeprom_compact(); + } + + FLASH_Unlock(); + + /* Pack address and value into the same word */ + uint64_t value = (((uint64_t)(~DataBuf[Address])) << 32) | (FEE_BYTE_FLAG) | Address; + + /* write to flash */ + FLASH_Status status = FLASH_ProgramDoubleWord((uint32_t)empty_slot, value); + + empty_slot += 2; + + FLASH_Lock(); + + return status; +} + +static uint8_t eeprom_write_log_word_entry(uint16_t Address) { + /* if we can't find an empty spot, we must compact emulated eeprom */ + if (empty_slot >= (uint32_t *)FEE_WRITE_LOG_LAST_ADDRESS) { + /* compact the write log into the compacted flash area */ + return eeprom_compact(); + } + + FLASH_Unlock(); + + /* Pack address and value into the same word */ + uint64_t value = (((uint64_t)(~(*(uint16_t *)&DataBuf[Address]))) << 32) | (FEE_WORD_FLAG) | Address; + + /* write to flash */ + FLASH_Status status = FLASH_ProgramDoubleWord((uint32_t)empty_slot, value); + + empty_slot += 2; + + FLASH_Lock(); + + return status; +} + +static uint8_t eeprom_write_log_dword_entry(uint16_t Address) { + /* if we can't find an empty spot, we must compact emulated eeprom */ + if (empty_slot >= (uint32_t *)FEE_WRITE_LOG_LAST_ADDRESS) { + /* compact the write log into the compacted flash area */ + return eeprom_compact(); + } + + FLASH_Unlock(); + + /* Pack address and value into the same word */ + uint64_t value = (((uint64_t)(~(*(uint32_t *)&DataBuf[Address]))) << 32) | (FEE_DWORD_FLAG) | Address; + + /* write to flash */ + FLASH_Status status = FLASH_ProgramDoubleWord((uint32_t)empty_slot, value); + + empty_slot += 2; + + FLASH_Lock(); + + return status; +} + +uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) { + /* if the address is out-of-bounds, do nothing */ + if (Address >= (FEE_DENSITY_BYTES)) { + return FLASH_BAD_ADDRESS; + } + + /* if the value is the same, don't bother writing it */ + if (DataBuf[Address] == DataByte) { + return 0; + } + + /* keep DataBuf cache in sync */ + DataBuf[Address] = DataByte; + + /* perform the write into flash memory */ + /* First, attempt to write directly into the compacted flash area */ + FLASH_Status status = eeprom_write_direct_entry(Address); + + if (!status) { + eeprom_write_log_byte_entry(Address); + } + + return status; +} + +uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) { + /* if the address is out-of-bounds, do nothing */ + if (Address >= (FEE_DENSITY_BYTES)) { + return FLASH_BAD_ADDRESS; + } + + /* if the value is the same, don't bother writing it */ + if (*(uint16_t *)&DataBuf[Address] == DataWord) { + return 0; + } + + /* keep DataBuf cache in sync */ + *(uint16_t *)(&DataBuf[Address]) = DataWord; + + /* perform the write into flash memory */ + /* First, attempt to write directly into the compacted flash area */ + FLASH_Status status = eeprom_write_direct_entry(Address); + + if (!status) { + eeprom_write_log_word_entry(Address); + } + + return status; +} + +uint8_t EEPROM_WriteDataDWord(uint16_t Address, uint32_t DataDWord) { + /* if the address is out-of-bounds, do nothing */ + if (Address >= (FEE_DENSITY_BYTES)) { + return FLASH_BAD_ADDRESS; + } + + /* if the value is the same, don't bother writing it */ + if (*(uint32_t *)&DataBuf[Address] == DataDWord) { + return 0; + } + + /* keep DataBuf cache in sync */ + *(uint32_t *)&DataBuf[Address] = DataDWord; + + /* perform the write into flash memory */ + /* First, attempt to write directly into the compacted flash area */ + FLASH_Status status = eeprom_write_direct_entry(Address); + + if (!status) { + eeprom_write_log_dword_entry(Address); + } + + return status; +} + +uint8_t EEPROM_ReadDataByte(uint16_t Address) { + uint8_t DataByte = 0xFF; + + if (Address < FEE_DENSITY_BYTES) { + DataByte = DataBuf[Address]; + } + + return DataByte; +} + +uint16_t EEPROM_ReadDataWord(uint16_t Address) { + uint16_t DataWord = 0xFFFF; + + if (Address < FEE_DENSITY_BYTES - 1) { + /* Check word alignment */ + if (Address % 2) { + DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8); + } else { + DataWord = *(uint16_t *)(&DataBuf[Address]); + } + } + + return DataWord; +} + +/***************************************************************************** + * Bind to eeprom_driver.c + *******************************************************************************/ +void eeprom_driver_init(void) { EEPROM_Init(); } + +void eeprom_driver_erase(void) { EEPROM_Erase(); } + +void eeprom_read_block(void *buf, const void *addr, size_t len) { + const uint8_t *src = (const uint8_t *)addr; + uint8_t * dest = (uint8_t *)buf; + + /* Check word alignment */ + if (len && (uint32_t)src % 2) { + /* Read the unaligned first byte */ + *dest++ = EEPROM_ReadDataByte((const uintptr_t)((uint16_t *)src)); + --len; + } + + uint16_t value; + bool aligned = ((uint32_t)dest % 2 == 0); + while (len > 1) { + value = EEPROM_ReadDataWord((const uintptr_t)((uint16_t *)src)); + if (aligned) { + *(uint16_t *)dest = value; + dest += 2; + } else { + *dest++ = value; + *dest++ = value >> 8; + } + src += 2; + len -= 2; + } + if (len) { + *dest = EEPROM_ReadDataByte((const uintptr_t)src); + } +} + +void eeprom_write_block(const void *buf, void *addr, size_t len) { + uint8_t * dest = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + uint8_t write_len; + + while (len > 0) { + /* Check and try to write double word fisrt */ + if ((uintptr_t)dest % 4 == 0 && len >= 4) { + uint32_t dwvalue; + bool dwaligned = ((uint32_t)src % 4 == 0); + + if (dwaligned) { + dwvalue = *(uint32_t *)src; + } else { + dwvalue = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8) | (*(uint8_t *)(src + 2) << 16) | (*(uint8_t *)(src + 3) << 24); + } + EEPROM_WriteDataDWord((uintptr_t)((uint16_t *)dest), dwvalue); + write_len = 4; + } + /* Check and try to write word */ + else if ((uintptr_t)dest % 2 == 0 && len >= 2) { + uint16_t wvalue; + bool waligned = ((uintptr_t)src % 2 == 0); + + if (waligned) { + wvalue = *(uint16_t *)src; + } else { + wvalue = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8); + } + EEPROM_WriteDataWord((uintptr_t)((uint16_t *)dest), wvalue); + write_len = 2; + } else { + /* Write the unaligned or single byte */ + EEPROM_WriteDataByte((uintptr_t)dest++, *src++); + write_len = 1; + } + + dest += write_len; + src += write_len; + len -= write_len; + } +} \ No newline at end of file diff --git a/platforms/chibios/eeprom_stm32_l4.h b/platforms/chibios/eeprom_stm32_l4.h new file mode 100644 index 000000000000..eb29d4d87d22 --- /dev/null +++ b/platforms/chibios/eeprom_stm32_l4.h @@ -0,0 +1,34 @@ +/* + * This software is experimental and a work in progress. + * Under no circumstances should these files be used in relation to any critical system(s). + * Use of these files is at your own risk. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by + * Artur F. + * + * Modifications for QMK and STM32L432 by lalalademaxiya1 & lokher + * + * To add a new MCU, please provide the flash page size and the total flash size in Kb. + * The number of available pages must be at least two. Only one page for the total EEPROM size. + * It is recommend to set the number of log page to 3~5 times of data page for better Wear leveling. + * + */ + +#pragma once + +typedef unsigned long long uint64_t; + +uint16_t EEPROM_Init(void); +void EEPROM_Erase(void); +uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte); +uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord); +uint8_t EEPROM_WriteDataDWord(uint16_t Address, uint32_t DataDWord); +uint8_t EEPROM_ReadDataByte(uint16_t Address); +uint16_t EEPROM_ReadDataWord(uint16_t Address); diff --git a/platforms/chibios/flash_stm32.c b/platforms/chibios/flash_stm32.c index 72c41b8b784d..6953a3ead319 100644 --- a/platforms/chibios/flash_stm32.c +++ b/platforms/chibios/flash_stm32.c @@ -51,6 +51,15 @@ static uint8_t ADDR2PAGE(uint32_t Page_Address) { } #endif +#if defined(STM32L4XX) +# define FLASH_SR_PGERR FLASH_SR_PROGERR +# define FLASH_OBR_OPTERR FLASH_SR_OPERR +# define FLASH_KEY1 0x45670123U +# define FLASH_KEY2 0xCDEF89ABU + +static uint32_t ADDR2PAGE(uint32_t Page_Address) { return (Page_Address - FLASH_BASE) / 0x800; } +#endif + /* Delay definition */ #define EraseTimeout ((uint32_t)0x00000FFF) #define ProgramTimeout ((uint32_t)0x0000001F) @@ -128,6 +137,9 @@ FLASH_Status FLASH_ErasePage(uint32_t Page_Address) { #if defined(FLASH_CR_SNB) FLASH->CR &= ~FLASH_CR_SNB; FLASH->CR |= FLASH_CR_SER | (ADDR2PAGE(Page_Address) << FLASH_CR_SNB_Pos); +#elif defined(FLASH_CR_PNB) + FLASH->CR &= ~FLASH_CR_PNB; + FLASH->CR |= FLASH_CR_PER | (ADDR2PAGE(Page_Address) << FLASH_CR_PNB_Pos); #else FLASH->CR |= FLASH_CR_PER; FLASH->AR = Page_Address; @@ -140,6 +152,8 @@ FLASH_Status FLASH_ErasePage(uint32_t Page_Address) { /* if the erase operation is completed, disable the configured Bits */ #if defined(FLASH_CR_SNB) FLASH->CR &= ~(FLASH_CR_SER | FLASH_CR_SNB); +#elif defined(FLASH_CR_PNB) + FLASH->CR &= ~(FLASH_CR_PER | FLASH_CR_PNB); #else FLASH->CR &= ~FLASH_CR_PER; #endif @@ -150,6 +164,46 @@ FLASH_Status FLASH_ErasePage(uint32_t Page_Address) { return status; } +#if defined(STM32L4XX) +/** + * @brief Programs double words at a specified address. + * @param Address: specifies the address to be programmed. + * @param Data: specifies the data to be programmed. + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_ProgramDoubleWord(uint32_t Address, uint64_t Data) { + FLASH_Status status = FLASH_BAD_ADDRESS; + + if (IS_FLASH_ADDRESS(Address)) { + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + if (status == FLASH_COMPLETE) { + /* if the previous operation is completed, proceed to program the new data */ + /* disable data cache first */ + FLASH->ACR &= ~FLASH_ACR_DCEN; + FLASH->CR |= FLASH_CR_PG; + *(__IO uint32_t*)Address = (uint32_t)Data; + __ISB(); + *(__IO uint32_t*)(Address + 4U) = (uint32_t)(Data >> 32); + /* Wait for last operation to be completed */ + status = FLASH_WaitForLastOperation(ProgramTimeout); + if (status != FLASH_TIMEOUT) { + /* if the program operation is completed, disable the PG Bit */ + FLASH->CR &= ~FLASH_CR_PG; + } + FLASH->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR); + /* reset data cache */ + FLASH->ACR |= FLASH_ACR_DCRST; + FLASH->ACR &= ~FLASH_ACR_DCRST; + /* enable data cache */ + FLASH->ACR |= FLASH_ACR_DCEN; + } + } + return status; +} + +#else /** * @brief Programs a half word at a specified address. * @param Address: specifies the address to be programmed. @@ -166,10 +220,10 @@ FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) { if (status == FLASH_COMPLETE) { /* if the previous operation is completed, proceed to program the new data */ -#if defined(FLASH_CR_PSIZE) +# if defined(FLASH_CR_PSIZE) FLASH->CR &= ~FLASH_CR_PSIZE; FLASH->CR |= FLASH_CR_PSIZE_0; -#endif +# endif FLASH->CR |= FLASH_CR_PG; *(__IO uint16_t*)Address = Data; /* Wait for last operation to be completed */ @@ -183,6 +237,7 @@ FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) { } return status; } +#endif /** * @brief Unlocks the FLASH Program Erase Controller. diff --git a/platforms/chibios/flash_stm32.h b/platforms/chibios/flash_stm32.h index 6c66642ec5c7..97f8ea7cfe2b 100644 --- a/platforms/chibios/flash_stm32.h +++ b/platforms/chibios/flash_stm32.h @@ -35,6 +35,7 @@ typedef enum { FLASH_BUSY = 1, FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_ERROR_OPT, FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout); FLASH_Status FLASH_ErasePage(uint32_t Page_Address); FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data); +FLASH_Status FLASH_ProgramDoubleWord(uint32_t Address, uint64_t Data); void FLASH_Unlock(void); void FLASH_Lock(void);