Bootloader: Support switching to Quad I/O mode during boot process

This commit is contained in:
Angus Gratton 2017-01-30 14:29:50 +11:00
parent 0a678ebe8c
commit 68cba2a1fb
8 changed files with 262 additions and 30 deletions

View file

@ -46,6 +46,7 @@
#include "bootloader_random.h"
#include "bootloader_config.h"
#include "rtc.h"
#include "flash_qio_mode.h"
extern int _bss_start;
extern int _bss_end;
@ -263,6 +264,10 @@ void bootloader_main()
ESP_LOGI(TAG, "Enabling RNG early entropy source...");
bootloader_random_enable();
#if CONFIG_FLASHMODE_QIO || CONFIG_FLASHMODE_QOUT
bootloader_enable_qio_mode();
#endif
if(esp_image_load_header(0x1000, true, &fhdr) != ESP_OK) {
ESP_LOGE(TAG, "failed to load bootloader header!");
return;
@ -632,28 +637,21 @@ void print_flash_info(const esp_image_header_t* phdr)
}
ESP_LOGI(TAG, "SPI Speed : %s", str );
switch ( phdr->spi_mode ) {
case ESP_IMAGE_SPI_MODE_QIO:
/* SPI mode could have been set to QIO during boot already,
so test the SPI registers not the flash header */
uint32_t spi_ctrl = REG_READ(SPI_CTRL_REG(0));
if (spi_ctrl & SPI_FREAD_QIO) {
str = "QIO";
break;
case ESP_IMAGE_SPI_MODE_QOUT:
} else if (spi_ctrl & SPI_FREAD_QUAD) {
str = "QOUT";
break;
case ESP_IMAGE_SPI_MODE_DIO:
} else if (spi_ctrl & SPI_FREAD_DIO) {
str = "DIO";
break;
case ESP_IMAGE_SPI_MODE_DOUT:
} else if (spi_ctrl & SPI_FREAD_DUAL) {
str = "DOUT";
break;
case ESP_IMAGE_SPI_MODE_FAST_READ:
} else if (spi_ctrl & SPI_FASTRD_MODE) {
str = "FAST READ";
break;
case ESP_IMAGE_SPI_MODE_SLOW_READ:
} else {
str = "SLOW READ";
break;
default:
str = "DIO";
break;
}
ESP_LOGI(TAG, "SPI Mode : %s", str );

View file

@ -8,6 +8,7 @@
LINKER_SCRIPTS := \
esp32.bootloader.ld \
$(IDF_PATH)/components/esp32/ld/esp32.rom.ld \
$(IDF_PATH)/components/esp32/ld/esp32.peripherals.ld \
esp32.bootloader.rom.ld
COMPONENT_ADD_LDFLAGS := -L $(COMPONENT_PATH) -lmain $(addprefix -T ,$(LINKER_SCRIPTS))

View file

@ -0,0 +1,187 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stddef.h>
#include <stdint.h>
#include "flash_qio_mode.h"
#include "esp_log.h"
#include "rom/spi_flash.h"
#include "soc/spi_struct.h"
#include "sdkconfig.h"
/* SPI flash controller */
#define SPIFLASH SPI1
/* SPI commands (actual on-wire commands not SPI controller bitmasks)
Suitable for use with the execute_flash_command static function.
*/
#define CMD_RDID 0x9F
#define CMD_WRSR 0x01
#define CMD_WRSR2 0x31 /* Not all SPI flash uses this command */
#define CMD_WREN 0x06
#define CMD_WRDI 0x04
#define CMD_RDSR 0x05
#define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */
static const char *TAG = "qio_mode";
typedef struct __attribute__((packed)) {
const char *manufacturer;
uint8_t mfg_id; /* 8-bit JEDEC manufacturer ID */
uint16_t flash_id; /* 16-bit JEDEC flash chip ID */
uint16_t id_mask; /* Bits to match on in flash chip ID */
uint8_t read_status_command;
uint8_t write_status_command;
uint8_t status_qio_bit; /* Currently assumes same bit for read/write status */
} qio_info_t;
/* Array of known flash chips and data to enable Quad I/O mode
Manufacturer & flash ID can be tested by running "esptool.py
flash_id"
If manufacturer ID matches, and flash ID ORed with flash ID mask
matches, enable_qio_mode() will execute "Read Cmd", test if bit
number "QIE Bit" is set, and if not set it will call "Write Cmd"
with this bit set.
Searching of this table stops when the first match is found.
(This table currently makes a lot of assumptions about how Quad I/O
mode is enabled, some flash chips in future may require more complex
handlers - for example a function pointer to a handler function.)
*/
const static qio_info_t chip_data[] = {
/* Manufacturer, mfg_id, flash_id, id mask, Read Cmd, Write Cmd, QIE Bit */
{ "MXIC", 0xC2, 0x2000, 0xFF00, CMD_RDSR, CMD_WRSR, 6 },
{ "ISSI", 0x9D, 0x4000, 0xFF00, CMD_RDSR, CMD_WRSR, 6 },
/* Final entry is default entry, if no other IDs have matched.
This approach works for chips including:
GigaDevice (mfg ID 0xC8, flash IDs including 4016),
FM25Q32 (mfg ID 0xA1, flash IDs including 4016)
*/
{ NULL, 0xFF, 0xFFFF, 0xFFFF, CMD_RDSR2, CMD_WRSR2, 1 }, /* Bit 9 of status register (second byte) */
};
#define NUM_CHIPS (sizeof(chip_data) / sizeof(qio_info_t))
static void enable_qio_mode(uint8_t read_status_command,
uint8_t write_status_command,
uint8_t status_qio_bit);
/* Generic function to use the "user command" SPI controller functionality
to send commands to the SPI flash and read the respopnse.
The command passed here is always the on-the-wire command given to the SPI flash unit.
*/
static uint32_t execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len);
void bootloader_enable_qio_mode(void)
{
uint32_t raw_flash_id;
uint8_t mfg_id;
uint16_t flash_id;
int i;
ESP_LOGD(TAG, "Probing for QIO mode enable...");
SPI_Wait_Idle(&g_rom_flashchip);
/* Set up some of the SPIFLASH user/ctrl variables which don't change
while we're probing using execute_flash_command() */
SPIFLASH.ctrl.val = 0;
SPIFLASH.user.usr_dummy = 0;
SPIFLASH.user.usr_addr = 0;
SPIFLASH.user.usr_command = 1;
SPIFLASH.user2.usr_command_bitlen = 7;
raw_flash_id = execute_flash_command(CMD_RDID, 0, 0, 24);
ESP_LOGD(TAG, "Raw SPI flash chip id 0x%x", raw_flash_id);
mfg_id = raw_flash_id & 0xFF;
flash_id = (raw_flash_id >> 16) | (raw_flash_id & 0xFF00);
ESP_LOGD(TAG, "Manufacturer ID 0x%02x chip ID 0x%04x", mfg_id, flash_id);
for (i = 0; i < NUM_CHIPS-1; i++) {
const qio_info_t *chip = &chip_data[i];
if (mfg_id == chip->mfg_id && (flash_id & chip->id_mask) == (chip->flash_id & chip->id_mask)) {
ESP_LOGI(TAG, "Enabling QIO for flash chip %s", chip_data[i].manufacturer);
break;
}
}
if (i == NUM_CHIPS - 1) {
ESP_LOGI(TAG, "Enabling default flash chip QIO");
}
enable_qio_mode(chip_data[i].read_status_command,
chip_data[i].write_status_command,
chip_data[i].status_qio_bit);
}
static void enable_qio_mode(uint8_t read_status_command,
uint8_t write_status_command,
uint8_t status_qio_bit)
{
uint32_t status_len = (status_qio_bit + 8) & ~7; /* 8, 16, 24 bit status values */
uint32_t status;
SPI_Wait_Idle(&g_rom_flashchip);
status = execute_flash_command(read_status_command, 0, 0, status_len);
ESP_LOGD(TAG, "Initial flash chip status 0x%x", status);
if ((status & (1<<status_qio_bit)) == 0) {
execute_flash_command(CMD_WREN, 0, 0, 0);
execute_flash_command(write_status_command, status | (1<<status_qio_bit), status_len, 0);
SPI_Wait_Idle(&g_rom_flashchip);
status = execute_flash_command(read_status_command, 0, 0, status_len);
ESP_LOGD(TAG, "Updated flash chip status 0x%x", status);
if ((status & (1<<status_qio_bit)) == 0) {
ESP_LOGE(TAG, "Failed to set QIE bit, not enabling QIO mode");
return;
}
} else {
ESP_LOGD(TAG, "QIO mode already enabled in flash");
}
ESP_LOGD(TAG, "Enabling QIO mode...");
SpiFlashRdMode mode;
#if CONFIG_FLASHMODE_QOUT
mode = SPI_FLASH_QOUT_MODE;
#else
mode = SPI_FLASH_QIO_MODE;
#endif
SPIMasterReadModeCnfig(mode);
}
static uint32_t execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len)
{
SPIFLASH.user2.usr_command_value = command;
SPIFLASH.user.usr_miso = miso_len > 0;
SPIFLASH.miso_dlen.usr_miso_dbitlen = miso_len ? (miso_len - 1) : 0;
SPIFLASH.user.usr_mosi = mosi_len > 0;
SPIFLASH.mosi_dlen.usr_mosi_dbitlen = mosi_len ? (mosi_len - 1) : 0;
SPIFLASH.data_buf[0] = mosi_data;
SPIFLASH.cmd.usr = 1;
while(SPIFLASH.cmd.usr != 0)
{ }
return SPIFLASH.data_buf[0];
}

View file

@ -0,0 +1,29 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Enable Quad I/O mode in bootloader (if configured)
*
* Queries attached SPI flash ID and sends correct SPI flash
* commands to enable QIO or QOUT mode, then enables this mode.
*/
void bootloader_enable_qio_mode(void);
#ifdef __cplusplus
}
#endif

View file

@ -504,12 +504,25 @@ void SPI_Write_Encrypt_Disable(void);
* @param uint32_t len : Length to write, should be 32 bytes aligned.
*
* @return SPI_FLASH_RESULT_OK : Data written successfully.
* SPI_FLASH_RESULT_ERR : Encrypto write error.
* SPI_FLASH_RESULT_ERR : Encryption write error.
* SPI_FLASH_RESULT_TIMEOUT : Encrypto write timeout.
*/
SpiFlashOpResult SPI_Encrypt_Write(uint32_t flash_addr, uint32_t *data, uint32_t len);
/** @brief Wait until SPI flash write operation is complete
*
* @note Please do not call this function in SDK.
*
* Reads the Write In Progress bit of the SPI flash status register,
* repeats until this bit is zero (indicating write complete).
*
* @return SPI_FLASH_RESULT_OK : Write is complete
* SPI_FLASH_RESULT_ERR : Error while reading status.
*/
SpiFlashOpResult SPI_Wait_Idle(SpiFlashChip *spi);
/** @brief Global SpiFlashChip structure used by ROM functions
*
*/

View file

@ -1563,6 +1563,8 @@ PROVIDE ( SPI_Write_Encrypt_Disable = 0x40062e60 );
PROVIDE ( SPI_Write_Encrypt_Enable = 0x40062df4 );
/* This is static function, but can be used, not generated by script*/
PROVIDE ( SPI_write_status = 0x400622f0 );
/* This is static function, but can be used, not generated by script */
PROVIDE ( SPI_Wait_Idle = 0x400622c0 );
PROVIDE ( srand = 0x40001004 );
PROVIDE ( __sread = 0x40001118 );
PROVIDE ( __srefill_r = 0x400593d4 );

View file

@ -46,30 +46,32 @@ config ESPTOOLPY_COMPRESSED
decompress it on the fly before flashing it. For most payloads, this should result in a
speed increase.
choice ESPTOOLPY_FLASHMODE
choice FLASHMODE
prompt "Flash SPI mode"
default ESPTOOLPY_FLASHMODE_DIO
default FLASHMODE_DIO
help
Mode the flash chip is flashed in, as well as the default mode for the
binary to run in.
config ESPTOOLPY_FLASHMODE_QIO
config FLASHMODE_QIO
bool "QIO"
config ESPTOOLPY_FLASHMODE_QOUT
config FLASHMODE_QOUT
bool "QOUT"
config ESPTOOLPY_FLASHMODE_DIO
config FLASHMODE_DIO
bool "DIO"
config ESPTOOLPY_FLASHMODE_DOUT
config FLASHMODE_DOUT
bool "DOUT"
endchoice
# Note: we use esptool.py to flash bootloader in
# dio mode for QIO/QOUT, bootloader then upgrades
# itself to quad mode during initialisation
config ESPTOOLPY_FLASHMODE
string
default "qio" if ESPTOOLPY_FLASHMODE_QIO
default "qout" if ESPTOOLPY_FLASHMODE_QOUT
default "dio" if ESPTOOLPY_FLASHMODE_DIO
default "dout" if ESPTOOLPY_FLASHMODE_DOUT
default "dio" if FLASHMODE_QIO
default "dio" if FLASHMODE_QOUT
default "dio" if FLASHMODE_DIO
default "dout" if FLASHMODE_DOUT
choice ESPTOOLPY_FLASHFREQ
prompt "Flash SPI speed"

View file

@ -148,9 +148,9 @@ function run_tests()
make
assert_rebuilt ${APP_BINS} ${BOOTLOADER_BINS}
print_status "Touching peripherals ld file should only re-link app"
print_status "Touching app-only ld file should only re-link app"
take_build_snapshot
touch ${IDF_PATH}/components/esp32/ld/esp32.peripherals.ld
touch ${IDF_PATH}/components/esp32/ld/esp32.common.ld
make
assert_rebuilt ${APP_BINS}
assert_not_rebuilt ${BOOTLOADER_BINS}