diff --git a/esp-hal/src/clock/clocks_ll/esp32h2.rs b/esp-hal/src/clock/clocks_ll/esp32h2.rs index 883d98568ea..cace1bc1d33 100644 --- a/esp-hal/src/clock/clocks_ll/esp32h2.rs +++ b/esp-hal/src/clock/clocks_ll/esp32h2.rs @@ -1,257 +1,4 @@ -use crate::{ - clock::{ApbClock, Clock, CpuClock, PllClock, XtalClock}, - peripherals::{I2C_ANA_MST, LP_AON, MODEM_LPCON, MODEM_SYSCON, PCR, PMU}, -}; - -const I2C_BBPLL: u8 = 0x66; -const I2C_BBPLL_HOSTID: u8 = 0; -const I2C_BBPLL_OC_REF_DIV: u8 = 2; -const I2C_BBPLL_OC_REF_DIV_MSB: u8 = 3; -const I2C_BBPLL_OC_REF_DIV_LSB: u8 = 0; - -const I2C_BBPLL_OC_DIV: u8 = 3; -const I2C_BBPLL_OC_DIV_MSB: u8 = 5; -const I2C_BBPLL_OC_DIV_LSB: u8 = 0; - -const I2C_BBPLL_OC_DHREF_SEL: u8 = 5; -const I2C_BBPLL_OC_DHREF_SEL_MSB: u8 = 5; -const I2C_BBPLL_OC_DHREF_SEL_LSB: u8 = 4; - -const I2C_BBPLL_OC_DLREF_SEL: u8 = 5; -const I2C_BBPLL_OC_DLREF_SEL_MSB: u8 = 7; -const I2C_BBPLL_OC_DLREF_SEL_LSB: u8 = 6; - -// May be needed for enabling I2C clock -const MODEM_LPCON_CLK_I2C_SEL_96M: u32 = 1 << 0; - -const REGI2C_BBPLL: u8 = 0x66; -const REGI2C_BIAS: u8 = 0x6a; -const REGI2C_PMU: u8 = 0x6d; -const REGI2C_ULP_CAL: u8 = 0x61; -const REGI2C_SAR_I2C: u8 = 0x69; - -const I2C_MST_ANA_CONF1_M: u32 = 0x00FFFFFF; - -pub(crate) fn esp32h2_rtc_bbpll_configure(_xtal_freq: XtalClock, _pll_freq: PllClock) { - // Enable I2C master clock - MODEM_LPCON::regs() - .clk_conf_force_on() - .modify(|_, w| w.clk_i2c_mst_fo().set_bit()); - - // Set I2C clock to 96MHz - MODEM_LPCON::regs() - .clk_conf() - .modify(|r, w| unsafe { w.bits(r.bits() | MODEM_LPCON_CLK_I2C_SEL_96M) }); - - // BPPLL calibration start - I2C_ANA_MST::regs().ana_conf0().modify(|_, w| { - w.bbpll_stop_force_high().clear_bit(); - w.bbpll_stop_force_low().set_bit() - }); - - let oc_ref_div = 0; - let oc_div = 1; - let oc_dhref_sel = 3; - let oc_dlref_sel = 1; - - regi2c_write_mask( - I2C_BBPLL, - I2C_BBPLL_HOSTID, - I2C_BBPLL_OC_REF_DIV, - I2C_BBPLL_OC_REF_DIV_MSB, - I2C_BBPLL_OC_REF_DIV_LSB, - oc_ref_div, - ); - - regi2c_write_mask( - I2C_BBPLL, - I2C_BBPLL_HOSTID, - I2C_BBPLL_OC_DIV, - I2C_BBPLL_OC_DIV_MSB, - I2C_BBPLL_OC_DIV_LSB, - oc_div, - ); - - regi2c_write_mask( - I2C_BBPLL, - I2C_BBPLL_HOSTID, - I2C_BBPLL_OC_DHREF_SEL, - I2C_BBPLL_OC_DHREF_SEL_MSB, - I2C_BBPLL_OC_DHREF_SEL_LSB, - oc_dhref_sel, - ); - - regi2c_write_mask( - I2C_BBPLL, - I2C_BBPLL_HOSTID, - I2C_BBPLL_OC_DLREF_SEL, - I2C_BBPLL_OC_DLREF_SEL_MSB, - I2C_BBPLL_OC_DLREF_SEL_LSB, - oc_dlref_sel, - ); - - // WAIT CALIBRATION DONE - while I2C_ANA_MST::regs() - .ana_conf0() - .read() - .cal_done() - .bit_is_clear() - {} - - // workaround bbpll calibration might stop early - crate::rom::ets_delay_us(10); - - // BBPLL CALIBRATION STOP - 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 esp32h2_rtc_bbpll_enable() { - PMU::regs().imm_hp_ck_power().modify(|_, w| { - w.tie_high_xpd_bb_i2c().set_bit(); - w.tie_high_xpd_bbpll().set_bit(); - w.tie_high_xpd_bbpll_i2c().set_bit() - }); - - PMU::regs() - .imm_hp_ck_power() - .modify(|_, w| w.tie_high_global_bbpll_icg().set_bit()); -} - -pub(crate) fn esp32h2_rtc_update_to_xtal(freq: XtalClock, div: u8) { - 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. - clk_ll_cpu_set_divider(div as u32); - clk_ll_ahb_set_divider(div as u32); - - // Switch clock source - PCR::regs() - .sysclk_conf() - .modify(|_, w| unsafe { w.soc_clk_sel().bits(0) }); - - clk_ll_bus_update(); -} - -pub(crate) fn esp32h2_rtc_freq_to_pll_mhz(cpu_clock_speed: CpuClock) { - let cpu_divider = 96 / cpu_clock_speed.mhz(); - clk_ll_cpu_set_divider(cpu_divider); - let ahb_divider = match cpu_divider { - 1 | 2 => cpu_divider + 2, - _ => cpu_divider, - }; - clk_ll_ahb_set_divider(ahb_divider); - - PCR::regs() - .sysclk_conf() - .modify(|_, w| unsafe { w.soc_clk_sel().bits(1) }); - - clk_ll_bus_update(); - - crate::rom::ets_update_cpu_frequency_rom(cpu_clock_speed.mhz()); -} - -pub(crate) fn esp32h2_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); - - LP_AON::regs() - .store5() - .modify(|_, w| unsafe { w.data().bits(value) }); -} - -fn clk_ll_cpu_set_divider(divider: u32) { - assert!(divider >= 1); - - PCR::regs() - .cpu_freq_conf() - .modify(|_, w| unsafe { w.cpu_div_num().bits((divider - 1) as u8) }); -} - -fn clk_ll_ahb_set_divider(divider: u32) { - assert!(divider >= 1); - - PCR::regs() - .ahb_freq_conf() - .modify(|_, w| unsafe { w.ahb_div_num().bits((divider - 1) as u8) }); -} - -fn clk_ll_bus_update() { - PCR::regs() - .bus_clk_update() - .modify(|_, w| w.bus_clock_update().bit(true)); - - // reg_get_bit - while PCR::regs() - .bus_clk_update() - .read() - .bus_clock_update() - .bit_is_set() - {} -} - -fn regi2c_enable_block(block: u8) -> usize { - MODEM_LPCON::regs() - .clk_conf() - .modify(|_, w| w.clk_i2c_mst_en().set_bit()); - - // Before config I2C register, enable corresponding slave. - let i2c_sel_bits = I2C_ANA_MST::regs().ana_conf2().read(); - let i2c_sel = match block { - v if v == REGI2C_BBPLL => i2c_sel_bits.bbpll_mst_sel().bit_is_set(), - v if v == REGI2C_BIAS => i2c_sel_bits.bias_mst_sel().bit_is_set(), - v if v == REGI2C_PMU => i2c_sel_bits.dig_reg_mst_sel().bit_is_set(), - v if v == REGI2C_ULP_CAL => i2c_sel_bits.ulp_cal_mst_sel().bit_is_set(), - v if v == REGI2C_SAR_I2C => i2c_sel_bits.sar_i2c_mst_sel().bit_is_set(), - _ => unreachable!(), - }; - I2C_ANA_MST::regs().ana_conf1().write(|w| unsafe { - w.bits(I2C_MST_ANA_CONF1_M); - match block { - v if v == REGI2C_BBPLL => w.bbpll_rd().clear_bit(), - v if v == REGI2C_BIAS => w.bias_rd().clear_bit(), - v if v == REGI2C_PMU => w.dig_reg_rd().clear_bit(), - v if v == REGI2C_ULP_CAL => w.ulp_cal_rd().clear_bit(), - v if v == REGI2C_SAR_I2C => w.sar_i2c_rd().clear_bit(), - _ => unreachable!(), - } - }); - - if i2c_sel { 0 } else { 1 } -} - -pub(crate) fn regi2c_write_mask(block: u8, _host_id: u8, reg_add: u8, msb: u8, lsb: u8, data: u8) { - assert!(msb < 8 + lsb); - let master = regi2c_enable_block(block); - - // Read the i2c bus register - I2C_ANA_MST::regs().i2c_ctrl(master).write(|w| unsafe { - w.slave_addr().bits(block); - w.slave_reg_addr().bits(reg_add) - }); - - while I2C_ANA_MST::regs().i2c_ctrl(master).read().busy().bit() {} - - // Example: LSB=2, MSB = 5 - // unwritten_bits = 1100 0011 - // data_mask = 0000 1111 - // data_bits = 00xx xx00 - let unwritten_bits = (!(u32::MAX << lsb) | (u32::MAX << (msb + 1))) as u8; - let data_mask = !(u32::MAX << (msb - lsb + 1)) as u8; - let data_bits = (data & data_mask) << lsb; - - I2C_ANA_MST::regs().i2c_ctrl(master).modify(|r, w| unsafe { - w.slave_addr().bits(block); - w.slave_reg_addr().bits(reg_add); - w.read_write().set_bit(); - w.data() - .bits((r.data().bits() & unwritten_bits) | data_bits) - }); - - while I2C_ANA_MST::regs().i2c_ctrl(master).read().busy().bit() {} -} +use crate::peripherals::{MODEM_LPCON, MODEM_SYSCON, PMU}; pub(super) fn enable_phy(en: bool) { MODEM_LPCON::regs() diff --git a/esp-hal/src/clock/mod.rs b/esp-hal/src/clock/mod.rs index 42cac66899c..1ce84150687 100644 --- a/esp-hal/src/clock/mod.rs +++ b/esp-hal/src/clock/mod.rs @@ -1073,14 +1073,6 @@ pub struct Clocks { /// Crypto clock frequency #[cfg(any(esp32c6, esp32h2))] pub crypto_clock: Rate, - - /// PLL 48M clock frequency (fixed) - #[cfg(esp32h2)] - pub pll_48m_clock: Rate, - - /// PLL 96M clock frequency (fixed) - #[cfg(esp32h2)] - pub pll_96m_clock: Rate, } static mut ACTIVE_CLOCKS: Option = None; @@ -1256,34 +1248,20 @@ 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; - let apb_freq; - if cpu_clock_speed != CpuClock::default() { - if cpu_clock_speed.mhz() <= xtal_freq.mhz() { - apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz()); - clocks_ll::esp32h2_rtc_update_to_xtal(xtal_freq, 1); - clocks_ll::esp32h2_rtc_apb_freq_update(apb_freq); - } else { - let pll_freq = PllClock::Pll96MHz; - apb_freq = ApbClock::ApbFreq32MHz; - clocks_ll::esp32h2_rtc_bbpll_enable(); - clocks_ll::esp32h2_rtc_bbpll_configure(xtal_freq, pll_freq); - clocks_ll::esp32h2_rtc_freq_to_pll_mhz(cpu_clock_speed); - clocks_ll::esp32h2_rtc_apb_freq_update(apb_freq); - } - } else { - apb_freq = ApbClock::ApbFreq32MHz; + // TODO: expose the whole new enum for custom options + match cpu_clock_speed { + CpuClock::_96MHz => crate::soc::clocks::CpuClock::_96MHz, } + .configure(); - Self { - cpu_clock: cpu_clock_speed.frequency(), - apb_clock: apb_freq.frequency(), - xtal_clock: xtal_freq.frequency(), - pll_48m_clock: Rate::from_mhz(48), - crypto_clock: Rate::from_mhz(96), - pll_96m_clock: Rate::from_mhz(96), - } + ClockTree::with(|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::xtal_clk_frequency(clocks)), + crypto_clock: Rate::from_hz(crate::soc::clocks::hp_root_clk_frequency(clocks)), + }) } } diff --git a/esp-hal/src/rtc_cntl/rtc/esp32h2.rs b/esp-hal/src/rtc_cntl/rtc/esp32h2.rs index 0eb51ba2ad2..9b8672fea85 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32h2.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32h2.rs @@ -1,121 +1,22 @@ use strum::FromRepr; use crate::{ - clock::{ - RtcClock, - RtcFastClock, - RtcSlowClock, - XtalClock, - clocks_ll::{esp32h2_rtc_update_to_xtal, regi2c_write_mask}, - }, + clock::{RtcClock, RtcFastClock, RtcSlowClock}, peripherals::{LP_AON, PMU}, rtc_cntl::RtcCalSel, + soc::regi2c, }; -const I2C_PMU: u8 = 0x6d; -const I2C_PMU_HOSTID: u8 = 0; - -const I2C_PMU_EN_I2C_RTC_DREG: u8 = 8; -const I2C_PMU_EN_I2C_RTC_DREG_MSB: u8 = 0; -const I2C_PMU_EN_I2C_RTC_DREG_LSB: u8 = 0; - -const I2C_PMU_EN_I2C_DIG_DREG: u8 = 8; -const I2C_PMU_EN_I2C_DIG_DREG_MSB: u8 = 1; -const I2C_PMU_EN_I2C_DIG_DREG_LSB: u8 = 1; - -const I2C_PMU_EN_I2C_RTC_DREG_SLP: u8 = 8; -const I2C_PMU_EN_I2C_RTC_DREG_SLP_MSB: u8 = 2; -const I2C_PMU_EN_I2C_RTC_DREG_SLP_LSB: u8 = 2; - -const I2C_PMU_EN_I2C_DIG_DREG_SLP: u8 = 8; -const I2C_PMU_EN_I2C_DIG_DREG_SLP_MSB: u8 = 3; -const I2C_PMU_EN_I2C_DIG_DREG_SLP_LSB: u8 = 3; - -const I2C_PMU_OR_XPD_RTC_REG: u8 = 8; -const I2C_PMU_OR_XPD_RTC_REG_MSB: u8 = 4; -const I2C_PMU_OR_XPD_RTC_REG_LSB: u8 = 4; - -const I2C_PMU_OR_XPD_DIG_REG: u8 = 8; -const I2C_PMU_OR_XPD_DIG_REG_MSB: u8 = 5; -const I2C_PMU_OR_XPD_DIG_REG_LSB: u8 = 5; - -const I2C_PMU_OR_XPD_TRX: u8 = 15; -const I2C_PMU_OR_XPD_TRX_MSB: u8 = 2; -const I2C_PMU_OR_XPD_TRX_LSB: u8 = 2; - -const I2C_BIAS: u8 = 0x6a; -const I2C_BIAS_HOSTID: u8 = 0; - -const I2C_BIAS_DREG_0P8: u8 = 0; -const I2C_BIAS_DREG_0P8_MSB: u8 = 7; -const I2C_BIAS_DREG_0P8_LSB: u8 = 4; - pub(crate) fn init() { // * No peripheral reg i2c power up required on the target */ - regi2c_write_mask( - I2C_PMU, - I2C_PMU_HOSTID, - I2C_PMU_EN_I2C_RTC_DREG, - I2C_PMU_EN_I2C_RTC_DREG_MSB, - I2C_PMU_EN_I2C_RTC_DREG_LSB, - 0, - ); - regi2c_write_mask( - I2C_PMU, - I2C_PMU_HOSTID, - I2C_PMU_EN_I2C_DIG_DREG, - I2C_PMU_EN_I2C_DIG_DREG_MSB, - I2C_PMU_EN_I2C_DIG_DREG_LSB, - 0, - ); - regi2c_write_mask( - I2C_PMU, - I2C_PMU_HOSTID, - I2C_PMU_EN_I2C_RTC_DREG_SLP, - I2C_PMU_EN_I2C_RTC_DREG_SLP_MSB, - I2C_PMU_EN_I2C_RTC_DREG_SLP_LSB, - 0, - ); - regi2c_write_mask( - I2C_PMU, - I2C_PMU_HOSTID, - I2C_PMU_EN_I2C_DIG_DREG_SLP, - I2C_PMU_EN_I2C_DIG_DREG_SLP_MSB, - I2C_PMU_EN_I2C_DIG_DREG_SLP_LSB, - 0, - ); - regi2c_write_mask( - I2C_PMU, - I2C_PMU_HOSTID, - I2C_PMU_OR_XPD_RTC_REG, - I2C_PMU_OR_XPD_RTC_REG_MSB, - I2C_PMU_OR_XPD_RTC_REG_LSB, - 0, - ); - regi2c_write_mask( - I2C_PMU, - I2C_PMU_HOSTID, - I2C_PMU_OR_XPD_DIG_REG, - I2C_PMU_OR_XPD_DIG_REG_MSB, - I2C_PMU_OR_XPD_DIG_REG_LSB, - 0, - ); - regi2c_write_mask( - I2C_PMU, - I2C_PMU_HOSTID, - I2C_PMU_OR_XPD_TRX, - I2C_PMU_OR_XPD_TRX_MSB, - I2C_PMU_OR_XPD_TRX_LSB, - 0, - ); - regi2c_write_mask( - I2C_BIAS, - I2C_BIAS_HOSTID, - I2C_BIAS_DREG_0P8, - I2C_BIAS_DREG_0P8_MSB, - I2C_BIAS_DREG_0P8_LSB, - 8, - ); + regi2c::I2C_PMU_EN_I2C_RTC_DREG.write_field(0); + regi2c::I2C_PMU_EN_I2C_DIG_DREG.write_field(0); + regi2c::I2C_PMU_EN_I2C_RTC_DREG_SLP.write_field(0); + regi2c::I2C_PMU_EN_I2C_DIG_DREG_SLP.write_field(0); + regi2c::I2C_PMU_OR_XPD_RTC_REG.write_field(0); + regi2c::I2C_PMU_OR_XPD_DIG_REG.write_field(0); + regi2c::I2C_PMU_OR_XPD_TRX.write_field(0); + regi2c::I2C_BIAS_DREG_0P8.write_field(8); let pmu = PMU::regs(); unsafe { @@ -320,8 +221,3 @@ bitfield::bitfield! { pub bool, dig_pause_wdt , set_dig_pause_wdt : 28; pub bool, dig_cpu_stall , set_dig_cpu_stall : 29; } - -pub(crate) fn rtc_clk_cpu_freq_set_xtal() { - // rtc_clk_cpu_set_to_default_config - esp32h2_rtc_update_to_xtal(XtalClock::_32M, 1); -} diff --git a/esp-hal/src/rtc_cntl/sleep/esp32c6.rs b/esp-hal/src/rtc_cntl/sleep/esp32c6.rs index a630b1e69e1..d9128b92d93 100644 --- a/esp-hal/src/rtc_cntl/sleep/esp32c6.rs +++ b/esp-hal/src/rtc_cntl/sleep/esp32c6.rs @@ -1025,8 +1025,7 @@ impl RtcSleepConfig { // TODO: IDF-7370 #[cfg(not(soc_has_pmu))] if !(self.deep && wakeup_triggers.touch) { - let saradc = &*esp32c6::APB_SARADC::ptr(); - saradc + APB_SARADC::regs() .ctrl() .modify(|_, w| w.saradc2_pwdet_drv().bit(false)); } diff --git a/esp-hal/src/rtc_cntl/sleep/esp32h2.rs b/esp-hal/src/rtc_cntl/sleep/esp32h2.rs index 0ae2907aa53..f8d8da490b6 100644 --- a/esp-hal/src/rtc_cntl/sleep/esp32h2.rs +++ b/esp-hal/src/rtc_cntl/sleep/esp32h2.rs @@ -3,13 +3,15 @@ use core::ops::Not; use crate::{ clock::Clock, efuse::Efuse, + peripherals::APB_SARADC, rtc_cntl::{ Rtc, RtcCalSel, RtcClock, - rtc::{HpSysCntlReg, HpSysPower, LpSysPower, rtc_clk_cpu_freq_set_xtal}, + rtc::{HpSysCntlReg, HpSysPower, LpSysPower}, sleep::{TimerWakeupSource, WakeSource, WakeTriggers}, }, + soc::clocks::{ClockTree, CpuClkConfig, HpRootClkConfig}, }; impl WakeSource for TimerWakeupSource { @@ -637,13 +639,20 @@ impl RtcSleepConfig { wakeup_mask & reject_mask }; - // TODO: save and restore clock config after light sleep wakeup - rtc_clk_cpu_freq_set_xtal(); + let cpu_freq_config = ClockTree::with(|clocks| { + let cpu_freq_config = SavedClockConfig::save(clocks); + crate::soc::clocks::configure_hp_root_clk( + clocks, + crate::soc::clocks::HpRootClkConfig::Xtal, + ); + crate::soc::clocks::configure_cpu_clk(clocks, crate::soc::clocks::CpuClkConfig::new(0)); + cpu_freq_config + }); // misc_modules_sleep_prepare // TODO: IDF-7370 - let saradc = unsafe { &*esp32h2::APB_SARADC::ptr() }; - saradc + + APB_SARADC::regs() .ctrl() .modify(|_, w| w.saradc2_pwdet_drv().bit(false)); @@ -714,8 +723,45 @@ impl RtcSleepConfig { } } } + + // esp-idf returns if the sleep was rejected, we don't return anything + + ClockTree::with(|clocks| { + cpu_freq_config.restore(clocks); + }); } /// Cleans up after sleep pub(crate) fn finish_sleep(&self) {} } + +#[derive(Clone, Copy)] +pub(crate) struct SavedClockConfig { + /// The clock from which CPU clock is derived + old_hp_root_clk: Option, + + /// CPU divider + old_cpu_divider: Option, +} + +impl SavedClockConfig { + pub(crate) fn save(clocks: &ClockTree) -> Self { + let old_hp_root_clk = clocks.hp_root_clk(); + let old_cpu_divider = clocks.cpu_clk(); + + SavedClockConfig { + old_hp_root_clk, + old_cpu_divider, + } + } + + // rtc_clk_cpu_freq_set_config + pub(crate) fn restore(self, clocks: &mut ClockTree) { + if let Some(old_hp_root_clk) = self.old_hp_root_clk { + crate::soc::clocks::configure_hp_root_clk(clocks, old_hp_root_clk); + } + if let Some(old_cpu_divider) = self.old_cpu_divider { + crate::soc::clocks::configure_cpu_clk(clocks, old_cpu_divider); + } + } +} diff --git a/esp-hal/src/soc/esp32h2/clocks.rs b/esp-hal/src/soc/esp32h2/clocks.rs new file mode 100644 index 00000000000..f85cbc460d1 --- /dev/null +++ b/esp-hal/src/soc/esp32h2/clocks.rs @@ -0,0 +1,428 @@ +//! Clock tree definitions and implementations for ESP32-H2. +//! +//! 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 (130k RC_SLOW and 8M 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. +#![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 crate::{ + peripherals::{I2C_ANA_MST, LP_AON, LP_CLKRST, MODEM_LPCON, PCR, PMU, TIMG0, TIMG1}, + soc::regi2c, +}; + +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] + _96MHz, + Custom(ClockConfig), +} + +impl CpuClock { + pub(crate) fn configure(self) { + // Resolve presets + let mut config = match self { + CpuClock::_96MHz => ClockConfig { + xtal_clk: None, + hp_root_clk: Some(HpRootClkConfig::Pll96), + cpu_clk: Some(CpuClkConfig::new(0)), + ahb_clk: Some(AhbClkConfig::new(0)), + apb_clk: Some(ApbClkConfig::new(0)), + lp_fast_clk: Some(LpFastClkConfig::RcFastClk), + lp_slow_clk: Some(LpSlowClkConfig::RcSlow), + }, + CpuClock::Custom(clock_config) => clock_config, + }; + + if config.xtal_clk.is_none() { + config.xtal_clk = Some(XtalClkConfig::_32); + } + + config.apply(); + } +} + +fn clk_ll_bus_update() { + PCR::regs() + .bus_clk_update() + .modify(|_, w| w.bus_clock_update().bit(true)); + + // reg_get_bit + while PCR::regs() + .bus_clk_update() + .read() + .bus_clock_update() + .bit_is_set() + {} +} + +// XTAL_CLK + +fn configure_xtal_clk_impl(_clocks: &mut ClockTree, config: XtalClkConfig) { + // 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; + LP_AON::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)) | disable_rom_log_bit; + w.data().bits(half | (half << 16)) + }); +} + +// PLL_F96M_CLK + +fn enable_pll_f96m_clk_impl(_clocks: &mut ClockTree, en: bool) { + if en { + PMU::regs().imm_hp_ck_power().modify(|_, w| { + w.tie_high_xpd_bb_i2c().set_bit(); + w.tie_high_xpd_bbpll().set_bit(); + w.tie_high_xpd_bbpll_i2c().set_bit() + }); + PMU::regs() + .imm_hp_ck_power() + .modify(|_, w| w.tie_high_global_bbpll_icg().set_bit()); + } else { + PMU::regs() + .imm_hp_ck_power() + .modify(|_, w| w.tie_low_global_bbpll_icg().set_bit()); + PMU::regs().imm_hp_ck_power().modify(|_, w| { + w.tie_low_xpd_bbpll().set_bit(); + w.tie_low_xpd_bbpll_i2c().set_bit() + }); + + return; + } + + // Enable I2C master clock + MODEM_LPCON::regs() + .clk_conf_force_on() + .modify(|_, w| w.clk_i2c_mst_fo().set_bit()); + + // Set I2C clock to 96MHz + const MODEM_LPCON_CLK_I2C_SEL_96M: u32 = 1 << 0; // FIXME add to PAC + MODEM_LPCON::regs() + .clk_conf() + .modify(|r, w| unsafe { w.bits(r.bits() | MODEM_LPCON_CLK_I2C_SEL_96M) }); + + // BPPLL calibration start + I2C_ANA_MST::regs().ana_conf0().modify(|_, w| { + w.bbpll_stop_force_high().clear_bit(); + w.bbpll_stop_force_low().set_bit() + }); + + regi2c::I2C_BBPLL_OC_REF_DIV.write_field(0); + regi2c::I2C_BBPLL_OC_DIV.write_field(1); + regi2c::I2C_BBPLL_OC_DHREF_SEL.write_field(3); + regi2c::I2C_BBPLL_OC_DLREF_SEL.write_field(1); + + // WAIT CALIBRATION DONE + while I2C_ANA_MST::regs() + .ana_conf0() + .read() + .cal_done() + .bit_is_clear() + {} + + // workaround bbpll calibration might stop early + crate::rom::ets_delay_us(10); + + // BBPLL CALIBRATION STOP + I2C_ANA_MST::regs().ana_conf0().modify(|_, w| { + w.bbpll_stop_force_high().set_bit(); + w.bbpll_stop_force_low().clear_bit() + }); +} + +// PLL_F64M_CLK - called Flash PLL in esp-idf + +fn enable_pll_f64m_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +// PLL_F48M_CLK + +fn enable_pll_f48m_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +// RC_FAST_CLK + +fn enable_rc_fast_clk_impl(_clocks: &mut ClockTree, en: bool) { + PMU::regs() + .hp_sleep_lp_ck_power() + .modify(|_, w| w.hp_sleep_xpd_fosc_clk().bit(en)); + // Enable for digital part + LP_CLKRST::regs() + .clk_to_hp() + .modify(|_, w| w.icg_hp_fosc().bit(en)); +} + +// XTAL32K_CLK + +fn enable_xtal32k_clk_impl(_clocks: &mut ClockTree, en: bool) { + PMU::regs() + .hp_sleep_lp_ck_power() + .modify(|_, w| w.hp_sleep_xpd_xtal32k().bit(en)); + // Enable for digital part + LP_CLKRST::regs() + .clk_to_hp() + .modify(|_, w| w.icg_hp_xtal32k().bit(en)); +} + +// OSC_SLOW_CLK + +fn enable_osc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { + unimplemented!() +} + +// RC_SLOW_CLK + +fn enable_rc_slow_clk_impl(_clocks: &mut ClockTree, en: bool) { + PMU::regs() + .hp_sleep_lp_ck_power() + .modify(|_, w| w.hp_sleep_xpd_rc32k().bit(en)); + // Enable for digital part + LP_CLKRST::regs() + .clk_to_hp() + .modify(|_, w| w.icg_hp_osc32k().bit(en)); +} + +// PLL_LP_CLK + +fn enable_pll_lp_clk_impl(_clocks: &mut ClockTree, en: bool) { + PMU::regs() + .hp_sleep_lp_ck_power() + .modify(|_, w| w.hp_sleep_xpd_lppll().bit(en)); +} + +// HP_ROOT_CLK + +fn enable_hp_root_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do +} + +fn configure_hp_root_clk_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + new_selector: HpRootClkConfig, +) { + PCR::regs().sysclk_conf().modify(|_, w| unsafe { + w.soc_clk_sel().bits(match new_selector { + HpRootClkConfig::Pll96 => 1, + HpRootClkConfig::Pll64 => 3, + HpRootClkConfig::Xtal => 0, + HpRootClkConfig::RcFast => 2, + }) + }); + + // "When selecting the clock source of HP_ROOT_CLK, or configuring the clock divisor for CPU_CLK + // and AHB_CLK ..." + clk_ll_bus_update(); +} + +// CPU_CLK + +fn enable_cpu_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do +} + +fn configure_cpu_clk_impl(_clocks: &mut ClockTree, new_config: CpuClkConfig) { + PCR::regs() + .cpu_freq_conf() + .modify(|_, w| unsafe { w.cpu_div_num().bits(new_config.value() as u8) }); + + // "When selecting the clock source of HP_ROOT_CLK, or configuring the clock divisor for CPU_CLK + // and AHB_CLK ..." + clk_ll_bus_update(); +} + +// AHB_CLK + +fn enable_ahb_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do +} + +fn configure_ahb_clk_impl(_clocks: &mut ClockTree, new_config: AhbClkConfig) { + PCR::regs() + .ahb_freq_conf() + .modify(|_, w| unsafe { w.ahb_div_num().bits(new_config.value() as u8) }); + + // "When selecting the clock source of HP_ROOT_CLK, or configuring the clock divisor for CPU_CLK + // and AHB_CLK ..." + clk_ll_bus_update(); +} + +// APB_CLK + +fn enable_apb_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +fn configure_apb_clk_impl(_clocks: &mut ClockTree, new_config: ApbClkConfig) { + PCR::regs() + .apb_freq_conf() + .modify(|_, w| unsafe { w.apb_div_num().bits(new_config.value() as u8) }); +} + +// XTAL_D2_CLK + +fn enable_xtal_d2_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +// LP_FAST_CLK + +fn enable_lp_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +fn configure_lp_fast_clk_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + new_selector: LpFastClkConfig, +) { + LP_CLKRST::regs().lp_clk_conf().modify(|_, w| unsafe { + w.fast_clk_sel().bits(match new_selector { + LpFastClkConfig::RcFastClk => 0, + LpFastClkConfig::XtalD2Clk => 1, + LpFastClkConfig::PllLpClk => 2, + }) + }); +} + +// LP_SLOW_CLK + +fn enable_lp_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Nothing to do. +} + +fn configure_lp_slow_clk_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + new_selector: LpSlowClkConfig, +) { + LP_CLKRST::regs().lp_clk_conf().modify(|_, w| unsafe { + w.slow_clk_sel().bits(match new_selector { + LpSlowClkConfig::RcSlow => 0, + LpSlowClkConfig::Xtal32kClk => 1, + LpSlowClkConfig::OscSlow => 2, + }) + }); +} + +// TIMG0_FUNCTION_CLOCK + +fn enable_timg0_function_clock_impl(_clocks: &mut ClockTree, en: bool) { + PCR::regs() + .timergroup0_timer_clk_conf() + .modify(|_, w| w.tg0_timer_clk_en().bit(en)); +} + +fn configure_timg0_function_clock_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + new_selector: Timg0FunctionClockConfig, +) { + // TODO: add variants to PAC + PCR::regs() + .timergroup0_timer_clk_conf() + .modify(|_, w| unsafe { + w.tg0_timer_clk_sel().bits(match new_selector { + Timg0FunctionClockConfig::XtalClk => 0, + Timg0FunctionClockConfig::RcFastClk => 1, + Timg0FunctionClockConfig::PllF48m => 2, + }) + }); +} + +// 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::RtcSlowClk => 0, + Timg0CalibrationClockConfig::RcFastClk => 1, + Timg0CalibrationClockConfig::Xtal32kClk => 2, + }) + }); +} + +// TIMG1_FUNCTION_CLOCK + +fn enable_timg1_function_clock_impl(_clocks: &mut ClockTree, en: bool) { + PCR::regs() + .timergroup1_timer_clk_conf() + .modify(|_, w| w.tg1_timer_clk_en().bit(en)); +} + +fn configure_timg1_function_clock_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + new_selector: Timg0FunctionClockConfig, +) { + // TODO: add variants to PAC + PCR::regs() + .timergroup1_timer_clk_conf() + .modify(|_, w| unsafe { + w.tg1_timer_clk_sel().bits(match new_selector { + Timg0FunctionClockConfig::XtalClk => 0, + Timg0FunctionClockConfig::RcFastClk => 1, + Timg0FunctionClockConfig::PllF48m => 2, + }) + }); +} + +// TIMG1_CALIBRATION_CLOCK + +fn enable_timg1_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_timg1_calibration_clock_impl( + _clocks: &mut ClockTree, + _old_selector: Option, + new_selector: Timg0CalibrationClockConfig, +) { + TIMG1::regs().rtccalicfg().modify(|_, w| unsafe { + w.rtc_cali_clk_sel().bits(match new_selector { + Timg0CalibrationClockConfig::RtcSlowClk => 0, + Timg0CalibrationClockConfig::RcFastClk => 1, + Timg0CalibrationClockConfig::Xtal32kClk => 2, + }) + }); +} diff --git a/esp-hal/src/soc/esp32h2/mod.rs b/esp-hal/src/soc/esp32h2/mod.rs index e82f94ba7f8..19363ea04e2 100644 --- a/esp-hal/src/soc/esp32h2/mod.rs +++ b/esp-hal/src/soc/esp32h2/mod.rs @@ -13,6 +13,7 @@ crate::unstable_module! { pub mod trng; } +pub mod clocks; pub mod gpio; pub(crate) mod regi2c; diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index 26a9c67714b..69099039c5c 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -506,7 +506,8 @@ impl Config { cfg_if::cfg_if! { if #[cfg(esp32h2)] { // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK - clocks.pll_48m_clock + let _clocks = clocks; + Rate::from_mhz(48) } else if #[cfg(esp32c6)] { // We select the 80MHz PLL as the clock source in the driver // FIXME we state that the default clock source is APB, which just isn't true diff --git a/esp-hal/src/timer/timg.rs b/esp-hal/src/timer/timg.rs index 6b9e659fd79..1be3e4780f3 100644 --- a/esp-hal/src/timer/timg.rs +++ b/esp-hal/src/timer/timg.rs @@ -770,7 +770,7 @@ where cfg_if::cfg_if! { if #[cfg(esp32h2)] { // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK - let clk_src = Clocks::get().pll_48m_clock; + let clk_src = Rate::from_mhz(48); } else { let clk_src = Clocks::get().apb_clock; } diff --git a/esp-metadata-generated/src/_build_script_utils.rs b/esp-metadata-generated/src/_build_script_utils.rs index 416ffa525b4..0ba6e918fb7 100644 --- a/esp-metadata-generated/src/_build_script_utils.rs +++ b/esp-metadata-generated/src/_build_script_utils.rs @@ -1907,6 +1907,26 @@ impl Chip { "soc_rc_fast_clk_default_is_set", "soc_rc_slow_clock=\"136000\"", "soc_rc_slow_clock_is_set", + "soc_has_clock_node_xtal_clk", + "soc_has_clock_node_pll_f96m_clk", + "soc_has_clock_node_pll_f64m_clk", + "soc_has_clock_node_pll_f48m_clk", + "soc_has_clock_node_rc_fast_clk", + "soc_has_clock_node_xtal32k_clk", + "soc_has_clock_node_osc_slow_clk", + "soc_has_clock_node_rc_slow_clk", + "soc_has_clock_node_pll_lp_clk", + "soc_has_clock_node_hp_root_clk", + "soc_has_clock_node_cpu_clk", + "soc_has_clock_node_ahb_clk", + "soc_has_clock_node_apb_clk", + "soc_has_clock_node_xtal_d2_clk", + "soc_has_clock_node_lp_fast_clk", + "soc_has_clock_node_lp_slow_clk", + "soc_has_clock_node_timg0_function_clock", + "soc_has_clock_node_timg0_calibration_clock", + "soc_has_clock_node_timg1_function_clock", + "soc_has_clock_node_timg1_calibration_clock", "has_dram_region", "has_dram2_uninit_region", "soc_xtal_frequency=\"32\"", @@ -2098,6 +2118,26 @@ 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_xtal_clk", + "cargo:rustc-cfg=soc_has_clock_node_pll_f96m_clk", + "cargo:rustc-cfg=soc_has_clock_node_pll_f64m_clk", + "cargo:rustc-cfg=soc_has_clock_node_pll_f48m_clk", + "cargo:rustc-cfg=soc_has_clock_node_rc_fast_clk", + "cargo:rustc-cfg=soc_has_clock_node_xtal32k_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_pll_lp_clk", + "cargo:rustc-cfg=soc_has_clock_node_hp_root_clk", + "cargo:rustc-cfg=soc_has_clock_node_cpu_clk", + "cargo:rustc-cfg=soc_has_clock_node_ahb_clk", + "cargo:rustc-cfg=soc_has_clock_node_apb_clk", + "cargo:rustc-cfg=soc_has_clock_node_xtal_d2_clk", + "cargo:rustc-cfg=soc_has_clock_node_lp_fast_clk", + "cargo:rustc-cfg=soc_has_clock_node_lp_slow_clk", + "cargo:rustc-cfg=soc_has_clock_node_timg0_function_clock", + "cargo:rustc-cfg=soc_has_clock_node_timg0_calibration_clock", + "cargo:rustc-cfg=soc_has_clock_node_timg1_function_clock", + "cargo:rustc-cfg=soc_has_clock_node_timg1_calibration_clock", "cargo:rustc-cfg=has_dram_region", "cargo:rustc-cfg=has_dram2_uninit_region", "cargo:rustc-cfg=soc_xtal_frequency=\"32\"", @@ -3348,6 +3388,10 @@ pub fn emit_check_cfg_directives() { println!("cargo:rustc-check-cfg=cfg(uart_peripheral_controls_mem_clk)"); println!("cargo:rustc-check-cfg=cfg(wifi_has_wifi6)"); println!("cargo:rustc-check-cfg=cfg(esp32h2)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_f96m_clk)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_f64m_clk)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_f48m_clk)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_lp_clk)"); println!("cargo:rustc-check-cfg=cfg(esp32s2)"); println!("cargo:rustc-check-cfg=cfg(soc_has_dedicated_gpio)"); println!("cargo:rustc-check-cfg=cfg(soc_has_pms)"); diff --git a/esp-metadata-generated/src/_generated_esp32h2.rs b/esp-metadata-generated/src/_generated_esp32h2.rs index b95e5848cc1..17dc321041a 100644 --- a/esp-metadata-generated/src/_generated_esp32h2.rs +++ b/esp-metadata-generated/src/_generated_esp32h2.rs @@ -279,19 +279,963 @@ macro_rules! for_each_soc_xtal_options { #[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run +/// // XTAL_CLK +/// +/// fn configure_xtal_clk_impl(_clocks: &mut ClockTree, _config: XtalClkConfig) { +/// todo!() +/// } +/// +/// // PLL_F96M_CLK +/// +/// fn enable_pll_f96m_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // PLL_F64M_CLK +/// +/// fn enable_pll_f64m_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // PLL_F48M_CLK +/// +/// fn enable_pll_f48m_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // RC_FAST_CLK +/// +/// fn enable_rc_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // XTAL32K_CLK +/// +/// fn enable_xtal32k_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!() +/// } +/// +/// // PLL_LP_CLK +/// +/// fn enable_pll_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // HP_ROOT_CLK +/// +/// fn enable_hp_root_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_hp_root_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: HpRootClkConfig, +/// ) { +/// todo!() +/// } +/// +/// // CPU_CLK +/// +/// fn enable_cpu_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_cpu_clk_impl(_clocks: &mut ClockTree, _new_config: CpuClkConfig) { +/// todo!() +/// } +/// +/// // AHB_CLK +/// +/// fn enable_ahb_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_ahb_clk_impl(_clocks: &mut ClockTree, _new_config: AhbClkConfig) { +/// todo!() +/// } +/// +/// // APB_CLK +/// +/// fn enable_apb_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_apb_clk_impl(_clocks: &mut ClockTree, _new_config: ApbClkConfig) { +/// todo!() +/// } +/// +/// // XTAL_D2_CLK +/// +/// fn enable_xtal_d2_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // LP_FAST_CLK +/// +/// fn enable_lp_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_lp_fast_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: LpFastClkConfig, +/// ) { +/// todo!() +/// } +/// +/// // LP_SLOW_CLK +/// +/// fn enable_lp_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_lp_slow_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: LpSlowClkConfig, +/// ) { +/// 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!() +/// } +/// +/// // TIMG1_FUNCTION_CLOCK +/// +/// fn enable_timg1_function_clock_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_timg1_function_clock_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: Timg0FunctionClockConfig, +/// ) { +/// todo!() +/// } +/// +/// // TIMG1_CALIBRATION_CLOCK +/// +/// fn enable_timg1_calibration_clock_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_timg1_calibration_clock_impl( +/// _clocks: &mut ClockTree, +/// _old_selector: Option, +/// _new_selector: Timg0CalibrationClockConfig, +/// ) { +/// todo!() +/// } /// ``` macro_rules! define_clock_tree_types { () => { + /// Selects the output frequency of `XTAL_CLK`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum XtalClkConfig { + /// 32 MHz + _32, + } + impl XtalClkConfig { + pub fn value(&self) -> u32 { + match self { + XtalClkConfig::_32 => 32000000, + } + } + } + /// The list of clock signals that the `HP_ROOT_CLK` multiplexer can output. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum HpRootClkConfig { + /// Selects `PLL_F96M_CLK`. + Pll96, + /// Selects `PLL_F64M_CLK`. + Pll64, + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RC_FAST_CLK`. + RcFast, + } + /// Configures the `CPU_CLK` clock divider. + /// + /// The output is calculated as `OUTPUT = HP_ROOT_CLK / (DIVISOR + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct CpuClkConfig(u32); + impl CpuClkConfig { + /// Creates a new divider configuration. + pub const fn new(divisor: u32) -> Self { + Self(divisor) + } + fn value(self) -> u32 { + self.0 + } + } + /// Configures the `AHB_CLK` clock divider. + /// + /// The output is calculated as `OUTPUT = HP_ROOT_CLK / (DIVISOR + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct AhbClkConfig(u32); + impl AhbClkConfig { + /// Creates a new divider configuration. + pub const fn new(divisor: u32) -> Self { + Self(divisor) + } + fn value(self) -> u32 { + self.0 + } + } + /// Configures the `APB_CLK` clock divider. + /// + /// The output is calculated as `OUTPUT = AHB_CLK / (DIVISOR + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct ApbClkConfig(u32); + impl ApbClkConfig { + /// Creates a new divider configuration. + /// + /// # Panics + /// + /// Panics if the divisor value is outside the + /// valid range (0 ..= 255). + pub const fn new(divisor: u32) -> Self { + ::core::assert!( + divisor <= 255u32, + "`APB_CLK` divisor value must be between 0 and 255 (inclusive)." + ); + Self(divisor) + } + fn value(self) -> u32 { + self.0 + } + } + /// The list of clock signals that the `LP_FAST_CLK` multiplexer can output. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum LpFastClkConfig { + /// Selects `RC_FAST_CLK`. + RcFastClk, + /// Selects `PLL_LP_CLK`. + PllLpClk, + /// Selects `XTAL_D2_CLK`. + XtalD2Clk, + } + /// The list of clock signals that the `LP_SLOW_CLK` multiplexer can output. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum LpSlowClkConfig { + /// Selects `XTAL32K_CLK`. + Xtal32kClk, + /// Selects `RC_SLOW_CLK`. + RcSlow, + /// Selects `OSC_SLOW_CLK`. + OscSlow, + } + /// 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 { + #[default] + /// Selects `XTAL_CLK`. + XtalClk, + /// Selects `RC_FAST_CLK`. + RcFastClk, + /// Selects `PLL_F48M_CLK`. + PllF48m, + } + /// 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 `LP_SLOW_CLK`. + RtcSlowClk, + /// Selects `RC_FAST_CLK`. + RcFastClk, + /// Selects `XTAL32K_CLK`. + Xtal32kClk, + } /// Represents the device's clock tree. - pub struct ClockTree {} + pub struct ClockTree { + xtal_clk: Option, + hp_root_clk: Option, + cpu_clk: Option, + ahb_clk: Option, + apb_clk: Option, + lp_fast_clk: Option, + lp_slow_clk: Option, + timg0_function_clock: Option, + timg0_calibration_clock: Option, + timg1_function_clock: Option, + timg1_calibration_clock: Option, + pll_f96m_clk_refcount: u32, + pll_f48m_clk_refcount: u32, + rc_fast_clk_refcount: u32, + xtal32k_clk_refcount: u32, + hp_root_clk_refcount: u32, + cpu_clk_refcount: u32, + apb_clk_refcount: u32, + lp_fast_clk_refcount: u32, + lp_slow_clk_refcount: u32, + timg0_function_clock_refcount: u32, + timg0_calibration_clock_refcount: u32, + timg1_function_clock_refcount: u32, + timg1_calibration_clock_refcount: u32, + } impl ClockTree { /// Locks the clock tree for exclusive access. pub fn with(f: impl FnOnce(&mut ClockTree) -> R) -> R { CLOCK_TREE.with(f) } + /// Returns the current configuration of the XTAL_CLK clock tree node + pub fn xtal_clk(&self) -> Option { + self.xtal_clk + } + /// Returns the current configuration of the HP_ROOT_CLK clock tree node + pub fn hp_root_clk(&self) -> Option { + self.hp_root_clk + } + /// Returns the current configuration of the CPU_CLK clock tree node + pub fn cpu_clk(&self) -> Option { + self.cpu_clk + } + /// Returns the current configuration of the AHB_CLK clock tree node + pub fn ahb_clk(&self) -> Option { + self.ahb_clk + } + /// Returns the current configuration of the APB_CLK clock tree node + pub fn apb_clk(&self) -> Option { + self.apb_clk + } + /// Returns the current configuration of the LP_FAST_CLK clock tree node + pub fn lp_fast_clk(&self) -> Option { + self.lp_fast_clk + } + /// Returns the current configuration of the LP_SLOW_CLK clock tree node + pub fn lp_slow_clk(&self) -> Option { + self.lp_slow_clk + } + /// Returns the current configuration of the TIMG0_FUNCTION_CLOCK clock tree node + pub fn timg0_function_clock(&self) -> Option { + self.timg0_function_clock + } + /// Returns the current configuration of the TIMG0_CALIBRATION_CLOCK clock tree node + pub fn timg0_calibration_clock(&self) -> Option { + self.timg0_calibration_clock + } + /// Returns the current configuration of the TIMG1_FUNCTION_CLOCK clock tree node + pub fn timg1_function_clock(&self) -> Option { + self.timg1_function_clock + } + /// Returns the current configuration of the TIMG1_CALIBRATION_CLOCK clock tree node + pub fn timg1_calibration_clock(&self) -> Option { + self.timg1_calibration_clock + } } static CLOCK_TREE: ::esp_sync::NonReentrantMutex = - ::esp_sync::NonReentrantMutex::new(ClockTree {}); + ::esp_sync::NonReentrantMutex::new(ClockTree { + xtal_clk: None, + hp_root_clk: None, + cpu_clk: None, + ahb_clk: None, + apb_clk: None, + lp_fast_clk: None, + lp_slow_clk: None, + timg0_function_clock: None, + timg0_calibration_clock: None, + timg1_function_clock: None, + timg1_calibration_clock: None, + pll_f96m_clk_refcount: 0, + pll_f48m_clk_refcount: 0, + rc_fast_clk_refcount: 0, + xtal32k_clk_refcount: 0, + hp_root_clk_refcount: 0, + cpu_clk_refcount: 0, + apb_clk_refcount: 0, + lp_fast_clk_refcount: 0, + lp_slow_clk_refcount: 0, + timg0_function_clock_refcount: 0, + timg0_calibration_clock_refcount: 0, + timg1_function_clock_refcount: 0, + timg1_calibration_clock_refcount: 0, + }); + pub fn configure_xtal_clk(clocks: &mut ClockTree, config: XtalClkConfig) { + clocks.xtal_clk = Some(config); + configure_xtal_clk_impl(clocks, config); + } + fn request_xtal_clk(_clocks: &mut ClockTree) {} + fn release_xtal_clk(_clocks: &mut ClockTree) {} + pub fn xtal_clk_frequency(clocks: &mut ClockTree) -> u32 { + unwrap!(clocks.xtal_clk).value() + } + pub fn request_pll_f96m_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.pll_f96m_clk_refcount) { + request_xtal_clk(clocks); + enable_pll_f96m_clk_impl(clocks, true); + } + } + pub fn release_pll_f96m_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.pll_f96m_clk_refcount) { + enable_pll_f96m_clk_impl(clocks, false); + release_xtal_clk(clocks); + } + } + pub fn pll_f96m_clk_frequency(clocks: &mut ClockTree) -> u32 { + 96000000 + } + pub fn request_pll_f64m_clk(clocks: &mut ClockTree) { + request_pll_f96m_clk(clocks); + enable_pll_f64m_clk_impl(clocks, true); + } + pub fn release_pll_f64m_clk(clocks: &mut ClockTree) { + enable_pll_f64m_clk_impl(clocks, false); + release_pll_f96m_clk(clocks); + } + pub fn pll_f64m_clk_frequency(clocks: &mut ClockTree) -> u32 { + ((pll_f96m_clk_frequency(clocks) * 2) / 3) + } + pub fn request_pll_f48m_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.pll_f48m_clk_refcount) { + request_pll_f96m_clk(clocks); + enable_pll_f48m_clk_impl(clocks, true); + } + } + pub fn release_pll_f48m_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.pll_f48m_clk_refcount) { + enable_pll_f48m_clk_impl(clocks, false); + release_pll_f96m_clk(clocks); + } + } + pub fn pll_f48m_clk_frequency(clocks: &mut ClockTree) -> u32 { + (pll_f96m_clk_frequency(clocks) / 2) + } + 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 { + 8000000 + } + pub fn request_xtal32k_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.xtal32k_clk_refcount) { + enable_xtal32k_clk_impl(clocks, true); + } + } + pub fn release_xtal32k_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.xtal32k_clk_refcount) { + enable_xtal32k_clk_impl(clocks, false); + } + } + pub fn xtal32k_clk_frequency(clocks: &mut ClockTree) -> u32 { + 32768 + } + pub fn request_osc_slow_clk(clocks: &mut ClockTree) { + enable_osc_slow_clk_impl(clocks, true); + } + pub fn release_osc_slow_clk(clocks: &mut ClockTree) { + 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) { + enable_rc_slow_clk_impl(clocks, true); + } + pub fn release_rc_slow_clk(clocks: &mut ClockTree) { + enable_rc_slow_clk_impl(clocks, false); + } + pub fn rc_slow_clk_frequency(clocks: &mut ClockTree) -> u32 { + 130000 + } + pub fn request_pll_lp_clk(clocks: &mut ClockTree) { + request_xtal32k_clk(clocks); + enable_pll_lp_clk_impl(clocks, true); + } + pub fn release_pll_lp_clk(clocks: &mut ClockTree) { + enable_pll_lp_clk_impl(clocks, false); + release_xtal32k_clk(clocks); + } + pub fn pll_lp_clk_frequency(clocks: &mut ClockTree) -> u32 { + 8000000 + } + pub fn configure_hp_root_clk(clocks: &mut ClockTree, new_selector: HpRootClkConfig) { + let old_selector = clocks.hp_root_clk.replace(new_selector); + if clocks.hp_root_clk_refcount > 0 { + hp_root_clk_request_upstream(clocks, new_selector); + configure_hp_root_clk_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + hp_root_clk_release_upstream(clocks, old_selector); + } + } else { + configure_hp_root_clk_impl(clocks, old_selector, new_selector); + } + } + fn hp_root_clk_request_upstream(clocks: &mut ClockTree, selector: HpRootClkConfig) { + match selector { + HpRootClkConfig::Pll96 => request_pll_f96m_clk(clocks), + HpRootClkConfig::Pll64 => request_pll_f64m_clk(clocks), + HpRootClkConfig::Xtal => request_xtal_clk(clocks), + HpRootClkConfig::RcFast => request_rc_fast_clk(clocks), + } + } + fn hp_root_clk_release_upstream(clocks: &mut ClockTree, selector: HpRootClkConfig) { + match selector { + HpRootClkConfig::Pll96 => release_pll_f96m_clk(clocks), + HpRootClkConfig::Pll64 => release_pll_f64m_clk(clocks), + HpRootClkConfig::Xtal => release_xtal_clk(clocks), + HpRootClkConfig::RcFast => release_rc_fast_clk(clocks), + } + } + pub fn request_hp_root_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.hp_root_clk_refcount) { + let selector = unwrap!(clocks.hp_root_clk); + hp_root_clk_request_upstream(clocks, selector); + enable_hp_root_clk_impl(clocks, true); + } + } + pub fn release_hp_root_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.hp_root_clk_refcount) { + enable_hp_root_clk_impl(clocks, false); + let selector = unwrap!(clocks.hp_root_clk); + hp_root_clk_release_upstream(clocks, selector); + } + } + pub fn hp_root_clk_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.hp_root_clk) { + HpRootClkConfig::Pll96 => pll_f96m_clk_frequency(clocks), + HpRootClkConfig::Pll64 => pll_f64m_clk_frequency(clocks), + HpRootClkConfig::Xtal => xtal_clk_frequency(clocks), + HpRootClkConfig::RcFast => rc_fast_clk_frequency(clocks), + } + } + pub fn configure_cpu_clk(clocks: &mut ClockTree, config: CpuClkConfig) { + clocks.cpu_clk = Some(config); + configure_cpu_clk_impl(clocks, config); + } + pub fn request_cpu_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.cpu_clk_refcount) { + request_hp_root_clk(clocks); + enable_cpu_clk_impl(clocks, true); + } + } + pub fn release_cpu_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.cpu_clk_refcount) { + enable_cpu_clk_impl(clocks, false); + release_hp_root_clk(clocks); + } + } + pub fn cpu_clk_frequency(clocks: &mut ClockTree) -> u32 { + (hp_root_clk_frequency(clocks) / (unwrap!(clocks.cpu_clk).value() + 1)) + } + pub fn configure_ahb_clk(clocks: &mut ClockTree, config: AhbClkConfig) { + clocks.ahb_clk = Some(config); + configure_ahb_clk_impl(clocks, config); + } + pub fn request_ahb_clk(clocks: &mut ClockTree) { + request_hp_root_clk(clocks); + enable_ahb_clk_impl(clocks, true); + } + pub fn release_ahb_clk(clocks: &mut ClockTree) { + enable_ahb_clk_impl(clocks, false); + release_hp_root_clk(clocks); + } + pub fn ahb_clk_frequency(clocks: &mut ClockTree) -> u32 { + (hp_root_clk_frequency(clocks) / (unwrap!(clocks.ahb_clk).value() + 1)) + } + pub fn configure_apb_clk(clocks: &mut ClockTree, config: ApbClkConfig) { + clocks.apb_clk = Some(config); + configure_apb_clk_impl(clocks, config); + } + pub fn request_apb_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.apb_clk_refcount) { + request_ahb_clk(clocks); + 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); + release_ahb_clk(clocks); + } + } + pub fn apb_clk_frequency(clocks: &mut ClockTree) -> u32 { + (ahb_clk_frequency(clocks) / (unwrap!(clocks.apb_clk).value() + 1)) + } + pub fn request_xtal_d2_clk(clocks: &mut ClockTree) { + request_xtal_clk(clocks); + enable_xtal_d2_clk_impl(clocks, true); + } + pub fn release_xtal_d2_clk(clocks: &mut ClockTree) { + enable_xtal_d2_clk_impl(clocks, false); + release_xtal_clk(clocks); + } + pub fn xtal_d2_clk_frequency(clocks: &mut ClockTree) -> u32 { + (xtal_clk_frequency(clocks) / 2) + } + pub fn configure_lp_fast_clk(clocks: &mut ClockTree, new_selector: LpFastClkConfig) { + let old_selector = clocks.lp_fast_clk.replace(new_selector); + if clocks.lp_fast_clk_refcount > 0 { + lp_fast_clk_request_upstream(clocks, new_selector); + configure_lp_fast_clk_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + lp_fast_clk_release_upstream(clocks, old_selector); + } + } else { + configure_lp_fast_clk_impl(clocks, old_selector, new_selector); + } + } + fn lp_fast_clk_request_upstream(clocks: &mut ClockTree, selector: LpFastClkConfig) { + match selector { + LpFastClkConfig::RcFastClk => request_rc_fast_clk(clocks), + LpFastClkConfig::PllLpClk => request_pll_lp_clk(clocks), + LpFastClkConfig::XtalD2Clk => request_xtal_d2_clk(clocks), + } + } + fn lp_fast_clk_release_upstream(clocks: &mut ClockTree, selector: LpFastClkConfig) { + match selector { + LpFastClkConfig::RcFastClk => release_rc_fast_clk(clocks), + LpFastClkConfig::PllLpClk => release_pll_lp_clk(clocks), + LpFastClkConfig::XtalD2Clk => release_xtal_d2_clk(clocks), + } + } + pub fn request_lp_fast_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.lp_fast_clk_refcount) { + let selector = unwrap!(clocks.lp_fast_clk); + lp_fast_clk_request_upstream(clocks, selector); + enable_lp_fast_clk_impl(clocks, true); + } + } + pub fn release_lp_fast_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.lp_fast_clk_refcount) { + enable_lp_fast_clk_impl(clocks, false); + let selector = unwrap!(clocks.lp_fast_clk); + lp_fast_clk_release_upstream(clocks, selector); + } + } + pub fn lp_fast_clk_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.lp_fast_clk) { + LpFastClkConfig::RcFastClk => rc_fast_clk_frequency(clocks), + LpFastClkConfig::PllLpClk => pll_lp_clk_frequency(clocks), + LpFastClkConfig::XtalD2Clk => xtal_d2_clk_frequency(clocks), + } + } + pub fn configure_lp_slow_clk(clocks: &mut ClockTree, new_selector: LpSlowClkConfig) { + let old_selector = clocks.lp_slow_clk.replace(new_selector); + if clocks.lp_slow_clk_refcount > 0 { + lp_slow_clk_request_upstream(clocks, new_selector); + configure_lp_slow_clk_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + lp_slow_clk_release_upstream(clocks, old_selector); + } + } else { + configure_lp_slow_clk_impl(clocks, old_selector, new_selector); + } + } + fn lp_slow_clk_request_upstream(clocks: &mut ClockTree, selector: LpSlowClkConfig) { + match selector { + LpSlowClkConfig::Xtal32kClk => request_xtal32k_clk(clocks), + LpSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), + LpSlowClkConfig::OscSlow => request_osc_slow_clk(clocks), + } + } + fn lp_slow_clk_release_upstream(clocks: &mut ClockTree, selector: LpSlowClkConfig) { + match selector { + LpSlowClkConfig::Xtal32kClk => release_xtal32k_clk(clocks), + LpSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), + LpSlowClkConfig::OscSlow => release_osc_slow_clk(clocks), + } + } + pub fn request_lp_slow_clk(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.lp_slow_clk_refcount) { + let selector = unwrap!(clocks.lp_slow_clk); + lp_slow_clk_request_upstream(clocks, selector); + enable_lp_slow_clk_impl(clocks, true); + } + } + pub fn release_lp_slow_clk(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.lp_slow_clk_refcount) { + enable_lp_slow_clk_impl(clocks, false); + let selector = unwrap!(clocks.lp_slow_clk); + lp_slow_clk_release_upstream(clocks, selector); + } + } + pub fn lp_slow_clk_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.lp_slow_clk) { + LpSlowClkConfig::Xtal32kClk => xtal32k_clk_frequency(clocks), + LpSlowClkConfig::RcSlow => rc_slow_clk_frequency(clocks), + LpSlowClkConfig::OscSlow => osc_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_xtal_clk(clocks), + Timg0FunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), + Timg0FunctionClockConfig::PllF48m => request_pll_f48m_clk(clocks), + } + } + fn timg0_function_clock_release_upstream( + clocks: &mut ClockTree, + selector: Timg0FunctionClockConfig, + ) { + match selector { + Timg0FunctionClockConfig::XtalClk => release_xtal_clk(clocks), + Timg0FunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), + Timg0FunctionClockConfig::PllF48m => release_pll_f48m_clk(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 => xtal_clk_frequency(clocks), + Timg0FunctionClockConfig::RcFastClk => rc_fast_clk_frequency(clocks), + Timg0FunctionClockConfig::PllF48m => pll_f48m_clk_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::RtcSlowClk => request_lp_slow_clk(clocks), + Timg0CalibrationClockConfig::RcFastClk => request_rc_fast_clk(clocks), + Timg0CalibrationClockConfig::Xtal32kClk => request_xtal32k_clk(clocks), + } + } + fn timg0_calibration_clock_release_upstream( + clocks: &mut ClockTree, + selector: Timg0CalibrationClockConfig, + ) { + match selector { + Timg0CalibrationClockConfig::RtcSlowClk => release_lp_slow_clk(clocks), + Timg0CalibrationClockConfig::RcFastClk => release_rc_fast_clk(clocks), + Timg0CalibrationClockConfig::Xtal32kClk => release_xtal32k_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::RtcSlowClk => lp_slow_clk_frequency(clocks), + Timg0CalibrationClockConfig::RcFastClk => rc_fast_clk_frequency(clocks), + Timg0CalibrationClockConfig::Xtal32kClk => xtal32k_clk_frequency(clocks), + } + } + pub fn configure_timg1_function_clock( + clocks: &mut ClockTree, + new_selector: Timg0FunctionClockConfig, + ) { + let old_selector = clocks.timg1_function_clock.replace(new_selector); + if clocks.timg1_function_clock_refcount > 0 { + timg1_function_clock_request_upstream(clocks, new_selector); + configure_timg1_function_clock_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + timg1_function_clock_release_upstream(clocks, old_selector); + } + } else { + configure_timg1_function_clock_impl(clocks, old_selector, new_selector); + } + } + fn timg1_function_clock_request_upstream( + clocks: &mut ClockTree, + selector: Timg0FunctionClockConfig, + ) { + match selector { + Timg0FunctionClockConfig::XtalClk => request_xtal_clk(clocks), + Timg0FunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), + Timg0FunctionClockConfig::PllF48m => request_pll_f48m_clk(clocks), + } + } + fn timg1_function_clock_release_upstream( + clocks: &mut ClockTree, + selector: Timg0FunctionClockConfig, + ) { + match selector { + Timg0FunctionClockConfig::XtalClk => release_xtal_clk(clocks), + Timg0FunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), + Timg0FunctionClockConfig::PllF48m => release_pll_f48m_clk(clocks), + } + } + pub fn request_timg1_function_clock(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.timg1_function_clock_refcount) { + let selector = unwrap!(clocks.timg1_function_clock); + timg1_function_clock_request_upstream(clocks, selector); + enable_timg1_function_clock_impl(clocks, true); + } + } + pub fn release_timg1_function_clock(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.timg1_function_clock_refcount) { + enable_timg1_function_clock_impl(clocks, false); + let selector = unwrap!(clocks.timg1_function_clock); + timg1_function_clock_release_upstream(clocks, selector); + } + } + pub fn timg1_function_clock_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.timg1_function_clock) { + Timg0FunctionClockConfig::XtalClk => xtal_clk_frequency(clocks), + Timg0FunctionClockConfig::RcFastClk => rc_fast_clk_frequency(clocks), + Timg0FunctionClockConfig::PllF48m => pll_f48m_clk_frequency(clocks), + } + } + pub fn configure_timg1_calibration_clock( + clocks: &mut ClockTree, + new_selector: Timg0CalibrationClockConfig, + ) { + let old_selector = clocks.timg1_calibration_clock.replace(new_selector); + if clocks.timg1_calibration_clock_refcount > 0 { + timg1_calibration_clock_request_upstream(clocks, new_selector); + configure_timg1_calibration_clock_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + timg1_calibration_clock_release_upstream(clocks, old_selector); + } + } else { + configure_timg1_calibration_clock_impl(clocks, old_selector, new_selector); + } + } + fn timg1_calibration_clock_request_upstream( + clocks: &mut ClockTree, + selector: Timg0CalibrationClockConfig, + ) { + match selector { + Timg0CalibrationClockConfig::RtcSlowClk => request_lp_slow_clk(clocks), + Timg0CalibrationClockConfig::RcFastClk => request_rc_fast_clk(clocks), + Timg0CalibrationClockConfig::Xtal32kClk => request_xtal32k_clk(clocks), + } + } + fn timg1_calibration_clock_release_upstream( + clocks: &mut ClockTree, + selector: Timg0CalibrationClockConfig, + ) { + match selector { + Timg0CalibrationClockConfig::RtcSlowClk => release_lp_slow_clk(clocks), + Timg0CalibrationClockConfig::RcFastClk => release_rc_fast_clk(clocks), + Timg0CalibrationClockConfig::Xtal32kClk => release_xtal32k_clk(clocks), + } + } + pub fn request_timg1_calibration_clock(clocks: &mut ClockTree) { + if increment_reference_count(&mut clocks.timg1_calibration_clock_refcount) { + let selector = unwrap!(clocks.timg1_calibration_clock); + timg1_calibration_clock_request_upstream(clocks, selector); + enable_timg1_calibration_clock_impl(clocks, true); + } + } + pub fn release_timg1_calibration_clock(clocks: &mut ClockTree) { + if decrement_reference_count(&mut clocks.timg1_calibration_clock_refcount) { + enable_timg1_calibration_clock_impl(clocks, false); + let selector = unwrap!(clocks.timg1_calibration_clock); + timg1_calibration_clock_release_upstream(clocks, selector); + } + } + pub fn timg1_calibration_clock_frequency(clocks: &mut ClockTree) -> u32 { + match unwrap!(clocks.timg1_calibration_clock) { + Timg0CalibrationClockConfig::RtcSlowClk => lp_slow_clk_frequency(clocks), + Timg0CalibrationClockConfig::RcFastClk => rc_fast_clk_frequency(clocks), + Timg0CalibrationClockConfig::Xtal32kClk => xtal32k_clk_frequency(clocks), + } + } /// Clock tree configuration. /// /// The fields of this struct are optional, with the following caveats: @@ -301,10 +1245,47 @@ 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 { + /// `XTAL_CLK` configuration. + pub xtal_clk: Option, + /// `HP_ROOT_CLK` configuration. + pub hp_root_clk: Option, + /// `CPU_CLK` configuration. + pub cpu_clk: Option, + /// `AHB_CLK` configuration. + pub ahb_clk: Option, + /// `APB_CLK` configuration. + pub apb_clk: Option, + /// `LP_FAST_CLK` configuration. + pub lp_fast_clk: Option, + /// `LP_SLOW_CLK` configuration. + pub lp_slow_clk: Option, + } impl ClockConfig { fn apply(&self) { - ClockTree::with(|clocks| {}); + ClockTree::with(|clocks| { + if let Some(config) = self.xtal_clk { + configure_xtal_clk(clocks, config); + } + if let Some(config) = self.hp_root_clk { + configure_hp_root_clk(clocks, config); + } + if let Some(config) = self.cpu_clk { + configure_cpu_clk(clocks, config); + } + if let Some(config) = self.ahb_clk { + configure_ahb_clk(clocks, config); + } + if let Some(config) = self.apb_clk { + configure_apb_clk(clocks, config); + } + if let Some(config) = self.lp_fast_clk { + configure_lp_fast_clk(clocks, config); + } + if let Some(config) = self.lp_slow_clk { + configure_lp_slow_clk(clocks, config); + } + }); } } fn increment_reference_count(refcount: &mut u32) -> bool { diff --git a/esp-metadata/devices/esp32h2.toml b/esp-metadata/devices/esp32h2.toml index 777d7626be2..8869d835000 100644 --- a/esp-metadata/devices/esp32h2.toml +++ b/esp-metadata/devices/esp32h2.toml @@ -118,7 +118,48 @@ memory_map = { ranges = [ { name = "dram2_uninit", start = 0x4083_EFD0, end = 0x4084_FEE0 }, ] } -clocks = { peripheral_clocks = { templates = [ +clocks = { system_clocks = { clock_tree = [ + # High-speed clock sources + { name = "XTAL_CLK", type = "source", values = "32", output = "VALUE * 1_000_000", always_on = true }, + { name = "PLL_F96M_CLK", type = "derived", from = "XTAL_CLK", output = "96_000_000" }, + { name = "PLL_F64M_CLK", type = "divider", output = "PLL_F96M_CLK * 2 / 3" }, + { name = "PLL_F48M_CLK", type = "divider", output = "PLL_F96M_CLK / 2" }, + { name = "RC_FAST_CLK", type = "source", output = "8_000_000" }, + + # Low-speed clocks + { name = "XTAL32K_CLK", type = "source", output = "32768" }, + { name = "OSC_SLOW_CLK", type = "source", output = "32768" }, + { name = "RC_SLOW_CLK", type = "source", output = "130_000" }, + { name = "PLL_LP_CLK", type = "derived", from = "XTAL32K_CLK", output = "8_000_000" }, + + { name = "HP_ROOT_CLK", type = "mux", variants = [ + { name = "PLL96", outputs = "PLL_F96M_CLK" }, + { name = "PLL64", outputs = "PLL_F64M_CLK" }, + { name = "XTAL", outputs = "XTAL_CLK" }, + { name = "RC_FAST", outputs = "RC_FAST_CLK" }, + ] }, + + { name = "CPU_CLK", type = "divider", output = "HP_ROOT_CLK / (DIVISOR + 1)", always_on = true }, # AHB_CLK * integer + { name = "AHB_CLK", type = "divider", output = "HP_ROOT_CLK / (DIVISOR + 1)", always_on = true }, # Max 32M + { name = "APB_CLK", type = "divider", divisors = "0..=255", output = "AHB_CLK / (DIVISOR + 1)" }, + # configurable, but automatic - there's no point in making APB_DECREASE_DIV part of the model as consumers will always see APB_CLK + # { name = "APB_DECREASE_DIV", type = "divider", output = "APB_DIV / (DIVISOR + 1)" }, + + # LP clocks + { name = "XTAL_D2_CLK", type = "divider", output = "XTAL_CLK / 2" }, + { name = "LP_FAST_CLK", type = "mux", variants = [ + { name = "RC_FAST_CLK", outputs = "RC_FAST_CLK" }, + { name = "PLL_LP_CLK", outputs = "PLL_LP_CLK" }, + { name = "XTAL_D2_CLK", outputs = "XTAL_D2_CLK" }, + ] }, + { name = "LP_SLOW_CLK", type = "mux", variants = [ + { name = "XTAL32K_CLK", outputs = "XTAL32K_CLK" }, + { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, + { name = "OSC_SLOW", outputs = "OSC_SLOW_CLK" }, + ] }, + # LP_DYN_SLOW_CLK is LP_SLOW_CLK + # LP_DYN_FAST_CLK is automatically managed based on power mode. Not sure how to model it, some LP peripherals need it. +] }, peripheral_clocks = { templates = [ # templates { name = "default_clk_en_template", value = "{{control}}::regs().{{conf_register}}().modify(|_, w| w.{{clk_en_field}}().bit(enable));" }, { name = "clk_en_template", value = "{{default_clk_en_template}}" }, # Indirection allows appending to the template for TWAI @@ -138,8 +179,20 @@ clocks = { peripheral_clocks = { templates = [ { name = "Uhci0", template_params = { peripheral = "uhci" } }, { name = "Rmt" }, { name = "Ledc" }, - { name = "Timg0", template_params = { clk_en_template = "{{default_clk_en_template}} {{peri_clk_template}}", conf_register = "timergroup0_conf", peripheral = "tg0", peri_clk_template = "{{control}}::regs().timergroup0_timer_clk_conf().modify(|_, w| w.tg0_timer_clk_en().bit(enable));" }, keep_enabled = true }, # used for clock calibration - { name = "Timg1", template_params = { clk_en_template = "{{default_clk_en_template}} {{peri_clk_template}}", conf_register = "timergroup1_conf", peripheral = "tg1", peri_clk_template = "{{control}}::regs().timergroup1_timer_clk_conf().modify(|_, w| w.tg1_timer_clk_en().bit(enable));" } }, + { name = "Timg0", template_params = { clk_en_template = "{{default_clk_en_template}} {{peri_clk_template}}", conf_register = "timergroup0_conf", peripheral = "tg0", peri_clk_template = "{{control}}::regs().timergroup0_timer_clk_conf().modify(|_, w| w.tg0_timer_clk_en().bit(enable));" }, keep_enabled = true, clocks = [ + { name = "FUNCTION_CLOCK", type = "mux", default = "XTAL_CLK", variants = [ + { name = "XTAL_CLK", outputs = "XTAL_CLK" }, + { name = "RC_FAST_CLK", outputs = "RC_FAST_CLK" }, + { name = "PLL_F48M", outputs = "PLL_F48M_CLK" }, + ] }, + { name = "CALIBRATION_CLOCK", type = "mux", variants = [ + { name = "RTC_SLOW_CLK", outputs = "LP_SLOW_CLK" }, + { name = "RC_FAST_CLK", outputs = "RC_FAST_CLK" }, + { name = "XTAL32K_CLK", outputs = "XTAL32K_CLK" }, + ] }, + # TODO: WDT clocks + ] }, + { name = "Timg1", template_params = { clk_en_template = "{{default_clk_en_template}} {{peri_clk_template}}", conf_register = "timergroup1_conf", peripheral = "tg1", peri_clk_template = "{{control}}::regs().timergroup1_timer_clk_conf().modify(|_, w| w.tg1_timer_clk_en().bit(enable));" }, clocks = "Timg0" }, { name = "Systimer", keep_enabled = true }, { name = "Twai0", template_params = { clk_en_template = "{{default_clk_en_template}} {{func_clk_template}}", func_clk_template = "{{control}}::regs().twai0_func_clk_conf().modify(|_, w| w.twai0_func_clk_en().bit(enable));" } }, { name = "I2s0", template_params = { peripheral = "i2s" } },