feat(esp_hw_support): support esp32s31 clock tree management

This commit is contained in:
wuzhenghui
2026-07-06 17:01:31 +08:00
parent d67ba6dd0c
commit f9428ecc1e
23 changed files with 795 additions and 227 deletions

View File

@@ -9,6 +9,8 @@ archive: libesp_hw_support.a
entries:
if ADC_ONESHOT_CTRL_FUNC_IN_IRAM = y:
esp_clk_tree: esp_clk_tree_enable_src (noflash)
if IDF_TARGET_ESP32P4 || IDF_TARGET_ESP32S31 = y:
esp_clk_tree: s_gated_ref_clks (noflash)
[mapping:adc_hal]
archive: libesp_hal_ana_conv.a

View File

@@ -256,6 +256,19 @@ FORCE_INLINE_ATTR void _clk_gate_ll_ref_50m_clk_en(bool enable)
_clk_gate_ll_ref_50m_clk_en(__VA_ARGS__); \
} while(0)
/**
* Enable or disable the clock gate for ref_60m.
* @param enable Enable / disable
*/
FORCE_INLINE_ATTR void _clk_gate_ll_ref_60m_clk_en(bool enable)
{
HP_SYS_CLKRST.ref_60m_ctrl0.reg_ref_60m_clk_en = enable;
}
#define clk_gate_ll_ref_60m_clk_en(...) do { \
(void)__DECLARE_RCC_ATOMIC_ENV; \
_clk_gate_ll_ref_60m_clk_en(__VA_ARGS__); \
} while (0)
/**
* Enable or disable the clock gate for ref_80m (derived from BBPLL).
* @param enable Enable / disable
@@ -271,6 +284,32 @@ FORCE_INLINE_ATTR void _clk_gate_ll_ref_80m_clk_en(bool enable)
_clk_gate_ll_ref_80m_clk_en(__VA_ARGS__); \
} while(0)
/**
* Enable or disable the ref_80m mux clock gate.
* @param en 0: disable 1: enable
*/
FORCE_INLINE_ATTR void _clk_gate_ll_ref_80m_mux_clk_en(bool en)
{
HP_SYS_CLKRST.ref_80m_ctrl0.reg_ref_80m_mux_clk_en = en;
}
#define clk_gate_ll_ref_80m_mux_clk_en(...) do { \
(void)__DECLARE_RCC_ATOMIC_ENV; \
_clk_gate_ll_ref_80m_mux_clk_en(__VA_ARGS__); \
} while (0)
/**
* Enable or disable the clock gate for ref_120m.
* @param enable Enable / disable
*/
FORCE_INLINE_ATTR void _clk_gate_ll_ref_120m_clk_en(bool enable)
{
HP_SYS_CLKRST.ref_120m_ctrl0.reg_ref_120m_clk_en = enable;
}
#define clk_gate_ll_ref_120m_clk_en(...) do { \
(void)__DECLARE_RCC_ATOMIC_ENV; \
_clk_gate_ll_ref_120m_clk_en(__VA_ARGS__); \
} while (0)
/**
* Enable or disable the clock gate for ref_160m (derived from BBPLL).
* @param enable Enable / disable

View File

@@ -29,7 +29,9 @@
#define CLK_LL_PLL_8M_FREQ_MHZ (8)
#define CLK_LL_PLL_60M_FREQ_MHZ (60)
#define CLK_LL_PLL_80M_FREQ_MHZ (80)
#define CLK_LL_PLL_120M_FREQ_MHZ (120)
#define CLK_LL_PLL_160M_FREQ_MHZ (160)
#define CLK_LL_PLL_240M_FREQ_MHZ (240)
#define CLK_LL_PLL_320M_FREQ_MHZ (320)
@@ -105,6 +107,47 @@ static inline __attribute__((always_inline)) void clk_ll_cpll_disable(void)
CLEAR_PERI_REG_MASK(HP_ALIVE_SYS_HP_CLK_CTRL_REG, HP_ALIVE_SYS_HP_CPLL_300M_CLK_EN);
}
/**
* @brief Power up XTALX2 circuit
*/
static inline __attribute__((always_inline)) void clk_ll_xtalx2_enable(void)
{
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_0_REG, PMU_TIE_HIGH_XPD_XTALX2);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_0_REG, PMU_TIE_HIGH_GLOBAL_XTALX2_ICG);
SET_PERI_REG_MASK(HP_ALIVE_SYS_HP_CLK_CTRL_REG, HP_ALIVE_SYS_HP_XTALX2_80M_CLK_EN);
}
/**
* @brief Power down XTALX2 circuit
*/
static inline __attribute__((always_inline)) void clk_ll_xtalx2_disable(void)
{
CLEAR_PERI_REG_MASK(HP_ALIVE_SYS_HP_CLK_CTRL_REG, HP_ALIVE_SYS_HP_XTALX2_80M_CLK_EN);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_0_REG, PMU_TIE_LOW_GLOBAL_XTALX2_ICG);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_0_REG, PMU_TIE_LOW_XPD_XTALX2);
}
/**
* @brief Get ref_80m clock source mux selection
*
* @return false: BBPLL divided path, true: XTALx2 80MHz (`clk_xtalx2_80m`)
*/
static inline __attribute__((always_inline)) bool clk_ll_ref_80m_get_src(void)
{
return (bool)HP_SYS_CLKRST.ref_80m_ctrl0.reg_ref_80m_sel;
}
/**
* @brief Select REF_80M_CLK source
*
* @param in_sel 0 selects BBPLL divided 80MHz, 1 selects XTALx2 80MHz.
*/
static inline __attribute__((always_inline)) void clk_ll_ref_80m_set_src(uint8_t in_sel)
{
HAL_ASSERT(in_sel == 0 || in_sel == 1);
HP_SYS_CLKRST.ref_80m_ctrl0.reg_ref_80m_sel = in_sel;
}
/**
* @brief Power up BBPLL circuit
*/
@@ -722,6 +765,16 @@ static inline __attribute__((always_inline)) void clk_ll_ref_500m_set_src(uint8_
HP_SYS_CLKRST.ref_500m_ctrl0.reg_ref_500m_sel = in_sel;
}
/**
* @brief Get ref_500m clock source mux selection
*
* @return false: CPLL (320MHz), true: MPLL (500MHz)
*/
static inline __attribute__((always_inline)) bool clk_ll_ref_500m_get_src(void)
{
return (bool)HP_SYS_CLKRST.ref_500m_ctrl0.reg_ref_500m_sel;
}
/**
* @brief Set PLL_F50M_CLK divider. freq of PLL_F50M_CLK = freq of MPLL_CLK / divider
*
@@ -765,6 +818,26 @@ static inline __attribute__((always_inline)) uint32_t clk_ll_pll_f20m_get_divide
return HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.ref_20m_ctrl0, reg_ref_20m_clk_div_num) + 1;
}
/**
* @brief Get PLL_F50M_CLK divider
*
* @return Divider. Divider = (CLK_DIV_NUM + 1).
*/
static inline __attribute__((always_inline)) uint32_t clk_ll_pll_f50m_get_divider(void)
{
return HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.ref_50m_ctrl0, reg_ref_50m_clk_div_num) + 1;
}
/**
* @brief Get PLL_F25M_CLK divider
*
* @return Divider. Divider = (CLK_DIV_NUM + 1).
*/
static inline __attribute__((always_inline)) uint32_t clk_ll_pll_f25m_get_divider(void)
{
return HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.ref_25m_ctrl0, reg_ref_25m_clk_div_num) + 1;
}
/**
* @brief Select the frequency calculation clock source for timergroup0
*

View File

@@ -712,7 +712,7 @@ static inline void gpio_ll_iomux_set_clk_src(soc_module_clk_t src)
case SOC_MOD_CLK_XTAL:
HP_SYS_CLKRST.iomux_ctrl0.reg_iomux_clk_src_sel = 0;
break;
case SOC_MOD_CLK_PLL_F80M:
case SOC_MOD_CLK_REF_F80M:
HP_SYS_CLKRST.iomux_ctrl0.reg_iomux_clk_src_sel = 1;
break;
default:

View File

@@ -124,6 +124,13 @@ bool esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool enable);
*/
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit);
/**
* @brief Chip-specific root clock circuit power status (port/esp_clk_tree.c per target).
*
* Handles all soc_root_clk_circuit_t values except MPLL/APLL (those are in esp_clk_tree_common.c).
*/
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit);
#if SOC_CLK_APLL_SUPPORTED
/**
* @brief Enable APLL power if it has not enabled

View File

@@ -60,11 +60,13 @@ typedef struct {
typedef struct {
soc_module_clk_t clk_id; /*!< Module clock id this descriptor belongs to */
void (*set_src)(uint8_t mux_sel); /*!< Optional: program the upstream PLL mux. NULL when the clock has no mux (e.g. ESP32-P4 PLL_F50M is fixed to MPLL). */
void (*set_divider)(uint32_t divider); /*!< Required: program the divider register (divider value, not the raw "div_num - 1" form). */
void (*set_divider)(uint32_t divider); /*!< Optional: program the divider register (divider value, not div_num - 1). NULL when HW divider is fixed. */
void (*set_gate)(bool enable); /*!< Required: enable/disable the clock gate. */
const esp_clk_tree_derived_upstream_t *upstreams; /*!< Required: candidate upstream PLLs in preference order (used by both auto-pick and explicit-upstream paths). */
size_t upstream_count; /*!< Required: number of entries in `upstreams[]`. */
esp_clk_tree_derived_clk_state_t *state; /*!< Required: pointer to a statically-allocated mutable state slot owned by the target. Engine reads/writes ref_cnt / cur_upstream / cur_divider through this pointer. */
esp_err_t (*acquire_parent)(void); /*!< Optional: power upstream (first acquire or after mux/div commit). Called with s_derived_clk_spinlock held.*/
esp_err_t (*release_parent)(void); /*!< Optional: release upstream on last release. Called with s_derived_clk_spinlock held.*/
} esp_clk_tree_derived_clk_desc_t;
/**

View File

@@ -13,12 +13,15 @@ entries:
cpu: esp_cpu_compare_and_set (noflash)
esp_memory_utils (noflash)
clk_utils (noflash)
# TODO: PM-630
if IDF_TARGET_ESP32S31 != y:
esp_clk_tree: esp_clk_tree_enable_src (noflash)
esp_clk_tree: esp_clk_tree_enable_power (noflash)
esp_clk_tree: esp_clk_tree_is_power_on (noflash)
esp_clk_tree: esp_clk_tree_enable_src (noflash)
if RTC_CLK_FUNC_IN_IRAM = y:
esp_clk_tree: esp_clk_tree_enable_power (noflash)
esp_clk_tree_common:esp_clk_tree_is_power_on (noflash)
if SOC_CLK_MPLL_SUPPORTED = y:
esp_clk_tree_common:esp_clk_tree_mpll_release (noflash)
if SOC_CLK_APLL_SUPPORTED = y:
esp_clk_tree_common:esp_clk_tree_apll_acquire (noflash)
esp_clk_tree_common:esp_clk_tree_apll_release (noflash)
rtc_clk (noflash)
if IDF_TARGET_ESP32 = y:
rtc_clk:rtc_clk_cpu_freq_to_pll_mhz (noflash)

View File

@@ -99,7 +99,7 @@ void esp_clk_tree_initialize(void)
{
}
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit)
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
(void)clk_circuit;
return false;

View File

@@ -79,7 +79,7 @@ void esp_clk_tree_initialize(void)
{
}
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit)
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
(void)clk_circuit;
return false;

View File

@@ -79,7 +79,7 @@ void esp_clk_tree_initialize(void)
{
}
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit)
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
(void)clk_circuit;
return false;

View File

@@ -112,7 +112,7 @@ void esp_clk_tree_initialize(void)
esp_clk_tree_initialized = true;
}
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit)
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
(void)clk_circuit;
return false;

View File

@@ -79,7 +79,7 @@ void esp_clk_tree_initialize(void)
{
}
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit)
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
(void)clk_circuit;
return false;

View File

@@ -79,7 +79,7 @@ void esp_clk_tree_initialize(void)
{
}
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit)
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
(void)clk_circuit;
return false;

View File

@@ -76,7 +76,7 @@ void esp_clk_tree_initialize(void)
{
}
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit)
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
(void)clk_circuit;
return false;

View File

@@ -90,15 +90,13 @@ void esp_clk_tree_initialize(void)
// PLL_F64M ++ for MSPI
}
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit)
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
switch (clk_circuit) {
case SOC_ROOT_CIRCUIT_CLK_XTAL_X2:
if (clk_circuit == SOC_ROOT_CIRCUIT_CLK_XTAL_X2) {
return s_xtal_x2_ref_cnt > 0;
case SOC_ROOT_CIRCUIT_CLK_BBPLL:
}
if (clk_circuit == SOC_ROOT_CIRCUIT_CLK_BBPLL) {
return s_bbpll_ref_cnt > 0;
default:
break;
}
return false;
}

View File

@@ -92,15 +92,13 @@ void esp_clk_tree_initialize(void)
// PLL_F64M ++ for MSPI
}
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit)
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
switch (clk_circuit) {
case SOC_ROOT_CIRCUIT_CLK_XTAL_X2:
if (clk_circuit == SOC_ROOT_CIRCUIT_CLK_XTAL_X2) {
return s_xtal_x2_ref_cnt > 0;
case SOC_ROOT_CIRCUIT_CLK_BBPLL:
}
if (clk_circuit == SOC_ROOT_CIRCUIT_CLK_BBPLL) {
return s_bbpll_ref_cnt > 0;
default:
break;
}
return false;
}

View File

@@ -5,9 +5,9 @@
*/
#include <stdint.h>
#include <stdatomic.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"
@@ -21,6 +21,7 @@
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/esp_clk_tree_derived.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/critical_section.h"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
@@ -166,8 +167,12 @@ esp_err_t esp_clk_tree_src_set_freq_hz(soc_module_clk_t clk_src, uint32_t expt_f
return ret;
}
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
static bool esp_clk_tree_initialized = false;
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 };
static bool s_clk_tree_initialized = false;
void esp_clk_tree_initialize(void)
{
@@ -175,7 +180,7 @@ void esp_clk_tree_initialize(void)
if ((rst_reason == RESET_REASON_CPU_SW) || (rst_reason == RESET_REASON_CPU_MWDT) \
|| (rst_reason == RESET_REASON_CPU_RWDT) || (rst_reason == RESET_REASON_CPU_JTAG) \
|| (rst_reason == RESET_REASON_CPU_LOCKUP)) {
esp_clk_tree_initialized = true;
s_clk_tree_initialized = true;
return;
}
@@ -189,10 +194,10 @@ void esp_clk_tree_initialize(void)
_clk_gate_ll_ref_160m_clk_en(false);
#endif
_clk_gate_ll_ref_240m_clk_en(false);
esp_clk_tree_initialized = true;
s_clk_tree_initialized = true;
}
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit)
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
(void)clk_circuit;
return false;
@@ -204,77 +209,129 @@ bool esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool enable)
return false; // TODO: PM-653
}
#define ENABLE_CLK_GATE(clk_src_en_func, enable) \
PERIPH_RCC_ATOMIC() { \
clk_src_en_func(enable); \
/* -------------------------------------------------------------------------- */
/* Fixed ref clocks: gate only (no parent power management on ESP32-P4) */
/* -------------------------------------------------------------------------- */
typedef void (*esp_clk_tree_gate_fn_t)(bool enable);
typedef struct {
soc_module_clk_t clk_id;
esp_clk_tree_gate_fn_t set_gate;
} esp_clk_tree_gated_clk_t;
static void esp_clk_tree_gate_rc_fast(bool enable)
{
if (enable) {
rtc_dig_clk8m_enable();
} else {
rtc_dig_clk8m_disable();
}
}
typedef enum {
ESP_CLK_TREE_GATED_CLK_RC_FAST,
ESP_CLK_TREE_GATED_CLK_PLL_F20M,
ESP_CLK_TREE_GATED_CLK_PLL_F25M,
ESP_CLK_TREE_GATED_CLK_PLL_F80M,
ESP_CLK_TREE_GATED_CLK_PLL_F120M,
ESP_CLK_TREE_GATED_CLK_PLL_F160M,
ESP_CLK_TREE_GATED_CLK_PLL_F240M,
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, esp_clk_tree_gate_rc_fast },
[ESP_CLK_TREE_GATED_CLK_PLL_F20M] = { SOC_MOD_CLK_PLL_F20M, _clk_gate_ll_ref_20m_clk_en },
[ESP_CLK_TREE_GATED_CLK_PLL_F25M] = { SOC_MOD_CLK_PLL_F25M, _clk_gate_ll_ref_25m_clk_en },
[ESP_CLK_TREE_GATED_CLK_PLL_F80M] = { SOC_MOD_CLK_PLL_F80M, _clk_gate_ll_ref_80m_clk_en },
[ESP_CLK_TREE_GATED_CLK_PLL_F120M] = { SOC_MOD_CLK_PLL_F120M, _clk_gate_ll_ref_120m_clk_en },
[ESP_CLK_TREE_GATED_CLK_PLL_F160M] = { SOC_MOD_CLK_PLL_F160M, _clk_gate_ll_ref_160m_clk_en },
[ESP_CLK_TREE_GATED_CLK_PLL_F240M] = { SOC_MOD_CLK_PLL_F240M, _clk_gate_ll_ref_240m_clk_en },
};
#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;
esp_os_enter_critical(&s_clk_tree_spinlock);
if (enable) {
prev_ref_cnt = s_mod_clk_gate_ref_cnt[entry->clk_id]++;
} 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;
esp_os_exit_critical(&s_clk_tree_spinlock);
ESP_EARLY_LOGW(TAG, "soc_module_clk_t %d disabled multiple times!!", entry->clk_id);
return ESP_OK;
}
}
esp_os_exit_critical(&s_clk_tree_spinlock);
if (prev_ref_cnt == 0 && enable) {
ENABLE_CLK_GATE(entry->set_gate, true);
} else if (prev_ref_cnt == 1 && !enable) {
ENABLE_CLK_GATE(entry->set_gate, false);
}
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;
}
if (!esp_clk_tree_initialized) {
if (!s_clk_tree_initialized) {
return ESP_OK;
}
// Derived PLL clocks (PLL_F50M, ...) that participate in the shared
// refcount/lock engine route through that engine instead of the
// global s_pll_src_cg_ref_cnt array below.
if (esp_clk_tree_get_derived_clk_desc(clk_src) != NULL) {
return enable ? esp_clk_tree_derived_clk_acquire(clk_src)
: esp_clk_tree_derived_clk_release(clk_src);
}
esp_clk_tree_gated_clk_id_t gated_clk_id;
// these clock sources have their own reference counting
switch (clk_src) {
case SOC_MOD_CLK_APLL:
if (enable) {
esp_clk_tree_apll_acquire();
} else {
esp_clk_tree_apll_release();
}
return ESP_OK;
case SOC_MOD_CLK_MPLL:
if (enable) {
return esp_clk_tree_mpll_acquire();
} else {
esp_clk_tree_mpll_release();
}
return ESP_OK;
default:
break;
}
// other clock sources use the global reference counting
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);
case SOC_MOD_CLK_APLL:
if (enable) {
esp_clk_tree_apll_acquire();
} else {
esp_clk_tree_apll_release();
}
return ESP_OK;
case SOC_MOD_CLK_MPLL:
if (enable) {
return esp_clk_tree_mpll_acquire();
} else {
esp_clk_tree_mpll_release();
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_PLL_F20M: ENABLE_CLK_GATE(clk_gate_ll_ref_20m_clk_en, enable); break;
case SOC_MOD_CLK_PLL_F25M: ENABLE_CLK_GATE(clk_gate_ll_ref_25m_clk_en, enable); break;
case SOC_MOD_CLK_PLL_F80M: ENABLE_CLK_GATE(clk_gate_ll_ref_80m_clk_en, enable); break;
case SOC_MOD_CLK_PLL_F120M: ENABLE_CLK_GATE(clk_gate_ll_ref_120m_clk_en, enable); break;
case SOC_MOD_CLK_RC_FAST: gated_clk_id = ESP_CLK_TREE_GATED_CLK_RC_FAST; break;
case SOC_MOD_CLK_PLL_F20M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F20M; break;
case SOC_MOD_CLK_PLL_F25M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F25M; break;
case SOC_MOD_CLK_PLL_F80M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F80M; break;
case SOC_MOD_CLK_PLL_F120M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F120M; break;
#if !CONFIG_ESP_ENABLE_PVT
// PLL_F160M must always on if PVT is enabled.
case SOC_MOD_CLK_PLL_F160M: ENABLE_CLK_GATE(clk_gate_ll_ref_160m_clk_en, enable); break;
// PLL_F160M must always on if PVT is enabled.
case SOC_MOD_CLK_PLL_F160M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F160M; break;
#endif
case SOC_MOD_CLK_PLL_F240M: ENABLE_CLK_GATE(clk_gate_ll_ref_240m_clk_en, enable); break;
default: break;
case SOC_MOD_CLK_PLL_F240M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F240M; break;
default:
// Derived PLL clocks (PLL_F50M, ...) that participate in the shared
// refcount/lock engine route through that engine instead of the
// global s_pll_src_cg_ref_cnt array below.
if (esp_clk_tree_get_derived_clk_desc(clk_src) != NULL) {
return enable ? esp_clk_tree_derived_clk_acquire(clk_src)
: esp_clk_tree_derived_clk_release(clk_src);
}
return ESP_OK;
}
return ESP_OK;
return esp_clk_tree_enable_gated_clk(&s_gated_ref_clks[gated_clk_id], enable);
}

View File

@@ -97,7 +97,7 @@ void esp_clk_tree_initialize(void)
{
}
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit)
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
(void)clk_circuit;
return false;

View File

@@ -89,7 +89,7 @@ void esp_clk_tree_initialize(void)
{
}
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit)
bool esp_clk_tree_port_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
(void)clk_circuit;
return false;

View File

@@ -5,59 +5,316 @@
*/
#include <stdint.h>
#include <stdatomic.h>
#include "sdkconfig.h"
#include <assert.h>
#include "esp_clk_tree.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "soc/clk_tree_defs.h"
#include "esp_rom_sys.h"
#include "soc/rtc.h"
#include "soc/reset_reasons.h"
#include "soc/soc_caps.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/esp_clk_tree_derived.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/critical_section.h"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
/* -------------------------------------------------------------------------- */
/* Derived clocks: configurable divider and/or upstream mux + multi-user lock */
/* -------------------------------------------------------------------------- */
enum {
ESP_CLK_TREE_DERIVED_PLL_F25M = 0,
ESP_CLK_TREE_DERIVED_PLL_F50M,
ESP_CLK_TREE_DERIVED_REF_F80M,
ESP_CLK_TREE_DERIVED_PLL_NUM,
};
static esp_clk_tree_derived_clk_state_t s_derived_pll_states[ESP_CLK_TREE_DERIVED_PLL_NUM];
static int8_t s_ref_500m_committed_mux = -1;
/**
* Power ref_25m/ref_50m shared upstream (ref_500m_sel: 0 = CPLL, 1 = MPLL).
* @param mux_sel 0 or 1; values < 0 are ignored.
*/
static esp_err_t esp_clk_tree_ref_500m_parent_power(int8_t mux_sel, bool enable)
{
if (mux_sel < 0) {
return ESP_OK;
}
if (mux_sel == 0) {
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_CPLL, enable);
return ESP_OK;
}
if (enable) {
return esp_clk_tree_mpll_acquire();
}
esp_clk_tree_mpll_release();
return ESP_OK;
}
/**
* Shared ref_500m_sel: PLL_F25M and PLL_F50M must use the same upstream when both are on.
*/
static void esp_clk_tree_ref_500m_set_src(uint8_t mux_sel)
{
if (s_derived_pll_states[ESP_CLK_TREE_DERIVED_PLL_F25M].ref_cnt > 0
&& s_derived_pll_states[ESP_CLK_TREE_DERIVED_PLL_F50M].ref_cnt > 0
&& s_ref_500m_committed_mux >= 0) {
assert(mux_sel == (uint8_t)s_ref_500m_committed_mux);
}
clk_ll_ref_500m_set_src(mux_sel);
}
static esp_err_t esp_clk_tree_ref_500m_derived_acquire_parent(void)
{
int8_t mux = (int8_t)clk_ll_ref_500m_get_src();
if (mux == s_ref_500m_committed_mux) {
return ESP_OK;
}
if (s_derived_pll_states[ESP_CLK_TREE_DERIVED_PLL_F25M].ref_cnt > 0
&& s_derived_pll_states[ESP_CLK_TREE_DERIVED_PLL_F50M].ref_cnt > 0
&& s_ref_500m_committed_mux >= 0) {
assert(mux == s_ref_500m_committed_mux);
}
esp_clk_tree_ref_500m_parent_power(s_ref_500m_committed_mux, false);
esp_err_t ret = esp_clk_tree_ref_500m_parent_power(mux, true);
if (ret != ESP_OK) {
esp_clk_tree_ref_500m_parent_power(s_ref_500m_committed_mux, true);
return ret;
}
s_ref_500m_committed_mux = mux;
return ESP_OK;
}
static esp_err_t esp_clk_tree_ref_500m_derived_release_parent(void)
{
if (s_derived_pll_states[ESP_CLK_TREE_DERIVED_PLL_F25M].ref_cnt > 0
|| s_derived_pll_states[ESP_CLK_TREE_DERIVED_PLL_F50M].ref_cnt > 0) {
return ESP_OK;
}
esp_clk_tree_ref_500m_parent_power(s_ref_500m_committed_mux, false);
s_ref_500m_committed_mux = -1;
return ESP_OK;
}
/** F80M parent path committed at first enable: 0 = BBPLL, 1 = XTALx2 (-1 = none). */
static int8_t s_ref_80m_committed_sel = -1;
/**
* REF_F80M enable-time source policy:
* 1) If BBPLL is already on → BBPLL/6 path + one more BBPLL power user.
* 2) Else → power XTALx2 and select ref_80m via clk_ll_ref_80m_set_src(1).
*/
static esp_err_t esp_clk_tree_ref_80m_derived_acquire_parent(void)
{
if (s_ref_80m_committed_sel >= 0) {
return ESP_OK;
}
_clk_gate_ll_ref_80m_mux_clk_en(true);
if (esp_clk_tree_port_is_power_on(SOC_ROOT_CIRCUIT_CLK_BBPLL)) {
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_BBPLL, true);
clk_ll_ref_80m_set_src(0);
s_ref_80m_committed_sel = 0;
} else {
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_XTAL_X2, true);
clk_ll_ref_80m_set_src(1);
s_ref_80m_committed_sel = 1;
}
return ESP_OK;
}
static esp_err_t esp_clk_tree_ref_80m_derived_release_parent(void)
{
if (s_derived_pll_states[ESP_CLK_TREE_DERIVED_REF_F80M].ref_cnt > 0) {
return ESP_OK;
}
if (s_ref_80m_committed_sel == 0) {
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_BBPLL, false);
} else if (s_ref_80m_committed_sel == 1) {
esp_clk_tree_enable_power(SOC_ROOT_CIRCUIT_CLK_XTAL_X2, false);
}
_clk_gate_ll_ref_80m_mux_clk_en(false);
s_ref_80m_committed_sel = -1;
return ESP_OK;
}
// Allowed upstreams for PLL_F50M, in preference order (used by both auto-pick
// and explicit-upstream paths). Mux selectors match the HP_SYS_CLKRST
// `reg_ref_500m_sel` field: 0 = CPLL, 1 = MPLL.
static const esp_clk_tree_derived_upstream_t s_pll_f50m_upstreams[] = {
// `reg_ref_500m_sel` field: 0 = CPLL, 1 = MPLL. (shared by PLL_F25M and PLL_F50M).
static const esp_clk_tree_derived_upstream_t s_ref_500m_upstreams[] = {
{ SOC_MOD_CLK_CPLL, 0 },
{ SOC_MOD_CLK_MPLL, 1 },
};
static esp_clk_tree_derived_clk_state_t s_pll_f50m_state = {
.ref_cnt = 0,
.cur_upstream = SOC_MOD_CLK_INVALID,
.cur_divider = 0,
};
static const esp_clk_tree_derived_clk_desc_t s_pll_f50m_desc = {
.clk_id = SOC_MOD_CLK_PLL_F50M,
.set_src = clk_ll_ref_500m_set_src,
.set_divider = clk_ll_pll_f50m_set_divider,
.set_gate = _clk_gate_ll_ref_50m_clk_en, // using RCC_ATOMIC lock free function version to avoid nesting critical sections
.upstreams = s_pll_f50m_upstreams,
.upstream_count = sizeof(s_pll_f50m_upstreams) / sizeof(s_pll_f50m_upstreams[0]),
.state = &s_pll_f50m_state,
static const esp_clk_tree_derived_clk_desc_t s_derived_pll_descs[ESP_CLK_TREE_DERIVED_PLL_NUM] = {
[ESP_CLK_TREE_DERIVED_PLL_F25M] = {
.clk_id = SOC_MOD_CLK_PLL_F25M,
.set_src = esp_clk_tree_ref_500m_set_src,
.set_divider = clk_ll_pll_f25m_set_divider,
.set_gate = _clk_gate_ll_ref_25m_clk_en,
.upstreams = s_ref_500m_upstreams,
.upstream_count = sizeof(s_ref_500m_upstreams) / sizeof(s_ref_500m_upstreams[0]),
.state = &s_derived_pll_states[ESP_CLK_TREE_DERIVED_PLL_F25M],
.acquire_parent = esp_clk_tree_ref_500m_derived_acquire_parent,
.release_parent = esp_clk_tree_ref_500m_derived_release_parent,
},
[ESP_CLK_TREE_DERIVED_PLL_F50M] = {
.clk_id = SOC_MOD_CLK_PLL_F50M,
.set_src = esp_clk_tree_ref_500m_set_src,
.set_divider = clk_ll_pll_f50m_set_divider,
.set_gate = _clk_gate_ll_ref_50m_clk_en,
.upstreams = s_ref_500m_upstreams,
.upstream_count = sizeof(s_ref_500m_upstreams) / sizeof(s_ref_500m_upstreams[0]),
.state = &s_derived_pll_states[ESP_CLK_TREE_DERIVED_PLL_F50M],
.acquire_parent = esp_clk_tree_ref_500m_derived_acquire_parent,
.release_parent = esp_clk_tree_ref_500m_derived_release_parent,
},
[ESP_CLK_TREE_DERIVED_REF_F80M] = {
.clk_id = SOC_MOD_CLK_REF_F80M,
.set_src = NULL,
.set_divider = NULL,
.set_gate = _clk_gate_ll_ref_80m_clk_en,
.upstreams = NULL,
.upstream_count = 0,
.state = &s_derived_pll_states[ESP_CLK_TREE_DERIVED_REF_F80M],
.acquire_parent = esp_clk_tree_ref_80m_derived_acquire_parent,
.release_parent = esp_clk_tree_ref_80m_derived_release_parent,
},
};
const esp_clk_tree_derived_clk_desc_t *esp_clk_tree_get_derived_clk_desc(soc_module_clk_t clk_src)
{
switch (clk_src) {
case SOC_MOD_CLK_PLL_F25M:
return &s_derived_pll_descs[ESP_CLK_TREE_DERIVED_PLL_F25M];
case SOC_MOD_CLK_PLL_F50M:
return &s_pll_f50m_desc;
return &s_derived_pll_descs[ESP_CLK_TREE_DERIVED_PLL_F50M];
case SOC_MOD_CLK_REF_F80M:
return &s_derived_pll_descs[ESP_CLK_TREE_DERIVED_REF_F80M];
default:
return NULL;
}
}
/* -------------------------------------------------------------------------- */
/* Fixed ref clocks: gate + static BBPLL parent only */
/* -------------------------------------------------------------------------- */
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_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_CPLL
|| 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_CPLL:
clk_ll_cpll_enable();
break;
case SOC_ROOT_CIRCUIT_CLK_XTAL_X2:
clk_ll_xtalx2_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_CPLL
|| 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_CPLL:
clk_ll_cpll_disable();
break;
case SOC_ROOT_CIRCUIT_CLK_XTAL_X2:
clk_ll_xtalx2_disable();
break;
default:
break;
}
}
esp_os_exit_critical(&s_clk_tree_spinlock);
return prev;
}
static uint32_t esp_clk_tree_ref_500m_pll_get_freq_hz(uint32_t div_num)
{
uint32_t up_hz;
if (clk_ll_ref_500m_get_src() == 0) {
up_hz = clk_ll_cpll_get_freq_mhz(clk_hal_xtal_get_freq_mhz()) * MHZ;
} else {
up_hz = clk_ll_mpll_get_freq_mhz(clk_hal_xtal_get_freq_mhz()) * MHZ;
}
return up_hz / div_num;
}
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)
{
@@ -79,9 +336,21 @@ esp_err_t esp_clk_tree_src_get_freq_hz(soc_module_clk_t clk_src, esp_clk_tree_sr
case SOC_MOD_CLK_PLL_F20M:
clk_src_freq = CLK_LL_PLL_480M_FREQ_MHZ / clk_ll_pll_f20m_get_divider() * MHZ;
break;
case SOC_MOD_CLK_PLL_F80M:
case SOC_MOD_CLK_PLL_F25M:
clk_src_freq = esp_clk_tree_ref_500m_pll_get_freq_hz(clk_ll_pll_f25m_get_divider());
break;
case SOC_MOD_CLK_PLL_F50M:
clk_src_freq = esp_clk_tree_ref_500m_pll_get_freq_hz(clk_ll_pll_f50m_get_divider());
break;
case SOC_MOD_CLK_PLL_F60M:
clk_src_freq = CLK_LL_PLL_60M_FREQ_MHZ * MHZ;
break;
case SOC_MOD_CLK_REF_F80M:
clk_src_freq = CLK_LL_PLL_80M_FREQ_MHZ * MHZ;
break;
case SOC_MOD_CLK_PLL_F120M:
clk_src_freq = CLK_LL_PLL_120M_FREQ_MHZ * MHZ;
break;
case SOC_MOD_CLK_PLL_F160M:
clk_src_freq = CLK_LL_PLL_160M_FREQ_MHZ * MHZ;
break;
@@ -159,81 +428,159 @@ esp_err_t esp_clk_tree_src_set_freq_hz(soc_module_clk_t clk_src, uint32_t expt_f
return ret;
}
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
static bool s_clk_tree_initialized = false;
static int16_t s_cpll_ref_cnt = 0;
void esp_clk_tree_initialize(void)
{
// TODO: IDF-15502
/*soc_reset_reason_t rst_reason = esp_rom_get_reset_reason(0);
if ((rst_reason == RESET_REASON_CPU_SW) || (rst_reason == RESET_REASON_CPU_MWDT) \
|| (rst_reason == RESET_REASON_CPU_RWDT) || (rst_reason == RESET_REASON_CPU_JTAG) \
|| (rst_reason == RESET_REASON_CPU_LOCKUP)) {
soc_reset_reason_t rst_reason = esp_rom_get_reset_reason(0);
if ((rst_reason == RESET_REASON_CPU_SW) || (rst_reason == RESET_REASON_CPU_MWDT) ||
(rst_reason == RESET_REASON_CPU_RWDT) || (rst_reason == RESET_REASON_CPU_JTAG) ||
(rst_reason == RESET_REASON_CPU_LOCKUP)) {
s_clk_tree_initialized = true;
return;
}*/
// Power
soc_cpu_clk_src_t cpu_clk_src_btld = clk_ll_cpu_get_src();
if (cpu_clk_src_btld == SOC_CPU_CLK_SRC_CPLL) {
s_cpll_ref_cnt++;
} else if (cpu_clk_src_btld == SOC_CPU_CLK_SRC_PLL_F240M) {
// TODO: IDF-15502
s_pll_src_cg_ref_cnt[SOC_MOD_CLK_PLL_F240M] = 1;
}
// flash clock source is set to BBPLL in bootloader
s_pll_src_cg_ref_cnt[SOC_MOD_CLK_BBPLL] = 1;
// Cold boot only
soc_cpu_clk_src_t cpu_src = clk_ll_cpu_get_src();
if (cpu_src == SOC_CPU_CLK_SRC_PLL_F240M) {
s_mod_clk_gate_ref_cnt[SOC_MOD_CLK_PLL_F240M] = 1;
s_root_pll_power_ref_cnt[SOC_ROOT_CIRCUIT_CLK_BBPLL] = 1;
} else if (cpu_src == SOC_CPU_CLK_SRC_CPLL) {
s_root_pll_power_ref_cnt[SOC_ROOT_CIRCUIT_CLK_CPLL] = 1;
}
if (cpu_src != SOC_CPU_CLK_SRC_PLL_F240M) {
_clk_gate_ll_ref_240m_clk_en(false);
// Not do clk_ll_bbpll_disable since MSPI depends on BBPLL: TODO: IDF-15889
}
// Add ref count for Flash using. // TODO: IDF-15889
s_root_pll_power_ref_cnt[SOC_ROOT_CIRCUIT_CLK_BBPLL]++;
if (cpu_src != SOC_CPU_CLK_SRC_CPLL) {
clk_ll_cpll_disable();
}
_clk_gate_ll_ref_160m_clk_en(false);
_clk_gate_ll_ref_120m_clk_en(false);
_clk_gate_ll_ref_80m_clk_en(false);
_clk_gate_ll_ref_60m_clk_en(false);
_clk_gate_ll_ref_20m_clk_en(false);
_clk_gate_ll_ref_50m_clk_en(false);
_clk_gate_ll_ref_25m_clk_en(false);
clk_ll_xtalx2_disable();
HP_ALIVE_SYS.hp_clk_ctrl.hp_audio_pll_clk_en = 0;
HP_ALIVE_SYS.hp_clk_ctrl.hp_sdio_pll2_clk_en = 0;
HP_ALIVE_SYS.hp_clk_ctrl.hp_sdio_pll1_clk_en = 0;
HP_ALIVE_SYS.hp_clk_ctrl.hp_sdio_pll0_clk_en = 0;
// Gating: disable all PLL-derived reference clocks; they will be re-enabled on demand via esp_clk_tree_enable_src
// TODO: IDF-15502
//_clk_gate_ll_ref_20m_clk_en(false);
//_clk_gate_ll_ref_25m_clk_en(false);
//_clk_gate_ll_ref_50m_clk_en(false);
//_clk_gate_ll_ref_80m_clk_en(false);
//_clk_gate_ll_ref_160m_clk_en(false);
//if (s_pll_src_cg_ref_cnt[SOC_MOD_CLK_PLL_F240M] == 0) _clk_gate_ll_ref_240m_clk_en(false);
s_clk_tree_initialized = true;
}
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_CPLL:
case SOC_ROOT_CIRCUIT_CLK_BBPLL:
case SOC_ROOT_CIRCUIT_CLK_XTAL_X2: {
if (enable) {
s_cpll_ref_cnt++;
toggled = (esp_clk_tree_root_pll_power_acquire(clk_circuit) == 0);
} else {
s_cpll_ref_cnt--;
toggled = (esp_clk_tree_root_pll_power_release(clk_circuit) == 1);
}
// Note that a calibration is usually needed after enabling CPLL
if (s_cpll_ref_cnt == 1) {
clk_ll_cpll_enable();
toggled = true;
} else if (s_cpll_ref_cnt == 0) {
clk_ll_cpll_disable();
toggled = true;
}
assert(s_cpll_ref_cnt >= 0);
break;
}
default:
break;
}
return toggled;
}
#define ENABLE_CLK_GATE(clk_src_en_func, enable) \
PERIPH_RCC_ATOMIC() { \
clk_src_en_func(enable); \
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_CPLL
|| 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_F20M,
ESP_CLK_TREE_GATED_CLK_PLL_F60M,
ESP_CLK_TREE_GATED_CLK_PLL_F120M,
ESP_CLK_TREE_GATED_CLK_PLL_F160M,
ESP_CLK_TREE_GATED_CLK_PLL_F240M,
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 },
[ESP_CLK_TREE_GATED_CLK_PLL_F20M] = { SOC_MOD_CLK_PLL_F20M, _clk_gate_ll_ref_20m_clk_en, esp_clk_tree_parent_bbpll },
[ESP_CLK_TREE_GATED_CLK_PLL_F60M] = { SOC_MOD_CLK_PLL_F60M, _clk_gate_ll_ref_60m_clk_en, esp_clk_tree_parent_bbpll },
[ESP_CLK_TREE_GATED_CLK_PLL_F120M] = { SOC_MOD_CLK_PLL_F120M, _clk_gate_ll_ref_120m_clk_en, esp_clk_tree_parent_bbpll },
[ESP_CLK_TREE_GATED_CLK_PLL_F160M] = { SOC_MOD_CLK_PLL_F160M, _clk_gate_ll_ref_160m_clk_en, esp_clk_tree_parent_bbpll },
[ESP_CLK_TREE_GATED_CLK_PLL_F240M] = { SOC_MOD_CLK_PLL_F240M, _clk_gate_ll_ref_240m_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;
esp_os_enter_critical(&s_clk_tree_spinlock);
if (enable) {
prev_ref_cnt = s_mod_clk_gate_ref_cnt[entry->clk_id]++;
} 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;
esp_os_exit_critical(&s_clk_tree_spinlock);
ESP_EARLY_LOGW(TAG, "soc_module_clk_t %d disabled multiple times!!", entry->clk_id);
return ESP_OK;
}
}
esp_os_exit_critical(&s_clk_tree_spinlock);
if (prev_ref_cnt == 0 && enable) {
if (entry->parent_power != NULL) {
entry->parent_power(true);
}
ENABLE_CLK_GATE(entry->set_gate, true);
} else if (prev_ref_cnt == 1 && !enable) {
ENABLE_CLK_GATE(entry->set_gate, false);
if (entry->parent_power != NULL) {
entry->parent_power(false);
}
}
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;
}
@@ -241,59 +588,36 @@ esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
return ESP_OK;
}
int16_t prev_ref_cnt = 0;
// Derived PLL clocks (PLL_F50M, ...) that participate in the shared
// refcount/lock engine route through that engine instead of the
// global s_pll_src_cg_ref_cnt array below.
if (esp_clk_tree_get_derived_clk_desc(clk_src) != NULL) {
return enable ? esp_clk_tree_derived_clk_acquire(clk_src)
: esp_clk_tree_derived_clk_release(clk_src);
}
esp_clk_tree_gated_clk_id_t gated_clk_id;
// these clock sources have their own reference counting
switch (clk_src) {
case SOC_MOD_CLK_APLL:
if (enable) {
esp_clk_tree_apll_acquire();
} else {
esp_clk_tree_apll_release();
}
return ESP_OK;
case SOC_MOD_CLK_MPLL:
if (enable) {
return esp_clk_tree_mpll_acquire();
} else {
esp_clk_tree_mpll_release();
}
return ESP_OK;
default:
break;
}
// other clock sources use the global reference counting
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);
case SOC_MOD_CLK_APLL:
if (enable) {
esp_clk_tree_apll_acquire();
} else {
esp_clk_tree_apll_release();
}
return ESP_OK;
case SOC_MOD_CLK_MPLL:
if (enable) {
return esp_clk_tree_mpll_acquire();
} else {
esp_clk_tree_mpll_release();
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_F20M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F20M; break;
case SOC_MOD_CLK_PLL_F60M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F60M; break;
case SOC_MOD_CLK_PLL_F120M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F120M; break;
case SOC_MOD_CLK_PLL_F160M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F160M; break;
case SOC_MOD_CLK_PLL_F240M: gated_clk_id = ESP_CLK_TREE_GATED_CLK_PLL_F240M; break;
default:
// Derived PLL clocks (PLL_F25M/F50M/F80M) use the shared derived-clk engine.
if (esp_clk_tree_get_derived_clk_desc(clk_src) != NULL) {
return enable ? esp_clk_tree_derived_clk_acquire(clk_src)
: esp_clk_tree_derived_clk_release(clk_src);
}
return ESP_OK;
}
// TODO: IDF-15502
//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_PLL_F20M: ENABLE_CLK_GATE(clk_gate_ll_ref_20m_clk_en, enable); break;
// case SOC_MOD_CLK_PLL_F25M: ENABLE_CLK_GATE(clk_gate_ll_ref_25m_clk_en, enable); break;
// case SOC_MOD_CLK_PLL_F80M: ENABLE_CLK_GATE(clk_gate_ll_ref_80m_clk_en, enable); break;
// case SOC_MOD_CLK_PLL_F160M: ENABLE_CLK_GATE(clk_gate_ll_ref_160m_clk_en, enable); break;
// case SOC_MOD_CLK_PLL_F240M: ENABLE_CLK_GATE(clk_gate_ll_ref_240m_clk_en, enable); break;
// default: break;
// }
//}
return ESP_OK;
return esp_clk_tree_enable_gated_clk(&s_gated_ref_clks[gated_clk_id], enable);
}

View File

@@ -357,7 +357,9 @@ static void rtc_clk_cpu_src_clk_enable(soc_cpu_clk_src_t new_src, uint32_t new_s
rtc_clk_cpll_configure(rtc_clk_xtal_freq_get(), new_src_freq_mhz);
}
} else if (new_src == SOC_CPU_CLK_SRC_PLL_F240M) {
#if !BOOTLOADER_BUILD
#if BOOTLOADER_BUILD
clk_ll_bbpll_enable();
#else
if (!s_is_pll_f240m_acquired) {
esp_clk_tree_enable_src(SOC_MOD_CLK_PLL_F240M, true);
s_is_pll_f240m_acquired = true;
@@ -380,7 +382,9 @@ static void rtc_clk_cpu_src_clk_disable(soc_cpu_clk_src_t old_src)
}
#endif
} else if (old_src == SOC_CPU_CLK_SRC_PLL_F240M) {
#if !BOOTLOADER_BUILD
#if BOOTLOADER_BUILD
/* Do not clk_ll_bbpll_disable(): Flash may still use BBPLL (reg_flash_clk_src_sel==1) */
#else
assert(s_is_pll_f240m_acquired);
s_is_pll_f240m_acquired = false;
esp_clk_tree_enable_src(SOC_MOD_CLK_PLL_F240M, false);

View File

@@ -409,8 +409,8 @@ end:
* dispatchers route into.
*
* Semantics:
* - acquire: ref_cnt++; on first acquire enable the gate.
* - release: ref_cnt--; on last release disable the gate.
* - acquire: ref_cnt++; on first acquire acquire_parent then enable the gate.
* - release: ref_cnt--; on last release disable the gate and release_parent.
* - select_upstream: program the mux to source from `upstream`.
* - freq_set: pick a divider for `state->cur_upstream` (if `select_upstream`
* was called) or auto-pick an upstream that divides cleanly
@@ -436,14 +436,21 @@ esp_err_t esp_clk_tree_derived_clk_acquire(soc_module_clk_t clk_src)
return ESP_ERR_NOT_SUPPORTED;
}
esp_clk_tree_derived_clk_state_t *state = desc->state;
esp_err_t ret = ESP_OK;
esp_os_enter_critical(&s_derived_clk_spinlock);
state->ref_cnt++;
if (state->ref_cnt == 1) {
desc->set_gate(true);
if (desc->acquire_parent != NULL) {
ret = desc->acquire_parent();
}
if (ret == ESP_OK) {
desc->set_gate(true);
} else {
state->ref_cnt--;
}
}
esp_os_exit_critical(&s_derived_clk_spinlock);
return ESP_OK;
return ret;
}
esp_err_t esp_clk_tree_derived_clk_release(soc_module_clk_t clk_src)
@@ -455,6 +462,7 @@ esp_err_t esp_clk_tree_derived_clk_release(soc_module_clk_t clk_src)
esp_clk_tree_derived_clk_state_t *state = desc->state;
bool released_too_many = false;
esp_err_t ret = ESP_OK;
esp_os_enter_critical(&s_derived_clk_spinlock);
if (state->ref_cnt <= 0) {
state->ref_cnt = 0;
@@ -465,13 +473,17 @@ esp_err_t esp_clk_tree_derived_clk_release(soc_module_clk_t clk_src)
desc->set_gate(false);
state->cur_upstream = SOC_MOD_CLK_INVALID;
state->cur_divider = 0;
if (desc->release_parent != NULL) {
ret = desc->release_parent();
}
}
}
esp_os_exit_critical(&s_derived_clk_spinlock);
if (released_too_many) {
ESP_HW_LOGW(TAG, "derived clk %d released without matching acquire", (int)clk_src);
return ESP_OK;
}
return ESP_OK;
return ret;
}
/**
@@ -521,6 +533,7 @@ esp_err_t esp_clk_tree_derived_clk_freq_set(soc_module_clk_t clk_src,
if (desc == NULL || desc->state == NULL || desc->upstreams == NULL) {
return ESP_ERR_NOT_SUPPORTED;
}
esp_clk_tree_derived_clk_state_t *state = desc->state;
ESP_RETURN_ON_FALSE(expt_freq_hz > 0, ESP_ERR_INVALID_ARG, TAG, "freq must be > 0");
@@ -551,32 +564,49 @@ esp_err_t esp_clk_tree_derived_clk_freq_set(soc_module_clk_t clk_src,
// (e.g. another peer ran enable_src already but hasn't called freq_set
// yet). Mirrors MPLL's `cur == 0 || ref_cnt < 2` check.
bool first_commit = (state->cur_divider == 0);
bool upstream_changed = false;
if (state->ref_cnt < 2 || same_config || first_commit) {
// First-time configuration: also program the mux. When the caller
// already invoked `select_upstream`, `state->cur_upstream` already
// matches `upstream` so the mux is left untouched.
if (state->cur_upstream != upstream && desc->set_src != NULL) {
desc->set_src(mux_sel);
if (state->cur_upstream != upstream) {
upstream_changed = true;
if (desc->set_src != NULL) {
desc->set_src(mux_sel);
}
}
if (desc->set_divider != NULL) {
desc->set_divider(divider);
}
desc->set_divider(divider);
state->cur_upstream = upstream;
state->cur_divider = divider;
} else {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK && upstream_changed && desc->acquire_parent != NULL) {
ret = desc->acquire_parent();
}
esp_os_exit_critical(&s_derived_clk_spinlock);
reported_upstream = state->cur_upstream;
reported_divider = state->cur_divider;
if (real_freq_hz != NULL) {
uint32_t up_hz = 0;
if (reported_upstream != SOC_MOD_CLK_INVALID && reported_divider != 0 &&
esp_clk_tree_src_get_freq_hz(reported_upstream,
ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX,
&up_hz) == ESP_OK) {
*real_freq_hz = up_hz / reported_divider;
if (desc->set_divider == NULL) {
if (esp_clk_tree_src_get_freq_hz(clk_src,
ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX,
real_freq_hz) != ESP_OK) {
*real_freq_hz = 0;
}
} else {
*real_freq_hz = 0;
uint32_t up_hz = 0;
if (reported_upstream != SOC_MOD_CLK_INVALID && reported_divider != 0 &&
esp_clk_tree_src_get_freq_hz(reported_upstream,
ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX,
&up_hz) == ESP_OK) {
*real_freq_hz = up_hz / reported_divider;
} else {
*real_freq_hz = 0;
}
}
}
return ret;
@@ -626,7 +656,33 @@ esp_err_t esp_clk_tree_src_select_upstream(soc_module_clk_t clk_src,
// Divider for the previous upstream is no longer meaningful; the next
// `set_freq_hz` call will program a fresh divider for `upstream`.
state->cur_divider = 0;
if (desc->acquire_parent != NULL) {
ret = desc->acquire_parent();
}
}
esp_os_exit_critical(&s_derived_clk_spinlock);
return ret;
}
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit)
{
#if SOC_CLK_MPLL_SUPPORTED
if (clk_circuit == SOC_ROOT_CIRCUIT_CLK_MPLL) {
bool on;
esp_os_enter_critical(&s_periph_mpll_spinlock);
on = s_mpll_ref_cnt > 0;
esp_os_exit_critical(&s_periph_mpll_spinlock);
return on;
}
#endif
#if SOC_CLK_APLL_SUPPORTED
if (clk_circuit == SOC_ROOT_CIRCUIT_CLK_APLL) {
bool on;
esp_os_enter_critical(&s_periph_apll_spinlock);
on = s_apll_ref_cnt > 0;
esp_os_exit_critical(&s_periph_apll_spinlock);
return on;
}
#endif
return esp_clk_tree_port_is_power_on(clk_circuit);
}

View File

@@ -77,6 +77,8 @@ typedef enum {
SOC_ROOT_CIRCUIT_CLK_CPLL, /*!< CPLL_CLK is the output of the CPLL generator circuit */
SOC_ROOT_CIRCUIT_CLK_MPLL, /*!< MPLL_CLK is the output of the MPLL generator circuit */
SOC_ROOT_CIRCUIT_CLK_APLL, /*!< APLL_CLK is the output of the APLL generator circuit */
SOC_ROOT_CIRCUIT_CLK_XTAL_X2, /*!< XTALx2 80MHz; ref_80m mux alternate source (see clk_ll_xtalx2_enable) */
SOC_ROOT_CIRCUIT_CLK_MAX,
} soc_root_clk_circuit_t;
/**
@@ -141,9 +143,12 @@ typedef enum {
// For digital domain: peripherals
SOC_MOD_CLK_SYS, /*!< SYS_CLK is the system clock, derived from SOC_CLK clock source */
SOC_MOD_CLK_PLL_F20M, /*!< PLL_F20M_CLK is derived from BBPLL (clock gating + default divider 24), its default frequency is 20MHz */
SOC_MOD_CLK_PLL_F25M, /*!< PLL_F25M_CLK is derived from MPLL (clock gating + configurable divider), it will have a frequency of 25MHz */
SOC_MOD_CLK_PLL_F50M, /*!< PLL_F50M_CLK is derived from C/MPLL (clock gating + configurable divider 10), it will have a frequency of 50MHz */
SOC_MOD_CLK_PLL_F25M, /*!< PLL_F25M_CLK is from ref_25m (clock gating + configurable divider). Shares HP_SYS `ref_500m_sel` with PLL_F50M (0=CPLL, 1=MPLL); if both are used, they must select the same upstream. */
SOC_MOD_CLK_PLL_F50M, /*!< PLL_F50M_CLK is from ref_50m (clock gating + configurable divider). Shares `ref_500m_sel` with PLL_F25M; frequency via esp_clk_tree_src_set_freq_hz (CPLL or MPLL upstream). */
SOC_MOD_CLK_PLL_F60M, /*!< PLL_F60M_CLK is derived from BBPLL (clock gating + default divider 8), its default frequency is 60MHz */
SOC_MOD_CLK_REF_F80M, /*!< REF_F80M_CLK from ref_80m (gate). Mux: BBPLL/6 (default) or XTALx2 80MHz (`reg_ref_80m_sel`). Frequency 80MHz either path. */
SOC_MOD_CLK_PLL_F80M, /*!< PLL_F80M_CLK is derived from BBPLL (clock gating + default divider 6), its default frequency is 80MHz */
SOC_MOD_CLK_PLL_F120M, /*!< PLL_F120M_CLK is derived from BBPLL (clock gating + default divider 4), its default frequency is 120MHz */
SOC_MOD_CLK_PLL_F160M, /*!< PLL_F160M_CLK is derived from BBPLL (clock gating + default divider 3), its default frequency is 160MHz */
SOC_MOD_CLK_PLL_F240M, /*!< PLL_F240M_CLK is derived from BBPLL (clock gating + default divider 2), its default frequency is 240MHz */
SOC_MOD_CLK_CPLL, /*!< CPLL is from 40MHz XTAL oscillator frequency multipliers */