/* 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 #include #include "debug/printf.h" #if defined(ARDUINO_TEENSY40) #define FLASH_BASEADDR 0x601F0000 #define FLASH_SECTORS 15 #elif defined(ARDUINO_TEENSY41) #define FLASH_BASEADDR 0x607C0000 #define FLASH_SECTORS 63 #elif defined(ARDUINO_TEENSY_MICROMOD) #define FLASH_BASEADDR 0x60FC0000 #define FLASH_SECTORS 63 #endif #if E2END > (255*FLASH_SECTORS-1) #error "E2END is set larger than the maximum possible EEPROM size" #endif // Conversation about how this code works & what the upper limits are // https://forum.pjrc.com/threads/57377?p=214566&viewfull=1#post214566 // To be called from LittleFS_Program, any other use at your own risk! void eepromemu_flash_write(void *addr, const void *data, uint32_t len); void eepromemu_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); eepromemu_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); eepromemu_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); eepromemu_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) void eepromemu_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_LUT61 = 0; FLEXSPI_LUT62 = 0; FLEXSPI_LUT63 = 0; 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 & 0x00FFFFFF; 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 void eepromemu_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_LUT61 = 0; FLEXSPI_LUT62 = 0; FLEXSPI_LUT63 = 0; 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_IPCR0 = (uint32_t)addr & 0x00FFF000; 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(); }