aboutsummaryrefslogtreecommitdiff
path: root/platforms/chibios/eeprom_teensy.c
diff options
context:
space:
mode:
Diffstat (limited to 'platforms/chibios/eeprom_teensy.c')
-rw-r--r--platforms/chibios/eeprom_teensy.c795
1 files changed, 795 insertions, 0 deletions
diff --git a/platforms/chibios/eeprom_teensy.c b/platforms/chibios/eeprom_teensy.c
new file mode 100644
index 000000000..97da6f9e1
--- /dev/null
+++ b/platforms/chibios/eeprom_teensy.c
@@ -0,0 +1,795 @@
1#include <ch.h>
2#include <hal.h>
3
4#include "eeconfig.h"
5
6/*************************************/
7/* Hardware backend */
8/* */
9/* Code from PJRC/Teensyduino */
10/*************************************/
11
12/* Teensyduino Core Library
13 * http://www.pjrc.com/teensy/
14 * Copyright (c) 2013 PJRC.COM, LLC.
15 *
16 * Permission is hereby granted, free of charge, to any person obtaining
17 * a copy of this software and associated documentation files (the
18 * "Software"), to deal in the Software without restriction, including
19 * without limitation the rights to use, copy, modify, merge, publish,
20 * distribute, sublicense, and/or sell copies of the Software, and to
21 * permit persons to whom the Software is furnished to do so, subject to
22 * the following conditions:
23 *
24 * 1. The above copyright notice and this permission notice shall be
25 * included in all copies or substantial portions of the Software.
26 *
27 * 2. If the Software is incorporated into a build system that allows
28 * selection among a list of target devices, then similar target
29 * devices manufactured by PJRC.COM must be included in the list of
30 * target devices and selectable in the same manner.
31 *
32 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
36 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
37 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
38 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39 * SOFTWARE.
40 */
41
42#define SMC_PMSTAT_RUN ((uint8_t)0x01)
43#define SMC_PMSTAT_HSRUN ((uint8_t)0x80)
44
45#define F_CPU KINETIS_SYSCLK_FREQUENCY
46
47static inline int kinetis_hsrun_disable(void) {
48#if defined(MK66F18)
49 if (SMC->PMSTAT == SMC_PMSTAT_HSRUN) {
50// First, reduce the CPU clock speed, but do not change
51// the peripheral speed (F_BUS). Serial1 & Serial2 baud
52// rates will be impacted, but most other peripherals
53// will continue functioning at the same speed.
54# if F_CPU == 256000000 && F_BUS == 64000000
55 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7); // TODO: TEST
56# elif F_CPU == 256000000 && F_BUS == 128000000
57 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // TODO: TEST
58# elif F_CPU == 240000000 && F_BUS == 60000000
59 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7); // ok
60# elif F_CPU == 240000000 && F_BUS == 80000000
61 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8); // ok
62# elif F_CPU == 240000000 && F_BUS == 120000000
63 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // ok
64# elif F_CPU == 216000000 && F_BUS == 54000000
65 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7); // ok
66# elif F_CPU == 216000000 && F_BUS == 72000000
67 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8); // ok
68# elif F_CPU == 216000000 && F_BUS == 108000000
69 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // ok
70# elif F_CPU == 192000000 && F_BUS == 48000000
71 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7); // ok
72# elif F_CPU == 192000000 && F_BUS == 64000000
73 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8); // ok
74# elif F_CPU == 192000000 && F_BUS == 96000000
75 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // ok
76# elif F_CPU == 180000000 && F_BUS == 60000000
77 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8); // ok
78# elif F_CPU == 180000000 && F_BUS == 90000000
79 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7); // ok
80# elif F_CPU == 168000000 && F_BUS == 56000000
81 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 5); // ok
82# elif F_CPU == 144000000 && F_BUS == 48000000
83 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 5); // ok
84# elif F_CPU == 144000000 && F_BUS == 72000000
85 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 5); // ok
86# elif F_CPU == 120000000 && F_BUS == 60000000
87 SIM->CLKDIV1 = SIM_CLKDIV1_OUTDIV1(KINETIS_CLKDIV1_OUTDIV1 - 1) | SIM_CLKDIV1_OUTDIV2(KINETIS_CLKDIV1_OUTDIV2 - 1) |
88# if defined(MK66F18)
89 SIM_CLKDIV1_OUTDIV3(KINETIS_CLKDIV1_OUTDIV3 - 1) |
90# endif
91 SIM_CLKDIV1_OUTDIV4(KINETIS_CLKDIV1_OUTDIV4 - 1);
92# else
93 return 0;
94# endif
95 // Then turn off HSRUN mode
96 SMC->PMCTRL = SMC_PMCTRL_RUNM_SET(0);
97 while (SMC->PMSTAT == SMC_PMSTAT_HSRUN)
98 ; // wait
99 return 1;
100 }
101#endif
102 return 0;
103}
104
105static inline int kinetis_hsrun_enable(void) {
106#if defined(MK66F18)
107 if (SMC->PMSTAT == SMC_PMSTAT_RUN) {
108 // Turn HSRUN mode on
109 SMC->PMCTRL = SMC_PMCTRL_RUNM_SET(3);
110 while (SMC->PMSTAT != SMC_PMSTAT_HSRUN) {
111 ;
112 } // wait
113// Then configure clock for full speed
114# if F_CPU == 256000000 && F_BUS == 64000000
115 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 7);
116# elif F_CPU == 256000000 && F_BUS == 128000000
117 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 7);
118# elif F_CPU == 240000000 && F_BUS == 60000000
119 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 7);
120# elif F_CPU == 240000000 && F_BUS == 80000000
121 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 7);
122# elif F_CPU == 240000000 && F_BUS == 120000000
123 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 7);
124# elif F_CPU == 216000000 && F_BUS == 54000000
125 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 7);
126# elif F_CPU == 216000000 && F_BUS == 72000000
127 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 7);
128# elif F_CPU == 216000000 && F_BUS == 108000000
129 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 7);
130# elif F_CPU == 192000000 && F_BUS == 48000000
131 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 6);
132# elif F_CPU == 192000000 && F_BUS == 64000000
133 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 6);
134# elif F_CPU == 192000000 && F_BUS == 96000000
135 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 6);
136# elif F_CPU == 180000000 && F_BUS == 60000000
137 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 6);
138# elif F_CPU == 180000000 && F_BUS == 90000000
139 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 6);
140# elif F_CPU == 168000000 && F_BUS == 56000000
141 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 5);
142# elif F_CPU == 144000000 && F_BUS == 48000000
143 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 4);
144# elif F_CPU == 144000000 && F_BUS == 72000000
145 SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 4);
146# elif F_CPU == 120000000 && F_BUS == 60000000
147 SIM->CLKDIV1 = SIM_CLKDIV1_OUTDIV1(KINETIS_CLKDIV1_OUTDIV1 - 1) | SIM_CLKDIV1_OUTDIV2(KINETIS_CLKDIV1_OUTDIV2 - 1) |
148# if defined(MK66F18)
149 SIM_CLKDIV1_OUTDIV3(KINETIS_CLKDIV1_OUTDIV3 - 1) |
150# endif
151 SIM_CLKDIV1_OUTDIV4(KINETIS_CLKDIV1_OUTDIV4 - 1);
152# else
153 return 0;
154# endif
155 return 1;
156 }
157#endif
158 return 0;
159}
160
161#if defined(K20x) || defined(MK66F18) /* chip selection */
162/* Teensy 3.0, 3.1, 3.2; mchck; infinity keyboard */
163
164// The EEPROM is really RAM with a hardware-based backup system to
165// flash memory. Selecting a smaller size EEPROM allows more wear
166// leveling, for higher write endurance. If you edit this file,
167// set this to the smallest size your application can use. Also,
168// due to Freescale's implementation, writing 16 or 32 bit words
169// (aligned to 2 or 4 byte boundaries) has twice the endurance
170// compared to writing 8 bit bytes.
171//
172# ifndef EEPROM_SIZE
173# define EEPROM_SIZE 32
174# endif
175
176/*
177 ^^^ Here be dragons:
178 NXP AppNote AN4282 section 3.1 states that partitioning must only be done once.
179 Once EEPROM partitioning is done, the size is locked to this initial configuration.
180 Attempts to modify the EEPROM_SIZE setting may brick your board.
181*/
182
183// Writing unaligned 16 or 32 bit data is handled automatically when
184// this is defined, but at a cost of extra code size. Without this,
185// any unaligned write will cause a hard fault exception! If you're
186// absolutely sure all 16 and 32 bit writes will be aligned, you can
187// remove the extra unnecessary code.
188//
189# define HANDLE_UNALIGNED_WRITES
190
191# if defined(K20x)
192# define EEPROM_MAX 2048
193# define EEPARTITION 0x03 // all 32K dataflash for EEPROM, none for Data
194# define EEESPLIT 0x30 // must be 0x30 on these chips
195# elif defined(MK66F18)
196# define EEPROM_MAX 4096
197# define EEPARTITION 0x05 // 128K dataflash for EEPROM, 128K for Data
198# define EEESPLIT 0x10 // best endurance: 0x00 = first 12%, 0x10 = first 25%, 0x30 = all equal
199# endif
200
201// Minimum EEPROM Endurance
202// ------------------------
203# if (EEPROM_SIZE == 4096)
204# define EEESIZE 0x02
205# elif (EEPROM_SIZE == 2048) // 35000 writes/byte or 70000 writes/word
206# define EEESIZE 0x03
207# elif (EEPROM_SIZE == 1024) // 75000 writes/byte or 150000 writes/word
208# define EEESIZE 0x04
209# elif (EEPROM_SIZE == 512) // 155000 writes/byte or 310000 writes/word
210# define EEESIZE 0x05
211# elif (EEPROM_SIZE == 256) // 315000 writes/byte or 630000 writes/word
212# define EEESIZE 0x06
213# elif (EEPROM_SIZE == 128) // 635000 writes/byte or 1270000 writes/word
214# define EEESIZE 0x07
215# elif (EEPROM_SIZE == 64) // 1275000 writes/byte or 2550000 writes/word
216# define EEESIZE 0x08
217# elif (EEPROM_SIZE == 32) // 2555000 writes/byte or 5110000 writes/word
218# define EEESIZE 0x09
219# endif
220
221/** \brief eeprom initialization
222 *
223 * FIXME: needs doc
224 */
225void eeprom_initialize(void) {
226 uint32_t count = 0;
227 uint16_t do_flash_cmd[] = {0xf06f, 0x037f, 0x7003, 0x7803, 0xf013, 0x0f80, 0xd0fb, 0x4770};
228 uint8_t status;
229
230 if (FTFL->FCNFG & FTFL_FCNFG_RAMRDY) {
231 uint8_t stat = FTFL->FSTAT & 0x70;
232 if (stat) FTFL->FSTAT = stat;
233
234 // FlexRAM is configured as traditional RAM
235 // We need to reconfigure for EEPROM usage
236 kinetis_hsrun_disable();
237 FTFL->FCCOB0 = 0x80; // PGMPART = Program Partition Command
238 FTFL->FCCOB3 = 0;
239 FTFL->FCCOB4 = EEESPLIT | EEESIZE;
240 FTFL->FCCOB5 = EEPARTITION;
241 __disable_irq();
242 // do_flash_cmd() must execute from RAM. Luckily the C syntax is simple...
243 (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFL->FSTAT));
244 __enable_irq();
245 kinetis_hsrun_enable();
246 status = FTFL->FSTAT;
247 if (status & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL)) {
248 FTFL->FSTAT = (status & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL));
249 return; // error
250 }
251 }
252 // wait for eeprom to become ready (is this really necessary?)
253 while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) {
254 if (++count > 200000) break;
255 }
256}
257
258# define FlexRAM ((volatile uint8_t *)0x14000000)
259
260/** \brief eeprom read byte
261 *
262 * FIXME: needs doc
263 */
264uint8_t eeprom_read_byte(const uint8_t *addr) {
265 uint32_t offset = (uint32_t)addr;
266 if (offset >= EEPROM_SIZE) return 0;
267 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
268 return FlexRAM[offset];
269}
270
271/** \brief eeprom read word
272 *
273 * FIXME: needs doc
274 */
275uint16_t eeprom_read_word(const uint16_t *addr) {
276 uint32_t offset = (uint32_t)addr;
277 if (offset >= EEPROM_SIZE - 1) return 0;
278 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
279 return *(uint16_t *)(&FlexRAM[offset]);
280}
281
282/** \brief eeprom read dword
283 *
284 * FIXME: needs doc
285 */
286uint32_t eeprom_read_dword(const uint32_t *addr) {
287 uint32_t offset = (uint32_t)addr;
288 if (offset >= EEPROM_SIZE - 3) return 0;
289 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
290 return *(uint32_t *)(&FlexRAM[offset]);
291}
292
293/** \brief eeprom read block
294 *
295 * FIXME: needs doc
296 */
297void eeprom_read_block(void *buf, const void *addr, uint32_t len) {
298 uint32_t offset = (uint32_t)addr;
299 uint8_t *dest = (uint8_t *)buf;
300 uint32_t end = offset + len;
301
302 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
303 if (end > EEPROM_SIZE) end = EEPROM_SIZE;
304 while (offset < end) {
305 *dest++ = FlexRAM[offset++];
306 }
307}
308
309/** \brief eeprom is ready
310 *
311 * FIXME: needs doc
312 */
313int eeprom_is_ready(void) { return (FTFL->FCNFG & FTFL_FCNFG_EEERDY) ? 1 : 0; }
314
315/** \brief flexram wait
316 *
317 * FIXME: needs doc
318 */
319static void flexram_wait(void) {
320 while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) {
321 // TODO: timeout
322 }
323}
324
325/** \brief eeprom_write_byte
326 *
327 * FIXME: needs doc
328 */
329void eeprom_write_byte(uint8_t *addr, uint8_t value) {
330 uint32_t offset = (uint32_t)addr;
331
332 if (offset >= EEPROM_SIZE) return;
333 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
334 if (FlexRAM[offset] != value) {
335 kinetis_hsrun_disable();
336 uint8_t stat = FTFL->FSTAT & 0x70;
337 if (stat) FTFL->FSTAT = stat;
338 FlexRAM[offset] = value;
339 flexram_wait();
340 kinetis_hsrun_enable();
341 }
342}
343
344/** \brief eeprom write word
345 *
346 * FIXME: needs doc
347 */
348void eeprom_write_word(uint16_t *addr, uint16_t value) {
349 uint32_t offset = (uint32_t)addr;
350
351 if (offset >= EEPROM_SIZE - 1) return;
352 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
353# ifdef HANDLE_UNALIGNED_WRITES
354 if ((offset & 1) == 0) {
355# endif
356 if (*(uint16_t *)(&FlexRAM[offset]) != value) {
357 kinetis_hsrun_disable();
358 uint8_t stat = FTFL->FSTAT & 0x70;
359 if (stat) FTFL->FSTAT = stat;
360 *(uint16_t *)(&FlexRAM[offset]) = value;
361 flexram_wait();
362 kinetis_hsrun_enable();
363 }
364# ifdef HANDLE_UNALIGNED_WRITES
365 } else {
366 if (FlexRAM[offset] != value) {
367 kinetis_hsrun_disable();
368 uint8_t stat = FTFL->FSTAT & 0x70;
369 if (stat) FTFL->FSTAT = stat;
370 FlexRAM[offset] = value;
371 flexram_wait();
372 kinetis_hsrun_enable();
373 }
374 if (FlexRAM[offset + 1] != (value >> 8)) {
375 kinetis_hsrun_disable();
376 uint8_t stat = FTFL->FSTAT & 0x70;
377 if (stat) FTFL->FSTAT = stat;
378 FlexRAM[offset + 1] = value >> 8;
379 flexram_wait();
380 kinetis_hsrun_enable();
381 }
382 }
383# endif
384}
385
386/** \brief eeprom write dword
387 *
388 * FIXME: needs doc
389 */
390void eeprom_write_dword(uint32_t *addr, uint32_t value) {
391 uint32_t offset = (uint32_t)addr;
392
393 if (offset >= EEPROM_SIZE - 3) return;
394 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
395# ifdef HANDLE_UNALIGNED_WRITES
396 switch (offset & 3) {
397 case 0:
398# endif
399 if (*(uint32_t *)(&FlexRAM[offset]) != value) {
400 kinetis_hsrun_disable();
401 uint8_t stat = FTFL->FSTAT & 0x70;
402 if (stat) FTFL->FSTAT = stat;
403 *(uint32_t *)(&FlexRAM[offset]) = value;
404 flexram_wait();
405 kinetis_hsrun_enable();
406 }
407 return;
408# ifdef HANDLE_UNALIGNED_WRITES
409 case 2:
410 if (*(uint16_t *)(&FlexRAM[offset]) != value) {
411 kinetis_hsrun_disable();
412 uint8_t stat = FTFL->FSTAT & 0x70;
413 if (stat) FTFL->FSTAT = stat;
414 *(uint16_t *)(&FlexRAM[offset]) = value;
415 flexram_wait();
416 kinetis_hsrun_enable();
417 }
418 if (*(uint16_t *)(&FlexRAM[offset + 2]) != (value >> 16)) {
419 kinetis_hsrun_disable();
420 uint8_t stat = FTFL->FSTAT & 0x70;
421 if (stat) FTFL->FSTAT = stat;
422 *(uint16_t *)(&FlexRAM[offset + 2]) = value >> 16;
423 flexram_wait();
424 kinetis_hsrun_enable();
425 }
426 return;
427 default:
428 if (FlexRAM[offset] != value) {
429 kinetis_hsrun_disable();
430 uint8_t stat = FTFL->FSTAT & 0x70;
431 if (stat) FTFL->FSTAT = stat;
432 FlexRAM[offset] = value;
433 flexram_wait();
434 kinetis_hsrun_enable();
435 }
436 if (*(uint16_t *)(&FlexRAM[offset + 1]) != (value >> 8)) {
437 kinetis_hsrun_disable();
438 uint8_t stat = FTFL->FSTAT & 0x70;
439 if (stat) FTFL->FSTAT = stat;
440 *(uint16_t *)(&FlexRAM[offset + 1]) = value >> 8;
441 flexram_wait();
442 kinetis_hsrun_enable();
443 }
444 if (FlexRAM[offset + 3] != (value >> 24)) {
445 kinetis_hsrun_disable();
446 uint8_t stat = FTFL->FSTAT & 0x70;
447 if (stat) FTFL->FSTAT = stat;
448 FlexRAM[offset + 3] = value >> 24;
449 flexram_wait();
450 kinetis_hsrun_enable();
451 }
452 }
453# endif
454}
455
456/** \brief eeprom write block
457 *
458 * FIXME: needs doc
459 */
460void eeprom_write_block(const void *buf, void *addr, uint32_t len) {
461 uint32_t offset = (uint32_t)addr;
462 const uint8_t *src = (const uint8_t *)buf;
463
464 if (offset >= EEPROM_SIZE) return;
465 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
466 if (len >= EEPROM_SIZE) len = EEPROM_SIZE;
467 if (offset + len >= EEPROM_SIZE) len = EEPROM_SIZE - offset;
468 kinetis_hsrun_disable();
469 while (len > 0) {
470 uint32_t lsb = offset & 3;
471 if (lsb == 0 && len >= 4) {
472 // write aligned 32 bits
473 uint32_t val32;
474 val32 = *src++;
475 val32 |= (*src++ << 8);
476 val32 |= (*src++ << 16);
477 val32 |= (*src++ << 24);
478 if (*(uint32_t *)(&FlexRAM[offset]) != val32) {
479 uint8_t stat = FTFL->FSTAT & 0x70;
480 if (stat) FTFL->FSTAT = stat;
481 *(uint32_t *)(&FlexRAM[offset]) = val32;
482 flexram_wait();
483 }
484 offset += 4;
485 len -= 4;
486 } else if ((lsb == 0 || lsb == 2) && len >= 2) {
487 // write aligned 16 bits
488 uint16_t val16;
489 val16 = *src++;
490 val16 |= (*src++ << 8);
491 if (*(uint16_t *)(&FlexRAM[offset]) != val16) {
492 uint8_t stat = FTFL->FSTAT & 0x70;
493 if (stat) FTFL->FSTAT = stat;
494 *(uint16_t *)(&FlexRAM[offset]) = val16;
495 flexram_wait();
496 }
497 offset += 2;
498 len -= 2;
499 } else {
500 // write 8 bits
501 uint8_t val8 = *src++;
502 if (FlexRAM[offset] != val8) {
503 uint8_t stat = FTFL->FSTAT & 0x70;
504 if (stat) FTFL->FSTAT = stat;
505 FlexRAM[offset] = val8;
506 flexram_wait();
507 }
508 offset++;
509 len--;
510 }
511 }
512 kinetis_hsrun_enable();
513}
514
515/*
516void do_flash_cmd(volatile uint8_t *fstat)
517{
518 *fstat = 0x80;
519 while ((*fstat & 0x80) == 0) ; // wait
520}
52100000000 <do_flash_cmd>:
522 0: f06f 037f mvn.w r3, #127 ; 0x7f
523 4: 7003 strb r3, [r0, #0]
524 6: 7803 ldrb r3, [r0, #0]
525 8: f013 0f80 tst.w r3, #128 ; 0x80
526 c: d0fb beq.n 6 <do_flash_cmd+0x6>
527 e: 4770 bx lr
528*/
529
530#elif defined(KL2x) /* chip selection */
531/* Teensy LC (emulated) */
532
533# define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0))
534
535extern uint32_t __eeprom_workarea_start__;
536extern uint32_t __eeprom_workarea_end__;
537
538# define EEPROM_SIZE 128
539
540static uint32_t flashend = 0;
541
542void eeprom_initialize(void) {
543 const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__);
544
545 do {
546 if (*p++ == 0xFFFF) {
547 flashend = (uint32_t)(p - 2);
548 return;
549 }
550 } while (p < (uint16_t *)SYMVAL(__eeprom_workarea_end__));
551 flashend = (uint32_t)(p - 1);
552}
553
554uint8_t eeprom_read_byte(const uint8_t *addr) {
555 uint32_t offset = (uint32_t)addr;
556 const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__);
557 const uint16_t *end = (const uint16_t *)((uint32_t)flashend);
558 uint16_t val;
559 uint8_t data = 0xFF;
560
561 if (!end) {
562 eeprom_initialize();
563 end = (const uint16_t *)((uint32_t)flashend);
564 }
565 if (offset < EEPROM_SIZE) {
566 while (p <= end) {
567 val = *p++;
568 if ((val & 255) == offset) data = val >> 8;
569 }
570 }
571 return data;
572}
573
574static void flash_write(const uint16_t *code, uint32_t addr, uint32_t data) {
575 // with great power comes great responsibility....
576 uint32_t stat;
577 *(uint32_t *)&(FTFA->FCCOB3) = 0x06000000 | (addr & 0x00FFFFFC);
578 *(uint32_t *)&(FTFA->FCCOB7) = data;
579 __disable_irq();
580 (*((void (*)(volatile uint8_t *))((uint32_t)code | 1)))(&(FTFA->FSTAT));
581 __enable_irq();
582 stat = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL);
583 if (stat) {
584 FTFA->FSTAT = stat;
585 }
586 MCM->PLACR |= MCM_PLACR_CFCC;
587}
588
589void eeprom_write_byte(uint8_t *addr, uint8_t data) {
590 uint32_t offset = (uint32_t)addr;
591 const uint16_t *p, *end = (const uint16_t *)((uint32_t)flashend);
592 uint32_t i, val, flashaddr;
593 uint16_t do_flash_cmd[] = {0x2380, 0x7003, 0x7803, 0xb25b, 0x2b00, 0xdafb, 0x4770};
594 uint8_t buf[EEPROM_SIZE];
595
596 if (offset >= EEPROM_SIZE) return;
597 if (!end) {
598 eeprom_initialize();
599 end = (const uint16_t *)((uint32_t)flashend);
600 }
601 if (++end < (uint16_t *)SYMVAL(__eeprom_workarea_end__)) {
602 val = (data << 8) | offset;
603 flashaddr = (uint32_t)end;
604 flashend = flashaddr;
605 if ((flashaddr & 2) == 0) {
606 val |= 0xFFFF0000;
607 } else {
608 val <<= 16;
609 val |= 0x0000FFFF;
610 }
611 flash_write(do_flash_cmd, flashaddr, val);
612 } else {
613 for (i = 0; i < EEPROM_SIZE; i++) {
614 buf[i] = 0xFF;
615 }
616 val = 0;
617 for (p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); p < (uint16_t *)SYMVAL(__eeprom_workarea_end__); p++) {
618 val = *p;
619 if ((val & 255) < EEPROM_SIZE) {
620 buf[val & 255] = val >> 8;
621 }
622 }
623 buf[offset] = data;
624 for (flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); flashaddr < (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_end__); flashaddr += 1024) {
625 *(uint32_t *)&(FTFA->FCCOB3) = 0x09000000 | flashaddr;
626 __disable_irq();
627 (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFA->FSTAT));
628 __enable_irq();
629 val = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL);
630 ;
631 if (val) FTFA->FSTAT = val;
632 MCM->PLACR |= MCM_PLACR_CFCC;
633 }
634 flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__);
635 for (i = 0; i < EEPROM_SIZE; i++) {
636 if (buf[i] == 0xFF) continue;
637 if ((flashaddr & 2) == 0) {
638 val = (buf[i] << 8) | i;
639 } else {
640 val = val | (buf[i] << 24) | (i << 16);
641 flash_write(do_flash_cmd, flashaddr, val);
642 }
643 flashaddr += 2;
644 }
645 flashend = flashaddr;
646 if ((flashaddr & 2)) {
647 val |= 0xFFFF0000;
648 flash_write(do_flash_cmd, flashaddr, val);
649 }
650 }
651}
652
653/*
654void do_flash_cmd(volatile uint8_t *fstat)
655{
656 *fstat = 0x80;
657 while ((*fstat & 0x80) == 0) ; // wait
658}
65900000000 <do_flash_cmd>:
660 0: 2380 movs r3, #128 ; 0x80
661 2: 7003 strb r3, [r0, #0]
662 4: 7803 ldrb r3, [r0, #0]
663 6: b25b sxtb r3, r3
664 8: 2b00 cmp r3, #0
665 a: dafb bge.n 4 <do_flash_cmd+0x4>
666 c: 4770 bx lr
667*/
668
669uint16_t eeprom_read_word(const uint16_t *addr) {
670 const uint8_t *p = (const uint8_t *)addr;
671 return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8);
672}
673
674uint32_t eeprom_read_dword(const uint32_t *addr) {
675 const uint8_t *p = (const uint8_t *)addr;
676 return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8) | (eeprom_read_byte(p + 2) << 16) | (eeprom_read_byte(p + 3) << 24);
677}
678
679void eeprom_read_block(void *buf, const void *addr, uint32_t len) {
680 const uint8_t *p = (const uint8_t *)addr;
681 uint8_t * dest = (uint8_t *)buf;
682 while (len--) {
683 *dest++ = eeprom_read_byte(p++);
684 }
685}
686
687int eeprom_is_ready(void) { return 1; }
688
689void eeprom_write_word(uint16_t *addr, uint16_t value) {
690 uint8_t *p = (uint8_t *)addr;
691 eeprom_write_byte(p++, value);
692 eeprom_write_byte(p, value >> 8);
693}
694
695void eeprom_write_dword(uint32_t *addr, uint32_t value) {
696 uint8_t *p = (uint8_t *)addr;
697 eeprom_write_byte(p++, value);
698 eeprom_write_byte(p++, value >> 8);
699 eeprom_write_byte(p++, value >> 16);
700 eeprom_write_byte(p, value >> 24);
701}
702
703void eeprom_write_block(const void *buf, void *addr, uint32_t len) {
704 uint8_t * p = (uint8_t *)addr;
705 const uint8_t *src = (const uint8_t *)buf;
706 while (len--) {
707 eeprom_write_byte(p++, *src++);
708 }
709}
710
711#else
712// No EEPROM supported, so emulate it
713
714# ifndef EEPROM_SIZE
715# include "eeconfig.h"
716# define EEPROM_SIZE (((EECONFIG_SIZE + 3) / 4) * 4) // based off eeconfig's current usage, aligned to 4-byte sizes, to deal with LTO
717# endif
718__attribute__((aligned(4))) static uint8_t buffer[EEPROM_SIZE];
719
720uint8_t eeprom_read_byte(const uint8_t *addr) {
721 uint32_t offset = (uint32_t)addr;
722 return buffer[offset];
723}
724
725void eeprom_write_byte(uint8_t *addr, uint8_t value) {
726 uint32_t offset = (uint32_t)addr;
727 buffer[offset] = value;
728}
729
730uint16_t eeprom_read_word(const uint16_t *addr) {
731 const uint8_t *p = (const uint8_t *)addr;
732 return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8);
733}
734
735uint32_t eeprom_read_dword(const uint32_t *addr) {
736 const uint8_t *p = (const uint8_t *)addr;
737 return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8) | (eeprom_read_byte(p + 2) << 16) | (eeprom_read_byte(p + 3) << 24);
738}
739
740void eeprom_read_block(void *buf, const void *addr, size_t len) {
741 const uint8_t *p = (const uint8_t *)addr;
742 uint8_t * dest = (uint8_t *)buf;
743 while (len--) {
744 *dest++ = eeprom_read_byte(p++);
745 }
746}
747
748void eeprom_write_word(uint16_t *addr, uint16_t value) {
749 uint8_t *p = (uint8_t *)addr;
750 eeprom_write_byte(p++, value);
751 eeprom_write_byte(p, value >> 8);
752}
753
754void eeprom_write_dword(uint32_t *addr, uint32_t value) {
755 uint8_t *p = (uint8_t *)addr;
756 eeprom_write_byte(p++, value);
757 eeprom_write_byte(p++, value >> 8);
758 eeprom_write_byte(p++, value >> 16);
759 eeprom_write_byte(p, value >> 24);
760}
761
762void eeprom_write_block(const void *buf, void *addr, size_t len) {
763 uint8_t * p = (uint8_t *)addr;
764 const uint8_t *src = (const uint8_t *)buf;
765 while (len--) {
766 eeprom_write_byte(p++, *src++);
767 }
768}
769
770#endif /* chip selection */
771// The update functions just calls write for now, but could probably be optimized
772
773void eeprom_update_byte(uint8_t *addr, uint8_t value) { eeprom_write_byte(addr, value); }
774
775void eeprom_update_word(uint16_t *addr, uint16_t value) {
776 uint8_t *p = (uint8_t *)addr;
777 eeprom_write_byte(p++, value);
778 eeprom_write_byte(p, value >> 8);
779}
780
781void eeprom_update_dword(uint32_t *addr, uint32_t value) {
782 uint8_t *p = (uint8_t *)addr;
783 eeprom_write_byte(p++, value);
784 eeprom_write_byte(p++, value >> 8);
785 eeprom_write_byte(p++, value >> 16);
786 eeprom_write_byte(p, value >> 24);
787}
788
789void eeprom_update_block(const void *buf, void *addr, size_t len) {
790 uint8_t * p = (uint8_t *)addr;
791 const uint8_t *src = (const uint8_t *)buf;
792 while (len--) {
793 eeprom_write_byte(p++, *src++);
794 }
795}