esp-hal/esp-hal-common/src/clock/mod.rs

678 lines
22 KiB
Rust

//! # Clock Control
//!
//! ## Overview
//! This `Clock` driver provides an interface for configuring and managing
//! various clocks present on the `ESP` microcontrollers.
//!
//! Proper clock configuration is essential for the correct functioning of the
//! microcontroller and its peripherals.
//!
//! The `Clock` driver supports configuring multiple clocks, including:
//! * CPU clock
//! * APB (Advanced Peripheral Bus) clock
//! * XTAL clock
//! * PLL clock
//!
//! and other specific clocks based on the ESP microcontroller's architecture.
//!
//! The `CPU clock` is responsible for defining the speed at which the central
//! processing unit (CPU) operates. This driver provides predefined options for
//! different CPU clock speeds, such
//! * 80 MHz
//! * 96 MHz
//! * 120 MHz
//! * 160 MHz
//! * 240 MHz
//!
//! and others, depending on the microcontroller model.
//!
//! #### Clock Control
//! The `ClockControl` struct allows users to configure the desired clock
//! frequencies before applying them. It offers flexibility in selecting
//! appropriate clock frequencies based on specific application requirements.
//!
//! #### Frozen clock frequencies
//! Once the clock configuration is applied using the `freeze` function of the
//! ClockControl struct, the clock frequencies become `frozen` and cannot be
//! changed. The `Clocks` struct is returned after freezing, providing read-only
//! access to the configured clock frequencies.
//!
//! ## Examples
//!
//! #### Initialize with default clock frequency for this chip
//! ```no_run
//! let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
//! ```
//!
//! #### Initialize with the highest possible frequency for this chip
//! ```no_run
//! let clocks = ClockControl::max(system.clock_control).freeze();
//! ```
//!
//! #### Initialize with custom clock frequency
//! ```no_run
//! let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock160MHz).freeze();
//! ```
use fugit::HertzU32;
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(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()
}
}
/// CPU clock speed
#[derive(Debug, Clone, Copy)]
pub enum CpuClock {
#[cfg(not(esp32h2))]
Clock80MHz,
#[cfg(esp32h2)]
Clock96MHz,
#[cfg(esp32c2)]
Clock120MHz,
#[cfg(not(any(esp32c2, esp32h2)))]
Clock160MHz,
#[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))]
Clock240MHz,
}
#[allow(dead_code)]
impl Clock for CpuClock {
fn frequency(&self) -> HertzU32 {
match self {
#[cfg(not(esp32h2))]
CpuClock::Clock80MHz => HertzU32::MHz(80),
#[cfg(esp32h2)]
CpuClock::Clock96MHz => HertzU32::MHz(96),
#[cfg(esp32c2)]
CpuClock::Clock120MHz => HertzU32::MHz(120),
#[cfg(not(any(esp32c2, esp32h2)))]
CpuClock::Clock160MHz => HertzU32::MHz(160),
#[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))]
CpuClock::Clock240MHz => HertzU32::MHz(240),
}
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum XtalClock {
#[cfg(esp32)]
RtcXtalFreq24M,
#[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(esp32)]
XtalClock::RtcXtalFreq24M => HertzU32::MHz(24),
#[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(unused)]
#[derive(Debug, Clone, Copy)]
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)))]
Pll320MHz,
#[cfg(not(esp32h2))]
Pll480MHz,
}
#[allow(unused)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum ApbClock {
#[cfg(esp32h2)]
ApbFreq32MHz,
#[cfg(not(esp32h2))]
ApbFreq40MHz,
#[cfg(not(esp32h2))]
ApbFreq80MHz,
ApbFreqOther(u32),
}
impl Clock for ApbClock {
fn frequency(&self) -> HertzU32 {
match self {
#[cfg(esp32h2)]
ApbClock::ApbFreq32MHz => HertzU32::MHz(32),
#[cfg(not(esp32h2))]
ApbClock::ApbFreq40MHz => HertzU32::MHz(40),
#[cfg(not(esp32h2))]
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<'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,
// TODO chip specific additional ones as needed
}
#[doc(hidden)]
impl<'d> Clocks<'d> {
/// 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(
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,
// 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<'d> {
_private: PeripheralRef<'d, SystemClockControl>,
desired_rates: RawClocks,
}
impl<'d> ClockControl<'d> {
/// 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<'d> {
Clocks::from_raw_clocks(self._private, self.desired_rates)
}
}
#[cfg(esp32)]
impl<'d> ClockControl<'d> {
/// Use what is considered the default settings after boot.
pub fn boot_defaults(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
) -> ClockControl<'d> {
#[cfg(feature = "xtal-40mhz")]
return ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: HertzU32::MHz(80),
apb_clock: HertzU32::MHz(80),
xtal_clock: HertzU32::MHz(40),
i2c_clock: HertzU32::MHz(80),
pwm_clock: HertzU32::MHz(160),
},
};
#[cfg(feature = "xtal-26mhz")]
return ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: HertzU32::MHz(80),
apb_clock: HertzU32::MHz(80),
xtal_clock: HertzU32::MHz(26),
i2c_clock: HertzU32::MHz(80),
pwm_clock: HertzU32::MHz(160),
},
};
}
/// Configure the CPU clock speed.
pub fn configure(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
cpu_clock_speed: CpuClock,
) -> ClockControl<'d> {
// like NuttX use 40M hardcoded - if it turns out to be a problem
// we will take care then
#[cfg(feature = "xtal-40mhz")]
let xtal_freq = XtalClock::RtcXtalFreq40M;
#[cfg(feature = "xtal-26mhz")]
let xtal_freq = 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),
// The docs are unclear here. pwm_clock seems to be tied to clocks.apb_clock
// while simultaneously being fixed at 160 MHz.
// Testing showed 160 MHz to be correct for current clock configurations.
pwm_clock: HertzU32::MHz(160),
},
}
}
/// Use the highest possible frequency for a particular chip
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock240MHz)
}
}
#[cfg(esp32c2)]
impl<'d> ClockControl<'d> {
/// Use what is considered the default settings after boot.
pub fn boot_defaults(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
) -> ClockControl<'d> {
#[cfg(feature = "xtal-40mhz")]
return ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: HertzU32::MHz(80),
apb_clock: HertzU32::MHz(40),
xtal_clock: HertzU32::MHz(40),
},
};
#[cfg(feature = "xtal-26mhz")]
return ClockControl {
_private: clock_control.into_ref(),
desired_rates: RawClocks {
cpu_clock: HertzU32::MHz(80),
apb_clock: HertzU32::MHz(40),
xtal_clock: HertzU32::MHz(26),
},
};
}
/// Configure the CPU clock speed.
pub fn configure(
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
cpu_clock_speed: CpuClock,
) -> ClockControl<'d> {
let apb_freq;
#[cfg(feature = "xtal-40mhz")]
let xtal_freq = XtalClock::RtcXtalFreq40M;
#[cfg(feature = "xtal-26mhz")]
let xtal_freq = 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(),
},
}
}
/// Use the highest possible frequency for a particular chip
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock120MHz)
}
}
#[cfg(esp32c3)]
impl<'d> ClockControl<'d> {
/// Use what is considered the default settings after boot.
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),
},
}
}
/// Configure the CPU clock speed.
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(),
},
}
}
/// Use the highest possible frequency for a particular chip
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock160MHz)
}
}
#[cfg(esp32c6)]
impl<'d> ClockControl<'d> {
/// Use what is considered the default settings after boot.
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),
},
}
}
/// Configure the CPU clock speed.
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),
},
}
}
/// Use the highest possible frequency for a particular chip
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock160MHz)
}
}
#[cfg(esp32h2)]
impl<'d> ClockControl<'d> {
/// Use what is considered the default settings after boot.
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),
},
}
}
/// Configure the CPU clock speed.
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),
},
}
}
/// Use the highest possible frequency for a particular chip
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock96MHz)
}
}
#[cfg(esp32s2)]
impl<'d> ClockControl<'d> {
/// Use what is considered the default settings after boot.
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),
},
}
}
/// Configure the CPU clock speed.
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),
},
}
}
/// Use the highest possible frequency for a particular chip
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock240MHz)
}
}
#[cfg(esp32s3)]
impl<'d> ClockControl<'d> {
/// Use what is considered the default settings after boot.
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),
},
}
}
/// Configure the CPU clock speed.
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),
},
}
}
/// Use the highest possible frequency for a particular chip
pub fn max(clock_control: impl Peripheral<P = SystemClockControl> + 'd) -> ClockControl<'d> {
Self::configure(clock_control, CpuClock::Clock240MHz)
}
}