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 }