Merge branch 'feat/ldo_esp32s31' into 'master'

feat(LDO): Support LDO for psram on esp32s31

See merge request espressif/esp-idf!47174
This commit is contained in:
C.S.M
2026-04-02 10:43:57 +08:00
10 changed files with 217 additions and 2 deletions
@@ -59,6 +59,7 @@ extern "C" {
PSRAM_CTRLR_LL_EVENT_TX_TRANS_UDF)
#define PSRAM_CTRLR_LL_INTR_EVENT_SUPPORTED 1
#define PSRAM_CTRLR_LL_DEDICATED_LDO 1
/**
* @brief Set PSRAM write cmd
@@ -0,0 +1,123 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "hal/misc.h"
#include "soc/pmu_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
#define LDO_LL_RAIL_VOLTAGE_MV 3300
/**
* @brief Convert voltage to dref and mul value
*
* @param ldo_unit LDO unit
* @param voltage_mv Voltage in mV
* @param dref Returned dref value
* @param mul Returned mul value
* @param use_rail_voltage Returned value to indicate if the rail voltage should be used
*/
__attribute__((always_inline))
static inline void ldo_ll_voltage_to_dref_mul(int ldo_unit, int voltage_mv, uint8_t *dref, uint8_t *mul, bool *use_rail_voltage)
{
(void)ldo_unit;
// to avoid using FPU, enlarge the constants by 1000 as fixed point
int K_1000 = 1000;
int Vos_1000 = 0;
int C_1000 = 1000;
// TODO: [ESP32S31] IDF-15510 For efuse calibration.
// iterate all the possible dref and mul values to find the best match
int min_voltage_diff = 400000000;
uint8_t matched_dref = 0;
uint8_t matched_mul = 0;
for (uint8_t dref_val = 0; dref_val < 16; dref_val++) {
int vref_20 = (dref_val < 9) ? (10 + dref_val) : (20 + (dref_val - 9) * 2);
for (uint8_t mul_val = 0; mul_val < 8; mul_val++) {
int vout_80000000 = (vref_20 * K_1000 + 20 * Vos_1000) * (4000 + mul_val * C_1000);
int diff = voltage_mv * 80000 - vout_80000000;
if (diff < 0) {
diff = -diff;
}
if (diff < min_voltage_diff) {
min_voltage_diff = diff;
matched_dref = dref_val;
matched_mul = mul_val;
}
}
}
*dref = matched_dref;
*mul = matched_mul;
// if the expected voltage is 3.3V, use the rail voltage directly
*use_rail_voltage = (voltage_mv == LDO_LL_RAIL_VOLTAGE_MV);
}
/**
* @brief Adjust voltage of a LDO unit
*
* @note When bypass is enabled, the input voltage is sourced directly to the output.
* The dref and mul values will be ignored.
*
* @param ldo_unit LDO unit
* @param dref A parameter which controls the internal reference voltage
* @param mul Multiply factor
* @param use_rail_voltage Use rail voltage directly (i.e. bypass the LDO)
*/
__attribute__((always_inline))
static inline void ldo_ll_adjust_voltage(int ldo_unit, uint8_t dref, uint8_t mul, bool use_rail_voltage)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(PMU.ext_ldo_ctrl, ext_ldo_dref, dref);
PMU.ext_ldo_ctrl.ext_ldo_mul = mul;
PMU.ext_ldo_ctrl.ext_ldo_tie_high = use_rail_voltage;
}
/**
* @brief Enable power on the PSRAM domain
*
* @param enable True: enable; False: disable
*/
__attribute__((always_inline))
static inline void ldo_ll_psram_power_enable(bool enable)
{
PMU.psram_cfg.psram_xpd = enable;
}
/**
* @brief Enable ripple suppression of a LDO unit
*
* @param ldo_unit LDO unit
* @param enable True: enable; False: disable
*/
__attribute__((always_inline))
static inline void ldo_ll_enable_ripple_suppression(int ldo_unit, bool enable)
{
(void)ldo_unit;
PMU.ext_ldo_ctrl.ext_ldo_en_vdet = enable;
}
/**
* @brief Enable current limit of a LDO unit to avoid inrush current
*
* @param ldo_unit LDO unit
* @param enable True: enable; False: disable
*/
__attribute__((always_inline))
static inline void ldo_ll_enable_current_limit(int ldo_unit, bool enable)
{
(void)ldo_unit;
PMU.ext_ldo_ctrl.ext_cur_lim = enable;
}
#ifdef __cplusplus
}
#endif
@@ -143,7 +143,7 @@ uint32_t mspi_timing_psram_select_best_tuning_phase(const void *configs, uint32_
}
if (success) {
ESP_DRAM_LOGI(TAG, "tuning success, best phase id is %"PRIu32, best_phase_id);
ESP_DRAM_LOGD(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);
}
@@ -197,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_LOGI(TAG, "tuning success, best delayline id is %"PRIu32, bset_delayline_id);
ESP_DRAM_LOGD(TAG, "tuning success, best delayline id is %"PRIu32, bset_delayline_id);
}
return bset_delayline_id;
+6
View File
@@ -15,10 +15,16 @@ if(${target} STREQUAL "esp32")
list(APPEND priv_requires bootloader_support esp_driver_gpio)
endif()
if(${target} STREQUAL "esp32s31")
# only esp32s31 needs esp_hal_pmu for ldo.
list(APPEND priv_requires esp_hal_pmu)
endif()
set(srcs "system_layer/esp_psram_mspi.c")
if(CONFIG_SPIRAM)
list(APPEND srcs "system_layer/esp_psram.c")
list(APPEND srcs "system_layer/esp_psram_ldo_supply.c")
if(${target} STREQUAL "esp32")
list(APPEND srcs "esp32/esp_psram_extram_cache.c"
@@ -11,6 +11,7 @@
#include "esp_private/periph_ctrl.h"
#include "esp_private/mspi_timing_tuning.h"
#include "esp_private/esp_psram_impl.h"
#include "esp_private/esp_psram_ldo.h"
#include "hal/psram_ctrlr_ll.h"
#include "hal/mspi_ll.h"
#include "clk_ctrl_os.h"
@@ -11,6 +11,7 @@
#include "esp_private/periph_ctrl.h"
#include "esp_private/mspi_timing_tuning.h"
#include "esp_private/esp_psram_impl.h"
#include "esp_private/esp_psram_ldo.h"
#include "hal/psram_ctrlr_ll.h"
#include "hal/mspi_ll.h"
#include "clk_ctrl_os.h"
@@ -413,6 +414,12 @@ static void s_configure_psram_ecc(void)
esp_err_t esp_psram_impl_enable(void)
{
#if PSRAM_CTRLR_LL_DEDICATED_LDO
esp_psram_power_cfg_t config = {
.voltage_mv = CONFIG_SPIRAM_LDO_VOLTAGE_DOMAIN,
};
esp_psram_power_init(&config);
#endif
#if SOC_CLK_MPLL_SUPPORTED
periph_rtc_mpll_acquire();
uint32_t real_mpll_freq = 0;
@@ -94,3 +94,20 @@ menu "PSRAM config"
source "$IDF_PATH/components/esp_psram/Kconfig.spiram.common" # insert non-chip-specific items here
endmenu
menu "PSRAM LDO Configurations"
choice SPIRAM_LDO_VOLTAGE_DOMAIN
prompt "PSRAM power domain voltage"
default SPIRAM_LDO_VOLTAGE_1800_MV
help
Select the voltage used by the PSRAM power domain.
config SPIRAM_LDO_VOLTAGE_1800_MV
bool "1.8V"
endchoice
config SPIRAM_LDO_VOLTAGE_DOMAIN
int
default 1800 if SPIRAM_LDO_VOLTAGE_1800_MV
endmenu
@@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Configuration for PSRAM domain LDO supply initialization
*/
typedef struct {
uint32_t voltage_mv; /*!< PSRAM supply voltage in mV */
} esp_psram_power_cfg_t;
/**
* @brief Initialize the PSRAM supply (LDO voltage and power switch)
*
* @param config Configuration for the PSRAM supply
*/
void esp_psram_power_init(const esp_psram_power_cfg_t *config);
#ifdef __cplusplus
}
#endif
+2
View File
@@ -26,3 +26,5 @@ entries:
esp_psram: esp_psram_init (noflash)
esp_psram: s_psram_chip_init (noflash)
esp_psram: s_xip_psram_placement (noflash)
if IDF_TARGET_ESP32S31 = y:
esp_psram_ldo_supply: esp_psram_power_init (noflash)
@@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#if !CONFIG_IDF_TARGET_ESP32 && !CONFIG_IDF_TARGET_ESP32S2
#include "hal/psram_ctrlr_ll.h"
#endif
#if PSRAM_CTRLR_LL_DEDICATED_LDO
#include "hal/ldo_ll.h"
#include "esp_private/esp_psram_ldo.h"
void esp_psram_power_init(const esp_psram_power_cfg_t *config)
{
uint8_t dref = 0;
uint8_t mul = 0;
bool use_rail_voltage = false;
ldo_ll_enable_current_limit(0, true);
ldo_ll_voltage_to_dref_mul(0, config->voltage_mv, &dref, &mul, &use_rail_voltage);
ldo_ll_adjust_voltage(0, dref, mul, use_rail_voltage);
ldo_ll_psram_power_enable(true);
ldo_ll_enable_current_limit(0, false);
}
#endif