feat(ldo): support s31 psram sdmmc power domain

Move S31 PSRAM/SDMMC power setup onto the common LDO path so shared users can control the domain consistently.
This commit is contained in:
armando
2026-05-18 11:27:21 +08:00
parent 09ff879535
commit 6d12e30433
12 changed files with 118 additions and 104 deletions

View File

@@ -9,13 +9,45 @@
#include <stdint.h>
#include <stdbool.h>
#include "hal/misc.h"
#include "hal/pmu_types.h"
#include "soc/pmu_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
#define LDO_LL_RAIL_VOLTAGE_MV 3300
#define LDO_LL_NUM_UNITS 1 // Number of LDO units
#define LDO_LL_ADJUSTABLE_CHAN_MASK 0x01 // all the channels are adjustable by setting "mul" and "dref" registers
#define LDO_LL_RECOMMEND_MAX_VOLTAGE_MV 2700
#define LDO_LL_RECOMMEND_MIN_VOLTAGE_MV 500
#define LDO_LL_RAIL_VOLTAGE_MV 3300
/**
* @brief In the analog design, the LDO output "channel" is index from 1, i.e., VO1, VO2, VO3, VO4.
* But in software, we mapped them to "LDO unit", which is index from 0, i.e., 0, 1, 2, 3.
*/
#define LDO_ID2UNIT(ldo_id) ((ldo_id) - 1)
/**
* @brief LDO unit owner
*/
typedef enum {
LDO_LL_UNIT_OWNER_HW, // LDO unit is controlled by hardware
LDO_LL_UNIT_OWNER_SW, // LDO unit is controlled by software
} ldo_ll_unit_owner_t;
/**
* @brief Check if a LDO channel is valid
*
* @param ldo_chan LDO channel ID, note, this is indexed from 1
* @return True for valid, false for invalid
*/
__attribute__((always_inline))
static inline bool ldo_ll_is_valid_ldo_channel(int ldo_chan)
{
return (ldo_chan > 0) && (ldo_chan <= LDO_LL_NUM_UNITS);
}
/**
* @brief Convert voltage to dref and mul value
@@ -62,6 +94,20 @@ static inline void ldo_ll_voltage_to_dref_mul(int ldo_unit, int voltage_mv, uint
*use_rail_voltage = (voltage_mv == LDO_LL_RAIL_VOLTAGE_MV);
}
/**
* @brief Set owner of a LDO unit
*
* @note Even if the LDO unit is controlled by hardware, its voltage can still be changed by software by `ldo_ll_adjust_voltage`
*
* @param ldo_unit LDO unit
* @param owner Owner of the LDO unit
*/
__attribute__((always_inline))
static inline void ldo_ll_set_owner(int ldo_unit, ldo_ll_unit_owner_t owner)
{
//for compatibility
}
/**
* @brief Adjust voltage of a LDO unit
*
@@ -81,17 +127,6 @@ static inline void ldo_ll_adjust_voltage(int ldo_unit, uint8_t dref, uint8_t 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
*
@@ -105,6 +140,20 @@ static inline void ldo_ll_enable_ripple_suppression(int ldo_unit, bool enable)
PMU.ext_ldo_ctrl.ext_ldo_en_vdet = enable;
}
/**
* @brief Enable a LDO unit
*
* @param ldo_unit LDO unit
* @param enable True: enable; False: disable
*/
__attribute__((always_inline))
static inline void ldo_ll_enable(int ldo_unit, bool enable)
{
//for compatibility
//PMU.hp_sys[PMU_MODE_HP_ACTIVE].regulator0.xpd is for chip internal LDO for chip power
//this will not be controlled by this general purpose LDO file
}
/**
* @brief Enable current limit of a LDO unit to avoid inrush current
*

View File

@@ -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
*/
@@ -87,6 +87,7 @@ esp_err_t esp_ldo_acquire_channel(const esp_ldo_channel_config_t *config, esp_ld
uint8_t dref = 0;
uint8_t mul = 0;
bool use_rail_voltage = false;
ldo_ll_enable_current_limit(unit_id, true);
// calculate the dref and mul
ldo_ll_voltage_to_dref_mul(unit_id, config->voltage_mv, &dref, &mul, &use_rail_voltage);
ldo_ll_adjust_voltage(unit_id, dref, mul, use_rail_voltage);
@@ -95,6 +96,7 @@ esp_err_t esp_ldo_acquire_channel(const esp_ldo_channel_config_t *config, esp_ld
// suppress voltage ripple
ldo_ll_enable_ripple_suppression(unit_id, true);
ldo_ll_enable(unit_id, true);
ldo_ll_enable_current_limit(unit_id, false);
}
// update the channel attributes
channel->ref_cnt++;
@@ -146,7 +148,9 @@ esp_err_t esp_ldo_release_channel(esp_ldo_channel_handle_t chan)
esp_err_t esp_ldo_channel_adjust_voltage(esp_ldo_channel_handle_t chan, int voltage_mv)
{
ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(chan->flags.adjustable, ESP_ERR_NOT_SUPPORTED, TAG, "LDO is not adjustable");
if (chan->voltage_mv != voltage_mv) {
ESP_RETURN_ON_FALSE(chan->flags.adjustable, ESP_ERR_NOT_SUPPORTED, TAG, "LDO is not adjustable");
}
// check if the target voltage is within the recommended range
if (!((voltage_mv == LDO_LL_RAIL_VOLTAGE_MV) ||
(voltage_mv >= LDO_LL_RECOMMEND_MIN_VOLTAGE_MV && voltage_mv <= LDO_LL_RECOMMEND_MAX_VOLTAGE_MV))) {

View File

@@ -0,0 +1,34 @@
menu "LDO Regulator Configurations"
depends on SOC_GP_LDO_SUPPORTED
config ESP_LDO_RESERVE_PSRAM
bool "Reserve one LDO regulator channel for PSRAM (READ HELP)"
default y
help
The LDO channel can be used to power the PSRAM chip.
If the PSRAM chip is not powered by ESP internal LDO, you can disable this option.
Then you will free up one LDO channel for other general purpose.
config ESP_LDO_CHAN_PSRAM_DOMAIN
int "LDO regulator channel that used to power PSRAM and MPLL (READ HELP)"
depends on ESP_LDO_RESERVE_PSRAM
default 1
range 1 1
help
Select which LDO channel to connect to the PSRAM chip.
choice ESP_LDO_VOLTAGE_PSRAM_DOMAIN
prompt "PSRAM power domain voltage"
depends on ESP_LDO_RESERVE_PSRAM
default ESP_LDO_VOLTAGE_PSRAM_1800_MV
help
Select the voltage used by the PSRAM power domain.
config ESP_LDO_VOLTAGE_PSRAM_1800_MV
bool "1.8V"
endchoice
config ESP_LDO_VOLTAGE_PSRAM_DOMAIN
int
default 1800 if ESP_LDO_VOLTAGE_PSRAM_1800_MV
endmenu

View File

@@ -10,7 +10,9 @@ if(CONFIG_SOC_RNG_SUPPORTED)
list(APPEND srcs "test_random.c")
endif()
if(CONFIG_SOC_GP_LDO_SUPPORTED)
if(CONFIG_SOC_GP_LDO_SUPPORTED AND NOT CONFIG_IDF_TARGET_ESP32S31)
# on s31 ldo is general purpose (and also for psram), but runners all have psram
# so we don't run ldo test on s31 to avoid breaking psram
list(APPEND srcs "test_ldo.c")
endif()

View File

@@ -24,7 +24,6 @@ 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"

View File

@@ -11,7 +11,6 @@
#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 "soc/rtc.h"

View File

@@ -13,7 +13,6 @@
#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 "soc/rtc.h"
@@ -415,12 +414,6 @@ esp_err_t esp_psram_impl_enable(void)
{
psram_ctrlr_ll_enable_power(true);
#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
// We need to use the acquire and freq_set functions directly instead of general clk_tree API for IRAM safe function
esp_clk_tree_mpll_acquire();

View File

@@ -93,20 +93,3 @@ 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

View File

@@ -1,31 +0,0 @@
/*
* 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

View File

@@ -26,5 +26,3 @@ 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)

View File

@@ -1,27 +0,0 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -18,6 +18,14 @@
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
#include "sd_pwr_ctrl_interface.h"
#if SOC_SDMMC_IO_UHS_POWER_EXTERNAL
#define SD_VOLT_MV 1800
#define SD_VOLT_ADJUST_MODE false
#else
#define SD_VOLT_MV 3300
#define SD_VOLT_ADJUST_MODE true
#endif
typedef struct {
esp_ldo_channel_handle_t ldo_chan;
int voltage_mv;
@@ -38,9 +46,9 @@ esp_err_t sd_pwr_ctrl_new_on_chip_ldo(const sd_pwr_ctrl_ldo_config_t *configs, s
ESP_GOTO_ON_FALSE(ctx, ESP_ERR_NO_MEM, err, TAG, "no mem for on-chip ldo control driver context");
esp_ldo_channel_config_t chan_cfg = {
.voltage_mv = 3300,
.voltage_mv = SD_VOLT_MV,
.chan_id = configs->ldo_chan_id,
.flags.adjustable = true, // the SDMMC power control driver will adjust the voltage later according to different speed mode
.flags.adjustable = SD_VOLT_ADJUST_MODE, // the SDMMC power control driver will adjust the voltage later according to different speed mode
};
esp_ldo_channel_handle_t ldo_chan = NULL;
ESP_GOTO_ON_ERROR(esp_ldo_acquire_channel(&chan_cfg, &ldo_chan), err, TAG, "failed to enable the on-chip LDO unit");
@@ -51,6 +59,8 @@ esp_err_t sd_pwr_ctrl_new_on_chip_ldo(const sd_pwr_ctrl_ldo_config_t *configs, s
ctx->voltage_mv = 0;
*ret_drv = driver;
ESP_LOGD(TAG, "on-chip LDO unit %d enabled", configs->ldo_chan_id);
return ESP_OK;
err:
@@ -77,6 +87,7 @@ static esp_err_t s_ldo_set_voltage(void *arg, int voltage_mv)
{
//API checks done by caller
sd_pwr_ctrl_ldo_ctx_t *ctx = arg;
ESP_LOGD(TAG, "setting LDO unit output voltage to %dmV", voltage_mv);
ESP_RETURN_ON_ERROR(esp_ldo_channel_adjust_voltage(ctx->ldo_chan, voltage_mv), TAG, "failed to set LDO unit output voltage");
ctx->voltage_mv = voltage_mv;
return ESP_OK;