feat(esp_hw_support): support esp32h4/h21 clk tree management

This commit is contained in:
hebinglin
2026-08-27 14:40:42 +08:00
parent b033a9fedd
commit df77b4bd38
7 changed files with 581 additions and 263 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
*/
@@ -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);
}
@@ -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);
}
@@ -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;