#![cfg_attr(docsrs, procmacros::doc_replace)]
#![cfg_attr(not(esp32h2), doc = "* 80MHz")]
#![cfg_attr(esp32h2, doc = "* 96MHz")]
#![cfg_attr(esp32c2, doc = "* 120MHz")]
#![cfg_attr(not(any(esp32c2, esp32h2)), doc = "* 160MHz")]
#![cfg_attr(any(esp32c5, xtensa), doc = "* 240MHz")]
#![cfg_attr(not(feature = "rt"), expect(unused))]
#[cfg(soc_has_clock_node_lp_slow_clk)]
use clocks::LpSlowClkConfig;
#[cfg(all(not(esp32s2), soc_has_clock_node_rtc_slow_clk))]
use clocks::RtcSlowClkConfig;
#[cfg(soc_has_clock_node_timg_function_clock)]
use clocks::TimgFunctionClockConfig;
#[doc = ""]
#[instability::unstable]
pub mod ll {
#[instability::unstable]
pub use crate::soc::clocks::*;
}
#[cfg(timergroup_rc_fast_calibration_divider)]
use crate::efuse::ChipRevision;
#[cfg(all(
soc_has_clock_node_timg_calibration_clock,
timergroup_rc_fast_calibration_tick_enable
))]
use crate::peripherals::PCR;
#[instability::unstable]
pub use crate::soc::clocks::ClockConfig;
pub use crate::soc::clocks::CpuClock;
use crate::{ESP_HAL_LOCK, soc::clocks, time::Rate};
#[cfg(soc_has_clock_node_timg_calibration_clock)]
use crate::{peripherals::TIMG0, soc::clocks::ClockTree};
impl CpuClock {
#[procmacros::doc_replace]
pub const fn max() -> Self {
cfg_if::cfg_if! {
if #[cfg(esp32c2)] {
Self::_120MHz
} else if #[cfg(any(esp32c3, esp32c6, esp32c61))] {
Self::_160MHz
} else if #[cfg(esp32h2)] {
Self::_96MHz
} else {
Self::_240MHz
}
}
}
}
#[instability::unstable]
pub struct RtcClock;
#[cfg(soc_has_clock_node_timg_calibration_clock)]
use crate::soc::clocks::TimgCalibrationClockConfig;
impl RtcClock {
const CAL_FRACT: u32 = 19;
#[instability::unstable]
#[cfg(any(soc_has_clock_node_lp_slow_clk, soc_has_clock_node_rtc_slow_clk))]
pub fn slow_freq() -> Rate {
cfg_if::cfg_if! {
if #[cfg(soc_has_clock_node_rtc_slow_clk)] {
let getter = clocks::rtc_slow_clk_frequency;
} else {
let getter = clocks::lp_slow_clk_frequency;
}
}
Rate::from_hz(ClockTree::with(getter))
}
#[cfg(soc_has_clock_node_timg_calibration_clock)]
pub(crate) fn calibrate(cal_clk: TimgCalibrationClockConfig, slowclk_cycles: u32) -> u32 {
ClockTree::with(|clocks| {
let xtal_freq = Rate::from_hz(clocks::xtal_clk_frequency(clocks));
let (xtal_cycles, _) = Clocks::measure_rtc_clock(
clocks,
cal_clk,
#[cfg(soc_has_clock_node_timg_function_clock)]
TimgFunctionClockConfig::XtalClk,
slowclk_cycles,
);
if xtal_cycles == 0 {
warn!("{:?} calibration failed", cal_clk);
} else {
debug!("Counted {} XTAL cycles", xtal_cycles);
debug!(
"{:?} frequency: {}",
cal_clk,
(xtal_freq / xtal_cycles) * slowclk_cycles
);
}
let divider = xtal_freq.as_mhz() as u64 * slowclk_cycles as u64;
let period_64 =
(((xtal_cycles as u64) << RtcClock::CAL_FRACT) + divider / 2u64 - 1u64) / divider;
(period_64 & u32::MAX as u64) as u32
})
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
#[doc(hidden)]
#[instability::unstable]
pub struct Clocks {
#[cfg(soc_has_clock_node_cpu_clk)]
pub cpu_clock: Rate,
#[cfg(soc_has_clock_node_apb_clk)]
pub apb_clock: Rate,
#[cfg(soc_has_clock_node_xtal_clk)]
pub xtal_clock: Rate,
}
static mut ACTIVE_CLOCKS: Option<Clocks> = None;
impl Clocks {
pub(crate) fn init(cpu_clock_config: ClockConfig) {
ESP_HAL_LOCK.lock(|| {
crate::rtc_cntl::rtc::init();
let config = Self::configure(cpu_clock_config);
unsafe { ACTIVE_CLOCKS = Some(config) };
});
Clocks::calibrate_rtc_slow_clock();
}
fn try_get<'a>() -> Option<&'a Clocks> {
unsafe {
let clocks = &*core::ptr::addr_of!(ACTIVE_CLOCKS);
clocks.as_ref()
}
}
pub fn get<'a>() -> &'a Clocks {
unwrap!(Self::try_get())
}
}
impl Clocks {
pub(crate) fn configure(clock_config: ClockConfig) -> Self {
use crate::soc::clocks::ClockTree;
clock_config.configure();
ClockTree::with(|clocks| {
#[cfg(soc_has_clock_node_mcpwm_function_clock)]
{
clocks::McpwmInstance::Mcpwm0.configure_function_clock(clocks, Default::default());
#[cfg(soc_has_mcpwm1)]
clocks::McpwmInstance::Mcpwm1.configure_function_clock(clocks, Default::default());
}
#[cfg(soc_has_clock_node_rc_fast_clk)]
clocks::request_rc_fast_clk(clocks);
#[cfg(soc_has_clock_node_rc_slow_clk)]
clocks::request_rc_slow_clk(clocks);
#[cfg(soc_has_clock_node_pll_clk)]
clocks::request_pll_clk(clocks);
#[cfg(soc_has_clock_node_pll_f96m_clk)]
clocks::request_pll_f96m_clk(clocks);
#[cfg(soc_has_clock_node_pll_f240m)]
clocks::request_pll_f240m(clocks);
#[cfg(soc_has_clock_node_rc_fast_div_clk)]
clocks::request_rc_fast_div_clk(clocks);
Self {
#[cfg(soc_has_clock_node_cpu_clk)]
cpu_clock: Rate::from_hz(clocks::cpu_clk_frequency(clocks)),
#[cfg(soc_has_clock_node_apb_clk)]
apb_clock: Rate::from_hz(clocks::apb_clk_frequency(clocks)),
#[cfg(soc_has_clock_node_xtal_clk)]
xtal_clock: Rate::from_hz(clocks::xtal_clk_frequency(clocks)),
}
})
}
#[cfg(soc_has_clock_node_timg_calibration_clock)]
pub(crate) fn measure_rtc_clock(
clocks: &mut ClockTree,
rtc_clock: TimgCalibrationClockConfig,
#[cfg(soc_has_clock_node_timg_function_clock)] function_clock: TimgFunctionClockConfig,
slow_cycles: u32,
) -> (u32, Rate) {
#[cfg(not(esp32))]
if TIMG0::regs()
.rtccalicfg()
.read()
.rtc_cali_start_cycling()
.bit()
{
TIMG0::regs()
.rtccalicfg2()
.modify(|_, w| unsafe { w.rtc_cali_timeout_thres().bits(1) });
loop {
if TIMG0::regs()
.rtccalicfg()
.read()
.rtc_cali_rdy()
.bit_is_set()
|| TIMG0::regs()
.rtccalicfg2()
.read()
.rtc_cali_timeout()
.bit_is_set()
{
break;
}
}
}
TIMG0::regs()
.rtccalicfg()
.modify(|_, w| w.rtc_cali_start_cycling().clear_bit());
use esp_rom_sys::rom::ets_delay_us;
#[cfg(timergroup_rc_fast_calibration_divider)]
let calibration_divider = if rtc_clock == TimgCalibrationClockConfig::RcFastDivClk
&& crate::soc::chip_revision_above(ChipRevision::from_combined(property!(
"timergroup.rc_fast_calibration_divider_min_rev"
))) {
property!("timergroup.rc_fast_calibration_divider")
} else {
1
};
#[cfg(not(timergroup_rc_fast_calibration_divider))]
let calibration_divider = 1;
#[cfg(timergroup_rc_fast_calibration_tick_enable)]
let use_rc_fast_calibration_divider = rtc_clock == TimgCalibrationClockConfig::RcFastDivClk
&& crate::soc::chip_revision_above(ChipRevision::from_combined(property!(
"timergroup.rc_fast_calibration_divider_min_rev"
)));
let calibration_cycles = (slow_cycles / calibration_divider).max(1);
#[cfg(not(esp32))]
TIMG0::regs().rtccalicfg2().reset();
TIMG0::regs()
.rtccalicfg()
.modify(|_, w| w.rtc_cali_start().clear_bit());
cfg_if::cfg_if! {
if #[cfg(soc_has_clock_node_timg_function_clock)] {
let current_function_clock = clocks::TimgInstance::Timg0.function_clock_config(clocks);
clocks::TimgInstance::Timg0.configure_function_clock(clocks, function_clock);
clocks::TimgInstance::Timg0.request_function_clock(clocks);
}
}
let current_calib_clock = clocks::timg_calibration_clock_config(clocks);
clocks::configure_timg_calibration_clock(clocks, rtc_clock);
clocks::request_timg_calibration_clock(clocks);
#[cfg(timergroup_rc_fast_calibration_tick_enable)]
if use_rc_fast_calibration_divider {
PCR::regs()
.ctrl_tick_conf()
.modify(|_, w| w.tick_enable().set_bit());
}
let calibration_clock_frequency = clocks::timg_calibration_clock_frequency(clocks);
let effective_calibration_clock_frequency =
calibration_clock_frequency / calibration_divider;
#[cfg(not(esp32))]
{
let function_clk_freq =
clocks::TimgInstance::Timg0.function_clock_frequency(clocks) as u64;
let expected_function_clock_cycles = (function_clk_freq * slow_cycles as u64
/ effective_calibration_clock_frequency as u64)
as u32;
TIMG0::regs().rtccalicfg2().modify(|_, w| unsafe {
let writer = w.rtc_cali_timeout_thres();
let mask = (1 << writer.width()) - 1;
writer.bits((expected_function_clock_cycles * 2).min(mask));
w
});
}
TIMG0::regs().rtccalicfg().modify(|_, w| unsafe {
w.rtc_cali_max().bits(calibration_cycles as u16);
w.rtc_cali_start_cycling().clear_bit();
w.rtc_cali_start().set_bit()
});
let us_time_estimate = slow_cycles * 1_000_000 / effective_calibration_clock_frequency;
ets_delay_us(us_time_estimate);
#[cfg(esp32)]
let mut timeout_us = us_time_estimate;
let cali_value = loop {
if TIMG0::regs()
.rtccalicfg()
.read()
.rtc_cali_rdy()
.bit_is_set()
{
break TIMG0::regs().rtccalicfg1().read().rtc_cali_value().bits();
}
#[cfg(not(esp32))]
if TIMG0::regs()
.rtccalicfg2()
.read()
.rtc_cali_timeout()
.bit_is_set()
{
break 0;
}
#[cfg(esp32)]
if timeout_us > 0 {
timeout_us -= 1;
ets_delay_us(1);
} else {
break 0;
}
};
TIMG0::regs()
.rtccalicfg()
.modify(|_, w| w.rtc_cali_start().clear_bit());
#[cfg(timergroup_rc_fast_calibration_tick_enable)]
if use_rc_fast_calibration_divider {
PCR::regs()
.ctrl_tick_conf()
.modify(|_, w| w.tick_enable().clear_bit());
}
if let Some(calib_clock) = current_calib_clock
&& calib_clock != rtc_clock
{
clocks::configure_timg_calibration_clock(clocks, calib_clock);
}
clocks::release_timg_calibration_clock(clocks);
#[cfg(soc_has_clock_node_timg_function_clock)]
{
if let Some(func_clock) = current_function_clock
&& func_clock != function_clock
{
clocks::TimgInstance::Timg0.configure_function_clock(clocks, func_clock);
}
clocks::TimgInstance::Timg0.release_function_clock(clocks);
}
(cali_value, Rate::from_hz(calibration_clock_frequency))
}
#[cfg(not(soc_has_clock_node_timg_calibration_clock))]
pub(crate) fn calibrate_rtc_slow_clock() {
}
#[cfg(soc_has_clock_node_timg_calibration_clock)]
pub(crate) fn calibrate_rtc_slow_clock() {
cfg_if::cfg_if! {
if #[cfg(esp32s2)] {
let slow_clk = TimgCalibrationClockConfig::RtcClk;
} else if #[cfg(soc_has_clock_node_rtc_slow_clk)] {
let slow_clk = match unwrap!(ClockTree::with(clocks::rtc_slow_clk_config)) {
RtcSlowClkConfig::RcFast => TimgCalibrationClockConfig::RcFastDivClk,
RtcSlowClkConfig::RcSlow => TimgCalibrationClockConfig::RcSlowClk,
#[cfg(not(esp32c2))]
RtcSlowClkConfig::Xtal32k => TimgCalibrationClockConfig::Xtal32kClk,
#[cfg(esp32c2)]
RtcSlowClkConfig::OscSlow => TimgCalibrationClockConfig::Osc32kClk,
};
} else if #[cfg(soc_has_clock_node_lp_slow_clk)]{
let slow_clk = match unwrap!(ClockTree::with(clocks::lp_slow_clk_config)) {
LpSlowClkConfig::OscSlow => TimgCalibrationClockConfig::Xtal32kClk, LpSlowClkConfig::Xtal32k => TimgCalibrationClockConfig::Xtal32kClk,
LpSlowClkConfig::RcSlow => TimgCalibrationClockConfig::RcSlowClk,
};
}
}
let cal_val = RtcClock::calibrate(slow_clk, 1024);
cfg_if::cfg_if! {
if #[cfg(soc_has_lp_aon)] {
use crate::peripherals::LP_AON;
} else {
use crate::peripherals::LPWR as LP_AON;
}
}
LP_AON::regs()
.store1()
.write(|w| unsafe { w.bits(cal_val) });
}
}
pub fn cpu_clock() -> Rate {
Clocks::get().cpu_clock
}
pub fn xtal_clock() -> Rate {
Clocks::get().xtal_clock
}
fn rtc_slow_cal_period() -> u64 {
cfg_if::cfg_if! {
if #[cfg(soc_has_lp_aon)] {
use crate::peripherals::LP_AON;
} else {
use crate::peripherals::LPWR as LP_AON;
}
}
LP_AON::regs().store1().read().bits() as u64
}
#[cfg(lp_timer_driver_supported)]
pub(crate) fn rtc_ticks_to_us(ticks: u64) -> u64 {
let period = rtc_slow_cal_period();
const MASK: u64 = (1 << RtcClock::CAL_FRACT) - 1;
let upper = (ticks & !MASK) >> RtcClock::CAL_FRACT;
let lower = ticks & MASK;
upper * period + ((lower * period) >> RtcClock::CAL_FRACT)
}
pub(crate) fn us_to_rtc_ticks(time_in_us: u64) -> u64 {
let period = rtc_slow_cal_period();
if time_in_us > (u64::MAX >> RtcClock::CAL_FRACT) {
((time_in_us / period) << RtcClock::CAL_FRACT)
+ ((time_in_us % period) << RtcClock::CAL_FRACT) / period
} else {
(time_in_us << RtcClock::CAL_FRACT) / period
}
}