use crate::clock::Hertz;
use mik32_pac::pm::ahb_mux::{AhbClkMux, ForceMux};
use mik32_pac::pm::cpu_rtc_clk_mux::CpuRtcClkMux;
use mik32_pac::wake_up::clocks_bu::RtcClkMux;
use mik32_pac::wake_up::clocks_sys::Force32kClk;
use mik32_pac::{Peripherals, Pm, WakeUp};
pub const HSI32M_FREQ: Hertz = Hertz(32_000_000);
pub const OSC32M_FREQ: Hertz = Hertz(32_000_000);
pub const LSI32K_FREQ: Hertz = Hertz(32_768);
pub const OSC32K_FREQ: Hertz = Hertz(32_768);
const CLOCKSWITCH_TIMEOUT_VALUE: u32 = 500_000;
const SWITCH_SETTLE_CYCLES: u32 = 100;
const LSI32K_CALIBRATION_MAX: u8 = 15;
#[derive(Debug, Clone, Copy)]
pub struct Clocks {
sys: Hertz,
ahb: Hertz,
apb_m: Hertz,
apb_p: Hertz,
ahb_div: u32,
apb_m_div: u32,
apb_p_div: u32,
}
impl Clocks {
pub const fn new(sys: Hertz, ahb_div: u32) -> Self {
Self::from_config(sys, ahb_div, 0, 0)
}
pub const fn from_config(sys: Hertz, ahb_div: u32, apb_m_div: u32, apb_p_div: u32) -> Self {
let ahb = Hertz(sys.0 / (ahb_div + 1));
Self {
sys,
ahb,
apb_m: Hertz(ahb.0 / (apb_m_div + 1)),
apb_p: Hertz(ahb.0 / (apb_p_div + 1)),
ahb_div,
apb_m_div,
apb_p_div,
}
}
pub fn sysclk(&self) -> Hertz {
self.sys
}
pub fn ahbclk(&self) -> Hertz {
self.ahb
}
pub fn apb_m_clk(&self) -> Hertz {
self.apb_m
}
pub fn apb_p_clk(&self) -> Hertz {
self.apb_p
}
pub fn ahb_div_clk(&self) -> u32 {
self.ahb_div
}
pub fn apb_m_div_clk(&self) -> u32 {
self.apb_m_div
}
pub fn apb_p_div_clk(&self) -> u32 {
self.apb_p_div
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClockSource {
Hsi32m,
Osc32m,
Lsi32k,
Osc32k,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClockSwitchStage {
FreqMonitorRef,
SystemClock,
RtcClock,
CpuRtcClock,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
DisabledClockSelected(ClockSource),
No32kClockEnabled,
ClockNotReady {
stage: ClockSwitchStage,
source: ClockSource,
},
InvalidCalibration {
source: ClockSource,
value: u8,
},
}
pub struct FreqMonitor {
pub sys: AhbClkMux,
pub force_osc_sys: ForceMux,
pub force32k_clk: Force32kClk,
}
impl Default for FreqMonitor {
fn default() -> Self {
Self {
sys: AhbClkMux::Osc32m,
force_osc_sys: ForceMux::Unfixed,
force32k_clk: Force32kClk::Automatic,
}
}
}
pub struct RCC {
pub hsi32m: bool,
pub osc32m: bool,
pub lsi32k: bool,
pub osc32k: bool,
pub freq_monitor: FreqMonitor,
pub ahb_div: u8,
pub apb_m_div: u8,
pub apb_p_div: u8,
pub hsi32m_calibration_value: u8,
pub lsi32k_calibration_value: u8,
pub rtcclk: RtcClkMux,
pub rtccpuclk: CpuRtcClkMux,
pub clocks: Clocks,
}
impl Default for RCC {
fn default() -> Self {
Self {
hsi32m: true,
osc32m: true,
lsi32k: true,
osc32k: true,
freq_monitor: FreqMonitor::default(),
ahb_div: 0,
apb_m_div: 0,
apb_p_div: 0,
hsi32m_calibration_value: 128,
lsi32k_calibration_value: 8,
rtcclk: RtcClkMux::Automatic,
rtccpuclk: CpuRtcClkMux::Osc32k,
clocks: Clocks::from_config(OSC32M_FREQ, 0, 0, 0),
}
}
}
impl RCC {
pub fn init(config: &RCC) -> Result<Clocks, Error> {
Self::validate(config)?;
let wu = unsafe { WakeUp::steal() };
let pm = unsafe { Pm::steal() };
let mut first_error = None;
wu.clocks_sys()
.modify(|_, w| w.hsi32m_en().enable().osc32m_en().enable());
wu.clocks_bu()
.modify(|_, w| w.lsi32k_en().enable().osc32k_en().enable());
wu.clocks_sys()
.modify(|_, w| unsafe { w.adj_hsi32m().bits(config.hsi32m_calibration_value) });
wu.clocks_bu()
.modify(|_, w| unsafe { w.adj_lsi32k().bits(config.lsi32k_calibration_value) });
match config.freq_monitor.force32k_clk {
Force32kClk::Automatic => {
wu.clocks_sys().modify(|_, w| w.force_32k_clk().automatic());
}
Force32kClk::Lsi32k => {
if Self::wait_freq_status(&pm, ClockSource::Lsi32k) {
wu.clocks_sys().modify(|_, w| w.force_32k_clk().lsi32k());
Self::switch_settle_delay();
} else {
Self::record_error(
&mut first_error,
Error::ClockNotReady {
stage: ClockSwitchStage::FreqMonitorRef,
source: ClockSource::Lsi32k,
},
);
}
}
Force32kClk::Osc32k => {
if Self::wait_freq_status(&pm, ClockSource::Osc32k) {
wu.clocks_sys().modify(|_, w| w.force_32k_clk().osc32k());
Self::switch_settle_delay();
} else {
Self::record_error(
&mut first_error,
Error::ClockNotReady {
stage: ClockSwitchStage::FreqMonitorRef,
source: ClockSource::Osc32k,
},
);
}
}
}
let system_source = Self::source_for_ahb(config.freq_monitor.sys);
if Self::wait_freq_status(&pm, system_source) {
pm.ahb_mux().modify(|_, w| {
let w = match config.freq_monitor.sys {
AhbClkMux::Osc32m => w.ahb_clk_mux().osc32m(),
AhbClkMux::Hsi32m => w.ahb_clk_mux().hsi32m(),
AhbClkMux::Osc32k => w.ahb_clk_mux().osc32k(),
AhbClkMux::Lsi32k => w.ahb_clk_mux().lsi32k(),
};
match config.freq_monitor.force_osc_sys {
ForceMux::Unfixed => w.force_mux().unfixed(),
ForceMux::Fixed => w.force_mux().fixed(),
}
});
Self::switch_settle_delay();
} else {
pm.ahb_mux().modify(|_, w| match config.freq_monitor.sys {
AhbClkMux::Osc32m => w.ahb_clk_mux().osc32m().force_mux().unfixed(),
AhbClkMux::Hsi32m => w.ahb_clk_mux().hsi32m().force_mux().unfixed(),
AhbClkMux::Osc32k => w.ahb_clk_mux().osc32k().force_mux().unfixed(),
AhbClkMux::Lsi32k => w.ahb_clk_mux().lsi32k().force_mux().unfixed(),
});
Self::record_error(
&mut first_error,
Error::ClockNotReady {
stage: ClockSwitchStage::SystemClock,
source: system_source,
},
);
}
pm.div_ahb()
.modify(|_, w| unsafe { w.bits(config.ahb_div as u32) });
pm.div_apb_m()
.modify(|_, w| unsafe { w.bits(config.apb_m_div as u32) });
pm.div_apb_p()
.modify(|_, w| unsafe { w.bits(config.apb_p_div as u32) });
match config.rtcclk {
RtcClkMux::Automatic => {
wu.clocks_bu().modify(|_, w| w.rtc_clk_mux().automatic());
}
RtcClkMux::Lsi32k => {
if Self::wait_freq_status(&pm, ClockSource::Lsi32k) {
wu.clocks_bu().modify(|_, w| w.rtc_clk_mux().lsi32k());
Self::pulse_rtc_reset(&wu);
Self::switch_settle_delay();
} else {
Self::record_error(
&mut first_error,
Error::ClockNotReady {
stage: ClockSwitchStage::RtcClock,
source: ClockSource::Lsi32k,
},
);
}
}
RtcClkMux::Osc32k => {
if Self::wait_freq_status(&pm, ClockSource::Osc32k) {
wu.clocks_bu().modify(|_, w| w.rtc_clk_mux().osc32k());
Self::pulse_rtc_reset(&wu);
Self::switch_settle_delay();
} else {
Self::record_error(
&mut first_error,
Error::ClockNotReady {
stage: ClockSwitchStage::RtcClock,
source: ClockSource::Osc32k,
},
);
}
}
}
let cpu_rtc_source = Self::source_for_cpu_rtc(config.rtccpuclk);
if Self::wait_freq_status(&pm, cpu_rtc_source) {
pm.cpu_rtc_clk_mux().modify(|_, w| match config.rtccpuclk {
CpuRtcClkMux::Osc32k => w.cpu_rtc_clk_mux().osc32k(),
CpuRtcClkMux::Lsi32k => w.cpu_rtc_clk_mux().lsi32k(),
});
Self::switch_settle_delay();
} else {
Self::record_error(
&mut first_error,
Error::ClockNotReady {
stage: ClockSwitchStage::CpuRtcClock,
source: cpu_rtc_source,
},
);
}
if !config.osc32m {
wu.clocks_sys().modify(|_, w| w.osc32m_en().disable());
}
if !config.hsi32m {
wu.clocks_sys().modify(|_, w| w.hsi32m_en().disable());
}
if !config.osc32k {
wu.clocks_bu().modify(|_, w| w.osc32k_en().disable());
}
if !config.lsi32k {
wu.clocks_bu().modify(|_, w| w.lsi32k_en().disable());
}
if let Some(error) = first_error {
Err(error)
} else {
Ok(Self::clocks(config))
}
}
pub fn clocks(config: &RCC) -> Clocks {
Clocks::from_config(
Self::sysclk_for(config.freq_monitor.sys),
config.ahb_div as u32,
config.apb_m_div as u32,
config.apb_p_div as u32,
)
}
fn validate(config: &RCC) -> Result<(), Error> {
if config.lsi32k_calibration_value > LSI32K_CALIBRATION_MAX {
return Err(Error::InvalidCalibration {
source: ClockSource::Lsi32k,
value: config.lsi32k_calibration_value,
});
}
Self::ensure_enabled(config, Self::source_for_ahb(config.freq_monitor.sys))?;
match config.freq_monitor.force32k_clk {
Force32kClk::Automatic => Self::ensure_any_32k_enabled(config)?,
Force32kClk::Lsi32k => Self::ensure_enabled(config, ClockSource::Lsi32k)?,
Force32kClk::Osc32k => Self::ensure_enabled(config, ClockSource::Osc32k)?,
}
match config.rtcclk {
RtcClkMux::Automatic => Self::ensure_any_32k_enabled(config)?,
RtcClkMux::Lsi32k => Self::ensure_enabled(config, ClockSource::Lsi32k)?,
RtcClkMux::Osc32k => Self::ensure_enabled(config, ClockSource::Osc32k)?,
}
match config.rtccpuclk {
CpuRtcClkMux::Osc32k => Self::ensure_enabled(config, ClockSource::Osc32k)?,
CpuRtcClkMux::Lsi32k => Self::ensure_enabled(config, ClockSource::Lsi32k)?,
}
Ok(())
}
fn ensure_any_32k_enabled(config: &RCC) -> Result<(), Error> {
if config.lsi32k || config.osc32k {
Ok(())
} else {
Err(Error::No32kClockEnabled)
}
}
fn ensure_enabled(config: &RCC, source: ClockSource) -> Result<(), Error> {
let enabled = match source {
ClockSource::Hsi32m => config.hsi32m,
ClockSource::Osc32m => config.osc32m,
ClockSource::Lsi32k => config.lsi32k,
ClockSource::Osc32k => config.osc32k,
};
if enabled {
Ok(())
} else {
Err(Error::DisabledClockSelected(source))
}
}
fn source_for_ahb(source: AhbClkMux) -> ClockSource {
match source {
AhbClkMux::Osc32m => ClockSource::Osc32m,
AhbClkMux::Hsi32m => ClockSource::Hsi32m,
AhbClkMux::Osc32k => ClockSource::Osc32k,
AhbClkMux::Lsi32k => ClockSource::Lsi32k,
}
}
fn source_for_cpu_rtc(source: CpuRtcClkMux) -> ClockSource {
match source {
CpuRtcClkMux::Osc32k => ClockSource::Osc32k,
CpuRtcClkMux::Lsi32k => ClockSource::Lsi32k,
}
}
fn wait_freq_status(pm: &Pm, source: ClockSource) -> bool {
for _ in 0..CLOCKSWITCH_TIMEOUT_VALUE {
if Self::freq_status_ready(pm, source) {
return true;
}
}
false
}
fn freq_status_ready(pm: &Pm, source: ClockSource) -> bool {
let status = pm.freq_status().read();
match source {
ClockSource::Hsi32m => status.mask_hsi32m().bit_is_set(),
ClockSource::Osc32m => status.mask_osc32m().bit_is_set(),
ClockSource::Lsi32k => status.mask_lsi32k().bit_is_set(),
ClockSource::Osc32k => status.mask_osc32k().bit_is_set(),
}
}
fn pulse_rtc_reset(wu: &WakeUp) {
wu.rtc_control().write(|w| unsafe { w.bits(1) });
wu.rtc_control().write(|w| unsafe { w.bits(0) });
}
fn switch_settle_delay() {
for _ in 0..SWITCH_SETTLE_CYCLES {
core::hint::spin_loop();
}
}
fn record_error(slot: &mut Option<Error>, error: Error) {
if slot.is_none() {
*slot = Some(error);
}
}
fn sysclk_for(source: AhbClkMux) -> Hertz {
match source {
AhbClkMux::Osc32m => OSC32M_FREQ,
AhbClkMux::Hsi32m => HSI32M_FREQ,
AhbClkMux::Osc32k => OSC32K_FREQ,
AhbClkMux::Lsi32k => LSI32K_FREQ,
}
}
}
pub fn system_clock() -> Hertz {
let p = unsafe { Peripherals::steal() };
match p.pm.ahb_mux().read().ahb_clk_mux().variant() {
AhbClkMux::Osc32m => OSC32M_FREQ,
AhbClkMux::Hsi32m => HSI32M_FREQ,
AhbClkMux::Osc32k => OSC32K_FREQ,
AhbClkMux::Lsi32k => LSI32K_FREQ,
}
}
pub fn clocks() -> Clocks {
let p = unsafe { Peripherals::steal() };
Clocks::from_config(
system_clock(),
p.pm.div_ahb().read().bits(),
p.pm.div_apb_m().read().bits(),
p.pm.div_apb_p().read().bits(),
)
}