diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 7b68932f87c..ffaf87d251a 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +- The `ESP_HAL_CONFIG_XTAL_FREQUENCY` configuration option has been removed (#4517) ## [v1.0.0] - 2025-10-30 diff --git a/esp-hal/esp_config.yml b/esp-hal/esp_config.yml index 6e89295aca1..b3a0e5a3d67 100644 --- a/esp-hal/esp_config.yml +++ b/esp-hal/esp_config.yml @@ -27,41 +27,6 @@ options: default: - value: false -# Ideally, we should be able to set any clock frequency for any chip. However, -# currently only the 32 and C2 implements any sort of configurability, and -# the rest have a fixed clock frequency. -- name: xtal-frequency - description: "The frequency of the crystal oscillator, in MHz. Set to `auto` to - automatically detect the frequency. `auto` may not be able to identify the clock - frequency in some cases. Also, configuring a specific frequency may increase - performance slightly." - default: - - if: 'chip == "esp32" || chip == "esp32c2"' - value: '"auto"' - - if: 'chip == "esp32c3" || chip == "esp32c6" || chip == "esp32s2" || chip == "esp32s3"' - value: '"40"' - - if: 'chip == "esp32h2"' - value: '"32"' - constraints: - - if: 'chip == "esp32" || chip == "esp32c2"' - type: - validator: enumeration - value: - - 'auto' - - '26' - - '40' - - if: 'chip == "esp32c3" || chip == "esp32c6" || chip == "esp32s2" || chip == "esp32s3"' - type: - validator: enumeration - value: - - '40' - - if: 'chip == "esp32h2"' - type: - validator: enumeration - value: - - '32' - active: 'chip == "esp32" || chip == "esp32c2"' - - name: spi-address-workaround description: "Enables a workaround for the issue where SPI in half-duplex mode incorrectly transmits the address on a single line if the diff --git a/esp-hal/src/clock/clocks_ll/esp32c2.rs b/esp-hal/src/clock/clocks_ll/esp32c2.rs index 9ecb0ef59f8..a2d71556f2b 100644 --- a/esp-hal/src/clock/clocks_ll/esp32c2.rs +++ b/esp-hal/src/clock/clocks_ll/esp32c2.rs @@ -1,135 +1,8 @@ use crate::{ - clock::{ApbClock, Clock, CpuClock, PllClock, RtcClock, XtalClock}, - peripherals::{APB_CTRL, I2C_ANA_MST, LPWR, MODEM_CLKRST, SYSTEM}, - soc::regi2c, + clock::{RtcClock, XtalClock}, + peripherals::{APB_CTRL, MODEM_CLKRST}, }; -const I2C_BBPLL_OC_DCHGP_LSB: u32 = 4; -const I2C_BBPLL_OC_DHREF_SEL_LSB: u32 = 4; -const I2C_BBPLL_OC_DLREF_SEL_LSB: u32 = 6; - -pub(crate) fn esp32c2_rtc_bbpll_configure(xtal_freq: XtalClock, _pll_freq: PllClock) { - let div_ref: u8; - let div7_0: u8; - let dr1: u8; - let dr3: u8; - let dchgp: u8; - let dcur: u8; - let dbias: u8; - - // Set this register to let the digital part know 480M PLL is used - SYSTEM::regs() - .cpu_per_conf() - .modify(|_, w| w.pll_freq_sel().set_bit()); - - I2C_ANA_MST::regs().ana_conf0().modify(|_, w| { - w.bbpll_stop_force_high().clear_bit(); - w.bbpll_stop_force_low().set_bit() - }); - - // Configure 480M PLL - match xtal_freq { - XtalClock::_26M => { - div_ref = 12; - div7_0 = 236; - dr1 = 4; - dr3 = 4; - dchgp = 0; - dcur = 0; - dbias = 2; - } - XtalClock::_40M => { - div_ref = 0; - div7_0 = 8; - dr1 = 0; - dr3 = 0; - dchgp = 5; - dcur = 3; - dbias = 2; - } - } - - regi2c::I2C_BBPLL_REG4.write_reg(0x6b); - - let i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref; - let i2c_bbpll_dcur = - (1 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (3 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur; - - regi2c::I2C_BBPLL_OC_REF.write_reg(i2c_bbpll_lref); - regi2c::I2C_BBPLL_OC_DIV_REG.write_reg(div7_0); - regi2c::I2C_BBPLL_OC_DR1.write_field(dr1); - regi2c::I2C_BBPLL_OC_DR3.write_field(dr3); - regi2c::I2C_BBPLL_REG6.write_reg(i2c_bbpll_dcur); - regi2c::I2C_BBPLL_OC_VCO_DBIAS.write_field(dbias); - - // WAIT CALIBRATION DONE - while I2C_ANA_MST::regs() - .ana_conf0() - .read() - .bbpll_cal_done() - .bit_is_clear() - {} - - // workaround bbpll calibration might stop early - crate::rom::ets_delay_us(10); - - // Stop BBPLL self-calibration - I2C_ANA_MST::regs().ana_conf0().modify(|_, w| { - w.bbpll_stop_force_high().set_bit(); - w.bbpll_stop_force_low().clear_bit() - }); -} - -pub(crate) fn esp32c2_rtc_bbpll_enable() { - LPWR::regs().options0().modify(|_, w| { - w.bb_i2c_force_pd().clear_bit(); - w.bbpll_force_pd().clear_bit(); - w.bbpll_i2c_force_pd().clear_bit() - }); -} - -pub(crate) fn esp32c2_rtc_update_to_xtal(freq: XtalClock, div: u32) { - crate::rom::ets_update_cpu_frequency_rom(freq.mhz()); - - // Set divider from XTAL to APB clock. Need to set divider to 1 (reg. value 0) - // first. - SYSTEM::regs().sysclk_conf().modify(|_, w| unsafe { - w.pre_div_cnt().bits(0); - w.pre_div_cnt().bits((div - 1) as u16) - }); - - // No need to adjust the REF_TICK - - // Switch clock source - SYSTEM::regs() - .sysclk_conf() - .modify(|_, w| unsafe { w.soc_clk_sel().bits(0) }); -} - -pub(crate) fn esp32c2_rtc_freq_to_pll_mhz(cpu_clock_speed: CpuClock) { - SYSTEM::regs().sysclk_conf().modify(|_, w| unsafe { - w.pre_div_cnt().bits(0); - w.soc_clk_sel().bits(1) - }); - SYSTEM::regs().cpu_per_conf().modify(|_, w| unsafe { - w.cpuperiod_sel().bits(match cpu_clock_speed { - CpuClock::_80MHz => 0, - CpuClock::_120MHz => 1, - }) - }); - - crate::rom::ets_update_cpu_frequency_rom(cpu_clock_speed.mhz()); -} - -pub(crate) fn esp32c2_rtc_apb_freq_update(apb_freq: ApbClock) { - let value = ((apb_freq.hz() >> 12) & u16::MAX as u32) - | (((apb_freq.hz() >> 12) & u16::MAX as u32) << 16); - - LPWR::regs() - .store5() - .modify(|_, w| unsafe { w.data().bits(value) }); -} - // Mask for clock bits used by both WIFI and Bluetooth, 0, 1, 2, 3, 7, 8, 9, 10, // 19, 20, 21, 22, 23 const SYSTEM_WIFI_CLK_WIFI_BT_COMMON_M: u32 = 0x78078F; diff --git a/esp-hal/src/clock/mod.rs b/esp-hal/src/clock/mod.rs index 201fe1dffae..7a2cb117407 100644 --- a/esp-hal/src/clock/mod.rs +++ b/esp-hal/src/clock/mod.rs @@ -432,7 +432,7 @@ impl RtcClock { } /// Select source for RTC_SLOW_CLK. - #[cfg(not(any(esp32c6, esp32h2)))] + #[cfg(not(any(esp32c2, esp32c6, esp32h2)))] pub(crate) fn set_slow_freq(slow_freq: RtcSlowClock) { LPWR::regs().clk_conf().modify(|_, w| { unsafe { @@ -455,7 +455,7 @@ impl RtcClock { } /// Select source for RTC_FAST_CLK. - #[cfg(not(any(esp32c6, esp32h2)))] + #[cfg(not(any(esp32c2, esp32c6, esp32h2)))] pub(crate) fn set_fast_freq(fast_freq: RtcFastClock) { LPWR::regs().clk_conf().modify(|_, w| { w.fast_clk_rtc_sel().bit(match fast_freq { @@ -1093,7 +1093,9 @@ impl Clocks { let config = Self::configure(cpu_clock_speed); RtcClock::update_xtal_freq_mhz(config.xtal_clock.as_mhz()); unsafe { ACTIVE_CLOCKS = Some(config) }; - }) + }); + + crate::rtc_cntl::rtc::configure_clock(); } fn try_get<'a>() -> Option<&'a Clocks> { @@ -1116,51 +1118,30 @@ impl Clocks { #[cfg(systimer)] #[inline] pub(crate) fn xtal_freq() -> Rate { - if esp_config::esp_config_str!("ESP_HAL_CONFIG_XTAL_FREQUENCY") == "auto" - && let Some(clocks) = Self::try_get() - { + if let Some(clocks) = Self::try_get() { return clocks.xtal_clock; } Self::measure_xtal_frequency().frequency() } - #[cfg(not(esp32))] // unused - const fn xtal_frequency_from_config() -> Option { - let frequency_conf = esp_config::esp_config_str!("ESP_HAL_CONFIG_XTAL_FREQUENCY"); - - for_each_soc_xtal_options!( - (all $( ($freq:literal) ),*) => { - paste::paste! { - return match frequency_conf.as_bytes() { - b"auto" => None, - - // If the frequency is a pre-set value for the chip, return the associated enum variant. - $( _ if esp_config::esp_config_int_parse!(u32, frequency_conf) == $freq => Some(XtalClock::[<_ $freq M>]), )* + #[cfg(not(esp32))] + fn measure_xtal_frequency() -> XtalClock { + cfg_if::cfg_if! { + if #[cfg(esp32h2)] { + XtalClock::_32M + } else if #[cfg(not(esp32c2))] { + XtalClock::_40M + } else { + // TODO: we should be able to read from a retention register, but probe-rs flashes a + // bootloader that assumes a frequency, instead of choosing a matching one. + let mhz = RtcClock::estimate_xtal_frequency(); - _ => None, - }; - } - }; - ); - } + debug!("Working with a {}MHz crystal", mhz); - #[cfg(not(esp32))] // unused - the build-time config can be removed in favour of explicit configuration via esp_hal::init - fn measure_xtal_frequency() -> XtalClock { - if let Some(clock) = const { Self::xtal_frequency_from_config() } { - // Use the configured frequency - clock - } else if esp_config::esp_config_str!("ESP_HAL_CONFIG_XTAL_FREQUENCY") == "auto" { - // TODO: we should be able to read from a retention register, but probe-rs flashes a - // bootloader that assumes a frequency, instead of choosing a matching one. - let mhz = RtcClock::estimate_xtal_frequency(); - - debug!("Working with a {}MHz crystal", mhz); - - // Try to guess the closest possible crystal value. - XtalClock::closest_from_mhz(mhz) - } else { - unreachable!("Invalid crystal frequency configured, this should not be possible.") + // Try to guess the closest possible crystal value. + XtalClock::closest_from_mhz(mhz) + } } } } @@ -1199,32 +1180,25 @@ impl Clocks { impl Clocks { /// Configure the CPU clock speed. pub(crate) fn configure(cpu_clock_speed: CpuClock) -> Self { - let xtal_freq = Self::measure_xtal_frequency(); + use crate::soc::clocks::{ClockTree, request_low_power_clk}; - let apb_freq; - if cpu_clock_speed != CpuClock::default() { - let pll_freq = PllClock::Pll480MHz; - - if cpu_clock_speed.mhz() <= xtal_freq.mhz() { - apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz()); - clocks_ll::esp32c2_rtc_update_to_xtal(xtal_freq, 1); - clocks_ll::esp32c2_rtc_apb_freq_update(apb_freq); - } else { - apb_freq = ApbClock::ApbFreq40MHz; - clocks_ll::esp32c2_rtc_bbpll_enable(); - clocks_ll::esp32c2_rtc_bbpll_configure(xtal_freq, pll_freq); - clocks_ll::esp32c2_rtc_freq_to_pll_mhz(cpu_clock_speed); - clocks_ll::esp32c2_rtc_apb_freq_update(apb_freq); - } - } else { - apb_freq = ApbClock::ApbFreq40MHz; + // TODO: expose the whole new enum for custom options + match cpu_clock_speed { + CpuClock::_80MHz => crate::soc::clocks::CpuClock::_80MHz, + CpuClock::_120MHz => crate::soc::clocks::CpuClock::_120MHz, } + .configure(); - Self { - cpu_clock: cpu_clock_speed.frequency(), - apb_clock: apb_freq.frequency(), - xtal_clock: xtal_freq.frequency(), - } + ClockTree::with(|clocks| { + // TODO: this should be managed by esp-radio. The actual clock is probably managed by + // the hardware, but we need to make sure the upstream clocks are running. + request_low_power_clk(clocks); + Self { + cpu_clock: Rate::from_hz(crate::soc::clocks::cpu_clk_frequency(clocks)), + apb_clock: Rate::from_hz(crate::soc::clocks::apb_clk_frequency(clocks)), + xtal_clock: Rate::from_hz(crate::soc::clocks::xtl_clk_frequency(clocks)), + } + }) } } diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index 05a8ccdb5c3..353d6dc2581 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -689,8 +689,6 @@ pub fn init(config: Config) -> Peripherals { Clocks::init(config.cpu_clock()); - crate::rtc_cntl::rtc::configure_clock(); - // RTC domain must be enabled before we try to disable let mut rtc = crate::rtc_cntl::Rtc::new(peripherals.LPWR.reborrow()); diff --git a/esp-hal/src/rtc_cntl/rtc/esp32c2.rs b/esp-hal/src/rtc_cntl/rtc/esp32c2.rs index 836b8f0a053..d8714011536 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32c2.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32c2.rs @@ -1,7 +1,7 @@ use strum::FromRepr; use crate::{ - clock::{RtcClock, RtcFastClock, RtcSlowClock}, + clock::RtcClock, peripherals::{APB_CTRL, EXTMEM, LPWR, SPI0, SPI1, SYSTEM}, rtc_cntl::RtcCalSel, soc::regi2c, @@ -14,9 +14,10 @@ pub(crate) fn init() { regi2c::I2C_DIG_REG_XPD_RTC_REG.write_field(0); unsafe { - rtc_cntl - .timer1() - .modify(|_, w| w.pll_buf_wait().bits(20u8).ck8m_wait().bits(20u8)); + rtc_cntl.timer1().modify(|_, w| { + w.pll_buf_wait().bits(20u8); + w.ck8m_wait().bits(20u8) + }); rtc_cntl.timer5().modify(|_, w| w.min_slp_val().bits(2u8)); } @@ -35,28 +36,16 @@ pub(crate) fn init() { } regi2c::I2C_ULP_IR_FORCE_XPD_CK.write_field(0); - - // from esp_clk_init: - // clk_ll_rc_fast_enable(); - rtc_cntl.clk_conf().modify(|_, w| w.enb_ck8m().clear_bit()); - rtc_cntl - .timer1() - .modify(|_, w| unsafe { w.ck8m_wait().bits(5) }); - // esp_rom_delay_us(SOC_DELAY_RC_FAST_ENABLE); - crate::rom::ets_delay_us(50); - - RtcClock::set_fast_freq(RtcFastClock::RcFast); - RtcClock::set_slow_freq(RtcSlowClock::RcSlow); } pub(crate) fn configure_clock() { + // TODO: this needs to be re-done, too. "RtcMux" is just RC_SLOW_CLK. let cal_val = loop { let res = RtcClock::calibrate(RtcCalSel::RtcMux, 1024); if res != 0 { break res; } }; - unsafe { LPWR::regs().store1().write(|w| w.bits(cal_val)) }; } diff --git a/esp-hal/src/soc/esp32/clocks.rs b/esp-hal/src/soc/esp32/clocks.rs index 379115710f8..4e07980131f 100644 --- a/esp-hal/src/soc/esp32/clocks.rs +++ b/esp-hal/src/soc/esp32/clocks.rs @@ -45,6 +45,7 @@ pub(crate) enum CpuClock { impl CpuClock { pub(crate) fn configure(self) { // Resolve presets + // TODO: set some defaults to RTC clocks let mut config = match self { CpuClock::_80MHz => ClockConfig { xtl_clk: None, @@ -102,7 +103,7 @@ fn detect_xtal_freq(clocks: &mut ClockTree) -> XtlClkConfig { // Just an assumption for things to not panic. configure_xtl_clk(clocks, XtlClkConfig::_40); - configure_syscon_pre_div(clocks, SysconPreDivConfig(0)); + configure_syscon_pre_div(clocks, SysconPreDivConfig::new(0)); configure_cpu_clk(clocks, CpuClkConfig::Xtal); // Make sure the process doesn't time out due to some spooky configuration. @@ -167,8 +168,23 @@ const BBPLL_BBADC_DSMP_VAL_480M: u8 = 0x74; // XTL_CLK -fn configure_xtl_clk_impl(_clocks: &mut ClockTree, _config: XtlClkConfig) { - // Nothing to do, the stored configuration affects PLL settings instead. +fn configure_xtl_clk_impl(_clocks: &mut ClockTree, config: XtlClkConfig) { + // The stored configuration affects PLL settings instead. We save the value in a register + // similar to ESP-IDF, just in case something relies on that, or, if we can in the future read + // back the value instead of wasting RAM on it. + + const DISABLE_ROM_LOG: u32 = 1; + + let freq_mhz = config.value() / 1_000_000; + LPWR::regs().store4().modify(|r, w| unsafe { + // The data is stored in two copies of 16-bit values. The first bit overwrites the LSB of + // the frequency value with DISABLE_ROM_LOG. + + // Copy the DISABLE_ROM_LOG bit + let disable_rom_log_bit = r.bits() & DISABLE_ROM_LOG; + let half = (freq_mhz & 0xFFFF) | disable_rom_log_bit; + w.data().bits(half | (half << 16)) + }); } // PLL_CLK @@ -493,10 +509,10 @@ fn configure_cpu_clk_impl( ets_update_cpu_frequency_rom(cpu_freq.as_mhz()); let apb_freq = Rate::from_hz(apb_clk_frequency(clocks)); - update_cpu_frequency(apb_freq); + update_apb_frequency(apb_freq); } -fn update_cpu_frequency(freq: Rate) { +fn update_apb_frequency(freq: Rate) { let freq_shifted = (freq.as_hz() >> 12) & 0xFFFF; let value = freq_shifted | (freq_shifted << 16); LPWR::regs() diff --git a/esp-hal/src/soc/esp32c2/clocks.rs b/esp-hal/src/soc/esp32c2/clocks.rs new file mode 100644 index 00000000000..473b37c7b87 --- /dev/null +++ b/esp-hal/src/soc/esp32c2/clocks.rs @@ -0,0 +1,623 @@ +//! Clock tree definitions and implementations for ESP32-C2. +//! +//! Remarks: +//! - Enabling a clock node assumes it has first been configured. Some fixed clock nodes don't need +//! to be configured. +//! - Some information may be assumed, e.g. the possibility to disable watchdog timers before clock +//! configuration. +//! - Internal RC oscillators (136k RC_SLOW and 17.5M RC_FAST) are not calibrated here, this system +//! can only give a rough estimate of their frequency. They can be calibrated separately using a +//! known crystal frequency. +//! - Some of the SOC capabilities are not implemented: using external 32K oscillator, divider for +//! RC_FAST and possibly more. +#![allow(dead_code, reason = "Some of this is bound to be unused")] + +// TODO: This is a temporary place for this, should probably be moved into clocks_ll. + +use esp_rom_sys::rom::{ets_delay_us, ets_update_cpu_frequency_rom}; + +use crate::{ + peripherals::{I2C_ANA_MST, LPWR, SYSTEM, TIMG0}, + soc::regi2c, + time::Rate, +}; + +define_clock_tree_types!(); + +// TODO: this should replace the current CpuClock enum. CpuClock is a bit of a misleading +// name as this will configure multiple things. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[allow( + clippy::enum_variant_names, + reason = "MHz suffix indicates physical unit." +)] +#[non_exhaustive] +pub(crate) enum CpuClock { + #[default] + _80MHz, + _120MHz, + Custom(ClockConfig), +} + +impl CpuClock { + pub(crate) fn configure(self) { + // Resolve presets + let mut config = match self { + CpuClock::_80MHz => ClockConfig { + xtl_clk: None, + system_pre_div: None, + cpu_pll_div: Some(CpuPllDivConfig::_6), + cpu_clk: Some(CpuClkConfig::Pll), + rc_fast_clk_div_n: Some(RcFastClkDivNConfig::new(0)), + rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow), + rtc_fast_clk: Some(RtcFastClkConfig::Rc), + low_power_clk: Some(LowPowerClkConfig::RtcSlow), + }, + CpuClock::_120MHz => ClockConfig { + xtl_clk: None, + system_pre_div: None, + cpu_pll_div: Some(CpuPllDivConfig::_4), + cpu_clk: Some(CpuClkConfig::Pll), + rc_fast_clk_div_n: Some(RcFastClkDivNConfig::new(0)), + rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow), + rtc_fast_clk: Some(RtcFastClkConfig::Rc), + low_power_clk: Some(LowPowerClkConfig::RtcSlow), + }, + CpuClock::Custom(clock_config) => clock_config, + }; + + // Detect XTL if unset. + // FIXME: this doesn't support running from RC_FAST_CLK. We should rework detection to only + // run when requesting XTL. + ClockTree::with(|clocks| { + if config.xtl_clk.is_none() { + // While the bootloader stores a crystal frequency in a retention register, + // that comes from a constant that we should not trust. If the user did not + // provide a crystal frequency, we should detect it. + let xtal = detect_xtal_freq(clocks); + debug!("Auto-detected XTAL frequency: {}", xtal.value()); + config.xtl_clk = Some(xtal); + } + }); + + config.apply(); + } +} + +// TODO: this could be chip-independent +// We're rather impolite in this function by not saving and restoring configuration, but this is +// expected to run before configuring CPU clocks anyway. +fn detect_xtal_freq(clocks: &mut ClockTree) -> XtlClkConfig { + const SLOW_CLOCK_CYCLES: u32 = 100; + + // Just an assumption for things to not panic. + configure_xtl_clk(clocks, XtlClkConfig::_40); + configure_system_pre_div(clocks, SystemPreDivConfig::new(0)); + configure_cpu_clk(clocks, CpuClkConfig::Xtal); + + // By default the TIMG0 bus clock is running. Do not create a peripheral guard as dropping it + // would reset the timer, and it would enable its WDT. + + // Make sure the process doesn't time out due to some spooky configuration. + #[cfg(not(esp32))] + TIMG0::regs().rtccalicfg2().reset(); + + TIMG0::regs() + .rtccalicfg() + .modify(|_, w| w.rtc_cali_start().clear_bit()); + + // Make sure we measure the crystal. + #[cfg(soc_has_clock_node_timg0_function_clock)] + { + configure_timg0_function_clock(clocks, Timg0FunctionClockConfig::XtalClk); + request_timg0_function_clock(clocks); + } + + configure_timg0_calibration_clock(clocks, Timg0CalibrationClockConfig::RcSlowClk); + request_timg0_calibration_clock(clocks); + + let calibration_clock_frequency = timg0_calibration_clock_frequency(clocks); + + TIMG0::regs().rtccalicfg().modify(|_, w| unsafe { + w.rtc_cali_max().bits(SLOW_CLOCK_CYCLES as u16); + w.rtc_cali_start_cycling().clear_bit(); + w.rtc_cali_start().set_bit() + }); + + // Delay, otherwise the CPU may read back the previous state of the completion flag and skip + // waiting. + ets_delay_us(SLOW_CLOCK_CYCLES * 1_000_000 / calibration_clock_frequency); + + // Wait for the calibration to finish + while TIMG0::regs() + .rtccalicfg() + .read() + .rtc_cali_rdy() + .bit_is_clear() + {} + + let cali_value = TIMG0::regs().rtccalicfg1().read().rtc_cali_value().bits(); + + TIMG0::regs() + .rtccalicfg() + .modify(|_, w| w.rtc_cali_start().clear_bit()); + release_timg0_calibration_clock(clocks); + + #[cfg(soc_has_clock_node_timg0_function_clock)] + release_timg0_function_clock(clocks); + + let mhz = (cali_value * (calibration_clock_frequency / SLOW_CLOCK_CYCLES)) / 1_000_000; + if mhz.abs_diff(40) < mhz.abs_diff(26) { + XtlClkConfig::_40 + } else { + XtlClkConfig::_26 + } +} + +// XTL_CLK + +fn configure_xtl_clk_impl(_clocks: &mut ClockTree, config: XtlClkConfig) { + // The stored configuration affects PLL settings instead. We save the value in a register + // similar to ESP-IDF, just in case something relies on that, or, if we can in the future read + // back the value instead of wasting RAM on it. + + const DISABLE_ROM_LOG: u32 = 1; + + let freq_mhz = config.value() / 1_000_000; + LPWR::regs().store4().modify(|r, w| unsafe { + // The data is stored in two copies of 16-bit values. The first bit overwrites the LSB of + // the frequency value with DISABLE_ROM_LOG. + + // Copy the DISABLE_ROM_LOG bit + let disable_rom_log_bit = r.bits() & DISABLE_ROM_LOG; + let half = (freq_mhz & 0xFFFF) | disable_rom_log_bit; + w.data().bits(half | (half << 16)) + }); +} + +// PLL_CLK + +fn enable_pll_clk_impl(clocks: &mut ClockTree, en: bool) { + const I2C_BBPLL_OC_DCHGP_LSB: u32 = 4; + const I2C_BBPLL_OC_DHREF_SEL_LSB: u32 = 4; + const I2C_BBPLL_OC_DLREF_SEL_LSB: u32 = 6; + + // regi2c_ctrl_ll_i2c_bbpll_enable + I2C_ANA_MST::regs() + .ana_config() + .modify(|_, w| w.bbpll_pd().bit(!en)); + + LPWR::regs().options0().modify(|_, w| { + let power_down = !en; + w.bb_i2c_force_pd().bit(power_down); + w.bbpll_force_pd().bit(power_down); + w.bbpll_i2c_force_pd().bit(power_down) + }); + + if !en { + return; + } + + // Digital part + + SYSTEM::regs() + .cpu_per_conf() + .modify(|_, w| w.pll_freq_sel().set_bit()); // Undocumented, selects 480MHz PLL. + + // Analog part + + // Start BBPLL self-calibration + I2C_ANA_MST::regs().ana_conf0().modify(|_, w| { + w.bbpll_stop_force_high().clear_bit(); + w.bbpll_stop_force_low().set_bit() + }); + + let div_ref: u8; + let div7_0: u8; + let dr1: u8; + let dr3: u8; + let dchgp: u8; + let dcur: u8; + let dbias: u8; + // Configure 480M PLL + match unwrap!(clocks.xtl_clk) { + XtlClkConfig::_26 => { + // Divide by 13 -> reference = 2MHz + div_ref = 12; + // Multiply by 236 + 4 -> PLL output = 480MHz + div7_0 = 236; + dr1 = 4; + dr3 = 4; + dchgp = 0; + dcur = 0; + dbias = 2; + } + XtlClkConfig::_40 => { + // Divide by 1 -> reference = 40MHz + div_ref = 0; + // Multiply by 8 + 4 -> PLL output = 480MHz + div7_0 = 8; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 3; + dbias = 2; + } + } + + regi2c::I2C_BBPLL_REG4.write_reg(0x6b); + + let i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref; + let i2c_bbpll_dcur = + (1 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (3 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur; + + regi2c::I2C_BBPLL_OC_REF.write_reg(i2c_bbpll_lref); + regi2c::I2C_BBPLL_OC_DIV_REG.write_reg(div7_0); + regi2c::I2C_BBPLL_OC_DR1.write_field(dr1); + regi2c::I2C_BBPLL_OC_DR3.write_field(dr3); + regi2c::I2C_BBPLL_REG6.write_reg(i2c_bbpll_dcur); + regi2c::I2C_BBPLL_OC_VCO_DBIAS.write_field(dbias); + + while I2C_ANA_MST::regs() + .ana_conf0() + .read() + .bbpll_cal_done() + .bit_is_clear() + {} + + ets_delay_us(10); + + // Stop BBPLL self-calibration + I2C_ANA_MST::regs().ana_conf0().modify(|_, w| { + w.bbpll_stop_force_high().set_bit(); + w.bbpll_stop_force_low().clear_bit() + }); +} + +// RC_FAST_CLK + +fn enable_rc_fast_clk_impl(_clocks: &mut ClockTree, en: bool) { + // XPD_RC_OSCILLATOR exists but we'll manage that separately + const RTC_CNTL_FOSC_DFREQ_DEFAULT: u8 = 172; + LPWR::regs().clk_conf().modify(|_, w| { + // Confusing CK8M naming inherited from ESP32? + + // CK8M_DFREQ value controls tuning of 8M clock. + unsafe { w.ck8m_dfreq().bits(RTC_CNTL_FOSC_DFREQ_DEFAULT) }; + + w.enb_ck8m().bit(!en); + w.dig_clk8m_en().bit(en); // digital system clock gate + // Do not force the clock either way. + w.ck8m_force_pd().clear_bit(); + w.ck8m_force_pu().clear_bit() + }); + LPWR::regs() + .timer1() + .modify(|_, w| unsafe { w.ck8m_wait().bits(if en { 5 } else { 20 }) }); +} + +// OSC_SLOW_CLK + +fn enable_osc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +// RC_SLOW_CLK + +fn enable_rc_slow_clk_impl(_clocks: &mut ClockTree, en: bool) { + if !en { + return; + } + + // SCK_DCAP value controls tuning of 136k clock. The higher the value of DCAP, the lower the + // frequency. There is no separate enable bit, just make sure the calibration value is set. + const RTC_CNTL_SCK_DCAP_DEFAULT: u8 = 255; + LPWR::regs() + .rtc_cntl() + .modify(|_, w| unsafe { w.sck_dcap().bits(RTC_CNTL_SCK_DCAP_DEFAULT) }); + + // Also configure the divider here to its usual value of 1. + + // Updating the divider should be part of the RC_SLOW_CLK divider config: + let slow_clk_conf = LPWR::regs().slow_clk_conf(); + // Invalidate + let new_value = slow_clk_conf.modify(|_, w| w.ana_clk_div_vld().clear_bit()); + // Update divider + let new_value = slow_clk_conf.write(|w| unsafe { + w.bits(new_value); + w.ana_clk_div().bits(0) + }); + // Re-synchronize + slow_clk_conf.write(|w| { + unsafe { w.bits(new_value) }; + w.ana_clk_div_vld().set_bit() + }); +} + +// RC_FAST_DIV_CLK + +fn enable_rc_fast_div_clk_impl(_clocks: &mut ClockTree, en: bool) { + LPWR::regs() + .clk_conf() + .modify(|_, w| w.enb_ck8m_div().bit(!en)); +} + +// SYSTEM_PRE_DIV_IN + +// Not an actual MUX, used to allow configuring the DIV divider as one block. +// Related to CPU clock source configuration. +fn enable_system_pre_div_in_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +fn configure_system_pre_div_in_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + _new_selector: SystemPreDivInConfig, +) { + // Nothing to do. +} + +// SYSTEM_PRE_DIV + +fn enable_system_pre_div_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +fn configure_system_pre_div_impl(_clocks: &mut ClockTree, new_config: SystemPreDivConfig) { + SYSTEM::regs() + .sysclk_conf() + .modify(|_, w| unsafe { w.pre_div_cnt().bits(new_config.value() as u16 & 0x3FF) }); +} + +// CPU_PLL_DIV + +fn enable_cpu_pll_div_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +fn configure_cpu_pll_div_impl(_clocks: &mut ClockTree, _new_config: CpuPllDivConfig) { + // Nothing to do. +} + +// APB_CLK + +fn enable_apb_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +fn configure_apb_clk_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + _new_selector: ApbClkConfig, +) { + // Nothing to do. +} + +// CRYPTO_CLK + +fn enable_crypto_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +fn configure_crypto_clk_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + _new_selector: CryptoClkConfig, +) { + // Nothing to do, determined by CPU clock. +} + +// MSPI_CLK + +fn enable_mspi_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +fn configure_mspi_clk_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + _new_selector: MspiClkConfig, +) { + // Nothing to do, determined by CPU clock. +} + +// CPU_CLK + +fn configure_cpu_clk_impl( + clocks: &mut ClockTree, + _old_selector: Option, + new_selector: CpuClkConfig, +) { + // Based on TRM Table 6.2-2 + if new_selector == CpuClkConfig::Pll { + SYSTEM::regs().cpu_per_conf().modify(|_, w| unsafe { + w.cpuperiod_sel().bits(match unwrap!(clocks.cpu_pll_div) { + CpuPllDivConfig::_4 => 1, + CpuPllDivConfig::_6 => 0, + }) + }); + } + + SYSTEM::regs().sysclk_conf().modify(|_, w| unsafe { + w.soc_clk_sel().bits(match new_selector { + CpuClkConfig::Xtal => 0, + CpuClkConfig::RcFast => 2, + CpuClkConfig::Pll => 1, + }) + }); + + let apb_freq = Rate::from_hz(apb_clk_frequency(clocks)); + update_apb_frequency(apb_freq); + + let cpu_freq = Rate::from_hz(cpu_clk_frequency(clocks)); + ets_update_cpu_frequency_rom(cpu_freq.as_mhz()); +} + +fn update_apb_frequency(freq: Rate) { + let freq_shifted = (freq.as_hz() >> 12) & 0xFFFF; + let value = freq_shifted | (freq_shifted << 16); + LPWR::regs() + .store5() + .modify(|_, w| unsafe { w.data().bits(value) }); +} + +// PLL_40M + +fn enable_pll_40m_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +// PLL_60M + +fn enable_pll_60m_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +// PLL_80M + +fn enable_pll_80m_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +// CPU_DIV2 + +fn enable_cpu_div2_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +// RC_FAST_CLK_DIV_N + +fn enable_rc_fast_clk_div_n_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +fn configure_rc_fast_clk_div_n_impl(_clocks: &mut ClockTree, new_config: RcFastClkDivNConfig) { + let clk_conf = LPWR::regs().clk_conf(); + // Invalidate because we may be changing the divider from some other value + let new_value = clk_conf.modify(|_, w| w.ck8m_div_sel_vld().clear_bit()); + // Update divider + let new_value = clk_conf.write(|w| unsafe { + w.bits(new_value); + w.ck8m_div_sel().bits(new_config.value() as u8) + }); + // Re-synchronize + clk_conf.write(|w| { + unsafe { w.bits(new_value) }; + w.ck8m_div_sel_vld().set_bit() + }); +} + +// XTAL_DIV_CLK + +fn enable_xtal_div_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +// RTC_SLOW_CLK + +fn enable_rtc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +fn configure_rtc_slow_clk_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + new_selector: RtcSlowClkConfig, +) { + LPWR::regs().clk_conf().modify(|_, w| unsafe { + // TODO: variants should be in PAC + w.ana_clk_rtc_sel().bits(match new_selector { + RtcSlowClkConfig::OscSlow => 1, + RtcSlowClkConfig::RcSlow => 0, + RtcSlowClkConfig::RcFast => 2, + }) + }); + + ets_delay_us(300); +} + +// RTC_FAST_CLK + +fn enable_rtc_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +fn configure_rtc_fast_clk_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + new_selector: RtcFastClkConfig, +) { + // TODO: variants should be fixed in PAC + LPWR::regs().clk_conf().modify(|_, w| match new_selector { + RtcFastClkConfig::Xtal => w.fast_clk_rtc_sel().clear_bit(), + RtcFastClkConfig::Rc => w.fast_clk_rtc_sel().set_bit(), + }); + ets_delay_us(3); +} + +// LOW_POWER_CLK + +fn enable_low_power_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing in esp-idf does this - is this managed by hardware, or the radio blobs? + // SYSTEM::regs() + // .bt_lpck_div_frac() + // .modify(|_, w| w.lpclk_rtc_en().bit(en)); +} + +fn configure_low_power_clk_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + new_selector: LowPowerClkConfig, +) { + SYSTEM::regs().bt_lpck_div_frac().modify(|_, w| { + w.lpclk_sel_8m() + .bit(new_selector == LowPowerClkConfig::RcFast); + w.lpclk_sel_rtc_slow() + .bit(new_selector == LowPowerClkConfig::RtcSlow); + w.lpclk_sel_xtal() + .bit(new_selector == LowPowerClkConfig::Xtal); + w.lpclk_sel_xtal32k() + .bit(new_selector == LowPowerClkConfig::OscSlow) + }); +} + +fn enable_timg0_function_clock_impl(_clocks: &mut ClockTree, en: bool) { + // TODO: should we model T0_DIVIDER, too? + TIMG0::regs() + .regclk() + .modify(|_, w| w.timer_clk_is_active().bit(en)); +} + +fn configure_timg0_function_clock_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + new_selector: Timg0FunctionClockConfig, +) { + TIMG0::regs().t(0).config().modify(|_, w| { + w.use_xtal() + .bit(new_selector == Timg0FunctionClockConfig::XtalClk) + }); +} + +// TIMG0_CALIBRATION_CLOCK + +fn enable_timg0_calibration_clock_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do, calibration clocks can only be selected. They are gated by the CALI_START bit, + // which is managed by the calibration process. +} + +fn configure_timg0_calibration_clock_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + new_selector: Timg0CalibrationClockConfig, +) { + TIMG0::regs().rtccalicfg().modify(|_, w| unsafe { + w.rtc_cali_clk_sel().bits(match new_selector { + Timg0CalibrationClockConfig::RcSlowClk => 0, + Timg0CalibrationClockConfig::RcFastDivClk => 1, + Timg0CalibrationClockConfig::Osc32kClk => 2, + }) + }); +} diff --git a/esp-hal/src/soc/esp32c2/mod.rs b/esp-hal/src/soc/esp32c2/mod.rs index bbeebbcc4c2..cb757b0993f 100644 --- a/esp-hal/src/soc/esp32c2/mod.rs +++ b/esp-hal/src/soc/esp32c2/mod.rs @@ -8,6 +8,7 @@ crate::unstable_module! { pub mod trng; } +pub mod clocks; pub mod gpio; pub(crate) mod regi2c; diff --git a/esp-hal/src/timer/timg.rs b/esp-hal/src/timer/timg.rs index 6bc23007f4b..2a18ec72491 100644 --- a/esp-hal/src/timer/timg.rs +++ b/esp-hal/src/timer/timg.rs @@ -144,6 +144,15 @@ impl TimerGroupInstance for TIMG0<'_> { } fn configure_src_clk() { + #[cfg(soc_has_clock_node_timg0_function_clock)] + crate::soc::clocks::ClockTree::with(|clocks| { + crate::soc::clocks::configure_timg0_function_clock( + clocks, + crate::soc::clocks::Timg0FunctionClockConfig::default(), + ); + crate::soc::clocks::request_timg0_function_clock(clocks); + }); + cfg_if::cfg_if! { if #[cfg(not(timergroup_default_clock_source_is_set))] { // Clock source is not configurable diff --git a/esp-metadata-generated/src/_build_script_utils.rs b/esp-metadata-generated/src/_build_script_utils.rs index f76f655ac23..652cecb1d10 100644 --- a/esp-metadata-generated/src/_build_script_utils.rs +++ b/esp-metadata-generated/src/_build_script_utils.rs @@ -636,6 +636,30 @@ impl Chip { "soc_rc_fast_clk_default_is_set", "soc_rc_slow_clock=\"136000\"", "soc_rc_slow_clock_is_set", + "soc_has_clock_node_xtl_clk", + "soc_has_clock_node_pll_clk", + "soc_has_clock_node_rc_fast_clk", + "soc_has_clock_node_osc_slow_clk", + "soc_has_clock_node_rc_slow_clk", + "soc_has_clock_node_rc_fast_div_clk", + "soc_has_clock_node_system_pre_div_in", + "soc_has_clock_node_system_pre_div", + "soc_has_clock_node_cpu_pll_div", + "soc_has_clock_node_cpu_clk", + "soc_has_clock_node_pll_40m", + "soc_has_clock_node_pll_60m", + "soc_has_clock_node_pll_80m", + "soc_has_clock_node_cpu_div2", + "soc_has_clock_node_apb_clk", + "soc_has_clock_node_crypto_clk", + "soc_has_clock_node_mspi_clk", + "soc_has_clock_node_rc_fast_clk_div_n", + "soc_has_clock_node_xtal_div_clk", + "soc_has_clock_node_rtc_slow_clk", + "soc_has_clock_node_rtc_fast_clk", + "soc_has_clock_node_low_power_clk", + "soc_has_clock_node_timg0_function_clock", + "soc_has_clock_node_timg0_calibration_clock", "has_dram_region", "has_dram2_uninit_region", "soc_has_multiple_xtal_options", @@ -758,6 +782,30 @@ impl Chip { "cargo:rustc-cfg=soc_rc_fast_clk_default_is_set", "cargo:rustc-cfg=soc_rc_slow_clock=\"136000\"", "cargo:rustc-cfg=soc_rc_slow_clock_is_set", + "cargo:rustc-cfg=soc_has_clock_node_xtl_clk", + "cargo:rustc-cfg=soc_has_clock_node_pll_clk", + "cargo:rustc-cfg=soc_has_clock_node_rc_fast_clk", + "cargo:rustc-cfg=soc_has_clock_node_osc_slow_clk", + "cargo:rustc-cfg=soc_has_clock_node_rc_slow_clk", + "cargo:rustc-cfg=soc_has_clock_node_rc_fast_div_clk", + "cargo:rustc-cfg=soc_has_clock_node_system_pre_div_in", + "cargo:rustc-cfg=soc_has_clock_node_system_pre_div", + "cargo:rustc-cfg=soc_has_clock_node_cpu_pll_div", + "cargo:rustc-cfg=soc_has_clock_node_cpu_clk", + "cargo:rustc-cfg=soc_has_clock_node_pll_40m", + "cargo:rustc-cfg=soc_has_clock_node_pll_60m", + "cargo:rustc-cfg=soc_has_clock_node_pll_80m", + "cargo:rustc-cfg=soc_has_clock_node_cpu_div2", + "cargo:rustc-cfg=soc_has_clock_node_apb_clk", + "cargo:rustc-cfg=soc_has_clock_node_crypto_clk", + "cargo:rustc-cfg=soc_has_clock_node_mspi_clk", + "cargo:rustc-cfg=soc_has_clock_node_rc_fast_clk_div_n", + "cargo:rustc-cfg=soc_has_clock_node_xtal_div_clk", + "cargo:rustc-cfg=soc_has_clock_node_rtc_slow_clk", + "cargo:rustc-cfg=soc_has_clock_node_rtc_fast_clk", + "cargo:rustc-cfg=soc_has_clock_node_low_power_clk", + "cargo:rustc-cfg=soc_has_clock_node_timg0_function_clock", + "cargo:rustc-cfg=soc_has_clock_node_timg0_calibration_clock", "cargo:rustc-cfg=has_dram_region", "cargo:rustc-cfg=has_dram2_uninit_region", "cargo:rustc-cfg=soc_has_multiple_xtal_options", @@ -3059,6 +3107,18 @@ pub fn emit_check_cfg_directives() { println!("cargo:rustc-check-cfg=cfg(ecc)"); println!("cargo:rustc-check-cfg=cfg(systimer)"); println!("cargo:rustc-check-cfg=cfg(soc_cpu_has_csr_pc)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_osc_slow_clk)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_system_pre_div_in)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_system_pre_div)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_40m)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_60m)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_80m)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_cpu_div2)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_crypto_clk)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_mspi_clk)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_rc_fast_clk_div_n)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_low_power_clk)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_timg0_function_clock)"); println!("cargo:rustc-check-cfg=cfg(assist_debug_has_sp_monitor)"); println!("cargo:rustc-check-cfg=cfg(i2c_master_has_fsm_timeouts)"); println!("cargo:rustc-check-cfg=cfg(i2c_master_has_hw_bus_clear)"); diff --git a/esp-metadata-generated/src/_generated_esp32c2.rs b/esp-metadata-generated/src/_generated_esp32c2.rs index b20fa4611fc..0bf61bb9efd 100644 --- a/esp-metadata-generated/src/_generated_esp32c2.rs +++ b/esp-metadata-generated/src/_generated_esp32c2.rs @@ -220,11 +220,462 @@ macro_rules! for_each_soc_xtal_options { #[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run +/// // XTL_CLK +/// +/// fn configure_xtl_clk_impl(_clocks: &mut ClockTree, _config: XtlClkConfig) { +/// todo!() +/// } +/// +/// // PLL_CLK +/// +/// fn enable_pll_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // RC_FAST_CLK +/// +/// fn enable_rc_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // OSC_SLOW_CLK +/// +/// fn enable_osc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // RC_SLOW_CLK +/// +/// fn enable_rc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // RC_FAST_DIV_CLK +/// +/// fn enable_rc_fast_div_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // SYSTEM_PRE_DIV_IN +/// +/// fn enable_system_pre_div_in_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_system_pre_div_in_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: SystemPreDivInConfig, +/// ) { +/// todo!() +/// } +/// +/// // SYSTEM_PRE_DIV +/// +/// fn enable_system_pre_div_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_system_pre_div_impl(_clocks: &mut ClockTree, _new_config: SystemPreDivConfig) { +/// todo!() +/// } +/// +/// // CPU_PLL_DIV +/// +/// fn enable_cpu_pll_div_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_cpu_pll_div_impl(_clocks: &mut ClockTree, _new_config: CpuPllDivConfig) { +/// todo!() +/// } +/// +/// // APB_CLK +/// +/// fn enable_apb_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_apb_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: ApbClkConfig, +/// ) { +/// todo!() +/// } +/// +/// // CRYPTO_CLK +/// +/// fn enable_crypto_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_crypto_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: CryptoClkConfig, +/// ) { +/// todo!() +/// } +/// +/// // MSPI_CLK +/// +/// fn enable_mspi_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_mspi_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: MspiClkConfig, +/// ) { +/// todo!() +/// } +/// +/// // CPU_CLK +/// +/// fn configure_cpu_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: CpuClkConfig, +/// ) { +/// todo!() +/// } +/// +/// // PLL_40M +/// +/// fn enable_pll_40m_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // PLL_60M +/// +/// fn enable_pll_60m_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // PLL_80M +/// +/// fn enable_pll_80m_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // CPU_DIV2 +/// +/// fn enable_cpu_div2_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // RC_FAST_CLK_DIV_N +/// +/// fn enable_rc_fast_clk_div_n_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_rc_fast_clk_div_n_impl(_clocks: &mut ClockTree, _new_config: RcFastClkDivNConfig) { +/// todo!() +/// } +/// +/// // XTAL_DIV_CLK +/// +/// fn enable_xtal_div_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // RTC_SLOW_CLK +/// +/// fn enable_rtc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_rtc_slow_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: RtcSlowClkConfig, +/// ) { +/// todo!() +/// } +/// +/// // RTC_FAST_CLK +/// +/// fn enable_rtc_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_rtc_fast_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: RtcFastClkConfig, +/// ) { +/// todo!() +/// } +/// +/// // LOW_POWER_CLK +/// +/// fn enable_low_power_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_low_power_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: LowPowerClkConfig, +/// ) { +/// todo!() +/// } +/// +/// // TIMG0_FUNCTION_CLOCK +/// +/// fn enable_timg0_function_clock_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_timg0_function_clock_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: Timg0FunctionClockConfig, +/// ) { +/// todo!() +/// } +/// +/// // TIMG0_CALIBRATION_CLOCK +/// +/// fn enable_timg0_calibration_clock_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_timg0_calibration_clock_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: Timg0CalibrationClockConfig, +/// ) { +/// todo!() +/// } /// ``` macro_rules! define_clock_tree_types { () => { + /// Selects the output frequency of `XTL_CLK`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum XtlClkConfig { + /// 26 MHz + _26, + /// 40 MHz + _40, + } + impl XtlClkConfig { + pub fn value(&self) -> u32 { + match self { + XtlClkConfig::_26 => 26000000, + XtlClkConfig::_40 => 40000000, + } + } + } + /// The list of clock signals that the `SYSTEM_PRE_DIV_IN` multiplexer can output. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum SystemPreDivInConfig { + /// Selects `XTL_CLK`. + Xtal, + /// Selects `RC_FAST_CLK`. + RcFast, + } + /// Configures the `SYSTEM_PRE_DIV` clock divider. + /// + /// The output is calculated as `OUTPUT = SYSTEM_PRE_DIV_IN / (DIVISOR + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct SystemPreDivConfig(u32); + impl SystemPreDivConfig { + /// Creates a new divider configuration. + /// + /// # Panics + /// + /// Panics if the divisor value is outside the + /// valid range (0 ..= 1023). + pub const fn new(divisor: u32) -> Self { + ::core::assert!( + divisor >= 0u32 && divisor <= 1023u32, + "`SYSTEM_PRE_DIV` divisor value must be between 0 and 1023 (inclusive)." + ); + Self(divisor) + } + fn value(self) -> u32 { + self.0 + } + } + /// Configures the `CPU_PLL_DIV` clock divider. + /// + /// The output is calculated as `OUTPUT = PLL_CLK / DIVISOR`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum CpuPllDivConfig { + /// Selects `DIVISOR = 4`. + _4 = 4, + /// Selects `DIVISOR = 6`. + _6 = 6, + } + impl CpuPllDivConfig { + const fn new(raw: u32) -> Self { + match raw { + 4 => CpuPllDivConfig::_4, + 6 => CpuPllDivConfig::_6, + _ => ::core::panic!("Invalid CPU_PLL_DIV divider value"), + } + } + fn value(self) -> u32 { + match self { + CpuPllDivConfig::_4 => 4, + CpuPllDivConfig::_6 => 6, + } + } + } + /// The list of clock signals that the `APB_CLK` multiplexer can output. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum ApbClkConfig { + /// Selects `PLL_40M`. + Pll40m, + /// Selects `CPU_CLK`. + Cpu, + } + /// The list of clock signals that the `CRYPTO_CLK` multiplexer can output. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum CryptoClkConfig { + /// Selects `PLL_80M`. + Pll80m, + /// Selects `CPU_CLK`. + Cpu, + } + /// The list of clock signals that the `MSPI_CLK` multiplexer can output. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum MspiClkConfig { + /// Selects `CPU_DIV2`. + CpuDiv2, + /// Selects `CPU_CLK`. + Cpu, + } + /// The list of clock signals that the `CPU_CLK` multiplexer can output. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum CpuClkConfig { + /// Selects `SYSTEM_PRE_DIV`. + Xtal, + /// Selects `SYSTEM_PRE_DIV`. + RcFast, + /// Selects `CPU_PLL_DIV`. + Pll, + } + /// Configures the `RC_FAST_CLK_DIV_N` clock divider. + /// + /// The output is calculated as `OUTPUT = RC_FAST_CLK / (DIVISOR + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct RcFastClkDivNConfig(u32); + impl RcFastClkDivNConfig { + /// Creates a new divider configuration. + /// + /// # Panics + /// + /// Panics if the divisor value is outside the + /// valid range (0 ..= 3). + pub const fn new(divisor: u32) -> Self { + ::core::assert!( + divisor >= 0u32 && divisor <= 3u32, + "`RC_FAST_CLK_DIV_N` divisor value must be between 0 and 3 (inclusive)." + ); + Self(divisor) + } + fn value(self) -> u32 { + self.0 + } + } + /// The list of clock signals that the `RTC_SLOW_CLK` multiplexer can output. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum RtcSlowClkConfig { + /// Selects `OSC_SLOW_CLK`. + OscSlow, + /// Selects `RC_SLOW_CLK`. + RcSlow, + /// Selects `RC_FAST_DIV_CLK`. + RcFast, + } + /// The list of clock signals that the `RTC_FAST_CLK` multiplexer can output. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum RtcFastClkConfig { + /// Selects `XTAL_DIV_CLK`. + Xtal, + /// Selects `RC_FAST_CLK_DIV_N`. + Rc, + } + /// The list of clock signals that the `LOW_POWER_CLK` multiplexer can output. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum LowPowerClkConfig { + /// Selects `XTL_CLK`. + Xtal, + /// Selects `RC_FAST_CLK`. + RcFast, + /// Selects `OSC_SLOW_CLK`. + OscSlow, + /// Selects `RTC_SLOW_CLK`. + RtcSlow, + } + /// The list of clock signals that the `TIMG0_FUNCTION_CLOCK` multiplexer can output. + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum Timg0FunctionClockConfig { + /// Selects `XTL_CLK`. + XtalClk, + #[default] + /// Selects `PLL_40M`. + Pll40m, + } + /// The list of clock signals that the `TIMG0_CALIBRATION_CLOCK` multiplexer can output. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum Timg0CalibrationClockConfig { + /// Selects `RC_SLOW_CLK`. + RcSlowClk, + /// Selects `RC_FAST_DIV_CLK`. + RcFastDivClk, + /// Selects `OSC_SLOW_CLK`. + Osc32kClk, + } /// Represents the device's clock tree. - pub struct ClockTree {} + pub struct ClockTree { + xtl_clk: Option, + system_pre_div_in: Option, + system_pre_div: Option, + cpu_pll_div: Option, + apb_clk: Option, + crypto_clk: Option, + mspi_clk: Option, + cpu_clk: Option, + rc_fast_clk_div_n: Option, + rtc_slow_clk: Option, + rtc_fast_clk: Option, + low_power_clk: Option, + timg0_function_clock: Option, + timg0_calibration_clock: Option, + rc_fast_clk_refcount: u32, + osc_slow_clk_refcount: u32, + rc_slow_clk_refcount: u32, + rc_fast_div_clk_refcount: u32, + apb_clk_refcount: u32, + crypto_clk_refcount: u32, + mspi_clk_refcount: u32, + pll_40m_refcount: u32, + pll_60m_refcount: u32, + rtc_fast_clk_refcount: u32, + low_power_clk_refcount: u32, + timg0_function_clock_refcount: u32, + timg0_calibration_clock_refcount: u32, + } impl ClockTree { /// Locks the clock tree for exclusive access. pub fn with(f: impl FnOnce(&mut ClockTree) -> R) -> R { @@ -232,7 +683,686 @@ macro_rules! define_clock_tree_types { } } static CLOCK_TREE: ::esp_sync::NonReentrantMutex = - ::esp_sync::NonReentrantMutex::new(ClockTree {}); + ::esp_sync::NonReentrantMutex::new(ClockTree { + xtl_clk: None, + system_pre_div_in: None, + system_pre_div: None, + cpu_pll_div: None, + apb_clk: None, + crypto_clk: None, + mspi_clk: None, + cpu_clk: None, + rc_fast_clk_div_n: None, + rtc_slow_clk: None, + rtc_fast_clk: None, + low_power_clk: None, + timg0_function_clock: None, + timg0_calibration_clock: None, + rc_fast_clk_refcount: 0, + osc_slow_clk_refcount: 0, + rc_slow_clk_refcount: 0, + rc_fast_div_clk_refcount: 0, + apb_clk_refcount: 0, + crypto_clk_refcount: 0, + mspi_clk_refcount: 0, + pll_40m_refcount: 0, + pll_60m_refcount: 0, + rtc_fast_clk_refcount: 0, + low_power_clk_refcount: 0, + timg0_function_clock_refcount: 0, + timg0_calibration_clock_refcount: 0, + }); + pub fn configure_xtl_clk(clocks: &mut ClockTree, config: XtlClkConfig) { + clocks.xtl_clk = Some(config); + configure_xtl_clk_impl(clocks, config); + } + fn request_xtl_clk(_clocks: &mut ClockTree) {} + fn release_xtl_clk(_clocks: &mut ClockTree) {} + pub fn xtl_clk_frequency(clocks: &mut ClockTree) -> u32 { + unwrap!(clocks.xtl_clk).value() + } + pub fn request_pll_clk(clocks: &mut ClockTree) { + request_xtl_clk(clocks); + enable_pll_clk_impl(clocks, true); + } + pub fn release_pll_clk(clocks: &mut ClockTree) { + enable_pll_clk_impl(clocks, false); + release_xtl_clk(clocks); + } + pub fn pll_clk_frequency(clocks: &mut ClockTree) -> u32 { + 480000000 + } + pub fn request_rc_fast_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.rc_fast_clk_refcount) { + enable_rc_fast_clk_impl(clocks, true); + } + } + pub fn release_rc_fast_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.rc_fast_clk_refcount) { + enable_rc_fast_clk_impl(clocks, false); + } + } + pub fn rc_fast_clk_frequency(clocks: &mut ClockTree) -> u32 { + 17500000 + } + pub fn request_osc_slow_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.osc_slow_clk_refcount) { + enable_osc_slow_clk_impl(clocks, true); + } + } + pub fn release_osc_slow_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.osc_slow_clk_refcount) { + enable_osc_slow_clk_impl(clocks, false); + } + } + pub fn osc_slow_clk_frequency(clocks: &mut ClockTree) -> u32 { + 32768 + } + pub fn request_rc_slow_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.rc_slow_clk_refcount) { + enable_rc_slow_clk_impl(clocks, true); + } + } + pub fn release_rc_slow_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.rc_slow_clk_refcount) { + enable_rc_slow_clk_impl(clocks, false); + } + } + pub fn rc_slow_clk_frequency(clocks: &mut ClockTree) -> u32 { + 136000 + } + pub fn request_rc_fast_div_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.rc_fast_div_clk_refcount) { + request_rc_fast_clk(clocks); + enable_rc_fast_div_clk_impl(clocks, true); + } + } + pub fn release_rc_fast_div_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.rc_fast_div_clk_refcount) { + enable_rc_fast_div_clk_impl(clocks, false); + release_rc_fast_clk(clocks); + } + } + pub fn rc_fast_div_clk_frequency(clocks: &mut ClockTree) -> u32 { + (rc_fast_clk_frequency(clocks) / 256) + } + pub fn configure_system_pre_div_in( + clocks: &mut ClockTree, + new_selector: SystemPreDivInConfig, + ) { + let old_selector = clocks.system_pre_div_in.replace(new_selector); + system_pre_div_in_request_upstream(clocks, new_selector); + configure_system_pre_div_in_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + system_pre_div_in_release_upstream(clocks, old_selector); + } + } + fn system_pre_div_in_request_upstream( + clocks: &mut ClockTree, + selector: SystemPreDivInConfig, + ) { + match selector { + SystemPreDivInConfig::Xtal => request_xtl_clk(clocks), + SystemPreDivInConfig::RcFast => request_rc_fast_clk(clocks), + } + } + fn system_pre_div_in_release_upstream( + clocks: &mut ClockTree, + selector: SystemPreDivInConfig, + ) { + match selector { + SystemPreDivInConfig::Xtal => release_xtl_clk(clocks), + SystemPreDivInConfig::RcFast => release_rc_fast_clk(clocks), + } + } + pub fn request_system_pre_div_in(clocks: &mut ClockTree) { + let selector = unwrap!(clocks.system_pre_div_in); + system_pre_div_in_request_upstream(clocks, selector); + enable_system_pre_div_in_impl(clocks, true); + } + pub fn release_system_pre_div_in(clocks: &mut ClockTree) { + enable_system_pre_div_in_impl(clocks, false); + let selector = unwrap!(clocks.system_pre_div_in); + system_pre_div_in_release_upstream(clocks, selector); + } + pub fn system_pre_div_in_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.system_pre_div_in) { + SystemPreDivInConfig::Xtal => xtl_clk_frequency(clocks), + SystemPreDivInConfig::RcFast => rc_fast_clk_frequency(clocks), + } + } + pub fn configure_system_pre_div(clocks: &mut ClockTree, config: SystemPreDivConfig) { + clocks.system_pre_div = Some(config); + configure_system_pre_div_impl(clocks, config); + } + pub fn request_system_pre_div(clocks: &mut ClockTree) { + request_system_pre_div_in(clocks); + enable_system_pre_div_impl(clocks, true); + } + pub fn release_system_pre_div(clocks: &mut ClockTree) { + enable_system_pre_div_impl(clocks, false); + release_system_pre_div_in(clocks); + } + pub fn system_pre_div_frequency(clocks: &mut ClockTree) -> u32 { + (system_pre_div_in_frequency(clocks) / (unwrap!(clocks.system_pre_div).value() + 1)) + } + pub fn configure_cpu_pll_div(clocks: &mut ClockTree, config: CpuPllDivConfig) { + clocks.cpu_pll_div = Some(config); + configure_cpu_pll_div_impl(clocks, config); + } + pub fn request_cpu_pll_div(clocks: &mut ClockTree) { + request_pll_clk(clocks); + enable_cpu_pll_div_impl(clocks, true); + } + pub fn release_cpu_pll_div(clocks: &mut ClockTree) { + enable_cpu_pll_div_impl(clocks, false); + release_pll_clk(clocks); + } + pub fn cpu_pll_div_frequency(clocks: &mut ClockTree) -> u32 { + (pll_clk_frequency(clocks) / unwrap!(clocks.cpu_pll_div).value()) + } + pub fn configure_apb_clk(clocks: &mut ClockTree, new_selector: ApbClkConfig) { + let old_selector = clocks.apb_clk.replace(new_selector); + if clocks.apb_clk_refcount > 0 { + apb_clk_request_upstream(clocks, new_selector); + configure_apb_clk_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + apb_clk_release_upstream(clocks, old_selector); + } + } else { + configure_apb_clk_impl(clocks, old_selector, new_selector); + } + } + fn apb_clk_request_upstream(clocks: &mut ClockTree, selector: ApbClkConfig) { + match selector { + ApbClkConfig::Pll40m => request_pll_40m(clocks), + ApbClkConfig::Cpu => request_cpu_clk(clocks), + } + } + fn apb_clk_release_upstream(clocks: &mut ClockTree, selector: ApbClkConfig) { + match selector { + ApbClkConfig::Pll40m => release_pll_40m(clocks), + ApbClkConfig::Cpu => release_cpu_clk(clocks), + } + } + pub fn request_apb_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.apb_clk_refcount) { + let selector = unwrap!(clocks.apb_clk); + apb_clk_request_upstream(clocks, selector); + enable_apb_clk_impl(clocks, true); + } + } + pub fn release_apb_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.apb_clk_refcount) { + enable_apb_clk_impl(clocks, false); + let selector = unwrap!(clocks.apb_clk); + apb_clk_release_upstream(clocks, selector); + } + } + pub fn apb_clk_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.apb_clk) { + ApbClkConfig::Pll40m => pll_40m_frequency(clocks), + ApbClkConfig::Cpu => cpu_clk_frequency(clocks), + } + } + pub fn configure_crypto_clk(clocks: &mut ClockTree, new_selector: CryptoClkConfig) { + let old_selector = clocks.crypto_clk.replace(new_selector); + if clocks.crypto_clk_refcount > 0 { + crypto_clk_request_upstream(clocks, new_selector); + configure_crypto_clk_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + crypto_clk_release_upstream(clocks, old_selector); + } + } else { + configure_crypto_clk_impl(clocks, old_selector, new_selector); + } + } + fn crypto_clk_request_upstream(clocks: &mut ClockTree, selector: CryptoClkConfig) { + match selector { + CryptoClkConfig::Pll80m => request_pll_80m(clocks), + CryptoClkConfig::Cpu => request_cpu_clk(clocks), + } + } + fn crypto_clk_release_upstream(clocks: &mut ClockTree, selector: CryptoClkConfig) { + match selector { + CryptoClkConfig::Pll80m => release_pll_80m(clocks), + CryptoClkConfig::Cpu => release_cpu_clk(clocks), + } + } + pub fn request_crypto_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.crypto_clk_refcount) { + let selector = unwrap!(clocks.crypto_clk); + crypto_clk_request_upstream(clocks, selector); + enable_crypto_clk_impl(clocks, true); + } + } + pub fn release_crypto_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.crypto_clk_refcount) { + enable_crypto_clk_impl(clocks, false); + let selector = unwrap!(clocks.crypto_clk); + crypto_clk_release_upstream(clocks, selector); + } + } + pub fn crypto_clk_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.crypto_clk) { + CryptoClkConfig::Pll80m => pll_80m_frequency(clocks), + CryptoClkConfig::Cpu => cpu_clk_frequency(clocks), + } + } + pub fn configure_mspi_clk(clocks: &mut ClockTree, new_selector: MspiClkConfig) { + let old_selector = clocks.mspi_clk.replace(new_selector); + if clocks.mspi_clk_refcount > 0 { + mspi_clk_request_upstream(clocks, new_selector); + configure_mspi_clk_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + mspi_clk_release_upstream(clocks, old_selector); + } + } else { + configure_mspi_clk_impl(clocks, old_selector, new_selector); + } + } + fn mspi_clk_request_upstream(clocks: &mut ClockTree, selector: MspiClkConfig) { + match selector { + MspiClkConfig::CpuDiv2 => request_cpu_div2(clocks), + MspiClkConfig::Cpu => request_cpu_clk(clocks), + } + } + fn mspi_clk_release_upstream(clocks: &mut ClockTree, selector: MspiClkConfig) { + match selector { + MspiClkConfig::CpuDiv2 => release_cpu_div2(clocks), + MspiClkConfig::Cpu => release_cpu_clk(clocks), + } + } + pub fn request_mspi_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.mspi_clk_refcount) { + let selector = unwrap!(clocks.mspi_clk); + mspi_clk_request_upstream(clocks, selector); + enable_mspi_clk_impl(clocks, true); + } + } + pub fn release_mspi_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.mspi_clk_refcount) { + enable_mspi_clk_impl(clocks, false); + let selector = unwrap!(clocks.mspi_clk); + mspi_clk_release_upstream(clocks, selector); + } + } + pub fn mspi_clk_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.mspi_clk) { + MspiClkConfig::CpuDiv2 => cpu_div2_frequency(clocks), + MspiClkConfig::Cpu => cpu_clk_frequency(clocks), + } + } + pub fn configure_cpu_clk(clocks: &mut ClockTree, new_selector: CpuClkConfig) { + let old_selector = clocks.cpu_clk.replace(new_selector); + match new_selector { + CpuClkConfig::Xtal => { + configure_apb_clk(clocks, ApbClkConfig::Cpu); + configure_crypto_clk(clocks, CryptoClkConfig::Cpu); + configure_mspi_clk(clocks, MspiClkConfig::Cpu); + configure_system_pre_div_in(clocks, SystemPreDivInConfig::Xtal); + } + CpuClkConfig::RcFast => { + configure_apb_clk(clocks, ApbClkConfig::Cpu); + configure_crypto_clk(clocks, CryptoClkConfig::Cpu); + configure_mspi_clk(clocks, MspiClkConfig::Cpu); + configure_system_pre_div_in(clocks, SystemPreDivInConfig::RcFast); + } + CpuClkConfig::Pll => { + configure_apb_clk(clocks, ApbClkConfig::Pll40m); + configure_crypto_clk(clocks, CryptoClkConfig::Pll80m); + configure_mspi_clk(clocks, MspiClkConfig::CpuDiv2); + } + } + cpu_clk_request_upstream(clocks, new_selector); + configure_cpu_clk_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + cpu_clk_release_upstream(clocks, old_selector); + } + } + fn cpu_clk_request_upstream(clocks: &mut ClockTree, selector: CpuClkConfig) { + match selector { + CpuClkConfig::Xtal => request_system_pre_div(clocks), + CpuClkConfig::RcFast => request_system_pre_div(clocks), + CpuClkConfig::Pll => request_cpu_pll_div(clocks), + } + } + fn cpu_clk_release_upstream(clocks: &mut ClockTree, selector: CpuClkConfig) { + match selector { + CpuClkConfig::Xtal => release_system_pre_div(clocks), + CpuClkConfig::RcFast => release_system_pre_div(clocks), + CpuClkConfig::Pll => release_cpu_pll_div(clocks), + } + } + fn request_cpu_clk(_clocks: &mut ClockTree) {} + fn release_cpu_clk(_clocks: &mut ClockTree) {} + pub fn cpu_clk_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.cpu_clk) { + CpuClkConfig::Xtal => system_pre_div_frequency(clocks), + CpuClkConfig::RcFast => system_pre_div_frequency(clocks), + CpuClkConfig::Pll => cpu_pll_div_frequency(clocks), + } + } + pub fn request_pll_40m(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.pll_40m_refcount) { + request_cpu_clk(clocks); + enable_pll_40m_impl(clocks, true); + } + } + pub fn release_pll_40m(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.pll_40m_refcount) { + enable_pll_40m_impl(clocks, false); + release_cpu_clk(clocks); + } + } + pub fn pll_40m_frequency(clocks: &mut ClockTree) -> u32 { + 40000000 + } + pub fn request_pll_60m(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.pll_60m_refcount) { + request_cpu_clk(clocks); + enable_pll_60m_impl(clocks, true); + } + } + pub fn release_pll_60m(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.pll_60m_refcount) { + enable_pll_60m_impl(clocks, false); + release_cpu_clk(clocks); + } + } + pub fn pll_60m_frequency(clocks: &mut ClockTree) -> u32 { + 60000000 + } + pub fn request_pll_80m(clocks: &mut ClockTree) { + request_cpu_clk(clocks); + enable_pll_80m_impl(clocks, true); + } + pub fn release_pll_80m(clocks: &mut ClockTree) { + enable_pll_80m_impl(clocks, false); + release_cpu_clk(clocks); + } + pub fn pll_80m_frequency(clocks: &mut ClockTree) -> u32 { + 80000000 + } + pub fn request_cpu_div2(clocks: &mut ClockTree) { + request_cpu_clk(clocks); + enable_cpu_div2_impl(clocks, true); + } + pub fn release_cpu_div2(clocks: &mut ClockTree) { + enable_cpu_div2_impl(clocks, false); + release_cpu_clk(clocks); + } + pub fn cpu_div2_frequency(clocks: &mut ClockTree) -> u32 { + (cpu_clk_frequency(clocks) / 2) + } + pub fn configure_rc_fast_clk_div_n(clocks: &mut ClockTree, config: RcFastClkDivNConfig) { + clocks.rc_fast_clk_div_n = Some(config); + configure_rc_fast_clk_div_n_impl(clocks, config); + } + pub fn request_rc_fast_clk_div_n(clocks: &mut ClockTree) { + request_rc_fast_clk(clocks); + enable_rc_fast_clk_div_n_impl(clocks, true); + } + pub fn release_rc_fast_clk_div_n(clocks: &mut ClockTree) { + enable_rc_fast_clk_div_n_impl(clocks, false); + release_rc_fast_clk(clocks); + } + pub fn rc_fast_clk_div_n_frequency(clocks: &mut ClockTree) -> u32 { + (rc_fast_clk_frequency(clocks) / (unwrap!(clocks.rc_fast_clk_div_n).value() + 1)) + } + pub fn request_xtal_div_clk(clocks: &mut ClockTree) { + request_xtl_clk(clocks); + enable_xtal_div_clk_impl(clocks, true); + } + pub fn release_xtal_div_clk(clocks: &mut ClockTree) { + enable_xtal_div_clk_impl(clocks, false); + release_xtl_clk(clocks); + } + pub fn xtal_div_clk_frequency(clocks: &mut ClockTree) -> u32 { + (xtl_clk_frequency(clocks) / 2) + } + pub fn configure_rtc_slow_clk(clocks: &mut ClockTree, new_selector: RtcSlowClkConfig) { + let old_selector = clocks.rtc_slow_clk.replace(new_selector); + rtc_slow_clk_request_upstream(clocks, new_selector); + configure_rtc_slow_clk_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + rtc_slow_clk_release_upstream(clocks, old_selector); + } + } + fn rtc_slow_clk_request_upstream(clocks: &mut ClockTree, selector: RtcSlowClkConfig) { + match selector { + RtcSlowClkConfig::OscSlow => request_osc_slow_clk(clocks), + RtcSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => request_rc_fast_div_clk(clocks), + } + } + fn rtc_slow_clk_release_upstream(clocks: &mut ClockTree, selector: RtcSlowClkConfig) { + match selector { + RtcSlowClkConfig::OscSlow => release_osc_slow_clk(clocks), + RtcSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => release_rc_fast_div_clk(clocks), + } + } + pub fn request_rtc_slow_clk(clocks: &mut ClockTree) { + let selector = unwrap!(clocks.rtc_slow_clk); + rtc_slow_clk_request_upstream(clocks, selector); + enable_rtc_slow_clk_impl(clocks, true); + } + pub fn release_rtc_slow_clk(clocks: &mut ClockTree) { + enable_rtc_slow_clk_impl(clocks, false); + let selector = unwrap!(clocks.rtc_slow_clk); + rtc_slow_clk_release_upstream(clocks, selector); + } + pub fn rtc_slow_clk_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.rtc_slow_clk) { + RtcSlowClkConfig::OscSlow => osc_slow_clk_frequency(clocks), + RtcSlowClkConfig::RcSlow => rc_slow_clk_frequency(clocks), + RtcSlowClkConfig::RcFast => rc_fast_div_clk_frequency(clocks), + } + } + pub fn configure_rtc_fast_clk(clocks: &mut ClockTree, new_selector: RtcFastClkConfig) { + let old_selector = clocks.rtc_fast_clk.replace(new_selector); + if clocks.rtc_fast_clk_refcount > 0 { + rtc_fast_clk_request_upstream(clocks, new_selector); + configure_rtc_fast_clk_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + rtc_fast_clk_release_upstream(clocks, old_selector); + } + } else { + configure_rtc_fast_clk_impl(clocks, old_selector, new_selector); + } + } + fn rtc_fast_clk_request_upstream(clocks: &mut ClockTree, selector: RtcFastClkConfig) { + match selector { + RtcFastClkConfig::Xtal => request_xtal_div_clk(clocks), + RtcFastClkConfig::Rc => request_rc_fast_clk_div_n(clocks), + } + } + fn rtc_fast_clk_release_upstream(clocks: &mut ClockTree, selector: RtcFastClkConfig) { + match selector { + RtcFastClkConfig::Xtal => release_xtal_div_clk(clocks), + RtcFastClkConfig::Rc => release_rc_fast_clk_div_n(clocks), + } + } + pub fn request_rtc_fast_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.rtc_fast_clk_refcount) { + let selector = unwrap!(clocks.rtc_fast_clk); + rtc_fast_clk_request_upstream(clocks, selector); + enable_rtc_fast_clk_impl(clocks, true); + } + } + pub fn release_rtc_fast_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.rtc_fast_clk_refcount) { + enable_rtc_fast_clk_impl(clocks, false); + let selector = unwrap!(clocks.rtc_fast_clk); + rtc_fast_clk_release_upstream(clocks, selector); + } + } + pub fn rtc_fast_clk_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.rtc_fast_clk) { + RtcFastClkConfig::Xtal => xtal_div_clk_frequency(clocks), + RtcFastClkConfig::Rc => rc_fast_clk_div_n_frequency(clocks), + } + } + pub fn configure_low_power_clk(clocks: &mut ClockTree, new_selector: LowPowerClkConfig) { + let old_selector = clocks.low_power_clk.replace(new_selector); + if clocks.low_power_clk_refcount > 0 { + low_power_clk_request_upstream(clocks, new_selector); + configure_low_power_clk_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + low_power_clk_release_upstream(clocks, old_selector); + } + } else { + configure_low_power_clk_impl(clocks, old_selector, new_selector); + } + } + fn low_power_clk_request_upstream(clocks: &mut ClockTree, selector: LowPowerClkConfig) { + match selector { + LowPowerClkConfig::Xtal => request_xtl_clk(clocks), + LowPowerClkConfig::RcFast => request_rc_fast_clk(clocks), + LowPowerClkConfig::OscSlow => request_osc_slow_clk(clocks), + LowPowerClkConfig::RtcSlow => request_rtc_slow_clk(clocks), + } + } + fn low_power_clk_release_upstream(clocks: &mut ClockTree, selector: LowPowerClkConfig) { + match selector { + LowPowerClkConfig::Xtal => release_xtl_clk(clocks), + LowPowerClkConfig::RcFast => release_rc_fast_clk(clocks), + LowPowerClkConfig::OscSlow => release_osc_slow_clk(clocks), + LowPowerClkConfig::RtcSlow => release_rtc_slow_clk(clocks), + } + } + pub fn request_low_power_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.low_power_clk_refcount) { + let selector = unwrap!(clocks.low_power_clk); + low_power_clk_request_upstream(clocks, selector); + enable_low_power_clk_impl(clocks, true); + } + } + pub fn release_low_power_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.low_power_clk_refcount) { + enable_low_power_clk_impl(clocks, false); + let selector = unwrap!(clocks.low_power_clk); + low_power_clk_release_upstream(clocks, selector); + } + } + pub fn low_power_clk_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.low_power_clk) { + LowPowerClkConfig::Xtal => xtl_clk_frequency(clocks), + LowPowerClkConfig::RcFast => rc_fast_clk_frequency(clocks), + LowPowerClkConfig::OscSlow => osc_slow_clk_frequency(clocks), + LowPowerClkConfig::RtcSlow => rtc_slow_clk_frequency(clocks), + } + } + pub fn configure_timg0_function_clock( + clocks: &mut ClockTree, + new_selector: Timg0FunctionClockConfig, + ) { + let old_selector = clocks.timg0_function_clock.replace(new_selector); + if clocks.timg0_function_clock_refcount > 0 { + timg0_function_clock_request_upstream(clocks, new_selector); + configure_timg0_function_clock_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + timg0_function_clock_release_upstream(clocks, old_selector); + } + } else { + configure_timg0_function_clock_impl(clocks, old_selector, new_selector); + } + } + fn timg0_function_clock_request_upstream( + clocks: &mut ClockTree, + selector: Timg0FunctionClockConfig, + ) { + match selector { + Timg0FunctionClockConfig::XtalClk => request_xtl_clk(clocks), + Timg0FunctionClockConfig::Pll40m => request_pll_40m(clocks), + } + } + fn timg0_function_clock_release_upstream( + clocks: &mut ClockTree, + selector: Timg0FunctionClockConfig, + ) { + match selector { + Timg0FunctionClockConfig::XtalClk => release_xtl_clk(clocks), + Timg0FunctionClockConfig::Pll40m => release_pll_40m(clocks), + } + } + pub fn request_timg0_function_clock(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.timg0_function_clock_refcount) { + let selector = unwrap!(clocks.timg0_function_clock); + timg0_function_clock_request_upstream(clocks, selector); + enable_timg0_function_clock_impl(clocks, true); + } + } + pub fn release_timg0_function_clock(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.timg0_function_clock_refcount) { + enable_timg0_function_clock_impl(clocks, false); + let selector = unwrap!(clocks.timg0_function_clock); + timg0_function_clock_release_upstream(clocks, selector); + } + } + pub fn timg0_function_clock_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.timg0_function_clock) { + Timg0FunctionClockConfig::XtalClk => xtl_clk_frequency(clocks), + Timg0FunctionClockConfig::Pll40m => pll_40m_frequency(clocks), + } + } + pub fn configure_timg0_calibration_clock( + clocks: &mut ClockTree, + new_selector: Timg0CalibrationClockConfig, + ) { + let old_selector = clocks.timg0_calibration_clock.replace(new_selector); + if clocks.timg0_calibration_clock_refcount > 0 { + timg0_calibration_clock_request_upstream(clocks, new_selector); + configure_timg0_calibration_clock_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + timg0_calibration_clock_release_upstream(clocks, old_selector); + } + } else { + configure_timg0_calibration_clock_impl(clocks, old_selector, new_selector); + } + } + fn timg0_calibration_clock_request_upstream( + clocks: &mut ClockTree, + selector: Timg0CalibrationClockConfig, + ) { + match selector { + Timg0CalibrationClockConfig::RcSlowClk => request_rc_slow_clk(clocks), + Timg0CalibrationClockConfig::RcFastDivClk => request_rc_fast_div_clk(clocks), + Timg0CalibrationClockConfig::Osc32kClk => request_osc_slow_clk(clocks), + } + } + fn timg0_calibration_clock_release_upstream( + clocks: &mut ClockTree, + selector: Timg0CalibrationClockConfig, + ) { + match selector { + Timg0CalibrationClockConfig::RcSlowClk => release_rc_slow_clk(clocks), + Timg0CalibrationClockConfig::RcFastDivClk => release_rc_fast_div_clk(clocks), + Timg0CalibrationClockConfig::Osc32kClk => release_osc_slow_clk(clocks), + } + } + pub fn request_timg0_calibration_clock(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.timg0_calibration_clock_refcount) { + let selector = unwrap!(clocks.timg0_calibration_clock); + timg0_calibration_clock_request_upstream(clocks, selector); + enable_timg0_calibration_clock_impl(clocks, true); + } + } + pub fn release_timg0_calibration_clock(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.timg0_calibration_clock_refcount) { + enable_timg0_calibration_clock_impl(clocks, false); + let selector = unwrap!(clocks.timg0_calibration_clock); + timg0_calibration_clock_release_upstream(clocks, selector); + } + } + pub fn timg0_calibration_clock_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.timg0_calibration_clock) { + Timg0CalibrationClockConfig::RcSlowClk => rc_slow_clk_frequency(clocks), + Timg0CalibrationClockConfig::RcFastDivClk => rc_fast_div_clk_frequency(clocks), + Timg0CalibrationClockConfig::Osc32kClk => osc_slow_clk_frequency(clocks), + } + } /// Clock tree configuration. /// /// The fields of this struct are optional, with the following caveats: @@ -242,10 +1372,52 @@ macro_rules! define_clock_tree_types { /// - Other unspecified clock sources will not be useable by peripherals. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub struct ClockConfig {} + pub struct ClockConfig { + /// `XTL_CLK` configuration. + pub xtl_clk: Option, + /// `SYSTEM_PRE_DIV` configuration. + pub system_pre_div: Option, + /// `CPU_PLL_DIV` configuration. + pub cpu_pll_div: Option, + /// `CPU_CLK` configuration. + pub cpu_clk: Option, + /// `RC_FAST_CLK_DIV_N` configuration. + pub rc_fast_clk_div_n: Option, + /// `RTC_SLOW_CLK` configuration. + pub rtc_slow_clk: Option, + /// `RTC_FAST_CLK` configuration. + pub rtc_fast_clk: Option, + /// `LOW_POWER_CLK` configuration. + pub low_power_clk: Option, + } impl ClockConfig { fn apply(&self) { - ClockTree::with(|clocks| {}); + ClockTree::with(|clocks| { + if let Some(config) = self.xtl_clk { + configure_xtl_clk(clocks, config); + } + if let Some(config) = self.system_pre_div { + configure_system_pre_div(clocks, config); + } + if let Some(config) = self.cpu_pll_div { + configure_cpu_pll_div(clocks, config); + } + if let Some(config) = self.cpu_clk { + configure_cpu_clk(clocks, config); + } + if let Some(config) = self.rc_fast_clk_div_n { + configure_rc_fast_clk_div_n(clocks, config); + } + if let Some(config) = self.rtc_slow_clk { + configure_rtc_slow_clk(clocks, config); + } + if let Some(config) = self.rtc_fast_clk { + configure_rtc_fast_clk(clocks, config); + } + if let Some(config) = self.low_power_clk { + configure_low_power_clk(clocks, config); + } + }); } } fn increment_reference_count(refcount: &mut u32) -> bool { diff --git a/esp-metadata/devices/esp32.toml b/esp-metadata/devices/esp32.toml index 87bcc749a91..48a8c6faa0a 100644 --- a/esp-metadata/devices/esp32.toml +++ b/esp-metadata/devices/esp32.toml @@ -247,6 +247,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "rst_field", value = "{{peripheral}}_rst" }, # {{peripheral}} is derived automatically (e.g. SpiDma -> spi_dma) ], peripheral_clocks = [ + # TODO: peripheral_clocks are implicit APB clock gates, we will need to tie them into the system to track APB use. { name = "Aes", template_params = { reg_group = "peri", peripheral = "crypto_aes" } }, { name = "Rsa", template_params = { reg_group = "peri", peripheral = "crypto_rsa" } }, { name = "Sha", template_params = { reg_group = "peri", peripheral = "crypto_sha" } }, diff --git a/esp-metadata/devices/esp32c2.toml b/esp-metadata/devices/esp32c2.toml index 8dda62602f3..81af3623ab4 100644 --- a/esp-metadata/devices/esp32c2.toml +++ b/esp-metadata/devices/esp32c2.toml @@ -89,7 +89,73 @@ memory_map = { ranges = [ ] } -clocks = { peripheral_clocks = { templates = [ +clocks = { system_clocks = { clock_tree = [ + # High-speed clock sources + { name = "XTL_CLK", type = "source", values = "26, 40", output = "VALUE * 1_000_000", always_on = true }, + { name = "PLL_CLK", type = "derived", from = "XTL_CLK", output = "480_000_000" }, + { name = "RC_FAST_CLK", type = "source", output = "17_500_000" }, + + # Low-speed clocks + { name = "OSC_SLOW_CLK", type = "source", output = "32768" }, # external slow clock from GPIO0 + { name = "RC_SLOW_CLK", type = "source", output = "136_000" }, # adjustable via RTC_CNTL_ANA_CLK_DIV + { name = "RC_FAST_DIV_CLK", type = "divider", output = "RC_FAST_CLK / 256" }, # 256 is just the default value? RTC_CNTL_FOSC_DIV + + # CPU clock source dividers + { name = "SYSTEM_PRE_DIV_IN", type = "mux", variants = [ + { name = "XTAL", outputs = "XTL_CLK" }, + { name = "RC_FAST", outputs = "RC_FAST_CLK" }, + ] }, + { name = "SYSTEM_PRE_DIV", type = "divider", divisors = "0 .. 1024", output = "SYSTEM_PRE_DIV_IN / (DIVISOR + 1)" }, + { name = "CPU_PLL_DIV", type = "divider", divisors = "4, 6", output = "PLL_CLK / DIVISOR" }, + + # The meta-switch + { name = "CPU_CLK", type = "mux", always_on = true, variants = [ + # source clock CPU clock additional config (mux = variant name, divider = divisor expression) + { name = "XTAL", outputs = "SYSTEM_PRE_DIV", configures = [ "APB_CLK = CPU", "CRYPTO_CLK = CPU", "MSPI_CLK = CPU", "SYSTEM_PRE_DIV_IN = XTAL" ] }, + { name = "RC_FAST", outputs = "SYSTEM_PRE_DIV", configures = [ "APB_CLK = CPU", "CRYPTO_CLK = CPU", "MSPI_CLK = CPU", "SYSTEM_PRE_DIV_IN = RC_FAST" ] }, + { name = "PLL", outputs = "CPU_PLL_DIV", configures = [ "APB_CLK = PLL_40M", "CRYPTO_CLK = PLL_80M", "MSPI_CLK = CPU_DIV2" ] }, + ] }, + + # CPU-dependent clock signals + { name = "PLL_40M", type = "derived", from = "CPU_CLK", output = "40_000_000" }, # PLL / 12 + { name = "PLL_60M", type = "derived", from = "CPU_CLK", output = "60_000_000" }, # PLL / 8 + { name = "PLL_80M", type = "derived", from = "CPU_CLK", output = "80_000_000" }, # PLL / 6 + { name = "CPU_DIV2", type = "divider", output = "CPU_CLK / 2" }, + + { name = "APB_CLK", type = "mux", variants = [ + { name = "PLL_40M", outputs = "PLL_40M" }, + { name = "CPU", outputs = "CPU_CLK" }, + ] }, + { name = "CRYPTO_CLK", type = "mux", variants = [ + { name = "PLL_80M", outputs = "PLL_80M" }, + { name = "CPU", outputs = "CPU_CLK" }, + ] }, + { name = "MSPI_CLK", type = "mux", variants = [ + { name = "CPU_DIV2", outputs = "CPU_DIV2" }, + { name = "CPU", outputs = "CPU_CLK" }, + ] }, + + # Low-power clocks + { name = "RC_FAST_CLK_DIV_N", type = "divider", divisors = "0 ..= 3", output = "RC_FAST_CLK / (DIVISOR + 1)" }, # RC_DIV + { name = "XTAL_DIV_CLK", type = "divider", output = "XTL_CLK / 2" }, # XTAL_DIV + { name = "RTC_SLOW_CLK", type = "mux", variants = [ # RTC_CNTL_ANA_CLK_RTC_SEL + { name = "OSC_SLOW", outputs = "OSC_SLOW_CLK" }, + { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, + { name = "RC_FAST", outputs = "RC_FAST_DIV_CLK" }, + ] }, + { name = "RTC_FAST_CLK", type = "mux", variants = [ # RTC_CNTL_FAST_CLK_RTC_SEL + { name = "XTAL", outputs = "XTAL_DIV_CLK" }, + { name = "RC", outputs = "RC_FAST_CLK_DIV_N" }, + ] }, + + # Low-power wireless clock source + { name = "LOW_POWER_CLK", type = "mux", variants = [ + { name = "XTAL", outputs = "XTL_CLK" }, + { name = "RC_FAST", outputs = "RC_FAST_CLK" }, + { name = "OSC_SLOW", outputs = "OSC_SLOW_CLK" }, + { name = "RTC_SLOW", outputs = "RTC_SLOW_CLK" }, + ] } +] }, peripheral_clocks = { templates = [ # templates { name = "clk_en_template", value = "{{control}}::regs().{{clk_en_register}}().modify(|_, w| w.{{clk_en_field}}().bit(enable));" }, { name = "rst_template", value = "{{control}}::regs().{{rst_register}}().modify(|_, w| w.{{rst_field}}().bit(reset));" }, @@ -101,6 +167,7 @@ clocks = { peripheral_clocks = { templates = [ { name = "rst_register", value = "perip_rst_{{reg_group}}" }, { name = "rst_field", value = "{{peripheral}}_rst" }, ], peripheral_clocks = [ + # TODO: peripheral_clocks are implicit APB clock gates, we will need to tie them into the system to track APB use. { name = "Tsens", template_params = { reg_group = "en1" } }, { name = "Dma", template_params = { reg_group = "en1" } }, { name = "Sha", template_params = { reg_group = "en1", peripheral = "crypto_sha" } }, @@ -110,7 +177,17 @@ clocks = { peripheral_clocks = { templates = [ { name = "Systimer", keep_enabled = true }, { name = "ApbSarAdc", template_params = { peripheral = "apb_saradc" } }, { name = "UartMem", keep_enabled = true }, # TODO: keep_enabled can be removed once esp-println needs explicit initialization - { name = "Timg0", template_params = { peripheral = "timergroup" }, keep_enabled = true }, # used for clock calibration + { name = "Timg0", template_params = { peripheral = "timergroup" }, keep_enabled = true, clocks = [ + { name = "FUNCTION_CLOCK", type = "mux", default = "PLL_40M", variants = [ + { name = "XTAL_CLK", outputs = "XTL_CLK" }, + { name = "PLL_40M", outputs = "PLL_40M" }, + ] }, + { name = "CALIBRATION_CLOCK", type = "mux", variants = [ + { name = "RC_SLOW_CLK", outputs = "RC_SLOW_CLK" }, + { name = "RC_FAST_DIV_CLK", outputs = "RC_FAST_DIV_CLK" }, + { name = "OSC32K_CLK", outputs = "OSC_SLOW_CLK" }, + ] }, + ] }, { name = "Ledc" }, { name = "I2cExt0" }, { name = "Spi2" },