spi_flash: fixed issue that enabling HPM-DC by default may cause app unable to restart

This commit is contained in:
Xiao Xufeng
2023-06-24 20:38:53 +08:00
parent ca32e5268b
commit 1f5fb3f921
33 changed files with 461 additions and 225 deletions

View File

@@ -29,14 +29,28 @@
* 4. Some flash chips do nothing.
******************************************************************************/
#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
/*
* Note: This file should only be compiled when HPM_ON, which is only available when !CONFIG_ESPTOOLPY_OCT_FLASH.
* However when HPM_ON, there are still some cases this file is not actually used:
*
* - !CONFIG_SPI_FLASH_UNDER_HIGH_FREQ:
* It mean that the flash not running under frequency requires HPM. spi_flash_enable_high_performance_mode() still
* called because caller shouldn't take care of the frequency.
*
* - bootloader_flash_is_octal_mode_enabled() == true:
* This is possible when `CONFIG_ESPTOOLPY_FLASH_MODE_AUTO_DETECT` selected
*
* Octal Flash for now all support 120M. No need to enable HPM. The file is compiled, but will not actually run
* into spi_flash_enable_high_performance_mode().
*/
void spi_flash_hpm_get_dummy_generic(spi_flash_hpm_dummy_conf_t *dummy_conf);
#if CONFIG_SPI_FLASH_UNDER_HIGH_FREQ
#if CONFIG_SPI_FLASH_HPM_AUTO
// This only happens on S3, where HPM_AUTO leads to HPM_ON
#warning High Performance Mode (QSPI Flash > 80MHz) is optional feature that depends on flash model. Read Docs First!
#endif
const static char *HPM_TAG = "flash HPM";
@@ -120,6 +134,7 @@ static esp_err_t spi_flash_high_performance_check_hpf_bit_5(void)
//-----------------For flash chips which enter HPM via adjust dummy-----------------------//
#if CONFIG_SPI_FLASH_HPM_DC_ON
/**
* @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
@@ -203,6 +218,14 @@ static void spi_flash_hpm_get_dummy_xmc(spi_flash_hpm_dummy_conf_t *dummy_conf)
dummy_conf->qout_dummy = SPI_FLASH_QOUT_DUMMY_BITLEN;
dummy_conf->fastrd_dummy = SPI_FLASH_FASTRD_DUMMY_BITLEN;
}
#elif !CONFIG_SPI_FLASH_HPM_DC_DISABLE
//This is because bootloader doesn't support this
#warning HPM-DC, which helps to run some flash > 80MHz by adjusting dummy cycles, is no longer enabled by default.
#warning To enable this feature, your bootloader needs to have the support for it (by explicitly selecting BOOTLOADER_FLASH_DC_AWARE).
#warning If your bootloader does not support it, select SPI_FLASH_HPM_DC_DISABLE to suppress the warning. READ DOCS FIRST!
#endif //CONFIG_SPI_FLASH_HPM_DC_ON
//-----------------For flash chips which enter HPM via write status register-----------------------//
@@ -284,6 +307,127 @@ static spi_flash_requirement_t spi_flash_hpm_chip_hpm_requirement_check_with_doi
return chip_cap;
}
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 */
{ "command", 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 },
#if CONFIG_SPI_FLASH_HPM_DC_ON
{ "dummy", 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},
#endif //CONFIG_SPI_FLASH_HPM_DC_ON
{ "write sr3-bit5", spi_flash_hpm_probe_chip_with_write_hpf_bit_5, spi_flash_hpm_chip_hpm_requirement_check_with_write_hpf_bit_5, spi_flash_turn_high_performance_write_hpf_bit_5, spi_flash_high_performance_check_hpf_bit_5, spi_flash_hpm_get_dummy_generic},
{ "noting-to-do", spi_flash_hpm_probe_chip_with_doing_nothing, spi_flash_hpm_chip_hpm_requirement_check_with_doing_nothing, NULL, NULL, spi_flash_hpm_get_dummy_generic},
// 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;
#if CONFIG_SPI_FLASH_HPM_DC_ON
static bool s_hpm_dummy_changed = false;
static spi_flash_hpm_dummy_conf_t s_dummy_conf;
const spi_flash_hpm_dummy_conf_t *spi_flash_hpm_get_dummy(void)
{
chip_hpm->flash_get_dummy(&s_dummy_conf);
return &s_dummy_conf;
}
bool spi_flash_hpm_dummy_adjust(void)
{
return s_hpm_dummy_changed;
}
#endif //CONFIG_SPI_FLASH_HPM_DC_ON
#if CONFIG_ESPTOOLPY_FLASHFREQ_120M
#define FLASH_FREQUENCY 120
#endif
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;
spi_flash_requirement_t hpm_requirement_check;
// voltage and temperature has not been implemented, just leave an interface here. Complete in the future.
int voltage = 0;
int temperature = 0;
#if CONFIG_SPI_FLASH_HPM_AUTO
ESP_EARLY_LOGW(HPM_TAG, "HPM mode is optional feature that depends on flash model. Read Docs First!");
#endif
#if CONFIG_SPI_FLASH_HPM_DC_DISABLE
// case 1: force disabled
ESP_EARLY_LOGI(HPM_TAG, "w/o HPM-DC support");
#elif CONFIG_SPI_FLASH_HPM_DC_ON
// case 2: auto, and actually enabled
ESP_EARLY_LOGI(HPM_TAG, "with HPM-DC support");
#else
// case 3: auto, but disabled (not supported by bootloader)
ESP_EARLY_LOGW(HPM_TAG, "HPM mode with DC adjustment is disabled. Some flash models may not be supported. Read Docs First!");
#endif
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;
/* When > 80 MHz, flash chips usually need special HPM support to run normally. The support is chip-specific. When
* the chip is not in the known flash list, nothing will be done and there will be an warning.
* When <= 80 MHz, it's assumed that all flash chips can run without chip-specific HPM support. This function will not be called and there will be no warning.
*/
if (ret != ESP_OK) {
ESP_EARLY_LOGW(HPM_TAG, "High performance mode of this flash model hasn't been supported.");
return ret;
}
hpm_requirement_check = chip_hpm->chip_hpm_requirement_check(flash_chip_id, flash_freq, voltage, temperature);
if ((hpm_requirement_check == SPI_FLASH_HPM_CMD_NEEDED) || (hpm_requirement_check == SPI_FLASH_HPM_DUMMY_NEEDED) || (hpm_requirement_check == SPI_FLASH_HPM_WRITE_SR_NEEDED)) {
ESP_EARLY_LOGI(HPM_TAG, "Enabling flash high speed mode by %s", chip_hpm->method);
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;
}
#if CONFIG_SPI_FLASH_HPM_DC_ON
s_hpm_dummy_changed = (hpm_requirement_check == SPI_FLASH_HPM_DUMMY_NEEDED) ? true : false;
#else
assert(hpm_requirement_check != SPI_FLASH_HPM_DUMMY_NEEDED);
#endif
} else if (hpm_requirement_check == 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;
}
#else
//!CONFIG_SPI_FLASH_UNDER_HIGH_FREQ
static spi_flash_hpm_dummy_conf_t s_dummy_conf;
esp_err_t spi_flash_enable_high_performance_mode(void)
{
return ESP_OK;
}
const spi_flash_hpm_dummy_conf_t *spi_flash_hpm_get_dummy(void)
{
spi_flash_hpm_get_dummy_generic(&s_dummy_conf);
return &s_dummy_conf;
}
bool spi_flash_hpm_dummy_adjust(void)
{
return false;
}
#endif //CONFIG_SPI_FLASH_UNDER_HIGH_FREQ
//-----------------------generic functions-------------------------------------//
/**
@@ -298,73 +442,3 @@ void __attribute__((weak)) spi_flash_hpm_get_dummy_generic(spi_flash_hpm_dummy_c
dummy_conf->qout_dummy = SPI_FLASH_QOUT_DUMMY_BITLEN;
dummy_conf->fastrd_dummy = SPI_FLASH_FASTRD_DUMMY_BITLEN;
}
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 */
{ "command", 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 },
{ "dummy", 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},
{ "write sr3-bit5", spi_flash_hpm_probe_chip_with_write_hpf_bit_5, spi_flash_hpm_chip_hpm_requirement_check_with_write_hpf_bit_5, spi_flash_turn_high_performance_write_hpf_bit_5, spi_flash_high_performance_check_hpf_bit_5, spi_flash_hpm_get_dummy_generic},
{ "noting-to-do", spi_flash_hpm_probe_chip_with_doing_nothing, spi_flash_hpm_chip_hpm_requirement_check_with_doing_nothing, NULL, NULL, spi_flash_hpm_get_dummy_generic},
// 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;
static bool hpm_dummy_changed = false;
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;
spi_flash_requirement_t hpm_requirement_check;
// 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) {
#if (FLASH_FREQUENCY == 120)
ESP_EARLY_LOGW(HPM_TAG, "Flash high performance mode hasn't been supported");
#endif
return ret;
}
hpm_requirement_check = chip_hpm->chip_hpm_requirement_check(flash_chip_id, flash_freq, voltage, temperature);
if ((hpm_requirement_check == SPI_FLASH_HPM_CMD_NEEDED) || (hpm_requirement_check == SPI_FLASH_HPM_DUMMY_NEEDED) || (hpm_requirement_check == SPI_FLASH_HPM_WRITE_SR_NEEDED)) {
ESP_EARLY_LOGI(HPM_TAG, "Enabling flash high speed mode by %s", chip_hpm->method);
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;
}
hpm_dummy_changed = (hpm_requirement_check == SPI_FLASH_HPM_DUMMY_NEEDED) ? true : false;
} else if (hpm_requirement_check == 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;
}
bool spi_flash_hpm_dummy_adjust(void)
{
return hpm_dummy_changed;
}