|
@@ -20,11 +20,16 @@
|
|
|
#include "esp_loader.h"
|
|
#include "esp_loader.h"
|
|
|
#include "md5_hash.h"
|
|
#include "md5_hash.h"
|
|
|
#include <string.h>
|
|
#include <string.h>
|
|
|
|
|
+#include <assert.h>
|
|
|
|
|
|
|
|
#ifndef MAX
|
|
#ifndef MAX
|
|
|
#define MAX(a, b) ((a) > (b)) ? (a) : (b)
|
|
#define MAX(a, b) ((a) > (b)) ? (a) : (b)
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
|
|
+#ifndef MIN
|
|
|
|
|
+#define MIN(a, b) ((a) < (b)) ? (a) : (b)
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
typedef struct {
|
|
typedef struct {
|
|
|
uint32_t reg_1;
|
|
uint32_t reg_1;
|
|
|
uint32_t reg_2;
|
|
uint32_t reg_2;
|
|
@@ -51,9 +56,53 @@ static const uint32_t DEFAULT_FLASH_TIMEOUT = 3000; // timeout for most fl
|
|
|
static const uint32_t ERASE_REGION_TIMEOUT_PER_MB = 3000; // timeout (per megabyte) for erasing a region
|
|
static const uint32_t ERASE_REGION_TIMEOUT_PER_MB = 3000; // timeout (per megabyte) for erasing a region
|
|
|
static const uint8_t PADDING_PATTERN = 0xFF;
|
|
static const uint8_t PADDING_PATTERN = 0xFF;
|
|
|
|
|
|
|
|
-
|
|
|
|
|
static uint32_t s_flash_write_size = 0;
|
|
static uint32_t s_flash_write_size = 0;
|
|
|
|
|
|
|
|
|
|
+#define MEGABYTE 1024 * 1024
|
|
|
|
|
+
|
|
|
|
|
+size_t size_id_to_flash_size[] = {
|
|
|
|
|
+ MEGABYTE / 4, // 256KB,
|
|
|
|
|
+ MEGABYTE / 2, // 512KB,
|
|
|
|
|
+ 1 * MEGABYTE, // 1MB,
|
|
|
|
|
+ 2 * MEGABYTE, // 2MB,
|
|
|
|
|
+ 4 * MEGABYTE, // 4MB,
|
|
|
|
|
+ 8 * MEGABYTE, // 8MB,
|
|
|
|
|
+ 16 * MEGABYTE // 16MB
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+typedef enum {
|
|
|
|
|
+ SPI_FLASH_READ_ID = 0x9F
|
|
|
|
|
+} spi_flash_cmd_t;
|
|
|
|
|
+
|
|
|
|
|
+#if defined TARGET_ESP8266
|
|
|
|
|
+#define SPI_REG_BASE 0x60000200
|
|
|
|
|
+#define SPI_CMD_REG SPI_REG_BASE + 0x00
|
|
|
|
|
+#define SPI_USR_REG SPI_REG_BASE + 0x1c
|
|
|
|
|
+#define SPI_USR1_REG SPI_REG_BASE + 0x20
|
|
|
|
|
+#define SPI_USR2_REG SPI_REG_BASE + 0x24
|
|
|
|
|
+#define SPI_W0_REG SPI_REG_BASE + 0x40
|
|
|
|
|
+#define SPI_MOSI_DLEN_REG 0
|
|
|
|
|
+#define SPI_MISO_DLEN_REG 0
|
|
|
|
|
+#elif defined TARGET_ESP32_S2
|
|
|
|
|
+#define SPI_REG_BASE 0x3f402000
|
|
|
|
|
+#define SPI_CMD_REG SPI_REG_BASE + 0x00
|
|
|
|
|
+#define SPI_USR_REG SPI_REG_BASE + 0x18
|
|
|
|
|
+#define SPI_USR1_REG SPI_REG_BASE + 0x1c
|
|
|
|
|
+#define SPI_USR2_REG SPI_REG_BASE + 0x20
|
|
|
|
|
+#define SPI_W0_REG SPI_REG_BASE + 0x58
|
|
|
|
|
+#define SPI_MOSI_DLEN_REG SPI_REG_BASE + 0x24
|
|
|
|
|
+#define SPI_MISO_DLEN_REG SPI_REG_BASE + 0x28
|
|
|
|
|
+#elif defined TARGET_ESP32
|
|
|
|
|
+#define SPI_REG_BASE 0x60002000
|
|
|
|
|
+#define SPI_CMD_REG SPI_REG_BASE + 0x00
|
|
|
|
|
+#define SPI_USR_REG SPI_REG_BASE + 0x1c
|
|
|
|
|
+#define SPI_USR1_REG SPI_REG_BASE + 0x20
|
|
|
|
|
+#define SPI_USR2_REG SPI_REG_BASE + 0x24
|
|
|
|
|
+#define SPI_W0_REG SPI_REG_BASE + 0x80
|
|
|
|
|
+#define SPI_MOSI_DLEN_REG SPI_REG_BASE + 0x28
|
|
|
|
|
+#define SPI_MISO_DLEN_REG SPI_REG_BASE + 0x2c
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
#if MD5_ENABLED
|
|
#if MD5_ENABLED
|
|
|
|
|
|
|
|
static const uint32_t MD5_TIMEOUT_PER_MB = 800;
|
|
static const uint32_t MD5_TIMEOUT_PER_MB = 800;
|
|
@@ -138,17 +187,142 @@ esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args)
|
|
|
return err;
|
|
return err;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#ifndef TARGET_ESP8266
|
|
|
|
|
+
|
|
|
|
|
+static esp_loader_error_t spi_set_data_lengths(size_t mosi_bits, size_t miso_bits)
|
|
|
|
|
+{
|
|
|
|
|
+ if (mosi_bits > 0) {
|
|
|
|
|
+ RETURN_ON_ERROR( esp_loader_write_register(SPI_MOSI_DLEN_REG, mosi_bits - 1) );
|
|
|
|
|
+ }
|
|
|
|
|
+ if (miso_bits > 0) {
|
|
|
|
|
+ RETURN_ON_ERROR( esp_loader_write_register(SPI_MISO_DLEN_REG, miso_bits - 1) );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return ESP_LOADER_SUCCESS;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#else
|
|
|
|
|
+
|
|
|
|
|
+static esp_loader_error_t spi_set_data_lengths(size_t mosi_bits, size_t miso_bits)
|
|
|
|
|
+{
|
|
|
|
|
+ uint32_t mosi_bitlen_shift = 17;
|
|
|
|
|
+ uint32_t miso_bitlen_shift = 8;
|
|
|
|
|
+ uint32_t mosi_mask = (mosi_bits == 0) ? 0 : mosi_bits - 1;
|
|
|
|
|
+ uint32_t miso_mask = (miso_bits == 0) ? 0 : miso_bits - 1;
|
|
|
|
|
+ uint32_t usr_reg = (miso_mask << miso_bitlen_shift) | (mosi_mask << mosi_bitlen_shift);
|
|
|
|
|
+
|
|
|
|
|
+ return esp_loader_write_register(SPI_USR1_REG, usr_reg);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+static esp_loader_error_t spi_flash_command(spi_flash_cmd_t cmd, void *data_tx, size_t tx_size, void *data_rx, size_t rx_size)
|
|
|
|
|
+{
|
|
|
|
|
+ assert(rx_size <= 32); // Reading more than 32 bits back from a SPI flash operation is unsupported
|
|
|
|
|
+ assert(tx_size <= 64); // Writing more than 64 bytes of data with one SPI command is unsupported
|
|
|
|
|
+
|
|
|
|
|
+ uint32_t SPI_USR_CMD = (1 << 31);
|
|
|
|
|
+ uint32_t SPI_USR_MISO = (1 << 28);
|
|
|
|
|
+ uint32_t SPI_USR_MOSI = (1 << 27);
|
|
|
|
|
+ uint32_t SPI_CMD_USR = (1 << 18);
|
|
|
|
|
+ uint32_t CMD_LEN_SHIFT = 28;
|
|
|
|
|
+
|
|
|
|
|
+ // Save SPI configuration
|
|
|
|
|
+ uint32_t old_spi_usr;
|
|
|
|
|
+ uint32_t old_spi_usr2;
|
|
|
|
|
+ RETURN_ON_ERROR( esp_loader_read_register(SPI_USR_REG, &old_spi_usr) );
|
|
|
|
|
+ RETURN_ON_ERROR( esp_loader_read_register(SPI_USR2_REG, &old_spi_usr2) );
|
|
|
|
|
+
|
|
|
|
|
+ RETURN_ON_ERROR( spi_set_data_lengths(tx_size, rx_size) );
|
|
|
|
|
+
|
|
|
|
|
+ uint32_t usr_reg_2 = (7 << CMD_LEN_SHIFT) | cmd;
|
|
|
|
|
+ uint32_t usr_reg = SPI_USR_CMD;
|
|
|
|
|
+ if (rx_size > 0) {
|
|
|
|
|
+ usr_reg |= SPI_USR_MISO;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (tx_size > 0) {
|
|
|
|
|
+ usr_reg |= SPI_USR_MOSI;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ RETURN_ON_ERROR( esp_loader_write_register(SPI_USR_REG, usr_reg) );
|
|
|
|
|
+ RETURN_ON_ERROR( esp_loader_write_register(SPI_USR2_REG, usr_reg_2 ) );
|
|
|
|
|
+
|
|
|
|
|
+ if (tx_size == 0) {
|
|
|
|
|
+ // clear data register before we read it
|
|
|
|
|
+ RETURN_ON_ERROR( esp_loader_write_register(SPI_W0_REG, 0) );
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uint32_t *data = (uint32_t *)data_tx;
|
|
|
|
|
+ uint32_t words_to_write = MIN((tx_size + 31) / 8 * 4, 1);
|
|
|
|
|
+ uint32_t data_reg_addr = SPI_W0_REG;
|
|
|
|
|
+
|
|
|
|
|
+ while (words_to_write--) {
|
|
|
|
|
+ uint32_t word = *data++;
|
|
|
|
|
+ RETURN_ON_ERROR( esp_loader_write_register(data_reg_addr, word) );
|
|
|
|
|
+ data_reg_addr += 4;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ RETURN_ON_ERROR( esp_loader_write_register(SPI_CMD_REG, SPI_CMD_USR) );
|
|
|
|
|
+
|
|
|
|
|
+ uint32_t trials = 10;
|
|
|
|
|
+ while (trials--) {
|
|
|
|
|
+ uint32_t cmd_reg;
|
|
|
|
|
+ RETURN_ON_ERROR( esp_loader_read_register(SPI_CMD_REG, &cmd_reg) );
|
|
|
|
|
+ if ((cmd_reg & SPI_CMD_USR) == 0) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (trials == 0) {
|
|
|
|
|
+ return ESP_LOADER_ERROR_TIMEOUT;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ RETURN_ON_ERROR( esp_loader_read_register(SPI_W0_REG, data_rx) );
|
|
|
|
|
+
|
|
|
|
|
+ // Restore SPI configuration
|
|
|
|
|
+ RETURN_ON_ERROR( esp_loader_write_register(SPI_USR_REG, old_spi_usr) );
|
|
|
|
|
+ RETURN_ON_ERROR( esp_loader_write_register(SPI_USR2_REG, old_spi_usr2) );
|
|
|
|
|
+
|
|
|
|
|
+ return ESP_LOADER_SUCCESS;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static esp_loader_error_t detect_flash_size(size_t *flash_size)
|
|
|
|
|
+{
|
|
|
|
|
+ uint32_t flash_id = 0;
|
|
|
|
|
+
|
|
|
|
|
+ RETURN_ON_ERROR( spi_flash_command(SPI_FLASH_READ_ID, NULL, 0, &flash_id, 24) );
|
|
|
|
|
+ uint32_t size_id = flash_id >> 16;
|
|
|
|
|
+
|
|
|
|
|
+ if (size_id < 0x12 && size_id > 0x18) {
|
|
|
|
|
+ return ESP_LOADER_ERROR_UNSUPPORTED_CHIP;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ *flash_size = size_id_to_flash_size[size_id - 0x12];
|
|
|
|
|
+
|
|
|
|
|
+ return ESP_LOADER_SUCCESS;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
|
|
|
esp_loader_error_t esp_loader_flash_start(uint32_t offset, uint32_t image_size, uint32_t block_size)
|
|
esp_loader_error_t esp_loader_flash_start(uint32_t offset, uint32_t image_size, uint32_t block_size)
|
|
|
{
|
|
{
|
|
|
uint32_t blocks_to_write = (image_size + block_size - 1) / block_size;
|
|
uint32_t blocks_to_write = (image_size + block_size - 1) / block_size;
|
|
|
uint32_t erase_size = block_size * blocks_to_write;
|
|
uint32_t erase_size = block_size * blocks_to_write;
|
|
|
s_flash_write_size = block_size;
|
|
s_flash_write_size = block_size;
|
|
|
|
|
+ size_t flash_size;
|
|
|
|
|
+
|
|
|
|
|
+ if (detect_flash_size(&flash_size) == ESP_LOADER_SUCCESS) {
|
|
|
|
|
+ if (image_size > flash_size) {
|
|
|
|
|
+ return ESP_LOADER_ERROR_IMAGE_SIZE;
|
|
|
|
|
+ }
|
|
|
|
|
+ loader_port_start_timer(DEFAULT_TIMEOUT);
|
|
|
|
|
+ RETURN_ON_ERROR( loader_spi_parameters(flash_size) );
|
|
|
|
|
+ } else {
|
|
|
|
|
+ loader_port_debug_print("Flash size detection failed, falling back to default");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
init_md5(offset, image_size);
|
|
init_md5(offset, image_size);
|
|
|
|
|
|
|
|
loader_port_start_timer(timeout_per_mb(erase_size, ERASE_REGION_TIMEOUT_PER_MB));
|
|
loader_port_start_timer(timeout_per_mb(erase_size, ERASE_REGION_TIMEOUT_PER_MB));
|
|
|
-
|
|
|
|
|
return loader_flash_begin_cmd(offset, erase_size, block_size, blocks_to_write);
|
|
return loader_flash_begin_cmd(offset, erase_size, block_size, blocks_to_write);
|
|
|
}
|
|
}
|
|
|
|
|
|