From 9b41d9ac5e50e7726010120fe9c265a8df1d7eea Mon Sep 17 00:00:00 2001 From: Mike Nix Date: Fri, 24 Apr 2020 00:15:21 +0800 Subject: [PATCH] XMC flash support - WIP (#6725) * Move the spi vendor list from Esp.h to its own header in eboot. * Fix ifdef issue with spi_vendors.h * Add initFlashQuirks() for any chip specific flash initialization. Called from user_init(). * namespace experimental for initFlashQuirks() * Slow down flash access during eboot firmware copy Part 1 - still some work to do * Slow down flash access during eboot firmware copy on XMC chips Part 2 - Identify the chip type. Note: there may still be issues with the access speed change. This is very much experimental. * Commit eboot.elf Co-authored-by: Develo Co-authored-by: Earle F. Philhower, III --- bootloaders/eboot/eboot.c | 62 ++++++++++++++ bootloaders/eboot/eboot.elf | Bin 34956 -> 36640 bytes bootloaders/eboot/spi_vendors.h | 63 +++++++++++++++ cores/esp8266/Esp.cpp | 6 ++ cores/esp8266/Esp.h | 38 +-------- cores/esp8266/core_esp8266_flash_quirks.cpp | 85 ++++++++++++++++++++ cores/esp8266/core_esp8266_main.cpp | 3 + cores/esp8266/flash_quirks.h | 42 ++++++++++ cores/esp8266/spi_flash_defs.h | 44 ++++++++++ cores/esp8266/spi_vendors.h | 40 +++++++++ 10 files changed, 346 insertions(+), 37 deletions(-) create mode 100644 bootloaders/eboot/spi_vendors.h create mode 100644 cores/esp8266/core_esp8266_flash_quirks.cpp create mode 100644 cores/esp8266/flash_quirks.h create mode 100644 cores/esp8266/spi_flash_defs.h create mode 100644 cores/esp8266/spi_vendors.h diff --git a/bootloaders/eboot/eboot.c b/bootloaders/eboot/eboot.c index c7cf285c0..39b14307a 100644 --- a/bootloaders/eboot/eboot.c +++ b/bootloaders/eboot/eboot.c @@ -12,6 +12,7 @@ #include #include "flash.h" #include "eboot_command.h" +#include "spi_vendors.h" #include extern unsigned char _gzip_dict; @@ -189,6 +190,28 @@ int copy_raw(const uint32_t src_addr, return 0; } +#define XMC_SUPPORT +#ifdef XMC_SUPPORT +// Define a few SPI0 registers we need access to +#define ESP8266_REG(addr) *((volatile uint32_t *)(0x60000000+(addr))) +#define SPI0CMD ESP8266_REG(0x200) +#define SPI0CLK ESP8266_REG(0x218) +#define SPI0C ESP8266_REG(0x208) +#define SPI0W0 ESP8266_REG(0x240) + +#define SPICMDRDID (1 << 28) + +/* spi_flash_get_id() + Returns the flash chip ID - same as the SDK function. + We need our own version as the SDK isn't available here. + */ +uint32_t __attribute__((noinline)) spi_flash_get_id() { + SPI0W0=0; + SPI0CMD=SPICMDRDID; + while (SPI0CMD) {} + return SPI0W0; +} +#endif // XMC_SUPPORT int main() { @@ -211,9 +234,48 @@ int main() if (cmd.action == ACTION_COPY_RAW) { ets_putc('c'); ets_putc('p'); ets_putc(':'); + +#ifdef XMC_SUPPORT + // save the flash access speed registers + uint32_t spi0clk = SPI0CLK; + uint32_t spi0c = SPI0C; + + uint32_t vendor = spi_flash_get_id() & 0x000000ff; + if (vendor == SPI_FLASH_VENDOR_XMC) { + uint32_t flashinfo=0; + if (SPIRead(0, &flashinfo, 4)) { + // failed to read the configured flash speed. + // Do not change anything, + } else { + // select an appropriate flash speed + // Register values are those used by ROM + switch ((flashinfo >> 24) & 0x0f) { + case 0x0: // 40MHz, slow to 20 + case 0x1: // 26MHz, slow to 20 + SPI0CLK = 0x00003043; + SPI0C = 0x00EAA313; + break; + case 0x2: // 20MHz, no change + break; + case 0xf: // 80MHz, slow to 26 + SPI0CLK = 0x00002002; + SPI0C = 0x00EAA202; + break; + default: + break; + } + } + } +#endif // XMC_SUPPORT ets_wdt_disable(); res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2]); ets_wdt_enable(); + +#ifdef XMC_SUPPORT + // restore the saved flash access speed registers + SPI0CLK = spi0clk; + SPI0C = spi0c; +#endif ets_putc('0'+res); ets_putc('\n'); if (res == 0) { cmd.action = ACTION_LOAD_APP; diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf index 0d862f6a9f6bf2b014610c46d4ba14d4eb088fc0..bbd4452c0e5b418cb3a065daba765d68d3e541f2 100755 GIT binary patch literal 36640 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 literal 34956 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 - -// Vendor IDs taken from Flashrom project -// https://review.coreboot.org/cgit/flashrom.git/tree/flashchips.h?h=1.0.x -typedef enum { - SPI_FLASH_VENDOR_ALLIANCE = 0x52, /* Alliance Semiconductor */ - SPI_FLASH_VENDOR_AMD = 0x01, /* AMD */ - SPI_FLASH_VENDOR_AMIC = 0x37, /* AMIC */ - SPI_FLASH_VENDOR_ATMEL = 0x1F, /* Atmel (now used by Adesto) */ - SPI_FLASH_VENDOR_BRIGHT = 0xAD, /* Bright Microelectronics */ - SPI_FLASH_VENDOR_CATALYST = 0x31, /* Catalyst */ - SPI_FLASH_VENDOR_EON = 0x1C, /* EON Silicon Devices, missing 0x7F prefix */ - SPI_FLASH_VENDOR_ESMT = 0x8C, /* Elite Semiconductor Memory Technology (ESMT) / EFST Elite Flash Storage */ - SPI_FLASH_VENDOR_EXCEL = 0x4A, /* ESI, missing 0x7F prefix */ - SPI_FLASH_VENDOR_FIDELIX = 0xF8, /* Fidelix */ - SPI_FLASH_VENDOR_FUJITSU = 0x04, /* Fujitsu */ - SPI_FLASH_VENDOR_GIGADEVICE = 0xC8, /* GigaDevice */ - SPI_FLASH_VENDOR_HYUNDAI = 0xAD, /* Hyundai */ - SPI_FLASH_VENDOR_INTEL = 0x89, /* Intel */ - SPI_FLASH_VENDOR_ISSI = 0xD5, /* ISSI Integrated Silicon Solutions, see also PMC. */ - SPI_FLASH_VENDOR_MACRONIX = 0xC2, /* Macronix (MX) */ - SPI_FLASH_VENDOR_NANTRONICS = 0xD5, /* Nantronics, missing prefix */ - SPI_FLASH_VENDOR_PMC = 0x9D, /* PMC, missing 0x7F prefix */ - SPI_FLASH_VENDOR_PUYA = 0x85, /* Puya semiconductor (shanghai) co. ltd */ - SPI_FLASH_VENDOR_SANYO = 0x62, /* Sanyo */ - SPI_FLASH_VENDOR_SHARP = 0xB0, /* Sharp */ - SPI_FLASH_VENDOR_SPANSION = 0x01, /* Spansion, same ID as AMD */ - SPI_FLASH_VENDOR_SST = 0xBF, /* SST */ - SPI_FLASH_VENDOR_ST = 0x20, /* ST / SGS/Thomson / Numonyx (later acquired by Micron) */ - SPI_FLASH_VENDOR_SYNCMOS_MVC = 0x40, /* SyncMOS (SM) and Mosel Vitelic Corporation (MVC) */ - SPI_FLASH_VENDOR_TENX = 0x5E, /* Tenx Technologies */ - SPI_FLASH_VENDOR_TI = 0x97, /* Texas Instruments */ - SPI_FLASH_VENDOR_TI_OLD = 0x01, /* TI chips from last century */ - SPI_FLASH_VENDOR_WINBOND = 0xDA, /* Winbond */ - SPI_FLASH_VENDOR_WINBOND_NEX = 0xEF, /* Winbond (ex Nexcom) serial flashes */ - - SPI_FLASH_VENDOR_UNKNOWN = 0xFF -} SPI_FLASH_VENDOR_t; +#include "spi_vendors.h" /** * AVR macros for WDT managment diff --git a/cores/esp8266/core_esp8266_flash_quirks.cpp b/cores/esp8266/core_esp8266_flash_quirks.cpp new file mode 100644 index 000000000..7128fcfe2 --- /dev/null +++ b/cores/esp8266/core_esp8266_flash_quirks.cpp @@ -0,0 +1,85 @@ +/* + flash_quirks.cpp - Chip specific flash init + Copyright (c) 2019 Mike Nix. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "spi_flash.h" + +#include "spi_utils.h" +#include "flash_quirks.h" + +#ifdef __cplusplus +extern "C" { +#endif + +namespace experimental { + +static int get_flash_mhz() { + // FIXME: copied from Esp.cpp - we really should define the magic values + uint32_t data; + uint8_t * bytes = (uint8_t *) &data; + // read first 4 byte (magic byte + flash config) + if(spi_flash_read(0x0000, &data, 4) == SPI_FLASH_RESULT_OK) { + switch (bytes[3] & 0x0F) { + case 0x0: // 40 MHz + return 40; + case 0x1: // 26 MHz + return 26; + case 0x2: // 20 MHz + return 20; + case 0xf: // 80 MHz + return 80; + default: // fail? + return 0; + } + } + return 0; +} + +/* initFlashQuirks() + * Do any chip-specific initialization to improve performance and reliability. + */ +void initFlashQuirks() { + using namespace experimental; + uint32_t vendor = spi_flash_get_id() & 0x000000ff; + + switch (vendor) { + case SPI_FLASH_VENDOR_XMC: + uint32_t SR3, newSR3; + if (SPI0Command(SPI_FLASH_CMD_RSR3, &SR3, 0, 8)==SPI_RESULT_OK) { // read SR3 + newSR3=SR3; + if (get_flash_mhz()>26) { // >26Mhz? + // Set the output drive to 100% + newSR3 &= ~(SPI_FLASH_SR3_XMC_DRV_MASK << SPI_FLASH_SR3_XMC_DRV_S); + newSR3 |= (SPI_FLASH_SR3_XMC_DRV_100 << SPI_FLASH_SR3_XMC_DRV_S); + } + if (newSR3 != SR3) { // only write if changed + if (SPI0Command(SPI_FLASH_CMD_WEVSR,NULL,0,0)==SPI_RESULT_OK) // write enable volatile SR + SPI0Command(SPI_FLASH_CMD_WSR3,&newSR3,8,0); // write to SR3 + SPI0Command(SPI_FLASH_CMD_WRDI,NULL,0,0); // write disable - probably not needed + } + } + } +} + +} // namespace experimental + +#ifdef __cplusplus +} +#endif diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 0ec4f1f3b..2d8436234 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -34,6 +34,7 @@ extern "C" { } #include #include "gdb_hooks.h" +#include "flash_quirks.h" #define LOOP_TASK_PRIORITY 1 #define LOOP_QUEUE_SIZE 1 @@ -334,6 +335,8 @@ extern "C" void user_init(void) { initVariant(); + experimental::initFlashQuirks(); // Chip specific flash init. + cont_init(g_pcont); preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable. diff --git a/cores/esp8266/flash_quirks.h b/cores/esp8266/flash_quirks.h new file mode 100644 index 000000000..0d05c6d3e --- /dev/null +++ b/cores/esp8266/flash_quirks.h @@ -0,0 +1,42 @@ +/* + flash_quirks.h + Copyright (c) 2019 Mike Nix. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FLASH_QUIRKS_H +#define FLASH_QUIRKS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "spi_vendors.h" +#include "spi_flash_defs.h" + +namespace experimental { + +void initFlashQuirks(); + +} // namespace experimental + +#ifdef __cplusplus +} +#endif + + +#endif // FLASH_QUIRKS_H diff --git a/cores/esp8266/spi_flash_defs.h b/cores/esp8266/spi_flash_defs.h new file mode 100644 index 000000000..b749fc4c2 --- /dev/null +++ b/cores/esp8266/spi_flash_defs.h @@ -0,0 +1,44 @@ +/* + spi_flash_defs.h - SPI Flash chip commands and status registers + Copyright (c) 2019 Mike Nix. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SPI_FLASH_DEFS_H +#define SPI_FLASH_DEFS_H + +// Flash chip Status Register 3: Vendor XMC Output Drive levels +#define SPI_FLASH_SR3_XMC_DRV_25 1 +#define SPI_FLASH_SR3_XMC_DRV_50 0 +#define SPI_FLASH_SR3_XMC_DRV_75 2 +#define SPI_FLASH_SR3_XMC_DRV_100 3 + +#define SPI_FLASH_SR3_XMC_DRV_S 5 +#define SPI_FLASH_SR3_XMC_DRV_MASK 0x03 + +// Flash Chip commands +#define SPI_FLASH_CMD_RSR1 0x05 //Read Flash Status Register... +#define SPI_FLASH_CMD_RSR2 0x35 +#define SPI_FLASH_CMD_RSR3 0x15 +#define SPI_FLASH_CMD_WSR1 0x01 //Write Flash Status Register... +#define SPI_FLASH_CMD_WSR2 0x31 +#define SPI_FLASH_CMD_WSR3 0x11 +#define SPI_FLASH_CMD_WEVSR 0x50 //Write Enable Volatile Status Registers +#define SPI_FLASH_CMD_WREN 0x06 //Write Enable +#define SPI_FLASH_CMD_WRDI 0x04 //Write Disable + +#endif // SPI_FLASH_DEFS_H diff --git a/cores/esp8266/spi_vendors.h b/cores/esp8266/spi_vendors.h new file mode 100644 index 000000000..b656f4cb5 --- /dev/null +++ b/cores/esp8266/spi_vendors.h @@ -0,0 +1,40 @@ +/* + spi_vendors.h - Vendor IDs for SPI chips + Copyright (c) 2019 Mike Nix. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SPI_VENDORS_H +#define SPI_VENDORS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Definitions are placed in eboot. Include them here rather than duplicate them. + * Also, prefer to have eboot standalone as much as possible and have the core depend on it + * rather than have eboot depend on the core. + */ +#include <../../bootloaders/eboot/spi_vendors.h> + + +#ifdef __cplusplus +} +#endif + + +#endif // SPI_VENDORS_H