From ad1237a1e46c43122986a37f55e0aa2ed2030324 Mon Sep 17 00:00:00 2001 From: morris Date: Fri, 27 Dec 2024 16:13:59 +0800 Subject: [PATCH] feat(ldo): support output rail voltage (3.3V) --- .../esp_hw_support/ldo/esp_ldo_regulator.c | 32 +++++++++++-------- .../ldo/include/esp_ldo_regulator.h | 2 +- .../main/test_ldo.c | 1 + components/hal/esp32p4/include/hal/ldo_ll.h | 18 +++++++---- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/components/esp_hw_support/ldo/esp_ldo_regulator.c b/components/esp_hw_support/ldo/esp_ldo_regulator.c index 63595dbbf2..ce87efb23b 100644 --- a/components/esp_hw_support/ldo/esp_ldo_regulator.c +++ b/components/esp_hw_support/ldo/esp_ldo_regulator.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,7 +11,6 @@ #include "freertos/FreeRTOS.h" #include "esp_log.h" #include "esp_check.h" -#include "esp_heap_caps.h" #include "soc/soc_caps.h" #include "hal/ldo_ll.h" #include "esp_ldo_regulator.h" @@ -24,7 +23,6 @@ typedef struct ldo_regulator_channel_t { int ref_cnt; struct { uint32_t adjustable : 1; - uint32_t bypass : 1; } flags; } ldo_regulator_channel_t; @@ -32,6 +30,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 +// Allocate LDO channel memory statically +// LDO is a fundamental module and should be usable even without a heap allocator static ldo_regulator_channel_t s_ldo_channels[LDO_LL_NUM_UNITS] = { [0 ... LDO_LL_NUM_UNITS - 1] = { .chan_id = -1, @@ -45,8 +45,11 @@ esp_err_t esp_ldo_acquire_channel(const esp_ldo_channel_config_t *config, esp_ld { ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); 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); + // check if the target voltage is within the recommended range + if (!((config->voltage_mv == LDO_LL_RAIL_VOLTAGE_MV) || + (config->voltage_mv >= LDO_LL_RECOMMEND_MIN_VOLTAGE_MV && config->voltage_mv <= LDO_LL_RECOMMEND_MAX_VOLTAGE_MV))) { + ESP_LOGW(TAG, "The voltage value %d is out of the recommended range [%d, %d]", config->voltage_mv, LDO_LL_RECOMMEND_MIN_VOLTAGE_MV, LDO_LL_RECOMMEND_MAX_VOLTAGE_MV); + } int unit_id = LDO_ID2UNIT(config->chan_id); ldo_regulator_channel_t *channel = &s_ldo_channels[unit_id]; @@ -82,9 +85,10 @@ esp_err_t esp_ldo_acquire_channel(const esp_ldo_channel_config_t *config, esp_ld // 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; + bool use_rail_voltage = false; // 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, config->flags.bypass); + 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); // 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 @@ -95,7 +99,6 @@ esp_err_t esp_ldo_acquire_channel(const esp_ldo_channel_config_t *config, esp_ld channel->ref_cnt++; channel->voltage_mv = config->voltage_mv; channel->flags.adjustable = config->flags.adjustable; - channel->flags.bypass = config->flags.bypass; channel->chan_id = config->chan_id; } portEXIT_CRITICAL(&s_spinlock); @@ -143,9 +146,11 @@ esp_err_t esp_ldo_channel_adjust_voltage(esp_ldo_channel_handle_t chan, int volt { 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"); - // check if the voltage is within the valid range - ESP_RETURN_ON_FALSE(voltage_mv >= LDO_LL_MIN_VOLTAGE_MV && voltage_mv <= LDO_LL_MAX_VOLTAGE_MV, - ESP_ERR_INVALID_ARG, TAG, "invalid voltage value: %d", voltage_mv); + // 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))) { + ESP_LOGW(TAG, "The voltage value %d is out of the recommended range [%d, %d]", voltage_mv, LDO_LL_RECOMMEND_MIN_VOLTAGE_MV, LDO_LL_RECOMMEND_MAX_VOLTAGE_MV); + } // About Thread Safety: // because there won't be more than 1 consumer for the same adjustable LDO channel (guaranteed by esp_ldo_acquire_channel) @@ -155,9 +160,10 @@ esp_err_t esp_ldo_channel_adjust_voltage(esp_ldo_channel_handle_t chan, int volt int unit_id = LDO_ID2UNIT(chan->chan_id); uint8_t dref = 0; uint8_t mul = 0; + bool use_rail_voltage = false; // 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, chan->flags.bypass); + ldo_ll_voltage_to_dref_mul(unit_id, voltage_mv, &dref, &mul, &use_rail_voltage); + ldo_ll_adjust_voltage(unit_id, dref, mul, use_rail_voltage); return ESP_OK; } 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 3bf31507cd..8db67237ca 100644 --- a/components/esp_hw_support/ldo/include/esp_ldo_regulator.h +++ b/components/esp_hw_support/ldo/include/esp_ldo_regulator.h @@ -31,7 +31,7 @@ typedef struct { 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 can be overridden by hardware (e.g. eFuse) */ - uint32_t bypass: 1; /*!< Whether to bypass the regulator, i.e., the input voltage is sourced directly to the output */ + uint32_t bypass: 1 __attribute__((deprecated)); /*!< Whether to bypass the regulator, i.e., the input voltage is sourced directly to the output */ } flags; /*!< Flags for the LDO channel */ } esp_ldo_channel_config_t; diff --git a/components/esp_hw_support/test_apps/esp_hw_support_unity_tests/main/test_ldo.c b/components/esp_hw_support/test_apps/esp_hw_support_unity_tests/main/test_ldo.c index f2231dc509..fbf38beb4a 100644 --- a/components/esp_hw_support/test_apps/esp_hw_support_unity_tests/main/test_ldo.c +++ b/components/esp_hw_support/test_apps/esp_hw_support_unity_tests/main/test_ldo.c @@ -63,6 +63,7 @@ TEST_CASE("LDO channel acquire and release (adjustable)", "[LDO]") // can change voltage for an adjustable channel TEST_ESP_OK(esp_ldo_channel_adjust_voltage(success_ldo_chan, 2500)); + TEST_ESP_OK(esp_ldo_channel_adjust_voltage(success_ldo_chan, 3300)); TEST_ESP_OK(esp_ldo_release_channel(success_ldo_chan)); } diff --git a/components/hal/esp32p4/include/hal/ldo_ll.h b/components/hal/esp32p4/include/hal/ldo_ll.h index abfd44e012..2b443b49ba 100644 --- a/components/hal/esp32p4/include/hal/ldo_ll.h +++ b/components/hal/esp32p4/include/hal/ldo_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,8 +23,9 @@ extern "C" { #define LDO_LL_NUM_UNITS 4 // Number of LDO units #define LDO_LL_ADJUSTABLE_CHAN_MASK 0x0F // all the 4 channels are adjustable by setting "mul" and "dref" registers -#define LDO_LL_MAX_VOLTAGE_MV 3300 -#define LDO_LL_MIN_VOLTAGE_MV 500 +#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. @@ -62,9 +63,10 @@ static inline bool ldo_ll_is_valid_ldo_channel(int ldo_chan) * @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) +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) { uint8_t efuse_k = 0; uint8_t efuse_vos = 0; @@ -136,6 +138,8 @@ static inline void ldo_ll_voltage_to_dref_mul(int ldo_unit, int voltage_mv, uint *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); } /** @@ -175,10 +179,10 @@ static inline void ldo_ll_set_owner(int ldo_unit, ldo_ll_unit_owner_t owner) * @param ldo_unit LDO unit * @param dref A parameter which controls the internal reference voltage * @param mul Multiply factor - * @param bypass True: bypass; False: not bypass. + * @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 bypass) +static inline void ldo_ll_adjust_voltage(int ldo_unit, uint8_t dref, uint8_t mul, bool use_rail_voltage) { uint8_t index_array[LDO_LL_NUM_UNITS] = {0, 3, 1, 4}; /** @@ -186,7 +190,7 @@ static inline void ldo_ll_adjust_voltage(int ldo_unit, uint8_t dref, uint8_t mul * - 0: Vref * Mul * - 1: 3.3V */ - PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo.tieh = bypass; + PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo.tieh = use_rail_voltage; 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; }