Skip to content

Commit

Permalink
Merge branch 'feat/ldo_can_output_3v3' into 'master'
Browse files Browse the repository at this point in the history
feat(ldo): support output rail voltage (3.3V)

See merge request espressif/esp-idf!36031
  • Loading branch information
suda-morris committed Jan 3, 2025
2 parents b077618 + 85f8f25 commit cf8521a
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 21 deletions.
32 changes: 19 additions & 13 deletions components/esp_hw_support/ldo/esp_ldo_regulator.c
Original file line number Diff line number Diff line change
@@ -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
*/
Expand All @@ -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"
Expand All @@ -24,14 +23,15 @@ typedef struct ldo_regulator_channel_t {
int ref_cnt;
struct {
uint32_t adjustable : 1;
uint32_t bypass : 1;
} flags;
} ldo_regulator_channel_t;

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,
Expand All @@ -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];

Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion components/esp_hw_support/ldo/include/esp_ldo_regulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand Down
18 changes: 11 additions & 7 deletions components/hal/esp32p4/include/hal/ldo_ll.h
Original file line number Diff line number Diff line change
@@ -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
*/
Expand All @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -175,18 +179,18 @@ 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};
/**
* tieh:
* - 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;
}
Expand Down

0 comments on commit cf8521a

Please sign in to comment.