diff --git a/components/esp_hw_support/clk_ctrl_os.c b/components/esp_hw_support/clk_ctrl_os.c index a8b2560abb..30842ff964 100644 --- a/components/esp_hw_support/clk_ctrl_os.c +++ b/components/esp_hw_support/clk_ctrl_os.c @@ -142,7 +142,7 @@ esp_err_t periph_rtc_apll_freq_set(uint32_t expt_freq, uint32_t *real_freq) esp_err_t IRAM_ATTR periph_rtc_mpll_acquire(void) { // power up LDO for the MPLL -#if defined(CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN) && CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN != -1 +#if CONFIG_ESP_LDO_RESERVE_PSRAM esp_ldo_channel_config_t ldo_mpll_config = { .chan_id = CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN, .voltage_mv = CONFIG_ESP_LDO_VOLTAGE_PSRAM_DOMAIN, diff --git a/components/esp_hw_support/ldo/esp_ldo_regulator.c b/components/esp_hw_support/ldo/esp_ldo_regulator.c index 97dcc6f4bb..2a59dd6de5 100644 --- a/components/esp_hw_support/ldo/esp_ldo_regulator.c +++ b/components/esp_hw_support/ldo/esp_ldo_regulator.c @@ -31,8 +31,8 @@ static portMUX_TYPE s_spinlock = portMUX_INITIALIZER_UNLOCKED; static const uint32_t s_ldo_channel_adjustable_mask = LDO_LL_ADJUSTABLE_CHAN_MASK; // each bit represents if the LDO channel is adjustable in hardware -static ldo_regulator_channel_t s_ldo_channels[LDO_LL_UNIT_NUM] = { - [0 ... LDO_LL_UNIT_NUM - 1] = { +static ldo_regulator_channel_t s_ldo_channels[LDO_LL_NUM_UNITS] = { + [0 ... LDO_LL_NUM_UNITS - 1] = { .chan_id = -1, .voltage_mv = 0, .ref_cnt = 0, @@ -43,7 +43,7 @@ static ldo_regulator_channel_t s_ldo_channels[LDO_LL_UNIT_NUM] = { esp_err_t esp_ldo_acquire_channel(const esp_ldo_channel_config_t *config, esp_ldo_channel_handle_t *out_handle) { ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(ldo_ll_is_valid_ldo_id(config->chan_id), ESP_ERR_INVALID_ARG, TAG, "invalid ldo channel ID"); + ESP_RETURN_ON_FALSE(ldo_ll_is_valid_ldo_channel(config->chan_id), ESP_ERR_INVALID_ARG, TAG, "invalid ldo channel ID"); ESP_RETURN_ON_FALSE(config->voltage_mv <= LDO_LL_MAX_VOLTAGE_MV, ESP_ERR_INVALID_ARG, TAG, "invalid voltage value: %d", config->voltage_mv); int unit_id = LDO_ID2UNIT(config->chan_id); @@ -78,8 +78,16 @@ esp_err_t esp_ldo_acquire_channel(const esp_ldo_channel_config_t *config, esp_ld } if (check_voltage_constraint_valid && check_adjustable_constraint_valid) { if (channel->ref_cnt == 0) { - // if the channel is not in use, we need to set the voltage and enable it - ldo_ll_set_output_voltage_mv(unit_id, config->voltage_mv); + // if the channel is not in use, we need to set the initial voltage and enable it + uint8_t dref = 0; + uint8_t mul = 0; + // calculate the dref and mul + ldo_ll_voltage_to_dref_mul(unit_id, config->voltage_mv, &dref, &mul); + ldo_ll_adjust_voltage(unit_id, dref, mul); + // set the ldo unit owner ship + ldo_ll_set_owner(unit_id, config->flags.owned_by_hw ? LDO_LL_UNIT_OWNER_HW : LDO_LL_UNIT_OWNER_SW); + // suppress voltage ripple + ldo_ll_enable_ripple_suppression(unit_id, true); ldo_ll_enable(unit_id, true); } // update the channel attributes @@ -143,7 +151,11 @@ esp_err_t esp_ldo_channel_adjust_voltage(esp_ldo_channel_handle_t chan, int volt // i.e., the handle is not shared between threads without mutex protection chan->voltage_mv = voltage_mv; int unit_id = LDO_ID2UNIT(chan->chan_id); - ldo_ll_set_output_voltage_mv(unit_id, voltage_mv); + uint8_t dref = 0; + uint8_t mul = 0; + // calculate the dref and mul + ldo_ll_voltage_to_dref_mul(unit_id, voltage_mv, &dref, &mul); + ldo_ll_adjust_voltage(unit_id, dref, mul); return ESP_OK; } @@ -153,7 +165,7 @@ esp_err_t esp_ldo_dump(FILE *stream) char line[100]; fprintf(stream, "ESP LDO Channel State:\n"); fprintf(stream, "%-5s %-5s %-10s %-12s %-5s\n", "Index", "ID", "ref_cnt", "voltage_mv", "adjustable"); - for (int i = 0; i < LDO_LL_UNIT_NUM; i++) { + for (int i = 0; i < LDO_LL_NUM_UNITS; i++) { char *buf = line; size_t len = sizeof(line); memset(line, 0x0, len); diff --git a/components/esp_hw_support/ldo/include/esp_ldo_regulator.h b/components/esp_hw_support/ldo/include/esp_ldo_regulator.h index 0540541003..868166acd5 100644 --- a/components/esp_hw_support/ldo/include/esp_ldo_regulator.h +++ b/components/esp_hw_support/ldo/include/esp_ldo_regulator.h @@ -30,6 +30,7 @@ typedef struct { /// Extra flags of a LDO channel struct ldo_extra_flags { uint32_t adjustable : 1; /*!< Whether the LDO channel is adjustable, and the voltage can be updated by `esp_ldo_channel_adjust_voltage` */ + uint32_t owned_by_hw: 1; /*!< If the LDO channel is owned by hardware, then software configurations will be overridden by hardware */ } flags; /*!< Flags for the LDO channel */ } esp_ldo_channel_config_t; diff --git a/components/esp_hw_support/port/esp32p4/Kconfig.ldo b/components/esp_hw_support/port/esp32p4/Kconfig.ldo index 6c0bea47ed..0e5a20fdd8 100644 --- a/components/esp_hw_support/port/esp32p4/Kconfig.ldo +++ b/components/esp_hw_support/port/esp32p4/Kconfig.ldo @@ -1,19 +1,26 @@ menu "LDO Regulator Configurations" depends on SOC_GP_LDO_SUPPORTED + config ESP_LDO_RESERVE_SPI_NOR_FLASH + bool "Reserve one LDO regulator channel for SPI NOR Flash (READ HELP)" + default y + help + The LDO channel 1 can be used to power the SPI Flash chip, + because the channel 1 is enabled by default after power on reset. + If your SPI flash 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_SPI_NOR_FLASH_DOMAIN int "LDO regulator channel that used to power SPI NOR Flash (READ HELP)" + depends on ESP_LDO_RESERVE_SPI_NOR_FLASH default 1 - range -1 4 + range 1 1 help - The internal LDO regulator can be used to power the SPI Flash specific power domain. - This option is to select which LDO channel to connect to that domain. - Please set this option correctly according to your schematic. - Set to -1 if the Flash is using any external power supply. + Select which LDO channel to connect to the SPI Flash chip. choice ESP_LDO_VOLTAGE_SPI_NOR_FLASH_DOMAIN prompt "SPI NOR Flash power domain voltage" - depends on ESP_LDO_CHAN_SPI_NOR_FLASH_DOMAIN != -1 + depends on ESP_LDO_RESERVE_SPI_NOR_FLASH default ESP_LDO_VOLTAGE_SPI_NOR_FLASH_3300_MV help Select the voltage used by the Flash power domain. @@ -26,19 +33,25 @@ menu "LDO Regulator Configurations" int default 3300 if ESP_LDO_VOLTAGE_SPI_NOR_FLASH_3300_MV + config ESP_LDO_RESERVE_PSRAM + bool "Reserve one LDO regulator channel for PSRAM (READ HELP)" + default y + help + The LDO channel 2 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 2 - range -1 4 + range 2 2 help - The internal LDO regulator can be used to power the PSRAM specific power domain. - This option is to select which LDO channel to connect to that domain. - Please set this option correctly according to your schematic. - Set to -1 if the PSRAM is using any external power supply. + 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_CHAN_PSRAM_DOMAIN != -1 + depends on ESP_LDO_RESERVE_PSRAM default ESP_LDO_VOLTAGE_PSRAM_1900_MV help Select the voltage used by the PSRAM power domain. diff --git a/components/hal/esp32p4/include/hal/ldo_ll.h b/components/hal/esp32p4/include/hal/ldo_ll.h index a047487ae3..6e707890ad 100644 --- a/components/hal/esp32p4/include/hal/ldo_ll.h +++ b/components/hal/esp32p4/include/hal/ldo_ll.h @@ -4,29 +4,19 @@ * SPDX-License-Identifier: Apache-2.0 */ -/******************************************************************************* - * NOTICE - * The ll is not public api, don't use in application code. - * See readme.md in hal/include/hal/readme.md - ******************************************************************************/ - #pragma once #include #include #include "esp_bit_defs.h" -#include "hal/assert.h" +#include "hal/misc.h" #include "soc/pmu_struct.h" #ifdef __cplusplus extern "C" { #endif -/** - * LDO capabilities - */ -#define LDO_LL_UNIT_NUM 4 - +#define LDO_LL_NUM_UNITS 4 // NUmber of LDO units #define LDO_LL_ADJUSTABLE_CHAN_MASK 0x0F // all the 4 channels can be adjustable #define LDO_LL_MAX_VOLTAGE_MV 3300 @@ -48,91 +38,186 @@ extern "C" { #define LDO_ID2UNIT(ldo_id) ((ldo_id) - 1) /** - * @brief Check if a LDO ID is valid + * @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 * - * @return True for valid + * @return True for valid, false for invalid */ __attribute__((always_inline)) -static inline bool ldo_ll_is_valid_ldo_id(int ldo_id) +static inline bool ldo_ll_is_valid_ldo_channel(int ldo_chan) { - return ((ldo_id > 0) && (ldo_id <= LDO_LL_UNIT_NUM)); + return (ldo_chan > 0) && (ldo_chan <= LDO_LL_NUM_UNITS); } /** - * @brief Enable a LDO + * @brief Convert voltage to dref and mul value * - * @param ldo_id LDO ID + * @note Vref = (dref < 9)?(0.5+dref*0.05):(1+(dref-9)*0.1) + * @note Vout = (Vref*K+Vos)*(1+0.25*mul*C), K, Vos, C are constants saved in the eFuse, for calibration + * + * @param ldo_unit LDO unit + * @param voltage_mv Voltage in mV + * @param dref Returned dref value + * @param mul Returned mul value + */ +__attribute__((always_inline)) +static inline void ldo_ll_voltage_to_dref_mul(int ldo_unit, int voltage_mv, uint8_t *dref, uint8_t *mul) +{ + // TODO [IDF-10754]: also take the calibration parameters into account + if (voltage_mv <= 500) { + *dref = 0; + *mul = 0; + } else if (voltage_mv <= 900) { + *mul = 0; + *dref = (voltage_mv - LDO_LL_EXT_LDO_DREF_VOL_L_BASE) / LDO_LL_EXT_LDO_DREF_VOL_L_STEP; + } else if (voltage_mv <= 1600) { + *mul = 1; + *dref = 6 + (voltage_mv - LDO_LL_EXT_LDO_DREF_VOL_H_BASE) / LDO_LL_EXT_LDO_DREF_VOL_H_STEP; + } else if (voltage_mv <= 2000) { + *mul = 4; + *dref = (voltage_mv / 2 - LDO_LL_EXT_LDO_DREF_VOL_L_BASE) / LDO_LL_EXT_LDO_DREF_VOL_L_STEP; + } else if (voltage_mv <= 3200) { + *mul = 4; + *dref = 9 + (voltage_mv / 2 - LDO_LL_EXT_LDO_DREF_VOL_H_BASE) / LDO_LL_EXT_LDO_DREF_VOL_H_STEP; + } else { + *mul = 7; + *dref = 15; + } +} + +/** + * @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) +{ + uint8_t index_array[LDO_LL_NUM_UNITS] = {0, 3, 1, 4}; + /* + * force_tieh_sel: + * - 0: efuse, i.e. by hardware + * - 1: tieh_sel, i.e. by software + */ + PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo.force_tieh_sel = owner; +} + +/** + * @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_id, bool enable) +static inline void ldo_ll_enable(int ldo_unit, bool enable) { - HAL_ASSERT(ldo_id < LDO_LL_UNIT_NUM); - uint8_t index_array[LDO_LL_UNIT_NUM] = {0,3,1,4}; - PMU.ext_ldo[index_array[ldo_id]].pmu_ext_ldo.xpd = enable; + uint8_t index_array[LDO_LL_NUM_UNITS] = {0, 3, 1, 4}; + PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo.xpd = enable; } /** - * @brief Enable a LDO + * @brief Adjust voltage of a LDO unit * - * @param ldo_id LDO ID - * @param voltage_mv Voltage in mV + * @param ldo_unit LDO unit + * @param dref A parameter which controls the internal reference voltage + * @param mul Multiply factor */ __attribute__((always_inline)) -static inline void ldo_ll_set_output_voltage_mv(int ldo_id, int voltage_mv) +static inline void ldo_ll_adjust_voltage(int ldo_unit, uint8_t dref, uint8_t mul) { - int dref = 0, mul = 0; - + uint8_t index_array[LDO_LL_NUM_UNITS] = {0, 3, 1, 4}; + PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo_ana.dref = dref; + PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo_ana.mul = mul; /** - * Vout = dref * mul + * tieh: + * - 0: Vref * Mul + * - 1: 3.3V * - * mul reg[2:0]: - * mul = 1~2.75, step = 0.25 - - * dref reg[3:0]: - * 0~8 : dref = 0.5V ~ 0.9V, step 50mV - * 9~15 : dref = 1.0V ~ 1.6V, step 100mV - */ - if (voltage_mv <= 500) { - dref = 0; - mul = 0; - } else if (voltage_mv <= 900) { - mul = 0; - dref = (voltage_mv - LDO_LL_EXT_LDO_DREF_VOL_L_BASE) / LDO_LL_EXT_LDO_DREF_VOL_L_STEP; - } else if (voltage_mv <= 1600) { - mul = 1; - dref = 6 + (voltage_mv - LDO_LL_EXT_LDO_DREF_VOL_H_BASE) / LDO_LL_EXT_LDO_DREF_VOL_H_STEP; - } else if (voltage_mv <= 2000) { - mul = 4; - dref = (voltage_mv / 2 - LDO_LL_EXT_LDO_DREF_VOL_L_BASE) / LDO_LL_EXT_LDO_DREF_VOL_L_STEP; - } else if (voltage_mv <= 3200) { - mul = 4; - dref = 9 + (voltage_mv / 2 - LDO_LL_EXT_LDO_DREF_VOL_H_BASE) / LDO_LL_EXT_LDO_DREF_VOL_H_STEP; - } else { - mul = 7; - dref = 15; - } - /** * tieh_sel: * - 0: tieh; * - 1: sdmmc0_tieh; * - 2: 3.3V; * - 3: sdmmc1_tieh; - * - * tieh: - * - 0: dref * mul - * - 1: 3.3V - * - * force_tieh_sel: - * - 0: efuse - * - 1: tieh_sel */ - uint8_t index_array[LDO_LL_UNIT_NUM] = {0,3,1,4}; - PMU.ext_ldo[index_array[ldo_id]].pmu_ext_ldo.tieh_sel = 0; - PMU.ext_ldo[index_array[ldo_id]].pmu_ext_ldo.tieh = 0; - PMU.ext_ldo[index_array[ldo_id]].pmu_ext_ldo.force_tieh_sel = 1; - PMU.ext_ldo[index_array[ldo_id]].pmu_ext_ldo_ana.dref = dref; - PMU.ext_ldo[index_array[ldo_id]].pmu_ext_ldo_ana.mul = mul; + PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo.tieh_sel = 0; + PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo.tieh = 0; +} + +/** + * @brief Enable power on delay of a LDO unit + * + * @param ldo_unit LDO unit + * @param enable True: enable; False: disable + */ +__attribute__((always_inline)) +static inline void ldo_ll_enable_power_on_delay(int ldo_unit, bool enable) +{ + uint8_t index_array[LDO_LL_NUM_UNITS] = {0, 3, 1, 4}; + PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo.tieh_pos_en = enable; +} + +/** + * @brief Enable power off delay of a LDO unit + * + * @param ldo_unit LDO unit + * @param enable True: enable; False: disable + */ +__attribute__((always_inline)) +static inline void ldo_ll_enable_power_off_delay(int ldo_unit, bool enable) +{ + uint8_t index_array[LDO_LL_NUM_UNITS] = {0, 3, 1, 4}; + PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo.tieh_neg_en = enable; +} + +/** + * @brief Set power on delay target of a LDO unit + * + * @param ldo_unit LDO unit + * @param target0 Target 0 + * @param target1 Target 1 + */ +__attribute__((always_inline)) +static inline void ldo_ll_set_delay_target(int ldo_unit, uint8_t target0, uint8_t target1) +{ + uint8_t index_array[LDO_LL_NUM_UNITS] = {0, 3, 1, 4}; + HAL_FORCE_MODIFY_U32_REG_FIELD(PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo, target0, target0); + HAL_FORCE_MODIFY_U32_REG_FIELD(PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo, target1, target1); +} + +/** + * @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) +{ + uint8_t index_array[LDO_LL_NUM_UNITS] = {0, 3, 1, 4}; + PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo_ana.en_cur_lim = 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) +{ + uint8_t index_array[LDO_LL_NUM_UNITS] = {0, 3, 1, 4}; + PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo_ana.en_vdet = enable; } #ifdef __cplusplus diff --git a/components/spi_flash/esp_flash_spi_init.c b/components/spi_flash/esp_flash_spi_init.c index 7c7235eab6..9f6c54678f 100644 --- a/components/spi_flash/esp_flash_spi_init.c +++ b/components/spi_flash/esp_flash_spi_init.c @@ -437,15 +437,18 @@ esp_err_t esp_flash_app_init(void) // Acquire the LDO channel used by the SPI NOR flash // in case the LDO voltage is changed by other users -#if defined(CONFIG_ESP_LDO_CHAN_SPI_NOR_FLASH_DOMAIN) && CONFIG_ESP_LDO_CHAN_SPI_NOR_FLASH_DOMAIN != -1 +#if CONFIG_ESP_LDO_RESERVE_SPI_NOR_FLASH static esp_ldo_channel_handle_t s_ldo_chan = NULL; esp_ldo_channel_config_t ldo_config = { .chan_id = CONFIG_ESP_LDO_CHAN_SPI_NOR_FLASH_DOMAIN, .voltage_mv = CONFIG_ESP_LDO_VOLTAGE_SPI_NOR_FLASH_DOMAIN, + .flags = { + .owned_by_hw = true, // LDO output is totally controlled by hardware + }, }; err = esp_ldo_acquire_channel(&ldo_config, &s_ldo_chan); if (err != ESP_OK) return err; -#endif +#endif // CONFIG_ESP_LDO_RESERVE_SPI_NOR_FLASH spi_flash_init_lock(); spi_flash_guard_set(&g_flash_guard_default_ops);