diff --git a/components/esp_hal_mspi/esp32s31/include/hal/psram_ctrlr_ll.h b/components/esp_hal_mspi/esp32s31/include/hal/psram_ctrlr_ll.h index 5e1613866a..bfeff96fdc 100644 --- a/components/esp_hal_mspi/esp32s31/include/hal/psram_ctrlr_ll.h +++ b/components/esp_hal_mspi/esp32s31/include/hal/psram_ctrlr_ll.h @@ -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 diff --git a/components/esp_hal_pmu/esp32s31/include/hal/ldo_ll.h b/components/esp_hal_pmu/esp32s31/include/hal/ldo_ll.h new file mode 100644 index 0000000000..d710b7df71 --- /dev/null +++ b/components/esp_hal_pmu/esp32s31/include/hal/ldo_ll.h @@ -0,0 +1,123 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#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 diff --git a/components/esp_hw_support/mspi/mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_dqs.c b/components/esp_hw_support/mspi/mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_dqs.c index ce0acc8f55..01fca537a4 100644 --- a/components/esp_hw_support/mspi/mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_dqs.c +++ b/components/esp_hw_support/mspi/mspi_timing_tuning/tuning_scheme_impl/mspi_timing_by_dqs.c @@ -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; diff --git a/components/esp_psram/CMakeLists.txt b/components/esp_psram/CMakeLists.txt index ac6f66c2f3..9a12c8fa79 100644 --- a/components/esp_psram/CMakeLists.txt +++ b/components/esp_psram/CMakeLists.txt @@ -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" diff --git a/components/esp_psram/device/esp_psram_impl_ap_hex.c b/components/esp_psram/device/esp_psram_impl_ap_hex.c index 09d9012a98..ec5b796b81 100644 --- a/components/esp_psram/device/esp_psram_impl_ap_hex.c +++ b/components/esp_psram/device/esp_psram_impl_ap_hex.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" diff --git a/components/esp_psram/device/esp_psram_impl_ap_oct.c b/components/esp_psram/device/esp_psram_impl_ap_oct.c index 0c8ef0b4e2..a57695864d 100644 --- a/components/esp_psram/device/esp_psram_impl_ap_oct.c +++ b/components/esp_psram/device/esp_psram_impl_ap_oct.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" @@ -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; diff --git a/components/esp_psram/esp32s31/Kconfig.spiram b/components/esp_psram/esp32s31/Kconfig.spiram index 97acbcb457..a4ae908d4d 100644 --- a/components/esp_psram/esp32s31/Kconfig.spiram +++ b/components/esp_psram/esp32s31/Kconfig.spiram @@ -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 diff --git a/components/esp_psram/include/esp_private/esp_psram_ldo.h b/components/esp_psram/include/esp_private/esp_psram_ldo.h new file mode 100644 index 0000000000..bc3488f0d3 --- /dev/null +++ b/components/esp_psram/include/esp_private/esp_psram_ldo.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#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 diff --git a/components/esp_psram/linker.lf b/components/esp_psram/linker.lf index c5473cff39..01f9d6df59 100644 --- a/components/esp_psram/linker.lf +++ b/components/esp_psram/linker.lf @@ -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) diff --git a/components/esp_psram/system_layer/esp_psram_ldo_supply.c b/components/esp_psram/system_layer/esp_psram_ldo_supply.c new file mode 100644 index 0000000000..27c8ff6603 --- /dev/null +++ b/components/esp_psram/system_layer/esp_psram_ldo_supply.c @@ -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