diff options
Diffstat (limited to 'platforms/chibios/eeprom_teensy.c')
-rw-r--r-- | platforms/chibios/eeprom_teensy.c | 795 |
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 | |||
47 | static 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 | |||
105 | static 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 | */ | ||
225 | void 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 | */ | ||
264 | uint8_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 | */ | ||
275 | uint16_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 | */ | ||
286 | uint32_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 | */ | ||
297 | void 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 | */ | ||
313 | int eeprom_is_ready(void) { return (FTFL->FCNFG & FTFL_FCNFG_EEERDY) ? 1 : 0; } | ||
314 | |||
315 | /** \brief flexram wait | ||
316 | * | ||
317 | * FIXME: needs doc | ||
318 | */ | ||
319 | static 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 | */ | ||
329 | void 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 | */ | ||
348 | void 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 | */ | ||
390 | void 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 | */ | ||
460 | void 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 | /* | ||
516 | void do_flash_cmd(volatile uint8_t *fstat) | ||
517 | { | ||
518 | *fstat = 0x80; | ||
519 | while ((*fstat & 0x80) == 0) ; // wait | ||
520 | } | ||
521 | 00000000 <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 | |||
535 | extern uint32_t __eeprom_workarea_start__; | ||
536 | extern uint32_t __eeprom_workarea_end__; | ||
537 | |||
538 | # define EEPROM_SIZE 128 | ||
539 | |||
540 | static uint32_t flashend = 0; | ||
541 | |||
542 | void 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 | |||
554 | uint8_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 | |||
574 | static 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 | |||
589 | void 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 | /* | ||
654 | void do_flash_cmd(volatile uint8_t *fstat) | ||
655 | { | ||
656 | *fstat = 0x80; | ||
657 | while ((*fstat & 0x80) == 0) ; // wait | ||
658 | } | ||
659 | 00000000 <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 | |||
669 | uint16_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 | |||
674 | uint32_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 | |||
679 | void 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 | |||
687 | int eeprom_is_ready(void) { return 1; } | ||
688 | |||
689 | void 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 | |||
695 | void 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 | |||
703 | void 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 | |||
720 | uint8_t eeprom_read_byte(const uint8_t *addr) { | ||
721 | uint32_t offset = (uint32_t)addr; | ||
722 | return buffer[offset]; | ||
723 | } | ||
724 | |||
725 | void eeprom_write_byte(uint8_t *addr, uint8_t value) { | ||
726 | uint32_t offset = (uint32_t)addr; | ||
727 | buffer[offset] = value; | ||
728 | } | ||
729 | |||
730 | uint16_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 | |||
735 | uint32_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 | |||
740 | void 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 | |||
748 | void 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 | |||
754 | void 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 | |||
762 | void 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 | |||
773 | void eeprom_update_byte(uint8_t *addr, uint8_t value) { eeprom_write_byte(addr, value); } | ||
774 | |||
775 | void 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 | |||
781 | void 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 | |||
789 | void 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 | } | ||