feat(psram): support psram device driver

This commit is contained in:
armando
2026-03-31 13:56:43 +08:00
parent 582451a451
commit ebbd9cdb59
35 changed files with 2871 additions and 907 deletions
+2
View File
@@ -33,6 +33,8 @@ static int s_apll_ref_cnt = 0;
#if SOC_CLK_MPLL_SUPPORTED
static uint32_t s_cur_mpll_freq_hz = 0;
static int s_mpll_ref_cnt = 0;
#endif
#if CONFIG_ESP_LDO_RESERVE_PSRAM
static esp_ldo_channel_handle_t s_ldo_chan = NULL;
#endif
+12 -11
View File
@@ -37,20 +37,21 @@ entries:
rtc_sleep:rtc_sleep_pd (noflash)
if IDF_TARGET_ESP32S3 = y || IDF_TARGET_ESP32C2 = y || IDF_TARGET_ESP32C3 = y:
rtc_sleep:rtc_sleep_pu (noflash)
if SOC_PMU_SUPPORTED = y && SOC_LIGHT_SLEEP_SUPPORTED = y:
pmu_sleep (noflash)
if SOC_PM_SLEEP_CLK_ICG_USE_REGDMA = y && PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP != y:
pmu_sleep_clock_icg:pmu_sleep_clock_icg_config (noflash)
sleep_mspi (noflash)
if SOC_PMU_SUPPORTED = y:
if SPIRAM_FLASH_LOAD_TO_PSRAM = y:
pmu_init (noflash)
pmu_param (noflash)
elif PM_SLP_IRAM_OPT = y && IDF_TARGET_ESP32P4 != y:
pmu_param:get_act_lp_dbias (noflash)
if IDF_TARGET_ESP32H4 || IDF_TARGET_ESP32H21 = y:
pmu_param:get_act_hp_drvb (noflash)
else:
pmu_param:get_act_hp_dbias (noflash)
if SOC_LIGHT_SLEEP_SUPPORTED = y:
pmu_sleep (noflash)
if SOC_PM_SLEEP_CLK_ICG_USE_REGDMA = y && PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP != y:
pmu_sleep_clock_icg:pmu_sleep_clock_icg_config (noflash)
sleep_mspi (noflash)
if PM_SLP_IRAM_OPT = y && IDF_TARGET_ESP32P4 != y:
pmu_param:get_act_lp_dbias (noflash)
if IDF_TARGET_ESP32H4 || IDF_TARGET_ESP32H21 = y:
pmu_param:get_act_hp_drvb (noflash)
else:
pmu_param:get_act_hp_dbias (noflash)
if SOC_PMU_PVT_SUPPORTED = y && ESP_ENABLE_PVT = y:
pmu_pvt (noflash)
if PM_SLP_IRAM_OPT = y && SOC_USB_SERIAL_JTAG_SUPPORTED = y:
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -245,9 +245,9 @@ static void s_sweep_for_success_sample_points(uint8_t *reference_data, void *con
}
}
ESP_DRAM_LOGD(TAG, "test nums: %" PRIu32 ", test result: [id][good/bad][good_times]:", s_tuning_cfg_drv.sweep_test_nums);
ESP_DRAM_LOGW(TAG, "test nums: %" PRIu32 ", test result: [id][good/bad][good_times]:", s_tuning_cfg_drv.sweep_test_nums);
for (config_idx = 0; config_idx < timing_config->available_config_num; config_idx++) {
ESP_DRAM_LOGD(TAG, "[%"PRIu32"][%s][%" PRIu32 "] ", config_idx, out_array[config_idx] == s_tuning_cfg_drv.sweep_test_nums ? "good" : "bad", out_array[config_idx]);
ESP_DRAM_LOGW(TAG, "[%"PRIu32"][%s][%" PRIu32 "] ", config_idx, out_array[config_idx] == s_tuning_cfg_drv.sweep_test_nums ? "good" : "bad", out_array[config_idx]);
}
}
@@ -0,0 +1,11 @@
target_include_directories(${COMPONENT_LIB} PUBLIC .)
set(srcs)
if(NOT BOOTLOADER_BUILD)
if(NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP)
list(APPEND srcs "mspi_timing_config.c")
endif()
endif()
target_sources(${COMPONENT_LIB} PRIVATE "${srcs}")
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "soc/soc_caps.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/mspi_timing_config.h"
#include "mspi_timing_tuning_configs.h"
#include "hal/psram_ctrlr_ll.h"
#include "hal/mspi_ll.h"
#include "soc/hp_sys_clkrst_struct.h"
ESP_LOG_ATTR_TAG(TAG, "MSPI Timing");
//-------------------------------------MSPI Clock Setting-------------------------------------//
void mspi_timing_config_set_psram_clock(uint32_t psram_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi)
{
uint32_t freqdiv = MSPI_TIMING_MPLL_FREQ_MHZ / MSPI_TIMING_CORE_CLOCK_DIV / psram_freq_mhz;
assert(freqdiv > 0);
ESP_DRAM_LOGD(TAG, "psram_freq_mhz: %" PRIu32 " mhz, bus clock div: %" PRIu32, psram_freq_mhz, freqdiv);
PERIPH_RCC_ATOMIC() {
//MSPI2 and MSPI3 share the register for core clock. So we only set MSPI2 here.
psram_ctrlr_ll_enable_core_clock(PSRAM_CTRLR_LL_MSPI_ID_2, true);
psram_ctrlr_ll_set_core_clock_div(PSRAM_CTRLR_LL_MSPI_ID_2, MSPI_TIMING_CORE_CLOCK_DIV);
psram_ctrlr_ll_set_bus_clock(PSRAM_CTRLR_LL_MSPI_ID_3, freqdiv);
psram_ctrlr_ll_set_bus_clock(PSRAM_CTRLR_LL_MSPI_ID_2, freqdiv);
}
}
void mspi_timing_config_set_flash_clock(uint32_t flash_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi)
{
#if MSPI_TIMING_FLASH_NEEDS_TUNING
assert(HP_SYS_CLKRST.peri_clk_ctrl00.reg_flash_clk_src_sel == 1);
uint32_t core_clock_mhz = MSPI_TIMING_SPLL_FREQ_MHZ / MSPI_TIMING_LL_HP_FLASH_CORE_CLK_DIV;
assert(core_clock_mhz == 120);
uint32_t freqdiv = core_clock_mhz / flash_freq_mhz;
PERIPH_RCC_ATOMIC() {
//core clock shared among SPI0 / SPI1
mspi_timing_ll_set_flash_core_clock(MSPI_TIMING_LL_MSPI_ID_0, core_clock_mhz);
}
mspi_timing_ll_set_flash_clock(MSPI_TIMING_LL_MSPI_ID_0, freqdiv);
if (control_both_mspi) {
mspi_timing_ll_set_flash_clock(MSPI_TIMING_LL_MSPI_ID_1, freqdiv);
}
mspi_timing_ll_mask_invalid_dqs(MSPI_TIMING_LL_MSPI_ID_0, true);
mspi_timing_ll_mask_invalid_dqs(MSPI_TIMING_LL_MSPI_ID_1, true);
#endif
}
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#define MSPI_TIMING_MSPI1_IS_INVOLVED CONFIG_ESPTOOLPY_FLASHFREQ_120M //This means esp flash driver needs to be notified
#define MSPI_TIMING_CONFIG_NUM_MAX 32 //This should be larger than the max available timing config num
#define MSPI_TIMING_TEST_DATA_LEN 128
#define MSPI_TIMING_PSRAM_TEST_DATA_ADDR 0x80
#define MSPI_TIMING_DELAYLINE_TEST_NUMS 100
#define MSPI_TIMING_FLASH_TEST_DATA_ADDR CONFIG_BOOTLOADER_OFFSET_IN_FLASH
#define MSPI_TIMING_CORE_CLOCK_DIV 1
#if CONFIG_SPIRAM_SPEED_250M
#define MSPI_TIMING_PSRAM_NEEDS_TUNING 1
#define MSPI_TIMING_MPLL_FREQ_MHZ 500
#elif CONFIG_SPIRAM_SPEED_200M
#define MSPI_TIMING_PSRAM_NEEDS_TUNING 1
#define MSPI_TIMING_MPLL_FREQ_MHZ 400
#elif CONFIG_SPIRAM_SPEED_80M
#define MSPI_TIMING_PSRAM_NEEDS_TUNING 1
#define MSPI_TIMING_MPLL_FREQ_MHZ 320
#else
#define MSPI_TIMING_MPLL_FREQ_MHZ 400
#endif
#define MSPI_TIMING_SPLL_FREQ_MHZ 480
#define MSPI_TIMING_PSRAM_DTR_MODE CONFIG_SPIRAM_MODE_OCT
#define MSPI_TIMING_FLASH_STR_MODE 1
#if CONFIG_ESPTOOLPY_FLASHFREQ_20M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 20
#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 40
#elif CONFIG_ESPTOOLPY_FLASHFREQ_80M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 80
#else //CONFIG_ESPTOOLPY_FLASHFREQ_120M
#define MSPI_TIMING_FLASH_MODULE_CLOCK 120
#endif
#define MSPI_TIMING_FLASH_NEEDS_TUNING (MSPI_TIMING_FLASH_MODULE_CLOCK > 80)
#if MSPI_TIMING_FLASH_NEEDS_TUNING
#define MSPI_TIMING_FLASH_CORE_CLOCK_MHZ 120
#else
#define MSPI_TIMING_FLASH_CORE_CLOCK_MHZ 80
#endif
//------------------------------------------Helper Macros to get FLASH/PSRAM tuning configs-----------------------------------------------//
#define __GET_TUNING_CONFIG(type, core_clock, module_clock, mode) \
(mspi_timing_config_t) { .tuning_config_table = MSPI_TIMING_##type##_CONFIG_TABLE_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode, \
.available_config_num = MSPI_TIMING_##type##_CONFIG_NUM_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode, \
.flash_default_config_id = MSPI_TIMING_##type##_DEFAULT_CONFIG_ID_CORE_CLK_##core_clock##M_MODULE_CLK_##module_clock##M_##mode }
#define _GET_TUNING_CONFIG(type, core_clock, module_clock, mode) __GET_TUNING_CONFIG(type, core_clock, module_clock, mode)
#define MSPI_TIMING_FLASH_GET_TUNING_CONFIG(core_clock_mhz, module_clock_mhz, mode) _GET_TUNING_CONFIG(FLASH, core_clock_mhz, module_clock_mhz, mode)
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -49,12 +49,14 @@ ESP_LOG_ATTR_TAG(TAG, "MSPI DQS");
const static uint32_t s_test_data[MSPI_TIMING_TEST_DATA_LEN] = {0x7f786655, 0xa5ff005a, 0x3f3c33aa, 0xa5ff5a00, 0x1f1e9955, 0xa5005aff, 0x0f0fccaa, 0xa55a00ff,
0x07876655, 0xffa55a00, 0x03c333aa, 0xff00a55a, 0x01e19955, 0xff005aa5, 0x00f0ccaa, 0xff5a00a5,
0x80786655, 0x00a5ff5a, 0xc03c33aa, 0x00a55aff, 0xe01e9355, 0x00ff5aa5, 0xf00fccaa, 0x005affa5,
0xf8876655, 0x5aa5ff00, 0xfcc333aa, 0x5affa500, 0xfee19955, 0x5a00a5ff, 0x11f0ccaa, 0x5a00ffa5};
0xf8876655, 0x5aa5ff00, 0xfcc333aa, 0x5affa500, 0xfee19955, 0x5a00a5ff, 0x11f0ccaa, 0x5a00ffa5
};
const static mspi_timing_config_t s_test_delayline_config = {
.delayline_table = {{0, 15}, {0, 14}, {0, 13}, {0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {0, 6}, {0, 5}, {0, 4}, {0, 3}, {0, 2}, {0, 1},
{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}, {10, 0}, {11, 0}, {12, 0}, {13, 0}, {14, 0}, {15, 0}},
.available_config_num = 31,
};
.delayline_table = {{0, 15}, {0, 14}, {0, 13}, {0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {0, 6}, {0, 5}, {0, 4}, {0, 3}, {0, 2}, {0, 1},
{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}, {10, 0}, {11, 0}, {12, 0}, {13, 0}, {14, 0}, {15, 0}
},
.available_config_num = 31,
};
static mspi_ll_dqs_phase_t s_psram_best_phase = MSPI_LL_DQS_PHASE_MAX;
static delayline_config_t s_psram_best_delayline = {WRONG_DELAYLINE, WRONG_DELAYLINE};
@@ -121,7 +123,9 @@ void mspi_timing_config_psram_set_tuning_phase(const void *configs, uint8_t id)
{
mspi_ll_dqs_phase_t phase = ((mspi_timing_config_t *)configs)->phase[id];
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_0, phase);
#if MSPI_LL_PSRAM_DQS1_SUPPORTED
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_1, phase);
#endif
ESP_DRAM_LOGD(TAG, "set to phase: %d", phase);
}
@@ -139,7 +143,7 @@ uint32_t mspi_timing_psram_select_best_tuning_phase(const void *configs, uint32_
}
if (success) {
ESP_DRAM_LOGD(TAG, "tuning success, best phase id is %"PRIu32, best_phase_id);
ESP_DRAM_LOGI(TAG, "tuning success, best phase id is %"PRIu32, best_phase_id);
} else {
ESP_DRAM_LOGW(TAG, "tuning fail, best phase id is fallen back to index %"PRIu32"", best_phase_id);
}
@@ -162,14 +166,22 @@ void mspi_timing_config_psram_set_tuning_delayline(const void *configs, uint8_t
{
assert(s_psram_best_phase != MSPI_LL_DQS_PHASE_MAX);
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_0, s_psram_best_phase);
#if MSPI_LL_PSRAM_DQS1_SUPPORTED
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_1, s_psram_best_phase);
#endif
ESP_DRAM_LOGD(TAG, "set to best phase: %d", s_psram_best_phase);
const delayline_config_t *delayline_config = &((mspi_timing_config_t *)configs)->delayline_table[id];
for (int i = 0; i < MSPI_LL_PIN_MAX; i++) {
if (i == MSPI_LL_PIN_DQS0 || i == MSPI_LL_PIN_DQS1) {
if (i == MSPI_LL_PIN_DQS0) {
mspi_timing_ll_set_delayline(i, delayline_config->dqs_delayline);
} else {
}
#if MSPI_LL_PSRAM_DQS1_SUPPORTED
else if (i == MSPI_LL_PIN_DQS1) {
mspi_timing_ll_set_delayline(i, delayline_config->dqs_delayline);
}
#endif
else {
mspi_timing_ll_set_delayline(i, delayline_config->data_delayline);
}
}
@@ -185,7 +197,7 @@ uint32_t mspi_timing_psram_select_best_tuning_delayline(const void *configs, uin
ESP_DRAM_LOGW(TAG, "tuning fail, best delayline id is fallen back to index %"PRIu32"", bset_delayline_id);
} else {
bset_delayline_id = end - consecutive_length / 2;
ESP_DRAM_LOGD(TAG, "tuning success, best delayline id is %"PRIu32, bset_delayline_id);
ESP_DRAM_LOGI(TAG, "tuning success, best delayline id is %"PRIu32, bset_delayline_id);
}
return bset_delayline_id;
@@ -199,8 +211,9 @@ void mspi_timing_psram_set_best_tuning_delayline(const void *configs, uint8_t be
void mspi_timing_psram_config_clear_tuning_regs(bool control_both_mspi)
{
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_0, 0);
#if MSPI_LL_PSRAM_DQS1_SUPPORTED
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_1, 0);
#endif
for (int i = 0; i < MSPI_LL_PIN_MAX; i++) {
mspi_timing_ll_set_delayline(i, 0);
}
@@ -209,13 +222,31 @@ void mspi_timing_psram_config_clear_tuning_regs(bool control_both_mspi)
void mspi_timing_psram_config_set_tuning_regs(bool control_both_mspi)
{
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_0, s_psram_best_phase);
#if MSPI_LL_PSRAM_DQS1_SUPPORTED
mspi_timing_ll_set_dqs_phase(MSPI_LL_DQS_ID_1, s_psram_best_phase);
#endif
for (int i = 0; i < MSPI_LL_PIN_MAX; i++) {
if (i == MSPI_LL_PIN_DQS0 || i == MSPI_LL_PIN_DQS1) {
if (i == MSPI_LL_PIN_DQS0) {
mspi_timing_ll_set_delayline(i, s_psram_best_delayline.dqs_delayline);
} else {
}
#if MSPI_LL_PSRAM_DQS1_SUPPORTED
else if (i == MSPI_LL_PIN_DQS1) {
mspi_timing_ll_set_delayline(i, s_psram_best_delayline.dqs_delayline);
}
#endif
else {
mspi_timing_ll_set_delayline(i, s_psram_best_delayline.data_delayline);
}
}
}
void mspi_timing_flash_config_set_tuning_regs(bool control_both_mspi)
{
//for compatibility
}
void mspi_timing_flash_config_clear_tuning_regs(bool control_both_mspi)
{
//for compatibility
}
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -40,4 +40,11 @@
void esp_cpu_configure_region_protection(void)
{
/* TODO: [ESP32S31] IDF-15238 */
__attribute__((unused)) const unsigned PMA_RW = PMA_L | PMA_EN | PMA_R | PMA_W;
__attribute__((unused)) const unsigned PMA_RX = PMA_L | PMA_EN | PMA_R | PMA_X;
__attribute__((unused)) const unsigned PMA_RWX = PMA_L | PMA_EN | PMA_R | PMA_W | PMA_X;
//without setting this, psram cannot be reached
PMA_RESET_AND_ENTRY_SET_NAPOT(7, SOC_EXTRAM_LOW, (SOC_EXTRAM_HIGH - SOC_EXTRAM_LOW), PMA_NAPOT | PMA_RWX);
}
@@ -32,7 +32,7 @@ static const char *TAG = "rtc_clk";
static int s_cur_cpll_freq = 0;
// MPLL frequency option, 400MHz. Zero if MPLL is not enabled.
static SPM_DRAM_ATTR uint32_t s_cur_mpll_freq = 0;
static uint32_t s_cur_mpll_freq = 0;
void rtc_clk_32k_enable(bool enable)
{
@@ -204,7 +204,7 @@ void rtc_clk_cpu_freq_set_xtal_for_sleep(void)
soc_xtal_freq_t rtc_clk_xtal_freq_get(void)
{
uint32_t xtal_freq_mhz = clk_ll_xtal_load_freq_mhz();
uint32_t xtal_freq_mhz = clk_ll_xtal_get_freq_mhz();
if (xtal_freq_mhz == 0) {
ESP_HW_LOGW(TAG, "invalid RTC_XTAL_FREQ_REG value, assume 40MHz");
return SOC_XTAL_FREQ_40M;
@@ -214,7 +214,7 @@ soc_xtal_freq_t rtc_clk_xtal_freq_get(void)
void rtc_clk_xtal_freq_update(soc_xtal_freq_t xtal_freq)
{
clk_ll_xtal_store_freq_mhz(xtal_freq);
// clk_ll_xtal_store_freq_mhz(xtal_freq);
}
uint32_t rtc_clk_apb_freq_get(void)
@@ -331,7 +331,26 @@ IRAM_ATTR void rtc_clk_mpll_enable(void)
void rtc_clk_mpll_configure(uint32_t xtal_freq, uint32_t mpll_freq, bool thread_safe)
{
// TODO: ["ESP32S31"] IDF-14678
/* Analog part */
if (thread_safe) {
_regi2c_ctrl_ll_master_enable_clock(true);
} else {
ANALOG_CLOCK_ENABLE();
}
/* MPLL calibration start */
clk_ll_mpll_calibration_start();
clk_ll_mpll_set_config(mpll_freq, xtal_freq);
/* wait calibration done */
while(!clk_ll_mpll_calibration_is_done());
/* MPLL calibration stop */
clk_ll_mpll_calibration_stop();
if (thread_safe) {
_regi2c_ctrl_ll_master_enable_clock(false);
} else {
ANALOG_CLOCK_DISABLE();
}
s_cur_mpll_freq = mpll_freq;
}
IRAM_ATTR uint32_t rtc_clk_mpll_get_freq(void)
@@ -213,7 +213,7 @@ uint64_t rtc_time_slowclk_to_us(uint64_t rtc_cycles, uint32_t period)
uint64_t rtc_time_get(void)
{
return 0;// TODO: ["ESP32S31"] IDF-14678
return rtc_timer_hal_get_cycle_count(0);
}
uint32_t rtc_clk_freq_cal(uint32_t cal_val)