diff --git a/components/bootloader_support/include_bootloader/bootloader_flash_priv.h b/components/bootloader_support/include_bootloader/bootloader_flash_priv.h index d898cae124..b83e945ca0 100644 --- a/components/bootloader_support/include_bootloader/bootloader_flash_priv.h +++ b/components/bootloader_support/include_bootloader/bootloader_flash_priv.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -24,10 +24,12 @@ #define CMD_RDID 0x9F #define CMD_WRSR 0x01 #define CMD_WRSR2 0x31 /* Not all SPI flash uses this command */ +#define CMD_WRSR3 0x11 /* 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 */ +#define CMD_RDSR3 0x15 /* Not all SPI flash uses this command */ #define CMD_OTPEN 0x3A /* Enable OTP mode, not all SPI flash uses this command */ #define CMD_RDSFDP 0x5A /* Read the SFDP of the flash */ #define CMD_WRAP 0x77 /* Set burst with wrap command */ diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c index 647e30d921..d5c902186e 100644 --- a/components/bootloader_support/src/bootloader_flash.c +++ b/components/bootloader_support/src/bootloader_flash.c @@ -573,7 +573,7 @@ esp_err_t IRAM_ATTR __attribute__((weak)) bootloader_flash_unlock(void) #ifndef g_rom_spiflash_dummy_len_plus // ESP32-C3 uses a macro to access ROM data here extern uint8_t g_rom_spiflash_dummy_len_plus[]; #endif -IRAM_ATTR static uint32_t bootloader_flash_execute_command_common( +IRAM_ATTR uint32_t bootloader_flash_execute_command_common( uint8_t command, uint32_t addr_len, uint32_t address, uint8_t dummy_len, diff --git a/components/bootloader_support/src/flash_qio_mode.c b/components/bootloader_support/src/flash_qio_mode.c index 3d5eff6b2c..ad84e92d07 100644 --- a/components/bootloader_support/src/flash_qio_mode.c +++ b/components/bootloader_support/src/flash_qio_mode.c @@ -168,7 +168,12 @@ unsigned bootloader_read_status_8b_rdsr2(void) return bootloader_execute_flash_command(CMD_RDSR2, 0, 0, 8); } -unsigned bootloader_read_status_16b_rdsr_rdsr2(void) +unsigned bootloader_read_status_8b_rdsr3(void) +{ + return bootloader_execute_flash_command(CMD_RDSR3, 0, 0, 8); +} + +static unsigned read_status_16b_rdsr_rdsr2(void) { return bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8) | (bootloader_execute_flash_command(CMD_RDSR2, 0, 0, 8) << 8); } @@ -183,6 +188,11 @@ void bootloader_write_status_8b_wrsr2(unsigned new_status) bootloader_execute_flash_command(CMD_WRSR2, new_status, 8, 0); } +void bootloader_write_status_8b_wrsr3(unsigned new_status) +{ + bootloader_execute_flash_command(CMD_WRSR3, new_status, 8, 0); +} + void bootloader_write_status_16b_wrsr(unsigned new_status) { bootloader_execute_flash_command(CMD_WRSR, new_status, 16, 0); diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index b5f0b3748f..6d4099fb7f 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -22,7 +22,8 @@ else() if(CONFIG_IDF_TARGET_ESP32S3) list(APPEND srcs "esp32s3/spi_timing_config.c" - "spi_flash_timing_tuning.c") + "spi_flash_timing_tuning.c" + "spi_flash_hpm_enable.c") endif() # New implementation after IDF v4.0 diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index e24c852f1d..637fec94cc 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -187,9 +187,13 @@ esp_err_t IRAM_ATTR spi_flash_init_chip_state(void) #if CONFIG_ESPTOOLPY_OCT_FLASH return esp_opiflash_init(rom_spiflash_legacy_data->chip.device_id); #else - //currently we don't need other setup for initialising Quad Flash +#if CONFIG_IDF_TARGET_ESP32S3 + // Currently, only esp32s3 allows high performance mode. + return spi_flash_enable_high_performance_mode(); +#else return ESP_OK; -#endif +#endif // CONFIG_IDF_TARGET_ESP32S3 +#endif // CONFIG_ESPTOOLPY_OCT_FLASH } void spi_flash_init(void) diff --git a/components/spi_flash/include/esp_private/spi_flash_os.h b/components/spi_flash/include/esp_private/spi_flash_os.h index 996606dbce..ed8adba74d 100644 --- a/components/spi_flash/include/esp_private/spi_flash_os.h +++ b/components/spi_flash/include/esp_private/spi_flash_os.h @@ -35,6 +35,7 @@ #include "esp_flash.h" #include "hal/spi_flash_hal.h" #include "soc/soc_caps.h" +#include "spi_flash_override.h" #ifdef __cplusplus extern "C" { @@ -138,6 +139,21 @@ bool spi_timing_is_tuned(void); */ void spi_flash_set_vendor_required_regs(void); +/** + * @brief Enable SPI flash high performance mode. + * + * @return ESP_OK if success. + */ +esp_err_t spi_flash_enable_high_performance_mode(void); + +/** + * @brief Get the flash dummy through this function + * This can be used when one flash has several dummy configurations to enable the high performance mode. + * @note Don't forget to subtract one when assign to the register of mspi e.g. if the value you get is 4, (4-1=3) should be assigned to the register. + * + * @return Pointer to bootlaoder_flash_dummy_conf_t. + */ +const spi_flash_hpm_dummy_conf_t *spi_flash_get_dummy(void); #ifdef __cplusplus } diff --git a/components/spi_flash/include/spi_flash/spi_flash_defs.h b/components/spi_flash/include/spi_flash/spi_flash_defs.h index 1ff0bfdea5..2f4984207d 100644 --- a/components/spi_flash/include/spi_flash/spi_flash_defs.h +++ b/components/spi_flash/include/spi_flash/spi_flash_defs.h @@ -1,16 +1,8 @@ -// Copyright 2015-2019 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. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once @@ -52,6 +44,7 @@ #define CMD_PROGRAM_PAGE_4B 0x12 #define CMD_SUSPEND 0x75 #define CMD_RESUME 0x7A +#define CMD_HPMEN 0xA3 /* Enable High Performance mode on flash */ #define CMD_RST_EN 0x66 #define CMD_RST_DEV 0x99 diff --git a/components/spi_flash/include/spi_flash_override.h b/components/spi_flash/include/spi_flash_override.h new file mode 100644 index 0000000000..1ca92d5cdf --- /dev/null +++ b/components/spi_flash/include/spi_flash_override.h @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" +#include "esp_err.h" + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Structure for flash dummy bits. + * For some flash chips, dummy bits are configurable under different conditions. + */ +typedef struct { + uint8_t dio_dummy; + uint8_t dout_dummy; + uint8_t qio_dummy; + uint8_t qout_dummy; + uint8_t fastrd_dummy; +} spi_flash_hpm_dummy_conf_t; + +typedef enum { + SPI_FLASH_HPM_NEEDED, // Means that in the certain condition, flash needs to enter the high performance mode. + SPI_FLASH_HPM_UNNEEDED, // Means that flash doesn't need to enter the high performance mode. + SPI_FLASH_HPM_BEYOND_LIMIT, // Means that flash has no capability to meet that condition. +} spi_flash_requirement_t; + +typedef void (*spi_flash_hpm_enable_fn_t)(void); +typedef esp_err_t (*spi_flash_hpf_check_fn_t)(void); +typedef void (*spi_flash_get_chip_dummy_fn_t)(spi_flash_hpm_dummy_conf_t *dummy_conf); +typedef esp_err_t (*spi_flash_hpm_probe_fn_t)(uint32_t flash_id); +typedef spi_flash_requirement_t (*spi_flash_hpm_chip_requirement_check_t)(uint32_t flash_id, uint32_t freq_mhz, int voltage_mv, int temperature); + +typedef struct __attribute__((packed)) +{ + const char *manufacturer; /* Flash vendor */ + spi_flash_hpm_probe_fn_t probe; + spi_flash_hpm_chip_requirement_check_t chip_hpm_requirement_check; + spi_flash_hpm_enable_fn_t flash_hpm_enable; + spi_flash_hpf_check_fn_t flash_hpf_check; + spi_flash_get_chip_dummy_fn_t flash_get_dummy; +} spi_flash_hpm_info_t; + +/** + * Array of known flash chips and method to enable flash high performance mode. + * + * Users can override this array. + */ +extern const spi_flash_hpm_info_t __attribute__((weak)) spi_flash_hpm_enable_list[]; + +#ifdef __cplusplus +} +#endif diff --git a/components/spi_flash/linker.lf b/components/spi_flash/linker.lf index fdba7df429..501e1fd415 100644 --- a/components/spi_flash/linker.lf +++ b/components/spi_flash/linker.lf @@ -14,9 +14,8 @@ entries: if IDF_TARGET_ESP32S3 = y: spi_flash_timing_tuning (noflash) spi_timing_config (noflash) + spi_flash_chip_mxic_opi (noflash) + spi_flash_hpm_enable (noflash) if IDF_TARGET_ESP32S3 = y && ESPTOOLPY_OCT_FLASH = y: spi_flash_oct_flash_init (noflash) - - if IDF_TARGET_ESP32S3 = y : - spi_flash_chip_mxic_opi (noflash) diff --git a/components/spi_flash/spi_flash_hpm_enable.c b/components/spi_flash/spi_flash_hpm_enable.c new file mode 100644 index 0000000000..7805f990e8 --- /dev/null +++ b/components/spi_flash/spi_flash_hpm_enable.c @@ -0,0 +1,267 @@ +/* + * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" +#include "esp_err.h" +#include "esp_log.h" +#include "spi_flash_defs.h" +#include "esp_rom_sys.h" +#include "esp32s3/rom/spi_flash.h" +#include "spi_flash_override.h" + +// TODO: These dependencies will be removed after remove bootloader_flash to G0.IDF-4609 +#include "../bootloader_support/include_bootloader/bootloader_flash_priv.h" + +/******************************************************************************* + * Flash high speed performance mode. + * HPM: High performance mode. + * HPF: High performance flag. + * + * Different flash chips might have different high performance strategy. + * 1. Some flash chips send A3H to enable the HPM. + * 2. Some flash chips write HPF bit in status register. + * 3. Some flash chips adjust dummy cycles. + ******************************************************************************/ + +#if CONFIG_ESPTOOLPY_FLASHFREQ_120M +#define FLASH_FREQUENCY 120 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_80M +#define FLASH_FREQUENCY 80 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M +#define FLASH_FREQUENCY 40 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_20M +#define FLASH_FREQUENCY 20 +#endif + +const static char *HPM_TAG = "flash HPM"; + +// TODO: This function will be changed after remove bootloader_flash to G0.IDF-4609 +extern uint32_t bootloader_flash_execute_command_common( + uint8_t command, + uint32_t addr_len, uint32_t address, + uint8_t dummy_len, + uint8_t mosi_len, uint32_t mosi_data, + uint8_t miso_len); +extern unsigned bootloader_read_status_8b_rdsr3(void); +extern void bootloader_write_status_8b_wrsr3(unsigned new_status); + +//-----------------For flash chips which enter HPM via command-----------------------// + +/** + * @brief Probe the chip whether use command to enable HPM mode. Take GD as an example: + * Some GD send 0xA3 command to enable HPM mode of the flash. + */ +static esp_err_t spi_flash_hpm_probe_chip_with_cmd(uint32_t flash_id) +{ + esp_err_t ret = ESP_OK; + switch (flash_id) { + /* The flash listed here should enter the HPM with command 0xA3 */ + case 0xC84016: + case 0xC84017: + break; + default: + ret = ESP_ERR_NOT_FOUND; + break; + } + return ret; +} + +static spi_flash_requirement_t spi_flash_hpm_chip_hpm_requirement_check_with_cmd(uint32_t flash_id, uint32_t freq_mhz, int voltage_mv, int temperautre) +{ + // voltage and temperature are not been used now, to be completed in the future. + (void)voltage_mv; + (void)temperautre; + spi_flash_requirement_t chip_cap = SPI_FLASH_HPM_UNNEEDED; + switch (flash_id) { + /* The flash listed here should enter the HPM with command 0xA3 */ + case 0xC84016: + case 0xC84017: + if (freq_mhz >= 80) { + chip_cap = SPI_FLASH_HPM_NEEDED; + } + break; + default: + chip_cap = SPI_FLASH_HPM_UNNEEDED; + break; + } + return chip_cap; +} + +/** + * @brief Send HPMEN command (A3H) + */ +static void spi_flash_enable_high_performance_send_cmd(void) +{ + uint32_t dummy = 24; + bootloader_flash_execute_command_common(CMD_HPMEN, 0, 0, dummy, 0, 0, 0); + // Delay for T(HPM) refering to datasheet. + esp_rom_delay_us(20); +} + +/** + * @brief Check whether flash HPM has been enabled. According to flash datasheets, majorities of + * HPF bit are at bit-5, sr-3. But some are not. Therefore, this function is only used for those + * HPF bit is at bit-5, sr-3. + */ +static esp_err_t spi_flash_high_performance_check_hpf_bit_5(void) +{ + if((bootloader_read_status_8b_rdsr3() & (1 << 4)) == 0) { + return ESP_FAIL; + } + return ESP_OK; +} + +//-----------------For flash chips which enter HPM via adjust dummy-----------------------// + +/** + * @brief Probe the chip whether adjust dummy to enable HPM mode. Take XMC as an example: + * Adjust dummy bits to enable HPM mode of the flash. If XMC works under 80MHz, the dummy bits + * might be 6, but when works under 120MHz, the dummy bits might be 10. + */ +static esp_err_t spi_flash_hpm_probe_chip_with_dummy(uint32_t flash_id) +{ + ESP_EARLY_LOGW(HPM_TAG, "Enter HPM by reconfiguring dummy has not been fully tested"); + esp_err_t ret = ESP_OK; + switch (flash_id) { + /* The flash listed here should enter the HPM by adjusting dummy cycles */ + case 0x204017: + break; + default: + ret = ESP_ERR_NOT_FOUND; + break; + } + return ret; +} + +static spi_flash_requirement_t spi_flash_hpm_chip_hpm_requirement_check_with_dummy(uint32_t flash_id, uint32_t freq_mhz, int voltage_mv, int temperautre) +{ + // voltage and temperature are not been used now, to be completed in the future. + (void)voltage_mv; + (void)temperautre; + spi_flash_requirement_t chip_cap = SPI_FLASH_HPM_UNNEEDED; + switch (flash_id) { + /* The flash listed here should enter the HPM with command 0xA3 */ + case 0x204017: + if (freq_mhz >= 104) { + chip_cap = SPI_FLASH_HPM_NEEDED; + } + break; + default: + chip_cap = SPI_FLASH_HPM_UNNEEDED; + break; + } + return chip_cap; +} + +/** + * @brief Adjust dummy cycles. This function modifies the Dummy Cycle Bits in SR3. + * Usually, the bits are at bit-0, bit-1, sr-3 and set DC[1:0]=[1,1]. + * + * @note Don't forget to adjust dummy configurations for MSPI, you can get the + * correct dummy from interface `spi_flash_hpm_get_dummy`. + */ +static void spi_flash_turn_high_performance_reconfig_dummy(void) +{ + uint8_t old_status_3 = bootloader_read_status_8b_rdsr3(); + uint8_t new_status = (old_status_3 | 0x03); + bootloader_execute_flash_command(CMD_WREN, 0, 0, 0); + bootloader_write_status_8b_wrsr3(new_status); + esp_rom_spiflash_wait_idle(&g_rom_flashchip); +} + +/** + * @brief Check whether HPM has been enabled. This function checks the DC bits + */ +static esp_err_t spi_flash_high_performance_check_dummy_sr(void) +{ + if((bootloader_read_status_8b_rdsr3() & 0x03) == 0) { + return ESP_FAIL; + } + return ESP_OK; +} + +static void spi_flash_hpm_get_dummy_xmc(spi_flash_hpm_dummy_conf_t *dummy_conf) +{ + dummy_conf->dio_dummy = 8; + dummy_conf->dout_dummy = 8; + dummy_conf->qio_dummy = 10; + dummy_conf->qout_dummy = 8; + dummy_conf->fastrd_dummy = 8; +} + + +//-----------------------generic functions-------------------------------------// + +/** + * @brief Default dummy for almost all flash chips. If your flash does't need to reconfigure dummy, + * just call this function. + */ +void __attribute__((weak)) spi_flash_hpm_get_dummy_generic(spi_flash_hpm_dummy_conf_t *dummy_conf) +{ + dummy_conf->dio_dummy = 4; + dummy_conf->dout_dummy = 8; + dummy_conf->qio_dummy = 6; + dummy_conf->qout_dummy = 8; + dummy_conf->fastrd_dummy = 8; +} + +const spi_flash_hpm_info_t __attribute__((weak)) spi_flash_hpm_enable_list[] = { + /* vendor, chip_id, freq_threshold, temperature threshold, operation for setting high performance, reading HPF status, get dummy */ + { "GD", spi_flash_hpm_probe_chip_with_cmd, spi_flash_hpm_chip_hpm_requirement_check_with_cmd, spi_flash_enable_high_performance_send_cmd, spi_flash_high_performance_check_hpf_bit_5, spi_flash_hpm_get_dummy_generic }, + { "XMC", spi_flash_hpm_probe_chip_with_dummy, spi_flash_hpm_chip_hpm_requirement_check_with_dummy, spi_flash_turn_high_performance_reconfig_dummy, spi_flash_high_performance_check_dummy_sr, spi_flash_hpm_get_dummy_xmc}, + // default: do nothing, but keep the dummy get function. The first item with NULL as its probe will be the fallback. + { "NULL", NULL, NULL, NULL, NULL, spi_flash_hpm_get_dummy_generic}, +}; + +static const spi_flash_hpm_info_t *chip_hpm = NULL; +static spi_flash_hpm_dummy_conf_t dummy_conf; + +esp_err_t spi_flash_enable_high_performance_mode(void) +{ + uint32_t flash_chip_id = g_rom_flashchip.device_id; + uint32_t flash_freq = FLASH_FREQUENCY; + // voltage and temperature has not been implemented, just leave an interface here. Complete in the future. + int voltage = 0; + int temperature = 0; + + const spi_flash_hpm_info_t *chip = spi_flash_hpm_enable_list; + esp_err_t ret = ESP_OK; + while (chip->probe) { + ret = chip->probe(flash_chip_id); + if (ret == ESP_OK) { + break; + } + chip++; + } + chip_hpm = chip; + + if (ret != ESP_OK) { + ESP_EARLY_LOGE(HPM_TAG, "Flash high performance mode hasn't been supported"); + return ret; + } + + if (chip_hpm->chip_hpm_requirement_check(flash_chip_id, flash_freq, voltage, temperature) == SPI_FLASH_HPM_NEEDED) { + ESP_EARLY_LOGI(HPM_TAG, "Enabling high speed mode for chip %s", chip_hpm->manufacturer); + chip_hpm->flash_hpm_enable(); + ESP_EARLY_LOGD(HPM_TAG, "Checking whether HPM has been executed"); + + if (chip_hpm->flash_hpf_check() != ESP_OK) { + ESP_EARLY_LOGE(HPM_TAG, "Flash high performance mode hasn't been executed successfully"); + return ESP_FAIL; + } + } else if (chip_hpm->chip_hpm_requirement_check(flash_chip_id, flash_freq, voltage, temperature) == SPI_FLASH_HPM_BEYOND_LIMIT) { + ESP_EARLY_LOGE(HPM_TAG, "Flash does not have the ability to raise to that frequency"); + return ESP_FAIL; + } + return ESP_OK; +} + +const spi_flash_hpm_dummy_conf_t *spi_flash_hpm_get_dummy(void) +{ + chip_hpm->flash_get_dummy(&dummy_conf); + return &dummy_conf; +}