forked from mirrors/qmk_userspace
Rework paths for eeprom locations. (#17326)
* Rework paths for eeprom locations. * File relocation. * Wrong file move. * Fixup test paths.
This commit is contained in:
parent
6df5fce073
commit
1085500e89
9 changed files with 12 additions and 17 deletions
629
platforms/chibios/drivers/eeprom/eeprom_stm32.c
Normal file
629
platforms/chibios/drivers/eeprom/eeprom_stm32.c
Normal file
|
@ -0,0 +1,629 @@
|
|||
/*
|
||||
* 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 http://engsta.com/stm32-flash-memory-eeprom-emulator/ by
|
||||
* Artur F.
|
||||
*
|
||||
* Modifications for QMK and STM32F303 by Yiancar
|
||||
* Modifications to add flash wear leveling by Ilya Zhuravlev
|
||||
* Modifications to increase flash density by Don Kjer
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include "eeprom_stm32.h"
|
||||
#include "flash_stm32.h"
|
||||
|
||||
/*
|
||||
* We emulate eeprom by writing a snapshot compacted view of eeprom contents,
|
||||
* followed by a write log of any change since that snapshot:
|
||||
*
|
||||
* === SIMULATED EEPROM CONTENTS ===
|
||||
*
|
||||
* ┌─ Compacted ┬ Write Log ─┐
|
||||
* │............│[BYTE][BYTE]│
|
||||
* │FFFF....FFFF│[WRD0][WRD1]│
|
||||
* │FFFFFFFFFFFF│[WORD][NEXT]│
|
||||
* │....FFFFFFFF│[BYTE][WRD0]│
|
||||
* ├────────────┼────────────┤
|
||||
* └──PAGE_BASE │ │
|
||||
* PAGE_LAST─┴─WRITE_BASE │
|
||||
* WRITE_LAST ┘
|
||||
*
|
||||
* Compacted contents are the 1's complement of the actual EEPROM contents.
|
||||
* e.g. An 'FFFF' represents a '0000' value.
|
||||
*
|
||||
* The size of the 'compacted' area is equal to the size of the 'emulated' eeprom.
|
||||
* The size of the compacted-area and write log are configurable, and the combined
|
||||
* size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent.
|
||||
* Simulated Eeprom contents are located at the end of available flash space.
|
||||
*
|
||||
* The following configuration defines can be set:
|
||||
*
|
||||
* FEE_PAGE_COUNT # Total number of pages to use for eeprom simulation (Compact + Write log)
|
||||
* FEE_DENSITY_BYTES # Size of simulated eeprom. (Defaults to half the space allocated by FEE_PAGE_COUNT)
|
||||
* NOTE: The current implementation does not include page swapping,
|
||||
* and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents.
|
||||
*
|
||||
* The maximum size of FEE_DENSITY_BYTES is currently 16384. The write log size equals
|
||||
* FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES.
|
||||
* The larger the write log, the less frequently the compacted area needs to be rewritten.
|
||||
*
|
||||
*
|
||||
* *** General Algorithm ***
|
||||
*
|
||||
* During initialization:
|
||||
* The contents of the Compacted-flash area are loaded and the 1's complement value
|
||||
* is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache).
|
||||
* Write log entries are processed until a 0xFFFF is reached.
|
||||
* Each log entry updates a byte or word in the cache.
|
||||
*
|
||||
* During reads:
|
||||
* EEPROM contents are given back directly from the cache in memory.
|
||||
*
|
||||
* During writes:
|
||||
* The contents of the cache is updated first.
|
||||
* If the Compacted-flash area corresponding to the write address is unprogrammed, the 1's complement of the value is written directly into Compacted-flash
|
||||
* Otherwise:
|
||||
* If the write log is full, erase both the Compacted-flash area and the Write log, then write cached contents to the Compacted-flash area.
|
||||
* Otherwise a Write log entry is constructed and appended to the next free position in the Write log.
|
||||
*
|
||||
*
|
||||
* *** Write Log Structure ***
|
||||
*
|
||||
* Write log entries allow for optimized byte writes to addresses below 128. Writing 0 or 1 words are also optimized when word-aligned.
|
||||
*
|
||||
* === WRITE LOG ENTRY FORMATS ===
|
||||
*
|
||||
* ╔═══ Byte-Entry ══╗
|
||||
* ║0XXXXXXX║YYYYYYYY║
|
||||
* ║ └──┬──┘║└──┬───┘║
|
||||
* ║ Address║ Value ║
|
||||
* ╚════════╩════════╝
|
||||
* 0 <= Address < 0x80 (128)
|
||||
*
|
||||
* ╔ Word-Encoded 0 ╗
|
||||
* ║100XXXXXXXXXXXXX║
|
||||
* ║ │└─────┬─────┘║
|
||||
* ║ │Address >> 1 ║
|
||||
* ║ └── Value: 0 ║
|
||||
* ╚════════════════╝
|
||||
* 0 <= Address <= 0x3FFE (16382)
|
||||
*
|
||||
* ╔ Word-Encoded 1 ╗
|
||||
* ║101XXXXXXXXXXXXX║
|
||||
* ║ │└─────┬─────┘║
|
||||
* ║ │Address >> 1 ║
|
||||
* ║ └── Value: 1 ║
|
||||
* ╚════════════════╝
|
||||
* 0 <= Address <= 0x3FFE (16382)
|
||||
*
|
||||
* ╔═══ Reserved ═══╗
|
||||
* ║110XXXXXXXXXXXXX║
|
||||
* ╚════════════════╝
|
||||
*
|
||||
* ╔═══════════ Word-Next ═══════════╗
|
||||
* ║111XXXXXXXXXXXXX║YYYYYYYYYYYYYYYY║
|
||||
* ║ └─────┬─────┘║└───────┬──────┘║
|
||||
* ║(Address-128)>>1║ ~Value ║
|
||||
* ╚════════════════╩════════════════╝
|
||||
* ( 0 <= Address < 0x0080 (128): Reserved)
|
||||
* 0x80 <= Address <= 0x3FFE (16382)
|
||||
*
|
||||
* Write Log entry ranges:
|
||||
* 0x0000 ... 0x7FFF - Byte-Entry; address is (Entry & 0x7F00) >> 4; value is (Entry & 0xFF)
|
||||
* 0x8000 ... 0x9FFF - Word-Encoded 0; address is (Entry & 0x1FFF) << 1; value is 0
|
||||
* 0xA000 ... 0xBFFF - Word-Encoded 1; address is (Entry & 0x1FFF) << 1; value is 1
|
||||
* 0xC000 ... 0xDFFF - Reserved
|
||||
* 0xE000 ... 0xFFBF - Word-Next; address is (Entry & 0x1FFF) << 1 + 0x80; value is ~(Next_Entry)
|
||||
* 0xFFC0 ... 0xFFFE - Reserved
|
||||
* 0xFFFF - Unprogrammed
|
||||
*
|
||||
*/
|
||||
|
||||
#include "eeprom_stm32_defs.h"
|
||||
/* These bits are used for optimizing encoding of bytes, 0 and 1 */
|
||||
#define FEE_WORD_ENCODING 0x8000
|
||||
#define FEE_VALUE_NEXT 0x6000
|
||||
#define FEE_VALUE_RESERVED 0x4000
|
||||
#define FEE_VALUE_ENCODED 0x2000
|
||||
#define FEE_BYTE_RANGE 0x80
|
||||
|
||||
/* Flash word value after erase */
|
||||
#define FEE_EMPTY_WORD ((uint16_t)0xFFFF)
|
||||
|
||||
#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) || !defined(FEE_MCU_FLASH_SIZE) || !defined(FEE_PAGE_BASE_ADDRESS)
|
||||
# error "not implemented."
|
||||
#endif
|
||||
|
||||
/* In-memory contents of emulated eeprom for faster access */
|
||||
/* *TODO: Implement page swapping */
|
||||
static uint16_t WordBuf[FEE_DENSITY_BYTES / 2];
|
||||
static uint8_t *DataBuf = (uint8_t *)WordBuf;
|
||||
|
||||
/* Pointer to the first available slot within the write log */
|
||||
static uint16_t *empty_slot;
|
||||
|
||||
// #define DEBUG_EEPROM_OUTPUT
|
||||
|
||||
/*
|
||||
* Debug print utils
|
||||
*/
|
||||
|
||||
#if defined(DEBUG_EEPROM_OUTPUT)
|
||||
|
||||
# define debug_eeprom debug_enable
|
||||
# define eeprom_println(s) println(s)
|
||||
# define eeprom_printf(fmt, ...) xprintf(fmt, ##__VA_ARGS__);
|
||||
|
||||
#else /* NO_DEBUG */
|
||||
|
||||
# define debug_eeprom false
|
||||
# define eeprom_println(s)
|
||||
# define eeprom_printf(fmt, ...)
|
||||
|
||||
#endif /* NO_DEBUG */
|
||||
|
||||
void print_eeprom(void) {
|
||||
#ifndef NO_DEBUG
|
||||
int empty_rows = 0;
|
||||
for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) {
|
||||
if (i % 16 == 0) {
|
||||
if (i >= FEE_DENSITY_BYTES - 16) {
|
||||
/* Make sure we display the last row */
|
||||
empty_rows = 0;
|
||||
}
|
||||
/* Check if this row is uninitialized */
|
||||
++empty_rows;
|
||||
for (uint16_t j = 0; j < 16; j++) {
|
||||
if (DataBuf[i + j]) {
|
||||
empty_rows = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (empty_rows > 1) {
|
||||
/* Repeat empty row */
|
||||
if (empty_rows == 2) {
|
||||
/* Only display the first repeat empty row */
|
||||
println("*");
|
||||
}
|
||||
i += 15;
|
||||
continue;
|
||||
}
|
||||
xprintf("%04x", i);
|
||||
}
|
||||
if (i % 8 == 0) print(" ");
|
||||
|
||||
xprintf(" %02x", DataBuf[i]);
|
||||
if ((i + 1) % 16 == 0) {
|
||||
println("");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t EEPROM_Init(void) {
|
||||
/* Load emulated eeprom contents from compacted flash into memory */
|
||||
uint16_t *src = (uint16_t *)FEE_COMPACTED_BASE_ADDRESS;
|
||||
uint16_t *dest = (uint16_t *)DataBuf;
|
||||
for (; src < (uint16_t *)FEE_COMPACTED_LAST_ADDRESS; ++src, ++dest) {
|
||||
*dest = ~*src;
|
||||
}
|
||||
|
||||
if (debug_eeprom) {
|
||||
println("EEPROM_Init Compacted Pages:");
|
||||
print_eeprom();
|
||||
println("EEPROM_Init Write Log:");
|
||||
}
|
||||
|
||||
/* Replay write log */
|
||||
uint16_t *log_addr;
|
||||
for (log_addr = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) {
|
||||
uint16_t address = *log_addr;
|
||||
if (address == FEE_EMPTY_WORD) {
|
||||
break;
|
||||
}
|
||||
/* Check for lowest 128-bytes optimization */
|
||||
if (!(address & FEE_WORD_ENCODING)) {
|
||||
uint8_t bvalue = (uint8_t)address;
|
||||
address >>= 8;
|
||||
DataBuf[address] = bvalue;
|
||||
eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue);
|
||||
} else {
|
||||
uint16_t wvalue;
|
||||
/* Check if value is in next word */
|
||||
if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) {
|
||||
/* Read value from next word */
|
||||
if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
|
||||
break;
|
||||
}
|
||||
wvalue = ~*log_addr;
|
||||
if (!wvalue) {
|
||||
eeprom_printf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr);
|
||||
/* Possibly incomplete write. Ignore and continue */
|
||||
continue;
|
||||
}
|
||||
address &= 0x1FFF;
|
||||
address <<= 1;
|
||||
/* Writes to addresses less than 128 are byte log entries */
|
||||
address += FEE_BYTE_RANGE;
|
||||
} else {
|
||||
/* Reserved for future use */
|
||||
if (address & FEE_VALUE_RESERVED) {
|
||||
eeprom_printf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr);
|
||||
continue;
|
||||
}
|
||||
/* Optimization for 0 or 1 values. */
|
||||
wvalue = (address & FEE_VALUE_ENCODED) >> 13;
|
||||
address &= 0x1FFF;
|
||||
address <<= 1;
|
||||
}
|
||||
if (address < FEE_DENSITY_BYTES) {
|
||||
eeprom_printf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue);
|
||||
*(uint16_t *)(&DataBuf[address]) = wvalue;
|
||||
} else {
|
||||
eeprom_printf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
empty_slot = log_addr;
|
||||
|
||||
if (debug_eeprom) {
|
||||
println("EEPROM_Init Final DataBuf:");
|
||||
print_eeprom();
|
||||
}
|
||||
|
||||
return FEE_DENSITY_BYTES;
|
||||
}
|
||||
|
||||
/* Clear flash contents (doesn't touch in-memory DataBuf) */
|
||||
static void eeprom_clear(void) {
|
||||
FLASH_Unlock();
|
||||
|
||||
for (uint16_t page_num = 0; page_num < FEE_PAGE_COUNT; ++page_num) {
|
||||
eeprom_printf("FLASH_ErasePage(0x%04x)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)));
|
||||
FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE));
|
||||
}
|
||||
|
||||
FLASH_Lock();
|
||||
|
||||
empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS;
|
||||
eeprom_printf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot);
|
||||
}
|
||||
|
||||
/* Erase emulated eeprom */
|
||||
void EEPROM_Erase(void) {
|
||||
eeprom_println("EEPROM_Erase");
|
||||
/* Erase compacted pages and write log */
|
||||
eeprom_clear();
|
||||
/* re-initialize to reset DataBuf */
|
||||
EEPROM_Init();
|
||||
}
|
||||
|
||||
/* Compact write log */
|
||||
static uint8_t eeprom_compact(void) {
|
||||
/* Erase compacted pages and write log */
|
||||
eeprom_clear();
|
||||
|
||||
FLASH_Unlock();
|
||||
|
||||
FLASH_Status final_status = FLASH_COMPLETE;
|
||||
|
||||
/* Write emulated eeprom contents from memory to compacted flash */
|
||||
uint16_t *src = (uint16_t *)DataBuf;
|
||||
uintptr_t dest = FEE_COMPACTED_BASE_ADDRESS;
|
||||
uint16_t value;
|
||||
for (; dest < FEE_COMPACTED_LAST_ADDRESS; ++src, dest += 2) {
|
||||
value = *src;
|
||||
if (value) {
|
||||
eeprom_printf("FLASH_ProgramHalfWord(0x%04x, 0x%04x)\n", (uint32_t)dest, ~value);
|
||||
FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value);
|
||||
if (status != FLASH_COMPLETE) final_status = status;
|
||||
}
|
||||
}
|
||||
|
||||
FLASH_Lock();
|
||||
|
||||
if (debug_eeprom) {
|
||||
println("eeprom_compacted:");
|
||||
print_eeprom();
|
||||
}
|
||||
|
||||
return final_status;
|
||||
}
|
||||
|
||||
static uint8_t eeprom_write_direct_entry(uint16_t Address) {
|
||||
/* Check if we can just write this directly to the compacted flash area */
|
||||
uintptr_t directAddress = FEE_COMPACTED_BASE_ADDRESS + (Address & 0xFFFE);
|
||||
if (*(uint16_t *)directAddress == FEE_EMPTY_WORD) {
|
||||
/* Write the value directly to the compacted area without a log entry */
|
||||
uint16_t value = ~*(uint16_t *)(&DataBuf[Address & 0xFFFE]);
|
||||
/* Early exit if a write isn't needed */
|
||||
if (value == FEE_EMPTY_WORD) return FLASH_COMPLETE;
|
||||
|
||||
FLASH_Unlock();
|
||||
|
||||
eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value);
|
||||
FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value);
|
||||
|
||||
FLASH_Lock();
|
||||
return status;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t eeprom_write_log_word_entry(uint16_t Address) {
|
||||
FLASH_Status final_status = FLASH_COMPLETE;
|
||||
|
||||
uint16_t value = *(uint16_t *)(&DataBuf[Address]);
|
||||
eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value);
|
||||
|
||||
/* MSB signifies the lowest 128-byte optimization is not in effect */
|
||||
uint16_t encoding = FEE_WORD_ENCODING;
|
||||
uint8_t entry_size;
|
||||
if (value <= 1) {
|
||||
encoding |= value << 13;
|
||||
entry_size = 2;
|
||||
} else {
|
||||
encoding |= FEE_VALUE_NEXT;
|
||||
entry_size = 4;
|
||||
/* Writes to addresses less than 128 are byte log entries */
|
||||
Address -= FEE_BYTE_RANGE;
|
||||
}
|
||||
|
||||
/* if we can't find an empty spot, we must compact emulated eeprom */
|
||||
if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) {
|
||||
/* compact the write log into the compacted flash area */
|
||||
return eeprom_compact();
|
||||
}
|
||||
|
||||
/* Word log writes should be word-aligned. Take back a bit */
|
||||
Address >>= 1;
|
||||
Address |= encoding;
|
||||
|
||||
/* ok we found a place let's write our data */
|
||||
FLASH_Unlock();
|
||||
|
||||
/* address */
|
||||
eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, Address);
|
||||
final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address);
|
||||
|
||||
/* value */
|
||||
if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) {
|
||||
eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, ~value);
|
||||
FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value);
|
||||
if (status != FLASH_COMPLETE) final_status = status;
|
||||
}
|
||||
|
||||
FLASH_Lock();
|
||||
|
||||
return final_status;
|
||||
}
|
||||
|
||||
static uint8_t eeprom_write_log_byte_entry(uint16_t Address) {
|
||||
eeprom_printf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]);
|
||||
|
||||
/* if couldn't find an empty spot, we must compact emulated eeprom */
|
||||
if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
|
||||
/* compact the write log into the compacted flash area */
|
||||
return eeprom_compact();
|
||||
}
|
||||
|
||||
/* ok we found a place let's write our data */
|
||||
FLASH_Unlock();
|
||||
|
||||
/* Pack address and value into the same word */
|
||||
uint16_t value = (Address << 8) | DataBuf[Address];
|
||||
|
||||
/* write to flash */
|
||||
eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, value);
|
||||
FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value);
|
||||
|
||||
FLASH_Lock();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
|
||||
/* if the address is out-of-bounds, do nothing */
|
||||
if (Address >= FEE_DENSITY_BYTES) {
|
||||
eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte);
|
||||
return FLASH_BAD_ADDRESS;
|
||||
}
|
||||
|
||||
/* if the value is the same, don't bother writing it */
|
||||
if (DataBuf[Address] == DataByte) {
|
||||
eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* keep DataBuf cache in sync */
|
||||
DataBuf[Address] = DataByte;
|
||||
eeprom_printf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]);
|
||||
|
||||
/* perform the write into flash memory */
|
||||
/* First, attempt to write directly into the compacted flash area */
|
||||
FLASH_Status status = eeprom_write_direct_entry(Address);
|
||||
if (!status) {
|
||||
/* Otherwise append to the write log */
|
||||
if (Address < FEE_BYTE_RANGE) {
|
||||
status = eeprom_write_log_byte_entry(Address);
|
||||
} else {
|
||||
status = eeprom_write_log_word_entry(Address & 0xFFFE);
|
||||
}
|
||||
}
|
||||
if (status != 0 && status != FLASH_COMPLETE) {
|
||||
eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) {
|
||||
/* if the address is out-of-bounds, do nothing */
|
||||
if (Address >= FEE_DENSITY_BYTES) {
|
||||
eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord);
|
||||
return FLASH_BAD_ADDRESS;
|
||||
}
|
||||
|
||||
/* Check for word alignment */
|
||||
FLASH_Status final_status = FLASH_COMPLETE;
|
||||
if (Address % 2) {
|
||||
final_status = EEPROM_WriteDataByte(Address, DataWord);
|
||||
FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8);
|
||||
if (status != FLASH_COMPLETE) final_status = status;
|
||||
if (final_status != 0 && final_status != FLASH_COMPLETE) {
|
||||
eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
|
||||
}
|
||||
return final_status;
|
||||
}
|
||||
|
||||
/* if the value is the same, don't bother writing it */
|
||||
uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]);
|
||||
if (oldValue == DataWord) {
|
||||
eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* keep DataBuf cache in sync */
|
||||
*(uint16_t *)(&DataBuf[Address]) = DataWord;
|
||||
eeprom_printf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address]));
|
||||
|
||||
/* perform the write into flash memory */
|
||||
/* First, attempt to write directly into the compacted flash area */
|
||||
final_status = eeprom_write_direct_entry(Address);
|
||||
if (!final_status) {
|
||||
/* Otherwise append to the write log */
|
||||
/* Check if we need to fall back to byte write */
|
||||
if (Address < FEE_BYTE_RANGE) {
|
||||
final_status = FLASH_COMPLETE;
|
||||
/* Only write a byte if it has changed */
|
||||
if ((uint8_t)oldValue != (uint8_t)DataWord) {
|
||||
final_status = eeprom_write_log_byte_entry(Address);
|
||||
}
|
||||
FLASH_Status status = FLASH_COMPLETE;
|
||||
/* Only write a byte if it has changed */
|
||||
if ((oldValue >> 8) != (DataWord >> 8)) {
|
||||
status = eeprom_write_log_byte_entry(Address + 1);
|
||||
}
|
||||
if (status != FLASH_COMPLETE) final_status = status;
|
||||
} else {
|
||||
final_status = eeprom_write_log_word_entry(Address);
|
||||
}
|
||||
}
|
||||
if (final_status != 0 && final_status != FLASH_COMPLETE) {
|
||||
eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
|
||||
}
|
||||
return final_status;
|
||||
}
|
||||
|
||||
uint8_t EEPROM_ReadDataByte(uint16_t Address) {
|
||||
uint8_t DataByte = 0xFF;
|
||||
|
||||
if (Address < FEE_DENSITY_BYTES) {
|
||||
DataByte = DataBuf[Address];
|
||||
}
|
||||
|
||||
eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte);
|
||||
|
||||
return DataByte;
|
||||
}
|
||||
|
||||
uint16_t EEPROM_ReadDataWord(uint16_t Address) {
|
||||
uint16_t DataWord = 0xFFFF;
|
||||
|
||||
if (Address < FEE_DENSITY_BYTES - 1) {
|
||||
/* Check word alignment */
|
||||
if (Address % 2) {
|
||||
DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8);
|
||||
} else {
|
||||
DataWord = *(uint16_t *)(&DataBuf[Address]);
|
||||
}
|
||||
}
|
||||
|
||||
eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord);
|
||||
|
||||
return DataWord;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Bind to eeprom_driver.c
|
||||
*******************************************************************************/
|
||||
void eeprom_driver_init(void) {
|
||||
EEPROM_Init();
|
||||
}
|
||||
|
||||
void eeprom_driver_erase(void) {
|
||||
EEPROM_Erase();
|
||||
}
|
||||
|
||||
void eeprom_read_block(void *buf, const void *addr, size_t len) {
|
||||
const uint8_t *src = (const uint8_t *)addr;
|
||||
uint8_t * dest = (uint8_t *)buf;
|
||||
|
||||
/* Check word alignment */
|
||||
if (len && (uintptr_t)src % 2) {
|
||||
/* Read the unaligned first byte */
|
||||
*dest++ = EEPROM_ReadDataByte((const uintptr_t)src++);
|
||||
--len;
|
||||
}
|
||||
|
||||
uint16_t value;
|
||||
bool aligned = ((uintptr_t)dest % 2 == 0);
|
||||
while (len > 1) {
|
||||
value = EEPROM_ReadDataWord((const uintptr_t)((uint16_t *)src));
|
||||
if (aligned) {
|
||||
*(uint16_t *)dest = value;
|
||||
dest += 2;
|
||||
} else {
|
||||
*dest++ = value;
|
||||
*dest++ = value >> 8;
|
||||
}
|
||||
src += 2;
|
||||
len -= 2;
|
||||
}
|
||||
if (len) {
|
||||
*dest = EEPROM_ReadDataByte((const uintptr_t)src);
|
||||
}
|
||||
}
|
||||
|
||||
void eeprom_write_block(const void *buf, void *addr, size_t len) {
|
||||
uint8_t * dest = (uint8_t *)addr;
|
||||
const uint8_t *src = (const uint8_t *)buf;
|
||||
|
||||
/* Check word alignment */
|
||||
if (len && (uintptr_t)dest % 2) {
|
||||
/* Write the unaligned first byte */
|
||||
EEPROM_WriteDataByte((uintptr_t)dest++, *src++);
|
||||
--len;
|
||||
}
|
||||
|
||||
uint16_t value;
|
||||
bool aligned = ((uintptr_t)src % 2 == 0);
|
||||
while (len > 1) {
|
||||
if (aligned) {
|
||||
value = *(uint16_t *)src;
|
||||
} else {
|
||||
value = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8);
|
||||
}
|
||||
EEPROM_WriteDataWord((uintptr_t)((uint16_t *)dest), value);
|
||||
dest += 2;
|
||||
src += 2;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
EEPROM_WriteDataByte((uintptr_t)dest, *src);
|
||||
}
|
||||
}
|
33
platforms/chibios/drivers/eeprom/eeprom_stm32.h
Normal file
33
platforms/chibios/drivers/eeprom/eeprom_stm32.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 http://engsta.com/stm32-flash-memory-eeprom-emulator/ by
|
||||
* Artur F.
|
||||
*
|
||||
* Modifications for QMK and STM32F303 by Yiancar
|
||||
*
|
||||
* This library assumes 8-bit data locations. To add a new MCU, please provide the flash
|
||||
* page size and the total flash size in Kb. The number of available pages must be a multiple
|
||||
* of 2. Only half of the pages account for the total EEPROM size.
|
||||
* This library also assumes that the pages are not used by the firmware.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
uint16_t EEPROM_Init(void);
|
||||
void EEPROM_Erase(void);
|
||||
uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte);
|
||||
uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord);
|
||||
uint8_t EEPROM_ReadDataByte(uint16_t Address);
|
||||
uint16_t EEPROM_ReadDataWord(uint16_t Address);
|
||||
|
||||
void print_eeprom(void);
|
136
platforms/chibios/drivers/eeprom/eeprom_stm32_defs.h
Normal file
136
platforms/chibios/drivers/eeprom/eeprom_stm32_defs.h
Normal file
|
@ -0,0 +1,136 @@
|
|||
/* Copyright 2021 QMK
|
||||
*
|
||||
* 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 3 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <hal.h>
|
||||
|
||||
#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT)
|
||||
# if defined(STM32F103xB) || defined(STM32F042x6) || defined(GD32VF103C8) || defined(GD32VF103CB)
|
||||
# ifndef FEE_PAGE_SIZE
|
||||
# define FEE_PAGE_SIZE 0x400 // Page size = 1KByte
|
||||
# endif
|
||||
# ifndef FEE_PAGE_COUNT
|
||||
# define FEE_PAGE_COUNT 2 // How many pages are used
|
||||
# endif
|
||||
# elif defined(STM32F103xE) || defined(STM32F303xC) || defined(STM32F303xE) || defined(STM32F072xB) || defined(STM32F070xB)
|
||||
# ifndef FEE_PAGE_SIZE
|
||||
# define FEE_PAGE_SIZE 0x800 // Page size = 2KByte
|
||||
# endif
|
||||
# ifndef FEE_PAGE_COUNT
|
||||
# define FEE_PAGE_COUNT 4 // How many pages are used
|
||||
# endif
|
||||
# elif defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F405xG) || defined(STM32F411xE)
|
||||
# ifndef FEE_PAGE_SIZE
|
||||
# define FEE_PAGE_SIZE 0x4000 // Page size = 16KByte
|
||||
# endif
|
||||
# ifndef FEE_PAGE_COUNT
|
||||
# define FEE_PAGE_COUNT 1 // How many pages are used
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(FEE_MCU_FLASH_SIZE)
|
||||
# if defined(STM32F042x6)
|
||||
# define FEE_MCU_FLASH_SIZE 32 // Size in Kb
|
||||
# elif defined(GD32VF103C8)
|
||||
# define FEE_MCU_FLASH_SIZE 64 // Size in Kb
|
||||
# elif defined(STM32F103xB) || defined(STM32F072xB) || defined(STM32F070xB) || defined(GD32VF103CB)
|
||||
# define FEE_MCU_FLASH_SIZE 128 // Size in Kb
|
||||
# elif defined(STM32F303xC) || defined(STM32F401xC)
|
||||
# define FEE_MCU_FLASH_SIZE 256 // Size in Kb
|
||||
# elif defined(STM32F103xE) || defined(STM32F303xE) || defined(STM32F401xE) || defined(STM32F411xE)
|
||||
# define FEE_MCU_FLASH_SIZE 512 // Size in Kb
|
||||
# elif defined(STM32F405xG)
|
||||
# define FEE_MCU_FLASH_SIZE 1024 // Size in Kb
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Start of the emulated eeprom */
|
||||
#if !defined(FEE_PAGE_BASE_ADDRESS)
|
||||
# if defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F405xG) || defined(STM32F411xE)
|
||||
# ifndef FEE_PAGE_BASE_ADDRESS
|
||||
# define FEE_PAGE_BASE_ADDRESS 0x08004000 // bodge to force 2nd 16k page
|
||||
# endif
|
||||
# else
|
||||
# ifndef FEE_FLASH_BASE
|
||||
# define FEE_FLASH_BASE 0x8000000
|
||||
# endif
|
||||
/* Default to end of flash */
|
||||
# define FEE_PAGE_BASE_ADDRESS ((uintptr_t)(FEE_FLASH_BASE) + FEE_MCU_FLASH_SIZE * 1024 - (FEE_PAGE_COUNT * FEE_PAGE_SIZE))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Addressable range 16KByte: 0 <-> (0x1FFF << 1) */
|
||||
#define FEE_ADDRESS_MAX_SIZE 0x4000
|
||||
|
||||
/* Size of combined compacted eeprom and write log pages */
|
||||
#define FEE_DENSITY_MAX_SIZE (FEE_PAGE_COUNT * FEE_PAGE_SIZE)
|
||||
|
||||
#ifndef FEE_MCU_FLASH_SIZE_IGNORE_CHECK /* *TODO: Get rid of this check */
|
||||
# if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024)
|
||||
# pragma message STR(FEE_DENSITY_MAX_SIZE) " > " STR(FEE_MCU_FLASH_SIZE * 1024)
|
||||
# error emulated eeprom: FEE_DENSITY_MAX_SIZE is greater than available flash size
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Size of emulated eeprom */
|
||||
#ifdef FEE_DENSITY_BYTES
|
||||
# if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE)
|
||||
# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE)
|
||||
# error emulated eeprom: FEE_DENSITY_BYTES exceeds FEE_DENSITY_MAX_SIZE
|
||||
# endif
|
||||
# if (FEE_DENSITY_BYTES == FEE_DENSITY_MAX_SIZE)
|
||||
# pragma message STR(FEE_DENSITY_BYTES) " == " STR(FEE_DENSITY_MAX_SIZE)
|
||||
# warning emulated eeprom: FEE_DENSITY_BYTES leaves no room for a write log. This will greatly increase the flash wear rate!
|
||||
# endif
|
||||
# if FEE_DENSITY_BYTES > FEE_ADDRESS_MAX_SIZE
|
||||
# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_ADDRESS_MAX_SIZE)
|
||||
# error emulated eeprom: FEE_DENSITY_BYTES is greater than FEE_ADDRESS_MAX_SIZE allows
|
||||
# endif
|
||||
# if ((FEE_DENSITY_BYTES) % 2) == 1
|
||||
# error emulated eeprom: FEE_DENSITY_BYTES must be even
|
||||
# endif
|
||||
#else
|
||||
/* Default to half of allocated space used for emulated eeprom, half for write log */
|
||||
# define FEE_DENSITY_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE / 2)
|
||||
#endif
|
||||
|
||||
/* Size of write log */
|
||||
#ifdef FEE_WRITE_LOG_BYTES
|
||||
# if ((FEE_DENSITY_BYTES + FEE_WRITE_LOG_BYTES) > FEE_DENSITY_MAX_SIZE)
|
||||
# pragma message STR(FEE_DENSITY_BYTES) " + " STR(FEE_WRITE_LOG_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE)
|
||||
# error emulated eeprom: FEE_WRITE_LOG_BYTES exceeds remaining FEE_DENSITY_MAX_SIZE
|
||||
# endif
|
||||
# if ((FEE_WRITE_LOG_BYTES) % 2) == 1
|
||||
# error emulated eeprom: FEE_WRITE_LOG_BYTES must be even
|
||||
# endif
|
||||
#else
|
||||
/* Default to use all remaining space */
|
||||
# define FEE_WRITE_LOG_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES)
|
||||
#endif
|
||||
|
||||
/* Start of the emulated eeprom compacted flash area */
|
||||
#define FEE_COMPACTED_BASE_ADDRESS FEE_PAGE_BASE_ADDRESS
|
||||
/* End of the emulated eeprom compacted flash area */
|
||||
#define FEE_COMPACTED_LAST_ADDRESS (FEE_COMPACTED_BASE_ADDRESS + FEE_DENSITY_BYTES)
|
||||
/* Start of the emulated eeprom write log */
|
||||
#define FEE_WRITE_LOG_BASE_ADDRESS FEE_COMPACTED_LAST_ADDRESS
|
||||
/* End of the emulated eeprom write log */
|
||||
#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES)
|
||||
|
||||
#if defined(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) && (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR >= FEE_DENSITY_BYTES)
|
||||
# error emulated eeprom: DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is greater than the FEE_DENSITY_BYTES available
|
||||
#endif
|
546
platforms/chibios/drivers/eeprom/eeprom_teensy.c
Normal file
546
platforms/chibios/drivers/eeprom/eeprom_teensy.c
Normal file
|
@ -0,0 +1,546 @@
|
|||
#include <ch.h>
|
||||
#include <hal.h>
|
||||
|
||||
#include "eeprom_teensy.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 */
|
||||
|
||||
/*
|
||||
^^^ Here be dragons:
|
||||
NXP AppNote AN4282 section 3.1 states that partitioning must only be done once.
|
||||
Once EEPROM partitioning is done, the size is locked to this initial configuration.
|
||||
Attempts to modify the EEPROM_SIZE setting may brick your board.
|
||||
*/
|
||||
|
||||
// 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 <do_flash_cmd>:
|
||||
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 <do_flash_cmd+0x6>
|
||||
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__;
|
||||
|
||||
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)(p - 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 <do_flash_cmd>:
|
||||
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 <do_flash_cmd+0x4>
|
||||
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
|
||||
# error Unsupported Teensy EEPROM.
|
||||
#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, size_t len) {
|
||||
uint8_t * p = (uint8_t *)addr;
|
||||
const uint8_t *src = (const uint8_t *)buf;
|
||||
while (len--) {
|
||||
eeprom_write_byte(p++, *src++);
|
||||
}
|
||||
}
|
25
platforms/chibios/drivers/eeprom/eeprom_teensy.h
Executable file
25
platforms/chibios/drivers/eeprom/eeprom_teensy.h
Executable file
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2022 Nick Brassel (@tzarc)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#pragma once
|
||||
|
||||
#include <ch.h>
|
||||
#include <hal.h>
|
||||
|
||||
#if defined(K20x)
|
||||
/* 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.
|
||||
//
|
||||
# ifndef EEPROM_SIZE
|
||||
# define EEPROM_SIZE 32
|
||||
# endif
|
||||
#elif defined(KL2x) /* Teensy LC (emulated) */
|
||||
# define EEPROM_SIZE 128
|
||||
#else
|
||||
# error Unsupported Teensy EEPROM.
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue