mirror of
https://github.com/espressif/esp-idf
synced 2025-03-10 09:39:10 -04:00
Merge branch 'change/esp_ldo_reserve_for_spi_flash' into 'master'
LDO calibration on ESP32-P4 Closes IDF-10754 See merge request espressif/esp-idf!32347
This commit is contained in:
commit
05beacad73
@ -24,6 +24,7 @@ typedef struct ldo_regulator_channel_t {
|
||||
int ref_cnt;
|
||||
struct {
|
||||
uint32_t adjustable : 1;
|
||||
uint32_t bypass : 1;
|
||||
} flags;
|
||||
} ldo_regulator_channel_t;
|
||||
|
||||
@ -83,7 +84,7 @@ esp_err_t esp_ldo_acquire_channel(const esp_ldo_channel_config_t *config, esp_ld
|
||||
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);
|
||||
ldo_ll_adjust_voltage(unit_id, dref, mul, config->flags.bypass);
|
||||
// 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
|
||||
@ -94,6 +95,7 @@ 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);
|
||||
@ -155,7 +157,7 @@ esp_err_t esp_ldo_channel_adjust_voltage(esp_ldo_channel_handle_t chan, int volt
|
||||
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);
|
||||
ldo_ll_adjust_voltage(unit_id, dref, mul, chan->flags.bypass);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
@ -30,7 +30,8 @@ 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 */
|
||||
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 */
|
||||
} flags; /*!< Flags for the LDO channel */
|
||||
} esp_ldo_channel_config_t;
|
||||
|
||||
|
@ -25,7 +25,7 @@ TEST_CASE("LDO channel acquire and release (no adjustable)", "[LDO]")
|
||||
TEST_ASSERT_EQUAL(success_ldo_chans[0], success_ldo_chans[1]);
|
||||
TEST_ASSERT_EQUAL(success_ldo_chans[0], success_ldo_chans[2]);
|
||||
// can't acquire with a different voltage
|
||||
ldo_chan_config.voltage_mv = 3300;
|
||||
ldo_chan_config.voltage_mv = 2500;
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_ldo_acquire_channel(&ldo_chan_config, &fail_ldo_chan));
|
||||
// the channel has been acquired as "not adjustable" before, so we can't acquire it as "adjustable" again
|
||||
ldo_chan_config = (esp_ldo_channel_config_t) {
|
||||
@ -36,7 +36,7 @@ TEST_CASE("LDO channel acquire and release (no adjustable)", "[LDO]")
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_ldo_acquire_channel(&ldo_chan_config, &fail_ldo_chan));
|
||||
|
||||
// can't change the voltage for a non-adjustable channel
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, esp_ldo_channel_adjust_voltage(success_ldo_chans[0], 3300));
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, esp_ldo_channel_adjust_voltage(success_ldo_chans[0], 1900));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
TEST_ESP_OK(esp_ldo_release_channel(success_ldo_chans[i]));
|
||||
@ -62,7 +62,7 @@ TEST_CASE("LDO channel acquire and release (adjustable)", "[LDO]")
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_ldo_acquire_channel(&ldo_chan_config, &fail_ldo_chan));
|
||||
|
||||
// can change voltage for an adjustable channel
|
||||
TEST_ESP_OK(esp_ldo_channel_adjust_voltage(success_ldo_chan, 3300));
|
||||
TEST_ESP_OK(esp_ldo_channel_adjust_voltage(success_ldo_chan, 2500));
|
||||
TEST_ESP_OK(esp_ldo_release_channel(success_ldo_chan));
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ TEST_CASE("LDO channel state dump", "[LDO][manual][ignore]")
|
||||
esp_ldo_channel_handle_t success_ldo_chans[3] = {};
|
||||
esp_ldo_channel_config_t ldo_chan_config = {
|
||||
.chan_id = 2,
|
||||
.voltage_mv = 1800,
|
||||
.voltage_mv = 1900,
|
||||
};
|
||||
TEST_ESP_OK(esp_ldo_acquire_channel(&ldo_chan_config, &success_ldo_chans[0]));
|
||||
|
||||
|
@ -73,7 +73,7 @@ __attribute__((always_inline)) static inline bool efuse_ll_get_disable_wafer_ver
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_blk_version_major(void)
|
||||
{
|
||||
return EFUSE.rd_mac_sys_2.disable_blk_version_major;
|
||||
return EFUSE.rd_mac_sys_2.blk_version_major;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_blk_version_minor(void)
|
||||
@ -83,7 +83,7 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_blk_version_m
|
||||
|
||||
__attribute__((always_inline)) static inline bool efuse_ll_get_disable_blk_version_major(void)
|
||||
{
|
||||
return EFUSE.rd_mac_sys_2.blk_version_major;
|
||||
return EFUSE.rd_mac_sys_2.disable_blk_version_major;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_chip_ver_pkg(void)
|
||||
|
@ -14,29 +14,21 @@
|
||||
#include "hal/efuse_hal.h"
|
||||
#include "hal/pmu_types.h"
|
||||
#include "soc/pmu_struct.h"
|
||||
#include "soc/efuse_struct.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#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_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
|
||||
|
||||
/**
|
||||
* LDO LL macros, these macros are in the unit of mV
|
||||
*/
|
||||
#define LDO_LL_EXT_LDO_DREF_VOL_H_BASE 1000
|
||||
#define LDO_LL_EXT_LDO_DREF_VOL_H_STEP 100
|
||||
#define LDO_LL_EXT_LDO_DREF_VOL_L_BASE 500
|
||||
#define LDO_LL_EXT_LDO_DREF_VOL_L_STEP 50
|
||||
#define LDO_LL_EXT_LDO_MUL_VOL_BASE 1000
|
||||
#define LDO_LL_EXT_LDO_MUL_VOL_STEP 250
|
||||
|
||||
/**
|
||||
* LDO ID to real unit ID
|
||||
* @brief In the analog design, the LDO output "channel" is index from 1, i.e., VO1, VO2, VO3, VO4.
|
||||
* But in software, we mapped them to "LDO unit", which is index from 0, i.e., 0, 1, 2, 3.
|
||||
*/
|
||||
#define LDO_ID2UNIT(ldo_id) ((ldo_id) - 1)
|
||||
|
||||
@ -51,6 +43,7 @@ typedef enum {
|
||||
/**
|
||||
* @brief Check if a LDO channel is valid
|
||||
*
|
||||
* @param ldo_chan LDO channel ID, note, this is indexed from 1
|
||||
* @return True for valid, false for invalid
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
@ -73,26 +66,76 @@ static inline bool ldo_ll_is_valid_ldo_channel(int ldo_chan)
|
||||
__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;
|
||||
uint8_t efuse_k = 0;
|
||||
uint8_t efuse_vos = 0;
|
||||
uint8_t efuse_c = 0;
|
||||
// to avoid using FPU, enlarge the constants by 1000 as fixed point
|
||||
int K_1000 = 1000;
|
||||
int Vos_1000 = 0;
|
||||
int C_1000 = 1000;
|
||||
|
||||
if (efuse_hal_blk_version() >= 1) {
|
||||
// load the calibration values from the eFuse
|
||||
if (ldo_unit == 2) {
|
||||
efuse_k = EFUSE.rd_mac_sys_3.ldo_vo3_k;
|
||||
efuse_vos = EFUSE.rd_mac_sys_3.ldo_vo3_vos;
|
||||
efuse_c = EFUSE.rd_mac_sys_3.ldo_vo3_c;
|
||||
}
|
||||
if (ldo_unit == 3) {
|
||||
efuse_k = (EFUSE.rd_mac_sys_4.ldo_vo4_k_1 << 6) + EFUSE.rd_mac_sys_3.ldo_vo4_k;
|
||||
efuse_vos = EFUSE.rd_mac_sys_4.ldo_vo4_vos;
|
||||
efuse_c = EFUSE.rd_mac_sys_4.ldo_vo4_c;
|
||||
}
|
||||
// convert the eFuse calibration values to fixed point, note these values are signed
|
||||
if (efuse_k) {
|
||||
K_1000 = efuse_k & 0x80 ? -1 * (efuse_k & 0x7F) + 975 : efuse_k + 975;
|
||||
}
|
||||
if (efuse_vos) {
|
||||
Vos_1000 = efuse_vos & 0x20 ? -1 * (efuse_vos & 0x1F) - 3 : efuse_vos - 3;
|
||||
}
|
||||
if (efuse_c) {
|
||||
C_1000 = efuse_c & 0x20 ? -1 * (efuse_c & 0x1F) + 990 : efuse_c + 990;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (efuse_hal_blk_version() >= 1) {
|
||||
// For unit0 and unit1, the mul and dref value are calibrated and saved in the efuse, load them when available
|
||||
if (ldo_unit == 0 && voltage_mv == 1800) {
|
||||
if (EFUSE.rd_mac_sys_2.ldo_vo1_dref && EFUSE.rd_mac_sys_3.ldo_vo1_mul) {
|
||||
matched_mul = EFUSE.rd_mac_sys_3.ldo_vo1_mul;
|
||||
matched_dref = EFUSE.rd_mac_sys_2.ldo_vo1_dref;
|
||||
}
|
||||
}
|
||||
if (ldo_unit == 1 && voltage_mv == 1900) {
|
||||
if (EFUSE.rd_mac_sys_2.ldo_vo2_dref && EFUSE.rd_mac_sys_3.ldo_vo2_mul) {
|
||||
matched_mul = EFUSE.rd_mac_sys_3.ldo_vo2_mul;
|
||||
matched_dref = EFUSE.rd_mac_sys_2.ldo_vo2_dref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*dref = matched_dref;
|
||||
*mul = matched_mul;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,6 +156,39 @@ static inline void ldo_ll_set_owner(int ldo_unit, ldo_ll_unit_owner_t owner)
|
||||
* - 1: tieh_sel, i.e. by software
|
||||
*/
|
||||
PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo.force_tieh_sel = owner;
|
||||
/**
|
||||
* tieh_sel:
|
||||
* - 0: tieh;
|
||||
* - 1: sdmmc0_tieh;
|
||||
* - 2: 3.3V;
|
||||
* - 3: sdmmc1_tieh;
|
||||
*/
|
||||
PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo.tieh_sel = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 bypass True: bypass; False: not bypass.
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void ldo_ll_adjust_voltage(int ldo_unit, uint8_t dref, uint8_t mul, bool bypass)
|
||||
{
|
||||
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_ana.dref = dref;
|
||||
PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo_ana.mul = mul;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -132,34 +208,6 @@ static inline void ldo_ll_enable(int ldo_unit, bool enable)
|
||||
PMU.ext_ldo[index_array[ldo_unit]].pmu_ext_ldo.xpd = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adjust voltage of a LDO unit
|
||||
*
|
||||
* @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_adjust_voltage(int ldo_unit, uint8_t dref, uint8_t mul)
|
||||
{
|
||||
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;
|
||||
/**
|
||||
* tieh:
|
||||
* - 0: Vref * Mul
|
||||
* - 1: 3.3V
|
||||
*
|
||||
* tieh_sel:
|
||||
* - 0: tieh;
|
||||
* - 1: sdmmc0_tieh;
|
||||
* - 2: 3.3V;
|
||||
* - 3: sdmmc1_tieh;
|
||||
*/
|
||||
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
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user