refactor(clk): split clock HAL into separate component

This commit is contained in:
Song Ruo Jing
2026-01-31 22:32:37 +08:00
committed by morris
parent 15be2a0176
commit 89da3742b8
77 changed files with 834 additions and 800 deletions
@@ -0,0 +1,85 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "hal/assert.h"
#include "hal/clk_tree_hal.h"
#include "hal/clk_tree_ll.h"
#include "hal/log.h"
HAL_LOG_ATTR_TAG(CLK_HAL_TAG, "clk_hal");
uint32_t clk_hal_soc_root_get_freq_mhz(soc_cpu_clk_src_t cpu_clk_src)
{
switch (cpu_clk_src) {
case SOC_CPU_CLK_SRC_XTAL:
return clk_hal_xtal_get_freq_mhz();
case SOC_CPU_CLK_SRC_PLL:
return clk_ll_bbpll_get_freq_mhz();
case SOC_CPU_CLK_SRC_RC_FAST:
return SOC_CLK_RC_FAST_FREQ_APPROX / MHZ;
case SOC_CPU_CLK_SRC_FLASH_PLL:
return clk_ll_flash_pll_get_freq_mhz();
default:
// Unknown CPU_CLK mux input
HAL_ASSERT(false);
return 0;
}
}
uint32_t clk_hal_cpu_get_freq_hz(void)
{
soc_cpu_clk_src_t source = clk_ll_cpu_get_src();
return clk_hal_soc_root_get_freq_mhz(source) * MHZ / clk_ll_cpu_get_divider();
}
static uint32_t clk_hal_ahb_get_freq_hz(void)
{
soc_cpu_clk_src_t source = clk_ll_cpu_get_src();
return clk_hal_soc_root_get_freq_mhz(source) * MHZ / clk_ll_ahb_get_divider();
}
uint32_t clk_hal_apb_get_freq_hz(void)
{
return clk_hal_ahb_get_freq_hz() / clk_ll_apb_get_divider();
}
uint32_t clk_hal_lp_slow_get_freq_hz(void)
{
switch (clk_ll_rtc_slow_get_src()) {
case SOC_RTC_SLOW_CLK_SRC_RC_SLOW:
return SOC_CLK_RC_SLOW_FREQ_APPROX;
case SOC_RTC_SLOW_CLK_SRC_XTAL32K:
return SOC_CLK_XTAL32K_FREQ_APPROX;
case SOC_RTC_SLOW_CLK_SRC_OSC_SLOW:
return SOC_CLK_OSC_SLOW_FREQ_APPROX;
case SOC_RTC_SLOW_CLK_SRC_RC32K:
return SOC_CLK_RC32K_FREQ_APPROX;
default:
// Unknown RTC_SLOW_CLK mux input
HAL_ASSERT(false);
return 0;
}
}
uint32_t clk_hal_xtal_get_freq_mhz(void)
{
uint32_t freq = clk_ll_xtal_load_freq_mhz();
if (freq == 0) {
HAL_LOGW(CLK_HAL_TAG, "invalid RTC_XTAL_FREQ_REG value, assume 32MHz");
return (uint32_t)SOC_XTAL_FREQ_32M;
}
return freq;
}
void clk_hal_clock_output_setup(soc_clkout_sig_id_t clk_sig, clock_out_channel_t channel_id)
{
clk_ll_bind_output_channel(clk_sig, channel_id);
}
void clk_hal_clock_output_teardown(clock_out_channel_t channel_id)
{
clk_ll_bind_output_channel(0, channel_id);
}
@@ -0,0 +1,216 @@
/*
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_attr.h"
#include "hal/assert.h"
#include "soc/periph_defs.h"
#include "soc/pcr_reg.h"
#include "soc/soc.h"
#include "soc/reset_reasons.h"
#include "soc/pcr_struct.h"
#include "soc/gdma_struct.h"
#include "soc/parl_io_struct.h"
#include "soc/lpperi_struct.h"
#include "soc/usb_serial_jtag_struct.h"
#include "soc/lp_clkrst_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
static inline uint32_t periph_ll_get_clk_en_mask(shared_periph_module_t periph)
{
switch (periph) {
case PERIPH_TIMG0_MODULE:
return PCR_TG0_CLK_EN;
case PERIPH_TIMG1_MODULE:
return PCR_TG1_CLK_EN;
case PERIPH_UHCI0_MODULE:
return PCR_UHCI_CLK_EN;
case PERIPH_SYSTIMER_MODULE:
return PCR_SYSTIMER_CLK_EN;
default:
return 0;
}
}
static inline uint32_t periph_ll_get_rst_en_mask(shared_periph_module_t periph, bool enable)
{
(void)enable; // unused
switch (periph) {
case PERIPH_TIMG0_MODULE:
return PCR_TG0_RST_EN;
case PERIPH_TIMG1_MODULE:
return PCR_TG1_RST_EN;
case PERIPH_UHCI0_MODULE:
return PCR_UHCI_RST_EN;
case PERIPH_SYSTIMER_MODULE:
return PCR_SYSTIMER_RST_EN;
default:
return 0;
}
}
static inline uint32_t periph_ll_get_clk_en_reg(shared_periph_module_t periph)
{
switch (periph) {
case PERIPH_TIMG0_MODULE:
return PCR_TIMERGROUP0_CONF_REG;
case PERIPH_TIMG1_MODULE:
return PCR_TIMERGROUP1_CONF_REG;
case PERIPH_UHCI0_MODULE:
return PCR_UHCI_CONF_REG;
case PERIPH_SYSTIMER_MODULE:
return PCR_SYSTIMER_CONF_REG;
default:
return 0;
}
}
static inline uint32_t periph_ll_get_rst_en_reg(shared_periph_module_t periph)
{
switch (periph) {
case PERIPH_TIMG0_MODULE:
return PCR_TIMERGROUP0_CONF_REG;
case PERIPH_TIMG1_MODULE:
return PCR_TIMERGROUP1_CONF_REG;
case PERIPH_UHCI0_MODULE:
return PCR_UHCI_CONF_REG;
case PERIPH_SYSTIMER_MODULE:
return PCR_SYSTIMER_CONF_REG;
default:
return 0;
}
}
static inline void periph_ll_enable_clk_clear_rst(shared_periph_module_t periph)
{
SET_PERI_REG_MASK(periph_ll_get_clk_en_reg(periph), periph_ll_get_clk_en_mask(periph));
CLEAR_PERI_REG_MASK(periph_ll_get_rst_en_reg(periph), periph_ll_get_rst_en_mask(periph, true));
}
static inline void periph_ll_disable_clk_set_rst(shared_periph_module_t periph)
{
CLEAR_PERI_REG_MASK(periph_ll_get_clk_en_reg(periph), periph_ll_get_clk_en_mask(periph));
SET_PERI_REG_MASK(periph_ll_get_rst_en_reg(periph), periph_ll_get_rst_en_mask(periph, false));
}
static inline void periph_ll_reset(shared_periph_module_t periph)
{
SET_PERI_REG_MASK(periph_ll_get_rst_en_reg(periph), periph_ll_get_rst_en_mask(periph, false));
CLEAR_PERI_REG_MASK(periph_ll_get_rst_en_reg(periph), periph_ll_get_rst_en_mask(periph, false));
}
static inline bool periph_ll_periph_enabled(shared_periph_module_t periph)
{
return REG_GET_BIT(periph_ll_get_rst_en_reg(periph), periph_ll_get_rst_en_mask(periph, false)) == 0 &&
REG_GET_BIT(periph_ll_get_clk_en_reg(periph), periph_ll_get_clk_en_mask(periph)) != 0;
}
/**
* @brief Configuration structure for peripheral clock gate settings
*/
typedef struct {
bool disable_uart0_clk; ///< Disable UART0 clock
bool disable_uart1_clk; ///< Disable UART1 clock
bool disable_mspi_flash_clk; ///< Disable MSPI flash clock
bool disable_assist_clk; ///< Disable ASSIST Debug module clock
bool disable_crypto_periph_clk; ///< Disable crypto peripherals clock
bool disable_usb_serial_jtag; ///< Disable USB-Serial-JTAG clock
} periph_ll_clk_gate_config_t;
static inline void periph_ll_clk_gate_set_default(soc_reset_reason_t rst_reason, const periph_ll_clk_gate_config_t *config)
{
if ((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 (config->disable_uart0_clk) {
PCR.uart0_conf.uart0_clk_en = 0;
} else if (config->disable_uart1_clk) {
PCR.uart1_conf.uart1_clk_en = 0;
}
PCR.i2c[0].i2c_conf.i2c_clk_en = 0;
PCR.i2c[1].i2c_conf.i2c_clk_en = 0;
PCR.i2c[0].i2c_sclk_conf.i2c_sclk_en = 0;
PCR.i2c[1].i2c_sclk_conf.i2c_sclk_en = 0;
PCR.rmt_conf.rmt_clk_en = 0;
PCR.rmt_sclk_conf.rmt_sclk_en = 0;
PCR.ledc_sclk_conf.ledc_sclk_en = 0;
PCR.ledc_conf.ledc_clk_en = 0;
PCR.timergroup0_timer_clk_conf.tg0_timer_clk_en = 0;
PCR.timergroup1_timer_clk_conf.tg1_timer_clk_en = 0;
PCR.timergroup0_conf.tg0_clk_en = 0;
PCR.timergroup1_conf.tg1_clk_en = 0;
PCR.twai0_func_clk_conf.twai0_func_clk_en = 0;
PCR.twai0_conf.twai0_clk_en = 0;
PCR.i2s_conf.i2s_clk_en = 0;
PCR.i2s_tx_clkm_conf.i2s_tx_clkm_en = 0;
PCR.i2s_rx_clkm_conf.i2s_rx_clkm_en = 0;
PCR.pcnt_conf.pcnt_clk_en = 0;
PCR.etm_conf.etm_clk_en = 0;
PCR.pwm_conf.pwm_clk_en = 0;
PCR.pwm_clk_conf.pwm_clkm_en = 0;
PCR.parl_io_conf.parl_clk_en = 0;
PCR.gdma_conf.gdma_clk_en = 0;
if (config->disable_mspi_flash_clk) {
PCR.mspi_conf.mspi_clk_en = 0;
}
PCR.spi2_conf.spi2_clk_en = 0;
PCR.tsens_clk_conf.tsens_clk_en = 0;
PCR.uhci_conf.uhci_clk_en = 0;
PCR.saradc_conf.saradc_clk_en = 0;
if (config->disable_assist_clk) {
/* Disable ASSIST Debug module clock if PC recoreding function is not used,
* if stack guard function needs it, it will be re-enabled at esp_hw_stack_guard_init */
PCR.assist_conf.assist_clk_en = 0;
}
if (config->disable_crypto_periph_clk) {
// NOTE: [ESP-TEE] The TEE is responsible for the AES and SHA peripherals
PCR.aes_conf.aes_clk_en = 0;
PCR.sha_conf.sha_clk_en = 0;
PCR.ecc_conf.ecc_clk_en = 0;
PCR.hmac_conf.hmac_clk_en = 0;
PCR.ds_conf.ds_clk_en = 0;
PCR.rsa_conf.rsa_clk_en = 0;
PCR.ecdsa_conf.ecdsa_clk_en = 0;
}
PCR.ctrl_tick_conf.tick_enable = 0;
PCR.trace_conf.trace_clk_en = 0;
PCR.mem_monitor_conf.mem_monitor_clk_en = 0;
PCR.pvt_monitor_conf.pvt_monitor_clk_en = 0;
PCR.pvt_monitor_func_clk_conf.pvt_monitor_func_clk_en = 0;
PCR.ctrl_clk_out_en.val = 0;
if (config->disable_usb_serial_jtag) {
// Disable USB-Serial-JTAG clock and it's pad if not used
USB_SERIAL_JTAG.conf0.usb_pad_enable = 0;
PCR.usb_device_conf.usb_device_clk_en = 0;
}
}
if ((rst_reason == RESET_REASON_CHIP_POWER_ON) || (rst_reason == RESET_REASON_CHIP_BROWN_OUT) \
|| (rst_reason == RESET_REASON_SYS_RTC_WDT) || (rst_reason == RESET_REASON_SYS_SUPER_WDT)) {
LPPERI_REG_SET(clk_en.val, 0);
LPPERI_REG_SET(clk_en.efuse_ck_en, 1); // keep efuse clock enabled
LP_CLKRST.lp_clk_po_en.val = 0;
}
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,807 @@
/*
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "soc/soc.h"
#include "soc/clk_tree_defs.h"
#include "soc/pcr_struct.h"
#include "soc/lp_clkrst_struct.h"
#include "soc/pmu_reg.h"
#include "hal/regi2c_ctrl.h"
#include "soc/regi2c_bbpll.h"
#include "soc/regi2c_pmu.h"
#include "soc/timer_group_struct.h"
#include "soc/io_mux_reg.h"
#include "hal/clkout_channel.h"
#include "hal/assert.h"
#include "hal/log.h"
#include "esp32h2/rom/rtc.h"
#include "hal/misc.h"
#define MHZ (1000000)
#define CLK_LL_PLL_8M_FREQ_MHZ (8)
#define CLK_LL_PLL_48M_FREQ_MHZ (48)
#define CLK_LL_PLL_64M_FREQ_MHZ (64)
#define CLK_LL_PLL_96M_FREQ_MHZ (96)
#define CLK_LL_XTAL32K_CONFIG_DEFAULT() { \
.dac = 3, \
.dres = 3, \
.dgm = 3, \
.dbuf = 1, \
}
/*
Set the frequency division factor of ref_tick
The FOSC of rtc calibration uses the 32 frequency division clock for ECO2,
So the frequency division factor of ref_tick must be greater than or equal to 32
*/
#define CLK_LL_RC_FAST_CALIB_TICK_DIV_BITS 5
#define REG_FOSC_TICK_NUM 255
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief XTAL32K_CLK enable modes
*/
typedef enum {
CLK_LL_XTAL32K_ENABLE_MODE_CRYSTAL, //!< Enable the external 32kHz crystal for XTAL32K_CLK
CLK_LL_XTAL32K_ENABLE_MODE_EXTERNAL, //!< Enable the external clock signal for XTAL32K_CLK
CLK_LL_XTAL32K_ENABLE_MODE_BOOTSTRAP, //!< Bootstrap the crystal oscillator for faster XTAL32K_CLK start up */
} clk_ll_xtal32k_enable_mode_t;
/**
* @brief XTAL32K_CLK configuration structure
*/
typedef struct {
uint32_t dac : 6;
uint32_t dres : 3;
uint32_t dgm : 3;
uint32_t dbuf: 1;
} clk_ll_xtal32k_config_t;
/**
* @brief Power up BBPLL circuit
*/
static inline __attribute__((always_inline)) void clk_ll_bbpll_enable(void)
{
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XPD_BB_I2C |
PMU_TIE_HIGH_XPD_BBPLL | PMU_TIE_HIGH_XPD_BBPLL_I2C);
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_GLOBAL_BBPLL_ICG);
}
/**
* @brief Power down BBPLL circuit
*/
static inline __attribute__((always_inline)) void clk_ll_bbpll_disable(void)
{
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_GLOBAL_BBPLL_ICG) ;
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_XPD_BBPLL | PMU_TIE_LOW_XPD_BBPLL_I2C);
}
/**
* @brief Enable the internal oscillator output for LP_PLL_CLK
*/
static inline __attribute__((always_inline)) void clk_ll_lp_pll_enable(void)
{
// Enable lp_pll xpd status
SET_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_LPPLL);
}
/**
* @brief Disable the internal oscillator output for LP_PLL_CLK
*/
static inline __attribute__((always_inline)) void clk_ll_lp_pll_disable(void)
{
// Disable lp_pll xpd status
CLEAR_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_LPPLL);
}
/**
* @brief Enable the 32kHz crystal oscillator
*
* @param mode Used to determine the xtal32k configuration parameters
*/
static inline __attribute__((always_inline)) void clk_ll_xtal32k_enable(clk_ll_xtal32k_enable_mode_t mode)
{
if (mode == CLK_LL_XTAL32K_ENABLE_MODE_EXTERNAL) {
// No need to configure anything for OSC_SLOW_CLK
return;
}
// Configure xtal32k
clk_ll_xtal32k_config_t cfg = CLK_LL_XTAL32K_CONFIG_DEFAULT();
LP_CLKRST.xtal32k.dac_xtal32k = cfg.dac;
LP_CLKRST.xtal32k.dres_xtal32k = cfg.dres;
LP_CLKRST.xtal32k.dgm_xtal32k = cfg.dgm;
LP_CLKRST.xtal32k.dbuf_xtal32k = cfg.dbuf;
// Enable xtal32k xpd
SET_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_XTAL32K);
}
/**
* @brief Disable the 32kHz crystal oscillator
*/
static inline __attribute__((always_inline)) void clk_ll_xtal32k_disable(void)
{
// Disable xtal32k xpd
CLEAR_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_XTAL32K);
}
/**
* @brief Get the state of the 32kHz crystal clock
*
* @return True if the 32kHz XTAL is enabled
*/
static inline __attribute__((always_inline)) bool clk_ll_xtal32k_is_enabled(void)
{
return REG_GET_FIELD(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_XTAL32K) == 1;
}
/**
* @brief Enable the internal oscillator output for RC32K_CLK
*/
static inline __attribute__((always_inline)) void clk_ll_rc32k_enable(void)
{
// Enable rc32k xpd status
SET_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_RC32K);
}
/**
* @brief Disable the internal oscillator output for RC32K_CLK
*/
static inline __attribute__((always_inline)) void clk_ll_rc32k_disable(void)
{
// Disable rc32k xpd status
CLEAR_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_RC32K);
}
/**
* @brief Get the state of the internal oscillator for RC32K_CLK
*
* @return True if the oscillator is enabled
*/
static inline __attribute__((always_inline)) bool clk_ll_rc32k_is_enabled(void)
{
return REG_GET_FIELD(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_RC32K) == 1;
}
/**
* @brief Enable the internal oscillator output for RC_FAST_CLK
*/
static inline __attribute__((always_inline)) void clk_ll_rc_fast_enable(void)
{
SET_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_FOSC_CLK);
}
/**
* @brief Disable the internal oscillator output for RC_FAST_CLK
*/
static inline __attribute__((always_inline)) void clk_ll_rc_fast_disable(void)
{
CLEAR_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_FOSC_CLK);
}
/**
* @brief Get the state of the internal oscillator for RC_FAST_CLK
*
* @return True if the oscillator is enabled
*/
static inline __attribute__((always_inline)) bool clk_ll_rc_fast_is_enabled(void)
{
return REG_GET_FIELD(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_FOSC_CLK) == 1;
}
/**
* @brief Enable the digital RC_FAST_CLK, which is used to support peripherals.
*/
static inline __attribute__((always_inline)) void clk_ll_rc_fast_digi_enable(void)
{
LP_CLKRST.clk_to_hp.icg_hp_fosc = 1;
}
/**
* @brief Disable the digital RC_FAST_CLK, which is used to support peripherals.
*/
static inline __attribute__((always_inline)) void clk_ll_rc_fast_digi_disable(void)
{
LP_CLKRST.clk_to_hp.icg_hp_fosc = 0;
}
/**
* @brief Get the state of the digital RC_FAST_CLK
*
* @return True if the digital RC_FAST_CLK is enabled
*/
static inline __attribute__((always_inline)) bool clk_ll_rc_fast_digi_is_enabled(void)
{
return LP_CLKRST.clk_to_hp.icg_hp_fosc;
}
/**
* @brief Enable the digital XTAL32K_CLK, which is used to support peripherals.
*/
static inline __attribute__((always_inline)) void clk_ll_xtal32k_digi_enable(void)
{
LP_CLKRST.clk_to_hp.icg_hp_xtal32k = 1;
}
/**
* @brief Disable the digital XTAL32K_CLK, which is used to support peripherals.
*/
static inline __attribute__((always_inline)) void clk_ll_xtal32k_digi_disable(void)
{
LP_CLKRST.clk_to_hp.icg_hp_xtal32k = 0;
}
/**
* @brief Get the state of the digital XTAL32K_CLK
*
* @return True if the digital XTAL32K_CLK is enabled
*/
static inline __attribute__((always_inline)) bool clk_ll_xtal32k_digi_is_enabled(void)
{
return LP_CLKRST.clk_to_hp.icg_hp_xtal32k;
}
/**
* @brief Enable the digital RC32K_CLK, which is used to support peripherals.
*/
static inline __attribute__((always_inline)) void clk_ll_rc32k_digi_enable(void)
{
LP_CLKRST.clk_to_hp.icg_hp_osc32k = 1;
}
/**
* @brief Disable the digital RC32K_CLK, which is used to support peripherals.
*/
static inline __attribute__((always_inline)) void clk_ll_rc32k_digi_disable(void)
{
LP_CLKRST.clk_to_hp.icg_hp_osc32k = 0;
}
/**
* @brief Get the state of the digital RC32K_CLK
*
* @return True if the digital RC32K_CLK is enabled
*/
static inline __attribute__((always_inline)) bool clk_ll_rc32k_digi_is_enabled(void)
{
return LP_CLKRST.clk_to_hp.icg_hp_osc32k;
}
/**
* @brief Get PLL_CLK frequency
*
* @return PLL clock frequency, in MHz. Returns 0 if register field value is invalid.
*/
static inline __attribute__((always_inline)) uint32_t clk_ll_bbpll_get_freq_mhz(void)
{
// The target has a fixed 96MHz SPLL
return CLK_LL_PLL_96M_FREQ_MHZ;
}
/**
* @brief Set BBPLL frequency from XTAL source (Digital part)
*
* @param pll_freq_mhz PLL frequency, in MHz
*/
static inline __attribute__((always_inline)) void clk_ll_bbpll_set_freq_mhz(uint32_t pll_freq_mhz)
{
// The target SPLL is fixed to 96MHz
// Do nothing
HAL_ASSERT(pll_freq_mhz == CLK_LL_PLL_96M_FREQ_MHZ);
}
/**
* @brief Set BBPLL frequency from XTAL source (Analog part)
*
* @param pll_freq_mhz PLL frequency, in MHz
* @param xtal_freq_mhz XTAL frequency, in MHz
*/
static inline __attribute__((always_inline)) void clk_ll_bbpll_set_config(uint32_t pll_freq_mhz, uint32_t xtal_freq_mhz)
{
HAL_ASSERT(xtal_freq_mhz == SOC_XTAL_FREQ_32M);
HAL_ASSERT(pll_freq_mhz == CLK_LL_PLL_96M_FREQ_MHZ);
uint8_t oc_ref_div;
uint8_t oc_div;
uint8_t oc_dhref_sel;
uint8_t oc_dlref_sel;
oc_ref_div = 0;
oc_div = 1;
oc_dhref_sel = 3;
oc_dlref_sel = 1;
REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, oc_ref_div);
REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DIV, oc_div);
REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DHREF_SEL, oc_dhref_sel);
REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DLREF_SEL, oc_dlref_sel);
}
/**
* @brief Get FLASH_PLL_CLK frequency
*
* @return FLASH_PLL clock frequency, in MHz
*/
static inline __attribute__((always_inline)) uint32_t clk_ll_flash_pll_get_freq_mhz(void)
{
// The target has a fixed 64MHz flash PLL, which is directly derived from BBPLL
return CLK_LL_PLL_64M_FREQ_MHZ;
}
/**
* @brief To enable the change of soc_clk_sel, cpu_div_num, and ahb_div_num
*/
static inline __attribute__((always_inline)) void clk_ll_bus_update(void)
{
PCR.bus_clk_update.bus_clock_update = 1;
while (PCR.bus_clk_update.bus_clock_update);
}
/**
* @brief Select the clock source for CPU_CLK (SOC Clock Root)
*
* @param in_sel One of the clock sources in soc_cpu_clk_src_t
*/
static inline __attribute__((always_inline)) void clk_ll_cpu_set_src(soc_cpu_clk_src_t in_sel)
{
switch (in_sel) {
case SOC_CPU_CLK_SRC_XTAL:
PCR.sysclk_conf.soc_clk_sel = 0;
break;
case SOC_CPU_CLK_SRC_PLL:
PCR.sysclk_conf.soc_clk_sel = 1;
break;
case SOC_CPU_CLK_SRC_RC_FAST:
PCR.sysclk_conf.soc_clk_sel = 2;
break;
case SOC_CPU_CLK_SRC_FLASH_PLL:
PCR.sysclk_conf.soc_clk_sel = 3;
break;
default:
// Unsupported CPU_CLK mux input sel
abort();
}
}
/**
* @brief Get the clock source for CPU_CLK (SOC Clock Root)
*
* @return Currently selected clock source (one of soc_cpu_clk_src_t values)
*/
static inline __attribute__((always_inline)) soc_cpu_clk_src_t clk_ll_cpu_get_src(void)
{
uint32_t clk_sel = PCR.sysclk_conf.soc_clk_sel;
switch (clk_sel) {
case 0:
return SOC_CPU_CLK_SRC_XTAL;
case 1:
return SOC_CPU_CLK_SRC_PLL;
case 2:
return SOC_CPU_CLK_SRC_RC_FAST;
case 3:
return SOC_CPU_CLK_SRC_FLASH_PLL;
default:
// Invalid SOC_CLK_SEL value
return SOC_CPU_CLK_SRC_INVALID;
}
}
/**
* @brief Set CPU_CLK divider
*
* @param divider Divider. PRE_DIV_CNT = divider - 1.
*/
static inline __attribute__((always_inline)) void clk_ll_cpu_set_divider(uint32_t divider)
{
HAL_ASSERT(divider >= 1);
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.cpu_freq_conf, cpu_div_num, divider - 1);
}
/**
* @brief Get CPU_CLK divider
*
* @return Divider. Divider = (PRE_DIV_CNT + 1).
*/
static inline __attribute__((always_inline)) uint32_t clk_ll_cpu_get_divider(void)
{
return HAL_FORCE_READ_U32_REG_FIELD(PCR.cpu_freq_conf, cpu_div_num) + 1;
}
/**
* @brief Set AHB_CLK divider
*
* @param divider Divider. PRE_DIV_CNT = divider - 1.
*/
static inline __attribute__((always_inline)) void clk_ll_ahb_set_divider(uint32_t divider)
{
HAL_ASSERT(divider >= 1);
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.ahb_freq_conf, ahb_div_num, divider - 1);
}
/**
* @brief Get AHB_CLK divider
*
* @return Divider. Divider = (PRE_DIV_CNT + 1).
*/
static inline __attribute__((always_inline)) uint32_t clk_ll_ahb_get_divider(void)
{
return HAL_FORCE_READ_U32_REG_FIELD(PCR.ahb_freq_conf, ahb_div_num) + 1;
}
/**
* @brief Set APB_CLK divider. freq of APB_CLK = freq of AHB_CLK / divider
*
* @param divider Divider. PCR_APB_DIV_NUM = divider - 1.
*/
static inline __attribute__((always_inline)) void clk_ll_apb_set_divider(uint32_t divider)
{
// AHB ------> APB
// Divider option: 1, 2, 4 (PCR_APB_DIV_NUM=0, 1, 3)
HAL_ASSERT(divider == 1 || divider == 2 || divider == 4);
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.apb_freq_conf, apb_div_num, divider - 1);
}
/**
* @brief Get APB_CLK divider
*
* @return Divider. Divider = (PCR_APB_DIV_NUM + 1).
*/
static inline __attribute__((always_inline)) uint32_t clk_ll_apb_get_divider(void)
{
return HAL_FORCE_READ_U32_REG_FIELD(PCR.apb_freq_conf, apb_div_num) + 1;
}
/**
* @brief Select the frequency calculation clock source for timergroup0
*
* @param clk_sel One of the clock sources in soc_clk_freq_calculation_src_t
*/
static inline __attribute__((always_inline)) void clk_ll_freq_calulation_set_target(soc_clk_freq_calculation_src_t clk_sel)
{
int timg_cali_clk_sel = -1;
int clk_32k_sel = -1;
switch (clk_sel) {
case CLK_CAL_RC32K:
timg_cali_clk_sel = 2;
clk_32k_sel = 0;
break;
case CLK_CAL_32K_XTAL:
timg_cali_clk_sel = 2;
clk_32k_sel = 1;
break;
case CLK_CAL_32K_OSC_SLOW:
timg_cali_clk_sel = 2;
clk_32k_sel = 2;
break;
case CLK_CAL_RC_SLOW:
timg_cali_clk_sel = 0;
break;
case CLK_CAL_RC_FAST:
timg_cali_clk_sel = 1;
break;
default:
// Unsupported CLK_CAL mux input
abort();
}
if (timg_cali_clk_sel >= 0) {
TIMERG0.rtccalicfg.rtc_cali_clk_sel = timg_cali_clk_sel;
}
if (clk_32k_sel >= 0) {
PCR.ctrl_32k_conf.clk_32k_sel = clk_32k_sel;
}
}
/**
* @brief Set the frequency division factor of RC_FAST clock
*/
static inline __attribute__((always_inline)) void clk_ll_rc_fast_tick_conf(void)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.ctrl_tick_conf, fosc_tick_num, REG_FOSC_TICK_NUM); // enable a division of 32 to the fosc clock
}
/**
* @brief Select the clock source for RTC_SLOW_CLK
*
* @param in_sel One of the clock sources in soc_rtc_slow_clk_src_t
*/
static inline __attribute__((always_inline)) void clk_ll_rtc_slow_set_src(soc_rtc_slow_clk_src_t in_sel)
{
switch (in_sel) {
case SOC_RTC_SLOW_CLK_SRC_RC_SLOW:
LP_CLKRST.lp_clk_conf.slow_clk_sel = 0;
break;
case SOC_RTC_SLOW_CLK_SRC_XTAL32K:
LP_CLKRST.lp_clk_conf.slow_clk_sel = 1;
break;
case SOC_RTC_SLOW_CLK_SRC_RC32K:
LP_CLKRST.lp_clk_conf.slow_clk_sel = 2;
break;
case SOC_RTC_SLOW_CLK_SRC_OSC_SLOW:
LP_CLKRST.lp_clk_conf.slow_clk_sel = 3;
break;
default:
// Unsupported RTC_SLOW_CLK mux input sel
abort();
}
}
/**
* @brief Get the clock source for RTC_SLOW_CLK
*
* @return Currently selected clock source (one of soc_rtc_slow_clk_src_t values)
*/
static inline __attribute__((always_inline)) soc_rtc_slow_clk_src_t clk_ll_rtc_slow_get_src(void)
{
uint32_t clk_sel = LP_CLKRST.lp_clk_conf.slow_clk_sel;
switch (clk_sel) {
case 0:
return SOC_RTC_SLOW_CLK_SRC_RC_SLOW;
case 1:
return SOC_RTC_SLOW_CLK_SRC_XTAL32K;
case 2:
return SOC_RTC_SLOW_CLK_SRC_RC32K;
case 3:
return SOC_RTC_SLOW_CLK_SRC_OSC_SLOW;
default:
return SOC_RTC_SLOW_CLK_SRC_INVALID;
}
}
/**
* @brief Select the clock source for LP_PLL_CLK
*
* @param in_sel One of the clock sources in soc_lp_pll_clk_src_t
*/
static inline __attribute__((always_inline)) void clk_ll_lp_pll_set_src(soc_lp_pll_clk_src_t in_sel)
{
uint32_t field_value;
switch (in_sel) {
case SOC_LP_PLL_CLK_SRC_RC32K:
field_value = 0;
break;
case SOC_LP_PLL_CLK_SRC_XTAL32K:
field_value = 1;
break;
default:
// Unsupported LP_PLL_CLK mux input sel
abort();
}
REGI2C_WRITE_MASK(I2C_PMU, I2C_PMU_SEL_PLL8M_REF, field_value);
}
/**
* @brief Get the clock source for LP_PLL_CLK
*
* @return Currently selected clock source (one of soc_lp_pll_clk_src_t values)
*/
static inline __attribute__((always_inline)) soc_lp_pll_clk_src_t clk_ll_lp_pll_get_src(void)
{
uint32_t clk_sel = REGI2C_READ_MASK(I2C_PMU, I2C_PMU_SEL_PLL8M_REF);
switch (clk_sel) {
case 0:
return SOC_LP_PLL_CLK_SRC_RC32K;
case 1:
return SOC_LP_PLL_CLK_SRC_XTAL32K;
default:
return SOC_LP_PLL_CLK_SRC_INVALID;
}
}
/**
* @brief Get LP_PLL_CLK frequency
*
* @return LP_PLL clock frequency, in MHz
*/
static inline __attribute__((always_inline)) uint32_t clk_ll_lp_pll_get_freq_mhz(void)
{
// The target has a fixed 8MHz LP_PLL
return CLK_LL_PLL_8M_FREQ_MHZ;
}
/**
* @brief Select the clock source for RTC_FAST_CLK
*
* @param in_sel One of the clock sources in soc_rtc_fast_clk_src_t
*/
static inline __attribute__((always_inline)) void clk_ll_rtc_fast_set_src(soc_rtc_fast_clk_src_t in_sel)
{
switch (in_sel) {
case SOC_RTC_FAST_CLK_SRC_RC_FAST:
LP_CLKRST.lp_clk_conf.fast_clk_sel = 0;
break;
case SOC_RTC_FAST_CLK_SRC_XTAL_D2:
LP_CLKRST.lp_clk_conf.fast_clk_sel = 1;
break;
case SOC_RTC_FAST_CLK_SRC_LP_PLL:
LP_CLKRST.lp_clk_conf.fast_clk_sel = 2;
break;
default:
// Unsupported RTC_FAST_CLK mux input sel
abort();
}
}
/**
* @brief Get the clock source for RTC_FAST_CLK
*
* @return Currently selected clock source (one of soc_rtc_fast_clk_src_t values)
*/
static inline __attribute__((always_inline)) soc_rtc_fast_clk_src_t clk_ll_rtc_fast_get_src(void)
{
uint32_t clk_sel = LP_CLKRST.lp_clk_conf.fast_clk_sel;
switch (clk_sel) {
case 0:
return SOC_RTC_FAST_CLK_SRC_RC_FAST;
case 1:
return SOC_RTC_FAST_CLK_SRC_XTAL_D2;
case 2:
return SOC_RTC_FAST_CLK_SRC_LP_PLL;
default:
return SOC_RTC_FAST_CLK_SRC_INVALID;
}
}
/**
* @brief Set RC_FAST_CLK divider. The output from the divider is passed into rtc_fast_clk MUX.
*
* @param divider Divider of RC_FAST_CLK. Usually this divider is set to 1 (reg. value is 0) in bootloader stage.
*/
static inline __attribute__((always_inline)) void clk_ll_rc_fast_set_divider(uint32_t divider)
{
// No divider on the target
HAL_ASSERT(divider == 1);
}
/**
* @brief Get RC_FAST_CLK divider
*
* @return Divider
*/
static inline __attribute__((always_inline)) uint32_t clk_ll_rc_fast_get_divider(void)
{
// No divider on the target, always return divider = 1
return 1;
}
/**
* @brief Set RC_SLOW_CLK divider
*
* @param divider Divider of RC_SLOW_CLK. Usually this divider is set to 1 (reg. value is 0) in bootloader stage.
*/
static inline __attribute__((always_inline)) void clk_ll_rc_slow_set_divider(uint32_t divider)
{
// No divider on the target
HAL_ASSERT(divider == 1);
}
/************************** CLOCK OUTPUT **************************/
/**
* @brief Clock output channel configuration
*
* @param clk_sig The clock signal source to be mapped to GPIOs
* @param channel_id The clock output channel ID
*/
static inline __attribute__((always_inline)) void clk_ll_bind_output_channel(soc_clkout_sig_id_t clk_sig, clock_out_channel_t channel_id)
{
SET_PERI_REG_BITS(PIN_CTRL, CLKOUT_CHANNEL_MASK(channel_id), clk_sig, CLKOUT_CHANNEL_SHIFT(channel_id));
}
/**
* @brief Enable the clock output channel
*
* @param channel_id The clock output channel ID
* @param enable Enable or disable the clock output channel
*/
static inline __attribute__((always_inline)) void clk_ll_enable_output_channel(clock_out_channel_t channel_id, bool enable)
{
// No such gating on the target
(void)channel_id;
(void)enable;
}
/**
* @brief Output the mapped clock after frequency division
*
* @param channel_id The clock output channel ID
* @param divider Clock frequency division value
*/
static inline __attribute__((always_inline)) void clk_ll_set_output_channel_divider(clock_out_channel_t channel_id, uint32_t divider)
{
// No divider on the target
HAL_ASSERT(divider == 1);
}
/**
* @brief Enable/Disable the clock gate for clock output signal source
*
* @param clk_src The clock output signal source
* @param en Enable or disable the clock output signal source
*/
static inline void clk_ll_enable_clkout_source(soc_clkout_sig_id_t clk_src, bool en)
{
if (clk_src == CLKOUT_SIG_XTAL) {
PCR.ctrl_clk_out_en.clk_xtal_oen = en;
}
}
/************************** LP STORAGE REGISTER STORE/LOAD **************************/
/**
* @brief Store XTAL_CLK frequency in RTC storage register
*
* Value of RTC_XTAL_FREQ_REG is stored as two copies in lower and upper 16-bit
* halves. These are the routines to work with that representation.
*
* @param xtal_freq_mhz XTAL frequency, in MHz. The frequency must necessarily be even,
* otherwise there will be a conflict with the low bit, which is used to disable logs
* in the ROM code.
*/
static inline __attribute__((always_inline)) void clk_ll_xtal_store_freq_mhz(uint32_t xtal_freq_mhz)
{
// Read the status of whether disabling logging from ROM code
uint32_t reg = READ_PERI_REG(RTC_XTAL_FREQ_REG) & RTC_DISABLE_ROM_LOG;
// If so, need to write back this setting
if (reg == RTC_DISABLE_ROM_LOG) {
xtal_freq_mhz |= 1;
}
WRITE_PERI_REG(RTC_XTAL_FREQ_REG, (xtal_freq_mhz & UINT16_MAX) | ((xtal_freq_mhz & UINT16_MAX) << 16));
}
/**
* @brief Load XTAL_CLK frequency from RTC storage register
*
* Value of RTC_XTAL_FREQ_REG is stored as two copies in lower and upper 16-bit
* halves. These are the routines to work with that representation.
*
* @return XTAL frequency, in MHz. Returns 0 if value in reg is invalid.
*/
static inline __attribute__((always_inline)) uint32_t clk_ll_xtal_load_freq_mhz(void)
{
// Read from RTC storage register
uint32_t xtal_freq_reg = READ_PERI_REG(RTC_XTAL_FREQ_REG);
if ((xtal_freq_reg & 0xFFFF) == ((xtal_freq_reg >> 16) & 0xFFFF) &&
xtal_freq_reg != 0 && xtal_freq_reg != UINT32_MAX) {
return xtal_freq_reg & ~RTC_DISABLE_ROM_LOG & UINT16_MAX;
}
// If the format in reg is invalid
return 0;
}
/**
* @brief Store RTC_SLOW_CLK calibration value in RTC storage register
*
* Value of RTC_SLOW_CLK_CAL_REG has to be in the same format as returned by rtc_clk_cal (microseconds,
* in Q13.19 fixed-point format).
*
* @param cal_value The calibration value of slow clock period in microseconds, in Q13.19 fixed point format
*/
static inline __attribute__((always_inline)) void clk_ll_rtc_slow_store_cal(uint32_t cal_value)
{
REG_WRITE(RTC_SLOW_CLK_CAL_REG, cal_value);
}
/**
* @brief Load the calibration value of RTC_SLOW_CLK frequency from RTC storage register
*
* This value gets updated (i.e. rtc slow clock gets calibrated) every time RTC_SLOW_CLK source switches
*
* @return The calibration value of slow clock period in microseconds, in Q13.19 fixed point format
*/
static inline __attribute__((always_inline)) uint32_t clk_ll_rtc_slow_load_cal(void)
{
return REG_READ(RTC_SLOW_CLK_CAL_REG);
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,41 @@
/*
* SPDX-FileCopyrightText: 2010-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_assert.h"
#include "soc/soc_caps.h"
#include "soc/io_mux_reg.h"
#include "soc/gpio_sig_map.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum clock_out_channel {
CLKOUT_CHANNEL_1,
CLKOUT_CHANNEL_2,
CLKOUT_CHANNEL_3,
CLKOUT_CHANNEL_MAX,
} clock_out_channel_t;
#define CLKOUT_CHANNEL_TO_GPIO_SIG_ID(channel) ((channel == CLKOUT_CHANNEL_1) ? CLK_OUT_OUT1_IDX : \
(channel == CLKOUT_CHANNEL_2) ? CLK_OUT_OUT2_IDX : \
(channel == CLKOUT_CHANNEL_3) ? CLK_OUT_OUT3_IDX : SIG_GPIO_OUT_IDX)
#define CLKOUT_CHANNEL_MASK(channel) ((channel == CLKOUT_CHANNEL_1) ? CLK_OUT1 : \
(channel == CLKOUT_CHANNEL_2) ? CLK_OUT2 : \
(channel == CLKOUT_CHANNEL_3) ? CLK_OUT3 : 0)
#define CLKOUT_CHANNEL_SHIFT(channel) ((channel == CLKOUT_CHANNEL_1) ? CLK_OUT1_S : \
(channel == CLKOUT_CHANNEL_2) ? CLK_OUT2_S : \
(channel == CLKOUT_CHANNEL_3) ? CLK_OUT3_S : 0)
ESP_STATIC_ASSERT(CLKOUT_CHANNEL_MAX == SOC_GPIO_CLOCKOUT_CHANNEL_NUM, "clock_out_channel enumeration mismatch");
#ifdef __cplusplus
}
#endif