feat(ldo): support output rail voltage (3.3V)

This commit is contained in:
morris 2024-12-27 16:13:59 +08:00
parent cfe7021801
commit 85f8f25b30
4 changed files with 32 additions and 21 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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));
}

View File

@ -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;
}