diff options
Diffstat (limited to 'lib/chibios-contrib/testhal/KINETIS/TEENSY_LC/EEPROM_EMU/eeprom.c')
-rw-r--r-- | lib/chibios-contrib/testhal/KINETIS/TEENSY_LC/EEPROM_EMU/eeprom.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/lib/chibios-contrib/testhal/KINETIS/TEENSY_LC/EEPROM_EMU/eeprom.c b/lib/chibios-contrib/testhal/KINETIS/TEENSY_LC/EEPROM_EMU/eeprom.c new file mode 100644 index 000000000..05ee313d3 --- /dev/null +++ b/lib/chibios-contrib/testhal/KINETIS/TEENSY_LC/EEPROM_EMU/eeprom.c | |||
@@ -0,0 +1,241 @@ | |||
1 | /* | ||
2 | * Eeprom emulation for KL2x chips. | ||
3 | * (c) 2015 flabbergast | ||
4 | * Most of the code is from PJRC/Teensyduino (license below) | ||
5 | * | ||
6 | * Notes: Some wear-levelling is done: | ||
7 | * - emulating 128 bytes of eeprom; i.e. 7 bit "eeprom addresses" | ||
8 | * - using 2048 bytes of flash | ||
9 | * - new values are written consecutively into flash | ||
10 | * as 16bit ("eeprom address",value) pairs | ||
11 | * - if all 2048 bytes of flash is used, it is erased and writes | ||
12 | * start from the beginning again | ||
13 | * - the 2048 bytes of flash used are at the end of the flash | ||
14 | * - BEWARE: there is no protection! Use a custom .ld script | ||
15 | * to make sure this area is never used for code! | ||
16 | */ | ||
17 | |||
18 | /* Teensyduino Core Library | ||
19 | * http://www.pjrc.com/teensy/ | ||
20 | * Copyright (c) 2013 PJRC.COM, LLC. | ||
21 | * | ||
22 | * Permission is hereby granted, free of charge, to any person obtaining | ||
23 | * a copy of this software and associated documentation files (the | ||
24 | * "Software"), to deal in the Software without restriction, including | ||
25 | * without limitation the rights to use, copy, modify, merge, publish, | ||
26 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
27 | * permit persons to whom the Software is furnished to do so, subject to | ||
28 | * the following conditions: | ||
29 | * | ||
30 | * 1. The above copyright notice and this permission notice shall be | ||
31 | * included in all copies or substantial portions of the Software. | ||
32 | * | ||
33 | * 2. If the Software is incorporated into a build system that allows | ||
34 | * selection among a list of target devices, then similar target | ||
35 | * devices manufactured by PJRC.COM must be included in the list of | ||
36 | * target devices and selectable in the same manner. | ||
37 | * | ||
38 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
39 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
40 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
41 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
42 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
43 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
44 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
45 | * SOFTWARE. | ||
46 | */ | ||
47 | |||
48 | #include "ch.h" | ||
49 | #include "hal.h" | ||
50 | |||
51 | #define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) | ||
52 | |||
53 | extern uint32_t __eeprom_workarea_start__; | ||
54 | extern uint32_t __eeprom_workarea_end__; | ||
55 | |||
56 | #define EEPROM_SIZE 128 | ||
57 | |||
58 | static uint32_t flashend = 0; | ||
59 | |||
60 | void eeprom_initialize(void) | ||
61 | { | ||
62 | const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); | ||
63 | |||
64 | do { | ||
65 | if (*p++ == 0xFFFF) { | ||
66 | flashend = (uint32_t)(p - 2); | ||
67 | return; | ||
68 | } | ||
69 | } while (p < (uint16_t *)SYMVAL(__eeprom_workarea_end__)); | ||
70 | flashend = (uint32_t)((uint16_t *)SYMVAL(__eeprom_workarea_end__) - 1); | ||
71 | } | ||
72 | |||
73 | uint8_t eeprom_read_byte(const uint8_t *addr) | ||
74 | { | ||
75 | uint32_t offset = (uint32_t)addr; | ||
76 | const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); | ||
77 | const uint16_t *end = (const uint16_t *)((uint32_t)flashend); | ||
78 | uint16_t val; | ||
79 | uint8_t data=0xFF; | ||
80 | |||
81 | if (!end) { | ||
82 | eeprom_initialize(); | ||
83 | end = (const uint16_t *)((uint32_t)flashend); | ||
84 | } | ||
85 | if (offset < EEPROM_SIZE) { | ||
86 | while (p <= end) { | ||
87 | val = *p++; | ||
88 | if ((val & 255) == offset) data = val >> 8; | ||
89 | } | ||
90 | } | ||
91 | return data; | ||
92 | } | ||
93 | |||
94 | static void flash_write(const uint16_t *code, uint32_t addr, uint32_t data) | ||
95 | { | ||
96 | // with great power comes great responsibility.... | ||
97 | uint32_t stat; | ||
98 | *(uint32_t *)&(FTFA->FCCOB3) = 0x06000000 | (addr & 0x00FFFFFC); | ||
99 | *(uint32_t *)&(FTFA->FCCOB7) = data; | ||
100 | __disable_irq(); | ||
101 | (*((void (*)(volatile uint8_t *))((uint32_t)code | 1)))(&(FTFA->FSTAT)); | ||
102 | __enable_irq(); | ||
103 | stat = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR|FTFA_FSTAT_ACCERR|FTFA_FSTAT_FPVIOL); | ||
104 | if (stat) { | ||
105 | FTFA->FSTAT = stat; | ||
106 | } | ||
107 | MCM->PLACR |= MCM_PLACR_CFCC; | ||
108 | } | ||
109 | |||
110 | void eeprom_write_byte(uint8_t *addr, uint8_t data) | ||
111 | { | ||
112 | uint32_t offset = (uint32_t)addr; | ||
113 | const uint16_t *p, *end = (const uint16_t *)((uint32_t)flashend); | ||
114 | uint32_t i, val, flashaddr; | ||
115 | uint16_t do_flash_cmd[] = { | ||
116 | 0x2380, 0x7003, 0x7803, 0xb25b, 0x2b00, 0xdafb, 0x4770}; | ||
117 | uint8_t buf[EEPROM_SIZE]; | ||
118 | |||
119 | if (offset >= EEPROM_SIZE) return; | ||
120 | if (!end) { | ||
121 | eeprom_initialize(); | ||
122 | end = (const uint16_t *)((uint32_t)flashend); | ||
123 | } | ||
124 | if (++end < (uint16_t *)SYMVAL(__eeprom_workarea_end__)) { | ||
125 | val = (data << 8) | offset; | ||
126 | flashaddr = (uint32_t)end; | ||
127 | flashend = flashaddr; | ||
128 | if ((flashaddr & 2) == 0) { | ||
129 | val |= 0xFFFF0000; | ||
130 | } else { | ||
131 | val <<= 16; | ||
132 | val |= 0x0000FFFF; | ||
133 | } | ||
134 | flash_write(do_flash_cmd, flashaddr, val); | ||
135 | } else { | ||
136 | for (i=0; i < EEPROM_SIZE; i++) { | ||
137 | buf[i] = 0xFF; | ||
138 | } | ||
139 | for (p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); p < (uint16_t *)SYMVAL(__eeprom_workarea_end__); p++) { | ||
140 | val = *p; | ||
141 | if ((val & 255) < EEPROM_SIZE) { | ||
142 | buf[val & 255] = val >> 8; | ||
143 | } | ||
144 | } | ||
145 | buf[offset] = data; | ||
146 | for (flashaddr=(uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); flashaddr < (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_end__); flashaddr += 1024) { | ||
147 | *(uint32_t *)&(FTFA->FCCOB3) = 0x09000000 | flashaddr; | ||
148 | __disable_irq(); | ||
149 | (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFA->FSTAT)); | ||
150 | __enable_irq(); | ||
151 | val = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR|FTFA_FSTAT_ACCERR|FTFA_FSTAT_FPVIOL);; | ||
152 | if (val) FTFA->FSTAT = val; | ||
153 | MCM->PLACR |= MCM_PLACR_CFCC; | ||
154 | } | ||
155 | flashaddr=(uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); | ||
156 | for (i=0; i < EEPROM_SIZE; i++) { | ||
157 | if (buf[i] == 0xFF) continue; | ||
158 | if ((flashaddr & 2) == 0) { | ||
159 | val = (buf[i] << 8) | i; | ||
160 | } else { | ||
161 | val = val | (buf[i] << 24) | (i << 16); | ||
162 | flash_write(do_flash_cmd, flashaddr, val); | ||
163 | } | ||
164 | flashaddr += 2; | ||
165 | } | ||
166 | flashend = flashaddr; | ||
167 | if ((flashaddr & 2)) { | ||
168 | val |= 0xFFFF0000; | ||
169 | flash_write(do_flash_cmd, flashaddr, val); | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | void do_flash_cmd(volatile uint8_t *fstat) | ||
176 | { | ||
177 | *fstat = 0x80; | ||
178 | while ((*fstat & 0x80) == 0) ; // wait | ||
179 | } | ||
180 | 00000000 <do_flash_cmd>: | ||
181 | 0: 2380 movs r3, #128 ; 0x80 | ||
182 | 2: 7003 strb r3, [r0, #0] | ||
183 | 4: 7803 ldrb r3, [r0, #0] | ||
184 | 6: b25b sxtb r3, r3 | ||
185 | 8: 2b00 cmp r3, #0 | ||
186 | a: dafb bge.n 4 <do_flash_cmd+0x4> | ||
187 | c: 4770 bx lr | ||
188 | */ | ||
189 | |||
190 | |||
191 | uint16_t eeprom_read_word(const uint16_t *addr) | ||
192 | { | ||
193 | const uint8_t *p = (const uint8_t *)addr; | ||
194 | return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8); | ||
195 | } | ||
196 | |||
197 | uint32_t eeprom_read_dword(const uint32_t *addr) | ||
198 | { | ||
199 | const uint8_t *p = (const uint8_t *)addr; | ||
200 | return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8) | ||
201 | | (eeprom_read_byte(p+2) << 16) | (eeprom_read_byte(p+3) << 24); | ||
202 | } | ||
203 | |||
204 | void eeprom_read_block(void *buf, const void *addr, uint32_t len) | ||
205 | { | ||
206 | const uint8_t *p = (const uint8_t *)addr; | ||
207 | uint8_t *dest = (uint8_t *)buf; | ||
208 | while (len--) { | ||
209 | *dest++ = eeprom_read_byte(p++); | ||
210 | } | ||
211 | } | ||
212 | |||
213 | int eeprom_is_ready(void) | ||
214 | { | ||
215 | return 1; | ||
216 | } | ||
217 | |||
218 | void eeprom_write_word(uint16_t *addr, uint16_t value) | ||
219 | { | ||
220 | uint8_t *p = (uint8_t *)addr; | ||
221 | eeprom_write_byte(p++, value); | ||
222 | eeprom_write_byte(p, value >> 8); | ||
223 | } | ||
224 | |||
225 | void eeprom_write_dword(uint32_t *addr, uint32_t value) | ||
226 | { | ||
227 | uint8_t *p = (uint8_t *)addr; | ||
228 | eeprom_write_byte(p++, value); | ||
229 | eeprom_write_byte(p++, value >> 8); | ||
230 | eeprom_write_byte(p++, value >> 16); | ||
231 | eeprom_write_byte(p, value >> 24); | ||
232 | } | ||
233 | |||
234 | void eeprom_write_block(const void *buf, void *addr, uint32_t len) | ||
235 | { | ||
236 | uint8_t *p = (uint8_t *)addr; | ||
237 | const uint8_t *src = (const uint8_t *)buf; | ||
238 | while (len--) { | ||
239 | eeprom_write_byte(p++, *src++); | ||
240 | } | ||
241 | } | ||