esp-hal/esp-hal-common/src/clock.rs
2022-11-02 13:11:00 +01:00

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),
},
}
}
}