From 30680c6eb396a2bb06928afd69edae9908ac84fb Mon Sep 17 00:00:00 2001 From: patrickmt <40182064+patrickmt@users.noreply.github.com> Date: Wed, 29 Aug 2018 15:07:52 -0400 Subject: Massdrop keyboard support (#3780) * Massdrop SAMD51 Massdrop SAMD51 keyboards initial project upload * Removing relocated files Removing files that were relocated and not deleted from previous location * LED queue fix and cleaning Cleaned some white space or comments. Fix for LED I2C command queue. Cleaned up interrupts. Added debug function for printing numbers to scope through m15 line. * Factory programmed serial usage Ability to use factory programmed serial in hub and keyboard usb descriptors * USB serial number and bugfix Added support for factory programmed serial and usage. Incorporated bootloader's conditional compiling to align project closer. Fixed issue when USB device attempted to send before enabled. General white space and comment cleanup. * Project cleanup Cleaned up project in terms of white space, commented code, and unecessary files. NKRO keyboard is now using correct setreport although KBD was fine to use. Fixed broken linkage to __xprintf for serial debug statements. * Fix for extra keys Fixed possible USB hang on extra keys report set missing * I2C cleanup I2C cleanup and file renames necessary for master branch merge * Boot tracing and clocks cleanup Added optional boot debug trace mode through debug LED codes. General clock code cleanup. * Relocate ARM/Atmel headers Moved ARM/Atmel header folder from drivers to lib and made necessary makefile changes. * Pull request changes Pull request changes * Keymap and compile flag fix Keymap fix for momentary layer. Potential compile flag fix for Travis CI failure. * va_list include fix Fix for va_list compile failure * Include file case fixes Fixes for include files with incorrect case * ctrl and alt67 keyboard readme Added ctrl and alt67 keyboard readme files --- tmk_core/common/arm_atsam/bootloader.c | 19 +++++++ tmk_core/common/arm_atsam/eeprom.c | 98 ++++++++++++++++++++++++++++++++++ tmk_core/common/arm_atsam/printf.h | 8 +++ tmk_core/common/arm_atsam/suspend.c | 17 ++++++ tmk_core/common/arm_atsam/timer.c | 59 ++++++++++++++++++++ tmk_core/common/print.h | 30 ++++++++++- tmk_core/common/report.h | 5 ++ 7 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 tmk_core/common/arm_atsam/bootloader.c create mode 100644 tmk_core/common/arm_atsam/eeprom.c create mode 100644 tmk_core/common/arm_atsam/printf.h create mode 100644 tmk_core/common/arm_atsam/suspend.c create mode 100644 tmk_core/common/arm_atsam/timer.c (limited to 'tmk_core/common') diff --git a/tmk_core/common/arm_atsam/bootloader.c b/tmk_core/common/arm_atsam/bootloader.c new file mode 100644 index 0000000000..5155d9ff04 --- /dev/null +++ b/tmk_core/common/arm_atsam/bootloader.c @@ -0,0 +1,19 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "bootloader.h" + +void bootloader_jump(void) {} diff --git a/tmk_core/common/arm_atsam/eeprom.c b/tmk_core/common/arm_atsam/eeprom.c new file mode 100644 index 0000000000..61cc039efa --- /dev/null +++ b/tmk_core/common/arm_atsam/eeprom.c @@ -0,0 +1,98 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "eeprom.h" + +#define EEPROM_SIZE 32 + +static uint8_t buffer[EEPROM_SIZE]; + +uint8_t eeprom_read_byte(const uint8_t *addr) { + uintptr_t offset = (uintptr_t)addr; + return buffer[offset]; +} + +void eeprom_write_byte(uint8_t *addr, uint8_t value) { + uintptr_t offset = (uintptr_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++); + } +} + +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/arm_atsam/printf.h b/tmk_core/common/arm_atsam/printf.h new file mode 100644 index 0000000000..582c83bf54 --- /dev/null +++ b/tmk_core/common/arm_atsam/printf.h @@ -0,0 +1,8 @@ +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#define __xprintf dpf +int dpf(const char *_Format, ...); + +#endif //_PRINTF_H_ + diff --git a/tmk_core/common/arm_atsam/suspend.c b/tmk_core/common/arm_atsam/suspend.c new file mode 100644 index 0000000000..01d1930ea5 --- /dev/null +++ b/tmk_core/common/arm_atsam/suspend.c @@ -0,0 +1,17 @@ +/* Copyright 2017 Fred Sundvik + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + diff --git a/tmk_core/common/arm_atsam/timer.c b/tmk_core/common/arm_atsam/timer.c new file mode 100644 index 0000000000..bcfe5002c3 --- /dev/null +++ b/tmk_core/common/arm_atsam/timer.c @@ -0,0 +1,59 @@ +#include "samd51j18a.h" +#include "timer.h" +#include "tmk_core/protocol/arm_atsam/clks.h" + +void set_time(uint64_t tset) +{ + ms_clk = tset; +} + +void timer_init(void) +{ + ms_clk = 0; +} + +uint16_t timer_read(void) +{ + return (uint16_t)ms_clk; +} + +uint32_t timer_read32(void) +{ + return (uint32_t)ms_clk; +} + +uint64_t timer_read64(void) +{ + return ms_clk; +} + +uint16_t timer_elapsed(uint16_t tlast) +{ + return TIMER_DIFF_16(timer_read(), tlast); +} + +uint32_t timer_elapsed32(uint32_t tlast) +{ + return TIMER_DIFF_32(timer_read32(), tlast); +} + +uint32_t timer_elapsed64(uint32_t tlast) +{ + uint64_t tnow = timer_read64(); + return (tnow >= tlast ? tnow - tlast : UINT64_MAX - tlast + tnow); +} + +void timer_clear(void) +{ + ms_clk = 0; +} + +void wait_ms(uint64_t msec) +{ + CLK_delay_ms(msec); +} + +void wait_us(uint16_t usec) +{ + CLK_delay_us(usec); +} diff --git a/tmk_core/common/print.h b/tmk_core/common/print.h index 8836c0fc7c..9cbe67bad6 100644 --- a/tmk_core/common/print.h +++ b/tmk_core/common/print.h @@ -99,6 +99,34 @@ void print_set_sendchar(int8_t (*print_sendchar_func)(uint8_t)); # endif /* USER_PRINT / NORMAL PRINT */ +#elif defined(PROTOCOL_ARM_ATSAM) /* PROTOCOL_ARM_ATSAM */ + +# include "arm_atsam/printf.h" + +# ifdef USER_PRINT /* USER_PRINT */ + +// Remove normal print defines +# define print(s) +# define println(s) +# define xprintf(fmt, ...) + +// Create user print defines +# define uprintf(fmt, ...) __xprintf(fmt, ##__VA_ARGS__) +# define uprint(s) xprintf(s) +# define uprintln(s) xprintf(s "\r\n") + +# else /* NORMAL PRINT */ + +// Create user & normal print defines +# define xprintf(fmt, ...) __xprintf(fmt, ##__VA_ARGS__) +# define print(s) xprintf(s) +# define println(s) xprintf(s "\r\n") +# define uprint(s) print(s) +# define uprintln(s) println(s) +# define uprintf(fmt, ...) xprintf(fmt, ...) + +# endif /* USER_PRINT / NORMAL PRINT */ + #elif defined(__arm__) /* __arm__ */ # include "mbed/xprintf.h" @@ -130,7 +158,7 @@ void print_set_sendchar(int8_t (*print_sendchar_func)(uint8_t)); /* TODO: to select output destinations: UART/USBSerial */ # define print_set_sendchar(func) -#endif /* __AVR__ / PROTOCOL_CHIBIOS / __arm__ */ +#endif /* __AVR__ / PROTOCOL_CHIBIOS / PROTOCOL_ARM_ATSAM / __arm__ */ // User print disables the normal print messages in the body of QMK/TMK code and // is meant as a lightweight alternative to NOPRINT. Use it when you only want to do diff --git a/tmk_core/common/report.h b/tmk_core/common/report.h index 6c27eb9dc6..167f382751 100644 --- a/tmk_core/common/report.h +++ b/tmk_core/common/report.h @@ -84,6 +84,11 @@ along with this program. If not, see . #define KEYBOARD_REPORT_SIZE NKRO_EPSIZE #define KEYBOARD_REPORT_KEYS (NKRO_EPSIZE - 2) #define KEYBOARD_REPORT_BITS (NKRO_EPSIZE - 1) + #elif defined(PROTOCOL_ARM_ATSAM) + #include "protocol/arm_atsam/usb/udi_device_epsize.h" + #define KEYBOARD_REPORT_SIZE NKRO_EPSIZE + #define KEYBOARD_REPORT_KEYS (NKRO_EPSIZE - 2) + #define KEYBOARD_REPORT_BITS (NKRO_EPSIZE - 1) #else #error "NKRO not supported with this protocol" #endif -- cgit v1.2.3 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[offs