From 1ba3126ae43915b712c759285c5e37f736159989 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sun, 11 Jul 2021 19:20:41 -0400
Subject: [PATCH] eeprom_stm32: add stm32f4 series support

based on code by @yulei in https://github.com/qmk/qmk_firmware/pull/7803
---
 common_features.mk                     |  5 +++
 tmk_core/common/chibios/eeprom_stm32.c |  4 ++
 tmk_core/common/chibios/eeprom_stm32.h | 12 +++++-
 tmk_core/common/chibios/flash_stm32.c  | 60 ++++++++++++++++----------
 tmk_core/common/chibios/flash_stm32.h  | 20 +++++++++
 5 files changed, 76 insertions(+), 25 deletions(-)

diff --git a/common_features.mk b/common_features.mk
index 26eaf4829b..5ec5c2e96c 100644
--- a/common_features.mk
+++ b/common_features.mk
@@ -171,6 +171,11 @@ else
         OPT_DEFS += -DEEPROM_DRIVER
         COMMON_VPATH += $(DRIVER_PATH)/eeprom
         SRC += eeprom_driver.c eeprom_stm32_L0_L1.c
+      else ifeq ($(MCU_SERIES), STM32F4xx)
+        SRC += $(PLATFORM_COMMON_DIR)/eeprom_stm32.c
+        SRC += $(PLATFORM_COMMON_DIR)/flash_stm32.c
+        OPT_DEFS += -DEEPROM_EMU_STM32F411xC
+        OPT_DEFS += -DSTM32_EEPROM_ENABLE
       else
         # This will effectively work the same as "transient" if not supported by the chip
         SRC += $(PLATFORM_COMMON_DIR)/eeprom_teensy.c
diff --git a/tmk_core/common/chibios/eeprom_stm32.c b/tmk_core/common/chibios/eeprom_stm32.c
index 2b0c78f714..056f886c6f 100644
--- a/tmk_core/common/chibios/eeprom_stm32.c
+++ b/tmk_core/common/chibios/eeprom_stm32.c
@@ -56,8 +56,12 @@ void EEPROM_Init(void) {
 static void eeprom_clear(void) {
     FLASH_Unlock();
 
+#if defined(EEPROM_EMU_STM32F4)
+    FLASH_ErasePage(FEE_SECTOR_ID);
+#else
     for (uint32_t erase_address = SNAPSHOT_START; erase_address < WRITELOG_END; erase_address += FEE_PAGE_SIZE)
         FLASH_ErasePage(erase_address);
+#endif
 
     FLASH_Lock();
 
diff --git a/tmk_core/common/chibios/eeprom_stm32.h b/tmk_core/common/chibios/eeprom_stm32.h
index 4fefd29f30..4a15ee7cc2 100644
--- a/tmk_core/common/chibios/eeprom_stm32.h
+++ b/tmk_core/common/chibios/eeprom_stm32.h
@@ -37,6 +37,8 @@
 #    define MCU_STM32F072CB
 #elif defined(EEPROM_EMU_STM32F042x6)
 #    define MCU_STM32F042K6
+#elif defined(EEPROM_EMU_STM32F411xC)
+#    define MCU_STM32F411xC
 #endif
 
 /* The page_size * density_pages should provide 8k of space, split 4k/4k between snapshot and writelog in the default config */
@@ -47,6 +49,10 @@
 #    elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) || defined(MCU_STM32F103RD) || defined(MCU_STM32F303CC) || defined(MCU_STM32F072CB)
 #        define FEE_PAGE_SIZE (uint16_t)0x800  // Page size = 2KByte
 #        define FEE_DENSITY_PAGES 4            // How many pages are used
+#    elif defined(MCU_STM32F411xC)
+#        define FEE_PAGE_SIZE (uint32_t)0x20000  // Page size = 128KByte
+#        define FEE_DENSITY_PAGES 1              // How many pages are used
+#        define FEE_SECTOR_ID 5                  // sector id of the flash
 #    else
 #        error "No MCU type specified and FEE_DENSITY_PAGES not defined.\
 Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)\
@@ -65,6 +71,8 @@ or define FEE_DENSITY_PAGES yourself."
 #        define FEE_MCU_FLASH_SIZE 384  // Size in Kb
 #    elif defined(MCU_STM32F303CC)
 #        define FEE_MCU_FLASH_SIZE 256  // Size in Kb
+#    elif defined(MCU_STM32F411xC)
+#        define FEE_MCU_FLASH_SIZE 256  // Size in Kb
 #    else
 #        error "No MCU type specified and FEE_MCU_FLASH_SIZE not defined.\
 Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)\
@@ -83,8 +91,8 @@ or define FEE_MCU_FLASH_SIZE yourself."
 #endif
 
 #ifndef FEE_WRITELOG_SIZE
-    /* Size of eeprom writelog, in bytes */
-    #define FEE_WRITELOG_SIZE 4096
+    /* Size of eeprom writelog, in bytes. By default, use all the remaining amount of flash bank. */
+    #define FEE_WRITELOG_SIZE (FEE_DENSITY_PAGES * FEE_PAGE_SIZE - FEE_SNAPSHOT_SIZE)
 #endif
 
 /* Flash word value after erase */
diff --git a/tmk_core/common/chibios/flash_stm32.c b/tmk_core/common/chibios/flash_stm32.c
index e8b3dc4bc9..0bead21039 100644
--- a/tmk_core/common/chibios/flash_stm32.c
+++ b/tmk_core/common/chibios/flash_stm32.c
@@ -16,28 +16,24 @@
  * Modifications for QMK and STM32F303 by Yiancar
  */
 
-#if defined(EEPROM_EMU_STM32F303xC)
-#    define STM32F303xC
-#    include "stm32f3xx.h"
-#elif defined(EEPROM_EMU_STM32F103xB)
-#    define STM32F103xB
-#    include "stm32f1xx.h"
-#elif defined(EEPROM_EMU_STM32F072xB)
-#    define STM32F072xB
-#    include "stm32f0xx.h"
-#elif defined(EEPROM_EMU_STM32F042x6)
-#    define STM32F042x6
-#    include "stm32f0xx.h"
-#else
-#    error "not implemented."
-#endif
-
 #include "flash_stm32.h"
 
 #if defined(EEPROM_EMU_STM32F103xB)
 #    define FLASH_SR_WRPERR FLASH_SR_WRPRTERR
 #endif
 
+#if defined(EEPROM_EMU_STM32F4)
+#    define FLASH_PSIZE_HFWORD FLASH_CR_PSIZE_0
+#    define FLASH_PSIZE_WORD FLASH_CR_PSIZE_1
+#    define FLASH_CR_SNB_POS 3
+/* the flash key was not defined in the CMSIS used by current chibios */
+#    define FLASH_KEY1 0x45670123
+#    define FLASH_KEY2 0xCDEF89AB
+#    define FLASH_SR_FLAGS (FLASH_SR_PGAERR|FLASH_SR_PGPERR|FLASH_SR_PGSERR|FLASH_SR_WRPERR)
+#else
+#    define FLASH_SR_FLAGS (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR)
+#endif
+
 /* Delay definition */
 #define EraseTimeout ((uint32_t)0x00000FFF)
 #define ProgramTimeout ((uint32_t)0x0000001F)
@@ -64,11 +60,14 @@ static void delay(void) {
 FLASH_Status FLASH_GetStatus(void) {
     if ((FLASH->SR & FLASH_SR_BSY) == FLASH_SR_BSY) return FLASH_BUSY;
 
+#if defined(EEPROM_EMU_STM32F4)
+    if ((FLASH->SR & (FLASH_SR_PGSERR | FLASH_SR_PGPERR | FLASH_SR_PGAERR))) return FLASH_ERROR_PG;
+    if ((FLASH->SR & FLASH_SR_WRPERR)) return FLASH_ERROR_WRP;
+#else
     if ((FLASH->SR & FLASH_SR_PGERR) != 0) return FLASH_ERROR_PG;
-
     if ((FLASH->SR & FLASH_SR_WRPERR) != 0) return FLASH_ERROR_WRP;
-
     if ((FLASH->SR & FLASH_OBR_OPTERR) != 0) return FLASH_ERROR_OPT;
+#endif
 
     return FLASH_COMPLETE;
 }
@@ -97,30 +96,41 @@ FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) {
 
 /**
  * @brief  Erases a specified FLASH page.
- * @param  Page_Address: The page address to be erased.
+ * @param  Page_Address: The page address or sector to be erased.
  * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG,
  *   FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  */
 FLASH_Status FLASH_ErasePage(uint32_t Page_Address) {
     FLASH_Status status = FLASH_COMPLETE;
+#ifndef EEPROM_EMU_STM32F4
     /* Check the parameters */
     ASSERT(IS_FLASH_ADDRESS(Page_Address));
+#endif
     /* Wait for last operation to be completed */
     status = FLASH_WaitForLastOperation(EraseTimeout);
 
     if (status == FLASH_COMPLETE) {
         /* if the previous operation is completed, proceed to erase the page */
+#if defined(EEPROM_EMU_STM32F4)
+        FLASH->CR &= ~FLASH_CR_SNB;
+        FLASH->CR |= FLASH_CR_SER | (Page_Address << FLASH_CR_SNB_POS);
+#else
         FLASH->CR |= FLASH_CR_PER;
         FLASH->AR = Page_Address;
+#endif
         FLASH->CR |= FLASH_CR_STRT;
 
         /* Wait for last operation to be completed */
         status = FLASH_WaitForLastOperation(EraseTimeout);
         if (status != FLASH_TIMEOUT) {
-            /* if the erase operation is completed, disable the PER Bit */
+            /* clear the SER or PER Bit */
+#if defined(EEPROM_EMU_STM32F4)
+            FLASH->CR &= ~(FLASH_CR_SER | FLASH_CR_SNB);
+#else
             FLASH->CR &= ~FLASH_CR_PER;
+#endif
         }
-        FLASH->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR);
+        FLASH_ClearFlag(FLASH_SR_FLAGS);
     }
     /* Return the Erase Status */
     return status;
@@ -136,11 +146,15 @@ FLASH_Status FLASH_ErasePage(uint32_t Page_Address) {
 FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) {
     FLASH_Status status = FLASH_BAD_ADDRESS;
 
-    if (IS_FLASH_ADDRESS(Address)) {
+    if (IS_FLASH_ADDRESS(Address) && (Address % sizeof(Data)) == 0) {
         /* 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 */
+#if defined(EEPROM_EMU_STM32F4)
+            FLASH->CR &= ~FLASH_CR_PSIZE;
+            FLASH->CR |= FLASH_PSIZE_HFWORD;
+#endif
             FLASH->CR |= FLASH_CR_PG;
             *(__IO uint16_t*)Address = Data;
             /* Wait for last operation to be completed */
@@ -149,7 +163,7 @@ FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) {
                 /* 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);
+            FLASH_ClearFlag(FLASH_SR_FLAGS);
         }
     }
     return status;
diff --git a/tmk_core/common/chibios/flash_stm32.h b/tmk_core/common/chibios/flash_stm32.h
index 90d5bff47e..f50507f3a5 100644
--- a/tmk_core/common/chibios/flash_stm32.h
+++ b/tmk_core/common/chibios/flash_stm32.h
@@ -22,6 +22,26 @@
 extern "C" {
 #endif
 
+#if defined(EEPROM_EMU_STM32F303xC)
+#    define STM32F303xC
+#    include "stm32f3xx.h"
+#elif defined(EEPROM_EMU_STM32F103xB)
+#    define STM32F103xB
+#    include "stm32f1xx.h"
+#elif defined(EEPROM_EMU_STM32F072xB)
+#    define STM32F072xB
+#    include "stm32f0xx.h"
+#elif defined(EEPROM_EMU_STM32F042x6)
+#    define STM32F042x6
+#    include "stm32f0xx.h"
+#elif defined(EEPROM_EMU_STM32F411xC)
+#    define EEPROM_EMU_STM32F4
+#    define STM32F411xE
+#    include "stm32f4xx.h"
+#else
+#    error "not implemented."
+#endif
+
 #include <ch.h>
 #include <hal.h>