367 lines
11 KiB
Rust
367 lines
11 KiB
Rust
//! # Clock Control
|
|
use fugit::HertzU32;
|
|
|
|
use crate::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(esp32s2, path = "clocks_ll/esp32s2.rs")]
|
|
#[cfg_attr(esp32s3, path = "clocks_ll/esp32s3.rs")]
|
|
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()
|
|
}
|
|
}
|
|
|
|
/// CPU clock speed
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub enum CpuClock {
|
|
Clock80MHz,
|
|
#[cfg(esp32c2)]
|
|
Clock120MHz,
|
|
#[cfg(not(esp32c2))]
|
|
Clock160MHz,
|
|
#[cfg(not(any(esp32c2, esp32c3)))]
|
|
Clock240MHz,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl Clock for CpuClock {
|
|
fn frequency(&self) -> HertzU32 {
|
|
match self {
|
|
CpuClock::Clock80MHz => HertzU32::MHz(80),
|
|
#[cfg(esp32c2)]
|
|
CpuClock::Clock120MHz => HertzU32::MHz(120),
|
|
#[cfg(not(esp32c2))]
|
|
CpuClock::Clock160MHz => HertzU32::MHz(160),
|
|
#[cfg(not(any(esp32c2, esp32c3)))]
|
|
CpuClock::Clock240MHz => HertzU32::MHz(240),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(unused)]
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub(crate) enum XtalClock {
|
|
#[cfg(esp32)]
|
|
RtcXtalFreq24M,
|
|
#[cfg(any(esp32, esp32c2))]
|
|
RtcXtalFreq26M,
|
|
#[cfg(any(esp32c3, esp32s3))]
|
|
RtcXtalFreq32M,
|
|
RtcXtalFreq40M,
|
|
RtcXtalFreqOther(u32),
|
|
}
|
|
|
|
impl Clock for XtalClock {
|
|
fn frequency(&self) -> HertzU32 {
|
|
match self {
|
|
#[cfg(esp32)]
|
|
XtalClock::RtcXtalFreq24M => HertzU32::MHz(24),
|
|
#[cfg(any(esp32, esp32c2))]
|
|
XtalClock::RtcXtalFreq26M => HertzU32::MHz(26),
|
|
#[cfg(any(esp32c3, esp32s3))]
|
|
XtalClock::RtcXtalFreq32M => HertzU32::MHz(32),
|
|
XtalClock::RtcXtalFreq40M => HertzU32::MHz(40),
|
|
XtalClock::RtcXtalFreqOther(mhz) => HertzU32::MHz(*mhz),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(unused)]
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub(crate) enum PllClock {
|
|
#[cfg(not(esp32c2))]
|
|
Pll320MHz,
|
|
Pll480MHz,
|
|
}
|
|
|
|
#[allow(unused)]
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub(crate) enum ApbClock {
|
|
ApbFreq40MHz,
|
|
ApbFreq80MHz,
|
|
ApbFreqOther(u32),
|
|
}
|
|
|
|
impl Clock for ApbClock {
|
|
fn frequency(&self) -> HertzU32 {
|
|
match self {
|
|
ApbClock::ApbFreq40MHz => HertzU32::MHz(40),
|
|
ApbClock::ApbFreq80MHz => HertzU32::MHz(80),
|
|
ApbClock::ApbFreqOther(mhz) => HertzU32::MHz(*mhz),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Frozen clock frequencies
|
|
///
|
|
/// The existence of this value indicates that the clock configuration can no
|
|
/// longer be changed
|
|
pub struct Clocks {
|
|
_private: (),
|
|
pub cpu_clock: HertzU32,
|
|
pub apb_clock: HertzU32,
|
|
pub xtal_clock: HertzU32,
|
|
pub i2c_clock: HertzU32,
|
|
// TODO chip specific additional ones as needed
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
impl Clocks {
|
|
/// This should not be used in user code.
|
|
/// The whole point this exists is make it possible to have other crates
|
|
/// (i.e. esp-wifi) create `Clocks`
|
|
#[doc(hidden)]
|
|
pub fn from_raw_clocks(raw_clocks: RawClocks) -> Clocks {
|
|
Self {
|
|
_private: (),
|
|
cpu_clock: raw_clocks.cpu_clock,
|
|
apb_clock: raw_clocks.apb_clock,
|
|
xtal_clock: raw_clocks.xtal_clock,
|
|
i2c_clock: raw_clocks.i2c_clock,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
pub struct RawClocks {
|
|
pub cpu_clock: HertzU32,
|
|
pub apb_clock: HertzU32,
|
|
pub xtal_clock: HertzU32,
|
|
pub i2c_clock: HertzU32,
|
|
// TODO chip specific additional ones as needed
|
|
}
|
|
|
|
/// Used to configure the frequencies of the clocks present in the chip.
|
|
///
|
|
/// After setting all frequencies, call the freeze function to apply the
|
|
/// configuration.
|
|
pub struct ClockControl {
|
|
_private: (),
|
|
desired_rates: RawClocks,
|
|
}
|
|
|
|
impl ClockControl {
|
|
/// Applies the clock configuration and returns a Clocks struct that
|
|
/// signifies that the clocks are frozen, and contains the frequencies
|
|
/// used. After this function is called, the clocks can not change
|
|
pub fn freeze(self) -> Clocks {
|
|
Clocks::from_raw_clocks(self.desired_rates)
|
|
}
|
|
}
|
|
|
|
#[cfg(esp32)]
|
|
impl ClockControl {
|
|
/// Use what is considered the default settings after boot.
|
|
#[allow(unused)]
|
|
pub fn boot_defaults(clock_control: SystemClockControl) -> ClockControl {
|
|
ClockControl {
|
|
_private: (),
|
|
desired_rates: RawClocks {
|
|
cpu_clock: HertzU32::MHz(80),
|
|
apb_clock: HertzU32::MHz(80),
|
|
xtal_clock: HertzU32::MHz(40),
|
|
i2c_clock: HertzU32::MHz(80),
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Configure the CPU clock speed.
|
|
#[allow(unused)]
|
|
pub fn configure(clock_control: SystemClockControl, cpu_clock_speed: CpuClock) -> ClockControl {
|
|
// like NuttX use 40M hardcoded - if it turns out to be a problem
|
|
// we will take care then
|
|
let xtal_freq = XtalClock::RtcXtalFreq40M;
|
|
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: (),
|
|
desired_rates: RawClocks {
|
|
cpu_clock: cpu_clock_speed.frequency(),
|
|
apb_clock: HertzU32::MHz(80),
|
|
xtal_clock: HertzU32::MHz(40),
|
|
i2c_clock: HertzU32::MHz(40),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(esp32c2)]
|
|
impl ClockControl {
|
|
/// Use what is considered the default settings after boot.
|
|
#[allow(unused)]
|
|
pub fn boot_defaults(clock_control: SystemClockControl) -> ClockControl {
|
|
ClockControl {
|
|
_private: (),
|
|
desired_rates: RawClocks {
|
|
cpu_clock: HertzU32::MHz(80),
|
|
apb_clock: HertzU32::MHz(40),
|
|
xtal_clock: HertzU32::MHz(40),
|
|
i2c_clock: HertzU32::MHz(40),
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Configure the CPU clock speed.
|
|
#[allow(unused)]
|
|
pub fn configure(clock_control: SystemClockControl, cpu_clock_speed: CpuClock) -> ClockControl {
|
|
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::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: (),
|
|
desired_rates: RawClocks {
|
|
cpu_clock: cpu_clock_speed.frequency(),
|
|
apb_clock: apb_freq.frequency(),
|
|
xtal_clock: xtal_freq.frequency(),
|
|
i2c_clock: HertzU32::MHz(40),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(esp32c3)]
|
|
impl ClockControl {
|
|
/// Use what is considered the default settings after boot.
|
|
#[allow(unused)]
|
|
pub fn boot_defaults(clock_control: SystemClockControl) -> ClockControl {
|
|
ClockControl {
|
|
_private: (),
|
|
desired_rates: RawClocks {
|
|
cpu_clock: HertzU32::MHz(80),
|
|
apb_clock: HertzU32::MHz(80),
|
|
xtal_clock: HertzU32::MHz(40),
|
|
i2c_clock: HertzU32::MHz(40),
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Configure the CPU clock speed.
|
|
#[allow(unused)]
|
|
pub fn configure(clock_control: SystemClockControl, cpu_clock_speed: CpuClock) -> ClockControl {
|
|
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: (),
|
|
desired_rates: RawClocks {
|
|
cpu_clock: cpu_clock_speed.frequency(),
|
|
apb_clock: apb_freq.frequency(),
|
|
xtal_clock: xtal_freq.frequency(),
|
|
i2c_clock: HertzU32::MHz(40),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(esp32s2)]
|
|
impl ClockControl {
|
|
/// Use what is considered the default settings after boot.
|
|
#[allow(unused)]
|
|
pub fn boot_defaults(clock_control: SystemClockControl) -> ClockControl {
|
|
ClockControl {
|
|
_private: (),
|
|
desired_rates: RawClocks {
|
|
cpu_clock: HertzU32::MHz(80),
|
|
apb_clock: HertzU32::MHz(80),
|
|
xtal_clock: HertzU32::MHz(40),
|
|
i2c_clock: HertzU32::MHz(80),
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Configure the CPU clock speed.
|
|
#[allow(unused)]
|
|
pub fn configure(clock_control: SystemClockControl, cpu_clock_speed: CpuClock) -> ClockControl {
|
|
clocks_ll::set_cpu_clock(cpu_clock_speed);
|
|
|
|
ClockControl {
|
|
_private: (),
|
|
desired_rates: RawClocks {
|
|
cpu_clock: cpu_clock_speed.frequency(),
|
|
apb_clock: HertzU32::MHz(80),
|
|
xtal_clock: HertzU32::MHz(40),
|
|
i2c_clock: HertzU32::MHz(40),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(esp32s3)]
|
|
impl ClockControl {
|
|
/// Use what is considered the default settings after boot.
|
|
#[allow(unused)]
|
|
pub fn boot_defaults(clock_control: SystemClockControl) -> ClockControl {
|
|
ClockControl {
|
|
_private: (),
|
|
desired_rates: RawClocks {
|
|
cpu_clock: HertzU32::MHz(80),
|
|
apb_clock: HertzU32::MHz(80),
|
|
xtal_clock: HertzU32::MHz(40),
|
|
i2c_clock: HertzU32::MHz(40),
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Configure the CPU clock speed.
|
|
#[allow(unused)]
|
|
pub fn configure(clock_control: SystemClockControl, cpu_clock_speed: CpuClock) -> ClockControl {
|
|
clocks_ll::set_cpu_clock(cpu_clock_speed);
|
|
|
|
ClockControl {
|
|
_private: (),
|
|
desired_rates: RawClocks {
|
|
cpu_clock: cpu_clock_speed.frequency(),
|
|
apb_clock: HertzU32::MHz(80),
|
|
xtal_clock: HertzU32::MHz(40),
|
|
i2c_clock: HertzU32::MHz(40),
|
|
},
|
|
}
|
|
}
|
|
}
|