eeprom_kinetis_flexram.c (17442B)
1 #include <ch.h> 2 #include <hal.h> 3 4 #include "eeprom_kinetis_flexram.h" 5 #include "eeconfig.h" 6 7 /*************************************/ 8 /* Hardware backend */ 9 /* */ 10 /* Code from PJRC/Teensyduino */ 11 /*************************************/ 12 13 /* Teensyduino Core Library 14 * http://www.pjrc.com/teensy/ 15 * Copyright (c) 2013 PJRC.COM, LLC. 16 * 17 * Permission is hereby granted, free of charge, to any person obtaining 18 * a copy of this software and associated documentation files (the 19 * "Software"), to deal in the Software without restriction, including 20 * without limitation the rights to use, copy, modify, merge, publish, 21 * distribute, sublicense, and/or sell copies of the Software, and to 22 * permit persons to whom the Software is furnished to do so, subject to 23 * the following conditions: 24 * 25 * 1. The above copyright notice and this permission notice shall be 26 * included in all copies or substantial portions of the Software. 27 * 28 * 2. If the Software is incorporated into a build system that allows 29 * selection among a list of target devices, then similar target 30 * devices manufactured by PJRC.COM must be included in the list of 31 * target devices and selectable in the same manner. 32 * 33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 34 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 35 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 36 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 37 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 38 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 39 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 40 * SOFTWARE. 41 */ 42 43 #if defined(K20x) /* chip selection */ 44 /* Teensy 3.0, 3.1, 3.2; mchck; infinity keyboard */ 45 46 /* 47 ^^^ Here be dragons: 48 NXP AppNote AN4282 section 3.1 states that partitioning must only be done once. 49 Once EEPROM partitioning is done, the size is locked to this initial configuration. 50 Attempts to modify the EEPROM_SIZE setting may brick your board. 51 */ 52 53 // Writing unaligned 16 or 32 bit data is handled automatically when 54 // this is defined, but at a cost of extra code size. Without this, 55 // any unaligned write will cause a hard fault exception! If you're 56 // absolutely sure all 16 and 32 bit writes will be aligned, you can 57 // remove the extra unnecessary code. 58 // 59 # define HANDLE_UNALIGNED_WRITES 60 61 // Minimum EEPROM Endurance 62 // ------------------------ 63 # if (EEPROM_SIZE == 2048) // 35000 writes/byte or 70000 writes/word 64 # define EEESIZE 0x33 65 # elif (EEPROM_SIZE == 1024) // 75000 writes/byte or 150000 writes/word 66 # define EEESIZE 0x34 67 # elif (EEPROM_SIZE == 512) // 155000 writes/byte or 310000 writes/word 68 # define EEESIZE 0x35 69 # elif (EEPROM_SIZE == 256) // 315000 writes/byte or 630000 writes/word 70 # define EEESIZE 0x36 71 # elif (EEPROM_SIZE == 128) // 635000 writes/byte or 1270000 writes/word 72 # define EEESIZE 0x37 73 # elif (EEPROM_SIZE == 64) // 1275000 writes/byte or 2550000 writes/word 74 # define EEESIZE 0x38 75 # elif (EEPROM_SIZE == 32) // 2555000 writes/byte or 5110000 writes/word 76 # define EEESIZE 0x39 77 # endif 78 79 /** \brief eeprom initialization 80 * 81 * FIXME: needs doc 82 */ 83 void eeprom_initialize(void) { 84 uint32_t count = 0; 85 uint16_t do_flash_cmd[] = {0xf06f, 0x037f, 0x7003, 0x7803, 0xf013, 0x0f80, 0xd0fb, 0x4770}; 86 uint8_t status; 87 88 if (FTFL->FCNFG & FTFL_FCNFG_RAMRDY) { 89 // FlexRAM is configured as traditional RAM 90 // We need to reconfigure for EEPROM usage 91 FTFL->FCCOB0 = 0x80; // PGMPART = Program Partition Command 92 FTFL->FCCOB4 = EEESIZE; // EEPROM Size 93 FTFL->FCCOB5 = 0x03; // 0K for Dataflash, 32K for EEPROM backup 94 __disable_irq(); 95 // do_flash_cmd() must execute from RAM. Luckily the C syntax is simple... 96 (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFL->FSTAT)); 97 __enable_irq(); 98 status = FTFL->FSTAT; 99 if (status & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL)) { 100 FTFL->FSTAT = (status & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL)); 101 return; // error 102 } 103 } 104 // wait for eeprom to become ready (is this really necessary?) 105 while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { 106 if (++count > 20000) break; 107 } 108 } 109 110 # define FlexRAM ((uint8_t *)0x14000000) 111 112 /** \brief eeprom read byte 113 * 114 * FIXME: needs doc 115 */ 116 uint8_t eeprom_read_byte(const uint8_t *addr) { 117 uint32_t offset = (uint32_t)addr; 118 if (offset >= EEPROM_SIZE) return 0; 119 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); 120 return FlexRAM[offset]; 121 } 122 123 /** \brief eeprom read word 124 * 125 * FIXME: needs doc 126 */ 127 uint16_t eeprom_read_word(const uint16_t *addr) { 128 uint32_t offset = (uint32_t)addr; 129 if (offset >= EEPROM_SIZE - 1) return 0; 130 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); 131 return *(uint16_t *)(&FlexRAM[offset]); 132 } 133 134 /** \brief eeprom read dword 135 * 136 * FIXME: needs doc 137 */ 138 uint32_t eeprom_read_dword(const uint32_t *addr) { 139 uint32_t offset = (uint32_t)addr; 140 if (offset >= EEPROM_SIZE - 3) return 0; 141 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); 142 return *(uint32_t *)(&FlexRAM[offset]); 143 } 144 145 /** \brief eeprom read block 146 * 147 * FIXME: needs doc 148 */ 149 void eeprom_read_block(void *buf, const void *addr, size_t len) { 150 uint32_t offset = (uint32_t)addr; 151 uint8_t *dest = (uint8_t *)buf; 152 uint32_t end = offset + len; 153 154 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); 155 if (end > EEPROM_SIZE) end = EEPROM_SIZE; 156 while (offset < end) { 157 *dest++ = FlexRAM[offset++]; 158 } 159 } 160 161 /** \brief eeprom is ready 162 * 163 * FIXME: needs doc 164 */ 165 int eeprom_is_ready(void) { 166 return (FTFL->FCNFG & FTFL_FCNFG_EEERDY) ? 1 : 0; 167 } 168 169 /** \brief flexram wait 170 * 171 * FIXME: needs doc 172 */ 173 static void flexram_wait(void) { 174 while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { 175 // TODO: timeout 176 } 177 } 178 179 /** \brief eeprom_write_byte 180 * 181 * FIXME: needs doc 182 */ 183 void eeprom_write_byte(uint8_t *addr, uint8_t value) { 184 uint32_t offset = (uint32_t)addr; 185 186 if (offset >= EEPROM_SIZE) return; 187 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); 188 if (FlexRAM[offset] != value) { 189 FlexRAM[offset] = value; 190 flexram_wait(); 191 } 192 } 193 194 /** \brief eeprom write word 195 * 196 * FIXME: needs doc 197 */ 198 void eeprom_write_word(uint16_t *addr, uint16_t value) { 199 uint32_t offset = (uint32_t)addr; 200 201 if (offset >= EEPROM_SIZE - 1) return; 202 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); 203 # ifdef HANDLE_UNALIGNED_WRITES 204 if ((offset & 1) == 0) { 205 # endif 206 if (*(uint16_t *)(&FlexRAM[offset]) != value) { 207 *(uint16_t *)(&FlexRAM[offset]) = value; 208 flexram_wait(); 209 } 210 # ifdef HANDLE_UNALIGNED_WRITES 211 } else { 212 if (FlexRAM[offset] != value) { 213 FlexRAM[offset] = value; 214 flexram_wait(); 215 } 216 if (FlexRAM[offset + 1] != (value >> 8)) { 217 FlexRAM[offset + 1] = value >> 8; 218 flexram_wait(); 219 } 220 } 221 # endif 222 } 223 224 /** \brief eeprom write dword 225 * 226 * FIXME: needs doc 227 */ 228 void eeprom_write_dword(uint32_t *addr, uint32_t value) { 229 uint32_t offset = (uint32_t)addr; 230 231 if (offset >= EEPROM_SIZE - 3) return; 232 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); 233 # ifdef HANDLE_UNALIGNED_WRITES 234 switch (offset & 3) { 235 case 0: 236 # endif 237 if (*(uint32_t *)(&FlexRAM[offset]) != value) { 238 *(uint32_t *)(&FlexRAM[offset]) = value; 239 flexram_wait(); 240 } 241 return; 242 # ifdef HANDLE_UNALIGNED_WRITES 243 case 2: 244 if (*(uint16_t *)(&FlexRAM[offset]) != value) { 245 *(uint16_t *)(&FlexRAM[offset]) = value; 246 flexram_wait(); 247 } 248 if (*(uint16_t *)(&FlexRAM[offset + 2]) != (value >> 16)) { 249 *(uint16_t *)(&FlexRAM[offset + 2]) = value >> 16; 250 flexram_wait(); 251 } 252 return; 253 default: 254 if (FlexRAM[offset] != value) { 255 FlexRAM[offset] = value; 256 flexram_wait(); 257 } 258 if (*(uint16_t *)(&FlexRAM[offset + 1]) != (value >> 8)) { 259 *(uint16_t *)(&FlexRAM[offset + 1]) = value >> 8; 260 flexram_wait(); 261 } 262 if (FlexRAM[offset + 3] != (value >> 24)) { 263 FlexRAM[offset + 3] = value >> 24; 264 flexram_wait(); 265 } 266 } 267 # endif 268 } 269 270 /** \brief eeprom write block 271 * 272 * FIXME: needs doc 273 */ 274 void eeprom_write_block(const void *buf, void *addr, size_t len) { 275 uint32_t offset = (uint32_t)addr; 276 const uint8_t *src = (const uint8_t *)buf; 277 278 if (offset >= EEPROM_SIZE) return; 279 if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); 280 if (len >= EEPROM_SIZE) len = EEPROM_SIZE; 281 if (offset + len >= EEPROM_SIZE) len = EEPROM_SIZE - offset; 282 while (len > 0) { 283 uint32_t lsb = offset & 3; 284 if (lsb == 0 && len >= 4) { 285 // write aligned 32 bits 286 uint32_t val32; 287 val32 = *src++; 288 val32 |= (*src++ << 8); 289 val32 |= (*src++ << 16); 290 val32 |= (*src++ << 24); 291 if (*(uint32_t *)(&FlexRAM[offset]) != val32) { 292 *(uint32_t *)(&FlexRAM[offset]) = val32; 293 flexram_wait(); 294 } 295 offset += 4; 296 len -= 4; 297 } else if ((lsb == 0 || lsb == 2) && len >= 2) { 298 // write aligned 16 bits 299 uint16_t val16; 300 val16 = *src++; 301 val16 |= (*src++ << 8); 302 if (*(uint16_t *)(&FlexRAM[offset]) != val16) { 303 *(uint16_t *)(&FlexRAM[offset]) = val16; 304 flexram_wait(); 305 } 306 offset += 2; 307 len -= 2; 308 } else { 309 // write 8 bits 310 uint8_t val8 = *src++; 311 if (FlexRAM[offset] != val8) { 312 FlexRAM[offset] = val8; 313 flexram_wait(); 314 } 315 offset++; 316 len--; 317 } 318 } 319 } 320 321 /* 322 void do_flash_cmd(volatile uint8_t *fstat) 323 { 324 *fstat = 0x80; 325 while ((*fstat & 0x80) == 0) ; // wait 326 } 327 00000000 <do_flash_cmd>: 328 0: f06f 037f mvn.w r3, #127 ; 0x7f 329 4: 7003 strb r3, [r0, #0] 330 6: 7803 ldrb r3, [r0, #0] 331 8: f013 0f80 tst.w r3, #128 ; 0x80 332 c: d0fb beq.n 6 <do_flash_cmd+0x6> 333 e: 4770 bx lr 334 */ 335 336 #elif defined(KL2x) /* chip selection */ 337 /* Teensy LC (emulated) */ 338 339 # define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) 340 341 extern uint32_t __eeprom_workarea_start__; 342 extern uint32_t __eeprom_workarea_end__; 343 344 static uint32_t flashend = 0; 345 346 void eeprom_initialize(void) { 347 const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); 348 349 do { 350 if (*p++ == 0xFFFF) { 351 flashend = (uint32_t)(p - 2); 352 return; 353 } 354 } while (p < (uint16_t *)SYMVAL(__eeprom_workarea_end__)); 355 flashend = (uint32_t)(p - 1); 356 } 357 358 uint8_t eeprom_read_byte(const uint8_t *addr) { 359 uint32_t offset = (uint32_t)addr; 360 const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); 361 const uint16_t *end = (const uint16_t *)((uint32_t)flashend); 362 uint16_t val; 363 uint8_t data = 0xFF; 364 365 if (!end) { 366 eeprom_initialize(); 367 end = (const uint16_t *)((uint32_t)flashend); 368 } 369 if (offset < EEPROM_SIZE) { 370 while (p <= end) { 371 val = *p++; 372 if ((val & 255) == offset) data = val >> 8; 373 } 374 } 375 return data; 376 } 377 378 static void flash_write(const uint16_t *code, uint32_t addr, uint32_t data) { 379 // with great power comes great responsibility.... 380 uint32_t stat; 381 *(uint32_t *)&(FTFA->FCCOB3) = 0x06000000 | (addr & 0x00FFFFFC); 382 *(uint32_t *)&(FTFA->FCCOB7) = data; 383 __disable_irq(); 384 (*((void (*)(volatile uint8_t *))((uint32_t)code | 1)))(&(FTFA->FSTAT)); 385 __enable_irq(); 386 stat = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL); 387 if (stat) { 388 FTFA->FSTAT = stat; 389 } 390 MCM->PLACR |= MCM_PLACR_CFCC; 391 } 392 393 void eeprom_write_byte(uint8_t *addr, uint8_t data) { 394 uint32_t offset = (uint32_t)addr; 395 const uint16_t *p, *end = (const uint16_t *)((uint32_t)flashend); 396 uint32_t i, val, flashaddr; 397 uint16_t do_flash_cmd[] = {0x2380, 0x7003, 0x7803, 0xb25b, 0x2b00, 0xdafb, 0x4770}; 398 uint8_t buf[EEPROM_SIZE]; 399 400 if (offset >= EEPROM_SIZE) return; 401 if (!end) { 402 eeprom_initialize(); 403 end = (const uint16_t *)((uint32_t)flashend); 404 } 405 if (++end < (uint16_t *)SYMVAL(__eeprom_workarea_end__)) { 406 val = (data << 8) | offset; 407 flashaddr = (uint32_t)end; 408 flashend = flashaddr; 409 if ((flashaddr & 2) == 0) { 410 val |= 0xFFFF0000; 411 } else { 412 val <<= 16; 413 val |= 0x0000FFFF; 414 } 415 flash_write(do_flash_cmd, flashaddr, val); 416 } else { 417 for (i = 0; i < EEPROM_SIZE; i++) { 418 buf[i] = 0xFF; 419 } 420 val = 0; 421 for (p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); p < (uint16_t *)SYMVAL(__eeprom_workarea_end__); p++) { 422 val = *p; 423 if ((val & 255) < EEPROM_SIZE) { 424 buf[val & 255] = val >> 8; 425 } 426 } 427 buf[offset] = data; 428 for (flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); flashaddr < (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_end__); flashaddr += 1024) { 429 *(uint32_t *)&(FTFA->FCCOB3) = 0x09000000 | flashaddr; 430 __disable_irq(); 431 (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFA->FSTAT)); 432 __enable_irq(); 433 val = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL); 434 ; 435 if (val) FTFA->FSTAT = val; 436 MCM->PLACR |= MCM_PLACR_CFCC; 437 } 438 flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); 439 for (i = 0; i < EEPROM_SIZE; i++) { 440 if (buf[i] == 0xFF) continue; 441 if ((flashaddr & 2) == 0) { 442 val = (buf[i] << 8) | i; 443 } else { 444 val = val | (buf[i] << 24) | (i << 16); 445 flash_write(do_flash_cmd, flashaddr, val); 446 } 447 flashaddr += 2; 448 } 449 flashend = flashaddr; 450 if ((flashaddr & 2)) { 451 val |= 0xFFFF0000; 452 flash_write(do_flash_cmd, flashaddr, val); 453 } 454 } 455 } 456 457 /* 458 void do_flash_cmd(volatile uint8_t *fstat) 459 { 460 *fstat = 0x80; 461 while ((*fstat & 0x80) == 0) ; // wait 462 } 463 00000000 <do_flash_cmd>: 464 0: 2380 movs r3, #128 ; 0x80 465 2: 7003 strb r3, [r0, #0] 466 4: 7803 ldrb r3, [r0, #0] 467 6: b25b sxtb r3, r3 468 8: 2b00 cmp r3, #0 469 a: dafb bge.n 4 <do_flash_cmd+0x4> 470 c: 4770 bx lr 471 */ 472 473 uint16_t eeprom_read_word(const uint16_t *addr) { 474 const uint8_t *p = (const uint8_t *)addr; 475 return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8); 476 } 477 478 uint32_t eeprom_read_dword(const uint32_t *addr) { 479 const uint8_t *p = (const uint8_t *)addr; 480 return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8) | (eeprom_read_byte(p + 2) << 16) | (eeprom_read_byte(p + 3) << 24); 481 } 482 483 void eeprom_read_block(void *buf, const void *addr, size_t len) { 484 const uint8_t *p = (const uint8_t *)addr; 485 uint8_t *dest = (uint8_t *)buf; 486 while (len--) { 487 *dest++ = eeprom_read_byte(p++); 488 } 489 } 490 491 int eeprom_is_ready(void) { 492 return 1; 493 } 494 495 void eeprom_write_word(uint16_t *addr, uint16_t value) { 496 uint8_t *p = (uint8_t *)addr; 497 eeprom_write_byte(p++, value); 498 eeprom_write_byte(p, value >> 8); 499 } 500 501 void eeprom_write_dword(uint32_t *addr, uint32_t value) { 502 uint8_t *p = (uint8_t *)addr; 503 eeprom_write_byte(p++, value); 504 eeprom_write_byte(p++, value >> 8); 505 eeprom_write_byte(p++, value >> 16); 506 eeprom_write_byte(p, value >> 24); 507 } 508 509 void eeprom_write_block(const void *buf, void *addr, size_t len) { 510 uint8_t *p = (uint8_t *)addr; 511 const uint8_t *src = (const uint8_t *)buf; 512 while (len--) { 513 eeprom_write_byte(p++, *src++); 514 } 515 } 516 517 #else 518 # error Unsupported Teensy EEPROM. 519 #endif /* chip selection */ 520 // The update functions just calls write for now, but could probably be optimized 521 522 void eeprom_update_byte(uint8_t *addr, uint8_t value) { 523 eeprom_write_byte(addr, value); 524 } 525 526 void eeprom_update_word(uint16_t *addr, uint16_t value) { 527 uint8_t *p = (uint8_t *)addr; 528 eeprom_write_byte(p++, value); 529 eeprom_write_byte(p, value >> 8); 530 } 531 532 void eeprom_update_dword(uint32_t *addr, uint32_t value) { 533 uint8_t *p = (uint8_t *)addr; 534 eeprom_write_byte(p++, value); 535 eeprom_write_byte(p++, value >> 8); 536 eeprom_write_byte(p++, value >> 16); 537 eeprom_write_byte(p, value >> 24); 538 } 539 540 void eeprom_update_block(const void *buf, void *addr, size_t len) { 541 uint8_t *p = (uint8_t *)addr; 542 const uint8_t *src = (const uint8_t *)buf; 543 while (len--) { 544 eeprom_write_byte(p++, *src++); 545 } 546 }