qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

eeprom_stm32_L0_L1.c (4139B)


      1 /* Copyright 2020 Nick Brassel (tzarc)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 2 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     15  */
     16 
     17 #include <stdint.h>
     18 #include <string.h>
     19 
     20 #include <hal.h>
     21 #include "eeprom_driver.h"
     22 #include "eeprom_stm32_L0_L1.h"
     23 
     24 #define EEPROM_BASE_ADDR 0x08080000
     25 #define EEPROM_ADDR(offset) (EEPROM_BASE_ADDR + (offset))
     26 #define EEPROM_PTR(offset) ((__IO uint8_t *)EEPROM_ADDR(offset))
     27 #define EEPROM_BYTE(location, offset) (*(EEPROM_PTR(((uint32_t)location) + ((uint32_t)offset))))
     28 #define EEPROM_WORD(location) (*(__IO uint32_t *)EEPROM_PTR(location))
     29 
     30 #define BUFFER_BYTE(buffer, offset) (*(((uint8_t *)buffer) + offset))
     31 
     32 #define FLASH_PEKEY1 0x89ABCDEF
     33 #define FLASH_PEKEY2 0x02030405
     34 
     35 static inline void STM32_L0_L1_EEPROM_WaitNotBusy(void) {
     36     while (FLASH->SR & FLASH_SR_BSY) {
     37         __WFI();
     38     }
     39 }
     40 
     41 static inline void STM32_L0_L1_EEPROM_Unlock(void) {
     42     STM32_L0_L1_EEPROM_WaitNotBusy();
     43     if (FLASH->PECR & FLASH_PECR_PELOCK) {
     44         FLASH->PEKEYR = FLASH_PEKEY1;
     45         FLASH->PEKEYR = FLASH_PEKEY2;
     46     }
     47 }
     48 
     49 static inline void STM32_L0_L1_EEPROM_Lock(void) {
     50     STM32_L0_L1_EEPROM_WaitNotBusy();
     51     FLASH->PECR |= FLASH_PECR_PELOCK;
     52 }
     53 
     54 void eeprom_driver_init(void) {}
     55 
     56 void eeprom_driver_format(bool erase) {
     57     if (erase) {
     58         eeprom_driver_erase();
     59     }
     60 }
     61 
     62 void eeprom_driver_erase(void) {
     63     STM32_L0_L1_EEPROM_Unlock();
     64 
     65     for (size_t offset = 0; offset < STM32_ONBOARD_EEPROM_SIZE; offset += sizeof(uint32_t)) {
     66 #ifdef QMK_MCU_SERIES_STM32L0XX
     67         FLASH->PECR |= FLASH_PECR_ERASE | FLASH_PECR_DATA;
     68 #endif
     69 
     70         EEPROM_WORD(offset) = (uint32_t)0;
     71 
     72         STM32_L0_L1_EEPROM_WaitNotBusy();
     73 #ifdef QMK_MCU_SERIES_STM32L0XX
     74         FLASH->PECR &= ~(FLASH_PECR_ERASE | FLASH_PECR_DATA);
     75 #endif
     76     }
     77 
     78     STM32_L0_L1_EEPROM_Lock();
     79 }
     80 
     81 void eeprom_read_block(void *buf, const void *addr, size_t len) {
     82     for (size_t offset = 0; offset < len; ++offset) {
     83         // Drop out if we've hit the limit of the EEPROM
     84         if ((((uint32_t)addr) + offset) >= STM32_ONBOARD_EEPROM_SIZE) {
     85             break;
     86         }
     87 
     88         STM32_L0_L1_EEPROM_WaitNotBusy();
     89         BUFFER_BYTE(buf, offset) = EEPROM_BYTE(addr, offset);
     90     }
     91 }
     92 
     93 void eeprom_write_block(const void *buf, void *addr, size_t len) {
     94     // use word-aligned write to overcome issues with writing null bytes
     95     uint32_t start_addr = (uint32_t)addr;
     96     if (start_addr >= (STM32_ONBOARD_EEPROM_SIZE)) {
     97         return;
     98     }
     99     uint32_t max_len = (STM32_ONBOARD_EEPROM_SIZE)-start_addr;
    100     if (len > max_len) {
    101         len = max_len;
    102     }
    103     uint32_t end_addr = start_addr + len;
    104 
    105     uint32_t aligned_start = start_addr & ~0x3;
    106     uint32_t aligned_end   = (end_addr + 3) & ~0x3;
    107 
    108     STM32_L0_L1_EEPROM_Unlock();
    109     for (uint32_t word_addr = aligned_start; word_addr < aligned_end; word_addr += 4) {
    110         uint32_t existing_word = EEPROM_WORD(word_addr);
    111         uint32_t new_word      = existing_word;
    112 
    113         // Update the relevant bytes in the word
    114         for (int i = 0; i < 4; i++) {
    115             uint32_t byte_addr = word_addr + i;
    116             if (byte_addr >= start_addr && byte_addr < end_addr) {
    117                 uint8_t new_byte = BUFFER_BYTE(buf, byte_addr - start_addr);
    118                 new_word         = (new_word & ~(0xFFU << (i * 8))) | ((uint32_t)new_byte << (i * 8));
    119             }
    120         }
    121 
    122         // Only write if the word has changed
    123         if (new_word != existing_word) {
    124             STM32_L0_L1_EEPROM_WaitNotBusy();
    125             EEPROM_WORD(word_addr) = new_word;
    126         }
    127     }
    128     STM32_L0_L1_EEPROM_Lock();
    129 }