Files
zOS/teensy3/eeprom.c

589 lines
16 KiB
C

/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// To configure the EEPROM size, edit E2END in avr/eeprom.h.
//
// Do *NOT* edit EEPROM_SIZE in this file. It will automatically
// change based on your changes to E2END in avr/eeprom.h.
//
// Generally you should avoid editing this code, unless you really
// know what you're doing.
#include "kinetis.h"
#include <avr/eeprom.h>
//#include "HardwareSerial.h"
#if F_CPU > 120000000 && defined(__MK66FX1M0__)
#include "core_pins.h" // delayMicroseconds()
#endif
#if defined(__MK20DX128__) || defined(__MK20DX256__)
#define EEPROM_MAX 2048
#define EEPARTITION 0x03 // all 32K dataflash for EEPROM, none for Data
#define EEESPLIT 0x30 // must be 0x30 on these chips
#elif defined(__MK64FX512__)
#define EEPROM_MAX 4096
#define EEPARTITION 0x05 // all 128K dataflash for EEPROM
#define EEESPLIT 0x10 // best endurance: 0x00 = first 12%, 0x10 = first 25%, 0x30 = all equal
#elif defined(__MK66FX1M0__)
#define EEPROM_MAX 4096
#define EEPARTITION 0x05 // 128K dataflash for EEPROM, 128K for Data
#define EEESPLIT 0x10 // best endurance: 0x00 = first 12%, 0x10 = first 25%, 0x30 = all equal
#elif defined(__MKL26Z64__)
#define EEPROM_MAX 255
#endif
#if E2END > (EEPROM_MAX-1)
#error "E2END is set larger than the maximum possible EEPROM size"
#endif
#if defined(KINETISK)
// The EEPROM is really RAM with a hardware-based backup system to
// flash memory. Selecting a smaller size EEPROM allows more wear
// leveling, for higher write endurance. If you edit this file,
// set this to the smallest size your application can use. Also,
// due to Freescale's implementation, writing 16 or 32 bit words
// (aligned to 2 or 4 byte boundaries) has twice the endurance
// compared to writing 8 bit bytes.
//
#if E2END < 32
#define EEPROM_SIZE 32
#define EEESIZE 0x09
#elif E2END < 64
#define EEPROM_SIZE 64
#define EEESIZE 0x08
#elif E2END < 128
#define EEPROM_SIZE 128
#define EEESIZE 0x07
#elif E2END < 256
#define EEPROM_SIZE 256
#define EEESIZE 0x06
#elif E2END < 512
#define EEPROM_SIZE 512
#define EEESIZE 0x05
#elif E2END < 1024
#define EEPROM_SIZE 1024
#define EEESIZE 0x04
#elif E2END < 2048
#define EEPROM_SIZE 2048
#define EEESIZE 0x03
#elif E2END < 4096
#define EEPROM_SIZE 4096
#define EEESIZE 0x02
#endif
// Writing unaligned 16 or 32 bit data is handled automatically when
// this is defined, but at a cost of extra code size. Without this,
// any unaligned write will cause a hard fault exception! If you're
// absolutely sure all 16 and 32 bit writes will be aligned, you can
// remove the extra unnecessary code.
//
#define HANDLE_UNALIGNED_WRITES
void eeprom_initialize(void)
{
uint32_t count=0;
uint16_t do_flash_cmd[] = {
0xf06f, 0x037f, 0x7003, 0x7803,
0xf013, 0x0f80, 0xd0fb, 0x4770};
uint8_t status;
if (FTFL_FCNFG & FTFL_FCNFG_RAMRDY) {
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
// FlexRAM is configured as traditional RAM
// We need to reconfigure for EEPROM usage
kinetis_hsrun_disable();
FTFL_FCCOB0 = 0x80; // PGMPART = Program Partition Command
FTFL_FCCOB3 = 0;
FTFL_FCCOB4 = EEESPLIT | EEESIZE;
FTFL_FCCOB5 = EEPARTITION;
__disable_irq();
// do_flash_cmd() must execute from RAM. Luckily the C syntax is simple...
(*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&FTFL_FSTAT);
__enable_irq();
kinetis_hsrun_enable();
status = FTFL_FSTAT;
if (status & 0x70) {
FTFL_FSTAT = (status & 0x70);
return; // error
}
}
// wait for eeprom to become ready (is this really necessary?)
while (!(FTFL_FCNFG & FTFL_FCNFG_EEERDY)) {
if (++count > 200000) break;
}
}
#define FlexRAM ((volatile uint8_t *)0x14000000)
uint8_t eeprom_read_byte(const uint8_t *addr)
{
uint32_t offset = (uint32_t)addr;
if (offset >= EEPROM_SIZE) return 0;
if (!(FTFL_FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
return FlexRAM[offset];
}
uint16_t eeprom_read_word(const uint16_t *addr)
{
uint32_t offset = (uint32_t)addr;
if (offset >= EEPROM_SIZE-1) return 0;
if (!(FTFL_FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
return *(uint16_t *)(&FlexRAM[offset]);
}
uint32_t eeprom_read_dword(const uint32_t *addr)
{
uint32_t offset = (uint32_t)addr;
if (offset >= EEPROM_SIZE-3) return 0;
if (!(FTFL_FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
return *(uint32_t *)(&FlexRAM[offset]);
}
void eeprom_read_block(void *buf, const void *addr, uint32_t len)
{
uint32_t offset = (uint32_t)addr;
uint8_t *dest = (uint8_t *)buf;
uint32_t end = offset + len;
if (!(FTFL_FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
if (end > EEPROM_SIZE) end = EEPROM_SIZE;
while (offset < end) {
*dest++ = FlexRAM[offset++];
}
}
int eeprom_is_ready(void)
{
return (FTFL_FCNFG & FTFL_FCNFG_EEERDY) ? 1 : 0;
}
static void flexram_wait(void)
{
while (!(FTFL_FCNFG & FTFL_FCNFG_EEERDY)) {
// TODO: timeout
}
}
void eeprom_write_byte(uint8_t *addr, uint8_t value)
{
uint32_t offset = (uint32_t)addr;
if (offset >= EEPROM_SIZE) return;
if (!(FTFL_FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
if (FlexRAM[offset] != value) {
kinetis_hsrun_disable();
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
FlexRAM[offset] = value;
flexram_wait();
kinetis_hsrun_enable();
}
}
void eeprom_write_word(uint16_t *addr, uint16_t value)
{
uint32_t offset = (uint32_t)addr;
if (offset >= EEPROM_SIZE-1) return;
if (!(FTFL_FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
#ifdef HANDLE_UNALIGNED_WRITES
if ((offset & 1) == 0) {
#endif
if (*(uint16_t *)(&FlexRAM[offset]) != value) {
kinetis_hsrun_disable();
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
*(uint16_t *)(&FlexRAM[offset]) = value;
flexram_wait();
kinetis_hsrun_enable();
}
#ifdef HANDLE_UNALIGNED_WRITES
} else {
if (FlexRAM[offset] != value) {
kinetis_hsrun_disable();
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
FlexRAM[offset] = value;
flexram_wait();
kinetis_hsrun_enable();
}
if (FlexRAM[offset + 1] != (value >> 8)) {
kinetis_hsrun_disable();
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
FlexRAM[offset + 1] = value >> 8;
flexram_wait();
kinetis_hsrun_enable();
}
}
#endif
}
void eeprom_write_dword(uint32_t *addr, uint32_t value)
{
uint32_t offset = (uint32_t)addr;
if (offset >= EEPROM_SIZE-3) return;
if (!(FTFL_FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
#ifdef HANDLE_UNALIGNED_WRITES
switch (offset & 3) {
case 0:
#endif
if (*(uint32_t *)(&FlexRAM[offset]) != value) {
kinetis_hsrun_disable();
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
*(uint32_t *)(&FlexRAM[offset]) = value;
flexram_wait();
kinetis_hsrun_enable();
}
return;
#ifdef HANDLE_UNALIGNED_WRITES
case 2:
if (*(uint16_t *)(&FlexRAM[offset]) != value) {
kinetis_hsrun_disable();
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
*(uint16_t *)(&FlexRAM[offset]) = value;
flexram_wait();
kinetis_hsrun_enable();
}
if (*(uint16_t *)(&FlexRAM[offset + 2]) != (value >> 16)) {
kinetis_hsrun_disable();
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
*(uint16_t *)(&FlexRAM[offset + 2]) = value >> 16;
flexram_wait();
kinetis_hsrun_enable();
}
return;
default:
if (FlexRAM[offset] != value) {
kinetis_hsrun_disable();
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
FlexRAM[offset] = value;
flexram_wait();
kinetis_hsrun_enable();
}
if (*(uint16_t *)(&FlexRAM[offset + 1]) != (value >> 8)) {
kinetis_hsrun_disable();
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
*(uint16_t *)(&FlexRAM[offset + 1]) = value >> 8;
flexram_wait();
kinetis_hsrun_enable();
}
if (FlexRAM[offset + 3] != (value >> 24)) {
kinetis_hsrun_disable();
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
FlexRAM[offset + 3] = value >> 24;
flexram_wait();
kinetis_hsrun_enable();
}
}
#endif
}
void eeprom_write_block(const void *buf, void *addr, uint32_t len)
{
uint32_t offset = (uint32_t)addr;
const uint8_t *src = (const uint8_t *)buf;
if (offset >= EEPROM_SIZE) return;
if (!(FTFL_FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();
if (len >= EEPROM_SIZE) len = EEPROM_SIZE;
if (offset + len >= EEPROM_SIZE) len = EEPROM_SIZE - offset;
while (len > 0) {
uint32_t lsb = offset & 3;
if (lsb == 0 && len >= 4) {
// write aligned 32 bits
uint32_t val32;
val32 = *src++;
val32 |= (*src++ << 8);
val32 |= (*src++ << 16);
val32 |= (*src++ << 24);
if (*(uint32_t *)(&FlexRAM[offset]) != val32) {
kinetis_hsrun_disable();
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
*(uint32_t *)(&FlexRAM[offset]) = val32;
flexram_wait();
kinetis_hsrun_enable();
}
offset += 4;
len -= 4;
} else if ((lsb == 0 || lsb == 2) && len >= 2) {
// write aligned 16 bits
uint16_t val16;
val16 = *src++;
val16 |= (*src++ << 8);
if (*(uint16_t *)(&FlexRAM[offset]) != val16) {
kinetis_hsrun_disable();
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
*(uint16_t *)(&FlexRAM[offset]) = val16;
flexram_wait();
kinetis_hsrun_enable();
}
offset += 2;
len -= 2;
} else {
// write 8 bits
uint8_t val8 = *src++;
if (FlexRAM[offset] != val8) {
kinetis_hsrun_disable();
uint8_t stat = FTFL_FSTAT & 0x70;
if (stat) FTFL_FSTAT = stat;
FlexRAM[offset] = val8;
flexram_wait();
kinetis_hsrun_enable();
}
offset++;
len--;
}
}
}
/*
void do_flash_cmd(volatile uint8_t *fstat)
{
*fstat = 0x80;
while ((*fstat & 0x80) == 0) ; // wait
}
00000000 <do_flash_cmd>:
0: f06f 037f mvn.w r3, #127 ; 0x7f
4: 7003 strb r3, [r0, #0]
6: 7803 ldrb r3, [r0, #0]
8: f013 0f80 tst.w r3, #128 ; 0x80
c: d0fb beq.n 6 <do_flash_cmd+0x6>
e: 4770 bx lr
*/
#elif defined(KINETISL)
#define EEPROM_SIZE (E2END+1)
#define FLASH_BEGIN (uint16_t *)63488
#define FLASH_END (uint16_t *)65536
static uint16_t flashend = 0;
void eeprom_initialize(void)
{
const uint16_t *p = FLASH_BEGIN;
do {
if (*p++ == 0xFFFF) {
flashend = (uint32_t)(p - 2);
return;
}
} while (p < FLASH_END);
flashend = (uint32_t)(FLASH_END - 1);
}
uint8_t eeprom_read_byte(const uint8_t *addr)
{
uint32_t offset = (uint32_t)addr;
const uint16_t *p = FLASH_BEGIN;
const uint16_t *end = (const uint16_t *)((uint32_t)flashend);
uint16_t val;
uint8_t data=0xFF;
if (!end) {
eeprom_initialize();
end = (const uint16_t *)((uint32_t)flashend);
}
if (offset < EEPROM_SIZE) {
while (p <= end) {
val = *p++;
if ((val & 255) == offset) data = val >> 8;
}
}
return data;
}
static void flash_write(const uint16_t *code, uint32_t addr, uint32_t data)
{
// with great power comes great responsibility....
uint32_t stat;
*(uint32_t *)&FTFL_FCCOB3 = 0x06000000 | (addr & 0x00FFFFFC);
*(uint32_t *)&FTFL_FCCOB7 = data;
__disable_irq();
(*((void (*)(volatile uint8_t *))((uint32_t)code | 1)))(&FTFL_FSTAT);
__enable_irq();
stat = FTFL_FSTAT & 0x70;
if (stat) {
FTFL_FSTAT = stat;
}
MCM_PLACR |= MCM_PLACR_CFCC;
}
void eeprom_write_byte(uint8_t *addr, uint8_t data)
{
uint32_t offset = (uint32_t)addr;
const uint16_t *p, *end = (const uint16_t *)((uint32_t)flashend);
uint32_t i, val, flashaddr;
uint16_t do_flash_cmd[] = {
0x2380, 0x7003, 0x7803, 0xb25b, 0x2b00, 0xdafb, 0x4770};
uint8_t buf[EEPROM_SIZE];
if (offset >= EEPROM_SIZE) return;
if (!end) {
eeprom_initialize();
end = (const uint16_t *)((uint32_t)flashend);
}
if (++end < FLASH_END) {
val = (data << 8) | offset;
flashaddr = (uint32_t)end;
flashend = flashaddr;
if ((flashaddr & 2) == 0) {
val |= 0xFFFF0000;
} else {
val <<= 16;
val |= 0x0000FFFF;
}
flash_write(do_flash_cmd, flashaddr, val);
} else {
for (i=0; i < EEPROM_SIZE; i++) {
buf[i] = 0xFF;
}
for (p = FLASH_BEGIN; p < FLASH_END; p++) {
val = *p;
if ((val & 255) < EEPROM_SIZE) {
buf[val & 255] = val >> 8;
}
}
buf[offset] = data;
for (flashaddr=(uint32_t)FLASH_BEGIN; flashaddr < (uint32_t)FLASH_END; flashaddr += 1024) {
*(uint32_t *)&FTFL_FCCOB3 = 0x09000000 | flashaddr;
__disable_irq();
(*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&FTFL_FSTAT);
__enable_irq();
val = FTFL_FSTAT & 0x70;
if (val) FTFL_FSTAT = val;
MCM_PLACR |= MCM_PLACR_CFCC;
}
flashaddr=(uint32_t)FLASH_BEGIN;
for (i=0; i < EEPROM_SIZE; i++) {
if (buf[i] == 0xFF) continue;
if ((flashaddr & 2) == 0) {
val = (buf[i] << 8) | i;
} else {
val = val | (buf[i] << 24) | (i << 16);
flash_write(do_flash_cmd, flashaddr, val);
}
flashaddr += 2;
}
flashend = flashaddr;
if ((flashaddr & 2)) {
val |= 0xFFFF0000;
flash_write(do_flash_cmd, flashaddr, val);
}
}
}
/*
void do_flash_cmd(volatile uint8_t *fstat)
{
*fstat = 0x80;
while ((*fstat & 0x80) == 0) ; // wait
}
00000000 <do_flash_cmd>:
0: 2380 movs r3, #128 ; 0x80
2: 7003 strb r3, [r0, #0]
4: 7803 ldrb r3, [r0, #0]
6: b25b sxtb r3, r3
8: 2b00 cmp r3, #0
a: dafb bge.n 4 <do_flash_cmd+0x4>
c: 4770 bx lr
*/
uint16_t eeprom_read_word(const uint16_t *addr)
{
const uint8_t *p = (const uint8_t *)addr;
return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8);
}
uint32_t eeprom_read_dword(const uint32_t *addr)
{
const uint8_t *p = (const uint8_t *)addr;
return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8)
| (eeprom_read_byte(p+2) << 16) | (eeprom_read_byte(p+3) << 24);
}
void eeprom_read_block(void *buf, const void *addr, uint32_t len)
{
const uint8_t *p = (const uint8_t *)addr;
uint8_t *dest = (uint8_t *)buf;
while (len--) {
*dest++ = eeprom_read_byte(p++);
}
}
int eeprom_is_ready(void)
{
return 1;
}
void eeprom_write_word(uint16_t *addr, uint16_t value)
{
uint8_t *p = (uint8_t *)addr;
eeprom_write_byte(p++, value);
eeprom_write_byte(p, value >> 8);
}
void eeprom_write_dword(uint32_t *addr, uint32_t value)
{
uint8_t *p = (uint8_t *)addr;
eeprom_write_byte(p++, value);
eeprom_write_byte(p++, value >> 8);
eeprom_write_byte(p++, value >> 16);
eeprom_write_byte(p, value >> 24);
}
void eeprom_write_block(const void *buf, void *addr, uint32_t len)
{
uint8_t *p = (uint8_t *)addr;
const uint8_t *src = (const uint8_t *)buf;
while (len--) {
eeprom_write_byte(p++, *src++);
}
}
#endif // KINETISL