mirror of
https://github.com/Keychron/qmk_firmware.git
synced 2024-12-01 21:26:55 +06:00
796 lines
27 KiB
C
796 lines
27 KiB
C
#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++);
|
|
}
|
|
}
|