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 0000000000..97da6f9e14 --- /dev/null +++ b/platforms/chibios/eeprom_teensy.c @@ -0,0 +1,795 @@ +#include <ch.h> +#include <hal.h> + +#include "eeconfig.h" + +/*************************************/ +/*          Hardware backend         */ +/*                                   */ +/*    Code from PJRC/Teensyduino     */ +/*************************************/ + +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2013 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. + */ + +#define SMC_PMSTAT_RUN ((uint8_t)0x01) +#define SMC_PMSTAT_HSRUN ((uint8_t)0x80) + +#define F_CPU KINETIS_SYSCLK_FREQUENCY + +static inline int kinetis_hsrun_disable(void) { +#if defined(MK66F18) +    if (SMC->PMSTAT == SMC_PMSTAT_HSRUN) { +// First, reduce the CPU clock speed, but do not change +// the peripheral speed (F_BUS).  Serial1 & Serial2 baud +// rates will be impacted, but most other peripherals +// will continue functioning at the same speed. +#    if F_CPU == 256000000 && F_BUS == 64000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7);  // TODO: TEST +#    elif F_CPU == 256000000 && F_BUS == 128000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7);  // TODO: TEST +#    elif F_CPU == 240000000 && F_BUS == 60000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7);  // ok +#    elif F_CPU == 240000000 && F_BUS == 80000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8);  // ok +#    elif F_CPU == 240000000 && F_BUS == 120000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7);  // ok +#    elif F_CPU == 216000000 && F_BUS == 54000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7);  // ok +#    elif F_CPU == 216000000 && F_BUS == 72000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8);  // ok +#    elif F_CPU == 216000000 && F_BUS == 108000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7);  // ok +#    elif F_CPU == 192000000 && F_BUS == 48000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 3, 1, 7);  // ok +#    elif F_CPU == 192000000 && F_BUS == 64000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8);  // ok +#    elif F_CPU == 192000000 && F_BUS == 96000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7);  // ok +#    elif F_CPU == 180000000 && F_BUS == 60000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 8);  // ok +#    elif F_CPU == 180000000 && F_BUS == 90000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 7);  // ok +#    elif F_CPU == 168000000 && F_BUS == 56000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 5);  // ok +#    elif F_CPU == 144000000 && F_BUS == 48000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(2, 2, 2, 5);  // ok +#    elif F_CPU == 144000000 && F_BUS == 72000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 5);  // ok +#    elif F_CPU == 120000000 && F_BUS == 60000000 +        SIM->CLKDIV1 = SIM_CLKDIV1_OUTDIV1(KINETIS_CLKDIV1_OUTDIV1 - 1) | SIM_CLKDIV1_OUTDIV2(KINETIS_CLKDIV1_OUTDIV2 - 1) | +#        if defined(MK66F18) +                       SIM_CLKDIV1_OUTDIV3(KINETIS_CLKDIV1_OUTDIV3 - 1) | +#        endif +                       SIM_CLKDIV1_OUTDIV4(KINETIS_CLKDIV1_OUTDIV4 - 1); +#    else +        return 0; +#    endif +        // Then turn off HSRUN mode +        SMC->PMCTRL = SMC_PMCTRL_RUNM_SET(0); +        while (SMC->PMSTAT == SMC_PMSTAT_HSRUN) +            ;  // wait +        return 1; +    } +#endif +    return 0; +} + +static inline int kinetis_hsrun_enable(void) { +#if defined(MK66F18) +    if (SMC->PMSTAT == SMC_PMSTAT_RUN) { +        // Turn HSRUN mode on +        SMC->PMCTRL = SMC_PMCTRL_RUNM_SET(3); +        while (SMC->PMSTAT != SMC_PMSTAT_HSRUN) { +            ; +        }  // wait +// Then configure clock for full speed +#    if F_CPU == 256000000 && F_BUS == 64000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 7); +#    elif F_CPU == 256000000 && F_BUS == 128000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 7); +#    elif F_CPU == 240000000 && F_BUS == 60000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 7); +#    elif F_CPU == 240000000 && F_BUS == 80000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 7); +#    elif F_CPU == 240000000 && F_BUS == 120000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 7); +#    elif F_CPU == 216000000 && F_BUS == 54000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 7); +#    elif F_CPU == 216000000 && F_BUS == 72000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 7); +#    elif F_CPU == 216000000 && F_BUS == 108000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 7); +#    elif F_CPU == 192000000 && F_BUS == 48000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 3, 0, 6); +#    elif F_CPU == 192000000 && F_BUS == 64000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 6); +#    elif F_CPU == 192000000 && F_BUS == 96000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 6); +#    elif F_CPU == 180000000 && F_BUS == 60000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 6); +#    elif F_CPU == 180000000 && F_BUS == 90000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 6); +#    elif F_CPU == 168000000 && F_BUS == 56000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 5); +#    elif F_CPU == 144000000 && F_BUS == 48000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 2, 0, 4); +#    elif F_CPU == 144000000 && F_BUS == 72000000 +        SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(0, 1, 0, 4); +#    elif F_CPU == 120000000 && F_BUS == 60000000 +        SIM->CLKDIV1 = SIM_CLKDIV1_OUTDIV1(KINETIS_CLKDIV1_OUTDIV1 - 1) | SIM_CLKDIV1_OUTDIV2(KINETIS_CLKDIV1_OUTDIV2 - 1) | +#        if defined(MK66F18) +                       SIM_CLKDIV1_OUTDIV3(KINETIS_CLKDIV1_OUTDIV3 - 1) | +#        endif +                       SIM_CLKDIV1_OUTDIV4(KINETIS_CLKDIV1_OUTDIV4 - 1); +#    else +        return 0; +#    endif +        return 1; +    } +#endif +    return 0; +} + +#if defined(K20x) || defined(MK66F18) /* chip selection */ +/* Teensy 3.0, 3.1, 3.2; mchck; infinity keyboard */ + +// 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. +// +#    ifndef EEPROM_SIZE +#        define EEPROM_SIZE 32 +#    endif + +/* +    ^^^ Here be dragons: +        NXP AppNote AN4282 section 3.1 states that partitioning must only be done once. +        Once EEPROM partitioning is done, the size is locked to this initial configuration. +        Attempts to modify the EEPROM_SIZE setting may brick your board. +*/ + +// 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 + +#    if defined(K20x) +#        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(MK66F18) +#        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 +#    endif + +// Minimum EEPROM Endurance +// ------------------------ +#    if (EEPROM_SIZE == 4096) +#        define EEESIZE 0x02 +#    elif (EEPROM_SIZE == 2048)  // 35000 writes/byte or 70000 writes/word +#        define EEESIZE 0x03 +#    elif (EEPROM_SIZE == 1024)  // 75000 writes/byte or 150000 writes/word +#        define EEESIZE 0x04 +#    elif (EEPROM_SIZE == 512)  // 155000 writes/byte or 310000 writes/word +#        define EEESIZE 0x05 +#    elif (EEPROM_SIZE == 256)  // 315000 writes/byte or 630000 writes/word +#        define EEESIZE 0x06 +#    elif (EEPROM_SIZE == 128)  // 635000 writes/byte or 1270000 writes/word +#        define EEESIZE 0x07 +#    elif (EEPROM_SIZE == 64)  // 1275000 writes/byte or 2550000 writes/word +#        define EEESIZE 0x08 +#    elif (EEPROM_SIZE == 32)  // 2555000 writes/byte or 5110000 writes/word +#        define EEESIZE 0x09 +#    endif + +/** \brief eeprom initialization + * + * FIXME: needs doc + */ +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 & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL)) { +            FTFL->FSTAT = (status & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL)); +            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) + +/** \brief eeprom read byte + * + * FIXME: needs doc + */ +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]; +} + +/** \brief eeprom read word + * + * FIXME: needs doc + */ +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]); +} + +/** \brief eeprom read dword + * + * FIXME: needs doc + */ +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]); +} + +/** \brief eeprom read block + * + * FIXME: needs doc + */ +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++]; +    } +} + +/** \brief eeprom is ready + * + * FIXME: needs doc + */ +int eeprom_is_ready(void) { return (FTFL->FCNFG & FTFL_FCNFG_EEERDY) ? 1 : 0; } + +/** \brief flexram wait + * + * FIXME: needs doc + */ +static void flexram_wait(void) { +    while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { +        // TODO: timeout +    } +} + +/** \brief eeprom_write_byte + * + * FIXME: needs doc + */ +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(); +    } +} + +/** \brief eeprom write word + * + * FIXME: needs doc + */ +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 +} + +/** \brief eeprom write dword + * + * FIXME: needs doc + */ +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 +} + +/** \brief eeprom write block + * + * FIXME: needs doc + */ +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; +    kinetis_hsrun_disable(); +    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) { +                uint8_t stat = FTFL->FSTAT & 0x70; +                if (stat) FTFL->FSTAT = stat; +                *(uint32_t *)(&FlexRAM[offset]) = val32; +                flexram_wait(); +            } +            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) { +                uint8_t stat = FTFL->FSTAT & 0x70; +                if (stat) FTFL->FSTAT = stat; +                *(uint16_t *)(&FlexRAM[offset]) = val16; +                flexram_wait(); +            } +            offset += 2; +            len -= 2; +        } else { +            // write 8 bits +            uint8_t val8 = *src++; +            if (FlexRAM[offset] != val8) { +                uint8_t stat = FTFL->FSTAT & 0x70; +                if (stat) FTFL->FSTAT = stat; +                FlexRAM[offset] = val8; +                flexram_wait(); +            } +            offset++; +            len--; +        } +    } +    kinetis_hsrun_enable(); +} + +/* +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(KL2x) /* chip selection */ +/* Teensy LC (emulated) */ + +#    define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) + +extern uint32_t __eeprom_workarea_start__; +extern uint32_t __eeprom_workarea_end__; + +#    define EEPROM_SIZE 128 + +static uint32_t flashend = 0; + +void eeprom_initialize(void) { +    const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); + +    do { +        if (*p++ == 0xFFFF) { +            flashend = (uint32_t)(p - 2); +            return; +        } +    } while (p < (uint16_t *)SYMVAL(__eeprom_workarea_end__)); +    flashend = (uint32_t)(p - 1); +} + +uint8_t eeprom_read_byte(const uint8_t *addr) { +    uint32_t        offset = (uint32_t)addr; +    const uint16_t *p      = (uint16_t *)SYMVAL(__eeprom_workarea_start__); +    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 *)&(FTFA->FCCOB3) = 0x06000000 | (addr & 0x00FFFFFC); +    *(uint32_t *)&(FTFA->FCCOB7) = data; +    __disable_irq(); +    (*((void (*)(volatile uint8_t *))((uint32_t)code | 1)))(&(FTFA->FSTAT)); +    __enable_irq(); +    stat = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL); +    if (stat) { +        FTFA->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 < (uint16_t *)SYMVAL(__eeprom_workarea_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; +        } +        val = 0; +        for (p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); p < (uint16_t *)SYMVAL(__eeprom_workarea_end__); p++) { +            val = *p; +            if ((val & 255) < EEPROM_SIZE) { +                buf[val & 255] = val >> 8; +            } +        } +        buf[offset] = data; +        for (flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); flashaddr < (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_end__); flashaddr += 1024) { +            *(uint32_t *)&(FTFA->FCCOB3) = 0x09000000 | flashaddr; +            __disable_irq(); +            (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFA->FSTAT)); +            __enable_irq(); +            val = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL); +            ; +            if (val) FTFA->FSTAT = val; +            MCM->PLACR |= MCM_PLACR_CFCC; +        } +        flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); +        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++); +    } +} + +#else +// No EEPROM supported, so emulate it + +#    ifndef EEPROM_SIZE +#        include "eeconfig.h" +#        define EEPROM_SIZE (((EECONFIG_SIZE + 3) / 4) * 4)  // based off eeconfig's current usage, aligned to 4-byte sizes, to deal with LTO +#    endif +__attribute__((aligned(4))) static uint8_t buffer[EEPROM_SIZE]; + +uint8_t eeprom_read_byte(const uint8_t *addr) { +    uint32_t offset = (uint32_t)addr; +    return buffer[offset]; +} + +void eeprom_write_byte(uint8_t *addr, uint8_t value) { +    uint32_t offset = (uint32_t)addr; +    buffer[offset]  = value; +} + +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, size_t len) { +    const uint8_t *p    = (const uint8_t *)addr; +    uint8_t *      dest = (uint8_t *)buf; +    while (len--) { +        *dest++ = eeprom_read_byte(p++); +    } +} + +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, size_t len) { +    uint8_t *      p   = (uint8_t *)addr; +    const uint8_t *src = (const uint8_t *)buf; +    while (len--) { +        eeprom_write_byte(p++, *src++); +    } +} + +#endif /* chip selection */ +// The update functions just calls write for now, but could probably be optimized + +void eeprom_update_byte(uint8_t *addr, uint8_t value) { eeprom_write_byte(addr, value); } + +void eeprom_update_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_update_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_update_block(const void *buf, void *addr, size_t len) { +    uint8_t *      p   = (uint8_t *)addr; +    const uint8_t *src = (const uint8_t *)buf; +    while (len--) { +        eeprom_write_byte(p++, *src++); +    } +}  | 
