From 621ce29a53e9e94e085fbd86c0b7134e9df4bfe5 Mon Sep 17 00:00:00 2001 From: yiancar Date: Wed, 29 Aug 2018 23:14:49 +0300 Subject: STM32 EEPROM Emulation (#3741) * STM32 EEPROM Emulation - Added EEPROM emulation libaries from libmaple and Arduino_STM32. https://github.com/rogerclarkmelbourne/Arduino_STM32 and https://github.com/leaflabs/libmaple. - Renamed teensy EEPROM library and added conditional selection of library. - Remapped EEPROM memory map for 16 byte blocks (as is with STM32f3xx MCUs). - Added EEPROM initialization in main.c of Chibios. - Added EEPROM format to clear the emulated pages when EEPROM is marked as invalid. * Fixed ifdef --- tmk_core/common/chibios/eeprom.c | 632 ------------------------------ tmk_core/common/chibios/eeprom_stm32.c | 673 ++++++++++++++++++++++++++++++++ tmk_core/common/chibios/eeprom_stm32.h | 89 +++++ tmk_core/common/chibios/eeprom_teensy.c | 632 ++++++++++++++++++++++++++++++ tmk_core/common/chibios/flash_stm32.c | 180 +++++++++ tmk_core/common/chibios/flash_stm32.h | 53 +++ tmk_core/common/eeconfig.c | 11 + tmk_core/common/eeconfig.h | 16 + 8 files changed, 1654 insertions(+), 632 deletions(-) delete mode 100644 tmk_core/common/chibios/eeprom.c create mode 100755 tmk_core/common/chibios/eeprom_stm32.c create mode 100755 tmk_core/common/chibios/eeprom_stm32.h create mode 100644 tmk_core/common/chibios/eeprom_teensy.c create mode 100755 tmk_core/common/chibios/flash_stm32.c create mode 100755 tmk_core/common/chibios/flash_stm32.h (limited to 'tmk_core/common') diff --git a/tmk_core/common/chibios/eeprom.c b/tmk_core/common/chibios/eeprom.c deleted file mode 100644 index 9061b790c4..0000000000 --- a/tmk_core/common/chibios/eeprom.c +++ /dev/null @@ -1,632 +0,0 @@ -#include "ch.h" -#include "hal.h" - -#include "eeconfig.h" - -/*************************************/ -/* Hardware backend */ -/* */ -/* Code from PJRC/Teensyduino */ -/*************************************/ - -/* Teensyduino Core Library - * http://www.pjrc.com/teensy/ - * Copyright (c) 2013 PJRC.COM, LLC. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * 1. The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 2. If the Software is incorporated into a build system that allows - * selection among a list of target devices, then similar target - * devices manufactured by PJRC.COM must be included in the list of - * target devices and selectable in the same manner. - * - * 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. - */ - - -#if defined(K20x) /* chip selection */ -/* Teensy 3.0, 3.1, 3.2; mchck; infinity keyboard */ - -// The EEPROM is really RAM with a hardware-based backup system to -// flash memory. Selecting a smaller size EEPROM allows more wear -// leveling, for higher write endurance. If you edit this file, -// set this to the smallest size your application can use. Also, -// due to Freescale's implementation, writing 16 or 32 bit words -// (aligned to 2 or 4 byte boundaries) has twice the endurance -// compared to writing 8 bit bytes. -// -#define EEPROM_SIZE 32 - -// Writing unaligned 16 or 32 bit data is handled automatically when -// this is defined, but at a cost of extra code size. Without this, -// any unaligned write will cause a hard fault exception! If you're -// absolutely sure all 16 and 32 bit writes will be aligned, you can -// remove the extra unnecessary code. -// -#define HANDLE_UNALIGNED_WRITES - -// Minimum EEPROM Endurance -// ------------------------ -#if (EEPROM_SIZE == 2048) // 35000 writes/byte or 70000 writes/word - #define EEESIZE 0x33 -#elif (EEPROM_SIZE == 1024) // 75000 writes/byte or 150000 writes/word - #define EEESIZE 0x34 -#elif (EEPROM_SIZE == 512) // 155000 writes/byte or 310000 writes/word - #define EEESIZE 0x35 -#elif (EEPROM_SIZE == 256) // 315000 writes/byte or 630000 writes/word - #define EEESIZE 0x36 -#elif (EEPROM_SIZE == 128) // 635000 writes/byte or 1270000 writes/word - #define EEESIZE 0x37 -#elif (EEPROM_SIZE == 64) // 1275000 writes/byte or 2550000 writes/word - #define EEESIZE 0x38 -#elif (EEPROM_SIZE == 32) // 2555000 writes/byte or 5110000 writes/word - #define EEESIZE 0x39 -#endif - -/** \brief eeprom initialization - * - * FIXME: needs doc - */ -void eeprom_initialize(void) -{ - uint32_t count=0; - uint16_t do_flash_cmd[] = { - 0xf06f, 0x037f, 0x7003, 0x7803, - 0xf013, 0x0f80, 0xd0fb, 0x4770}; - uint8_t status; - - if (FTFL->FCNFG & FTFL_FCNFG_RAMRDY) { - // FlexRAM is configured as traditional RAM - // We need to reconfigure for EEPROM usage - FTFL->FCCOB0 = 0x80; // PGMPART = Program Partition Command - FTFL->FCCOB4 = EEESIZE; // EEPROM Size - FTFL->FCCOB5 = 0x03; // 0K for Dataflash, 32K for EEPROM backup - __disable_irq(); - // do_flash_cmd() must execute from RAM. Luckily the C syntax is simple... - (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFL->FSTAT)); - __enable_irq(); - status = FTFL->FSTAT; - if (status & (FTFL_FSTAT_RDCOLERR|FTFL_FSTAT_ACCERR|FTFL_FSTAT_FPVIOL)) { - FTFL->FSTAT = (status & (FTFL_FSTAT_RDCOLERR|FTFL_FSTAT_ACCERR|FTFL_FSTAT_FPVIOL)); - return; // error - } - } - // wait for eeprom to become ready (is this really necessary?) - while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { - if (++count > 20000) break; - } -} - -#define FlexRAM ((uint8_t *)0x14000000) - -/** \brief eeprom read byte - * - * FIXME: needs doc - */ -uint8_t eeprom_read_byte(const uint8_t *addr) -{ - uint32_t offset = (uint32_t)addr; - if (offset >= EEPROM_SIZE) return 0; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); - return FlexRAM[offset]; -} - -/** \brief eeprom read word - * - * FIXME: needs doc - */ -uint16_t eeprom_read_word(const uint16_t *addr) -{ - uint32_t offset = (uint32_t)addr; - if (offset >= EEPROM_SIZE-1) return 0; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); - return *(uint16_t *)(&FlexRAM[offset]); -} - -/** \brief eeprom read dword - * - * FIXME: needs doc - */ -uint32_t eeprom_read_dword(const uint32_t *addr) -{ - uint32_t offset = (uint32_t)addr; - if (offset >= EEPROM_SIZE-3) return 0; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); - return *(uint32_t *)(&FlexRAM[offset]); -} - -/** \brief eeprom read block - * - * FIXME: needs doc - */ -void eeprom_read_block(void *buf, const void *addr, uint32_t len) -{ - uint32_t offset = (uint32_t)addr; - uint8_t *dest = (uint8_t *)buf; - uint32_t end = offset + len; - - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); - if (end > EEPROM_SIZE) end = EEPROM_SIZE; - while (offset < end) { - *dest++ = FlexRAM[offset++]; - } -} - -/** \brief eeprom is ready - * - * FIXME: needs doc - */ -int eeprom_is_ready(void) -{ - return (FTFL->FCNFG & FTFL_FCNFG_EEERDY) ? 1 : 0; -} - -/** \brief flexram wait - * - * FIXME: needs doc - */ -static void flexram_wait(void) -{ - while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { - // TODO: timeout - } -} - -/** \brief eeprom_write_byte - * - * FIXME: needs doc - */ -void eeprom_write_byte(uint8_t *addr, uint8_t value) -{ - uint32_t offset = (uint32_t)addr; - - if (offset >= EEPROM_SIZE) return; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); - if (FlexRAM[offset] != value) { - FlexRAM[offset] = value; - flexram_wait(); - } -} - -/** \brief eeprom write word - * - * FIXME: needs doc - */ -void eeprom_write_word(uint16_t *addr, uint16_t value) -{ - uint32_t offset = (uint32_t)addr; - - if (offset >= EEPROM_SIZE-1) return; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); -#ifdef HANDLE_UNALIGNED_WRITES - if ((offset & 1) == 0) { -#endif - if (*(uint16_t *)(&FlexRAM[offset]) != value) { - *(uint16_t *)(&FlexRAM[offset]) = value; - flexram_wait(); - } -#ifdef HANDLE_UNALIGNED_WRITES - } else { - if (FlexRAM[offset] != value) { - FlexRAM[offset] = value; - flexram_wait(); - } - if (FlexRAM[offset + 1] != (value >> 8)) { - FlexRAM[offset + 1] = value >> 8; - flexram_wait(); - } - } -#endif -} - -/** \brief eeprom write dword - * - * FIXME: needs doc - */ -void eeprom_write_dword(uint32_t *addr, uint32_t value) -{ - uint32_t offset = (uint32_t)addr; - - if (offset >= EEPROM_SIZE-3) return; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); -#ifdef HANDLE_UNALIGNED_WRITES - switch (offset & 3) { - case 0: -#endif - if (*(uint32_t *)(&FlexRAM[offset]) != value) { - *(uint32_t *)(&FlexRAM[offset]) = value; - flexram_wait(); - } - return; -#ifdef HANDLE_UNALIGNED_WRITES - case 2: - if (*(uint16_t *)(&FlexRAM[offset]) != value) { - *(uint16_t *)(&FlexRAM[offset]) = value; - flexram_wait(); - } - if (*(uint16_t *)(&FlexRAM[offset + 2]) != (value >> 16)) { - *(uint16_t *)(&FlexRAM[offset + 2]) = value >> 16; - flexram_wait(); - } - return; - default: - if (FlexRAM[offset] != value) { - FlexRAM[offset] = value; - flexram_wait(); - } - if (*(uint16_t *)(&FlexRAM[offset + 1]) != (value >> 8)) { - *(uint16_t *)(&FlexRAM[offset + 1]) = value >> 8; - flexram_wait(); - } - if (FlexRAM[offset + 3] != (value >> 24)) { - FlexRAM[offset + 3] = value >> 24; - flexram_wait(); - } - } -#endif -} - -/** \brief eeprom write block - * - * FIXME: needs doc - */ -void eeprom_write_block(const void *buf, void *addr, uint32_t len) -{ - uint32_t offset = (uint32_t)addr; - const uint8_t *src = (const uint8_t *)buf; - - if (offset >= EEPROM_SIZE) return; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); - if (len >= EEPROM_SIZE) len = EEPROM_SIZE; - if (offset + len >= EEPROM_SIZE) len = EEPROM_SIZE - offset; - while (len > 0) { - uint32_t lsb = offset & 3; - if (lsb == 0 && len >= 4) { - // write aligned 32 bits - uint32_t val32; - val32 = *src++; - val32 |= (*src++ << 8); - val32 |= (*src++ << 16); - val32 |= (*src++ << 24); - if (*(uint32_t *)(&FlexRAM[offset]) != val32) { - *(uint32_t *)(&FlexRAM[offset]) = val32; - flexram_wait(); - } - offset += 4; - len -= 4; - } else if ((lsb == 0 || lsb == 2) && len >= 2) { - // write aligned 16 bits - uint16_t val16; - val16 = *src++; - val16 |= (*src++ << 8); - if (*(uint16_t *)(&FlexRAM[offset]) != val16) { - *(uint16_t *)(&FlexRAM[offset]) = val16; - flexram_wait(); - } - offset += 2; - len -= 2; - } else { - // write 8 bits - uint8_t val8 = *src++; - if (FlexRAM[offset] != val8) { - FlexRAM[offset] = val8; - flexram_wait(); - } - offset++; - len--; - } - } -} - -/* -void do_flash_cmd(volatile uint8_t *fstat) -{ - *fstat = 0x80; - while ((*fstat & 0x80) == 0) ; // wait -} -00000000 : - 0: f06f 037f mvn.w r3, #127 ; 0x7f - 4: 7003 strb r3, [r0, #0] - 6: 7803 ldrb r3, [r0, #0] - 8: f013 0f80 tst.w r3, #128 ; 0x80 - c: d0fb beq.n 6 - e: 4770 bx lr -*/ - -#elif defined(KL2x) /* chip selection */ -/* Teensy LC (emulated) */ - -#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) - -extern uint32_t __eeprom_workarea_start__; -extern uint32_t __eeprom_workarea_end__; - -#define EEPROM_SIZE 128 - -static uint32_t flashend = 0; - -void eeprom_initialize(void) -{ - const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); - - do { - if (*p++ == 0xFFFF) { - flashend = (uint32_t)(p - 2); - return; - } - } while (p < (uint16_t *)SYMVAL(__eeprom_workarea_end__)); - flashend = (uint32_t)((uint16_t *)SYMVAL(__eeprom_workarea_end__) - 1); -} - -uint8_t eeprom_read_byte(const uint8_t *addr) -{ - uint32_t offset = (uint32_t)addr; - const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); - const uint16_t *end = (const uint16_t *)((uint32_t)flashend); - uint16_t val; - uint8_t data=0xFF; - - if (!end) { - eeprom_initialize(); - end = (const uint16_t *)((uint32_t)flashend); - } - if (offset < EEPROM_SIZE) { - while (p <= end) { - val = *p++; - if ((val & 255) == offset) data = val >> 8; - } - } - return data; -} - -static void flash_write(const uint16_t *code, uint32_t addr, uint32_t data) -{ - // with great power comes great responsibility.... - uint32_t stat; - *(uint32_t *)&(FTFA->FCCOB3) = 0x06000000 | (addr & 0x00FFFFFC); - *(uint32_t *)&(FTFA->FCCOB7) = data; - __disable_irq(); - (*((void (*)(volatile uint8_t *))((uint32_t)code | 1)))(&(FTFA->FSTAT)); - __enable_irq(); - stat = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR|FTFA_FSTAT_ACCERR|FTFA_FSTAT_FPVIOL); - if (stat) { - FTFA->FSTAT = stat; - } - MCM->PLACR |= MCM_PLACR_CFCC; -} - -void eeprom_write_byte(uint8_t *addr, uint8_t data) -{ - uint32_t offset = (uint32_t)addr; - const uint16_t *p, *end = (const uint16_t *)((uint32_t)flashend); - uint32_t i, val, flashaddr; - uint16_t do_flash_cmd[] = { - 0x2380, 0x7003, 0x7803, 0xb25b, 0x2b00, 0xdafb, 0x4770}; - uint8_t buf[EEPROM_SIZE]; - - if (offset >= EEPROM_SIZE) return; - if (!end) { - eeprom_initialize(); - end = (const uint16_t *)((uint32_t)flashend); - } - if (++end < (uint16_t *)SYMVAL(__eeprom_workarea_end__)) { - val = (data << 8) | offset; - flashaddr = (uint32_t)end; - flashend = flashaddr; - if ((flashaddr & 2) == 0) { - val |= 0xFFFF0000; - } else { - val <<= 16; - val |= 0x0000FFFF; - } - flash_write(do_flash_cmd, flashaddr, val); - } else { - for (i=0; i < EEPROM_SIZE; i++) { - buf[i] = 0xFF; - } - val = 0; - for (p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); p < (uint16_t *)SYMVAL(__eeprom_workarea_end__); p++) { - val = *p; - if ((val & 255) < EEPROM_SIZE) { - buf[val & 255] = val >> 8; - } - } - buf[offset] = data; - for (flashaddr=(uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); flashaddr < (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_end__); flashaddr += 1024) { - *(uint32_t *)&(FTFA->FCCOB3) = 0x09000000 | flashaddr; - __disable_irq(); - (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFA->FSTAT)); - __enable_irq(); - val = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR|FTFA_FSTAT_ACCERR|FTFA_FSTAT_FPVIOL);; - if (val) FTFA->FSTAT = val; - MCM->PLACR |= MCM_PLACR_CFCC; - } - flashaddr=(uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); - for (i=0; i < EEPROM_SIZE; i++) { - if (buf[i] == 0xFF) continue; - if ((flashaddr & 2) == 0) { - val = (buf[i] << 8) | i; - } else { - val = val | (buf[i] << 24) | (i << 16); - flash_write(do_flash_cmd, flashaddr, val); - } - flashaddr += 2; - } - flashend = flashaddr; - if ((flashaddr & 2)) { - val |= 0xFFFF0000; - flash_write(do_flash_cmd, flashaddr, val); - } - } -} - -/* -void do_flash_cmd(volatile uint8_t *fstat) -{ - *fstat = 0x80; - while ((*fstat & 0x80) == 0) ; // wait -} -00000000 : - 0: 2380 movs r3, #128 ; 0x80 - 2: 7003 strb r3, [r0, #0] - 4: 7803 ldrb r3, [r0, #0] - 6: b25b sxtb r3, r3 - 8: 2b00 cmp r3, #0 - a: dafb bge.n 4 - c: 4770 bx lr -*/ - - -uint16_t eeprom_read_word(const uint16_t *addr) -{ - const uint8_t *p = (const uint8_t *)addr; - return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8); -} - -uint32_t eeprom_read_dword(const uint32_t *addr) -{ - const uint8_t *p = (const uint8_t *)addr; - return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8) - | (eeprom_read_byte(p+2) << 16) | (eeprom_read_byte(p+3) << 24); -} - -void eeprom_read_block(void *buf, const void *addr, uint32_t len) -{ - const uint8_t *p = (const uint8_t *)addr; - uint8_t *dest = (uint8_t *)buf; - while (len--) { - *dest++ = eeprom_read_byte(p++); - } -} - -int eeprom_is_ready(void) -{ - return 1; -} - -void eeprom_write_word(uint16_t *addr, uint16_t value) -{ - uint8_t *p = (uint8_t *)addr; - eeprom_write_byte(p++, value); - eeprom_write_byte(p, value >> 8); -} - -void eeprom_write_dword(uint32_t *addr, uint32_t value) -{ - uint8_t *p = (uint8_t *)addr; - eeprom_write_byte(p++, value); - eeprom_write_byte(p++, value >> 8); - eeprom_write_byte(p++, value >> 16); - eeprom_write_byte(p, value >> 24); -} - -void eeprom_write_block(const void *buf, void *addr, uint32_t len) -{ - uint8_t *p = (uint8_t *)addr; - const uint8_t *src = (const uint8_t *)buf; - while (len--) { - eeprom_write_byte(p++, *src++); - } -} - -#else -// No EEPROM supported, so emulate it - -#define EEPROM_SIZE 32 -static uint8_t buffer[EEPROM_SIZE]; - -uint8_t eeprom_read_byte(const uint8_t *addr) { - uint32_t offset = (uint32_t)addr; - return buffer[offset]; -} - -void eeprom_write_byte(uint8_t *addr, uint8_t value) { - uint32_t offset = (uint32_t)addr; - buffer[offset] = value; -} - -uint16_t eeprom_read_word(const uint16_t *addr) { - const uint8_t *p = (const uint8_t *)addr; - return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8); -} - -uint32_t eeprom_read_dword(const uint32_t *addr) { - const uint8_t *p = (const uint8_t *)addr; - return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8) - | (eeprom_read_byte(p+2) << 16) | (eeprom_read_byte(p+3) << 24); -} - -void eeprom_read_block(void *buf, const void *addr, uint32_t len) { - const uint8_t *p = (const uint8_t *)addr; - uint8_t *dest = (uint8_t *)buf; - while (len--) { - *dest++ = eeprom_read_byte(p++); - } -} - -void eeprom_write_word(uint16_t *addr, uint16_t value) { - uint8_t *p = (uint8_t *)addr; - eeprom_write_byte(p++, value); - eeprom_write_byte(p, value >> 8); -} - -void eeprom_write_dword(uint32_t *addr, uint32_t value) { - uint8_t *p = (uint8_t *)addr; - eeprom_write_byte(p++, value); - eeprom_write_byte(p++, value >> 8); - eeprom_write_byte(p++, value >> 16); - eeprom_write_byte(p, value >> 24); -} - -void eeprom_write_block(const void *buf, void *addr, uint32_t len) { - uint8_t *p = (uint8_t *)addr; - const uint8_t *src = (const uint8_t *)buf; - while (len--) { - eeprom_write_byte(p++, *src++); - } -} - -#endif /* chip selection */ -// The update functions just calls write for now, but could probably be optimized - -void eeprom_update_byte(uint8_t *addr, uint8_t value) { - eeprom_write_byte(addr, value); -} - -void eeprom_update_word(uint16_t *addr, uint16_t value) { - uint8_t *p = (uint8_t *)addr; - eeprom_write_byte(p++, value); - eeprom_write_byte(p, value >> 8); -} - -void eeprom_update_dword(uint32_t *addr, uint32_t value) { - uint8_t *p = (uint8_t *)addr; - eeprom_write_byte(p++, value); - eeprom_write_byte(p++, value >> 8); - eeprom_write_byte(p++, value >> 16); - eeprom_write_byte(p, value >> 24); -} - -void eeprom_update_block(const void *buf, void *addr, uint32_t len) { - uint8_t *p = (uint8_t *)addr; - const uint8_t *src = (const uint8_t *)buf; - while (len--) { - eeprom_write_byte(p++, *src++); - } -} diff --git a/tmk_core/common/chibios/eeprom_stm32.c b/tmk_core/common/chibios/eeprom_stm32.c new file mode 100755 index 0000000000..3c19451223 --- /dev/null +++ b/tmk_core/common/chibios/eeprom_stm32.c @@ -0,0 +1,673 @@ +/* + * 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 https://github.com/rogerclarkmelbourne/Arduino_STM32 and + * https://github.com/leaflabs/libmaple + * + * Modifications for QMK and STM32F303 by Yiancar + */ + +#include "eeprom_stm32.h" + + FLASH_Status EE_ErasePage(uint32_t); + + uint16_t EE_CheckPage(uint32_t, uint16_t); + uint16_t EE_CheckErasePage(uint32_t, uint16_t); + uint16_t EE_Format(void); + uint32_t EE_FindValidPage(void); + uint16_t EE_GetVariablesCount(uint32_t, uint16_t); + uint16_t EE_PageTransfer(uint32_t, uint32_t, uint16_t); + uint16_t EE_VerifyPageFullWriteVariable(uint16_t, uint16_t); + + uint32_t PageBase0 = EEPROM_PAGE0_BASE; + uint32_t PageBase1 = EEPROM_PAGE1_BASE; + uint32_t PageSize = EEPROM_PAGE_SIZE; + uint16_t Status = EEPROM_NOT_INIT; + +// See http://www.st.com/web/en/resource/technical/document/application_note/CD00165693.pdf + +/** + * @brief Check page for blank + * @param page base address + * @retval Success or error + * EEPROM_BAD_FLASH: page not empty after erase + * EEPROM_OK: page blank + */ +uint16_t EE_CheckPage(uint32_t pageBase, uint16_t status) +{ + uint32_t pageEnd = pageBase + (uint32_t)PageSize; + + // Page Status not EEPROM_ERASED and not a "state" + if ((*(__IO uint16_t*)pageBase) != EEPROM_ERASED && (*(__IO uint16_t*)pageBase) != status) + return EEPROM_BAD_FLASH; + for(pageBase += 4; pageBase < pageEnd; pageBase += 4) + if ((*(__IO uint32_t*)pageBase) != 0xFFFFFFFF) // Verify if slot is empty + return EEPROM_BAD_FLASH; + return EEPROM_OK; +} + +/** + * @brief Erase page with increment erase counter (page + 2) + * @param page base address + * @retval Success or error + * FLASH_COMPLETE: success erase + * - Flash error code: on write Flash error + */ +FLASH_Status EE_ErasePage(uint32_t pageBase) +{ + FLASH_Status FlashStatus; + uint16_t data = (*(__IO uint16_t*)(pageBase)); + if ((data == EEPROM_ERASED) || (data == EEPROM_VALID_PAGE) || (data == EEPROM_RECEIVE_DATA)) + data = (*(__IO uint16_t*)(pageBase + 2)) + 1; + else + data = 0; + + FlashStatus = FLASH_ErasePage(pageBase); + if (FlashStatus == FLASH_COMPLETE) + FlashStatus = FLASH_ProgramHalfWord(pageBase + 2, data); + + return FlashStatus; +} + +/** + * @brief Check page for blank and erase it + * @param page base address + * @retval Success or error + * - Flash error code: on write Flash error + * - EEPROM_BAD_FLASH: page not empty after erase + * - EEPROM_OK: page blank + */ +uint16_t EE_CheckErasePage(uint32_t pageBase, uint16_t status) +{ + uint16_t FlashStatus; + if (EE_CheckPage(pageBase, status) != EEPROM_OK) + { + FlashStatus = EE_ErasePage(pageBase); + if (FlashStatus != FLASH_COMPLETE) + return FlashStatus; + return EE_CheckPage(pageBase, status); + } + return EEPROM_OK; +} + +/** + * @brief Find valid Page for write or read operation + * @param Page0: Page0 base address + * Page1: Page1 base address + * @retval Valid page address (PAGE0 or PAGE1) or NULL in case of no valid page was found + */ +uint32_t EE_FindValidPage(void) +{ + uint16_t status0 = (*(__IO uint16_t*)PageBase0); // Get Page0 actual status + uint16_t status1 = (*(__IO uint16_t*)PageBase1); // Get Page1 actual status + + if (status0 == EEPROM_VALID_PAGE && status1 == EEPROM_ERASED) + return PageBase0; + if (status1 == EEPROM_VALID_PAGE && status0 == EEPROM_ERASED) + return PageBase1; + + return 0; +} + +/** + * @brief Calculate unique variables in EEPROM + * @param start: address of first slot to check (page + 4) + * @param end: page end address + * @param address: 16 bit virtual address of the variable to excluse (or 0XFFFF) + * @retval count of variables + */ +uint16_t EE_GetVariablesCount(uint32_t pageBase, uint16_t skipAddress) +{ + uint16_t varAddress, nextAddress; + uint32_t idx; + uint32_t pageEnd = pageBase + (uint32_t)PageSize; + uint16_t count = 0; + + for (pageBase += 6; pageBase < pageEnd; pageBase += 4) + { + varAddress = (*(__IO uint16_t*)pageBase); + if (varAddress == 0xFFFF || varAddress == skipAddress) + continue; + + count++; + for(idx = pageBase + 4; idx < pageEnd; idx += 4) + { + nextAddress = (*(__IO uint16_t*)idx); + if (nextAddress == varAddress) + { + count--; + break; + } + } + } + return count; +} + +/** + * @brief Transfers last updated variables data from the full Page to an empty one. + * @param newPage: new page base address + * @param oldPage: old page base address + * @param SkipAddress: 16 bit virtual address of the variable (or 0xFFFF) + * @retval Success or error status: + * - FLASH_COMPLETE: on success + * - EEPROM_OUT_SIZE: if valid new page is full + * - Flash error code: on write Flash error + */ +uint16_t EE_PageTransfer(uint32_t newPage, uint32_t oldPage, uint16_t SkipAddress) +{ + uint32_t oldEnd, newEnd; + uint32_t oldIdx, newIdx, idx; + uint16_t address, data, found; + FLASH_Status FlashStatus; + + // Transfer process: transfer variables from old to the new active page + newEnd = newPage + ((uint32_t)PageSize); + + // Find first free element in new page + for (newIdx = newPage + 4; newIdx < newEnd; newIdx += 4) + if ((*(__IO uint32_t*)newIdx) == 0xFFFFFFFF) // Verify if element + break; // contents are 0xFFFFFFFF + if (newIdx >= newEnd) + return EEPROM_OUT_SIZE; + + oldEnd = oldPage + 4; + oldIdx = oldPage + (uint32_t)(PageSize - 2); + + for (; oldIdx > oldEnd; oldIdx -= 4) + { + address = *(__IO uint16_t*)oldIdx; + if (address == 0xFFFF || address == SkipAddress) + continue; // it's means that power off after write data + + found = 0; + for (idx = newPage + 6; idx < newIdx; idx += 4) + if ((*(__IO uint16_t*)(idx)) == address) + { + found = 1; + break; + } + + if (found) + continue; + + if (newIdx < newEnd) + { + data = (*(__IO uint16_t*)(oldIdx - 2)); + + FlashStatus = FLASH_ProgramHalfWord(newIdx, data); + if (FlashStatus != FLASH_COMPLETE) + return FlashStatus; + + FlashStatus = FLASH_ProgramHalfWord(newIdx + 2, address); + if (FlashStatus != FLASH_COMPLETE) + return FlashStatus; + + newIdx += 4; + } + else + return EEPROM_OUT_SIZE; + } + + // Erase the old Page: Set old Page status to EEPROM_EEPROM_ERASED status + data = EE_CheckErasePage(oldPage, EEPROM_ERASED); + if (data != EEPROM_OK) + return data; + + // Set new Page status + FlashStatus = FLASH_ProgramHalfWord(newPage, EEPROM_VALID_PAGE); + if (FlashStatus != FLASH_COMPLETE) + return FlashStatus; + + return EEPROM_OK; +} + +/** + * @brief Verify if active page is full and Writes variable in EEPROM. + * @param Address: 16 bit virtual address of the variable + * @param Data: 16 bit data to be written as variable value + * @retval Success or error status: + * - FLASH_COMPLETE: on success + * - EEPROM_PAGE_FULL: if valid page is full (need page transfer) + * - EEPROM_NO_VALID_PAGE: if no valid page was found + * - EEPROM_OUT_SIZE: if EEPROM size exceeded + * - Flash error code: on write Flash error + */ +uint16_t EE_VerifyPageFullWriteVariable(uint16_t Address, uint16_t Data) +{ + FLASH_Status FlashStatus; + uint32_t idx, pageBase, pageEnd, newPage; + uint16_t count; + + // Get valid Page for write operation + pageBase = EE_FindValidPage(); + if (pageBase == 0) + return EEPROM_NO_VALID_PAGE; + + // Get the valid Page end Address + pageEnd = pageBase + PageSize; // Set end of page + + for (idx = pageEnd - 2; idx > pageBase; idx -= 4) + { + if ((*(__IO uint16_t*)idx) == Address) // Find last value for address + { + count = (*(__IO uint16_t*)(idx - 2)); // Read last data + if (count == Data) + return EEPROM_OK; + if (count == 0xFFFF) + { + FlashStatus = FLASH_ProgramHalfWord(idx - 2, Data); // Set variable data + if (FlashStatus == FLASH_COMPLETE) + return EEPROM_OK; + } + break; + } + } + + // Check each active page address starting from begining + for (idx = pageBase + 4; idx < pageEnd; idx += 4) + if ((*(__IO uint32_t*)idx) == 0xFFFFFFFF) // Verify if element + { // contents are 0xFFFFFFFF + FlashStatus = FLASH_ProgramHalfWord(idx, Data); // Set variable data + if (FlashStatus != FLASH_COMPLETE) + return FlashStatus; + FlashStatus = FLASH_ProgramHalfWord(idx + 2, Address); // Set variable virtual address + if (FlashStatus != FLASH_COMPLETE) + return FlashStatus; + return EEPROM_OK; + } + + // Empty slot not found, need page transfer + // Calculate unique variables in page + count = EE_GetVariablesCount(pageBase, Address) + 1; + if (count >= (PageSize / 4 - 1)) + return EEPROM_OUT_SIZE; + + if (pageBase == PageBase1) + newPage = PageBase0; // New page address where variable will be moved to + else + newPage = PageBase1; + + // Set the new Page status to RECEIVE_DATA status + FlashStatus = FLASH_ProgramHalfWord(newPage, EEPROM_RECEIVE_DATA); + if (FlashStatus != FLASH_COMPLETE) + return FlashStatus; + + // Write the variable passed as parameter in the new active page + FlashStatus = FLASH_ProgramHalfWord(newPage + 4, Data); + if (FlashStatus != FLASH_COMPLETE) + return FlashStatus; + + FlashStatus = FLASH_ProgramHalfWord(newPage + 6, Address); + if (FlashStatus != FLASH_COMPLETE) + return FlashStatus; + + return EE_PageTransfer(newPage, pageBase, Address); +} + +/*EEPROMClass::EEPROMClass(void) +{ + PageBase0 = EEPROM_PAGE0_BASE; + PageBase1 = EEPROM_PAGE1_BASE; + PageSize = EEPROM_PAGE_SIZE; + Status = EEPROM_NOT_INIT; +}*/ +/* +uint16_t EEPROM_init(uint32_t pageBase0, uint32_t pageBase1, uint32_t pageSize) +{ + PageBase0 = pageBase0; + PageBase1 = pageBase1; + PageSize = pageSize; + return EEPROM_init(); +}*/ + +uint16_t EEPROM_init(void) +{ + uint16_t status0 = 6, status1 = 6; + FLASH_Status FlashStatus; + + FLASH_Unlock(); + Status = EEPROM_NO_VALID_PAGE; + + status0 = (*(__IO uint16_t *)PageBase0); + status1 = (*(__IO uint16_t *)PageBase1); + + switch (status0) + { +/* + Page0 Page1 + ----- ----- + EEPROM_ERASED EEPROM_VALID_PAGE Page1 valid, Page0 erased + EEPROM_RECEIVE_DATA Page1 need set to valid, Page0 erased + EEPROM_ERASED make EE_Format + any Error: EEPROM_NO_VALID_PAGE +*/ + case EEPROM_ERASED: + if (status1 == EEPROM_VALID_PAGE) // Page0 erased, Page1 valid + Status = EE_CheckErasePage(PageBase0, EEPROM_ERASED); + else if (status1 == EEPROM_RECEIVE_DATA) // Page0 erased, Page1 receive + { + FlashStatus = FLASH_ProgramHalfWord(PageBase1, EEPROM_VALID_PAGE); + if (FlashStatus != FLASH_COMPLETE) + Status = FlashStatus; + else + Status = EE_CheckErasePage(PageBase0, EEPROM_ERASED); + } + else if (status1 == EEPROM_ERASED) // Both in erased state so format EEPROM + Status = EEPROM_format(); + break; +/* + Page0 Page1 + ----- ----- + EEPROM_RECEIVE_DATA EEPROM_VALID_PAGE Transfer Page1 to Page0 + EEPROM_ERASED Page0 need set to valid, Page1 erased + any EEPROM_NO_VALID_PAGE +*/ + case EEPROM_RECEIVE_DATA: + if (status1 == EEPROM_VALID_PAGE) // Page0 receive, Page1 valid + Status = EE_PageTransfer(PageBase0, PageBase1, 0xFFFF); + else if (status1 == EEPROM_ERASED) // Page0 receive, Page1 erased + { + Status = EE_CheckErasePage(PageBase1, EEPROM_ERASED); + if (Status == EEPROM_OK) + { + FlashStatus = FLASH_ProgramHalfWord(PageBase0, EEPROM_VALID_PAGE); + if (FlashStatus != FLASH_COMPLETE) + Status = FlashStatus; + else + Status = EEPROM_OK; + } + } + break; +/* + Page0 Page1 + ----- ----- + EEPROM_VALID_PAGE EEPROM_VALID_PAGE Error: EEPROM_NO_VALID_PAGE + EEPROM_RECEIVE_DATA Transfer Page0 to Page1 + any Page0 valid, Page1 erased +*/ + case EEPROM_VALID_PAGE: + if (status1 == EEPROM_VALID_PAGE) // Both pages valid + Status = EEPROM_NO_VALID_PAGE; + else if (status1 == EEPROM_RECEIVE_DATA) + Status = EE_PageTransfer(PageBase1, PageBase0, 0xFFFF); + else + Status = EE_CheckErasePage(PageBase1, EEPROM_ERASED); + break; +/* + Page0 Page1 + ----- ----- + any EEPROM_VALID_PAGE Page1 valid, Page0 erased + EEPROM_RECEIVE_DATA Page1 valid, Page0 erased + any EEPROM_NO_VALID_PAGE +*/ + default: + if (status1 == EEPROM_VALID_PAGE) + Status = EE_CheckErasePage(PageBase0, EEPROM_ERASED); // Check/Erase Page0 + else if (status1 == EEPROM_RECEIVE_DATA) + { + FlashStatus = FLASH_ProgramHalfWord(PageBase1, EEPROM_VALID_PAGE); + if (FlashStatus != FLASH_COMPLETE) + Status = FlashStatus; + else + Status = EE_CheckErasePage(PageBase0, EEPROM_ERASED); + } + break; + } + return Status; +} + +/** + * @brief Erases PAGE0 and PAGE1 and writes EEPROM_VALID_PAGE / 0 header to PAGE0 + * @param PAGE0 and PAGE1 base addresses + * @retval Status of the last operation (Flash write or erase) done during EEPROM formating + */ +uint16_t EEPROM_format(void) +{ + uint16_t status; + FLASH_Status FlashStatus; + + FLASH_Unlock(); + + // Erase Page0 + status = EE_CheckErasePage(PageBase0, EEPROM_VALID_PAGE); + if (status != EEPROM_OK) + return status; + if ((*(__IO uint16_t*)PageBase0) == EEPROM_ERASED) + { + // Set Page0 as valid page: Write VALID_PAGE at Page0 base address + FlashStatus = FLASH_ProgramHalfWord(PageBase0, EEPROM_VALID_PAGE); + if (FlashStatus != FLASH_COMPLETE) + return FlashStatus; + } + // Erase Page1 + return EE_CheckErasePage(PageBase1, EEPROM_ERASED); +} + +/** + * @brief Returns the erase counter for current page + * @param Data: Global variable contains the read variable value + * @retval Success or error status: + * - EEPROM_OK: if erases counter return. + * - EEPROM_NO_VALID_PAGE: if no valid page was found. + */ +uint16_t EEPROM_erases(uint16_t *Erases) +{ + uint32_t pageBase; + if (Status != EEPROM_OK) + if (EEPROM_init() != EEPROM_OK) + return Status; + + // Get active Page for read operation + pageBase = EE_FindValidPage(); + if (pageBase == 0) + return EEPROM_NO_VALID_PAGE; + + *Erases = (*(__IO uint16_t*)pageBase+2); + return EEPROM_OK; +} + +/** + * @brief Returns the last stored variable data, if found, + * which correspond to the passed virtual address + * @param Address: Variable virtual address + * @retval Data for variable or EEPROM_DEFAULT_DATA, if any errors + */ +/* +uint16_t EEPROM_read (uint16_t Address) +{ + uint16_t data; + EEPROM_read(Address, &data); + return data; +}*/ + +/** + * @brief Returns the last stored variable data, if found, + * which correspond to the passed virtual address + * @param Address: Variable virtual address + * @param Data: Pointer to data variable + * @retval Success or error status: + * - EEPROM_OK: if variable was found + * - EEPROM_BAD_ADDRESS: if the variable was not found + * - EEPROM_NO_VALID_PAGE: if no valid page was found. + */ +uint16_t EEPROM_read(uint16_t Address, uint16_t *Data) +{ + uint32_t pageBase, pageEnd; + + // Set default data (empty EEPROM) + *Data = EEPROM_DEFAULT_DATA; + + if (Status == EEPROM_NOT_INIT) + if (EEPROM_init() != EEPROM_OK) + return Status; + + // Get active Page for read operation + pageBase = EE_FindValidPage(); + if (pageBase == 0) + return EEPROM_NO_VALID_PAGE; + + // Get the valid Page end Address + pageEnd = pageBase + ((uint32_t)(PageSize - 2)); + + // Check each active page address starting from end + for (pageBase += 6; pageEnd >= pageBase; pageEnd -= 4) + if ((*(__IO uint16_t*)pageEnd) == Address) // Compare the read address with the virtual address + { + *Data = (*(__IO uint16_t*)(pageEnd - 2)); // Get content of Address-2 which is variable value + return EEPROM_OK; + } + + // Return ReadStatus value: (0: variable exist, 1: variable doesn't exist) + return EEPROM_BAD_ADDRESS; +} + +/** + * @brief Writes/upadtes variable data in EEPROM. + * @param VirtAddress: Variable virtual address + * @param Data: 16 bit data to be written + * @retval Success or error status: + * - FLASH_COMPLETE: on success + * - EEPROM_BAD_ADDRESS: if address = 0xFFFF + * - EEPROM_PAGE_FULL: if valid page is full + * - EEPROM_NO_VALID_PAGE: if no valid page was found + * - EEPROM_OUT_SIZE: if no empty EEPROM variables + * - Flash error code: on write Flash error + */ +uint16_t EEPROM_write(uint16_t Address, uint16_t Data) +{ + if (Status == EEPROM_NOT_INIT) + if (EEPROM_init() != EEPROM_OK) + return Status; + + if (Address == 0xFFFF) + return EEPROM_BAD_ADDRESS; + + // Write the variable virtual address and value in the EEPROM + uint16_t status = EE_VerifyPageFullWriteVariable(Address, Data); + return status; +} + +/** + * @brief Writes/upadtes variable data in EEPROM. + The value is written only if differs from the one already saved at the same address. + * @param VirtAddress: Variable virtual address + * @param Data: 16 bit data to be written + * @retval Success or error status: + * - EEPROM_SAME_VALUE: If new Data matches existing EEPROM Data + * - FLASH_COMPLETE: on success + * - EEPROM_BAD_ADDRESS: if address = 0xFFFF + * - EEPROM_PAGE_FULL: if valid page is full + * - EEPROM_NO_VALID_PAGE: if no valid page was found + * - EEPROM_OUT_SIZE: if no empty EEPROM variables + * - Flash error code: on write Flash error + */ +uint16_t EEPROM_update(uint16_t Address, uint16_t Data) +{ + uint16_t temp; + EEPROM_read(Address, &temp); + if (Address == Data) + return EEPROM_SAME_VALUE; + else + return EEPROM_write(Address, Data); +} + +/** + * @brief Return number of variable + * @retval Number of variables + */ +uint16_t EEPROM_count(uint16_t *Count) +{ + if (Status == EEPROM_NOT_INIT) + if (EEPROM_init() != EEPROM_OK) + return Status; + + // Get valid Page for write operation + uint32_t pageBase = EE_FindValidPage(); + if (pageBase == 0) + return EEPROM_NO_VALID_PAGE; // No valid page, return max. numbers + + *Count = EE_GetVariablesCount(pageBase, 0xFFFF); + return EEPROM_OK; +} + +uint16_t EEPROM_maxcount(void) +{ + return ((PageSize / 4)-1); +} + + +uint8_t eeprom_read_byte (const uint8_t *Address) +{ + const uint16_t p = (const uint32_t) Address; + uint16_t temp; + EEPROM_read(p, &temp); + return (uint8_t) temp; +} + +void eeprom_write_byte (uint8_t *Address, uint8_t Value) +{ + uint16_t p = (uint32_t) Address; + EEPROM_write(p, (uint16_t) Value); +} + +void eeprom_update_byte (uint8_t *Address, uint8_t Value) +{ + uint16_t p = (uint32_t) Address; + EEPROM_update(p, (uint16_t) Value); +} + +uint16_t eeprom_read_word (const uint16_t *Address) +{ + const uint16_t p = (const uint32_t) Address; + uint16_t temp; + EEPROM_read(p, &temp); + return temp; +} + +void eeprom_write_word (uint16_t *Address, uint16_t Value) +{ + uint16_t p = (uint32_t) Address; + EEPROM_write(p, Value); +} + +void eeprom_update_word (uint16_t *Address, uint16_t Value) +{ + uint16_t p = (uint32_t) Address; + EEPROM_update(p, Value); +} + +uint32_t eeprom_read_dword (const uint32_t *Address) +{ + const uint16_t p = (const uint32_t) Address; + uint16_t temp1, temp2; + EEPROM_read(p, &temp1); + EEPROM_read(p + 1, &temp2); + return temp1 | (temp2 << 16); +} + +void eeprom_write_dword (uint32_t *Address, uint32_t Value) +{ + uint16_t temp = (uint16_t) Value; + uint16_t p = (uint32_t) Address; + EEPROM_write(p, temp); + temp = (uint16_t) (Value >> 16); + EEPROM_write(p + 1, temp); +} + +void eeprom_update_dword (uint32_t *Address, uint32_t Value) +{ + uint16_t temp = (uint16_t) Value; + uint16_t p = (uint32_t) Address; + EEPROM_update(p, temp); + temp = (uint16_t) (Value >> 16); + EEPROM_update(p + 1, temp); +} diff --git a/tmk_core/common/chibios/eeprom_stm32.h b/tmk_core/common/chibios/eeprom_stm32.h new file mode 100755 index 0000000000..d06d302665 --- /dev/null +++ b/tmk_core/common/chibios/eeprom_stm32.h @@ -0,0 +1,89 @@ +/* + * 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 https://github.com/rogerclarkmelbourne/Arduino_STM32 and + * https://github.com/leaflabs/libmaple + * + * Modifications for QMK and STM32F303 by Yiancar + */ + +// This file must be modified if the MCU is not defined below. +// This library also assumes that the pages are not used by the firmware. + +#ifndef __EEPROM_H +#define __EEPROM_H + +#include "ch.h" +#include "hal.h" +#include "flash_stm32.h" + +// HACK ALERT. This definition may not match your processor +// To Do. Work out correct value for EEPROM_PAGE_SIZE on the STM32F103CT6 etc +#define MCU_STM32F303CC + +#ifndef EEPROM_PAGE_SIZE + #if defined (MCU_STM32F103RB) + #define EEPROM_PAGE_SIZE (uint16_t)0x400 /* Page size = 1KByte */ + #elif defined (MCU_STM32F103ZE) || defined (MCU_STM32F103RE) || defined (MCU_STM32F103RD) || defined (MCU_STM32F303CC) + #define EEPROM_PAGE_SIZE (uint16_t)0x800 /* Page size = 2KByte */ + #else + #error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)." + #endif +#endif + +#ifndef EEPROM_START_ADDRESS + #if defined (MCU_STM32F103RB) + #define EEPROM_START_ADDRESS ((uint32_t)(0x8000000 + 128 * 1024 - 2 * EEPROM_PAGE_SIZE)) + #elif defined (MCU_STM32F103ZE) || defined (MCU_STM32F103RE) + #define EEPROM_START_ADDRESS ((uint32_t)(0x8000000 + 512 * 1024 - 2 * EEPROM_PAGE_SIZE)) + #elif defined (MCU_STM32F103RD) + #define EEPROM_START_ADDRESS ((uint32_t)(0x8000000 + 384 * 1024 - 2 * EEPROM_PAGE_SIZE)) + #elif defined (MCU_STM32F303CC) + #define EEPROM_START_ADDRESS ((uint32_t)(0x8000000 + 250 * 1024 - 2 * EEPROM_PAGE_SIZE)) + #else + #error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)." + #endif +#endif + +/* Pages 0 and 1 base and end addresses */ +#define EEPROM_PAGE0_BASE ((uint32_t)(EEPROM_START_ADDRESS + 0x000)) +#define EEPROM_PAGE1_BASE ((uint32_t)(EEPROM_START_ADDRESS + EEPROM_PAGE_SIZE)) + +/* Page status definitions */ +#define EEPROM_ERASED ((uint16_t)0xFFFF) /* PAGE is empty */ +#define EEPROM_RECEIVE_DATA ((uint16_t)0xEEEE) /* PAGE is marked to receive data */ +#define EEPROM_VALID_PAGE ((uint16_t)0x0000) /* PAGE containing valid data */ + +/* Page full define */ +enum uint16_t + { + EEPROM_OK = ((uint16_t)0x0000), + EEPROM_OUT_SIZE = ((uint16_t)0x0081), + EEPROM_BAD_ADDRESS = ((uint16_t)0x0082), + EEPROM_BAD_FLASH = ((uint16_t)0x0083), + EEPROM_NOT_INIT = ((uint16_t)0x0084), + EEPROM_SAME_VALUE = ((uint16_t)0x0085), + EEPROM_NO_VALID_PAGE = ((uint16_t)0x00AB) + }; + +#define EEPROM_DEFAULT_DATA 0xFFFF + + uint16_t EEPROM_init(void); + uint16_t EEPROM_format(void); + uint16_t EEPROM_erases(uint16_t *); + uint16_t EEPROM_read (uint16_t address, uint16_t *data); + uint16_t EEPROM_write(uint16_t address, uint16_t data); + uint16_t EEPROM_update(uint16_t address, uint16_t data); + uint16_t EEPROM_count(uint16_t *); + uint16_t EEPROM_maxcount(void); + +#endif /* __EEPROM_H */ diff --git a/tmk_core/common/chibios/eeprom_teensy.c b/tmk_core/common/chibios/eeprom_teensy.c new file mode 100644 index 0000000000..9061b790c4 --- /dev/null +++ b/tmk_core/common/chibios/eeprom_teensy.c @@ -0,0 +1,632 @@ +#include "ch.h" +#include "hal.h" + +#include "eeconfig.h" + +/*************************************/ +/* Hardware backend */ +/* */ +/* Code from PJRC/Teensyduino */ +/*************************************/ + +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2013 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * 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. + */ + + +#if defined(K20x) /* chip selection */ +/* Teensy 3.0, 3.1, 3.2; mchck; infinity keyboard */ + +// The EEPROM is really RAM with a hardware-based backup system to +// flash memory. Selecting a smaller size EEPROM allows more wear +// leveling, for higher write endurance. If you edit this file, +// set this to the smallest size your application can use. Also, +// due to Freescale's implementation, writing 16 or 32 bit words +// (aligned to 2 or 4 byte boundaries) has twice the endurance +// compared to writing 8 bit bytes. +// +#define EEPROM_SIZE 32 + +// Writing unaligned 16 or 32 bit data is handled automatically when +// this is defined, but at a cost of extra code size. Without this, +// any unaligned write will cause a hard fault exception! If you're +// absolutely sure all 16 and 32 bit writes will be aligned, you can +// remove the extra unnecessary code. +// +#define HANDLE_UNALIGNED_WRITES + +// Minimum EEPROM Endurance +// ------------------------ +#if (EEPROM_SIZE == 2048) // 35000 writes/byte or 70000 writes/word + #define EEESIZE 0x33 +#elif (EEPROM_SIZE == 1024) // 75000 writes/byte or 150000 writes/word + #define EEESIZE 0x34 +#elif (EEPROM_SIZE == 512) // 155000 writes/byte or 310000 writes/word + #define EEESIZE 0x35 +#elif (EEPROM_SIZE == 256) // 315000 writes/byte or 630000 writes/word + #define EEESIZE 0x36 +#elif (EEPROM_SIZE == 128) // 635000 writes/byte or 1270000 writes/word + #define EEESIZE 0x37 +#elif (EEPROM_SIZE == 64) // 1275000 writes/byte or 2550000 writes/word + #define EEESIZE 0x38 +#elif (EEPROM_SIZE == 32) // 2555000 writes/byte or 5110000 writes/word + #define EEESIZE 0x39 +#endif + +/** \brief eeprom initialization + * + * FIXME: needs doc + */ +void eeprom_initialize(void) +{ + uint32_t count=0; + uint16_t do_flash_cmd[] = { + 0xf06f, 0x037f, 0x7003, 0x7803, + 0xf013, 0x0f80, 0xd0fb, 0x4770}; + uint8_t status; + + if (FTFL->FCNFG & FTFL_FCNFG_RAMRDY) { + // FlexRAM is configured as traditional RAM + // We need to reconfigure for EEPROM usage + FTFL->FCCOB0 = 0x80; // PGMPART = Program Partition Command + FTFL->FCCOB4 = EEESIZE; // EEPROM Size + FTFL->FCCOB5 = 0x03; // 0K for Dataflash, 32K for EEPROM backup + __disable_irq(); + // do_flash_cmd() must execute from RAM. Luckily the C syntax is simple... + (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFL->FSTAT)); + __enable_irq(); + status = FTFL->FSTAT; + if (status & (FTFL_FSTAT_RDCOLERR|FTFL_FSTAT_ACCERR|FTFL_FSTAT_FPVIOL)) { + FTFL->FSTAT = (status & (FTFL_FSTAT_RDCOLERR|FTFL_FSTAT_ACCERR|FTFL_FSTAT_FPVIOL)); + return; // error + } + } + // wait for eeprom to become ready (is this really necessary?) + while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { + if (++count > 20000) break; + } +} + +#define FlexRAM ((uint8_t *)0x14000000) + +/** \brief eeprom read byte + * + * FIXME: needs doc + */ +uint8_t eeprom_read_byte(const uint8_t *addr) +{ + uint32_t offset = (uint32_t)addr; + if (offset >= EEPROM_SIZE) return 0; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + return FlexRAM[offset]; +} + +/** \brief eeprom read word + * + * FIXME: needs doc + */ +uint16_t eeprom_read_word(const uint16_t *addr) +{ + uint32_t offset = (uint32_t)addr; + if (offset >= EEPROM_SIZE-1) return 0; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + return *(uint16_t *)(&FlexRAM[offset]); +} + +/** \brief eeprom read dword + * + * FIXME: needs doc + */ +uint32_t eeprom_read_dword(const uint32_t *addr) +{ + uint32_t offset = (uint32_t)addr; + if (offset >= EEPROM_SIZE-3) return 0; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + return *(uint32_t *)(&FlexRAM[offset]); +} + +/** \brief eeprom read block + * + * FIXME: needs doc + */ +void eeprom_read_block(void *buf, const void *addr, uint32_t len) +{ + uint32_t offset = (uint32_t)addr; + uint8_t *dest = (uint8_t *)buf; + uint32_t end = offset + len; + + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + if (end > EEPROM_SIZE) end = EEPROM_SIZE; + while (offset < end) { + *dest++ = FlexRAM[offset++]; + } +} + +/** \brief eeprom is ready + * + * FIXME: needs doc + */ +int eeprom_is_ready(void) +{ + return (FTFL->FCNFG & FTFL_FCNFG_EEERDY) ? 1 : 0; +} + +/** \brief flexram wait + * + * FIXME: needs doc + */ +static void flexram_wait(void) +{ + while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { + // TODO: timeout + } +} + +/** \brief eeprom_write_byte + * + * FIXME: needs doc + */ +void eeprom_write_byte(uint8_t *addr, uint8_t value) +{ + uint32_t offset = (uint32_t)addr; + + if (offset >= EEPROM_SIZE) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + if (FlexRAM[offset] != value) { + FlexRAM[offset] = value; + flexram_wait(); + } +} + +/** \brief eeprom write word + * + * FIXME: needs doc + */ +void eeprom_write_word(uint16_t *addr, uint16_t value) +{ + uint32_t offset = (uint32_t)addr; + + if (offset >= EEPROM_SIZE-1) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); +#ifdef HANDLE_UNALIGNED_WRITES + if ((offset & 1) == 0) { +#endif + if (*(uint16_t *)(&FlexRAM[offset]) != value) { + *(uint16_t *)(&FlexRAM[offset]) = value; + flexram_wait(); + } +#ifdef HANDLE_UNALIGNED_WRITES + } else { + if (FlexRAM[offset] != value) { + FlexRAM[offset] = value; + flexram_wait(); + } + if (FlexRAM[offset + 1] != (value >> 8)) { + FlexRAM[offset + 1] = value >> 8; + flexram_wait(); + } + } +#endif +} + +/** \brief eeprom write dword + * + * FIXME: needs doc + */ +void eeprom_write_dword(uint32_t *addr, uint32_t value) +{ + uint32_t offset = (uint32_t)addr; + + if (offset >= EEPROM_SIZE-3) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); +#ifdef HANDLE_UNALIGNED_WRITES + switch (offset & 3) { + case 0: +#endif + if (*(uint32_t *)(&FlexRAM[offset]) != value) { + *(uint32_t *)(&FlexRAM[offset]) = value; + flexram_wait(); + } + return; +#ifdef HANDLE_UNALIGNED_WRITES + case 2: + if (*(uint16_t *)(&FlexRAM[offset]) != value) { + *(uint16_t *)(&FlexRAM[offset]) = value; + flexram_wait(); + } + if (*(uint16_t *)(&FlexRAM[offset + 2]) != (value >> 16)) { + *(uint16_t *)(&FlexRAM[offset + 2]) = value >> 16; + flexram_wait(); + } + return; + default: + if (FlexRAM[offset] != value) { + FlexRAM[offset] = value; + flexram_wait(); + } + if (*(uint16_t *)(&FlexRAM[offset + 1]) != (value >> 8)) { + *(uint16_t *)(&FlexRAM[offset + 1]) = value >> 8; + flexram_wait(); + } + if (FlexRAM[offset + 3] != (value >> 24)) { + FlexRAM[offset + 3] = value >> 24; + flexram_wait(); + } + } +#endif +} + +/** \brief eeprom write block + * + * FIXME: needs doc + */ +void eeprom_write_block(const void *buf, void *addr, uint32_t len) +{ + uint32_t offset = (uint32_t)addr; + const uint8_t *src = (const uint8_t *)buf; + + if (offset >= EEPROM_SIZE) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + if (len >= EEPROM_SIZE) len = EEPROM_SIZE; + if (offset + len >= EEPROM_SIZE) len = EEPROM_SIZE - offset; + while (len > 0) { + uint32_t lsb = offset & 3; + if (lsb == 0 && len >= 4) { + // write aligned 32 bits + uint32_t val32; + val32 = *src++; + val32 |= (*src++ << 8); + val32 |= (*src++ << 16); + val32 |= (*src++ << 24); + if (*(uint32_t *)(&FlexRAM[offset]) != val32) { + *(uint32_t *)(&FlexRAM[offset]) = val32; + flexram_wait(); + } + offset += 4; + len -= 4; + } else if ((lsb == 0 || lsb == 2) && len >= 2) { + // write aligned 16 bits + uint16_t val16; + val16 = *src++; + val16 |= (*src++ << 8); + if (*(uint16_t *)(&FlexRAM[offset]) != val16) { + *(uint16_t *)(&FlexRAM[offset]) = val16; + flexram_wait(); + } + offset += 2; + len -= 2; + } else { + // write 8 bits + uint8_t val8 = *src++; + if (FlexRAM[offset] != val8) { + FlexRAM[offset] = val8; + flexram_wait(); + } + offset++; + len--; + } + } +} + +/* +void do_flash_cmd(volatile uint8_t *fstat) +{ + *fstat = 0x80; + while ((*fstat & 0x80) == 0) ; // wait +} +00000000 : + 0: f06f 037f mvn.w r3, #127 ; 0x7f + 4: 7003 strb r3, [r0, #0] + 6: 7803 ldrb r3, [r0, #0] + 8: f013 0f80 tst.w r3, #128 ; 0x80 + c: d0fb beq.n 6 + e: 4770 bx lr +*/ + +#elif defined(KL2x) /* chip selection */ +/* Teensy LC (emulated) */ + +#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) + +extern uint32_t __eeprom_workarea_start__; +extern uint32_t __eeprom_workarea_end__; + +#define EEPROM_SIZE 128 + +static uint32_t flashend = 0; + +void eeprom_initialize(void) +{ + const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); + + do { + if (*p++ == 0xFFFF) { + flashend = (uint32_t)(p - 2); + return; + } + } while (p < (uint16_t *)SYMVAL(__eeprom_workarea_end__)); + flashend = (uint32_t)((uint16_t *)SYMVAL(__eeprom_workarea_end__) - 1); +} + +uint8_t eeprom_read_byte(const uint8_t *addr) +{ + uint32_t offset = (uint32_t)addr; + const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); + const uint16_t *end = (const uint16_t *)((uint32_t)flashend); + uint16_t val; + uint8_t data=0xFF; + + if (!end) { + eeprom_initialize(); + end = (const uint16_t *)((uint32_t)flashend); + } + if (offset < EEPROM_SIZE) { + while (p <= end) { + val = *p++; + if ((val & 255) == offset) data = val >> 8; + } + } + return data; +} + +static void flash_write(const uint16_t *code, uint32_t addr, uint32_t data) +{ + // with great power comes great responsibility.... + uint32_t stat; + *(uint32_t *)&(FTFA->FCCOB3) = 0x06000000 | (addr & 0x00FFFFFC); + *(uint32_t *)&(FTFA->FCCOB7) = data; + __disable_irq(); + (*((void (*)(volatile uint8_t *))((uint32_t)code | 1)))(&(FTFA->FSTAT)); + __enable_irq(); + stat = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR|FTFA_FSTAT_ACCERR|FTFA_FSTAT_FPVIOL); + if (stat) { + FTFA->FSTAT = stat; + } + MCM->PLACR |= MCM_PLACR_CFCC; +} + +void eeprom_write_byte(uint8_t *addr, uint8_t data) +{ + uint32_t offset = (uint32_t)addr; + const uint16_t *p, *end = (const uint16_t *)((uint32_t)flashend); + uint32_t i, val, flashaddr; + uint16_t do_flash_cmd[] = { + 0x2380, 0x7003, 0x7803, 0xb25b, 0x2b00, 0xdafb, 0x4770}; + uint8_t buf[EEPROM_SIZE]; + + if (offset >= EEPROM_SIZE) return; + if (!end) { + eeprom_initialize(); + end = (const uint16_t *)((uint32_t)flashend); + } + if (++end < (uint16_t *)SYMVAL(__eeprom_workarea_end__)) { + val = (data << 8) | offset; + flashaddr = (uint32_t)end; + flashend = flashaddr; + if ((flashaddr & 2) == 0) { + val |= 0xFFFF0000; + } else { + val <<= 16; + val |= 0x0000FFFF; + } + flash_write(do_flash_cmd, flashaddr, val); + } else { + for (i=0; i < EEPROM_SIZE; i++) { + buf[i] = 0xFF; + } + val = 0; + for (p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); p < (uint16_t *)SYMVAL(__eeprom_workarea_end__); p++) { + val = *p; + if ((val & 255) < EEPROM_SIZE) { + buf[val & 255] = val >> 8; + } + } + buf[offset] = data; + for (flashaddr=(uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); flashaddr < (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_end__); flashaddr += 1024) { + *(uint32_t *)&(FTFA->FCCOB3) = 0x09000000 | flashaddr; + __disable_irq(); + (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFA->FSTAT)); + __enable_irq(); + val = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR|FTFA_FSTAT_ACCERR|FTFA_FSTAT_FPVIOL);; + if (val) FTFA->FSTAT = val; + MCM->PLACR |= MCM_PLACR_CFCC; + } + flashaddr=(uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); + for (i=0; i < EEPROM_SIZE; i++) { + if (buf[i] == 0xFF) continue; + if ((flashaddr & 2) == 0) { + val = (buf[i] << 8) | i; + } else { + val = val | (buf[i] << 24) | (i << 16); + flash_write(do_flash_cmd, flashaddr, val); + } + flashaddr += 2; + } + flashend = flashaddr; + if ((flashaddr & 2)) { + val |= 0xFFFF0000; + flash_write(do_flash_cmd, flashaddr, val); + } + } +} + +/* +void do_flash_cmd(volatile uint8_t *fstat) +{ + *fstat = 0x80; + while ((*fstat & 0x80) == 0) ; // wait +} +00000000 : + 0: 2380 movs r3, #128 ; 0x80 + 2: 7003 strb r3, [r0, #0] + 4: 7803 ldrb r3, [r0, #0] + 6: b25b sxtb r3, r3 + 8: 2b00 cmp r3, #0 + a: dafb bge.n 4 + c: 4770 bx lr +*/ + + +uint16_t eeprom_read_word(const uint16_t *addr) +{ + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8); +} + +uint32_t eeprom_read_dword(const uint32_t *addr) +{ + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8) + | (eeprom_read_byte(p+2) << 16) | (eeprom_read_byte(p+3) << 24); +} + +void eeprom_read_block(void *buf, const void *addr, uint32_t len) +{ + const uint8_t *p = (const uint8_t *)addr; + uint8_t *dest = (uint8_t *)buf; + while (len--) { + *dest++ = eeprom_read_byte(p++); + } +} + +int eeprom_is_ready(void) +{ + return 1; +} + +void eeprom_write_word(uint16_t *addr, uint16_t value) +{ + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p, value >> 8); +} + +void eeprom_write_dword(uint32_t *addr, uint32_t value) +{ + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p++, value >> 8); + eeprom_write_byte(p++, value >> 16); + eeprom_write_byte(p, value >> 24); +} + +void eeprom_write_block(const void *buf, void *addr, uint32_t len) +{ + uint8_t *p = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + while (len--) { + eeprom_write_byte(p++, *src++); + } +} + +#else +// No EEPROM supported, so emulate it + +#define EEPROM_SIZE 32 +static uint8_t buffer[EEPROM_SIZE]; + +uint8_t eeprom_read_byte(const uint8_t *addr) { + uint32_t offset = (uint32_t)addr; + return buffer[offset]; +} + +void eeprom_write_byte(uint8_t *addr, uint8_t value) { + uint32_t offset = (uint32_t)addr; + buffer[offset] = value; +} + +uint16_t eeprom_read_word(const uint16_t *addr) { + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8); +} + +uint32_t eeprom_read_dword(const uint32_t *addr) { + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8) + | (eeprom_read_byte(p+2) << 16) | (eeprom_read_byte(p+3) << 24); +} + +void eeprom_read_block(void *buf, const void *addr, uint32_t len) { + const uint8_t *p = (const uint8_t *)addr; + uint8_t *dest = (uint8_t *)buf; + while (len--) { + *dest++ = eeprom_read_byte(p++); + } +} + +void eeprom_write_word(uint16_t *addr, uint16_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p, value >> 8); +} + +void eeprom_write_dword(uint32_t *addr, uint32_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p++, value >> 8); + eeprom_write_byte(p++, value >> 16); + eeprom_write_byte(p, value >> 24); +} + +void eeprom_write_block(const void *buf, void *addr, uint32_t len) { + uint8_t *p = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + while (len--) { + eeprom_write_byte(p++, *src++); + } +} + +#endif /* chip selection */ +// The update functions just calls write for now, but could probably be optimized + +void eeprom_update_byte(uint8_t *addr, uint8_t value) { + eeprom_write_byte(addr, value); +} + +void eeprom_update_word(uint16_t *addr, uint16_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p, value >> 8); +} + +void eeprom_update_dword(uint32_t *addr, uint32_t value) { + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p++, value >> 8); + eeprom_write_byte(p++, value >> 16); + eeprom_write_byte(p, value >> 24); +} + +void eeprom_update_block(const void *buf, void *addr, uint32_t len) { + uint8_t *p = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + while (len--) { + eeprom_write_byte(p++, *src++); + } +} diff --git a/tmk_core/common/chibios/flash_stm32.c b/tmk_core/common/chibios/flash_stm32.c new file mode 100755 index 0000000000..e7199ac7b1 --- /dev/null +++ b/tmk_core/common/chibios/flash_stm32.c @@ -0,0 +1,180 @@ +/* + * 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 https://github.com/rogerclarkmelbourne/Arduino_STM32 and + * https://github.com/leaflabs/libmaple + * + * Modifications for QMK and STM32F303 by Yiancar + */ + +#define STM32F303xC + +#include "stm32f3xx.h" +#include "flash_stm32.h" + +#define FLASH_KEY1 ((uint32_t)0x45670123) +#define FLASH_KEY2 ((uint32_t)0xCDEF89AB) + +/* Delay definition */ +#define EraseTimeout ((uint32_t)0x00000FFF) +#define ProgramTimeout ((uint32_t)0x0000001F) + +#define ASSERT(exp) (void)((0)) + +/** + * @brief Inserts a time delay. + * @param None + * @retval None + */ +static void delay(void) +{ + __IO uint32_t i = 0; + for(i = 0xFF; i != 0; i--) { } +} + +/** + * @brief Returns the FLASH Status. + * @param None + * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG, + * FLASH_ERROR_WRP or FLASH_COMPLETE + */ +FLASH_Status FLASH_GetStatus(void) +{ + if ((FLASH->SR & FLASH_SR_BSY) == FLASH_SR_BSY) + return FLASH_BUSY; + + 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; + + return FLASH_COMPLETE; +} + +/** + * @brief Waits for a Flash operation to complete or a TIMEOUT to occur. + * @param Timeout: FLASH progamming Timeout + * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, + * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. + */ +FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) +{ + FLASH_Status status; + + /* Check for the Flash Status */ + status = FLASH_GetStatus(); + /* Wait for a Flash operation to complete or a TIMEOUT to occur */ + while ((status == FLASH_BUSY) && (Timeout != 0x00)) + { + delay(); + status = FLASH_GetStatus(); + Timeout--; + } + if (Timeout == 0) + status = FLASH_TIMEOUT; + /* Return the operation status */ + return status; +} + +/** + * @brief Erases a specified FLASH page. + * @param Page_Address: The page address 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; + /* Check the parameters */ + ASSERT(IS_FLASH_ADDRESS(Page_Address)); + /* 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 */ + FLASH->CR |= FLASH_CR_PER; + FLASH->AR = Page_Address; + 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 */ + FLASH->CR &= ~FLASH_CR_PER; + } + FLASH->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR); + } + /* Return the Erase Status */ + return status; +} + +/** + * @brief Programs a half word 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_ProgramHalfWord(uint32_t Address, uint16_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 */ + FLASH->CR |= FLASH_CR_PG; + *(__IO uint16_t*)Address = Data; + /* 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); + } + } + return status; +} + +/** + * @brief Unlocks the FLASH Program Erase Controller. + * @param None + * @retval None + */ +void FLASH_Unlock(void) +{ + /* Authorize the FPEC Access */ + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; +} + +/** + * @brief Locks the FLASH Program Erase Controller. + * @param None + * @retval None + */ +void FLASH_Lock(void) +{ + /* Set the Lock Bit to lock the FPEC and the FCR */ + FLASH->CR |= FLASH_CR_LOCK; +} diff --git a/tmk_core/common/chibios/flash_stm32.h b/tmk_core/common/chibios/flash_stm32.h new file mode 100755 index 0000000000..cc065cbca2 --- /dev/null +++ b/tmk_core/common/chibios/flash_stm32.h @@ -0,0 +1,53 @@ +/* + * 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 https://github.com/rogerclarkmelbourne/Arduino_STM32 and + * https://github.com/leaflabs/libmaple + * + * Modifications for QMK and STM32F303 by Yiancar + */ + +#ifndef __FLASH_STM32_H +#define __FLASH_STM32_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include "ch.h" +#include "hal.h" + +typedef enum + { + FLASH_BUSY = 1, + FLASH_ERROR_PG, + FLASH_ERROR_WRP, + FLASH_ERROR_OPT, + FLASH_COMPLETE, + FLASH_TIMEOUT, + FLASH_BAD_ADDRESS + } FLASH_Status; + +#define IS_FLASH_ADDRESS(ADDRESS) (((ADDRESS) >= 0x08000000) && ((ADDRESS) < 0x0807FFFF)) + +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); + +void FLASH_Unlock(void); +void FLASH_Lock(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __FLASH_STM32_H */ diff --git a/tmk_core/common/eeconfig.c b/tmk_core/common/eeconfig.c index 3e5987ee3b..35de574a96 100644 --- a/tmk_core/common/eeconfig.c +++ b/tmk_core/common/eeconfig.c @@ -3,12 +3,20 @@ #include "eeprom.h" #include "eeconfig.h" +#ifdef STM32F303xC +#include "hal.h" +#include "eeprom_stm32.h" +#endif + /** \brief eeconfig initialization * * FIXME: needs doc */ void eeconfig_init(void) { +#ifdef STM32F303xC + EEPROM_format(); +#endif eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); eeprom_update_byte(EECONFIG_DEBUG, 0); eeprom_update_byte(EECONFIG_DEFAULT_LAYER, 0); @@ -43,6 +51,9 @@ void eeconfig_enable(void) */ void eeconfig_disable(void) { +#ifdef STM32F303xC + EEPROM_format(); +#endif eeprom_update_word(EECONFIG_MAGIC, 0xFFFF); } diff --git a/tmk_core/common/eeconfig.h b/tmk_core/common/eeconfig.h index 1397a90c79..fa498df48c 100644 --- a/tmk_core/common/eeconfig.h +++ b/tmk_core/common/eeconfig.h @@ -25,6 +25,7 @@ along with this program. If not, see . #define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEED /* eeprom parameteter address */ +#if !defined(STM32F303xC) #define EECONFIG_MAGIC (uint16_t *)0 #define EECONFIG_DEBUG (uint8_t *)2 #define EECONFIG_DEFAULT_LAYER (uint8_t *)3 @@ -38,6 +39,21 @@ along with this program. If not, see . // EEHANDS for two handed boards #define EECONFIG_HANDEDNESS (uint8_t *)14 +#else +/* STM32F3 uses 16byte block. Reconfigure memory map */ +#define EECONFIG_MAGIC (uint16_t *)0 +#define EECONFIG_DEBUG (uint8_t *)1 +#define EECONFIG_DEFAULT_LAYER (uint8_t *)2 +#define EECONFIG_KEYMAP (uint8_t *)3 +#define EECONFIG_MOUSEKEY_ACCEL (uint8_t *)4 +#define EECONFIG_BACKLIGHT (uint8_t *)5 +#define EECONFIG_AUDIO (uint8_t *)6 +#define EECONFIG_RGBLIGHT (uint32_t *)7 +#define EECONFIG_UNICODEMODE (uint8_t *)9 +#define EECONFIG_STENOMODE (uint8_t *)10 +// EEHANDS for two handed boards +#define EECONFIG_HANDEDNESS (uint8_t *)11 +#endif /* debug bit */ #define EECONFIG_DEBUG_ENABLE (1<<0) -- cgit v1.2.3