use stm32_metapac::rcc::vals::{Msikdiv, Msisdiv, Msissel};
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler, Sw as Sysclk};
use crate::pac::rcc::vals::{Hseext, Msipllsel, Msirgsel};
use crate::pac::{FLASH, PWR, RCC};
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 VoltageScale {
RANGE1,
RANGE2,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum MSIRange {
RANGE0_96MHZ,
RANGE1_48MHZ,
RANGE2_24MHZ,
RANGE3_12MHZ,
RANGE4_24MHZ,
RANGE5_12MHZ,
RANGE6_6MHZ,
RANGE7_3MHZ,
}
impl From<MSIRange> for Msisdiv {
fn from(range: MSIRange) -> Self {
match range {
MSIRange::RANGE0_96MHZ => Msisdiv::DIV1,
MSIRange::RANGE1_48MHZ => Msisdiv::DIV2,
MSIRange::RANGE2_24MHZ => Msisdiv::DIV4,
MSIRange::RANGE3_12MHZ => Msisdiv::DIV8,
MSIRange::RANGE4_24MHZ => Msisdiv::DIV1,
MSIRange::RANGE5_12MHZ => Msisdiv::DIV2,
MSIRange::RANGE6_6MHZ => Msisdiv::DIV4,
MSIRange::RANGE7_3MHZ => Msisdiv::DIV8,
}
}
}
impl From<MSIRange> for Msikdiv {
fn from(range: MSIRange) -> Self {
match range {
MSIRange::RANGE0_96MHZ => Msikdiv::DIV1,
MSIRange::RANGE1_48MHZ => Msikdiv::DIV2,
MSIRange::RANGE2_24MHZ => Msikdiv::DIV4,
MSIRange::RANGE3_12MHZ => Msikdiv::DIV8,
MSIRange::RANGE4_24MHZ => Msikdiv::DIV1,
MSIRange::RANGE5_12MHZ => Msikdiv::DIV2,
MSIRange::RANGE6_6MHZ => Msikdiv::DIV4,
MSIRange::RANGE7_3MHZ => Msikdiv::DIV8,
}
}
}
#[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, 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 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::RANGE5_12MHZ),
msik: Some(MSIRange::RANGE5_12MHZ),
hse: None,
hsi: false,
hsi48: Some(crate::rcc::Hsi48Config::new()),
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) {
match config.voltage_range {
VoltageScale::RANGE1 => {
if PWR.vosr().read().r2en() {
while !PWR.vosr().read().r2rdy() {}
}
PWR.vosr().modify(|w| {
w.set_r1en(true);
w.set_r2en(false);
});
while !PWR.vosr().read().r1rdy() {}
}
VoltageScale::RANGE2 => {
if PWR.vosr().read().r1en() {
while !PWR.vosr().read().r1rdy() {}
}
PWR.vosr().modify(|w| {
w.set_r2en(true);
w.set_r1en(false);
});
while !PWR.vosr().read().r2rdy() {}
}
}
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::RANGE2 => {
assert!(msirange_to_hertz(range).0 <= 48_000_000);
}
_ => {}
}
loop {
let cr = RCC.cr().read();
if cr.msison() == false || cr.msisrdy() == true {
break;
}
}
let msissel = if msirange_to_hertz(range).0 <= 24_000_000 {
Msissel::MSIRC1_24MHZ
} else {
Msissel::MSIRC0_96MHZ
};
RCC.icscr1().modify(|w| {
w.set_msissel(msissel);
w.set_msisdiv(range.into());
w.set_msirgsel(Msirgsel::RCC_ICSCR1);
});
RCC.cr().write(|w| {
w.set_msipll0en(false);
w.set_msipll1en(false);
w.set_msison(true);
});
let msis = if let (Some(freq), MsiAutoCalibration::MSIS) =
(lse_calibration_freq, config.auto_calibration.base_mode())
{
if msissel == Msissel::MSIRC0_96MHZ {
RCC.icscr1().modify(|w| w.set_msipll0sel(Msipllsel::LSE));
RCC.cr().modify(|w| w.set_msipll0en(true));
} else {
RCC.icscr1().modify(|w| w.set_msipll1sel(Msipllsel::LSE));
RCC.cr().modify(|w| w.set_msipll1en(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::RANGE2 => {
assert!(msirange_to_hertz(range).0 <= 48_000_000);
}
_ => {}
}
loop {
let cr = RCC.cr().read();
if cr.msikon() == false || cr.msikrdy() == true {
break;
}
}
RCC.icscr1().modify(|w| {
w.set_msikdiv(range.into());
w.set_msirgsel(Msirgsel::RCC_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())
{
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 == msik_range {
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_msipll0fast(true);
});
}
}
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 => {
assert!(hse.freq.0 <= 50_000_000);
}
VoltageScale::RANGE2 => {
assert!(hse.freq.0 <= 48_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 sys_clk = match config.sys {
Sysclk::HSE => hse.unwrap(),
Sysclk::HSI16 => hsi.unwrap(),
Sysclk::MSIS => msis.unwrap(),
Sysclk::_RESERVED_3 => unreachable!(),
};
if sys_clk >= Hertz::mhz(24) {
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,
_ => 3,
},
VoltageScale::RANGE2 => match sys_clk.0 {
..=16_000_000 => 0,
..=32_000_000 => 1,
..=48_000_000 => 2,
_ => 3,
},
};
FLASH.acr().modify(|w| {
w.set_latency(wait_states);
});
RCC.cfgr1().modify(|w| w.set_sw(config.sys));
while RCC.cfgr1().read().sw() != 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(96),
VoltageScale::RANGE2 => Hertz::mhz(48),
};
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();
let lse = config.ls.lse.map(|l| l.frequency);
let lsi = config.ls.lsi.then_some(LSI_FREQ);
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,
audioclk: None,
shsi: None,
);
}
fn msirange_to_hertz(range: MSIRange) -> Hertz {
match range {
MSIRange::RANGE0_96MHZ => Hertz(96_000_000),
MSIRange::RANGE1_48MHZ => Hertz(48_000_000),
MSIRange::RANGE2_24MHZ => Hertz(24_000_000),
MSIRange::RANGE3_12MHZ => Hertz(12_000_000),
MSIRange::RANGE4_24MHZ => Hertz(12_000_000),
MSIRange::RANGE5_12MHZ => Hertz(12_000_000),
MSIRange::RANGE6_6MHZ => Hertz(6_000_000),
MSIRange::RANGE7_3MHZ => Hertz(3_000_000),
}
}
#[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)
}