summaryrefslogtreecommitdiffstats
path: root/platforms/chibios
diff options
context:
space:
mode:
Diffstat (limited to 'platforms/chibios')
-rw-r--r--platforms/chibios/eeprom_stm32_defs.h7
-rw-r--r--platforms/chibios/eeprom_stm32_l4.c598
-rw-r--r--platforms/chibios/eeprom_stm32_l4.h34
-rw-r--r--platforms/chibios/flash_stm32.c55
-rw-r--r--platforms/chibios/flash_stm32.h1
5 files changed, 693 insertions, 2 deletions
diff --git a/platforms/chibios/eeprom_stm32_defs.h b/platforms/chibios/eeprom_stm32_defs.h
index 57d0440330..c944e5a30f 100644
--- a/platforms/chibios/eeprom_stm32_defs.h
+++ b/platforms/chibios/eeprom_stm32_defs.h
@@ -17,6 +17,9 @@
#include <hal.h>
+#define __PRINT_MACRO(x) #x
+#define PRINT_MARCO(x) #x"=" __PRINT_MACRO(x)
+
#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT)
# if defined(STM32F103xB) || defined(STM32F042x6) || defined(GD32VF103C8) || defined(GD32VF103CB)
# ifndef FEE_PAGE_SIZE
@@ -25,7 +28,7 @@
# ifndef FEE_PAGE_COUNT
# define FEE_PAGE_COUNT 2 // How many pages are used
# endif
-# elif defined(STM32F103xE) || defined(STM32F303xC) || defined(STM32F303xE) || defined(STM32F072xB) || defined(STM32F070xB)
+# elif defined(STM32F103xE) || defined(STM32F303xC) || defined(STM32F303xE) || defined(STM32F072xB) || defined(STM32F070xB) || defined(STM32L432xx)
# ifndef FEE_PAGE_SIZE
# define FEE_PAGE_SIZE 0x800 // Page size = 2KByte
# endif
@@ -47,7 +50,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 0000000000..33a6d284c8
--- /dev/null
+++ b/platforms/chibios/eeprom_stm32_l4.c
@@ -0,0 +1,598 @@
+/*
+ * 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 <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "flash_stm32.h"
+#include "eeprom_stm32_defs.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;
+/* ECC error flag, set in NMI when 2 bits ECC error is detected */
+static bool eccd;
+
+/* 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) {
+ eccd = false;
+
+ /* 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);
+
+ /* Break loop if ECC error is detected */
+ if (eccd) break;
+
+ 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;
+
+ /* Give more chance for NMI interrupt */
+ asm("nop");
+
+ /* Reset eeprom data if ECC error is detected*/
+ if (eccd) return 0;
+
+ 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 void blank_check(uint32_t Address) {
+ /* Align address to 64 bit */
+ Address &= (~0x07);
+
+ /* Check if target address is blank */
+ if (*(uint64_t *)(Address) != 0xFFFFFFFFFFFFFFFF) {
+ if ((Address & (FEE_PAGE_SIZE - 1)) == 0) {
+ /* Erase current page if first byte is not empty */
+ FLASH_Unlock();
+ FLASH_ErasePage(Address);
+ FLASH_Lock();
+ } else {
+ /* Compact data if we encounter non empty target address after page head */
+ eeprom_compact();
+ }
+ }
+}
+
+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();
+ }
+
+ blank_check((uint32_t)empty_slot);
+
+ 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();
+ }
+
+ blank_check((uint32_t)empty_slot);
+
+ 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();
+ }
+
+ blank_check((uint32_t)empty_slot);
+
+ 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) {
+ 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 - 1)) {
+ 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) {
+ 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 - 3)) {
+ 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) {
+ 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
+ *******************************************************************************/
+uint16_t eeprom_driver_init(void) {
+ return 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;
+ }
+}
+
+void NMI_Handler(void) {
+ if (FLASH->ECCR & FLASH_ECCR_ECCD) {
+ /* Clear ECCD error NMI */
+ FLASH->ECCR = FLASH_ECCR_ECCD;
+ eccd = true;
+ }
+}
diff --git a/platforms/chibios/eeprom_stm32_l4.h b/platforms/chibios/eeprom_stm32_l4.h
new file mode 100644
index 0000000000..77b36d5cce
--- /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 72c41b8b78..59c77e0bc5 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 | FLASH_SR_PGAERR | FLASH_SR_SIZERR | FLASH_SR_PGSERR)
+# 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.
@@ -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 6c66642ec5..97f8ea7cfe 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);