Merge branch 'feat/esp_idf_h4_clk_tree_management' into 'master'

Feat/esp idf h4 h21 clk tree management

Closes PM-858 and PM-653

See merge request espressif/esp-idf!50653
This commit is contained in:
Jiang Jiang Jian
2026-09-11 10:38:04 +08:00
30 changed files with 1039 additions and 438 deletions
@@ -24,16 +24,40 @@ extern "C" {
#endif
/**
* Enable or disable the clock gate for ref_40m.
* Enable or disable the clock gate for ref_8m.
* @param enable Enable / disable
*/
FORCE_INLINE_ATTR void _clk_gate_ll_ref_40m_clk_en(bool enable)
FORCE_INLINE_ATTR void _clk_gate_ll_ref_8m_clk_en(bool enable)
{
PCR.pll_div_clk_en.pll_40m_clk_en = enable;
PCR.pll_div_clk_en.pll_8m_clk_en = enable;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define clk_gate_ll_ref_40m_clk_en(...) (void)__DECLARE_RCC_ATOMIC_ENV; _clk_gate_ll_ref_40m_clk_en(__VA_ARGS__)
#define clk_gate_ll_ref_8m_clk_en(...) (void)__DECLARE_RCC_ATOMIC_ENV; _clk_gate_ll_ref_8m_clk_en(__VA_ARGS__)
/**
* Enable or disable the clock gate for ref_16m.
* @param enable Enable / disable
*/
FORCE_INLINE_ATTR void _clk_gate_ll_ref_16m_clk_en(bool enable)
{
PCR.pll_div_clk_en.pll_16m_clk_en = enable;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define clk_gate_ll_ref_16m_clk_en(...) (void)__DECLARE_RCC_ATOMIC_ENV; _clk_gate_ll_ref_16m_clk_en(__VA_ARGS__)
/**
* Enable or disable the clock gate for ref_32m.
* @param enable Enable / disable
*/
FORCE_INLINE_ATTR void _clk_gate_ll_ref_32m_clk_en(bool enable)
{
PCR.pll_div_clk_en.pll_32m_clk_en = enable;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define clk_gate_ll_ref_32m_clk_en(...) (void)__DECLARE_RCC_ATOMIC_ENV; _clk_gate_ll_ref_32m_clk_en(__VA_ARGS__)
/**
* Enable or disable the clock gate for ref_48m.
@@ -48,53 +72,28 @@ FORCE_INLINE_ATTR void _clk_gate_ll_ref_48m_clk_en(bool enable)
#define clk_gate_ll_ref_48m_clk_en(...) (void)__DECLARE_RCC_ATOMIC_ENV; _clk_gate_ll_ref_48m_clk_en(__VA_ARGS__)
/**
* Enable or disable the clock gate for ref_80m.
* Enable or disable the clock gate for ref_64m.
* @param enable Enable / disable
*/
FORCE_INLINE_ATTR void _clk_gate_ll_ref_80m_clk_en(bool enable)
FORCE_INLINE_ATTR void _clk_gate_ll_ref_64m_clk_en(bool enable)
{
PCR.pll_div_clk_en.pll_80m_clk_en = enable;
PCR.pll_div_clk_en.pll_64m_clk_en = enable;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define clk_gate_ll_ref_80m_clk_en(...) (void)__DECLARE_RCC_ATOMIC_ENV; _clk_gate_ll_ref_80m_clk_en(__VA_ARGS__)
#define clk_gate_ll_ref_64m_clk_en(...) (void)__DECLARE_RCC_ATOMIC_ENV; _clk_gate_ll_ref_64m_clk_en(__VA_ARGS__)
/**
* Enable or disable the clock gate for ref_120m.
* Enable or disable the clock gate for ref_96m.
* @param enable Enable / disable
*/
FORCE_INLINE_ATTR void _clk_gate_ll_ref_120m_clk_en(bool enable)
FORCE_INLINE_ATTR void _clk_gate_ll_ref_96m_clk_en(bool enable)
{
PCR.pll_div_clk_en.pll_120m_clk_en = enable;
PCR.pll_div_clk_en.pll_96m_clk_en = enable;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define clk_gate_ll_ref_120m_clk_en(...) (void)__DECLARE_RCC_ATOMIC_ENV; _clk_gate_ll_ref_120m_clk_en(__VA_ARGS__)
/**
* Enable or disable the clock gate for ref_160m.
* @param enable Enable / disable
*/
FORCE_INLINE_ATTR void _clk_gate_ll_ref_160m_clk_en(bool enable)
{
PCR.pll_div_clk_en.pll_160m_clk_en = enable;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define clk_gate_ll_ref_160m_clk_en(...) (void)__DECLARE_RCC_ATOMIC_ENV; _clk_gate_ll_ref_160m_clk_en(__VA_ARGS__)
/**
* Enable or disable the clock gate for ref_240m.
* @param enable Enable / disable
*/
FORCE_INLINE_ATTR void _clk_gate_ll_ref_240m_clk_en(bool enable)
{
PCR.pll_div_clk_en.pll_240m_clk_en = enable;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define clk_gate_ll_ref_240m_clk_en(...) (void)__DECLARE_RCC_ATOMIC_ENV; _clk_gate_ll_ref_240m_clk_en(__VA_ARGS__)
#define clk_gate_ll_ref_96m_clk_en(...) (void)__DECLARE_RCC_ATOMIC_ENV; _clk_gate_ll_ref_96m_clk_en(__VA_ARGS__)
/**
* @brief Configuration structure for peripheral clock gate settings
*/
@@ -68,6 +68,8 @@ typedef struct {
static inline __attribute__((always_inline)) void clk_ll_bbpll_enable(void)
{
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XPD_BBPLL | PMU_TIE_HIGH_XPD_BBPLL_I2C);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_GLOBAL_BBPLL_ICG);
SET_PERI_REG_MASK(PMU_HP_ACTIVE_HP_CK_POWER_REG, PMU_HP_ACTIVE_XPD_BBPLL_I2C | PMU_HP_ACTIVE_XPD_BBPLL);
}
/**
@@ -77,6 +79,7 @@ static inline __attribute__((always_inline)) void clk_ll_bbpll_disable(void)
{
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_GLOBAL_BBPLL_ICG) ;
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_XPD_BBPLL | PMU_TIE_LOW_XPD_BBPLL_I2C);
CLEAR_PERI_REG_MASK(PMU_HP_ACTIVE_HP_CK_POWER_REG, PMU_HP_ACTIVE_XPD_BBPLL_I2C | PMU_HP_ACTIVE_XPD_BBPLL);
}
/**
@@ -88,6 +91,7 @@ static inline __attribute__((always_inline)) void clk_ll_xtal_x2_enable(void)
CLEAR_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_GLOBAL_XTALX2_ICG);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XTALX2);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_GLOBAL_XTALX2_ICG);
SET_PERI_REG_MASK(PMU_HP_ACTIVE_XTAL_REG, PMU_HP_ACTIVE_XPD_XTALX2);
}
/**
@@ -98,6 +102,7 @@ static inline __attribute__((always_inline)) void clk_ll_xtal_x2_disable(void)
CLEAR_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XTALX2 | PMU_TIE_HIGH_GLOBAL_XTALX2_ICG);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_XPD_XTALX2);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_GLOBAL_XTALX2_ICG);
CLEAR_PERI_REG_MASK(PMU_HP_ACTIVE_XTAL_REG, PMU_HP_ACTIVE_XPD_XTALX2);
}
/**
@@ -66,6 +66,8 @@ typedef struct {
static inline __attribute__((always_inline)) void clk_ll_bbpll_enable(void)
{
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XPD_BBPLL | PMU_TIE_HIGH_XPD_BBPLL_I2C);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_GLOBAL_BBPLL_ICG);
SET_PERI_REG_MASK(PMU_HP_ACTIVE_HP_CK_POWER_REG, PMU_HP_ACTIVE_XPD_BBPLL_I2C | PMU_HP_ACTIVE_XPD_BBPLL);
}
/**
@@ -75,6 +77,7 @@ static inline __attribute__((always_inline)) void clk_ll_bbpll_disable(void)
{
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_GLOBAL_BBPLL_ICG) ;
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_XPD_BBPLL | PMU_TIE_LOW_XPD_BBPLL_I2C);
CLEAR_PERI_REG_MASK(PMU_HP_ACTIVE_HP_CK_POWER_REG, PMU_HP_ACTIVE_XPD_BBPLL_I2C | PMU_HP_ACTIVE_XPD_BBPLL);
}
/**
@@ -94,6 +97,7 @@ static inline __attribute__((always_inline)) void clk_ll_xtal_x2_enable(void)
CLEAR_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_GLOBAL_XTALX2_ICG);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XTALX2);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_GLOBAL_XTALX2_ICG);
SET_PERI_REG_MASK(PMU_HP_ACTIVE_XTAL_REG, PMU_HP_ACTIVE_XPD_XTALX2);
}
/**
@@ -104,6 +108,7 @@ static inline __attribute__((always_inline)) void clk_ll_xtal_x2_disable(void)
CLEAR_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XTALX2 | PMU_TIE_HIGH_GLOBAL_XTALX2_ICG);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_XPD_XTALX2);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_GLOBAL_XTALX2_ICG);
CLEAR_PERI_REG_MASK(PMU_HP_ACTIVE_XTAL_REG, PMU_HP_ACTIVE_XPD_XTALX2);
}
/**
@@ -94,6 +94,24 @@ FORCE_INLINE_ATTR void pmu_ll_hp_set_clk_power(pmu_dev_t *hw, pmu_hp_mode_t mode
hw->hp_sys[mode].clk_power.val = xpd_flag;
}
/**
* @brief Set the power and isolation of the analog i2c master shared by all the PLLs
*
* @param hw Beginning address of the peripheral registers.
* @param mode The pmu mode
* @param xpd_bb_i2c Power up the analog i2c master
* @param iso_en Isolate the analog i2c master interface
* @param retention Retain the analog i2c master registers
*
* @return None
*/
FORCE_INLINE_ATTR void pmu_ll_hp_set_ana_i2c_power(pmu_dev_t *hw, pmu_hp_mode_t mode, bool xpd_bb_i2c, bool iso_en, bool retention)
{
hw->hp_sys[mode].clk_power.xpd_bb_i2c = xpd_bb_i2c;
hw->hp_sys[mode].clk_power.i2c_iso_en = iso_en;
hw->hp_sys[mode].clk_power.i2c_retention = retention;
}
FORCE_INLINE_ATTR void pmu_ll_hp_set_dcdc_ccm_enable(pmu_dev_t *hw, pmu_hp_mode_t mode, bool enable)
{
hw->hp_sys[mode].bias.dcdc_ccm_enb = enable;
@@ -95,6 +95,29 @@ FORCE_INLINE_ATTR void pmu_ll_hp_set_clk_power(pmu_dev_t *hw, pmu_hp_mode_t mode
hw->hp_sys[mode].clk_power.val = xpd_flag;
}
FORCE_INLINE_ATTR uint32_t pmu_ll_hp_get_clk_power(pmu_dev_t *hw, pmu_hp_mode_t mode)
{
return hw->hp_sys[mode].clk_power.val;
}
/**
* @brief Set the power and isolation of the analog i2c master shared by all the PLLs
*
* @param hw Beginning address of the peripheral registers.
* @param mode The pmu mode
* @param xpd_bb_i2c Power up the analog i2c master
* @param iso_en Isolate the analog i2c master interface
* @param retention Retain the analog i2c master registers
*
* @return None
*/
FORCE_INLINE_ATTR void pmu_ll_hp_set_ana_i2c_power(pmu_dev_t *hw, pmu_hp_mode_t mode, bool xpd_bb_i2c, bool iso_en, bool retention)
{
hw->hp_sys[mode].clk_power.xpd_bb_i2c = xpd_bb_i2c;
hw->hp_sys[mode].clk_power.i2c_iso_en = iso_en;
hw->hp_sys[mode].clk_power.i2c_retention = retention;
}
FORCE_INLINE_ATTR void pmu_ll_hp_set_xtal_xpd(pmu_dev_t *hw, pmu_hp_mode_t mode, bool xpd_xtal)
{
hw->hp_sys[mode].xtal.xpd_xtal = xpd_xtal;
@@ -105,6 +128,11 @@ FORCE_INLINE_ATTR void pmu_ll_hp_set_xtalx2_xpd(pmu_dev_t *hw, pmu_hp_mode_t mod
hw->hp_sys[mode].xtal.xpd_xtalx2 = xpd_xtalx2;
}
FORCE_INLINE_ATTR uint32_t pmu_ll_hp_get_xtalx2_xpd(pmu_dev_t *hw, pmu_hp_mode_t mode)
{
return hw->hp_sys[mode].xtal.xpd_xtalx2;
}
FORCE_INLINE_ATTR void pmu_ll_hp_set_bias_xpd(pmu_dev_t *hw, pmu_hp_mode_t mode, bool xpd_bias)
{
hw->hp_sys[mode].bias.xpd_bias = xpd_bias;
+2 -1
View File
@@ -56,8 +56,9 @@ entries:
pmu_sleep:pmu_sleep_get_wakup_retention_cost (noflash)
if PM_SLEEP_CLK_ICG_ENABLE = y && PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP != y:
pmu_sleep_clock_icg:pmu_sleep_clock_icg_config (noflash)
if IDF_TARGET_ESP32H4 = y && BT_LE_MODEM_STATE_ENABLED = y:
if IDF_TARGET_ESP32H4 = y && PM_MODEM_STATE_ENABLED = y:
pmu_sleep_power:pmu_sleep_power_analog_wait_config (noflash)
pmu_sleep_power:pmu_sleep_power_clock_config (noflash)
sleep_mspi (noflash)
if PM_SLP_IRAM_OPT = y && IDF_TARGET_ESP32S31 != y:
pmu_param:get_act_lp_dbias (noflash)
@@ -5,23 +5,123 @@
*/
#include <stdint.h>
#include <stdatomic.h>
#include <assert.h>
#include "sdkconfig.h"
#include "esp_clk_tree.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "esp_rom_sys.h"
#include "soc/rtc.h"
#include "soc/reset_reasons.h"
#include "hal/clk_gate_ll.h"
#include "hal/clk_tree_hal.h"
#include "hal/clk_tree_ll.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/critical_section.h"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
/* -------------------------------------------------------------------------- */
/* Fixed ref clocks: gate + static parent power */
/* -------------------------------------------------------------------------- */
typedef void (*esp_clk_tree_gate_fn_t)(bool enable);
typedef void (*esp_clk_tree_parent_fn_t)(bool enable);
typedef struct {
soc_module_clk_t clk_id;
esp_clk_tree_gate_fn_t set_gate;
esp_clk_tree_parent_fn_t parent_power;
} esp_clk_tree_gated_clk_t;
static void esp_clk_tree_parent_bbpll(bool enable)
{
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_BBPLL, enable);
}
static void esp_clk_tree_parent_xtal_x2(bool enable)
{
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_XTAL_X2, enable);
}
// static void esp_clk_tree_parent_rc_fast(bool enable)
// {
// if (enable) {
// rtc_dig_clk8m_enable();
// } else {
// rtc_dig_clk8m_disable();
// }
// }
DEFINE_CRIT_SECTION_LOCK_STATIC(s_clk_tree_spinlock);
/** Per soc_module_clk_t: record clock gate consumers */
static int16_t s_mod_clk_gate_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
/** Per soc_root_clk_circuit_t: record clock power consumers */
static int16_t s_root_pll_power_ref_cnt[SOC_ROOT_CIRCUIT_CLK_MAX] = { 0 };
static bool s_clk_tree_initialized = false;
static int16_t esp_clk_tree_root_pll_power_acquire(soc_root_clk_circuit_t clk_circuit)
{
int16_t prev;
assert(clk_circuit == SOC_ROOT_CIRCUIT_CLK_BBPLL || clk_circuit == SOC_ROOT_CIRCUIT_CLK_XTAL_X2);
esp_os_enter_critical(&s_clk_tree_spinlock);
prev = s_root_pll_power_ref_cnt[clk_circuit]++;
if (prev == 0) {
switch (clk_circuit) {
case SOC_ROOT_CIRCUIT_CLK_BBPLL:
clk_ll_bbpll_enable();
break;
case SOC_ROOT_CIRCUIT_CLK_XTAL_X2:
clk_ll_xtal_x2_enable();
break;
default:
break;
}
}
esp_os_exit_critical(&s_clk_tree_spinlock);
return prev;
}
static int16_t esp_clk_tree_root_pll_power_release(soc_root_clk_circuit_t clk_circuit)
{
int16_t prev;
assert(clk_circuit == SOC_ROOT_CIRCUIT_CLK_BBPLL || clk_circuit == SOC_ROOT_CIRCUIT_CLK_XTAL_X2);
esp_os_enter_critical(&s_clk_tree_spinlock);
prev = s_root_pll_power_ref_cnt[clk_circuit];
if (prev <= 0) {
esp_os_exit_critical(&s_clk_tree_spinlock);
ESP_EARLY_LOGW(TAG, "soc_root_clk_circuit_t %d disabled multiple times!!", clk_circuit);
return prev;
}
s_root_pll_power_ref_cnt[clk_circuit] = prev - 1;
if (prev == 1) {
switch (clk_circuit) {
case SOC_ROOT_CIRCUIT_CLK_BBPLL:
clk_ll_bbpll_disable();
break;
case SOC_ROOT_CIRCUIT_CLK_XTAL_X2:
clk_ll_xtal_x2_disable();
break;
default:
break;
}
}
esp_os_exit_critical(&s_clk_tree_spinlock);
return prev;
}
esp_err_t esp_clk_tree_src_get_freq_hz(soc_module_clk_t clk_src, esp_clk_tree_src_freq_precision_t precision,
uint32_t *freq_value)
uint32_t *freq_value)
{
ESP_RETURN_ON_FALSE(clk_src > 0 && clk_src < SOC_MOD_CLK_INVALID, ESP_ERR_INVALID_ARG, TAG, "unknown clk src");
ESP_RETURN_ON_FALSE(precision < ESP_CLK_TREE_SRC_FREQ_PRECISION_INVALID, ESP_ERR_INVALID_ARG, TAG, "unknown precision");
@@ -38,9 +138,11 @@ uint32_t *freq_value)
case SOC_MOD_CLK_PLL_F48M:
clk_src_freq = CLK_LL_PLL_48M_FREQ_MHZ * MHZ;
break;
case SOC_MOD_CLK_XTAL_X2:
case SOC_MOD_CLK_XTAL_X2_F64M:
clk_src_freq = CLK_LL_PLL_64M_FREQ_MHZ * MHZ;
break;
case SOC_MOD_CLK_BBPLL:
case SOC_MOD_CLK_PLL_F96M:
clk_src_freq = CLK_LL_PLL_96M_FREQ_MHZ * MHZ;
break;
@@ -67,119 +169,174 @@ uint32_t *freq_value)
esp_err_t esp_clk_tree_src_set_freq_hz(soc_module_clk_t clk_src, uint32_t expt_freq_value, uint32_t *ret_freq_value)
{
(void)clk_src; (void)expt_freq_value; (void)ret_freq_value;
ESP_RETURN_ON_FALSE(clk_src > 0 && clk_src < SOC_MOD_CLK_INVALID, ESP_ERR_INVALID_ARG, TAG, "unknown clk src");
ESP_RETURN_ON_FALSE(expt_freq_value > 0, ESP_ERR_INVALID_ARG, TAG, "invalid frequency");
(void)ret_freq_value;
return ESP_ERR_NOT_SUPPORTED;
}
static int16_t s_xtal_x2_ref_cnt = 0;
static int16_t s_bbpll_ref_cnt = 0;
void esp_clk_tree_initialize(void)
{
// Power
// In bootloader, flash clock source will always be switched to use XTAL_X2 clock
s_xtal_x2_ref_cnt++;
soc_cpu_clk_src_t cpu_clk_src_btld = clk_ll_cpu_get_src();
if (cpu_clk_src_btld == SOC_CPU_CLK_SRC_XTAL_X2) {
s_xtal_x2_ref_cnt++;
} else if (cpu_clk_src_btld == SOC_CPU_CLK_SRC_PLL) {
s_bbpll_ref_cnt++;
soc_reset_reason_t rst_reason = esp_rom_get_reset_reason(0);
soc_cpu_clk_src_t cpu_src = clk_ll_cpu_get_src();
bool cpu_reset = (rst_reason == RESET_REASON_CPU0_MWDT0) || (rst_reason == RESET_REASON_CPU0_MWDT1) ||
(rst_reason == RESET_REASON_CPU0_SW) || (rst_reason == RESET_REASON_CPU0_RTC_WDT) ||
(rst_reason == RESET_REASON_CPU0_JTAG);
if (!cpu_reset) {
/* Cold boot only: gate / power-down clocks not in use.
* Flash MSPI defaults to XTAL_X2_F64M — keep that root/gate alive. */
_clk_gate_ll_ref_8m_clk_en(false);
_clk_gate_ll_ref_16m_clk_en(false);
_clk_gate_ll_ref_32m_clk_en(false);
_clk_gate_ll_ref_96m_clk_en(false);
#if CONFIG_USJ_ENABLE_USB_SERIAL_JTAG || CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED
/* Keep BBPLL / ref_48m: USJ PHY still uses them after bootloader. */
#else
_clk_gate_ll_ref_48m_clk_en(false);
if (cpu_src != SOC_CPU_CLK_SRC_PLL) {
clk_ll_bbpll_disable();
}
#endif
}
// Gating
// PLL_F64M ++ for MSPI
}
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
if (clk_circuit == SOC_ROOT_CIRCUIT_CLK_XTAL_X2) {
return s_xtal_x2_ref_cnt > 0;
s_clk_tree_initialized = true;
#if CONFIG_USJ_ENABLE_USB_SERIAL_JTAG || CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED
/* Bootloader / USJ may keep PLL_F48M on; declare a permanent hold. */
esp_clk_tree_enable_src(SOC_MOD_CLK_PLL_F48M, true);
#endif
/* Flash + CPU: sync clk_tree refs with HW already selected at boot.
* Flash uses gated F64M; CPU holds the ungated XTAL_X2 / PLL root, not the F64M / F96M gates. */
esp_clk_tree_enable_src(SOC_MOD_CLK_XTAL_X2_F64M, true);
if (cpu_src == SOC_CPU_CLK_SRC_PLL) {
esp_clk_tree_enable_src(SOC_MOD_CLK_BBPLL, true);
} else if (cpu_src == SOC_CPU_CLK_SRC_XTAL_X2) {
esp_clk_tree_enable_src(SOC_MOD_CLK_XTAL_X2, true);
}
if (clk_circuit == SOC_ROOT_CIRCUIT_CLK_BBPLL) {
return s_bbpll_ref_cnt > 0;
}
return false;
}
bool esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool enable)
{
if (clk_circuit >= SOC_ROOT_CIRCUIT_CLK_MAX) {
return false;
}
bool toggled = false;
switch (clk_circuit) {
case SOC_ROOT_CIRCUIT_CLK_XTAL_X2:
if (enable) {
s_xtal_x2_ref_cnt++;
} else {
s_xtal_x2_ref_cnt--;
}
if (s_xtal_x2_ref_cnt == 1) {
clk_ll_xtal_x2_enable();
toggled = true;
} else if (s_xtal_x2_ref_cnt == 0) {
clk_ll_xtal_x2_disable();
toggled = true;
}
assert(s_xtal_x2_ref_cnt >= 0);
break;
case SOC_ROOT_CIRCUIT_CLK_BBPLL:
case SOC_ROOT_CIRCUIT_CLK_XTAL_X2: {
if (enable) {
s_bbpll_ref_cnt++;
toggled = (esp_clk_tree_root_pll_power_acquire(clk_circuit) == 0);
} else {
s_bbpll_ref_cnt--;
toggled = (esp_clk_tree_root_pll_power_release(clk_circuit) == 1);
}
// Note that a calibration is usually needed after enabling BBPLL
if (s_bbpll_ref_cnt == 1) {
clk_ll_bbpll_enable();
toggled = true;
} else if (s_bbpll_ref_cnt == 0) {
clk_ll_bbpll_disable();
toggled = true;
}
assert(s_bbpll_ref_cnt >= 0);
break;
}
default:
break;
}
return toggled; // TODO: PM-653
return toggled;
}
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
if (clk_circuit >= SOC_ROOT_CIRCUIT_CLK_MAX) {
return false;
}
if (clk_circuit == SOC_ROOT_CIRCUIT_CLK_BBPLL || clk_circuit == SOC_ROOT_CIRCUIT_CLK_XTAL_X2) {
int16_t cnt;
esp_os_enter_critical(&s_clk_tree_spinlock);
cnt = s_root_pll_power_ref_cnt[clk_circuit];
esp_os_exit_critical(&s_clk_tree_spinlock);
return cnt > 0;
}
return false;
}
typedef enum {
ESP_CLK_TREE_GATED_CLK_RC_FAST,
ESP_CLK_TREE_GATED_CLK_PLL_F48M,
ESP_CLK_TREE_GATED_CLK_XTAL_X2_F64M,
ESP_CLK_TREE_GATED_CLK_PLL_F96M,
ESP_CLK_TREE_GATED_CLK_NUM,
} esp_clk_tree_gated_clk_id_t;
static const esp_clk_tree_gated_clk_t s_gated_ref_clks[] = {
// [ESP_CLK_TREE_GATED_CLK_RC_FAST] = { SOC_MOD_CLK_RC_FAST, NULL, esp_clk_tree_parent_rc_fast }, // TODO: PM-859
[ESP_CLK_TREE_GATED_CLK_PLL_F48M] = { SOC_MOD_CLK_PLL_F48M, _clk_gate_ll_ref_48m_clk_en, esp_clk_tree_parent_bbpll },
[ESP_CLK_TREE_GATED_CLK_XTAL_X2_F64M] = { SOC_MOD_CLK_XTAL_X2_F64M, _clk_gate_ll_ref_64m_clk_en, esp_clk_tree_parent_xtal_x2 },
[ESP_CLK_TREE_GATED_CLK_PLL_F96M] = { SOC_MOD_CLK_PLL_F96M, _clk_gate_ll_ref_96m_clk_en, esp_clk_tree_parent_bbpll },
};
#define ENABLE_CLK_GATE(clk_src_en_func, enable) \
do { \
if ((clk_src_en_func) != NULL) { \
PERIPH_RCC_ATOMIC() { \
(clk_src_en_func)(enable); \
}; \
} \
} while (0)
FORCE_INLINE_ATTR esp_err_t esp_clk_tree_enable_gated_clk(const esp_clk_tree_gated_clk_t *entry, bool enable)
{
int16_t prev_ref_cnt;
bool released_too_many = false;
esp_os_enter_critical(&s_clk_tree_spinlock);
if (enable) {
prev_ref_cnt = s_mod_clk_gate_ref_cnt[entry->clk_id]++;
if (prev_ref_cnt == 0) {
if (entry->parent_power != NULL) {
entry->parent_power(true);
}
ENABLE_CLK_GATE(entry->set_gate, true);
}
} else {
prev_ref_cnt = s_mod_clk_gate_ref_cnt[entry->clk_id]--;
if (prev_ref_cnt <= 0) {
s_mod_clk_gate_ref_cnt[entry->clk_id] = 0;
released_too_many = true;
} else if (prev_ref_cnt == 1) {
ENABLE_CLK_GATE(entry->set_gate, false);
if (entry->parent_power != NULL) {
entry->parent_power(false);
}
}
}
esp_os_exit_critical(&s_clk_tree_spinlock);
if (released_too_many) {
ESP_LOGW(TAG, "soc_module_clk_t %d disabled multiple times!!", entry->clk_id);
}
return ESP_OK;
}
esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
{
if (clk_src < 1 || clk_src >= SOC_MOD_CLK_INVALID) {
// some conditions is legal, e.g. -1 means external clock source
if (clk_src < 1 || clk_src >= SOC_MOD_CLK_INVALID || clk_src == SOC_MOD_CLK_XTAL) {
/* Not managed by esp_clk_tree */
return ESP_OK;
}
int16_t prev_ref_cnt = 0;
if (enable) {
prev_ref_cnt = atomic_fetch_add(&s_pll_src_cg_ref_cnt[clk_src], 1);
} else {
prev_ref_cnt = atomic_fetch_sub(&s_pll_src_cg_ref_cnt[clk_src], 1);
if (prev_ref_cnt <= 0) {
ESP_EARLY_LOGW(TAG, "soc_module_clk_t %d disabled multiple times!!", clk_src);
atomic_store(&s_pll_src_cg_ref_cnt[clk_src], 0);
return ESP_OK;
}
if (!s_clk_tree_initialized) {
return ESP_OK;
}
if ((prev_ref_cnt == 0 && enable) || (prev_ref_cnt == 1 && !enable)) {
switch (clk_src) {
case SOC_MOD_CLK_RC_FAST:
enable ? rtc_dig_clk8m_enable() : rtc_dig_clk8m_disable();
break;
case SOC_MOD_CLK_XTAL_X2_F64M:
// later, here should handle ref count for XTAL_X2_F64M clock gating, then also handle XTAL_X2 circuit enable/disable
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_XTAL_X2, enable);
break;
// case SOC_MOD_CLK_PLL_FxxM:
// bool truly_toggled = esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_BBPLL, enable);
// if (enable && truly_toggled) {
// ESP_LOGW(TAG, "BBPLL enabled, a calibration may be needed");
// }
default:
break;
}
esp_clk_tree_gated_clk_id_t gated_clk_id;
switch (clk_src) {
case SOC_MOD_CLK_XTAL_X2:
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_XTAL_X2, enable);
return ESP_OK;
case SOC_MOD_CLK_BBPLL:
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_BBPLL, enable);
return ESP_OK;
// case SOC_MOD_CLK_RC_FAST: gated_clk_id = ESP_CLK_TREE_GATED_CLK_RC_FAST; break;
case SOC_MOD_CLK_PLL_F48M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F48M; break;
case SOC_MOD_CLK_XTAL_X2_F64M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_XTAL_X2_F64M; break;
case SOC_MOD_CLK_PLL_F96M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F96M; break;
default:
return ESP_OK;
}
return ESP_OK;
return esp_clk_tree_enable_gated_clk(&s_gated_ref_clks[gated_clk_id], enable);
}
@@ -262,7 +262,9 @@ void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config);
*
* @param config CPU frequency configuration structure
*/
#ifndef BOOTLOADER_BUILD
void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t *config);
#endif
/**
* @brief Get the currently used CPU frequency configuration
@@ -270,6 +272,7 @@ void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t *config);
*/
void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t *out_config);
#ifndef BOOTLOADER_BUILD
/**
* @brief Switch CPU clock source to XTAL
*
@@ -277,11 +280,10 @@ void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t *out_config);
* rtc_clk_cpu_freq_set_config when a switch to XTAL is needed.
* Assumes that XTAL frequency has been determined — don't call in startup code.
*
* @note This function always disables BBPLL after switching the CPU clock source to XTAL for power saving purpose.
* If this is unwanted, please use rtc_clk_cpu_freq_set_config. It helps to check whether USB Serial JTAG is in use,
* if so, then BBPLL will not be turned off.
* Releases the CPU clk_tree hold on the previous root clock (BBPLL / XTAL_X2).
*/
void rtc_clk_cpu_freq_set_xtal(void);
#endif
/**
* @brief Get the current APB frequency.
@@ -64,9 +64,15 @@ void pmu_hp_system_init(pmu_context_t *ctx, pmu_hp_mode_t mode, const pmu_hp_sys
assert(ctx->hal);
/* Default configuration of hp-system power in active, modem and sleep modes */
pmu_ll_hp_set_dig_power(ctx->hal->dev, mode, power->dig_power.val);
pmu_ll_hp_set_clk_power(ctx->hal->dev, mode, power->clk_power.val);
if (mode == PMU_MODE_HP_ACTIVE) {
// In active mode the root clock circuit power (BBPLL/CPLL/MPLL/APLL/XTALx2, etc.) is owned by esp_clk_tree.
// The analog i2c master is shared by all the PLLs and is not refcounted there, so it is still configured here.
pmu_ll_hp_set_ana_i2c_power(ctx->hal->dev, mode, power->clk_power.xpd_bb_i2c, power->clk_power.i2c_iso_en, power->clk_power.i2c_retention);
} else {
pmu_ll_hp_set_clk_power(ctx->hal->dev, mode, power->clk_power.val);
pmu_ll_hp_set_xtalx2_xpd (ctx->hal->dev, mode, power->xtal.xpd_xtalx2);
}
pmu_ll_hp_set_xtal_xpd (ctx->hal->dev, mode, power->xtal.xpd_xtal);
pmu_ll_hp_set_xtalx2_xpd (ctx->hal->dev, mode, power->xtal.xpd_xtalx2);
/* Default configuration of hp-system clock in active, modem and sleep modes */
pmu_ll_hp_set_icg_func (ctx->hal->dev, mode, clock->icg_func);
@@ -25,16 +25,19 @@
ESP_HW_LOG_ATTR_TAG(TAG, "rtc_clk");
// Current PLL frequency, in 96MHz. Zero if PLL is not enabled.
static int s_cur_pll_freq;
#ifndef BOOTLOADER_BUILD
// BBPLL frequency option, in 96MHz. Zero if BBPLL is not enabled / needs recalibration.
static int s_cur_pll_freq = 0;
static uint32_t s_bbpll_digi_consumers_ref_count = 0; // Currently, it only tracks whether the 48MHz PHY clock is in-use by USB Serial/JTAG
#if !BOOTLOADER_BUILD
// Indicate whether the specific cpu clock source is acquired by the hp root clock (i.e. whether ref_cnt in esp_clk_tree.c is incremented by the hp root clock)
/**
* Whether CPU currently holds a clk_tree ref on BBPLL / XTAL_X2.
* Survives DFS set_config_fast(XTAL) (keep-hot) and light-sleep (PMU restores ACTIVE XPD on wake).
* Cleared only on real leave via set_config / set_xtal.
*/
static bool s_is_pll_acquired = (CONFIG_BOOTLOADER_CPU_CLK_FREQ_MHZ == 96 || CONFIG_BOOTLOADER_CPU_CLK_FREQ_MHZ == 48);
static bool s_is_xtal_x2_acquired = (CONFIG_BOOTLOADER_CPU_CLK_FREQ_MHZ == 64);
#endif
void rtc_clk_bbpll_add_consumer(void)
{
@@ -45,6 +48,7 @@ void rtc_clk_bbpll_remove_consumer(void)
{
s_bbpll_digi_consumers_ref_count -= 1;
}
#endif
void rtc_clk_32k_enable(bool enable)
{
@@ -137,19 +141,6 @@ soc_rtc_fast_clk_src_t rtc_clk_fast_src_get(void)
return clk_ll_rtc_fast_get_src();
}
#if BOOTLOADER_BUILD
static void rtc_clk_bbpll_disable(void)
{
clk_ll_bbpll_disable();
s_cur_pll_freq = 0;
}
static void rtc_clk_bbpll_enable(void)
{
clk_ll_bbpll_enable();
}
#endif
static void rtc_clk_bbpll_configure(soc_xtal_freq_t xtal_freq, int pll_freq)
{
/* Digital part */
@@ -167,7 +158,9 @@ static void rtc_clk_bbpll_configure(soc_xtal_freq_t xtal_freq, int pll_freq)
clk_ll_bbpll_calibration_stop();
ANALOG_CLOCK_DISABLE();
#ifndef BOOTLOADER_BUILD
s_cur_pll_freq = pll_freq;
#endif
}
/**
@@ -281,64 +274,62 @@ __attribute__((weak)) void rtc_clk_set_cpu_switch_to_pll(int event_id)
{
}
static void rtc_clk_cpu_src_clk_enable(soc_cpu_clk_src_t new_src, uint32_t new_src_freq_mhz)
static void rtc_clk_update_pll_state_on_cpu_src_switching_start(soc_cpu_clk_src_t new_src, uint32_t new_src_freq_mhz)
{
#ifdef BOOTLOADER_BUILD
if (new_src == SOC_CPU_CLK_SRC_PLL) {
bool truly_enabled = false;
#if BOOTLOADER_BUILD
rtc_clk_bbpll_enable();
truly_enabled = true;
clk_ll_bbpll_enable();
rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), new_src_freq_mhz);
} else if (new_src == SOC_CPU_CLK_SRC_XTAL_X2) {
clk_ll_xtal_x2_enable();
}
#else
if (new_src == SOC_CPU_CLK_SRC_PLL) {
bool need_configure = false;
if (!s_is_pll_acquired) {
truly_enabled = esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_BBPLL, true);
need_configure = !esp_clk_tree_is_power_on(SOC_ROOT_CIRCUIT_CLK_BBPLL);
esp_clk_tree_enable_src(SOC_MOD_CLK_BBPLL, true);
s_is_pll_acquired = true;
}
#endif
if (truly_enabled || (s_cur_pll_freq != new_src_freq_mhz)) {
if (need_configure || (s_cur_pll_freq != (int)new_src_freq_mhz)) {
rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), new_src_freq_mhz);
}
} else if (new_src == SOC_CPU_CLK_SRC_XTAL_X2) {
#if BOOTLOADER_BUILD
clk_ll_xtal_x2_enable();
#else
if (!s_is_xtal_x2_acquired) {
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_XTAL_X2, true);
esp_clk_tree_enable_src(SOC_MOD_CLK_XTAL_X2, true);
s_is_xtal_x2_acquired = true;
}
#endif
}
#endif
}
static void rtc_clk_cpu_src_clk_disable(soc_cpu_clk_src_t old_src)
#ifndef BOOTLOADER_BUILD
static void rtc_clk_update_pll_state_on_cpu_src_switching_end(soc_cpu_clk_src_t old_src)
{
if ((old_src == SOC_CPU_CLK_SRC_PLL) && !s_bbpll_digi_consumers_ref_count) {
#if BOOTLOADER_BUILD
rtc_clk_bbpll_disable();
#else
assert(s_is_pll_acquired);
bool truly_disabled = esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_BBPLL, false);
esp_clk_tree_enable_src(SOC_MOD_CLK_BBPLL, false);
s_is_pll_acquired = false;
if (truly_disabled) {
if (!esp_clk_tree_is_power_on(SOC_ROOT_CIRCUIT_CLK_BBPLL)) {
s_cur_pll_freq = 0;
}
#endif
} else if (old_src == SOC_CPU_CLK_SRC_XTAL_X2) {
#if BOOTLOADER_BUILD
clk_ll_xtal_x2_disable();
#else
assert(s_is_xtal_x2_acquired);
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_XTAL_X2, false);
esp_clk_tree_enable_src(SOC_MOD_CLK_XTAL_X2, false);
s_is_xtal_x2_acquired = false;
#endif
}
}
#endif
void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config)
{
soc_cpu_clk_src_t old_cpu_clk_src = clk_ll_cpu_get_src();
if (old_cpu_clk_src != config->source) {
rtc_clk_cpu_src_clk_enable(config->source, config->source_freq_mhz);
__attribute__((unused)) soc_cpu_clk_src_t old_cpu_clk_src = clk_ll_cpu_get_src();
#ifndef BOOTLOADER_BUILD
bool src_changed = (old_cpu_clk_src != config->source);
if (src_changed)
#endif
{
rtc_clk_update_pll_state_on_cpu_src_switching_start(config->source, config->source_freq_mhz);
}
if (config->source == SOC_CPU_CLK_SRC_XTAL) {
@@ -353,9 +344,11 @@ void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config)
rtc_clk_cpu_freq_to_xtal_x2(config->freq_mhz, config->div);
}
if (old_cpu_clk_src != config->source) {
rtc_clk_cpu_src_clk_disable(old_cpu_clk_src);
#ifndef BOOTLOADER_BUILD
if (src_changed) {
rtc_clk_update_pll_state_on_cpu_src_switching_end(old_cpu_clk_src);
}
#endif
}
void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t *out_config)
@@ -391,20 +384,21 @@ void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t *out_config)
};
}
#ifndef BOOTLOADER_BUILD
void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t *config)
{
/* Mux only — Fall back to set_config when PLL/XTALx2 must be reacquired or recalibrated
* (s_cur_pll_freq == 0 after sleep). */
if (config->source == SOC_CPU_CLK_SRC_XTAL) {
rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div);
} else if (config->source == SOC_CPU_CLK_SRC_PLL &&
s_cur_pll_freq == config->source_freq_mhz) {
s_is_pll_acquired &&
s_cur_pll_freq == (int)config->source_freq_mhz) {
rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz);
} else if (config->source == SOC_CPU_CLK_SRC_RC_FAST) {
rtc_clk_cpu_freq_to_rc_fast();
} else if (config->source == SOC_CPU_CLK_SRC_XTAL_X2
#if !BOOTLOADER_BUILD
&& s_is_xtal_x2_acquired
#endif
) {
} else if (config->source == SOC_CPU_CLK_SRC_XTAL_X2 &&
s_is_xtal_x2_acquired) {
rtc_clk_cpu_freq_to_xtal_x2(config->freq_mhz, config->div);
} else {
/* fallback */
@@ -419,22 +413,29 @@ void rtc_clk_cpu_freq_set_xtal(void)
rtc_clk_cpu_freq_to_xtal(freq_mhz, 1);
if (old_cpu_clk_src != SOC_CPU_CLK_SRC_XTAL) {
rtc_clk_cpu_src_clk_disable(old_cpu_clk_src);
rtc_clk_update_pll_state_on_cpu_src_switching_end(old_cpu_clk_src);
}
}
#endif
FORCE_IRAM_ATTR void rtc_clk_cpu_set_to_default_config(void)
{
int freq_mhz = (int)rtc_clk_xtal_freq_get();
rtc_clk_cpu_freq_to_xtal(freq_mhz, 1);
s_cur_pll_freq = 0; // no disable PLL, but set freq to 0 to trigger a PLL calibration after wake-up from sleep
}
#ifndef BOOTLOADER_BUILD
void rtc_clk_cpu_freq_set_xtal_for_sleep(void)
{
rtc_clk_cpu_set_to_default_config();
int freq_mhz = (int)rtc_clk_xtal_freq_get();
/* Mux only — do not release CPU clk_tree hold. PMU restores ACTIVE XPD on
* wake; clearing s_cur_pll_freq forces recalibration via set_config fallback. */
rtc_clk_cpu_freq_to_xtal(freq_mhz, 1);
s_cur_pll_freq = 0;
}
#endif
FORCE_IRAM_ATTR soc_xtal_freq_t rtc_clk_xtal_freq_get(void)
{
@@ -14,11 +14,9 @@ endif()
if(NOT BOOTLOADER_BUILD)
list(APPEND srcs "sar_periph_ctrl.c")
if(CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT)
list(APPEND srcs "pmu_sleep_power.c")
endif()
if(CONFIG_PM_MODEM_STATE_ENABLED)
list(APPEND srcs "pmu_sleep_power.c")
endif()
endif()
add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" "${srcs}")
@@ -5,23 +5,123 @@
*/
#include <stdint.h>
#include <stdatomic.h>
#include <assert.h>
#include "sdkconfig.h"
#include "esp_clk_tree.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "esp_rom_sys.h"
#include "soc/rtc.h"
#include "soc/reset_reasons.h"
#include "hal/clk_gate_ll.h"
#include "hal/clk_tree_hal.h"
#include "hal/clk_tree_ll.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/critical_section.h"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
/* -------------------------------------------------------------------------- */
/* Fixed ref clocks: gate + static parent power */
/* -------------------------------------------------------------------------- */
typedef void (*esp_clk_tree_gate_fn_t)(bool enable);
typedef void (*esp_clk_tree_parent_fn_t)(bool enable);
typedef struct {
soc_module_clk_t clk_id;
esp_clk_tree_gate_fn_t set_gate;
esp_clk_tree_parent_fn_t parent_power;
} esp_clk_tree_gated_clk_t;
static void esp_clk_tree_parent_bbpll(bool enable)
{
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_BBPLL, enable);
}
static void esp_clk_tree_parent_xtal_x2(bool enable)
{
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_XTAL_X2, enable);
}
// static void esp_clk_tree_parent_rc_fast(bool enable)
// {
// if (enable) {
// rtc_dig_clk8m_enable();
// } else {
// rtc_dig_clk8m_disable();
// }
// }
DEFINE_CRIT_SECTION_LOCK_STATIC(s_clk_tree_spinlock);
/** Per soc_module_clk_t: record clock gate consumers */
static int16_t s_mod_clk_gate_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
/** Per soc_root_clk_circuit_t: record clock power consumers */
static int16_t s_root_pll_power_ref_cnt[SOC_ROOT_CIRCUIT_CLK_MAX] = { 0 };
static bool s_clk_tree_initialized = false;
static int16_t esp_clk_tree_root_pll_power_acquire(soc_root_clk_circuit_t clk_circuit)
{
int16_t prev;
assert(clk_circuit == SOC_ROOT_CIRCUIT_CLK_BBPLL || clk_circuit == SOC_ROOT_CIRCUIT_CLK_XTAL_X2);
esp_os_enter_critical(&s_clk_tree_spinlock);
prev = s_root_pll_power_ref_cnt[clk_circuit]++;
if (prev == 0) {
switch (clk_circuit) {
case SOC_ROOT_CIRCUIT_CLK_BBPLL:
clk_ll_bbpll_enable();
break;
case SOC_ROOT_CIRCUIT_CLK_XTAL_X2:
clk_ll_xtal_x2_enable();
break;
default:
break;
}
}
esp_os_exit_critical(&s_clk_tree_spinlock);
return prev;
}
static int16_t esp_clk_tree_root_pll_power_release(soc_root_clk_circuit_t clk_circuit)
{
int16_t prev;
assert(clk_circuit == SOC_ROOT_CIRCUIT_CLK_BBPLL || clk_circuit == SOC_ROOT_CIRCUIT_CLK_XTAL_X2);
esp_os_enter_critical(&s_clk_tree_spinlock);
prev = s_root_pll_power_ref_cnt[clk_circuit];
if (prev <= 0) {
esp_os_exit_critical(&s_clk_tree_spinlock);
ESP_EARLY_LOGW(TAG, "soc_root_clk_circuit_t %d disabled multiple times!!", clk_circuit);
return prev;
}
s_root_pll_power_ref_cnt[clk_circuit] = prev - 1;
if (prev == 1) {
switch (clk_circuit) {
case SOC_ROOT_CIRCUIT_CLK_BBPLL:
clk_ll_bbpll_disable();
break;
case SOC_ROOT_CIRCUIT_CLK_XTAL_X2:
clk_ll_xtal_x2_disable();
break;
default:
break;
}
}
esp_os_exit_critical(&s_clk_tree_spinlock);
return prev;
}
esp_err_t esp_clk_tree_src_get_freq_hz(soc_module_clk_t clk_src, esp_clk_tree_src_freq_precision_t precision,
uint32_t *freq_value)
uint32_t *freq_value)
{
ESP_RETURN_ON_FALSE(clk_src > 0 && clk_src < SOC_MOD_CLK_INVALID, ESP_ERR_INVALID_ARG, TAG, "unknown clk src");
ESP_RETURN_ON_FALSE(precision < ESP_CLK_TREE_SRC_FREQ_PRECISION_INVALID, ESP_ERR_INVALID_ARG, TAG, "unknown precision");
@@ -41,9 +141,11 @@ uint32_t *freq_value)
case SOC_MOD_CLK_PLL_F48M:
clk_src_freq = CLK_LL_PLL_48M_FREQ_MHZ * MHZ;
break;
case SOC_MOD_CLK_XTAL_X2:
case SOC_MOD_CLK_XTAL_X2_F64M:
clk_src_freq = CLK_LL_PLL_64M_FREQ_MHZ * MHZ;
break;
case SOC_MOD_CLK_BBPLL:
case SOC_MOD_CLK_PLL_F96M:
clk_src_freq = CLK_LL_PLL_96M_FREQ_MHZ * MHZ;
break;
@@ -70,122 +172,177 @@ uint32_t *freq_value)
esp_err_t esp_clk_tree_src_set_freq_hz(soc_module_clk_t clk_src, uint32_t expt_freq_value, uint32_t *ret_freq_value)
{
(void)clk_src; (void)expt_freq_value; (void)ret_freq_value;
ESP_RETURN_ON_FALSE(clk_src > 0 && clk_src < SOC_MOD_CLK_INVALID, ESP_ERR_INVALID_ARG, TAG, "unknown clk src");
ESP_RETURN_ON_FALSE(expt_freq_value > 0, ESP_ERR_INVALID_ARG, TAG, "invalid frequency");
(void)ret_freq_value;
return ESP_ERR_NOT_SUPPORTED;
}
static int16_t s_xtal_x2_ref_cnt = 0;
static int16_t s_bbpll_ref_cnt = 0;
void esp_clk_tree_initialize(void)
{
// Power
// In bootloader, flash clock source will always be switched to use XTAL_X2 clock
s_xtal_x2_ref_cnt++;
soc_cpu_clk_src_t cpu_clk_src_btld = clk_ll_cpu_get_src();
if (cpu_clk_src_btld == SOC_CPU_CLK_SRC_XTAL_X2) {
s_xtal_x2_ref_cnt++;
} else if (cpu_clk_src_btld == SOC_CPU_CLK_SRC_PLL) {
s_bbpll_ref_cnt++;
soc_reset_reason_t rst_reason = esp_rom_get_reset_reason(0);
soc_cpu_clk_src_t cpu_src = clk_ll_cpu_get_src();
bool cpu_reset = (rst_reason == RESET_REASON_CPU0_MWDT0) || (rst_reason == RESET_REASON_CPU0_MWDT1) ||
(rst_reason == RESET_REASON_CPU0_SW) || (rst_reason == RESET_REASON_CPU0_RTC_WDT) ||
(rst_reason == RESET_REASON_CPU0_JTAG) || (rst_reason == RESET_REASON_CPU_LOCKUP);
if (!cpu_reset) {
/* Cold boot only: gate / power-down clocks not in use.
* Flash MSPI defaults to XTAL_X2_F64M — keep that root/gate alive. */
_clk_gate_ll_ref_8m_clk_en(false);
_clk_gate_ll_ref_16m_clk_en(false);
_clk_gate_ll_ref_32m_clk_en(false);
_clk_gate_ll_ref_96m_clk_en(false);
#if CONFIG_USJ_ENABLE_USB_SERIAL_JTAG || CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED
/* Keep BBPLL / ref_48m: USJ PHY still uses them after bootloader. */
#else
_clk_gate_ll_ref_48m_clk_en(false);
if (cpu_src != SOC_CPU_CLK_SRC_PLL) {
clk_ll_bbpll_disable();
}
#endif
}
// Gating
// PLL_F64M ++ for MSPI
}
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
if (clk_circuit == SOC_ROOT_CIRCUIT_CLK_XTAL_X2) {
return s_xtal_x2_ref_cnt > 0;
s_clk_tree_initialized = true;
#if CONFIG_USJ_ENABLE_USB_SERIAL_JTAG || CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED
/* Bootloader / USJ may keep PLL_F48M on; declare a permanent hold. */
esp_clk_tree_enable_src(SOC_MOD_CLK_PLL_F48M, true);
#endif
/* Flash + CPU: sync clk_tree refs with HW already selected at boot.
* Flash uses gated F64M; CPU holds the ungated XTAL_X2 / PLL root, not the F64M / F96M gates. */
esp_clk_tree_enable_src(SOC_MOD_CLK_XTAL_X2_F64M, true);
if (cpu_src == SOC_CPU_CLK_SRC_PLL) {
esp_clk_tree_enable_src(SOC_MOD_CLK_BBPLL, true);
} else if (cpu_src == SOC_CPU_CLK_SRC_XTAL_X2) {
esp_clk_tree_enable_src(SOC_MOD_CLK_XTAL_X2, true);
}
if (clk_circuit == SOC_ROOT_CIRCUIT_CLK_BBPLL) {
return s_bbpll_ref_cnt > 0;
}
return false;
}
bool esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool enable)
{
if (clk_circuit >= SOC_ROOT_CIRCUIT_CLK_MAX) {
return false;
}
bool toggled = false;
switch (clk_circuit) {
case SOC_ROOT_CIRCUIT_CLK_XTAL_X2:
if (enable) {
s_xtal_x2_ref_cnt++;
} else {
s_xtal_x2_ref_cnt--;
}
if (s_xtal_x2_ref_cnt == 1) {
clk_ll_xtal_x2_enable();
toggled = true;
} else if (s_xtal_x2_ref_cnt == 0) {
clk_ll_xtal_x2_disable();
toggled = true;
}
assert(s_xtal_x2_ref_cnt >= 0);
break;
case SOC_ROOT_CIRCUIT_CLK_BBPLL:
case SOC_ROOT_CIRCUIT_CLK_XTAL_X2: {
if (enable) {
s_bbpll_ref_cnt++;
toggled = (esp_clk_tree_root_pll_power_acquire(clk_circuit) == 0);
} else {
s_bbpll_ref_cnt--;
toggled = (esp_clk_tree_root_pll_power_release(clk_circuit) == 1);
}
// Note that a calibration is usually needed after enabling BBPLL
if (s_bbpll_ref_cnt == 1) {
clk_ll_bbpll_enable();
toggled = true;
} else if (s_bbpll_ref_cnt == 0) {
clk_ll_bbpll_disable();
toggled = true;
}
assert(s_bbpll_ref_cnt >= 0);
break;
}
default:
break;
}
return toggled;
}
esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
if (clk_src < 1 || clk_src >= SOC_MOD_CLK_INVALID) {
// some conditions is legal, e.g. -1 means external clock source
return ESP_OK;
if (clk_circuit >= SOC_ROOT_CIRCUIT_CLK_MAX) {
return false;
}
int16_t prev_ref_cnt = 0;
if (clk_circuit == SOC_ROOT_CIRCUIT_CLK_BBPLL || clk_circuit == SOC_ROOT_CIRCUIT_CLK_XTAL_X2) {
int16_t cnt;
esp_os_enter_critical(&s_clk_tree_spinlock);
cnt = s_root_pll_power_ref_cnt[clk_circuit];
esp_os_exit_critical(&s_clk_tree_spinlock);
return cnt > 0;
}
return false;
}
typedef enum {
ESP_CLK_TREE_GATED_CLK_RC_FAST,
ESP_CLK_TREE_GATED_CLK_XTAL_X2_F32M,
ESP_CLK_TREE_GATED_CLK_PLL_F48M,
ESP_CLK_TREE_GATED_CLK_XTAL_X2_F64M,
ESP_CLK_TREE_GATED_CLK_PLL_F96M,
ESP_CLK_TREE_GATED_CLK_NUM,
} esp_clk_tree_gated_clk_id_t;
static const esp_clk_tree_gated_clk_t s_gated_ref_clks[] = {
// [ESP_CLK_TREE_GATED_CLK_RC_FAST] = { SOC_MOD_CLK_RC_FAST, NULL, esp_clk_tree_parent_rc_fast }, // TODO: PM-859
[ESP_CLK_TREE_GATED_CLK_XTAL_X2_F32M] = { SOC_MOD_CLK_XTAL_X2_F32M, _clk_gate_ll_ref_32m_clk_en, esp_clk_tree_parent_xtal_x2 },
[ESP_CLK_TREE_GATED_CLK_PLL_F48M] = { SOC_MOD_CLK_PLL_F48M, _clk_gate_ll_ref_48m_clk_en, esp_clk_tree_parent_bbpll },
[ESP_CLK_TREE_GATED_CLK_XTAL_X2_F64M] = { SOC_MOD_CLK_XTAL_X2_F64M, _clk_gate_ll_ref_64m_clk_en, esp_clk_tree_parent_xtal_x2 },
[ESP_CLK_TREE_GATED_CLK_PLL_F96M] = { SOC_MOD_CLK_PLL_F96M, _clk_gate_ll_ref_96m_clk_en, esp_clk_tree_parent_bbpll },
};
#define ENABLE_CLK_GATE(clk_src_en_func, enable) \
do { \
if ((clk_src_en_func) != NULL) { \
PERIPH_RCC_ATOMIC() { \
(clk_src_en_func)(enable); \
}; \
} \
} while (0)
FORCE_INLINE_ATTR esp_err_t esp_clk_tree_enable_gated_clk(const esp_clk_tree_gated_clk_t *entry, bool enable)
{
int16_t prev_ref_cnt;
bool released_too_many = false;
esp_os_enter_critical(&s_clk_tree_spinlock);
if (enable) {
prev_ref_cnt = atomic_fetch_add(&s_pll_src_cg_ref_cnt[clk_src], 1);
prev_ref_cnt = s_mod_clk_gate_ref_cnt[entry->clk_id]++;
if (prev_ref_cnt == 0) {
if (entry->parent_power != NULL) {
entry->parent_power(true);
}
ENABLE_CLK_GATE(entry->set_gate, true);
}
} else {
prev_ref_cnt = atomic_fetch_sub(&s_pll_src_cg_ref_cnt[clk_src], 1);
prev_ref_cnt = s_mod_clk_gate_ref_cnt[entry->clk_id]--;
if (prev_ref_cnt <= 0) {
ESP_EARLY_LOGW(TAG, "soc_module_clk_t %d disabled multiple times!!", clk_src);
atomic_store(&s_pll_src_cg_ref_cnt[clk_src], 0);
return ESP_OK;
s_mod_clk_gate_ref_cnt[entry->clk_id] = 0;
released_too_many = true;
} else if (prev_ref_cnt == 1) {
ENABLE_CLK_GATE(entry->set_gate, false);
if (entry->parent_power != NULL) {
entry->parent_power(false);
}
}
}
if ((prev_ref_cnt == 0 && enable) || (prev_ref_cnt == 1 && !enable)) {
switch (clk_src) {
case SOC_MOD_CLK_RC_FAST:
enable ? rtc_dig_clk8m_enable() : rtc_dig_clk8m_disable();
break;
case SOC_MOD_CLK_XTAL_X2_F32M:
// later, here should handle ref count for XTAL_X2_F32M clock gating, then also handle XTAL_X2 circuit enable/disable
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_XTAL_X2, enable);
break;
case SOC_MOD_CLK_XTAL_X2_F64M:
// later, here should handle ref count for XTAL_X2_F64M clock gating, then also handle XTAL_X2 circuit enable/disable
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_XTAL_X2, enable);
break;
// case SOC_MOD_CLK_PLL_FxxM:
// bool truly_toggled = esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_BBPLL, enable);
// if (enable && truly_toggled) {
// ESP_LOGW(TAG, "BBPLL enabled, a calibration may be needed");
// }
default:
break;
}
esp_os_exit_critical(&s_clk_tree_spinlock);
if (released_too_many) {
ESP_LOGW(TAG, "soc_module_clk_t %d disabled multiple times!!", entry->clk_id);
}
return ESP_OK;
}
esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
{
if (clk_src < 1 || clk_src >= SOC_MOD_CLK_INVALID || clk_src == SOC_MOD_CLK_XTAL) {
/* Not managed by esp_clk_tree */
return ESP_OK;
}
if (!s_clk_tree_initialized) {
return ESP_OK;
}
esp_clk_tree_gated_clk_id_t gated_clk_id;
switch (clk_src) {
case SOC_MOD_CLK_XTAL_X2:
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_XTAL_X2, enable);
return ESP_OK;
case SOC_MOD_CLK_BBPLL:
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_BBPLL, enable);
return ESP_OK;
// case SOC_MOD_CLK_RC_FAST: gated_clk_id = ESP_CLK_TREE_GATED_CLK_RC_FAST; break;
case SOC_MOD_CLK_XTAL_X2_F32M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_XTAL_X2_F32M; break;
case SOC_MOD_CLK_PLL_F48M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F48M; break;
case SOC_MOD_CLK_XTAL_X2_F64M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_XTAL_X2_F64M; break;
case SOC_MOD_CLK_PLL_F96M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F96M; break;
default:
return ESP_OK;
}
return esp_clk_tree_enable_gated_clk(&s_gated_ref_clks[gated_clk_id], enable);
}
@@ -261,7 +261,9 @@ void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config);
*
* @param config CPU frequency configuration structure
*/
#ifndef BOOTLOADER_BUILD
void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t *config);
#endif
/**
* @brief Get the currently used CPU frequency configuration
@@ -269,6 +271,7 @@ void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t *config);
*/
void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t *out_config);
#ifndef BOOTLOADER_BUILD
/**
* @brief Switch CPU clock source to XTAL
*
@@ -276,11 +279,10 @@ void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t *out_config);
* rtc_clk_cpu_freq_set_config when a switch to XTAL is needed.
* Assumes that XTAL frequency has been determined — don't call in startup code.
*
* @note This function always disables BBPLL after switching the CPU clock source to XTAL for power saving purpose.
* If this is unwanted, please use rtc_clk_cpu_freq_set_config. It helps to check whether USB Serial JTAG is in use,
* if so, then BBPLL will not be turned off.
* Releases the CPU clk_tree hold on the previous root clock (BBPLL / XTAL_X2).
*/
void rtc_clk_cpu_freq_set_xtal(void);
#endif
/**
* @brief Switch root clock source to PLL (only used by sleep) release root clock source locked by PMU
@@ -68,9 +68,15 @@ void pmu_hp_system_init(pmu_context_t *ctx, pmu_hp_mode_t mode, const pmu_hp_sys
assert(ctx->hal);
/* Default configuration of hp-system power in active, modem and sleep modes */
pmu_ll_hp_set_dig_power(ctx->hal->dev, mode, power->dig_power.val);
pmu_ll_hp_set_clk_power(ctx->hal->dev, mode, power->clk_power.val);
if (mode == PMU_MODE_HP_ACTIVE) {
// In active mode the root clock circuit power (BBPLL/CPLL/MPLL/APLL/XTALx2, etc.) is owned by esp_clk_tree.
// The analog i2c master is shared by all the PLLs and is not refcounted there, so it is still configured here.
pmu_ll_hp_set_ana_i2c_power(ctx->hal->dev, mode, power->clk_power.xpd_bb_i2c, power->clk_power.i2c_iso_en, power->clk_power.i2c_retention);
} else {
pmu_ll_hp_set_clk_power(ctx->hal->dev, mode, power->clk_power.val);
pmu_ll_hp_set_xtalx2_xpd (ctx->hal->dev, mode, power->xtal.xpd_xtalx2);
}
pmu_ll_hp_set_xtal_xpd (ctx->hal->dev, mode, power->xtal.xpd_xtal);
pmu_ll_hp_set_xtalx2_xpd (ctx->hal->dev, mode, power->xtal.xpd_xtalx2);
/* Default configuration of hp-system clock in active, modem and sleep modes */
pmu_ll_hp_set_icg_func (ctx->hal->dev, mode, clock->icg_func);
@@ -250,14 +250,27 @@ static void pmu_sleep_param_init(pmu_context_t *ctx, const pmu_sleep_param_confi
pmu_ll_set_xtal_stable_wait_cycle(ctx->hal->dev, param->hp_lp.xtal_stable_wait_slow_clk_cycle);
pmu_ll_set_pll_stable_wait_cycle(ctx->hal->dev, param->hp_sys.pll_stable_wait_cycle);
#if CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT
#if CONFIG_PM_MODEM_STATE_ENABLED
uint16_t ana_wait[ANALOG_WAIT_CTRL_NUM] = {
0x20, // about 1.6us
param->hp_sys.analog_wait_target_cycle,
param->hp_sys.analog_wait_target_cycle,
};
pmu_sleep_power_analog_wait_config(ctx->priv, ana_wait);
#endif
#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND
pmu_hp_clk_power_reg_t hp_ck = { .val = pmu_ll_hp_get_clk_power(ctx->hal->dev, HP(ACTIVE)) };
uint32_t xtalx2_xpd = pmu_ll_hp_get_xtalx2_xpd(ctx->hal->dev, HP(ACTIVE));
pmu_imm_hp_clk_power_reg_t imm = {
.tie_high_xpd_bbpll = hp_ck.xpd_bbpll,
.tie_high_xpd_bbpll_i2c = hp_ck.xpd_bbpll_i2c,
.tie_high_global_bbpll_icg = hp_ck.xpd_bbpll || hp_ck.xpd_bbpll_i2c,
.tie_high_xtalx2 = xtalx2_xpd,
.tie_high_global_xtalx2_icg = xtalx2_xpd,
};
pmu_sleep_power_clock_config(ctx->priv, imm.val);
#endif // SOC_PM_MODEM_LOCK_CLK_WORKAROUND
#endif // CONFIG_PM_MODEM_STATE_ENABLED
}
bool pmu_sleep_pll_already_enabled(void)
@@ -20,17 +20,37 @@ ESP_LOG_ATTR_TAG(TAG, "sleep_power");
#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND
#define XTALX2_TIE_HIGH_MASK (PMU_TIE_HIGH_XTALX2_M | PMU_TIE_HIGH_GLOBAL_XTALX2_ICG_M)
#define XTALX2_TIE_HIGH_ON (PMU_TIE_HIGH_XTALX2 | PMU_TIE_HIGH_GLOBAL_XTALX2_ICG)
#define BBPLL_TIE_HIGH_MASK (PMU_TIE_HIGH_XPD_BBPLL_M | PMU_TIE_HIGH_XPD_BBPLL_I2C_M | PMU_TIE_HIGH_GLOBAL_BBPLL_ICG_M)
#define BBPLL_TIE_HIGH_ON (PMU_TIE_HIGH_XPD_BBPLL | PMU_TIE_HIGH_XPD_BBPLL_I2C | PMU_TIE_HIGH_GLOBAL_BBPLL_ICG)
typedef struct {
void *regdma_desc[2];
} pmu_sleep_power_clock_context_t;
static esp_err_t sleep_power_system_retention_init(void *arg)
{
const static sleep_retention_entries_config_t power_regs_retention[] = {
/* During the modem-to-active transition, the MODEM lock keeps the xtalx2, bbpll xpd status unchanged.
* Therefore, if xtalx2, bbpll is disabled in modem state, we need to set xtalx2, bbpll tie-high to enable it in active state. */
[0] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_POWER_LINK(0), PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XTALX2 | PMU_TIE_HIGH_GLOBAL_XTALX2_ICG, PMU_TIE_HIGH_XTALX2_M | PMU_TIE_HIGH_GLOBAL_XTALX2_ICG_M, 1, 0), .owner = ENTRY(2) },
[1] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_POWER_LINK(1), PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XPD_BBPLL | PMU_TIE_HIGH_XPD_BBPLL_I2C, PMU_TIE_HIGH_XPD_BBPLL_M | PMU_TIE_HIGH_XPD_BBPLL_I2C_M, 1, 0), .owner = ENTRY(2) },
* Therefore, if xtalx2, bbpll is disabled in modem state, we need to set xtalx2, bbpll tie-high to enable it in active state.
*
* XTALX2 and BBPLL write values are back-filled before sleep by pmu_sleep_power_clock_config(). */
[0] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_POWER_LINK(0), PMU_IMM_HP_CK_POWER_REG, XTALX2_TIE_HIGH_ON, XTALX2_TIE_HIGH_MASK, 1, 0), .owner = ENTRY(2) },
[1] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_POWER_LINK(1), PMU_IMM_HP_CK_POWER_REG, BBPLL_TIE_HIGH_ON, BBPLL_TIE_HIGH_MASK, 1, 0), .owner = ENTRY(2) },
};
esp_err_t err = sleep_retention_entries_create(power_regs_retention, ARRAY_SIZE(power_regs_retention), REGDMA_LINK_PRI_POWER, SLEEP_RETENTION_MODULE_POWER);
pmu_sleep_power_clock_context_t *clk = (pmu_sleep_power_clock_context_t *)arg;
int id_array[ARRAY_SIZE(clk->regdma_desc)] = { REGDMA_POWER_LINK(0), REGDMA_POWER_LINK(1) };
for (int i = 0; i < ARRAY_SIZE(id_array); i++) {
void *head = sleep_retention_find_link_by_id(id_array[i]);
if (head) {
clk->regdma_desc[i] = head;
}
}
ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for clock power retention");
ESP_LOGI(TAG, "Clock power sleep retention initialization");
return ESP_OK;
}
@@ -67,32 +87,45 @@ static esp_err_t sleep_power_analog_wait_ctrl_init(void *arg)
return ESP_OK;
}
typedef struct {
#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND
pmu_sleep_power_clock_context_t clock;
#endif
pmu_sleep_power_ana_wait_context_t ana_wait;
} pmu_sleep_power_context_t;
static esp_err_t sleep_power_retention_init(void *arg)
{
pmu_sleep_power_context_t *ctx = (pmu_sleep_power_context_t *)arg;
#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND
ESP_RETURN_ON_ERROR(sleep_power_system_retention_init(arg), TAG, "system retention init failed");
ESP_RETURN_ON_ERROR(sleep_power_system_retention_init(&ctx->clock), TAG, "system retention init failed");
#endif
ESP_RETURN_ON_ERROR(sleep_power_analog_wait_ctrl_init(arg), TAG, "analog wait ctrl retention init failed");
ESP_RETURN_ON_ERROR(sleep_power_analog_wait_ctrl_init(&ctx->ana_wait), TAG, "analog wait ctrl retention init failed");
return ESP_OK;
}
static esp_err_t sleep_power_retention_deinit(void *arg)
{
pmu_sleep_power_ana_wait_context_t *ana_wait_ctx = (pmu_sleep_power_ana_wait_context_t *)arg;
pmu_sleep_power_context_t *ctx = (pmu_sleep_power_context_t *)arg;
#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND
for (int i = 0; i < ARRAY_SIZE(ctx->clock.regdma_desc); i++) {
ctx->clock.regdma_desc[i] = NULL;
}
#endif
for (int i = 0; i < ANALOG_WAIT_CTRL_NUM; i++) {
ana_wait_ctx->regdma_desc[i] = NULL;
ctx->ana_wait.regdma_desc[i] = NULL;
}
return ESP_OK;
}
ESP_SYSTEM_INIT_FN(sleep_power_startup_init, SECONDARY, BIT(0), 108)
{
static DRAM_ATTR pmu_sleep_power_ana_wait_context_t ana_wait_ctx;
static DRAM_ATTR pmu_sleep_power_context_t power_context = { 0 };
sleep_retention_module_init_param_t init_param = {
.cbs = {
.create = { .handle = sleep_power_retention_init, .arg = &ana_wait_ctx },
.destroy = { .handle = sleep_power_retention_deinit, .arg = &ana_wait_ctx },
.create = { .handle = sleep_power_retention_init, .arg = &power_context },
.destroy = { .handle = sleep_power_retention_deinit, .arg = &power_context },
},
.attribute = SLEEP_RETENTION_MODULE_ATTR_PASSIVE | SLEEP_RETENTION_MODULE_ATTR_ATTACH
};
@@ -101,8 +134,13 @@ ESP_SYSTEM_INIT_FN(sleep_power_startup_init, SECONDARY, BIT(0), 108)
if (err != ESP_OK) {
ESP_LOGW(TAG, "failed to init power retention module, err=%d", err);
} else {
#if PMU_SLEEP_PRIV_ENABLED
pmu_sleep_data_t *data = (pmu_sleep_data_t *)PMU_instance()->priv;
data->func[PMU_SLEEP_PRIV_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT] = &ana_wait_ctx;
data->func[PMU_SLEEP_PRIV_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT] = &power_context.ana_wait;
#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND
data->func[PMU_SLEEP_PRIV_MODEM_LOCK_CLK_POWER] = &power_context.clock;
#endif
#endif
}
return ESP_OK;
@@ -123,3 +161,21 @@ void pmu_sleep_power_analog_wait_config(void *data, const uint16_t analog_wait[A
regdma_link_set_write_wait_content(ana_wait_ctx->regdma_desc[i], (uint32_t)analog_wait[i] << PMU_ANA_WAIT_TARGET_S, PMU_ANA_WAIT_TARGET_M);
}
}
#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND
void pmu_sleep_power_clock_config(void *data, const uint32_t config)
{
pmu_sleep_data_t *datap = (pmu_sleep_data_t *)data;
if (!datap || !datap->func[PMU_SLEEP_PRIV_MODEM_LOCK_CLK_POWER]) {
return;
}
pmu_sleep_power_clock_context_t *clk = (pmu_sleep_power_clock_context_t *)datap->func[PMU_SLEEP_PRIV_MODEM_LOCK_CLK_POWER];
if (clk->regdma_desc[0]) {
regdma_link_set_write_wait_content(clk->regdma_desc[0], config & XTALX2_TIE_HIGH_MASK, XTALX2_TIE_HIGH_MASK);
}
if (clk->regdma_desc[1]) {
regdma_link_set_write_wait_content(clk->regdma_desc[1], config & BBPLL_TIE_HIGH_MASK, BBPLL_TIE_HIGH_MASK);
}
}
#endif
@@ -112,7 +112,7 @@ typedef struct {
const pmu_lp_system_analog_param_t* pmu_lp_system_analog_param_default(pmu_lp_mode_t mode);
#if CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT
#if CONFIG_PM_MODEM_STATE_ENABLED
#define ANALOG_WAIT_CTRL_NUM 3 // S2M, M2S, M2A
/**
* @brief Update content of selected analog wait ctrl REGDMA links.
@@ -121,15 +121,29 @@ const pmu_lp_system_analog_param_t* pmu_lp_system_analog_param_default(pmu_lp_mo
* @param analog_wait Update analog wait values at S2M, M2S, M2A retention links.
*/
void pmu_sleep_power_analog_wait_config(void *data, const uint16_t analog_wait[ANALOG_WAIT_CTRL_NUM]);
#endif
#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND
/**
* @brief Back-fill M2A clk power REGDMA write values before sleep.
*
* @param data PMU sleep data context.
* @param config IMM TIE_HIGH bits to write.
*/
void pmu_sleep_power_clock_config(void *data, const uint32_t config);
#endif // SOC_PM_MODEM_LOCK_CLK_WORKAROUND
#endif // CONFIG_PM_MODEM_STATE_ENABLED
/* Enabled when this chip needs any pmu_sleep_data_t priv slot; conditions differ per chip. */
#define PMU_SLEEP_PRIV_ENABLED (CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT || SOC_PM_SUPPORT_PMU_RETENTION_CLK_ICG)
#define PMU_SLEEP_PRIV_ENABLED (CONFIG_PM_MODEM_STATE_ENABLED || SOC_PM_SUPPORT_PMU_RETENTION_CLK_ICG || SOC_PM_MODEM_LOCK_CLK_WORKAROUND)
#if PMU_SLEEP_PRIV_ENABLED
enum {
#if CONFIG_PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT
#if CONFIG_PM_MODEM_STATE_ENABLED
PMU_SLEEP_PRIV_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT,
#endif
#if SOC_PM_MODEM_LOCK_CLK_WORKAROUND
PMU_SLEEP_PRIV_MODEM_LOCK_CLK_POWER,
#endif // SOC_PM_MODEM_LOCK_CLK_WORKAROUND
#endif // CONFIG_PM_MODEM_STATE_ENABLED
#if SOC_PM_SUPPORT_PMU_RETENTION_CLK_ICG
PMU_SLEEP_PRIV_HW_RETENTION_ICG_CLK,
#endif
@@ -25,16 +25,19 @@
ESP_HW_LOG_ATTR_TAG(TAG, "rtc_clk");
// Current PLL frequency, in 96MHz. Zero if PLL is not enabled.
static int s_cur_pll_freq;
#ifndef BOOTLOADER_BUILD
// BBPLL frequency option, in 96MHz. Zero if BBPLL is not enabled / needs recalibration.
static int s_cur_pll_freq = 0;
static uint32_t s_bbpll_digi_consumers_ref_count = 0; // Currently, it only tracks whether the 48MHz PHY clock is in-use by USB Serial/JTAG
#if !BOOTLOADER_BUILD
// Indicate whether the specific cpu clock source is acquired by the hp root clock (i.e. whether ref_cnt in esp_clk_tree.c is incremented by the hp root clock)
/**
* Whether CPU currently holds a clk_tree ref on BBPLL / XTAL_X2.
* Survives DFS set_config_fast(XTAL) (keep-hot) and light-sleep (PMU restores ACTIVE XPD on wake).
* Cleared only on real leave via set_config / set_xtal.
*/
static bool s_is_pll_acquired = (CONFIG_BOOTLOADER_CPU_CLK_FREQ_MHZ == 96 || CONFIG_BOOTLOADER_CPU_CLK_FREQ_MHZ == 48);
static bool s_is_xtal_x2_acquired = (CONFIG_BOOTLOADER_CPU_CLK_FREQ_MHZ == 64);
#endif
void rtc_clk_bbpll_add_consumer(void)
{
@@ -45,6 +48,7 @@ void rtc_clk_bbpll_remove_consumer(void)
{
s_bbpll_digi_consumers_ref_count -= 1;
}
#endif
void rtc_clk_32k_enable(bool enable)
{
@@ -137,19 +141,6 @@ soc_rtc_fast_clk_src_t rtc_clk_fast_src_get(void)
return clk_ll_rtc_fast_get_src();
}
#if BOOTLOADER_BUILD
static void rtc_clk_bbpll_disable(void)
{
clk_ll_bbpll_disable();
s_cur_pll_freq = 0;
}
static void rtc_clk_bbpll_enable(void)
{
clk_ll_bbpll_enable();
}
#endif
static void rtc_clk_bbpll_configure(soc_xtal_freq_t xtal_freq, int pll_freq)
{
/* Digital part */
@@ -167,7 +158,9 @@ static void rtc_clk_bbpll_configure(soc_xtal_freq_t xtal_freq, int pll_freq)
clk_ll_bbpll_calibration_stop();
ANALOG_CLOCK_DISABLE();
#ifndef BOOTLOADER_BUILD
s_cur_pll_freq = pll_freq;
#endif
}
/**
@@ -281,64 +274,62 @@ __attribute__((weak)) void rtc_clk_set_cpu_switch_to_bbpll(int event_id)
{
}
static void rtc_clk_cpu_src_clk_enable(soc_cpu_clk_src_t new_src, uint32_t new_src_freq_mhz)
static void rtc_clk_update_pll_state_on_cpu_src_switching_start(soc_cpu_clk_src_t new_src, uint32_t new_src_freq_mhz)
{
#ifdef BOOTLOADER_BUILD
if (new_src == SOC_CPU_CLK_SRC_PLL) {
bool truly_enabled = false;
#if BOOTLOADER_BUILD
rtc_clk_bbpll_enable();
truly_enabled = true;
clk_ll_bbpll_enable();
rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), new_src_freq_mhz);
} else if (new_src == SOC_CPU_CLK_SRC_XTAL_X2) {
clk_ll_xtal_x2_enable();
}
#else
if (new_src == SOC_CPU_CLK_SRC_PLL) {
bool need_configure = false;
if (!s_is_pll_acquired) {
truly_enabled = esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_BBPLL, true);
need_configure = !esp_clk_tree_is_power_on(SOC_ROOT_CIRCUIT_CLK_BBPLL);
esp_clk_tree_enable_src(SOC_MOD_CLK_BBPLL, true);
s_is_pll_acquired = true;
}
#endif
if (truly_enabled || (s_cur_pll_freq != new_src_freq_mhz)) {
if (need_configure || (s_cur_pll_freq != (int)new_src_freq_mhz)) {
rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), new_src_freq_mhz);
}
} else if (new_src == SOC_CPU_CLK_SRC_XTAL_X2) {
#if BOOTLOADER_BUILD
clk_ll_xtal_x2_enable();
#else
if (!s_is_xtal_x2_acquired) {
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_XTAL_X2, true);
esp_clk_tree_enable_src(SOC_MOD_CLK_XTAL_X2, true);
s_is_xtal_x2_acquired = true;
}
#endif
}
#endif
}
static void rtc_clk_cpu_src_clk_disable(soc_cpu_clk_src_t old_src)
#ifndef BOOTLOADER_BUILD
static void rtc_clk_update_pll_state_on_cpu_src_switching_end(soc_cpu_clk_src_t old_src)
{
if ((old_src == SOC_CPU_CLK_SRC_PLL) && !s_bbpll_digi_consumers_ref_count) {
#if BOOTLOADER_BUILD
rtc_clk_bbpll_disable();
#else
assert(s_is_pll_acquired);
bool truly_disabled = esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_BBPLL, false);
esp_clk_tree_enable_src(SOC_MOD_CLK_BBPLL, false);
s_is_pll_acquired = false;
if (truly_disabled) {
if (!esp_clk_tree_is_power_on(SOC_ROOT_CIRCUIT_CLK_BBPLL)) {
s_cur_pll_freq = 0;
}
#endif
} else if (old_src == SOC_CPU_CLK_SRC_XTAL_X2) {
#if BOOTLOADER_BUILD
clk_ll_xtal_x2_disable();
#else
assert(s_is_xtal_x2_acquired);
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_XTAL_X2, false);
esp_clk_tree_enable_src(SOC_MOD_CLK_XTAL_X2, false);
s_is_xtal_x2_acquired = false;
#endif
}
}
#endif
void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config)
{
soc_cpu_clk_src_t old_cpu_clk_src = clk_ll_cpu_get_src();
if (old_cpu_clk_src != config->source) {
rtc_clk_cpu_src_clk_enable(config->source, config->source_freq_mhz);
__attribute__((unused)) soc_cpu_clk_src_t old_cpu_clk_src = clk_ll_cpu_get_src();
#ifndef BOOTLOADER_BUILD
bool src_changed = (old_cpu_clk_src != config->source);
if (src_changed)
#endif
{
rtc_clk_update_pll_state_on_cpu_src_switching_start(config->source, config->source_freq_mhz);
}
if (config->source == SOC_CPU_CLK_SRC_XTAL) {
@@ -353,9 +344,11 @@ void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config)
rtc_clk_cpu_freq_to_xtal_x2(config->freq_mhz, config->div);
}
if (old_cpu_clk_src != config->source) {
rtc_clk_cpu_src_clk_disable(old_cpu_clk_src);
#ifndef BOOTLOADER_BUILD
if (src_changed) {
rtc_clk_update_pll_state_on_cpu_src_switching_end(old_cpu_clk_src);
}
#endif
}
void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t *out_config)
@@ -391,20 +384,21 @@ void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t *out_config)
};
}
#ifndef BOOTLOADER_BUILD
void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t *config)
{
/* Mux only — Fall back to set_config when PLL/XTALx2 must be reacquired or recalibrated
* (s_cur_pll_freq == 0 after sleep). */
if (config->source == SOC_CPU_CLK_SRC_XTAL) {
rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div);
} else if (config->source == SOC_CPU_CLK_SRC_PLL &&
s_cur_pll_freq == config->source_freq_mhz) {
s_is_pll_acquired &&
s_cur_pll_freq == (int)config->source_freq_mhz) {
rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz);
} else if (config->source == SOC_CPU_CLK_SRC_RC_FAST) {
rtc_clk_cpu_freq_to_rc_fast();
} else if (config->source == SOC_CPU_CLK_SRC_XTAL_X2
#if !BOOTLOADER_BUILD
&& s_is_xtal_x2_acquired
#endif
) {
} else if (config->source == SOC_CPU_CLK_SRC_XTAL_X2 &&
s_is_xtal_x2_acquired) {
rtc_clk_cpu_freq_to_xtal_x2(config->freq_mhz, config->div);
} else {
/* fallback */
@@ -419,22 +413,29 @@ void rtc_clk_cpu_freq_set_xtal(void)
rtc_clk_cpu_freq_to_xtal(freq_mhz, 1);
if (old_cpu_clk_src != SOC_CPU_CLK_SRC_XTAL) {
rtc_clk_cpu_src_clk_disable(old_cpu_clk_src);
rtc_clk_update_pll_state_on_cpu_src_switching_end(old_cpu_clk_src);
}
}
#endif
FORCE_IRAM_ATTR void rtc_clk_cpu_set_to_default_config(void)
{
int freq_mhz = (int)rtc_clk_xtal_freq_get();
rtc_clk_cpu_freq_to_xtal(freq_mhz, 1);
s_cur_pll_freq = 0; // no disable PLL, but set freq to 0 to trigger a PLL calibration after wake-up from sleep
}
#ifndef BOOTLOADER_BUILD
void rtc_clk_cpu_freq_set_xtal_for_sleep(void)
{
rtc_clk_cpu_set_to_default_config();
int freq_mhz = (int)rtc_clk_xtal_freq_get();
/* Mux only — do not release CPU clk_tree hold. PMU restores ACTIVE XPD on
* wake; clearing s_cur_pll_freq forces recalibration via set_config fallback. */
rtc_clk_cpu_freq_to_xtal(freq_mhz, 1);
s_cur_pll_freq = 0;
}
#endif
void rtc_clk_cpu_freq_to_pll_and_pll_lock_release(int cpu_freq_mhz)
{
@@ -516,7 +516,9 @@ void rtc_clk_cpu_freq_set_xtal(void)
int freq_mhz = (int)rtc_clk_xtal_freq_get();
rtc_clk_cpu_freq_to_xtal(freq_mhz, 1, false);
rtc_clk_update_pll_state_on_cpu_src_switching_end(old_cpu_clk_src);
if (old_cpu_clk_src != SOC_CPU_CLK_SRC_XTAL) {
rtc_clk_update_pll_state_on_cpu_src_switching_end(old_cpu_clk_src);
}
}
FORCE_IRAM_ATTR void rtc_clk_cpu_set_to_default_config(void)
+3 -4
View File
@@ -345,12 +345,11 @@ menu "Power Management"
keeping all other peripheral clocks gated to minimize sleep
power consumption.
config PM_SKIP_MODEM_TO_ACTIVE_ANALOG_WAIT
config PM_MODEM_STATE_ENABLED
bool
depends on SOC_PM_SUPPORT_PMU_MODEM_STATE
depends on SOC_PM_SUPPORT_PMU_MODEM_STATE && IDF_TARGET_ESP32H4
default n
help
If enabled, the analog wait time when waking up from modem to active state will be skipped.
This will reduce the wakeup time.
If enabled, the modem state is supported. Note: This option is only supported on ESP32H4.
endmenu # "Power Management"
+1 -1
View File
@@ -2,7 +2,7 @@ menu "ESP Security Specific"
config ESP_CRYPTO_CLK_ON_DEMAND
bool "Enable on-demand crypto clock management"
depends on IDF_TARGET_ESP32S31
depends on IDF_TARGET_ESP32S31 || IDF_TARGET_ESP32H4 || IDF_TARGET_ESP32H21
default y if PM_ENABLE
default n
help
@@ -4,10 +4,72 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include "sdkconfig.h"
#include "esp_crypto_clk.h"
#include "soc/clk_tree_defs.h"
#include "hal/clk_gate_ll.h"
#include "esp_private/esp_clk_tree_common.h"
#if !NON_OS_BUILD
#include "esp_private/critical_section.h"
#endif
#if !NON_OS_BUILD
DEFINE_CRIT_SECTION_LOCK_STATIC(s_crypto_common_clk_mux);
#define CRYPTO_CLK_LOCK() esp_os_enter_critical_safe(&s_crypto_common_clk_mux)
#define CRYPTO_CLK_UNLOCK() esp_os_exit_critical_safe(&s_crypto_common_clk_mux)
#else
#define CRYPTO_CLK_LOCK()
#define CRYPTO_CLK_UNLOCK()
#endif
static void esp_crypto_pll_f96m_enable(bool enable)
{
#if !NON_OS_BUILD
esp_clk_tree_enable_src(SOC_MOD_CLK_PLL_F96M, enable);
#else
/* Bootloader: no esp_clk_tree; toggle the ref gate directly. */
_clk_gate_ll_ref_96m_clk_en(enable);
#endif
}
#if CONFIG_ESP_CRYPTO_CLK_ON_DEMAND
static int s_crypto_common_clk_ref_cnt;
void esp_crypto_common_clk_enable(bool enable)
{
(void)enable;
CRYPTO_CLK_LOCK();
if (enable) {
if (s_crypto_common_clk_ref_cnt++ == 0) {
/* Parent: PLL_F96M (see esp_crypto_clk_init() sec_clk_sel). */
esp_crypto_pll_f96m_enable(true);
}
} else if (s_crypto_common_clk_ref_cnt > 0 && --s_crypto_common_clk_ref_cnt == 0) {
esp_crypto_pll_f96m_enable(false);
}
CRYPTO_CLK_UNLOCK();
}
#else /* !CONFIG_ESP_CRYPTO_CLK_ON_DEMAND */
static bool s_crypto_clk_always_on_done;
static void esp_crypto_clk_always_on(void)
{
CRYPTO_CLK_LOCK();
if (!s_crypto_clk_always_on_done) {
esp_crypto_pll_f96m_enable(true);
s_crypto_clk_always_on_done = true;
}
CRYPTO_CLK_UNLOCK();
}
void esp_crypto_common_clk_enable(bool enable)
{
/* Keep clocks always on: enable once, ignore disable. */
if (enable) {
esp_crypto_clk_always_on();
}
}
#endif /* CONFIG_ESP_CRYPTO_CLK_ON_DEMAND */
@@ -4,20 +4,21 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc.h"
#include "soc/pcr_reg.h"
#include "esp_private/esp_clk_tree_common.h"
#include "hal/sec_ll.h"
#pragma once
#include <stdbool.h>
#include "sdkconfig.h"
#include "hal/sec_ll.h"
#include "soc/clk_tree_defs.h"
void esp_crypto_common_clk_enable(bool enable);
static inline void esp_crypto_clk_init(void)
{
#if !CONFIG_ESP_CRYPTO_CLK_ON_DEMAND
/* Keep crypto clocks always on for better crypto performance. */
esp_crypto_common_clk_enable(true);
#endif
// Set crypto clock (`clk_sec`) to use 96M PLL clock
esp_clk_tree_enable_src(SOC_MOD_CLK_PLL_F96M, true);
sec_ll_crypto_clk_src_sel(SOC_MOD_CLK_PLL_F96M);
}
@@ -4,10 +4,72 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include "sdkconfig.h"
#include "esp_crypto_clk.h"
#include "soc/clk_tree_defs.h"
#include "hal/clk_gate_ll.h"
#include "esp_private/esp_clk_tree_common.h"
#if !NON_OS_BUILD
#include "esp_private/critical_section.h"
#endif
#if !NON_OS_BUILD
DEFINE_CRIT_SECTION_LOCK_STATIC(s_crypto_common_clk_mux);
#define CRYPTO_CLK_LOCK() esp_os_enter_critical_safe(&s_crypto_common_clk_mux)
#define CRYPTO_CLK_UNLOCK() esp_os_exit_critical_safe(&s_crypto_common_clk_mux)
#else
#define CRYPTO_CLK_LOCK()
#define CRYPTO_CLK_UNLOCK()
#endif
static void esp_crypto_pll_f96m_enable(bool enable)
{
#if !NON_OS_BUILD
esp_clk_tree_enable_src(SOC_MOD_CLK_PLL_F96M, enable);
#else
/* Bootloader: no esp_clk_tree; toggle the ref gate directly. */
_clk_gate_ll_ref_96m_clk_en(enable);
#endif
}
#if CONFIG_ESP_CRYPTO_CLK_ON_DEMAND
static int s_crypto_common_clk_ref_cnt;
void esp_crypto_common_clk_enable(bool enable)
{
(void)enable;
CRYPTO_CLK_LOCK();
if (enable) {
if (s_crypto_common_clk_ref_cnt++ == 0) {
/* Parent: PLL_F96M (see esp_crypto_clk_init() sec_clk_sel). */
esp_crypto_pll_f96m_enable(true);
}
} else if (s_crypto_common_clk_ref_cnt > 0 && --s_crypto_common_clk_ref_cnt == 0) {
esp_crypto_pll_f96m_enable(false);
}
CRYPTO_CLK_UNLOCK();
}
#else /* !CONFIG_ESP_CRYPTO_CLK_ON_DEMAND */
static bool s_crypto_clk_always_on_done;
static void esp_crypto_clk_always_on(void)
{
CRYPTO_CLK_LOCK();
if (!s_crypto_clk_always_on_done) {
esp_crypto_pll_f96m_enable(true);
s_crypto_clk_always_on_done = true;
}
CRYPTO_CLK_UNLOCK();
}
void esp_crypto_common_clk_enable(bool enable)
{
/* Keep clocks always on: enable once, ignore disable. */
if (enable) {
esp_crypto_clk_always_on();
}
}
#endif /* CONFIG_ESP_CRYPTO_CLK_ON_DEMAND */
@@ -4,20 +4,21 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc.h"
#include "soc/pcr_reg.h"
#include "esp_private/esp_clk_tree_common.h"
#include "hal/sec_ll.h"
#pragma once
#include <stdbool.h>
#include "sdkconfig.h"
#include "hal/sec_ll.h"
#include "soc/clk_tree_defs.h"
void esp_crypto_common_clk_enable(bool enable);
static inline void esp_crypto_clk_init(void)
{
#if !CONFIG_ESP_CRYPTO_CLK_ON_DEMAND
/* Keep crypto clocks always on for better crypto performance. */
esp_crypto_common_clk_enable(true);
#endif
// Set crypto clock (`clk_sec`) to use 96M PLL clock
esp_clk_tree_enable_src(SOC_MOD_CLK_PLL_F96M, true);
sec_ll_crypto_clk_src_sel(SOC_MOD_CLK_PLL_F96M);
}
@@ -15,10 +15,10 @@ void esp_crypto_common_clk_enable(bool enable);
static inline void esp_crypto_clk_init(void)
{
// Set crypto clock (`clk_sec`) to use 240M PLL clock
sec_ll_crypto_clk_src_sel(SOC_MOD_CLK_PLL_F240M);
#if !CONFIG_ESP_CRYPTO_CLK_ON_DEMAND
/* Keep crypto clocks always on for better crypto performance. */
esp_crypto_common_clk_enable(true);
#endif
// Set crypto clock (`clk_sec`) to use 240M PLL clock
sec_ll_crypto_clk_src_sel(SOC_MOD_CLK_PLL_F240M);
}
@@ -69,6 +69,7 @@ typedef enum {
typedef enum {
SOC_ROOT_CIRCUIT_CLK_BBPLL, /*!< BBPLL_CLK is the output of the PLL generator circuit */
SOC_ROOT_CIRCUIT_CLK_XTAL_X2, /*!< XTAL_X2_CLK is the output of the XTAL_X2 generator circuit */
SOC_ROOT_CIRCUIT_CLK_MAX,
} soc_root_clk_circuit_t;
/**
@@ -140,6 +141,8 @@ typedef enum {
SOC_MOD_CLK_XTAL32K, /*!< XTAL32K_CLK comes from the external 32kHz crystal, passing a clock gating to the peripherals */
SOC_MOD_CLK_RC_FAST, /*!< RC_FAST_CLK comes from the internal 20MHz rc oscillator, passing a clock gating to the peripherals */
SOC_MOD_CLK_XTAL, /*!< XTAL_CLK comes from the external 32MHz crystal */
SOC_MOD_CLK_XTAL_X2, /*!< XTAL_X2_CLK is the output of the XTAL_X2 generator circuit (64MHz); CPU clock source, not the gated F64M */
SOC_MOD_CLK_BBPLL, /*!< BBPLL_CLK is the output of the PLL generator circuit (96MHz); CPU clock source, not the gated F96M */
SOC_MOD_CLK_INVALID, /*!< Indication of the end of the available module clock sources */
} soc_module_clk_t;
+37 -37
View File
@@ -1,5 +1,5 @@
/**
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -1947,54 +1947,54 @@ extern "C" {
* SPLL DIV clock-gating configuration register
*/
#define PCR_PLL_DIV_CLK_EN_REG (DR_REG_PCR_BASE + 0x124)
/** PCR_PLL_240M_CLK_EN : R/W; bitpos: [0]; default: 1;
/** PCR_PLL_96M_CLK_EN : R/W; bitpos: [0]; default: 1;
* This field is used to open 96 MHz clock (SPLL) driven from SPLL. 0: close, 1:
* open(default). Only available when high-speed clock-source SPLL is active.
*/
#define PCR_PLL_240M_CLK_EN (BIT(0))
#define PCR_PLL_240M_CLK_EN_M (PCR_PLL_240M_CLK_EN_V << PCR_PLL_240M_CLK_EN_S)
#define PCR_PLL_240M_CLK_EN_V 0x00000001U
#define PCR_PLL_240M_CLK_EN_S 0
/** PCR_PLL_160M_CLK_EN : R/W; bitpos: [1]; default: 1;
* This field is used to open 64 MHz clock (div3 of SPLL) driven from SPLL. 0: close,
#define PCR_PLL_96M_CLK_EN (BIT(0))
#define PCR_PLL_96M_CLK_EN_M (PCR_PLL_96M_CLK_EN_V << PCR_PLL_96M_CLK_EN_S)
#define PCR_PLL_96M_CLK_EN_V 0x00000001U
#define PCR_PLL_96M_CLK_EN_S 0
/** PCR_PLL_64M_CLK_EN : R/W; bitpos: [1]; default: 1;
* This field is used to open 64 MHz clock (XTALX2) driven from XTALX2. 0: close,
* 1: open(default). Only available when high-speed clock-source SPLL is active.
*/
#define PCR_PLL_160M_CLK_EN (BIT(1))
#define PCR_PLL_160M_CLK_EN_M (PCR_PLL_160M_CLK_EN_V << PCR_PLL_160M_CLK_EN_S)
#define PCR_PLL_160M_CLK_EN_V 0x00000001U
#define PCR_PLL_160M_CLK_EN_S 1
/** PCR_PLL_120M_CLK_EN : R/W; bitpos: [2]; default: 1;
* This field is used to open 48 MHz clock (div4 of SPLL) driven from SPLL. 0: close,
#define PCR_PLL_64M_CLK_EN (BIT(1))
#define PCR_PLL_64M_CLK_EN_M (PCR_PLL_64M_CLK_EN_V << PCR_PLL_64M_CLK_EN_S)
#define PCR_PLL_64M_CLK_EN_V 0x00000001U
#define PCR_PLL_64M_CLK_EN_S 1
/** PCR_PLL_48M_CLK_EN : R/W; bitpos: [2]; default: 1;
* This field is used to open 48 MHz clock (div2 of SPLL) driven from SPLL. 0: close,
* 1: open(default). Only available when high-speed clock-source SPLL is active.
*/
#define PCR_PLL_120M_CLK_EN (BIT(2))
#define PCR_PLL_120M_CLK_EN_M (PCR_PLL_120M_CLK_EN_V << PCR_PLL_120M_CLK_EN_S)
#define PCR_PLL_120M_CLK_EN_V 0x00000001U
#define PCR_PLL_120M_CLK_EN_S 2
/** PCR_PLL_80M_CLK_EN : R/W; bitpos: [3]; default: 1;
* This field is used to open 32 MHz clock (div6 of SPLL) driven from SPLL. 0: close,
* 1: open(default). Only available when high-speed clock-source SPLL is active.
*/
#define PCR_PLL_80M_CLK_EN (BIT(3))
#define PCR_PLL_80M_CLK_EN_M (PCR_PLL_80M_CLK_EN_V << PCR_PLL_80M_CLK_EN_S)
#define PCR_PLL_80M_CLK_EN_V 0x00000001U
#define PCR_PLL_80M_CLK_EN_S 3
/** PCR_PLL_48M_CLK_EN : R/W; bitpos: [4]; default: 1;
* This field is used to open 16 MHz clock (div10 of SPLL) driven from SPLL. 0: close,
* 1: open(default). Only available when high-speed clock-source SPLL is active.
*/
#define PCR_PLL_48M_CLK_EN (BIT(4))
#define PCR_PLL_48M_CLK_EN (BIT(2))
#define PCR_PLL_48M_CLK_EN_M (PCR_PLL_48M_CLK_EN_V << PCR_PLL_48M_CLK_EN_S)
#define PCR_PLL_48M_CLK_EN_V 0x00000001U
#define PCR_PLL_48M_CLK_EN_S 4
/** PCR_PLL_40M_CLK_EN : R/W; bitpos: [5]; default: 1;
#define PCR_PLL_48M_CLK_EN_S 2
/** PCR_PLL_32M_CLK_EN : R/W; bitpos: [3]; default: 1;
* This field is used to open 32 MHz clock (div2 of XTALX2) driven from XTALX2. 0: close,
* 1: open(default). Only available when high-speed clock-source SPLL is active.
*/
#define PCR_PLL_32M_CLK_EN (BIT(3))
#define PCR_PLL_32M_CLK_EN_M (PCR_PLL_32M_CLK_EN_V << PCR_PLL_32M_CLK_EN_S)
#define PCR_PLL_32M_CLK_EN_V 0x00000001U
#define PCR_PLL_32M_CLK_EN_S 3
/** PCR_PLL_16M_CLK_EN : R/W; bitpos: [4]; default: 1;
* This field is used to open 16 MHz clock (div6 of SPLL) driven from SPLL. 0: close,
* 1: open(default). Only available when high-speed clock-source SPLL is active.
*/
#define PCR_PLL_16M_CLK_EN (BIT(4))
#define PCR_PLL_16M_CLK_EN_M (PCR_PLL_16M_CLK_EN_V << PCR_PLL_16M_CLK_EN_S)
#define PCR_PLL_16M_CLK_EN_V 0x00000001U
#define PCR_PLL_16M_CLK_EN_S 4
/** PCR_PLL_8M_CLK_EN : R/W; bitpos: [5]; default: 1;
* This field is used to open 8 MHz clock (div12 of SPLL) driven from SPLL. 0: close,
* 1: open(default). Only available when high-speed clock-source SPLL is active.
*/
#define PCR_PLL_40M_CLK_EN (BIT(5))
#define PCR_PLL_40M_CLK_EN_M (PCR_PLL_40M_CLK_EN_V << PCR_PLL_40M_CLK_EN_S)
#define PCR_PLL_40M_CLK_EN_V 0x00000001U
#define PCR_PLL_40M_CLK_EN_S 5
#define PCR_PLL_8M_CLK_EN (BIT(5))
#define PCR_PLL_8M_CLK_EN_M (PCR_PLL_8M_CLK_EN_V << PCR_PLL_8M_CLK_EN_S)
#define PCR_PLL_8M_CLK_EN_V 0x00000001U
#define PCR_PLL_8M_CLK_EN_S 5
/** PCR_CTRL_CLK_OUT_EN_REG register
* CLK_OUT_EN configuration register
@@ -1,5 +1,5 @@
/**
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -1545,36 +1545,36 @@ typedef union {
*/
typedef union {
struct {
/** pll_240m_clk_en : R/W; bitpos: [0]; default: 1;
/** pll_96m_clk_en : R/W; bitpos: [0]; default: 1;
* This field is used to open 96 MHz clock (SPLL) driven from SPLL. 0: close, 1:
* open(default). Only available when high-speed clock-source SPLL is active.
*/
uint32_t pll_240m_clk_en:1;
/** pll_160m_clk_en : R/W; bitpos: [1]; default: 1;
uint32_t pll_96m_clk_en:1;
/** pll_64m_clk_en : R/W; bitpos: [1]; default: 1;
* This field is used to open 64 MHz clock (div3 of SPLL) driven from SPLL. 0: close,
* 1: open(default). Only available when high-speed clock-source SPLL is active.
*/
uint32_t pll_160m_clk_en:1;
/** pll_120m_clk_en : R/W; bitpos: [2]; default: 1;
uint32_t pll_64m_clk_en:1;
/** pll_48m_clk_en : R/W; bitpos: [2]; default: 1;
* This field is used to open 48 MHz clock (div4 of SPLL) driven from SPLL. 0: close,
* 1: open(default). Only available when high-speed clock-source SPLL is active.
*/
uint32_t pll_120m_clk_en:1;
/** pll_80m_clk_en : R/W; bitpos: [3]; default: 1;
uint32_t pll_48m_clk_en:1;
/** pll_32m_clk_en : R/W; bitpos: [3]; default: 1;
* This field is used to open 32 MHz clock (div6 of SPLL) driven from SPLL. 0: close,
* 1: open(default). Only available when high-speed clock-source SPLL is active.
*/
uint32_t pll_80m_clk_en:1;
/** pll_48m_clk_en : R/W; bitpos: [4]; default: 1;
uint32_t pll_32m_clk_en:1;
/** pll_16m_clk_en : R/W; bitpos: [4]; default: 1;
* This field is used to open 16 MHz clock (div10 of SPLL) driven from SPLL. 0: close,
* 1: open(default). Only available when high-speed clock-source SPLL is active.
*/
uint32_t pll_48m_clk_en:1;
/** pll_40m_clk_en : R/W; bitpos: [5]; default: 1;
uint32_t pll_16m_clk_en:1;
/** pll_8m_clk_en : R/W; bitpos: [5]; default: 1;
* This field is used to open 8 MHz clock (div12 of SPLL) driven from SPLL. 0: close,
* 1: open(default). Only available when high-speed clock-source SPLL is active.
*/
uint32_t pll_40m_clk_en:1;
uint32_t pll_8m_clk_en:1;
uint32_t reserved_6:26;
};
uint32_t val;
@@ -69,6 +69,7 @@ typedef enum {
typedef enum {
SOC_ROOT_CIRCUIT_CLK_BBPLL, /*!< BBPLL_CLK is the output of the PLL generator circuit */
SOC_ROOT_CIRCUIT_CLK_XTAL_X2, /*!< XTAL_X2_CLK is the output of the XTAL_X2 generator circuit */
SOC_ROOT_CIRCUIT_CLK_MAX,
} soc_root_clk_circuit_t;
/**
@@ -141,7 +142,8 @@ typedef enum {
SOC_MOD_CLK_XTAL32K, /*!< XTAL32K_CLK comes from the external 32kHz crystal, passing a clock gating to the peripherals */
SOC_MOD_CLK_RC_FAST, /*!< RC_FAST_CLK comes from the internal 20MHz rc oscillator, passing a clock gating to the peripherals */
SOC_MOD_CLK_XTAL, /*!< XTAL_CLK comes from the external 32MHz crystal */
SOC_MOD_CLK_XTAL_X2, /*!< XTAL_X2_CLK is the output of the XTAL_X2 generator circuit (64MHz); CPU clock source, not the gated F64M */
SOC_MOD_CLK_BBPLL, /*!< BBPLL_CLK is the output of the PLL generator circuit (96MHz); CPU clock source, not the gated F96M */
SOC_MOD_CLK_INVALID, /*!< Indication of the end of the available module clock sources */
} soc_module_clk_t;