use fugit::HertzU32;
#[cfg(any(esp32, esp32c2))]
use crate::rtc_cntl::RtcClock;
use crate::{
peripheral::{Peripheral, PeripheralRef},
system::SystemClockControl,
};
#[cfg_attr(esp32, path = "clocks_ll/esp32.rs")]
#[cfg_attr(esp32c2, path = "clocks_ll/esp32c2.rs")]
#[cfg_attr(esp32c3, path = "clocks_ll/esp32c3.rs")]
#[cfg_attr(esp32c6, path = "clocks_ll/esp32c6.rs")]
#[cfg_attr(esp32h2, path = "clocks_ll/esp32h2.rs")]
#[cfg_attr(esp32p4, path = "clocks_ll/esp32p4.rs")]
#[cfg_attr(esp32s2, path = "clocks_ll/esp32s2.rs")]
#[cfg_attr(esp32s3, path = "clocks_ll/esp32s3.rs")]
pub(crate) mod clocks_ll;
pub trait Clock {
fn frequency(&self) -> HertzU32;
fn mhz(&self) -> u32 {
self.frequency().to_MHz()
}
fn hz(&self) -> u32 {
self.frequency().to_Hz()
}
}
#[derive(Debug, Clone, Copy)]
pub enum CpuClock {
#[cfg(not(any(esp32h2, esp32p4)))]
Clock80MHz,
#[cfg(esp32p4)]
Clock90MHz,
#[cfg(esp32h2)]
Clock96MHz,
#[cfg(esp32c2)]
Clock120MHz,
#[cfg(not(any(esp32c2, esp32h2, esp32p4)))]
Clock160MHz,
#[cfg(esp32p4)]
Clock180MHz,
#[cfg(xtensa)]
Clock240MHz,
#[cfg(esp32p4)]
Clock360MHz,
#[cfg(esp32p4)]
Clock400MHz,
}
#[allow(dead_code)]
impl Clock for CpuClock {
fn frequency(&self) -> HertzU32 {
match self {
#[cfg(not(any(esp32h2, esp32p4)))]
CpuClock::Clock80MHz => HertzU32::MHz(80),
#[cfg(esp32p4)]
CpuClock::Clock90MHz => HertzU32::MHz(90),
#[cfg(esp32h2)]
CpuClock::Clock96MHz => HertzU32::MHz(96),
#[cfg(esp32c2)]
CpuClock::Clock120MHz => HertzU32::MHz(120),
#[cfg(not(any(esp32c2, esp32h2, esp32p4)))]
CpuClock::Clock160MHz => HertzU32::MHz(160),
#[cfg(esp32p4)]
CpuClock::Clock180MHz => HertzU32::MHz(180),
#[cfg(xtensa)]
CpuClock::Clock240MHz => HertzU32::MHz(240),
#[cfg(esp32p4)]
CpuClock::Clock360MHz => HertzU32::MHz(360),
#[cfg(esp32p4)]
CpuClock::Clock400MHz => HertzU32::MHz(400),
}
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum XtalClock {
#[cfg(any(esp32, esp32c2))]
RtcXtalFreq26M,
#[cfg(any(esp32c3, esp32h2, esp32s3))]
RtcXtalFreq32M,
#[cfg(not(esp32h2))]
RtcXtalFreq40M,
RtcXtalFreqOther(u32),
}
impl Clock for XtalClock {
fn frequency(&self) -> HertzU32 {
match self {
#[cfg(any(esp32, esp32c2))]
XtalClock::RtcXtalFreq26M => HertzU32::MHz(26),
#[cfg(any(esp32c3, esp32h2, esp32s3))]
XtalClock::RtcXtalFreq32M => HertzU32::MHz(32),
#[cfg(not(esp32h2))]
XtalClock::RtcXtalFreq40M => HertzU32::MHz(40),
XtalClock::RtcXtalFreqOther(mhz) => HertzU32::MHz(*mhz),
}
}
}
#[allow(clippy::enum_variant_names, unused)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum PllClock {
#[cfg(esp32h2)]
Pll8MHz,
#[cfg(any(esp32c6, esp32h2))]
Pll48MHz,
#[cfg(esp32h2)]
Pll64MHz,
#[cfg(esp32c6)]
Pll80MHz,
#[cfg(esp32h2)]
Pll96MHz,
#[cfg(esp32c6)]
Pll120MHz,
#[cfg(esp32c6)]
Pll160MHz,
#[cfg(esp32c6)]
Pll240MHz,
#[cfg(not(any(esp32c2, esp32c6, esp32h2, esp32p4)))]
Pll320MHz,
#[cfg(esp32p4)]
Pll360MHz,
#[cfg(esp32p4)]
Pll400MHz,
#[cfg(not(esp32h2))]
Pll480MHz,
#[cfg(esp32p4)]
Pll500MHz,
}
impl Clock for PllClock {
fn frequency(&self) -> HertzU32 {
match self {
#[cfg(esp32h2)]
Self::Pll8MHz => HertzU32::MHz(8),
#[cfg(any(esp32c6, esp32h2))]
Self::Pll48MHz => HertzU32::MHz(48),
#[cfg(esp32h2)]
Self::Pll64MHz => HertzU32::MHz(64),
#[cfg(esp32c6)]
Self::Pll80MHz => HertzU32::MHz(80),
#[cfg(esp32h2)]
Self::Pll96MHz => HertzU32::MHz(96),
#[cfg(esp32c6)]
Self::Pll120MHz => HertzU32::MHz(120),
#[cfg(esp32c6)]
Self::Pll160MHz => HertzU32::MHz(160),
#[cfg(esp32c6)]
Self::Pll240MHz => HertzU32::MHz(240),
#[cfg(not(any(esp32c2, esp32c6, esp32h2, esp32p4)))]
Self::Pll320MHz => HertzU32::MHz(320),
#[cfg(esp32p4)]
Self::Pll360MHz => HertzU32::MHz(360),
#[cfg(esp32p4)]
Self::Pll400MHz => HertzU32::MHz(400),
#[cfg(not(esp32h2))]
Self::Pll480MHz => HertzU32::MHz(480),
#[cfg(esp32p4)]
Self::Pll500MHz => HertzU32::MHz(500),
}
}
}
#[allow(unused)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum ApbClock {
#[cfg(esp32h2)]
ApbFreq32MHz,
#[cfg(not(any(esp32h2, esp32p4)))]
ApbFreq40MHz,
#[cfg(not(any(esp32h2, esp32p4)))]
ApbFreq80MHz,
#[cfg(esp32p4)]
ApbFreq100MHz,
ApbFreqOther(u32),
}
impl Clock for ApbClock {
fn frequency(&self) -> HertzU32 {
match self {
#[cfg(esp32h2)]
ApbClock::ApbFreq32MHz => HertzU32::MHz(32),
#[cfg(not(any(esp32h2, esp32p4)))]
ApbClock::ApbFreq40MHz => HertzU32::MHz(40),
#[cfg(not(any(esp32h2, esp32p4)))]
ApbClock::ApbFreq80MHz => HertzU32::MHz(80),
#[cfg(esp32p4)]
ApbClock::ApbFreq100MHz => HertzU32::MHz(100),
ApbClock::ApbFreqOther(mhz) => HertzU32::MHz(*mhz),
}
}
}
pub struct Clocks<'d> {
_private: PeripheralRef<'d, SystemClockControl>,
pub cpu_clock: HertzU32,
pub apb_clock: HertzU32,
pub xtal_clock: HertzU32,
#[cfg(esp32)]
pub i2c_clock: HertzU32,
#[cfg(esp32)]
pub pwm_clock: HertzU32,
#[cfg(esp32s3)]
pub crypto_pwm_clock: HertzU32,
#[cfg(any(esp32c6, esp32h2))]
pub crypto_clock: HertzU32,
#[cfg(esp32h2)]
pub pll_48m_clock: HertzU32,
#[cfg(esp32h2)]
pub pll_96m_clock: HertzU32,
}
#[doc(hidden)]
impl<'d> Clocks<'d> {
#[doc(hidden)]
pub fn from_raw_clocks(
system_clock_control: PeripheralRef<'d, SystemClockControl>,
raw_clocks: RawClocks,
) -> Clocks<'d> {
Self {
_private: system_clock_control,
cpu_clock: raw_clocks.cpu_clock,
apb_clock: raw_clocks.apb_clock,
xtal_clock: raw_clocks.xtal_clock,
#[cfg(esp32)]
i2c_clock: raw_clocks.i2c_clock,
#[cfg(esp32)]
pwm_clock: raw_clocks.pwm_clock,
#[cfg(esp32s3)]
crypto_pwm_clock: raw_clocks.crypto_pwm_clock,
#[cfg(any(esp32c6, esp32h2))]
crypto_clock: raw_clocks.crypto_clock,
#[cfg(esp32h2)]
pll_48m_clock: raw_clocks.pll_48m_clock,
#[cfg(esp32h2)]
pll_96m_clock: raw_clocks.pll_96m_clock,
}
}
}
#[doc(hidden)]
pub struct RawClocks {
pub cpu_clock: HertzU32,
pub apb_clock: HertzU32,
pub xtal_clock: HertzU32,
#[cfg(esp32)]
pub i2c_clock: HertzU32,
#[cfg(esp32)]
pub pwm_clock: HertzU32,
#[cfg(esp32s3)]
pub crypto_pwm_clock: HertzU32,
#[cfg(any(esp32c6, esp32h2))]
pub crypto_clock: HertzU32,
#[cfg(esp32h2)]
pub pll_48m_clock: HertzU32,
#[cfg(esp32h2)]
pub pll_96m_clock: HertzU32,
}
pub struct ClockControl<'d> {
_private: PeripheralRef<'d, SystemClockControl>,
desired_rates: RawClocks,
}
impl<'d> ClockControl<'d> {
pub fn freeze(self) -> Clocks<'d> {
Clocks::from_raw_clocks(self._private, self.desired_rates)
}
}
#[cfg(esp32)]
impl<'d> ClockControl<'d> {
pub fn boot_defaults(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
) -> ClockControl<'d> {
let xtal_freq = if RtcClock::estimate_xtal_frequency() > 33 {
40
} else {
26
};
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: HertzU32::MHz(80),
apb_clock: HertzU32::MHz(80),
xtal_clock: HertzU32::MHz(xtal_freq),
i2c_clock: HertzU32::MHz(80),
pwm_clock: HertzU32::MHz(160),
},
}
}
pub fn configure(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
cpu_clock_speed: CpuClock,
) -> ClockControl<'d> {
let xtal_freq = if RtcClock::estimate_xtal_frequency() > 33 {
XtalClock::RtcXtalFreq40M
} else {
XtalClock::RtcXtalFreq26M
};
let pll_freq = match cpu_clock_speed {
CpuClock::Clock80MHz => PllClock::Pll320MHz,
CpuClock::Clock160MHz => PllClock::Pll320MHz,
CpuClock::Clock240MHz => PllClock::Pll480MHz,
};
clocks_ll::esp32_rtc_update_to_xtal(xtal_freq, 1);
clocks_ll::esp32_rtc_bbpll_enable();
clocks_ll::esp32_rtc_bbpll_configure(xtal_freq, pll_freq);
clocks_ll::set_cpu_freq(cpu_clock_speed);
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: cpu_clock_speed.frequency(),
apb_clock: HertzU32::MHz(80),
xtal_clock: HertzU32::MHz(40),
i2c_clock: HertzU32::MHz(40),
pwm_clock: HertzU32::MHz(160),
},
}
}
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock240MHz)
}
}
#[cfg(esp32c2)]
impl<'d> ClockControl<'d> {
pub fn boot_defaults(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
) -> ClockControl<'d> {
let xtal_freq = if RtcClock::estimate_xtal_frequency() > 33 {
40
} else {
26
};
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: HertzU32::MHz(80),
apb_clock: HertzU32::MHz(40),
xtal_clock: HertzU32::MHz(xtal_freq),
},
}
}
pub fn configure(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
cpu_clock_speed: CpuClock,
) -> ClockControl<'d> {
let apb_freq;
let xtal_freq = if RtcClock::estimate_xtal_frequency() > 33 {
XtalClock::RtcXtalFreq40M
} else {
XtalClock::RtcXtalFreq26M
};
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);
}
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: cpu_clock_speed.frequency(),
apb_clock: apb_freq.frequency(),
xtal_clock: xtal_freq.frequency(),
},
}
}
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock120MHz)
}
}
#[cfg(esp32c3)]
impl<'d> ClockControl<'d> {
pub fn boot_defaults(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
) -> ClockControl<'d> {
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: HertzU32::MHz(80),
apb_clock: HertzU32::MHz(80),
xtal_clock: HertzU32::MHz(40),
},
}
}
pub fn configure(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
cpu_clock_speed: CpuClock,
) -> ClockControl<'d> {
let apb_freq;
let xtal_freq = XtalClock::RtcXtalFreq40M;
let pll_freq = PllClock::Pll480MHz;
if cpu_clock_speed.mhz() <= xtal_freq.mhz() {
apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz());
clocks_ll::esp32c3_rtc_update_to_xtal(xtal_freq, 1);
clocks_ll::esp32c3_rtc_apb_freq_update(apb_freq);
} else {
apb_freq = ApbClock::ApbFreq80MHz;
clocks_ll::esp32c3_rtc_bbpll_enable();
clocks_ll::esp32c3_rtc_bbpll_configure(xtal_freq, pll_freq);
clocks_ll::esp32c3_rtc_freq_to_pll_mhz(cpu_clock_speed);
clocks_ll::esp32c3_rtc_apb_freq_update(apb_freq);
}
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: cpu_clock_speed.frequency(),
apb_clock: apb_freq.frequency(),
xtal_clock: xtal_freq.frequency(),
},
}
}
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock160MHz)
}
}
#[cfg(esp32c6)]
impl<'d> ClockControl<'d> {
pub fn boot_defaults(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
) -> ClockControl<'d> {
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: HertzU32::MHz(80),
apb_clock: HertzU32::MHz(80),
xtal_clock: HertzU32::MHz(40),
crypto_clock: HertzU32::MHz(160),
},
}
}
pub fn configure(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
cpu_clock_speed: CpuClock,
) -> ClockControl<'d> {
let apb_freq;
let xtal_freq = XtalClock::RtcXtalFreq40M;
let pll_freq = PllClock::Pll480MHz;
if cpu_clock_speed.mhz() <= xtal_freq.mhz() {
apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz());
clocks_ll::esp32c6_rtc_update_to_xtal(xtal_freq, 1);
clocks_ll::esp32c6_rtc_apb_freq_update(apb_freq);
} else {
apb_freq = ApbClock::ApbFreq80MHz;
clocks_ll::esp32c6_rtc_bbpll_enable();
clocks_ll::esp32c6_rtc_bbpll_configure(xtal_freq, pll_freq);
clocks_ll::esp32c6_rtc_freq_to_pll_mhz(cpu_clock_speed);
clocks_ll::esp32c6_rtc_apb_freq_update(apb_freq);
}
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: cpu_clock_speed.frequency(),
apb_clock: apb_freq.frequency(),
xtal_clock: xtal_freq.frequency(),
crypto_clock: HertzU32::MHz(160),
},
}
}
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock160MHz)
}
}
#[cfg(esp32h2)]
impl<'d> ClockControl<'d> {
pub fn boot_defaults(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
) -> ClockControl<'d> {
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: HertzU32::MHz(96),
apb_clock: HertzU32::MHz(32),
xtal_clock: HertzU32::MHz(32),
pll_48m_clock: HertzU32::MHz(48),
crypto_clock: HertzU32::MHz(96),
pll_96m_clock: HertzU32::MHz(96),
},
}
}
pub fn configure(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
cpu_clock_speed: CpuClock,
) -> ClockControl<'d> {
let apb_freq;
let xtal_freq = XtalClock::RtcXtalFreq32M;
let pll_freq = PllClock::Pll96MHz;
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 {
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);
}
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: cpu_clock_speed.frequency(),
apb_clock: apb_freq.frequency(),
xtal_clock: xtal_freq.frequency(),
pll_48m_clock: HertzU32::MHz(48),
crypto_clock: HertzU32::MHz(96),
pll_96m_clock: HertzU32::MHz(96),
},
}
}
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock96MHz)
}
}
#[cfg(esp32p4)]
impl<'d> ClockControl<'d> {
pub fn boot_defaults(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
) -> ClockControl<'d> {
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: HertzU32::MHz(400),
apb_clock: HertzU32::MHz(100),
xtal_clock: HertzU32::MHz(40),
},
}
}
pub fn configure(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
cpu_clock_speed: CpuClock,
) -> ClockControl<'d> {
let apb_freq;
let xtal_freq = XtalClock::RtcXtalFreq40M;
let pll_freq = PllClock::Pll480MHz;
if cpu_clock_speed.mhz() <= xtal_freq.mhz() {
apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz());
clocks_ll::esp32p4_rtc_update_to_xtal(xtal_freq, 1, true);
} else {
apb_freq = ApbClock::ApbFreq100MHz;
clocks_ll::esp32p4_rtc_cpll_enable();
clocks_ll::esp32p4_rtc_update_to_xtal(xtal_freq, 1, false);
clocks_ll::esp32p4_rtc_cpll_configure(xtal_freq, pll_freq);
clocks_ll::esp32p4_rtc_freq_to_cpll_mhz(cpu_clock_speed);
}
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: cpu_clock_speed.frequency(),
apb_clock: apb_freq.frequency(),
xtal_clock: xtal_freq.frequency(),
},
}
}
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock400MHz)
}
}
#[cfg(esp32s2)]
impl<'d> ClockControl<'d> {
pub fn boot_defaults(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
) -> ClockControl<'d> {
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: HertzU32::MHz(80),
apb_clock: HertzU32::MHz(80),
xtal_clock: HertzU32::MHz(40),
},
}
}
pub fn configure(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
cpu_clock_speed: CpuClock,
) -> ClockControl<'d> {
clocks_ll::set_cpu_clock(cpu_clock_speed);
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: cpu_clock_speed.frequency(),
apb_clock: HertzU32::MHz(80),
xtal_clock: HertzU32::MHz(40),
},
}
}
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock240MHz)
}
}
#[cfg(esp32s3)]
impl<'d> ClockControl<'d> {
pub fn boot_defaults(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
) -> ClockControl<'d> {
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: HertzU32::MHz(80),
apb_clock: HertzU32::MHz(80),
xtal_clock: HertzU32::MHz(40),
crypto_pwm_clock: HertzU32::MHz(160),
},
}
}
pub fn configure(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
cpu_clock_speed: CpuClock,
) -> ClockControl<'d> {
clocks_ll::set_cpu_clock(cpu_clock_speed);
ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: cpu_clock_speed.frequency(),
apb_clock: HertzU32::MHz(80),
xtal_clock: HertzU32::MHz(40),
crypto_pwm_clock: HertzU32::MHz(160),
},
}
}
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock240MHz)
}
}