aboutsummaryrefslogtreecommitdiff
path: root/platforms/chibios/eeprom_stm32.c
diff options
context:
space:
mode:
authorJoel Challis <git@zvecr.com>2021-11-19 18:41:02 +0000
committerGitHub <noreply@github.com>2021-11-19 10:41:02 -0800
commit2728603fe6d73e805a539d337fd01051c46ca806 (patch)
tree5c83ffc7efa112da870bd5d8502a9d91d4792f35 /platforms/chibios/eeprom_stm32.c
parent43b9e23bae12916d5161f03700c9bfe46737324b (diff)
downloadqmk_firmware-2728603fe6d73e805a539d337fd01051c46ca806.tar.gz
qmk_firmware-2728603fe6d73e805a539d337fd01051c46ca806.zip
Move tmk_core/common/<plat> (#13918)
Diffstat (limited to 'platforms/chibios/eeprom_stm32.c')
-rw-r--r--platforms/chibios/eeprom_stm32.c687
1 files changed, 687 insertions, 0 deletions
diff --git a/platforms/chibios/eeprom_stm32.c b/platforms/chibios/eeprom_stm32.c
new file mode 100644
index 000000000..acc6a4851
--- /dev/null
+++ b/platforms/chibios/eeprom_stm32.c
@@ -0,0 +1,687 @@
1/*
2 * This software is experimental and a work in progress.
3 * Under no circumstances should these files be used in relation to any critical system(s).
4 * Use of these files is at your own risk.
5 *
6 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
8 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
9 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
10 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
11 * DEALINGS IN THE SOFTWARE.
12 *
13 * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by
14 * Artur F.
15 *
16 * Modifications for QMK and STM32F303 by Yiancar
17 * Modifications to add flash wear leveling by Ilya Zhuravlev
18 * Modifications to increase flash density by Don Kjer
19 */
20
21#include <stdio.h>
22#include <stdbool.h>
23#include "util.h"
24#include "debug.h"
25#include "eeprom_stm32.h"
26#include "flash_stm32.h"
27
28/*
29 * We emulate eeprom by writing a snapshot compacted view of eeprom contents,
30 * followed by a write log of any change since that snapshot:
31 *
32 * === SIMULATED EEPROM CONTENTS ===
33 *
34 * ┌─ Compacted ┬ Write Log ─┐
35 * │............│[BYTE][BYTE]│
36 * │FFFF....FFFF│[WRD0][WRD1]│
37 * │FFFFFFFFFFFF│[WORD][NEXT]│
38 * │....FFFFFFFF│[BYTE][WRD0]│
39 * ├────────────┼────────────┤
40 * └──PAGE_BASE │ │
41 * PAGE_LAST─┴─WRITE_BASE │
42 * WRITE_LAST ┘
43 *
44 * Compacted contents are the 1's complement of the actual EEPROM contents.
45 * e.g. An 'FFFF' represents a '0000' value.
46 *
47 * The size of the 'compacted' area is equal to the size of the 'emulated' eeprom.
48 * The size of the compacted-area and write log are configurable, and the combined
49 * size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent.
50 * Simulated Eeprom contents are located at the end of available flash space.
51 *
52 * The following configuration defines can be set:
53 *
54 * FEE_PAGE_COUNT # Total number of pages to use for eeprom simulation (Compact + Write log)
55 * FEE_DENSITY_BYTES # Size of simulated eeprom. (Defaults to half the space allocated by FEE_PAGE_COUNT)
56 * NOTE: The current implementation does not include page swapping,
57 * and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents.
58 *
59 * The maximum size of FEE_DENSITY_BYTES is currently 16384. The write log size equals
60 * FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES.
61 * The larger the write log, the less frequently the compacted area needs to be rewritten.
62 *
63 *
64 * *** General Algorithm ***
65 *
66 * During initialization:
67 * The contents of the Compacted-flash area are loaded and the 1's complement value
68 * is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache).
69 * Write log entries are processed until a 0xFFFF is reached.
70 * Each log entry updates a byte or word in the cache.
71 *
72 * During reads:
73 * EEPROM contents are given back directly from the cache in memory.
74 *
75 * During writes:
76 * The contents of the cache is updated first.
77 * 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
78 * Otherwise:
79 * 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.
80 * Otherwise a Write log entry is constructed and appended to the next free position in the Write log.
81 *
82 *
83 * *** Write Log Structure ***
84 *
85 * Write log entries allow for optimized byte writes to addresses below 128. Writing 0 or 1 words are also optimized when word-aligned.
86 *
87 * === WRITE LOG ENTRY FORMATS ===
88 *
89 * ╔═══ Byte-Entry ══╗
90 * ║0XXXXXXX║YYYYYYYY║
91 * ║ └──┬──┘║└──┬───┘║
92 * ║ Address║ Value ║
93 * ╚════════╩════════╝
94 * 0 <= Address < 0x80 (128)
95 *
96 * ╔ Word-Encoded 0 ╗
97 * ║100XXXXXXXXXXXXX║
98 * ║ │└─────┬─────┘║
99 * ║ │Address >> 1 ║
100 * ║ └── Value: 0 ║
101 * ╚════════════════╝
102 * 0 <= Address <= 0x3FFE (16382)
103 *
104 * ╔ Word-Encoded 1 ╗
105 * ║101XXXXXXXXXXXXX║
106 * ║ │└─────┬─────┘║
107 * ║ │Address >> 1 ║
108 * ║ └── Value: 1 ║
109 * ╚════════════════╝
110 * 0 <= Address <= 0x3FFE (16382)
111 *
112 * ╔═══ Reserved ═══╗
113 * ║110XXXXXXXXXXXXX║
114 * ╚════════════════╝
115 *
116 * ╔═══════════ Word-Next ═══════════╗
117 * ║111XXXXXXXXXXXXX║YYYYYYYYYYYYYYYY║
118 * ║ └─────┬─────┘║└───────┬──────┘║
119 * ║(Address-128)>>1║ ~Value ║
120 * ╚════════════════╩════════════════╝
121 * ( 0 <= Address < 0x0080 (128): Reserved)
122 * 0x80 <= Address <= 0x3FFE (16382)
123 *
124 * Write Log entry ranges:
125 * 0x0000 ... 0x7FFF - Byte-Entry; address is (Entry & 0x7F00) >> 4; value is (Entry & 0xFF)
126 * 0x8000 ... 0x9FFF - Word-Encoded 0; address is (Entry & 0x1FFF) << 1; value is 0
127 * 0xA000 ... 0xBFFF - Word-Encoded 1; address is (Entry & 0x1FFF) << 1; value is 1
128 * 0xC000 ... 0xDFFF - Reserved
129 * 0xE000 ... 0xFFBF - Word-Next; address is (Entry & 0x1FFF) << 1 + 0x80; value is ~(Next_Entry)
130 * 0xFFC0 ... 0xFFFE - Reserved
131 * 0xFFFF - Unprogrammed
132 *
133 */
134
135#include "eeprom_stm32_defs.h"
136#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) || !defined(FEE_MCU_FLASH_SIZE) || !defined(FEE_PAGE_BASE_ADDRESS)
137# error "not implemented."
138#endif
139
140/* These bits are used for optimizing encoding of bytes, 0 and 1 */
141#define FEE_WORD_ENCODING 0x8000
142#define FEE_VALUE_NEXT 0x6000
143#define FEE_VALUE_RESERVED 0x4000
144#define FEE_VALUE_ENCODED 0x2000
145#define FEE_BYTE_RANGE 0x80
146
147/* Addressable range 16KByte: 0 <-> (0x1FFF << 1) */
148#define FEE_ADDRESS_MAX_SIZE 0x4000
149
150/* Flash word value after erase */
151#define FEE_EMPTY_WORD ((uint16_t)0xFFFF)
152
153/* Size of combined compacted eeprom and write log pages */
154#define FEE_DENSITY_MAX_SIZE (FEE_PAGE_COUNT * FEE_PAGE_SIZE)
155
156#ifndef FEE_MCU_FLASH_SIZE_IGNORE_CHECK /* *TODO: Get rid of this check */
157# if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024)
158# pragma message STR(FEE_DENSITY_MAX_SIZE) " > " STR(FEE_MCU_FLASH_SIZE * 1024)
159# error emulated eeprom: FEE_DENSITY_MAX_SIZE is greater than available flash size
160# endif
161#endif
162
163/* Size of emulated eeprom */
164#ifdef FEE_DENSITY_BYTES
165# if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE)
166# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE)
167# error emulated eeprom: FEE_DENSITY_BYTES exceeds FEE_DENSITY_MAX_SIZE
168# endif
169# if (FEE_DENSITY_BYTES == FEE_DENSITY_MAX_SIZE)
170# pragma message STR(FEE_DENSITY_BYTES) " == " STR(FEE_DENSITY_MAX_SIZE)
171# warning emulated eeprom: FEE_DENSITY_BYTES leaves no room for a write log. This will greatly increase the flash wear rate!
172# endif
173# if FEE_DENSITY_BYTES > FEE_ADDRESS_MAX_SIZE
174# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_ADDRESS_MAX_SIZE)
175# error emulated eeprom: FEE_DENSITY_BYTES is greater than FEE_ADDRESS_MAX_SIZE allows
176# endif
177# if ((FEE_DENSITY_BYTES) % 2) == 1
178# error emulated eeprom: FEE_DENSITY_BYTES must be even
179# endif
180#else
181/* Default to half of allocated space used for emulated eeprom, half for write log */
182# define FEE_DENSITY_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE / 2)
183#endif
184
185/* Size of write log */
186#ifdef FEE_WRITE_LOG_BYTES
187# if ((FEE_DENSITY_BYTES + FEE_WRITE_LOG_BYTES) > FEE_DENSITY_MAX_SIZE)
188# pragma message STR(FEE_DENSITY_BYTES) " + " STR(FEE_WRITE_LOG_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE)
189# error emulated eeprom: FEE_WRITE_LOG_BYTES exceeds remaining FEE_DENSITY_MAX_SIZE
190# endif
191# if ((FEE_WRITE_LOG_BYTES) % 2) == 1
192# error emulated eeprom: FEE_WRITE_LOG_BYTES must be even
193# endif
194#else
195/* Default to use all remaining space */
196# define FEE_WRITE_LOG_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES)
197#endif
198
199/* Start of the emulated eeprom compacted flash area */
200#define FEE_COMPACTED_BASE_ADDRESS FEE_PAGE_BASE_ADDRESS
201/* End of the emulated eeprom compacted flash area */
202#define FEE_COMPACTED_LAST_ADDRESS (FEE_COMPACTED_BASE_ADDRESS + FEE_DENSITY_BYTES)
203/* Start of the emulated eeprom write log */
204#define FEE_WRITE_LOG_BASE_ADDRESS FEE_COMPACTED_LAST_ADDRESS
205/* End of the emulated eeprom write log */
206#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES)
207
208#if defined(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) && (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR >= FEE_DENSITY_BYTES)
209# error emulated eeprom: DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is greater than the FEE_DENSITY_BYTES available
210#endif
211
212/* In-memory contents of emulated eeprom for faster access */
213/* *TODO: Implement page swapping */
214static uint16_t WordBuf[FEE_DENSITY_BYTES / 2];
215static uint8_t *DataBuf = (uint8_t *)WordBuf;
216
217/* Pointer to the first available slot within the write log */
218static uint16_t *empty_slot;
219
220// #define DEBUG_EEPROM_OUTPUT
221
222/*
223 * Debug print utils
224 */
225
226#if defined(DEBUG_EEPROM_OUTPUT)
227
228# define debug_eeprom debug_enable
229# define eeprom_println(s) println(s)
230# define eeprom_printf(fmt, ...) xprintf(fmt, ##__VA_ARGS__);
231
232#else /* NO_DEBUG */
233
234# define debug_eeprom false
235# define eeprom_println(s)
236# define eeprom_printf(fmt, ...)
237
238#endif /* NO_DEBUG */
239
240void print_eeprom(void) {
241#ifndef NO_DEBUG
242 int empty_rows = 0;
243 for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) {
244 if (i % 16 == 0) {
245 if (i >= FEE_DENSITY_BYTES - 16) {
246 /* Make sure we display the last row */
247 empty_rows = 0;
248 }
249 /* Check if this row is uninitialized */
250 ++empty_rows;
251 for (uint16_t j = 0; j < 16; j++) {
252 if (DataBuf[i + j]) {
253 empty_rows = 0;
254 break;
255 }
256 }
257 if (empty_rows > 1) {
258 /* Repeat empty row */
259 if (empty_rows == 2) {
260 /* Only display the first repeat empty row */
261 println("*");
262 }
263 i += 15;
264 continue;
265 }
266 xprintf("%04x", i);
267 }
268 if (i % 8 == 0) print(" ");
269
270 xprintf(" %02x", DataBuf[i]);
271 if ((i + 1) % 16 == 0) {
272 println("");
273 }
274 }
275#endif
276}
277
278uint16_t EEPROM_Init(void) {
279 /* Load emulated eeprom contents from compacted flash into memory */
280 uint16_t *src = (uint16_t *)FEE_COMPACTED_BASE_ADDRESS;
281 uint16_t *dest = (uint16_t *)DataBuf;
282 for (; src < (uint16_t *)FEE_COMPACTED_LAST_ADDRESS; ++src, ++dest) {
283 *dest = ~*src;
284 }
285
286 if (debug_eeprom) {
287 println("EEPROM_Init Compacted Pages:");
288 print_eeprom();
289 println("EEPROM_Init Write Log:");
290 }
291
292 /* Replay write log */
293 uint16_t *log_addr;
294 for (log_addr = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) {
295 uint16_t address = *log_addr;
296 if (address == FEE_EMPTY_WORD) {
297 break;
298 }
299 /* Check for lowest 128-bytes optimization */
300 if (!(address & FEE_WORD_ENCODING)) {
301 uint8_t bvalue = (uint8_t)address;
302 address >>= 8;
303 DataBuf[address] = bvalue;
304 eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue);
305 } else {
306 uint16_t wvalue;
307 /* Check if value is in next word */
308 if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) {
309 /* Read value from next word */
310 if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
311 break;
312 }
313 wvalue = ~*log_addr;
314 if (!wvalue) {
315 eeprom_printf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr);
316 /* Possibly incomplete write. Ignore and continue */
317 continue;
318 }
319 address &= 0x1FFF;
320 address <<= 1;
321 /* Writes to addresses less than 128 are byte log entries */
322 address += FEE_BYTE_RANGE;
323 } else {
324 /* Reserved for future use */
325 if (address & FEE_VALUE_RESERVED) {
326 eeprom_printf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr);
327 continue;
328 }
329 /* Optimization for 0 or 1 values. */
330 wvalue = (address & FEE_VALUE_ENCODED) >> 13;
331 address &= 0x1FFF;
332 address <<= 1;
333 }
334 if (address < FEE_DENSITY_BYTES) {
335 eeprom_printf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue);
336 *(uint16_t *)(&DataBuf[address]) = wvalue;
337 } else {
338 eeprom_printf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue);
339 }
340 }
341 }
342
343 empty_slot = log_addr;
344
345 if (debug_eeprom) {
346 println("EEPROM_Init Final DataBuf:");
347 print_eeprom();
348 }
349
350 return FEE_DENSITY_BYTES;
351}
352
353/* Clear flash contents (doesn't touch in-memory DataBuf) */
354static void eeprom_clear(void) {
355 FLASH_Unlock();
356
357 for (uint16_t page_num = 0; page_num < FEE_PAGE_COUNT; ++page_num) {
358 eeprom_printf("FLASH_ErasePage(0x%04x)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)));
359 FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE));
360 }
361
362 FLASH_Lock();
363
364 empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS;
365 eeprom_printf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot);
366}
367
368/* Erase emulated eeprom */
369void EEPROM_Erase(void) {
370 eeprom_println("EEPROM_Erase");
371 /* Erase compacted pages and write log */
372 eeprom_clear();
373 /* re-initialize to reset DataBuf */
374 EEPROM_Init();
375}
376
377/* Compact write log */
378static uint8_t eeprom_compact(void) {
379 /* Erase compacted pages and write log */
380 eeprom_clear();
381
382 FLASH_Unlock();
383
384 FLASH_Status final_status = FLASH_COMPLETE;
385
386 /* Write emulated eeprom contents from memory to compacted flash */
387 uint16_t *src = (uint16_t *)DataBuf;
388 uintptr_t dest = FEE_COMPACTED_BASE_ADDRESS;
389 uint16_t value;
390 for (; dest < FEE_COMPACTED_LAST_ADDRESS; ++src, dest += 2) {
391 value = *src;
392 if (value) {
393 eeprom_printf("FLASH_ProgramHalfWord(0x%04x, 0x%04x)\n", (uint32_t)dest, ~value);
394 FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value);
395 if (status != FLASH_COMPLETE) final_status = status;
396 }
397 }
398
399 FLASH_Lock();
400
401 if (debug_eeprom) {
402 println("eeprom_compacted:");
403 print_eeprom();
404 }
405
406 return final_status;
407}
408
409static uint8_t eeprom_write_direct_entry(uint16_t Address) {
410 /* Check if we can just write this directly to the compacted flash area */
411 uintptr_t directAddress = FEE_COMPACTED_BASE_ADDRESS + (Address & 0xFFFE);
412 if (*(uint16_t *)directAddress == FEE_EMPTY_WORD) {
413 /* Write the value directly to the compacted area without a log entry */
414 uint16_t value = ~*(uint16_t *)(&DataBuf[Address & 0xFFFE]);
415 /* Early exit if a write isn't needed */
416 if (value == FEE_EMPTY_WORD) return FLASH_COMPLETE;
417
418 FLASH_Unlock();
419
420 eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value);
421 FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value);
422
423 FLASH_Lock();
424 return status;
425 }
426 return 0;
427}
428
429static uint8_t eeprom_write_log_word_entry(uint16_t Address) {
430 FLASH_Status final_status = FLASH_COMPLETE;
431
432 uint16_t value = *(uint16_t *)(&DataBuf[Address]);
433 eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value);
434
435 /* MSB signifies the lowest 128-byte optimization is not in effect */
436 uint16_t encoding = FEE_WORD_ENCODING;
437 uint8_t entry_size;
438 if (value <= 1) {
439 encoding |= value << 13;
440 entry_size = 2;
441 } else {
442 encoding |= FEE_VALUE_NEXT;
443 entry_size = 4;
444 /* Writes to addresses less than 128 are byte log entries */
445 Address -= FEE_BYTE_RANGE;
446 }
447
448 /* if we can't find an empty spot, we must compact emulated eeprom */
449 if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) {
450 /* compact the write log into the compacted flash area */
451 return eeprom_compact();
452 }
453
454 /* Word log writes should be word-aligned. Take back a bit */
455 Address >>= 1;
456 Address |= encoding;
457
458 /* ok we found a place let's write our data */
459 FLASH_Unlock();
460
461 /* address */
462 eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, Address);
463 final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address);
464
465 /* value */
466 if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) {
467 eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, ~value);
468 FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value);
469 if (status != FLASH_COMPLETE) final_status = status;
470 }
471
472 FLASH_Lock();
473
474 return final_status;
475}
476
477static uint8_t eeprom_write_log_byte_entry(uint16_t Address) {
478 eeprom_printf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]);
479
480 /* if couldn't find an empty spot, we must compact emulated eeprom */
481 if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
482 /* compact the write log into the compacted flash area */
483 return eeprom_compact();
484 }
485
486 /* ok we found a place let's write our data */
487 FLASH_Unlock();
488
489 /* Pack address and value into the same word */
490 uint16_t value = (Address << 8) | DataBuf[Address];
491
492 /* write to flash */
493 eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, value);
494 FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value);
495
496 FLASH_Lock();
497
498 return status;
499}
500
501uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
502 /* if the address is out-of-bounds, do nothing */
503 if (Address >= FEE_DENSITY_BYTES) {
504 eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte);
505 return FLASH_BAD_ADDRESS;
506 }
507
508 /* if the value is the same, don't bother writing it */
509 if (DataBuf[Address] == DataByte) {
510 eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte);
511 return 0;
512 }
513
514 /* keep DataBuf cache in sync */
515 DataBuf[Address] = DataByte;
516 eeprom_printf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]);
517
518 /* perform the write into flash memory */
519 /* First, attempt to write directly into the compacted flash area */
520 FLASH_Status status = eeprom_write_direct_entry(Address);
521 if (!status) {
522 /* Otherwise append to the write log */
523 if (Address < FEE_BYTE_RANGE) {
524 status = eeprom_write_log_byte_entry(Address);
525 } else {
526 status = eeprom_write_log_word_entry(Address & 0xFFFE);
527 }
528 }
529 if (status != 0 && status != FLASH_COMPLETE) {
530 eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status);
531 }
532 return status;
533}
534
535uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) {
536 /* if the address is out-of-bounds, do nothing */
537 if (Address >= FEE_DENSITY_BYTES) {
538 eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord);
539 return FLASH_BAD_ADDRESS;
540 }
541
542 /* Check for word alignment */
543 FLASH_Status final_status = FLASH_COMPLETE;
544 if (Address % 2) {
545 final_status = EEPROM_WriteDataByte(Address, DataWord);
546 FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8);
547 if (status != FLASH_COMPLETE) final_status = status;
548 if (final_status != 0 && final_status != FLASH_COMPLETE) {
549 eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
550 }
551 return final_status;
552 }
553
554 /* if the value is the same, don't bother writing it */
555 uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]);
556 if (oldValue == DataWord) {
557 eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord);
558 return 0;
559 }
560
561 /* keep DataBuf cache in sync */
562 *(uint16_t *)(&DataBuf[Address]) = DataWord;
563 eeprom_printf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address]));
564
565 /* perform the write into flash memory */
566 /* First, attempt to write directly into the compacted flash area */
567 final_status = eeprom_write_direct_entry(Address);
568 if (!final_status) {
569 /* Otherwise append to the write log */
570 /* Check if we need to fall back to byte write */
571 if (Address < FEE_BYTE_RANGE) {
572 final_status = FLASH_COMPLETE;
573 /* Only write a byte if it has changed */
574 if ((uint8_t)oldValue != (uint8_t)DataWord) {
575 final_status = eeprom_write_log_byte_entry(Address);
576 }
577 FLASH_Status status = FLASH_COMPLETE;
578 /* Only write a byte if it has changed */
579 if ((oldValue >> 8) != (DataWord >> 8)) {
580 status = eeprom_write_log_byte_entry(Address + 1);
581 }
582 if (status != FLASH_COMPLETE) final_status = status;
583 } else {
584 final_status = eeprom_write_log_word_entry(Address);
585 }
586 }
587 if (final_status != 0 && final_status != FLASH_COMPLETE) {
588 eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
589 }
590 return final_status;
591}
592
593uint8_t EEPROM_ReadDataByte(uint16_t Address) {
594 uint8_t DataByte = 0xFF;
595
596 if (Address < FEE_DENSITY_BYTES) {
597 DataByte = DataBuf[Address];
598 }
599
600 eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte);
601
602 return DataByte;
603}
604
605uint16_t EEPROM_ReadDataWord(uint16_t Address) {
606 uint16_t DataWord = 0xFFFF;
607
608 if (Address < FEE_DENSITY_BYTES - 1) {
609 /* Check word alignment */
610 if (Address % 2) {
611 DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8);
612 } else {
613 DataWord = *(uint16_t *)(&DataBuf[Address]);
614 }
615 }
616
617 eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord);
618
619 return DataWord;
620}
621
622/*****************************************************************************
623 * Bind to eeprom_driver.c
624 *******************************************************************************/
625void eeprom_driver_init(void) { EEPROM_Init(); }
626
627void eeprom_driver_erase(void) { EEPROM_Erase(); }
628
629void eeprom_read_block(void *buf, const void *addr, size_t len) {
630 const uint8_t *src = (const uint8_t *)addr;
631 uint8_t * dest = (uint8_t *)buf;
632
633 /* Check word alignment */
634 if (len && (uintptr_t)src % 2) {
635 /* Read the unaligned first byte */
636 *dest++ = EEPROM_ReadDataByte((const uintptr_t)src++);
637 --len;
638 }
639
640 uint16_t value;
641 bool aligned = ((uintptr_t)dest % 2 == 0);
642 while (len > 1) {
643 value = EEPROM_ReadDataWord((const uintptr_t)((uint16_t *)src));
644 if (aligned) {
645 *(uint16_t *)dest = value;
646 dest += 2;
647 } else {
648 *dest++ = value;
649 *dest++ = value >> 8;
650 }
651 src += 2;
652 len -= 2;
653 }
654 if (len) {
655 *dest = EEPROM_ReadDataByte((const uintptr_t)src);
656 }
657}
658
659void eeprom_write_block(const void *buf, void *addr, size_t len) {
660 uint8_t * dest = (uint8_t *)addr;
661 const uint8_t *src = (const uint8_t *)buf;
662
663 /* Check word alignment */
664 if (len && (uintptr_t)dest % 2) {
665 /* Write the unaligned first byte */
666 EEPROM_WriteDataByte((uintptr_t)dest++, *src++);
667 --len;
668 }
669
670 uint16_t value;
671 bool aligned = ((uintptr_t)src % 2 == 0);
672 while (len > 1) {
673 if (aligned) {
674 value = *(uint16_t *)src;
675 } else {
676 value = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8);
677 }
678 EEPROM_WriteDataWord((uintptr_t)((uint16_t *)dest), value);
679 dest += 2;
680 src += 2;
681 len -= 2;
682 }
683
684 if (len) {
685 EEPROM_WriteDataByte((uintptr_t)dest, *src);
686 }
687}