Skip to content
Permalink
Browse files

Add eeprom support for imxrt

  • Loading branch information...
PaulStoffregen committed May 30, 2019
1 parent 938be5e commit 5179c6b3ab83faf40b1bbd7f4f2e055c26ff18e8
Showing with 327 additions and 0 deletions.
  1. +36 −0 teensy4/avr/eeprom.h
  2. +291 −0 teensy4/eeprom.c
@@ -0,0 +1,36 @@
/* Simple compatibility headers for AVR code used with ARM chips
* Copyright (c) 2015 Paul Stoffregen <paul@pjrc.com>
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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.
*/

// Guidelines for editing this file:
// https://forum.pjrc.com/threads/34537-Teensy-LC-Increase-EEPROM-Size/page2

#ifndef _AVR_EEPROM_H_
#define _AVR_EEPROM_H_ 1

#include <stddef.h>
#include <stdint.h>

#include "avr_functions.h"

#define E2END 0x437

#endif
@@ -0,0 +1,291 @@
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2019 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.
//
// Generally you should avoid editing this code, unless you really
// know what you're doing.

#include "imxrt.h"
#include <avr/eeprom.h>
#include <string.h>
#include "debug/printf.h"

#define FLASH_BASEADDR 0x601F0000
#define FLASH_SECTORS 15

#if E2END > (255*FLASH_SECTORS-1)
#error "E2END is set larger than the maximum possible EEPROM size"
#endif

static void flash_write(void *addr, const void *data, uint32_t len);
static void flash_erase_sector(void *addr);

static uint8_t initialized=0;
static uint16_t sector_index[FLASH_SECTORS];

void eeprom_initialize(void)
{
uint32_t sector;
//printf("eeprom init\n");
for (sector=0; sector < FLASH_SECTORS; sector++) {
const uint16_t *p = (uint16_t *)(FLASH_BASEADDR + sector * 4096);
const uint16_t *end = (uint16_t *)(FLASH_BASEADDR + (sector + 1) * 4096);
uint16_t index = 0;
do {
if (*p++ == 0xFFFF) break;
index++;
} while (p < end);
sector_index[sector] = index;
}
initialized = 1;
}

uint8_t eeprom_read_byte(const uint8_t *addr_ptr)
{
uint32_t addr = (uint32_t)addr_ptr;
uint32_t sector, offset;
const uint16_t *p, *end;
uint8_t data=0xFF;

if (addr > E2END) return 0xFF;
if (!initialized) eeprom_initialize();
sector = (addr >> 2) % FLASH_SECTORS;
offset = (addr & 3) | (((addr >> 2) / FLASH_SECTORS) << 2);
//printf("ee_rd, addr=%u, sector=%u, offset=%u, len=%u\n",
//addr, sector, offset, sector_index[sector]);
p = (uint16_t *)(FLASH_BASEADDR + sector * 4096);
end = p + sector_index[sector];
while (p < end) {
uint32_t val = *p++;
if ((val & 255) == offset) data = val >> 8;
}
return data;
}

void eeprom_write_byte(uint8_t *addr_ptr, uint8_t data)
{
uint32_t addr = (uint32_t)addr_ptr;
uint32_t sector, offset, index, i;
uint16_t *p, *end;
uint8_t olddata=0xFF;
uint8_t buf[256];

if (addr > E2END) return;
if (!initialized) eeprom_initialize();

sector = (addr >> 2) % FLASH_SECTORS;
offset = (addr & 3) | (((addr >> 2) / FLASH_SECTORS) << 2);
//printf("ee_wr, addr=%u, sector=%u, offset=%u, len=%u\n",
//addr, sector, offset, sector_index[sector]);
p = (uint16_t *)(FLASH_BASEADDR + sector * 4096);
end = p + sector_index[sector];
while (p < end) {
uint16_t val = *p++;
if ((val & 255) == offset) olddata = val >> 8;
}
if (data == olddata) return;
if (sector_index[sector] < 2048) {
//printf("ee_wr, writing\n");
uint16_t newdata = offset | (data << 8);
flash_write(end, &newdata, 2);
sector_index[sector] = sector_index[sector] + 1;
} else {
//printf("ee_wr, erase then write\n");
memset(buf, 0xFF, sizeof(buf));
p = (uint16_t *)(FLASH_BASEADDR + sector * 4096);
end = p + 2048;
while (p < end) {
uint16_t val = *p++;
buf[val & 255] = val >> 8;
}
buf[offset] = data;
p = (uint16_t *)(FLASH_BASEADDR + sector * 4096);
flash_erase_sector(p);
index = 0;
for (i=0; i < 256; i++) {
if (buf[i] != 0xFF) {
// TODO: combining these to larger write
// would (probably) be more efficient
uint16_t newval = i | (buf[i] << 8);
flash_write(p + index, &newval, 2);
index = index + 1;
}
}
sector_index[sector] = index;
}
}

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++);
}
}





#define LUT0(opcode, pads, operand) (FLEXSPI_LUT_INSTRUCTION((opcode), (pads), (operand)))
#define LUT1(opcode, pads, operand) (FLEXSPI_LUT_INSTRUCTION((opcode), (pads), (operand)) << 16)
#define CMD_SDR FLEXSPI_LUT_OPCODE_CMD_SDR
#define ADDR_SDR FLEXSPI_LUT_OPCODE_RADDR_SDR
#define READ_SDR FLEXSPI_LUT_OPCODE_READ_SDR
#define WRITE_SDR FLEXSPI_LUT_OPCODE_WRITE_SDR
#define PINS1 FLEXSPI_LUT_NUM_PADS_1
#define PINS4 FLEXSPI_LUT_NUM_PADS_4

static void flash_wait()
{
FLEXSPI_LUT60 = LUT0(CMD_SDR, PINS1, 0x05) | LUT1(READ_SDR, PINS1, 1); // 05 = read status
FLEXSPI_LUT61 = 0;
uint8_t status;
do {
FLEXSPI_IPRXFCR = FLEXSPI_IPRXFCR_CLRIPRXF; // clear rx fifo
FLEXSPI_IPCR0 = 0;
FLEXSPI_IPCR1 = FLEXSPI_IPCR1_ISEQID(15) | FLEXSPI_IPCR1_IDATSZ(1);
FLEXSPI_IPCMD = FLEXSPI_IPCMD_TRG;
while (!(FLEXSPI_INTR & FLEXSPI_INTR_IPCMDDONE)) {
asm("nop");
}
FLEXSPI_INTR = FLEXSPI_INTR_IPCMDDONE;
status = *(uint8_t *)&FLEXSPI_RFDR0;
} while (status & 1);
FLEXSPI_MCR0 |= FLEXSPI_MCR0_SWRESET; // purge stale data from FlexSPI's AHB FIFO
while (FLEXSPI_MCR0 & FLEXSPI_MCR0_SWRESET) ; // wait
__enable_irq();
}

// write bytes into flash memory (which is already erased to 0xFF)
static void flash_write(void *addr, const void *data, uint32_t len)
{
__disable_irq();
FLEXSPI_LUTKEY = FLEXSPI_LUTKEY_VALUE;
FLEXSPI_LUTCR = FLEXSPI_LUTCR_UNLOCK;
FLEXSPI_IPCR0 = 0;
FLEXSPI_LUT60 = LUT0(CMD_SDR, PINS1, 0x06); // 06 = write enable
FLEXSPI_IPCR1 = FLEXSPI_IPCR1_ISEQID(15);
FLEXSPI_IPCMD = FLEXSPI_IPCMD_TRG;
arm_dcache_delete(addr, len); // purge old data from ARM's cache
while (!(FLEXSPI_INTR & FLEXSPI_INTR_IPCMDDONE)) ; // wait
FLEXSPI_INTR = FLEXSPI_INTR_IPCMDDONE;
FLEXSPI_LUT60 = LUT0(CMD_SDR, PINS1, 0x32) | LUT1(ADDR_SDR, PINS1, 24); // 32 = quad write
FLEXSPI_LUT61 = LUT0(WRITE_SDR, PINS4, 1);
FLEXSPI_IPTXFCR = FLEXSPI_IPTXFCR_CLRIPTXF; // clear tx fifo
FLEXSPI_IPCR0 = (uint32_t)addr & 0x001FFFFF;
FLEXSPI_IPCR1 = FLEXSPI_IPCR1_ISEQID(15) | FLEXSPI_IPCR1_IDATSZ(len);
FLEXSPI_IPCMD = FLEXSPI_IPCMD_TRG;
const uint8_t *src = (const uint8_t *)data;
uint32_t n;
while (!((n = FLEXSPI_INTR) & FLEXSPI_INTR_IPCMDDONE)) {
if (n & FLEXSPI_INTR_IPTXWE) {
uint32_t wrlen = len;
if (wrlen > 8) wrlen = 8;
if (wrlen > 0) {
memcpy((void *)&FLEXSPI_TFDR0, src, wrlen);
src += wrlen;
len -= wrlen;
}
FLEXSPI_INTR = FLEXSPI_INTR_IPTXWE;
}
}
FLEXSPI_INTR = FLEXSPI_INTR_IPCMDDONE | FLEXSPI_INTR_IPTXWE;
flash_wait();
}

// erase a 4K sector
static void flash_erase_sector(void *addr)
{
__disable_irq();
FLEXSPI_LUTKEY = FLEXSPI_LUTKEY_VALUE;
FLEXSPI_LUTCR = FLEXSPI_LUTCR_UNLOCK;
FLEXSPI_LUT60 = LUT0(CMD_SDR, PINS1, 0x06); // 06 = write enable
FLEXSPI_IPCR0 = 0;
FLEXSPI_IPCR1 = FLEXSPI_IPCR1_ISEQID(15);
FLEXSPI_IPCMD = FLEXSPI_IPCMD_TRG;
arm_dcache_delete((void *)((uint32_t)addr & 0xFFFFF000), 4096); // purge data from cache
while (!(FLEXSPI_INTR & FLEXSPI_INTR_IPCMDDONE)) ; // wait
FLEXSPI_INTR = FLEXSPI_INTR_IPCMDDONE;
FLEXSPI_LUT60 = LUT0(CMD_SDR, PINS1, 0x20) | LUT1(ADDR_SDR, PINS1, 24); // 20 = sector erase
FLEXSPI_LUT61 = 0;
FLEXSPI_IPCR0 = (uint32_t)addr & 0x001FF000;
FLEXSPI_IPCR1 = FLEXSPI_IPCR1_ISEQID(15);
FLEXSPI_IPCMD = FLEXSPI_IPCMD_TRG;
while (!(FLEXSPI_INTR & FLEXSPI_INTR_IPCMDDONE)) ; // wait
FLEXSPI_INTR = FLEXSPI_INTR_IPCMDDONE;
flash_wait();
}

0 comments on commit 5179c6b

Please sign in to comment.
You can’t perform that action at this time.