pub use crate::pac::pwr::vals::Vos as VoltageScale;
#[cfg(all(peri_usb_otg_hs))]
pub use crate::pac::rcc::vals::Otghssel;
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul,
Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,
};
use crate::pac::rcc::vals::{Hseext, Msipllfast, Msipllsel, Msirgsel, Pllmboost, Pllrge};
use crate::pac::{FLASH, PWR, RCC};
#[cfg(all(peri_usb_otg_hs))]
pub use crate::pac::{SYSCFG, syscfg::vals::Usbrefcksel};
use crate::rcc::LSI_FREQ;
use crate::time::Hertz;
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum HseMode {
Oscillator,
Bypass,
BypassDigital,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Hse {
pub freq: Hertz,
pub mode: HseMode,
}
#[derive(Clone, Copy)]
pub struct Pll {
pub source: PllSource,
pub prediv: PllPreDiv,
pub mul: PllMul,
pub divp: Option<PllDiv>,
pub divq: Option<PllDiv>,
pub divr: Option<PllDiv>,
}
#[derive(Clone, Copy, PartialEq)]
pub enum MsiAutoCalibration {
Disabled,
MSIS,
MSIK,
MsisFast,
MsikFast,
}
impl MsiAutoCalibration {
const fn default() -> Self {
MsiAutoCalibration::Disabled
}
fn base_mode(&self) -> Self {
match self {
MsiAutoCalibration::Disabled => MsiAutoCalibration::Disabled,
MsiAutoCalibration::MSIS => MsiAutoCalibration::MSIS,
MsiAutoCalibration::MSIK => MsiAutoCalibration::MSIK,
MsiAutoCalibration::MsisFast => MsiAutoCalibration::MSIS,
MsiAutoCalibration::MsikFast => MsiAutoCalibration::MSIK,
}
}
fn is_fast(&self) -> bool {
matches!(self, MsiAutoCalibration::MsisFast | MsiAutoCalibration::MsikFast)
}
}
impl Default for MsiAutoCalibration {
fn default() -> Self {
Self::default()
}
}
#[derive(Clone, Copy)]
pub struct Config {
pub msis: Option<MSIRange>,
pub msik: Option<MSIRange>,
pub hsi: bool,
pub hse: Option<Hse>,
pub hsi48: Option<super::Hsi48Config>,
pub pll1: Option<Pll>,
pub pll2: Option<Pll>,
pub pll3: Option<Pll>,
pub sys: Sysclk,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub apb3_pre: APBPrescaler,
pub voltage_range: VoltageScale,
pub ls: super::LsConfig,
pub mux: super::mux::ClockMux,
pub auto_calibration: MsiAutoCalibration,
}
impl Config {
pub const fn new() -> Self {
Self {
msis: Some(Msirange::RANGE_4MHZ),
msik: Some(Msirange::RANGE_4MHZ),
hse: None,
hsi: false,
hsi48: Some(crate::rcc::Hsi48Config::new()),
pll1: None,
pll2: None,
pll3: None,
sys: Sysclk::MSIS,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
apb3_pre: APBPrescaler::DIV1,
voltage_range: VoltageScale::RANGE1,
ls: crate::rcc::LsConfig::new(),
mux: super::mux::ClockMux::default(),
auto_calibration: MsiAutoCalibration::default(),
}
}
}
impl Default for Config {
fn default() -> Self {
Self::new()
}
}
pub(crate) unsafe fn init(config: Config) {
PWR.vosr().modify(|w| w.set_vos(config.voltage_range));
while !PWR.vosr().read().vosrdy() {}
let lse_calibration_freq = if config.auto_calibration != MsiAutoCalibration::Disabled {
let lse_config = config
.ls
.lse
.clone()
.expect("LSE must be configured for MSI auto-calibration");
assert!(lse_config.peripherals_clocked);
if (31_100..=34_400).contains(&lse_config.frequency.0) {
match (
config.auto_calibration.base_mode(),
config.msis.is_some(),
config.msik.is_some(),
) {
(MsiAutoCalibration::MSIS, true, _) => {
Some(lse_config.frequency)
}
(MsiAutoCalibration::MSIK, _, true) => {
Some(lse_config.frequency)
}
_ => panic!("MSIx auto-calibration is enabled for a source that has not been configured."),
}
} else {
panic!("LSE frequency more than 5% off from 32.768 kHz, cannot use for MSI auto-calibration");
}
} else {
None
};
let mut msis = config.msis.map(|range| {
match config.voltage_range {
VoltageScale::RANGE4 => {
assert!(msirange_to_hertz(range).0 <= 24_000_000);
}
_ => {}
}
loop {
let cr = RCC.cr().read();
if cr.msison() == false || cr.msisrdy() == true {
break;
}
}
RCC.icscr1().modify(|w| {
w.set_msisrange(range);
w.set_msirgsel(Msirgsel::ICSCR1);
});
RCC.cr().write(|w| {
w.set_msipllen(false);
w.set_msison(true);
});
let msis = if let (Some(freq), MsiAutoCalibration::MSIS) =
(lse_calibration_freq, config.auto_calibration.base_mode())
{
RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIS));
RCC.cr().modify(|w| w.set_msipllen(true));
calculate_calibrated_msi_frequency(range, freq)
} else {
msirange_to_hertz(range)
};
while !RCC.cr().read().msisrdy() {}
msis
});
let mut msik = config.msik.map(|range| {
match config.voltage_range {
VoltageScale::RANGE4 => {
assert!(msirange_to_hertz(range).0 <= 24_000_000);
}
_ => {}
}
loop {
let cr = RCC.cr().read();
if cr.msikon() == false || cr.msikrdy() == true {
break;
}
}
RCC.icscr1().modify(|w| {
w.set_msikrange(range);
w.set_msirgsel(Msirgsel::ICSCR1);
});
RCC.cr().modify(|w| {
w.set_msikon(true);
});
let msik = if let (Some(freq), MsiAutoCalibration::MSIK) =
(lse_calibration_freq, config.auto_calibration.base_mode())
{
RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIK));
RCC.cr().modify(|w| w.set_msipllen(true));
calculate_calibrated_msi_frequency(range, freq)
} else {
msirange_to_hertz(range)
};
while !RCC.cr().read().msikrdy() {}
msik
});
if let Some(lse_freq) = lse_calibration_freq {
if let (Some(msis_range), Some(msik_range)) = (config.msis, config.msik) {
if (msis_range as u8 >> 2) == (msik_range as u8 >> 2) {
match config.auto_calibration.base_mode() {
MsiAutoCalibration::MSIS => {
msik = Some(calculate_calibrated_msi_frequency(msik_range, lse_freq));
}
MsiAutoCalibration::MSIK => {
msis = Some(calculate_calibrated_msi_frequency(msis_range, lse_freq));
}
_ => {}
}
}
}
if config.auto_calibration.is_fast() {
RCC.cr().modify(|w| {
w.set_msipllfast(Msipllfast::FAST);
});
}
}
let hsi = config.hsi.then(|| {
RCC.cr().modify(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
HSI_FREQ
});
let hse = config.hse.map(|hse| {
match config.voltage_range {
VoltageScale::RANGE1 | VoltageScale::RANGE2 | VoltageScale::RANGE3 => {
assert!(hse.freq.0 <= 50_000_000);
}
VoltageScale::RANGE4 => {
assert!(hse.freq.0 <= 25_000_000);
}
}
RCC.cr().modify(|w| {
w.set_hseon(true);
w.set_hsebyp(hse.mode != HseMode::Oscillator);
w.set_hseext(match hse.mode {
HseMode::Oscillator | HseMode::Bypass => Hseext::ANALOG,
HseMode::BypassDigital => Hseext::DIGITAL,
});
});
while !RCC.cr().read().hserdy() {}
hse.freq
});
let hsi48 = config.hsi48.map(super::init_hsi48);
let default_system_clock_source = Config::default().sys;
RCC.cfgr1().modify(|w| w.set_sw(default_system_clock_source));
while RCC.cfgr1().read().sws() != default_system_clock_source {}
let pll_input = PllInput { hse, hsi, msi: msis };
let pll1 = config.pll1.map_or_else(
|| {
pll_enable(PllInstance::Pll1, false);
PllOutput::default()
},
|c| init_pll(PllInstance::Pll1, Some(c), &pll_input, config.voltage_range),
);
let pll2 = config.pll2.map_or_else(
|| {
pll_enable(PllInstance::Pll2, false);
PllOutput::default()
},
|c| init_pll(PllInstance::Pll2, Some(c), &pll_input, config.voltage_range),
);
let pll3 = config.pll3.map_or_else(
|| {
pll_enable(PllInstance::Pll3, false);
PllOutput::default()
},
|c| init_pll(PllInstance::Pll3, Some(c), &pll_input, config.voltage_range),
);
let sys_clk = match config.sys {
Sysclk::HSE => hse.unwrap(),
Sysclk::HSI => hsi.unwrap(),
Sysclk::MSIS => msis.unwrap(),
Sysclk::PLL1_R => pll1.r.unwrap(),
};
if sys_clk >= Hertz::mhz(55) {
PWR.vosr().modify(|w| w.set_boosten(true));
while !PWR.vosr().read().boostrdy() {}
}
let wait_states = match config.voltage_range {
VoltageScale::RANGE1 => match sys_clk.0 {
..=32_000_000 => 0,
..=64_000_000 => 1,
..=96_000_000 => 2,
..=128_000_000 => 3,
_ => 4,
},
VoltageScale::RANGE2 => match sys_clk.0 {
..=30_000_000 => 0,
..=60_000_000 => 1,
..=90_000_000 => 2,
_ => 3,
},
VoltageScale::RANGE3 => match sys_clk.0 {
..=24_000_000 => 0,
..=48_000_000 => 1,
_ => 2,
},
VoltageScale::RANGE4 => match sys_clk.0 {
..=12_000_000 => 0,
_ => 1,
},
};
FLASH.acr().modify(|w| {
w.set_latency(wait_states);
});
RCC.cfgr1().modify(|w| w.set_sw(config.sys));
while RCC.cfgr1().read().sws() != config.sys {}
RCC.cfgr2().modify(|w| {
w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
RCC.cfgr3().modify(|w| {
w.set_ppre3(config.apb3_pre);
});
let hclk = sys_clk / config.ahb_pre;
let hclk_max = match config.voltage_range {
VoltageScale::RANGE1 => Hertz::mhz(160),
VoltageScale::RANGE2 => Hertz::mhz(110),
VoltageScale::RANGE3 => Hertz::mhz(55),
VoltageScale::RANGE4 => Hertz::mhz(25),
};
assert!(hclk <= hclk_max);
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre);
let (pclk3, _) = super::util::calc_pclk(hclk, config.apb3_pre);
let rtc = config.ls.init();
#[cfg(all(stm32u5, peri_usb_otg_hs))]
let usb_refck = match config.mux.otghssel {
Otghssel::HSE => hse,
Otghssel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8),
Otghssel::PLL1_P => pll1.p,
Otghssel::PLL1_P_DIV_2 => pll1.p.map(|pll1p_val| pll1p_val / 2u8),
};
#[cfg(all(stm32u5, peri_usb_otg_hs))]
let usb_refck_sel = match usb_refck {
Some(clk_val) => match clk_val {
Hertz(16_000_000) => Usbrefcksel::MHZ16,
Hertz(19_200_000) => Usbrefcksel::MHZ19_2,
Hertz(20_000_000) => Usbrefcksel::MHZ20,
Hertz(24_000_000) => Usbrefcksel::MHZ24,
Hertz(26_000_000) => Usbrefcksel::MHZ26,
Hertz(32_000_000) => Usbrefcksel::MHZ32,
_ => panic!(
"cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz",
clk_val
),
},
None => Usbrefcksel::MHZ24,
};
#[cfg(all(stm32u5, peri_usb_otg_hs))]
SYSCFG.otghsphycr().modify(|w| {
w.set_clksel(usb_refck_sel);
});
let lse = config.ls.lse.map(|l| l.frequency);
let lsi = config.ls.lsi.then_some(LSI_FREQ);
if !config.hsi {
RCC.cr().modify(|w| w.set_hsion(false));
}
#[cfg(crs)]
if config.hsi48.is_none() {
super::disable_hsi48();
}
config.mux.init();
set_clocks!(
sys: Some(sys_clk),
hclk1: Some(hclk),
hclk2: Some(hclk),
hclk3: Some(hclk),
pclk1: Some(pclk1),
pclk2: Some(pclk2),
pclk3: Some(pclk3),
pclk1_tim: Some(pclk1_tim),
pclk2_tim: Some(pclk2_tim),
msik: msik,
hsi48: hsi48,
rtc: rtc,
lse: lse,
lsi: lsi,
hse: hse,
hsi: hsi,
pll1_p: pll1.p,
pll1_q: pll1.q,
pll1_r: pll1.r,
pll2_p: pll2.p,
pll2_q: pll2.q,
pll2_r: pll2.r,
pll3_p: pll3.p,
pll3_q: pll3.q,
pll3_r: pll3.r,
#[cfg(dsihost)]
dsi_phy: None,
audioclk: None,
shsi: None,
);
}
fn msirange_to_hertz(range: Msirange) -> Hertz {
match range {
Msirange::RANGE_48MHZ => Hertz(48_000_000),
Msirange::RANGE_24MHZ => Hertz(24_000_000),
Msirange::RANGE_16MHZ => Hertz(16_000_000),
Msirange::RANGE_12MHZ => Hertz(12_000_000),
Msirange::RANGE_4MHZ => Hertz(4_000_000),
Msirange::RANGE_2MHZ => Hertz(2_000_000),
Msirange::RANGE_1_33MHZ => Hertz(1_330_000),
Msirange::RANGE_1MHZ => Hertz(1_000_000),
Msirange::RANGE_3_072MHZ => Hertz(3_072_000),
Msirange::RANGE_1_536MHZ => Hertz(1_536_000),
Msirange::RANGE_1_024MHZ => Hertz(1_024_000),
Msirange::RANGE_768KHZ => Hertz(768_000),
Msirange::RANGE_400KHZ => Hertz(400_000),
Msirange::RANGE_200KHZ => Hertz(200_000),
Msirange::RANGE_133KHZ => Hertz(133_000),
Msirange::RANGE_100KHZ => Hertz(100_000),
}
}
pub(super) struct PllInput {
pub hsi: Option<Hertz>,
pub hse: Option<Hertz>,
pub msi: Option<Hertz>,
}
#[allow(unused)]
#[derive(Default)]
pub(super) struct PllOutput {
pub p: Option<Hertz>,
pub q: Option<Hertz>,
pub r: Option<Hertz>,
}
#[derive(PartialEq, Eq, Clone, Copy)]
enum PllInstance {
Pll1 = 0,
Pll2 = 1,
Pll3 = 2,
}
fn pll_enable(instance: PllInstance, enabled: bool) {
RCC.cr().modify(|w| w.set_pllon(instance as _, enabled));
while RCC.cr().read().pllrdy(instance as _) != enabled {}
}
fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput, voltage_range: VoltageScale) -> PllOutput {
pll_enable(instance, false);
let Some(pll) = config else { return PllOutput::default() };
let src_freq = match pll.source {
PllSource::DISABLE => panic!("must not select PLL source as DISABLE"),
PllSource::HSE => unwrap!(input.hse),
PllSource::HSI => unwrap!(input.hsi),
PllSource::MSIS => unwrap!(input.msi),
};
let ref_freq = src_freq / pll.prediv;
assert!(Hertz::mhz(4) <= ref_freq && ref_freq <= Hertz::mhz(16));
let (vco_min, vco_max, out_max) = match voltage_range {
VoltageScale::RANGE1 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(208)),
VoltageScale::RANGE2 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(110)),
VoltageScale::RANGE3 => (Hertz::mhz(128), Hertz::mhz(330), Hertz::mhz(55)),
VoltageScale::RANGE4 => panic!("PLL is unavailable in voltage range 4"),
};
let vco_freq = ref_freq * pll.mul;
assert!(vco_freq >= vco_min && vco_freq <= vco_max);
let p = pll.divp.map(|div| vco_freq / div);
let q = pll.divq.map(|div| vco_freq / div);
let r = pll.divr.map(|div| vco_freq / div);
for freq in [p, q, r] {
if let Some(freq) = freq {
assert!(freq <= out_max);
}
}
let divr = match instance {
PllInstance::Pll1 => RCC.pll1divr(),
PllInstance::Pll2 => RCC.pll2divr(),
PllInstance::Pll3 => RCC.pll3divr(),
};
divr.write(|w| {
w.set_plln(pll.mul);
w.set_pllp(pll.divp.unwrap_or(PllDiv::DIV1));
w.set_pllq(pll.divq.unwrap_or(PllDiv::DIV1));
w.set_pllr(pll.divr.unwrap_or(PllDiv::DIV1));
});
let input_range = match ref_freq.0 {
..=8_000_000 => Pllrge::FREQ_4TO8MHZ,
_ => Pllrge::FREQ_8TO16MHZ,
};
macro_rules! write_fields {
($w:ident) => {
$w.set_pllpen(pll.divp.is_some());
$w.set_pllqen(pll.divq.is_some());
$w.set_pllren(pll.divr.is_some());
$w.set_pllm(pll.prediv);
$w.set_pllsrc(pll.source);
$w.set_pllrge(input_range);
};
}
match instance {
PllInstance::Pll1 => RCC.pll1cfgr().write(|w| {
if r.unwrap() >= Hertz::mhz(55) {
let mboost = match src_freq.0 {
..=16_000_000 => Pllmboost::DIV1, ..=32_000_000 => Pllmboost::DIV2, _ => Pllmboost::DIV4, };
w.set_pllmboost(mboost);
}
write_fields!(w);
}),
PllInstance::Pll2 => RCC.pll2cfgr().write(|w| {
write_fields!(w);
}),
PllInstance::Pll3 => RCC.pll3cfgr().write(|w| {
write_fields!(w);
}),
}
pll_enable(instance, true);
PllOutput { p, q, r }
}
#[derive(Debug, Clone, Copy)]
struct MsiFraction {
numerator: u32,
denominator: u32,
}
impl MsiFraction {
const fn new(numerator: u32, denominator: u32) -> Self {
Self { numerator, denominator }
}
fn calculate_frequency(&self, lse_freq: Hertz) -> Hertz {
Hertz(lse_freq.0 * self.numerator / self.denominator)
}
}
fn get_msi_calibration_fraction(range: Msirange) -> MsiFraction {
let denominator = (range as u32 & 0x03) + 1;
let numerator = [1465, 122, 94, 12][range as usize >> 2];
MsiFraction::new(numerator, denominator)
}
fn calculate_calibrated_msi_frequency(range: Msirange, lse_freq: Hertz) -> Hertz {
let fraction = get_msi_calibration_fraction(range);
fraction.calculate_frequency(lse_freq)
}