refactor(clk_tree): use general api to enable the clk

This commit is contained in:
Chen Jichang
2026-05-15 13:55:37 +08:00
committed by BOT
parent 8f7209c611
commit d1a64bffc0
52 changed files with 1095 additions and 673 deletions
+17 -160
View File
@@ -1,209 +1,66 @@
/*
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <freertos/FreeRTOS.h>
#include "clk_ctrl_os.h"
#include "soc/rtc.h"
#include "esp_ldo_regulator.h"
#include "esp_attr.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/critical_section.h"
#include "esp_check.h"
#include "hal/clk_tree_hal.h"
#include "hal/clk_tree_ll.h"
#if SOC_CLK_MPLL_SUPPORTED
#include "rtc_clk.h"
#endif
#if SOC_CLK_APLL_SUPPORTED || SOC_CLK_MPLL_SUPPORTED
ESP_LOG_ATTR_TAG(TAG, "clk_ctrl_os");
#endif
static portMUX_TYPE __attribute__((unused)) periph_spinlock = portMUX_INITIALIZER_UNLOCKED;
static uint8_t s_periph_ref_counts = 0;
static uint32_t s_rc_fast_freq_hz = 0; // Frequency of the RC_FAST clock in Hz
#if SOC_CLK_APLL_SUPPORTED
// Current APLL frequency, in HZ. Zero if APLL is not enabled.
static uint32_t s_cur_apll_freq_hz = 0;
static int s_apll_ref_cnt = 0;
#endif
#if SOC_CLK_MPLL_SUPPORTED
static uint32_t s_cur_mpll_freq_hz = 0;
static int s_mpll_ref_cnt = 0;
static esp_ldo_channel_handle_t s_ldo_chan = NULL;
#endif
#include "soc/clk_tree_defs.h"
bool periph_rtc_dig_clk8m_enable(void)
{
esp_os_enter_critical(&periph_spinlock);
if (s_periph_ref_counts == 0) {
rtc_dig_clk8m_enable();
#if SOC_CLK_RC_FAST_SUPPORT_CALIBRATION
s_rc_fast_freq_hz = esp_clk_tree_rc_fast_get_freq_hz(ESP_CLK_TREE_SRC_FREQ_PRECISION_EXACT);
if (s_rc_fast_freq_hz == 0) {
rtc_dig_clk8m_disable();
esp_os_exit_critical(&periph_spinlock);
return false;
}
#endif //SOC_CLK_RC_FAST_SUPPORT_CALIBRATION
}
s_periph_ref_counts++;
esp_os_exit_critical(&periph_spinlock);
return true;
return esp_clk_tree_enable_src(SOC_MOD_CLK_RC_FAST, true) == ESP_OK;
}
uint32_t periph_rtc_dig_clk8m_get_freq(void)
{
#if !SOC_CLK_RC_FAST_SUPPORT_CALIBRATION
/* Workaround: CLK8M calibration cannot be performed, we can only return its theoretic value */
return SOC_CLK_RC_FAST_FREQ_APPROX;
#if SOC_CLK_RC_FAST_SUPPORT_CALIBRATION
return esp_clk_tree_rc_fast_get_freq_hz(ESP_CLK_TREE_SRC_FREQ_PRECISION_EXACT);
#else
return s_rc_fast_freq_hz;
return SOC_CLK_RC_FAST_FREQ_APPROX;
#endif
}
void periph_rtc_dig_clk8m_disable(void)
{
esp_os_enter_critical(&periph_spinlock);
assert(s_periph_ref_counts > 0);
s_periph_ref_counts--;
if (s_periph_ref_counts == 0) {
s_rc_fast_freq_hz = 0;
rtc_dig_clk8m_disable();
}
esp_os_exit_critical(&periph_spinlock);
(void)esp_clk_tree_enable_src(SOC_MOD_CLK_RC_FAST, false);
}
#if SOC_CLK_APLL_SUPPORTED
void periph_rtc_apll_acquire(void)
{
esp_os_enter_critical(&periph_spinlock);
s_apll_ref_cnt++;
if (s_apll_ref_cnt == 1) {
// For the first time enable APLL, need to set power up
rtc_clk_apll_enable(true);
}
esp_os_exit_critical(&periph_spinlock);
(void)esp_clk_tree_enable_src(SOC_MOD_CLK_APLL, true);
}
void periph_rtc_apll_release(void)
{
esp_os_enter_critical(&periph_spinlock);
assert(s_apll_ref_cnt > 0);
s_apll_ref_cnt--;
if (s_apll_ref_cnt == 0) {
// If there is no peripheral using APLL, shut down the power
s_cur_apll_freq_hz = 0;
rtc_clk_apll_enable(false);
}
esp_os_exit_critical(&periph_spinlock);
(void)esp_clk_tree_enable_src(SOC_MOD_CLK_APLL, false);
}
esp_err_t periph_rtc_apll_freq_set(uint32_t expt_freq_hz, uint32_t *real_freq_hz)
{
uint32_t o_div = 0;
uint32_t sdm0 = 0;
uint32_t sdm1 = 0;
uint32_t sdm2 = 0;
// Guarantee 'periph_rtc_apll_acquire' has been called before set apll freq
assert(s_apll_ref_cnt > 0);
uint32_t apll_freq = rtc_clk_apll_coeff_calc(expt_freq_hz, &o_div, &sdm0, &sdm1, &sdm2);
ESP_RETURN_ON_FALSE(apll_freq, ESP_ERR_INVALID_ARG, TAG, "APLL coefficients calculate failed");
bool need_config = true;
esp_os_enter_critical(&periph_spinlock);
/* If APLL is not in use or only one peripheral in use, its frequency can be changed as will
* But when more than one peripheral refers APLL, its frequency is not allowed to change once it is set */
if (s_cur_apll_freq_hz == 0 || s_apll_ref_cnt < 2) {
s_cur_apll_freq_hz = apll_freq;
} else {
apll_freq = s_cur_apll_freq_hz;
need_config = false;
}
esp_os_exit_critical(&periph_spinlock);
*real_freq_hz = apll_freq;
if (need_config) {
ESP_LOGD(TAG, "APLL will working at %"PRIu32" Hz with coefficients [sdm0] %"PRIu32" [sdm1] %"PRIu32" [sdm2] %"PRIu32" [o_div] %"PRIu32"",
apll_freq, sdm0, sdm1, sdm2, o_div);
/* Set coefficients for APLL, notice that it doesn't mean APLL will start */
rtc_clk_apll_coeff_set(o_div, sdm0, sdm1, sdm2);
} else {
return ESP_ERR_INVALID_STATE;
}
return ESP_OK;
return esp_clk_tree_src_set_freq_hz(SOC_MOD_CLK_APLL, expt_freq_hz, real_freq_hz);
}
#endif // SOC_CLK_APLL_SUPPORTED
#if SOC_CLK_MPLL_SUPPORTED
esp_err_t IRAM_ATTR periph_rtc_mpll_acquire(void)
{
// power up LDO for the MPLL
#if CONFIG_ESP_LDO_RESERVE_PSRAM
esp_ldo_channel_config_t ldo_mpll_config = {
.chan_id = CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN,
.voltage_mv = CONFIG_ESP_LDO_VOLTAGE_PSRAM_DOMAIN,
};
ESP_RETURN_ON_ERROR(esp_ldo_acquire_channel(&ldo_mpll_config, &s_ldo_chan), TAG, "acquire internal LDO for MPLL failed");
#endif
esp_os_enter_critical(&periph_spinlock);
s_mpll_ref_cnt++;
if (s_mpll_ref_cnt == 1) {
// For the first time enable MPLL, need to set power up
rtc_clk_mpll_enable();
}
esp_os_exit_critical(&periph_spinlock);
return ESP_OK;
// For IRAM compatibility, we do not use esp_clk_tree_enable_src here
return esp_clk_tree_mpll_acquire();
}
void periph_rtc_mpll_release(void)
{
#if defined(CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN) && CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN != -1
if (s_ldo_chan) {
esp_ldo_release_channel(s_ldo_chan);
}
#endif
esp_os_enter_critical(&periph_spinlock);
assert(s_mpll_ref_cnt > 0);
s_mpll_ref_cnt--;
if (s_mpll_ref_cnt == 0) {
// If there is no peripheral using MPLL, shut down the power
s_cur_mpll_freq_hz = 0;
rtc_clk_mpll_disable();
}
esp_os_exit_critical(&periph_spinlock);
// For IRAM compatibility, we do not use esp_clk_tree_enable_src here
(void)esp_clk_tree_mpll_release();
}
esp_err_t IRAM_ATTR periph_rtc_mpll_freq_set(uint32_t expt_freq_hz, uint32_t *real_freq_hz)
{
esp_err_t ret = ESP_OK;
// Guarantee 'periph_rtc_apll_acquire' has been called before set apll freq
assert(s_mpll_ref_cnt > 0);
esp_os_enter_critical(&periph_spinlock);
if (s_cur_mpll_freq_hz == expt_freq_hz) {
goto end;
}
/* If MPLL is not in use or only one peripheral in use, its frequency can be changed as will
* But when more than one peripheral refers MPLL, its frequency is not allowed to change once it is set */
if (s_cur_mpll_freq_hz == 0 || s_mpll_ref_cnt < 2) {
uint32_t xtal_freq_mhz = clk_ll_xtal_load_freq_mhz();
rtc_clk_mpll_configure(xtal_freq_mhz, expt_freq_hz / MHZ, false);
s_cur_mpll_freq_hz = clk_ll_mpll_get_freq_mhz(xtal_freq_mhz) * MHZ;
} else {
ret = ESP_ERR_INVALID_STATE;
}
end:
if (real_freq_hz != NULL) {
*real_freq_hz = s_cur_mpll_freq_hz;
}
esp_os_exit_critical(&periph_spinlock);
return ret;
// For IRAM compatibility, we do not use esp_clk_tree_src_set_freq_hz here
return esp_clk_tree_mpll_freq_set(expt_freq_hz, real_freq_hz);
}
#endif // SOC_CLK_MPLL_SUPPORTED
+10 -10
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -21,7 +21,7 @@ extern "C" {
*
* @return true: success for enable the RC_FAST clock, false: RC_FAST clock enable failed
*/
bool periph_rtc_dig_clk8m_enable(void);
bool periph_rtc_dig_clk8m_enable(void) __attribute__((deprecated("Please use esp_clk_tree_enable_src instead")));
/**
* @brief This function is used to disable the digital RC_FAST clock, which should be called
@@ -30,25 +30,25 @@ bool periph_rtc_dig_clk8m_enable(void);
* @note If this function is called a number of times, the `periph_rtc_dig_clk8m_disable`
* function needs to be called same times to disable.
*/
void periph_rtc_dig_clk8m_disable(void);
void periph_rtc_dig_clk8m_disable(void) __attribute__((deprecated("Please use esp_clk_tree_enable_src instead")));
/**
* @brief This function is used to get the real clock frequency value of RC_FAST clock
*
* @return The real clock value, in Hz
*/
uint32_t periph_rtc_dig_clk8m_get_freq(void);
uint32_t periph_rtc_dig_clk8m_get_freq(void) __attribute__((deprecated("Please use esp_clk_tree_src_get_freq_hz instead")));
#if SOC_CLK_APLL_SUPPORTED
/**
* @brief Enable APLL power if it has not enabled
*/
void periph_rtc_apll_acquire(void);
void periph_rtc_apll_acquire(void) __attribute__((deprecated("Please use esp_clk_tree_enable_src instead")));
/**
* @brief Shut down APLL power if no peripherals using APLL
*/
void periph_rtc_apll_release(void);
void periph_rtc_apll_release(void) __attribute__((deprecated("Please use esp_clk_tree_enable_src instead")));
/**
* @brief Calculate and set APLL coefficients by given frequency
@@ -69,19 +69,19 @@ void periph_rtc_apll_release(void);
* - ESP_ERR_INVALID_ARG: The input expt_freq is out of APLL support range
* - ESP_ERR_INVALID_STATE: APLL is referred by more than one peripherals, not allowed to change its frequency now
*/
esp_err_t periph_rtc_apll_freq_set(uint32_t expt_freq_hz, uint32_t *real_freq_hz);
esp_err_t periph_rtc_apll_freq_set(uint32_t expt_freq_hz, uint32_t *real_freq_hz) __attribute__((deprecated("Please use esp_clk_tree_src_set_freq_hz instead")));
#endif // SOC_CLK_APLL_SUPPORTED
#if SOC_CLK_MPLL_SUPPORTED
/**
* @brief Enable MPLL power if it has not enabled
*/
esp_err_t periph_rtc_mpll_acquire(void);
esp_err_t periph_rtc_mpll_acquire(void) __attribute__((deprecated("Please use esp_clk_tree_enable_src instead")));
/**
* @brief Shut down MPLL power if no peripherals using APLL
*/
void periph_rtc_mpll_release(void);
void periph_rtc_mpll_release(void) __attribute__((deprecated("Please use esp_clk_tree_enable_src instead")));
/**
* @brief Configure MPLL frequency
@@ -96,7 +96,7 @@ void periph_rtc_mpll_release(void);
* - ESP_OK: MPLL frequency set success
* - ESP_ERR_INVALID_STATE: MPLL is referred by more than one peripherals, not allowed to change its frequency now
*/
esp_err_t periph_rtc_mpll_freq_set(uint32_t expt_freq_hz, uint32_t *real_freq_hz);
esp_err_t periph_rtc_mpll_freq_set(uint32_t expt_freq_hz, uint32_t *real_freq_hz) __attribute__((deprecated("Please use esp_clk_tree_src_set_freq_hz instead")));
#endif // SOC_CLK_MPLL_SUPPORTED
#ifdef __cplusplus
@@ -1,13 +1,15 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include "esp_err.h"
#include "soc/clk_tree_defs.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
@@ -18,7 +20,7 @@ extern "C" {
*/
typedef enum {
ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, /*< Get value from the data cached by the driver; If the data is 0, then a calibration will be performed */
ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, /*< Get its approxiamte frequency value */
ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, /*< Get its approximate frequency value */
ESP_CLK_TREE_SRC_FREQ_PRECISION_EXACT, /*< Always perform a calibration */
ESP_CLK_TREE_SRC_FREQ_PRECISION_INVALID, /*< Invalid degree of precision */
} esp_clk_tree_src_freq_precision_t;
@@ -42,6 +44,20 @@ typedef enum {
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);
/**
* @brief Set frequency of module clock source
*
* @param[in] clk_src Clock source available to modules, in soc_module_clk_t
* @param[in] expt_freq_value Expected frequency of the clock source, in Hz
* @param[out] ret_freq_value Real frequency of the clock source, in Hz
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NOT_SUPPORTED Unsupported clock source
*/
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);
#ifdef __cplusplus
}
#endif
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -118,6 +118,72 @@ esp_err_t esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool ena
*/
bool esp_clk_tree_is_power_on(soc_root_clk_circuit_t clk_circuit);
#if SOC_CLK_APLL_SUPPORTED
/**
* @brief Enable APLL power if it has not enabled
*
* @note Do not use this function in applications or drivers, please use `esp_clk_tree_enable_src` instead.
*/
void esp_clk_tree_apll_acquire(void);
/**
* @brief Shut down APLL power if no peripherals using APLL
*
* @note Do not use this function in applications or drivers, please use `esp_clk_tree_enable_src` instead.
*/
void esp_clk_tree_apll_release(void);
/**
* @brief Calculate and set APLL coefficients by given frequency
* @note This calculation is based on the inequality:
* xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536) >= CLK_LL_APLL_MULTIPLIER_MIN_HZ(350 MHz)
* It will always calculate the minimum coefficients that can satisfy the inequality above, instead of loop them one by one.
* which means more appropriate coefficients are likely to exist.
* But this algorithm can meet almost all the cases and the accuracy can be guaranteed as well.
* @note The APLL frequency is only allowed to set when there is only one peripheral refer to it.
* If APLL is already set by another peripheral, this function will return `ESP_ERR_INVALID_STATE`
* and output the current frequency by parameter `real_freq`.
*
* @param expt_freq Expected APLL frequency (unit: Hz)
* @param real_freq APLL real working frequency [output] (unit: Hz)
* @return
* - ESP_OK: APLL frequency set success
* - ESP_ERR_INVALID_ARG: The input expt_freq is out of APLL support range
* - ESP_ERR_INVALID_STATE: APLL is referred by more than one peripherals, not allowed to change its frequency now
*/
esp_err_t esp_clk_tree_apll_freq_set(uint32_t expt_freq_hz, uint32_t *real_freq_hz);
#endif // SOC_CLK_APLL_SUPPORTED
#if SOC_CLK_MPLL_SUPPORTED
/**
* @brief Enable MPLL power if it has not enabled
*
* @note Do not use this function in applications or drivers, please use `esp_clk_tree_enable_src` instead.
*/
esp_err_t esp_clk_tree_mpll_acquire(void);
/**
* @brief Shut down MPLL power if no peripherals using MPLL
*
* @note Do not use this function in applications or drivers, please use `esp_clk_tree_enable_src` instead.
*/
void esp_clk_tree_mpll_release(void);
/**
* @brief Configure MPLL frequency
* @note The MPLL frequency is only allowed to set when there is only one peripheral refer to it.
* If MPLL is already set by another peripheral, this function will return `ESP_ERR_INVALID_STATE`
* and output the current frequency by parameter `real_freq`.
*
* @param expt_freq Expected MPLL frequency (unit: Hz)
* @param real_freq MPLL current working frequency [output] (unit: Hz)
* @return
* - ESP_OK: MPLL frequency set success
* - ESP_ERR_INVALID_STATE: MPLL is referred by more than one peripherals, not allowed to change its frequency now
*/
esp_err_t esp_clk_tree_mpll_freq_set(uint32_t expt_freq_hz, uint32_t *real_freq_hz);
#endif // SOC_CLK_MPLL_SUPPORTED
#ifdef __cplusplus
}
#endif
@@ -1,20 +1,25 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdatomic.h>
#include "esp_clk_tree.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "soc/rtc.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"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
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)
{
@@ -70,6 +75,26 @@ uint32_t *freq_value)
return ESP_OK;
}
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)
{
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");
uint32_t real_freq_value = 0;
esp_err_t ret = ESP_OK;
switch (clk_src) {
case SOC_MOD_CLK_APLL:
ret = esp_clk_tree_apll_freq_set(expt_freq_value, &real_freq_value);
break;
default:
return ESP_ERR_NOT_SUPPORTED;
}
if (ret_freq_value) {
*ret_freq_value = real_freq_value;
}
return ret;
}
void esp_clk_tree_initialize(void)
{
}
@@ -88,6 +113,41 @@ esp_err_t esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool ena
esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
{
(void)clk_src; (void)enable;
if (clk_src < 1 || clk_src >= SOC_MOD_CLK_INVALID) {
// some conditions is legal, e.g. -1 means external clock source
return ESP_OK;
}
// APLL has its own reference counting
if (clk_src == SOC_MOD_CLK_APLL) {
if (enable) {
esp_clk_tree_apll_acquire();
} else {
esp_clk_tree_apll_release();
}
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 ((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;
default:
break;
}
}
return ESP_OK;
}
@@ -1,20 +1,25 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdatomic.h>
#include "esp_clk_tree.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "soc/rtc.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"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
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)
{
@@ -64,6 +69,12 @@ uint32_t *freq_value)
return ESP_OK;
}
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;
return ESP_ERR_NOT_SUPPORTED;
}
void esp_clk_tree_initialize(void)
{
}
@@ -82,6 +93,31 @@ esp_err_t esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool ena
esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
{
(void)clk_src; (void)enable;
if (clk_src < 1 || clk_src >= SOC_MOD_CLK_INVALID) {
// some conditions is legal, e.g. -1 means external clock source
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 ((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;
default:
break;
}
}
return ESP_OK;
}
@@ -1,20 +1,25 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdatomic.h>
#include "esp_clk_tree.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "soc/rtc.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"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
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)
{
@@ -64,6 +69,12 @@ uint32_t *freq_value)
return ESP_OK;
}
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;
return ESP_ERR_NOT_SUPPORTED;
}
void esp_clk_tree_initialize(void)
{
}
@@ -82,6 +93,31 @@ esp_err_t esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool ena
esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
{
(void)clk_src; (void)enable;
if (clk_src < 1 || clk_src >= SOC_MOD_CLK_INVALID) {
// some conditions is legal, e.g. -1 means external clock source
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 ((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;
default:
break;
}
}
return ESP_OK;
}
@@ -1,20 +1,22 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdatomic.h>
#include "sdkconfig.h"
#include "esp_clk_tree.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "soc/rtc.h"
#include "hal/clk_tree_hal.h"
#include "hal/clk_tree_ll.h"
#include "hal/clk_gate_ll.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_rom_sys.h"
#include "esp_private/periph_ctrl.h"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
@@ -70,8 +72,13 @@ uint32_t *freq_value)
return ESP_OK;
}
#define ENUM2ARRAY(clk_src) (clk_src - SOC_MOD_CLK_PLL_F12M)
static int16_t s_pll_src_cg_ref_cnt[9] = { 0 };
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;
return ESP_ERR_NOT_SUPPORTED;
}
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
static bool esp_clk_tree_initialized = false;
void esp_clk_tree_initialize(void)
@@ -87,10 +94,10 @@ void esp_clk_tree_initialize(void)
soc_cpu_clk_src_t current_cpu_clk_src = clk_ll_cpu_get_src();
if (current_cpu_clk_src == SOC_CPU_CLK_SRC_PLL_F160M) {
s_pll_src_cg_ref_cnt[ENUM2ARRAY(SOC_MOD_CLK_PLL_F160M)] = 1;
s_pll_src_cg_ref_cnt[SOC_MOD_CLK_PLL_F160M] = 1;
_clk_gate_ll_ref_240m_clk_en(false);
} else if (current_cpu_clk_src == SOC_CPU_CLK_SRC_PLL_F240M) {
s_pll_src_cg_ref_cnt[ENUM2ARRAY(SOC_MOD_CLK_PLL_F240M)] = 1;
s_pll_src_cg_ref_cnt[SOC_MOD_CLK_PLL_F240M] = 1;
_clk_gate_ll_ref_160m_clk_en(false);
}
_clk_gate_ll_ref_120m_clk_en(false);
@@ -117,38 +124,47 @@ esp_err_t esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool ena
return ESP_OK; // TODO: PM-354
}
#define ENABLE_CLK_GATE(clk_src_en_func, enable) \
PERIPH_RCC_ATOMIC() { \
clk_src_en_func(enable); \
}
esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
{
if (!esp_clk_tree_initialized || (clk_src < SOC_MOD_CLK_PLL_F12M) || (clk_src > SOC_MOD_CLK_PLL_F240M)) {
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 (!esp_clk_tree_initialized) {
return ESP_OK;
}
PERIPH_RCC_ATOMIC() {
if (enable) {
s_pll_src_cg_ref_cnt[ENUM2ARRAY(clk_src)]++;
}
if (s_pll_src_cg_ref_cnt[ENUM2ARRAY(clk_src)] == 1) {
switch (clk_src) {
case SOC_MOD_CLK_PLL_F12M: clk_gate_ll_ref_12m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F20M: clk_gate_ll_ref_20m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F40M: clk_gate_ll_ref_40m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F48M: clk_gate_ll_ref_48m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F60M: clk_gate_ll_ref_60m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F80M: clk_gate_ll_ref_80m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F120M: clk_gate_ll_ref_120m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F160M: clk_gate_ll_ref_160m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F240M: clk_gate_ll_ref_240m_clk_en(enable); break;
default: break;
}
}
if (!enable) {
s_pll_src_cg_ref_cnt[ENUM2ARRAY(clk_src)]--;
}
if (s_pll_src_cg_ref_cnt[ENUM2ARRAY(clk_src)] < 0) {
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);
s_pll_src_cg_ref_cnt[ENUM2ARRAY(clk_src)] = 0;
atomic_store(&s_pll_src_cg_ref_cnt[clk_src], 0);
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_F12M: ENABLE_CLK_GATE(clk_gate_ll_ref_12m_clk_en, enable); break;
case SOC_MOD_CLK_PLL_F20M: ENABLE_CLK_GATE(clk_gate_ll_ref_20m_clk_en, enable); break;
case SOC_MOD_CLK_PLL_F40M: ENABLE_CLK_GATE(clk_gate_ll_ref_40m_clk_en, enable); break;
case SOC_MOD_CLK_PLL_F48M: ENABLE_CLK_GATE(clk_gate_ll_ref_48m_clk_en, enable); break;
case SOC_MOD_CLK_PLL_F60M: ENABLE_CLK_GATE(clk_gate_ll_ref_60m_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_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;
}
#undef ENUM2ARRAY
@@ -459,7 +459,9 @@ FORCE_IRAM_ATTR void rtc_clk_cpu_set_to_default_config(void)
#endif
rtc_clk_cpu_freq_to_xtal(freq_mhz, 1);
#ifndef BOOTLOADER_BUILD
esp_clk_tree_enable_src(old_cpu_clk_src, false);
if (old_cpu_clk_src != SOC_MOD_CLK_XTAL) {
esp_clk_tree_enable_src(old_cpu_clk_src, false);
}
#endif
s_cur_pll_freq = 0; // no disable PLL, but set freq to 0 to trigger a PLL calibration after wake-up from sleep
}
@@ -1,20 +1,25 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdatomic.h>
#include "esp_clk_tree.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "soc/rtc.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"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
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)
{
@@ -64,6 +69,12 @@ uint32_t *freq_value)
return ESP_OK;
}
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;
return ESP_ERR_NOT_SUPPORTED;
}
void esp_clk_tree_initialize(void)
{
}
@@ -82,6 +93,31 @@ esp_err_t esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool ena
esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
{
(void)clk_src; (void)enable;
if (clk_src < 1 || clk_src >= SOC_MOD_CLK_INVALID) {
// some conditions is legal, e.g. -1 means external clock source
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 ((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;
default:
break;
}
}
return ESP_OK;
}
@@ -1,20 +1,25 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdatomic.h>
#include "esp_clk_tree.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "soc/rtc.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"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
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)
{
@@ -64,6 +69,12 @@ uint32_t *freq_value)
return ESP_OK;
}
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;
return ESP_ERR_NOT_SUPPORTED;
}
void esp_clk_tree_initialize(void)
{
}
@@ -82,6 +93,29 @@ esp_err_t esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool ena
esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
{
(void)clk_src; (void)enable;
if (clk_src < 1 || clk_src >= SOC_MOD_CLK_INVALID) {
// some conditions is legal, e.g. -1 means external clock source
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 ((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;
default:
break;
}
}
return ESP_OK;
}
@@ -1,20 +1,25 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdatomic.h>
#include "esp_clk_tree.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "soc/rtc.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"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
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)
{
@@ -61,6 +66,12 @@ uint32_t *freq_value)
return ESP_OK;
}
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;
return ESP_ERR_NOT_SUPPORTED;
}
void esp_clk_tree_initialize(void)
{
}
@@ -79,6 +90,29 @@ esp_err_t esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool ena
esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
{
(void)clk_src; (void)enable;
if (clk_src < 1 || clk_src >= SOC_MOD_CLK_INVALID) {
// some conditions is legal, e.g. -1 means external clock source
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 ((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;
default:
break;
}
}
return ESP_OK;
}
@@ -5,13 +5,16 @@
*/
#include <stdint.h>
#include <stdatomic.h>
#include "esp_clk_tree.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "soc/rtc.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"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
@@ -1,17 +1,20 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdatomic.h>
#include "esp_clk_tree.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "soc/rtc.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"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
@@ -5,6 +5,7 @@
*/
#include <stdint.h>
#include <stdatomic.h>
#include "sdkconfig.h"
#include "esp_clk_tree.h"
#include "esp_err.h"
@@ -13,12 +14,12 @@
#include "soc/clk_tree_defs.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/periph_ctrl.h"
#include "esp_rom_sys.h"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
@@ -103,8 +104,30 @@ esp_err_t esp_clk_tree_src_get_freq_hz(soc_module_clk_t clk_src, esp_clk_tree_sr
return ESP_OK;
}
#define ENUM2ARRAY(clk_src) (clk_src - SOC_MOD_CLK_PLL_F20M)
static int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_PLL_F240M - SOC_MOD_CLK_PLL_F20M + 1] = { 0 };
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)
{
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");
uint32_t real_freq_value = 0;
esp_err_t ret = ESP_OK;
switch (clk_src) {
case SOC_MOD_CLK_APLL:
ret = esp_clk_tree_apll_freq_set(expt_freq_value, &real_freq_value);
break;
case SOC_MOD_CLK_MPLL:
ret = esp_clk_tree_mpll_freq_set(expt_freq_value, &real_freq_value);
break;
default:
return ESP_ERR_NOT_SUPPORTED;
}
if (ret_freq_value) {
*ret_freq_value = real_freq_value;
}
return ret;
}
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
static bool esp_clk_tree_initialized = false;
void esp_clk_tree_initialize(void)
@@ -139,36 +162,67 @@ esp_err_t esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool ena
return ESP_OK; // TODO: PM-354
}
#define ENABLE_CLK_GATE(clk_src_en_func, enable) \
PERIPH_RCC_ATOMIC() { \
clk_src_en_func(enable); \
}
esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
{
if (!esp_clk_tree_initialized || (clk_src < SOC_MOD_CLK_PLL_F20M) || (clk_src > SOC_MOD_CLK_PLL_F240M)) {
if (clk_src < 1 || clk_src >= SOC_MOD_CLK_INVALID) {
// some conditions is legal, e.g. -1 means external clock source
return ESP_OK;
}
PERIPH_RCC_ATOMIC() {
if (enable) {
s_pll_src_cg_ref_cnt[ENUM2ARRAY(clk_src)]++;
}
if (s_pll_src_cg_ref_cnt[ENUM2ARRAY(clk_src)] == 1) {
switch (clk_src) {
case SOC_MOD_CLK_PLL_F20M: clk_gate_ll_ref_20m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F25M: clk_gate_ll_ref_25m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F50M: clk_gate_ll_ref_50m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F80M: clk_gate_ll_ref_80m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F120M: clk_gate_ll_ref_120m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F160M: clk_gate_ll_ref_160m_clk_en(enable); break;
case SOC_MOD_CLK_PLL_F240M: clk_gate_ll_ref_240m_clk_en(enable); break;
default: break;
if (!esp_clk_tree_initialized) {
return ESP_OK;
}
// 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();
}
}
if (!enable) {
s_pll_src_cg_ref_cnt[ENUM2ARRAY(clk_src)]--;
}
if (s_pll_src_cg_ref_cnt[ENUM2ARRAY(clk_src)] < 0) {
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);
s_pll_src_cg_ref_cnt[ENUM2ARRAY(clk_src)] = 0;
atomic_store(&s_pll_src_cg_ref_cnt[clk_src], 0);
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_F50M: ENABLE_CLK_GATE(clk_gate_ll_ref_50m_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_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;
}
#undef ENUM2ARRAY
@@ -1,20 +1,25 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdatomic.h>
#include "esp_clk_tree.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "soc/rtc.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"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
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)
{
@@ -68,6 +73,26 @@ uint32_t *freq_value)
return ESP_OK;
}
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)
{
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");
uint32_t real_freq_value = 0;
esp_err_t ret = ESP_OK;
switch (clk_src) {
case SOC_MOD_CLK_APLL:
ret = esp_clk_tree_apll_freq_set(expt_freq_value, &real_freq_value);
break;
default:
return ESP_ERR_NOT_SUPPORTED;
}
if (ret_freq_value) {
*ret_freq_value = real_freq_value;
}
return ret;
}
void esp_clk_tree_initialize(void)
{
}
@@ -86,6 +111,40 @@ esp_err_t esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool ena
esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
{
(void)clk_src; (void)enable;
if (clk_src < 1 || clk_src >= SOC_MOD_CLK_INVALID) {
// some conditions is legal, e.g. -1 means external clock source
return ESP_OK;
}
// APLL has its own reference counting
if (clk_src == SOC_MOD_CLK_APLL) {
if (enable) {
esp_clk_tree_apll_acquire();
} else {
esp_clk_tree_apll_release();
}
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 ((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;
default:
break;
}
}
return ESP_OK;
}
@@ -1,20 +1,25 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdatomic.h>
#include "esp_clk_tree.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_log.h"
#include "soc/rtc.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"
ESP_LOG_ATTR_TAG(TAG, "esp_clk_tree");
static _Atomic int16_t s_pll_src_cg_ref_cnt[SOC_MOD_CLK_INVALID] = { 0 };
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)
{
@@ -68,6 +73,18 @@ uint32_t *freq_value)
return ESP_OK;
}
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)
{
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");
switch (clk_src) {
default:
return ESP_ERR_NOT_SUPPORTED;
}
return ESP_OK;
}
void esp_clk_tree_initialize(void)
{
}
@@ -86,6 +103,29 @@ esp_err_t esp_clk_tree_enable_power(soc_root_clk_circuit_t clk_circuit, bool ena
esp_err_t esp_clk_tree_enable_src(soc_module_clk_t clk_src, bool enable)
{
(void)clk_src; (void)enable;
if (clk_src < 1 || clk_src >= SOC_MOD_CLK_INVALID) {
// some conditions is legal, e.g. -1 means external clock source
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 ((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;
default:
break;
}
}
return ESP_OK;
}
@@ -1,10 +1,14 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <assert.h>
#include <stdbool.h>
#include <freertos/FreeRTOS.h>
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/critical_section.h"
#include "hal/clk_tree_hal.h"
#include "hal/clk_tree_ll.h"
#include "soc/rtc.h"
@@ -12,7 +16,13 @@
#include "soc/clk_tree_defs.h"
#include "soc/soc_caps.h"
#include "sdkconfig.h"
#include "esp_check.h"
#include "esp_hw_log.h"
#include "esp_log.h"
#if SOC_CLK_MPLL_SUPPORTED
#include "esp_private/rtc_clk.h"
#include "esp_ldo_regulator.h"
#endif
ESP_HW_LOG_ATTR_TAG(TAG, "esp_clk_tree_common");
@@ -201,3 +211,182 @@ uint32_t esp_clk_tree_lp_fast_get_freq_hz(esp_clk_tree_src_freq_precision_t prec
return 0;
}
}
#if SOC_CLK_APLL_SUPPORTED
// Current APLL frequency, in HZ. Zero if APLL is not enabled.
static portMUX_TYPE __attribute__((unused)) s_periph_apll_spinlock = portMUX_INITIALIZER_UNLOCKED;
static uint32_t s_cur_apll_freq_hz = 0;
static int s_apll_ref_cnt = 0;
// Pending APLL coefficients: calibration requires APLL to be powered on,
// so if freq is set before acquire, we defer rtc_clk_apll_coeff_set until enable.
static bool s_apll_coeff_pending = false;
static uint32_t s_apll_pending_o_div = 0;
static uint32_t s_apll_pending_sdm0 = 0;
static uint32_t s_apll_pending_sdm1 = 0;
static uint32_t s_apll_pending_sdm2 = 0;
void esp_clk_tree_apll_acquire(void)
{
bool apply_pending = false;
uint32_t o_div, sdm0, sdm1, sdm2;
esp_os_enter_critical(&s_periph_apll_spinlock);
s_apll_ref_cnt++;
if (s_apll_ref_cnt == 1) {
rtc_clk_apll_enable(true);
if (s_apll_coeff_pending) {
apply_pending = true;
o_div = s_apll_pending_o_div;
sdm0 = s_apll_pending_sdm0;
sdm1 = s_apll_pending_sdm1;
sdm2 = s_apll_pending_sdm2;
s_apll_coeff_pending = false;
}
}
esp_os_exit_critical(&s_periph_apll_spinlock);
if (apply_pending) {
rtc_clk_apll_coeff_set(o_div, sdm0, sdm1, sdm2);
}
}
void esp_clk_tree_apll_release(void)
{
esp_os_enter_critical(&s_periph_apll_spinlock);
assert(s_apll_ref_cnt > 0);
s_apll_ref_cnt--;
if (s_apll_ref_cnt == 0) {
s_cur_apll_freq_hz = 0;
s_apll_coeff_pending = false;
rtc_clk_apll_enable(false);
}
esp_os_exit_critical(&s_periph_apll_spinlock);
}
esp_err_t esp_clk_tree_apll_freq_set(uint32_t expt_freq_hz, uint32_t *real_freq_hz)
{
uint32_t o_div = 0;
uint32_t sdm0 = 0;
uint32_t sdm1 = 0;
uint32_t sdm2 = 0;
uint32_t apll_freq = rtc_clk_apll_coeff_calc(expt_freq_hz, &o_div, &sdm0, &sdm1, &sdm2);
ESP_RETURN_ON_FALSE(apll_freq, ESP_ERR_INVALID_ARG, TAG, "APLL coefficients calculate failed");
bool need_config = true;
bool apll_enabled = false;
esp_os_enter_critical(&s_periph_apll_spinlock);
/* If APLL is not in use or only one peripheral in use, its frequency can be changed as will
* But when more than one peripheral refers APLL, its frequency is not allowed to change once it is set */
if (s_cur_apll_freq_hz == 0 || s_apll_ref_cnt < 2) {
s_cur_apll_freq_hz = apll_freq;
apll_enabled = (s_apll_ref_cnt > 0);
if (!apll_enabled) {
// APLL not yet powered on, defer coeff_set (which includes calibration) until acquire
s_apll_coeff_pending = true;
s_apll_pending_o_div = o_div;
s_apll_pending_sdm0 = sdm0;
s_apll_pending_sdm1 = sdm1;
s_apll_pending_sdm2 = sdm2;
}
} else {
apll_freq = s_cur_apll_freq_hz;
need_config = false;
}
esp_os_exit_critical(&s_periph_apll_spinlock);
if (real_freq_hz != NULL) {
*real_freq_hz = apll_freq;
}
if (need_config) {
ESP_LOGD(TAG, "APLL works at %"PRIu32" Hz with coefficients [sdm0] %"PRIu32" [sdm1] %"PRIu32" [sdm2] %"PRIu32" [o_div] %"PRIu32"",
apll_freq, sdm0, sdm1, sdm2, o_div);
if (apll_enabled) {
rtc_clk_apll_coeff_set(o_div, sdm0, sdm1, sdm2);
}
} else {
return ESP_ERR_INVALID_STATE;
}
return ESP_OK;
}
#endif /* SOC_CLK_APLL_SUPPORTED */
#if SOC_CLK_MPLL_SUPPORTED
static portMUX_TYPE __attribute__((unused)) s_periph_mpll_spinlock = portMUX_INITIALIZER_UNLOCKED;
static uint32_t s_cur_mpll_freq_hz = 0;
static int s_mpll_ref_cnt = 0;
#if CONFIG_ESP_LDO_RESERVE_PSRAM
static esp_ldo_channel_handle_t s_ldo_chan = NULL;
#endif
esp_err_t IRAM_ATTR esp_clk_tree_mpll_acquire(void)
{
// power up LDO for the MPLL
#if CONFIG_ESP_LDO_RESERVE_PSRAM
if (s_ldo_chan == NULL) {
esp_ldo_channel_config_t ldo_mpll_config = {
.chan_id = CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN,
.voltage_mv = CONFIG_ESP_LDO_VOLTAGE_PSRAM_DOMAIN,
};
ESP_RETURN_ON_ERROR(esp_ldo_acquire_channel(&ldo_mpll_config, &s_ldo_chan), TAG, "acquire internal LDO for MPLL failed");
}
#endif
esp_os_enter_critical(&s_periph_mpll_spinlock);
s_mpll_ref_cnt++;
if (s_mpll_ref_cnt == 1) {
// For the first time enable MPLL, need to set power up
rtc_clk_mpll_enable();
}
esp_os_exit_critical(&s_periph_mpll_spinlock);
return ESP_OK;
}
void esp_clk_tree_mpll_release(void)
{
bool __attribute__((unused)) release_ldo = false;
esp_os_enter_critical(&s_periph_mpll_spinlock);
assert(s_mpll_ref_cnt > 0);
s_mpll_ref_cnt--;
if (s_mpll_ref_cnt == 0) {
s_cur_mpll_freq_hz = 0;
rtc_clk_mpll_disable();
release_ldo = true;
}
esp_os_exit_critical(&s_periph_mpll_spinlock);
#if defined(CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN) && CONFIG_ESP_LDO_CHAN_PSRAM_DOMAIN != -1
if (release_ldo && s_ldo_chan) {
esp_ldo_release_channel(s_ldo_chan);
s_ldo_chan = NULL;
}
#endif
}
esp_err_t IRAM_ATTR esp_clk_tree_mpll_freq_set(uint32_t expt_freq_hz, uint32_t *real_freq_hz)
{
esp_err_t ret = ESP_OK;
// Guarantee 'esp_clk_tree_mpll_acquire' has been called before set mpll freq
assert(s_mpll_ref_cnt > 0);
esp_os_enter_critical(&s_periph_mpll_spinlock);
if (s_cur_mpll_freq_hz == expt_freq_hz) {
goto end;
}
/* If MPLL is not in use or only one peripheral in use, its frequency can be changed as will
* But when more than one peripheral refers MPLL, its frequency is not allowed to change once it is set */
if (s_cur_mpll_freq_hz == 0 || s_mpll_ref_cnt < 2) {
uint32_t xtal_freq_mhz = clk_hal_xtal_get_freq_mhz();
rtc_clk_mpll_configure(xtal_freq_mhz, expt_freq_hz / MHZ, false);
s_cur_mpll_freq_hz = clk_ll_mpll_get_freq_mhz(xtal_freq_mhz) * MHZ;
} else {
ret = ESP_ERR_INVALID_STATE;
}
end:
if (real_freq_hz != NULL) {
*real_freq_hz = s_cur_mpll_freq_hz;
}
esp_os_exit_critical(&s_periph_mpll_spinlock);
return ret;
}
#endif /* SOC_CLK_MPLL_SUPPORTED */