// stm32u5xx-hal/src/rcc.rs //! Reset and Clock Control for STM32U575 (initial/minimal port) //! //! Scope: only features that clearly overlap between stm32l4xx-hal rcc.rs //! and the STM32U5 HAL C (stm32u5xx_hal_rcc.c/.h). //! //! Skipped (present in U5 HAL C, not here yet): //! - MSIK (kernel MSI), SHSI (secure HSI) //! - PLL2 / PLL3 //! - PLL FRACN and MBOOST/EPOD handling //! - LSE glitch filter, LSE CSS, LSESYS //! - LSI divider (LSIPREDIV) //! - STOP wake-up clock selection (STOPWUCK / STOPKERWUCK) //! - Security/privileged attributes //! - APB3 (PCLK3) prescaler & clocks //! - MCO configuration //! - Per-peripheral Enable/Reset tables (normally in rcc/enable.rs) use crate::pac::{rcc, RCC}; use crate::flash::ACR; use crate::pwr::Pwr; use crate::time::Hertz; use fugit::RateExtU32; // Uncomment when you add bus-peripheral enable/reset mapping like in L4 // mod enable; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum MsiFreq { // Order and names reflect STM32U5 MSIRANGE mapping (RCC_MSIRANGE_x): // 0..15 encode the specific frequencies per stm32u5xx_hal_rcc.h // We provide exact frequencies in Hz. MSI48, // 48_000_000 MSI24, // 24_000_000 MSI16, // 16_000_000 MSI12, // 12_000_000 MSI4, // 4_000_000 MSI2, // 2_000_000 MSI1_33, // 1_330_000 (approx 1.33 MHz) MSI1, // 1_000_000 MSI3_072, // 3_072_000 MSI1_536, // 1_536_000 MSI1_024, // 1_024_000 MSI_768K, // 768_000 MSI400K, // 400_000 MSI200K, // 200_000 MSI133K, // 133_000 MSI100K, // 100_000 } impl MsiFreq { fn to_hertz(self) -> Hertz { let hz = match self { Self::MSI48 => 48_000_000, Self::MSI24 => 24_000_000, Self::MSI16 => 16_000_000, Self::MSI12 => 12_000_000, Self::MSI4 => 4_000_000, Self::MSI2 => 2_000_000, Self::MSI1_33 => 1_330_000, Self::MSI1 => 1_000_000, Self::MSI3_072 => 3_072_000, Self::MSI1_536 => 1_536_000, Self::MSI1_024 => 1_024_000, Self::MSI_768K => 768_000, Self::MSI400K => 400_000, Self::MSI200K => 200_000, Self::MSI133K => 133_000, Self::MSI100K => 100_000, }; hz.Hz() } // Map to ICSCR1.MSISRANGE field encoding (0..15) fn to_range_bits(self) -> u8 { match self { Self::MSI48 => 0, Self::MSI24 => 1, Self::MSI16 => 2, Self::MSI12 => 3, Self::MSI4 => 4, Self::MSI2 => 5, Self::MSI1_33 => 6, Self::MSI1 => 7, Self::MSI3_072 => 8, Self::MSI1_536 => 9, Self::MSI1_024 => 10, Self::MSI_768K => 11, Self::MSI400K => 12, Self::MSI200K => 13, Self::MSI133K => 14, Self::MSI100K => 15, } } } /// Extension trait that constrains the `RCC` peripheral pub trait RccExt { /// Constrains the `RCC` peripheral so it plays nicely with the other abstractions fn constrain(self) -> Rcc; } impl RccExt for RCC { fn constrain(self) -> Rcc { Rcc { ahb1: AHB1::new(), ahb2_1: AHB2_1::new(), ahb2_2: AHB2_2::new(), ahb3: AHB3::new(), apb1r1: APB1R1::new(), apb1r2: APB1R2::new(), apb2: APB2::new(), apb3: APB3::new(), cfgr: CFGR { hse: None, lse: None, msi: None, hsi48: false, lsi: false, hclk: None, pclk1: None, pclk2: None, sysclk: None, pll_source: None, pll_config: None, }, } } } /// Constrained RCC peripheral pub struct Rcc { pub ahb1: AHB1, pub ahb2_1: AHB2_1, pub ahb2_2: AHB2_2, pub ahb3: AHB3, pub apb1r1: APB1R1, pub apb1r2: APB1R2, pub apb2: APB2, pub apb3: APB3, pub cfgr: CFGR, } /// Backup domain control register proxy pub struct BDCR { _0: (), } impl BDCR { #[allow(dead_code)] pub(crate) fn bdcr(&mut self) -> &rcc::BDCR { unsafe { &(*RCC::ptr()).bdcr } } } /// Control/Status Register proxy pub struct CSR { _0: (), } impl CSR { #[allow(dead_code)] pub(crate) fn csr(&mut self) -> &rcc::CSR { unsafe { &(*RCC::ptr()).csr } } } macro_rules! bus_struct_1 { ($busX:ident => ($EN:ident, $en:ident, $SMEN:ident, $smen:ident, $RST:ident, $rst:ident, $doc:literal),) => { #[doc = $doc] pub struct $busX { _0: (), } impl $busX { pub(crate) fn new() -> Self { Self { _0: () } } #[allow(unused)] pub(crate) fn enr(&self) -> &rcc::$EN { unsafe { &(*RCC::ptr()).$en } } #[allow(unused)] pub(crate) fn smenr(&self) -> &rcc::$SMEN { unsafe { &(*RCC::ptr()).$smen } } #[allow(unused)] pub(crate) fn rstr(&self) -> &rcc::$RST { unsafe { &(*RCC::ptr()).$rst } } } }; } // U5 splits AHB2/APB1 into *1/*2 register banks; we model both. bus_struct_1! { AHB1 => (AHB1ENR, ahb1enr, AHB1SMENR, ahb1smenr, AHB1RSTR, ahb1rstr, "AHB1 registers"), } bus_struct_1! { AHB2_1 => (AHB2ENR1, ahb2enr1, AHB2SMENR1, ahb2smenr1, AHB2RSTR1, ahb2rstr1, "AHB2 part 1 registers"), } bus_struct_1! { AHB2_2 => (AHB2ENR2, ahb2enr2, AHB2SMENR2, ahb2smenr2, AHB2RSTR2, ahb2rstr2, "AHB2 part 2 registers"), } bus_struct_1! { AHB3 => (AHB3ENR, ahb3enr, AHB3SMENR, ahb3smenr, AHB3RSTR, ahb3rstr, "AHB3 registers"), } bus_struct_1! { APB1R1 => (APB1ENR1, apb1enr1, APB1SMENR1, apb1smenr1, APB1RSTR1, apb1rstr1, "APB1 register set 1"), } bus_struct_1! { APB1R2 => (APB1ENR2, apb1enr2, APB1SMENR2, apb1smenr2, APB1RSTR2, apb1rstr2, "APB1 register set 2"), } bus_struct_1! { APB2 => (APB2ENR, apb2enr, APB2SMENR, apb2smenr, APB2RSTR, apb2rstr, "APB2 registers"), } bus_struct_1! { APB3 => (APB3ENR, apb3enr, APB3SMENR, apb3smenr, APB3RSTR, apb3rstr, "APB3 registers"), } /// Bus associated to peripheral pub trait RccBus: crate::Sealed { type Bus; } /// Enable/disable peripheral pub trait Enable: RccBus { fn enable(bus: &mut Self::Bus); fn disable(bus: &mut Self::Bus); fn is_enabled() -> bool; fn is_disabled() -> bool; unsafe fn enable_unchecked(); unsafe fn disable_unchecked(); } /// Enable/disable peripheral in sleep mode pub trait SMEnable: RccBus { fn enable_in_sleep_mode(bus: &mut Self::Bus); fn disable_in_sleep_mode(bus: &mut Self::Bus); fn is_enabled_in_sleep_mode() -> bool; fn is_disabled_in_sleep_mode() -> bool; unsafe fn enable_in_sleep_mode_unchecked(); unsafe fn disable_in_sleep_mode_unchecked(); } /// Reset peripheral pub trait Reset: RccBus { fn reset(bus: &mut Self::Bus); unsafe fn reset_unchecked(); } #[derive(Debug, PartialEq)] struct HseConfig { speed: u32, bypass: CrystalBypass, css: ClockSecuritySystem, } #[derive(Debug, PartialEq)] struct LseConfig { bypass: CrystalBypass, // Skipped: LSE CSS / glitch filter / LSESYS css: ClockSecuritySystem, // kept in struct for future, not applied } /// Crystal bypass selector #[derive(Clone, Copy, Debug, PartialEq)] pub enum CrystalBypass { Enable, Disable, } /// Clock Security System #[derive(Clone, Copy, Debug, PartialEq)] pub enum ClockSecuritySystem { Enable, Disable, } const HSI: u32 = 16_000_000; // Hz (HSI16) /// PLL Source (PLL1) #[derive(Clone, Copy, Debug, PartialEq)] pub enum PllSource { MSI, HSI16, HSE, } impl PllSource { // Map to RCC_PLL1CFGR PLL1SRC field bits (00: none, 01: MSI, 10: HSI, 11: HSE) fn to_pllsrc_bits(self) -> u8 { match self { Self::MSI => 0b01, Self::HSI16 => 0b10, Self::HSE => 0b11, } } } #[derive(Clone, Copy, Debug)] pub struct PllConfig { // Main PLL division factor M (1..16) -> register stores (M-1) m: u8, // Main PLL multiplication factor N (4..512) -> register stores (N-1) n: u16, // Main PLL division factor for PLLR (system clock) (1..128) -> reg stores (R-1) r: u8, // Skipped: P, Q outputs, MBOOST, FRACN, RGE beyond minimal } impl PllConfig { pub fn new(m: u8, n: u16, r: u8) -> Self { assert!(m >= 1 && m <= 16); assert!(n >= 4 && n <= 512); assert!(r >= 1 && r <= 128); Self { m, n, r } } } /// Clock configuration builder pub struct CFGR { hse: Option, lse: Option, msi: Option, hsi48: bool, lsi: bool, hclk: Option, pclk1: Option, pclk2: Option, sysclk: Option, pll_source: Option, pll_config: Option, } impl CFGR { pub fn hse(mut self, freq: Hertz, bypass: CrystalBypass, css: ClockSecuritySystem) -> Self { self.hse = Some(HseConfig { speed: freq.raw(), bypass, css, }); self } pub fn lse(mut self, bypass: CrystalBypass, css: ClockSecuritySystem) -> Self { self.lse = Some(LseConfig { bypass, css }); self } pub fn msi(mut self, range: MsiFreq) -> Self { self.msi = Some(range); self } pub fn lsi(mut self, on: bool) -> Self { self.lsi = on; self } pub fn hsi48(mut self, on: bool) -> Self { self.hsi48 = on; self } pub fn hclk(mut self, freq: Hertz) -> Self { self.hclk = Some(freq.raw()); self } pub fn pclk1(mut self, freq: Hertz) -> Self { self.pclk1 = Some(freq.raw()); self } pub fn pclk2(mut self, freq: Hertz) -> Self { self.pclk2 = Some(freq.raw()); self } pub fn sysclk(mut self, freq: Hertz) -> Self { self.sysclk = Some(freq.raw()); self } pub fn sysclk_with_pll(mut self, freq: Hertz, cfg: PllConfig) -> Self { self.sysclk = Some(freq.raw()); self.pll_config = Some(cfg); self } pub fn pll_source(mut self, source: PllSource) -> Self { self.pll_source = Some(source); self } pub fn freeze(&self, acr: &mut ACR, pwr: &mut Pwr) -> Clocks { let rcc = unsafe { &*RCC::ptr() }; // Ensure MSI is ON and selected first (safe base) if rcc.cr.read().msison().bit_is_clear() { rcc.cr.modify(|_, w| w.msison().set_bit()); while rcc.cr.read().msisrdy().bit_is_clear() {} } // Program MSI to 4 MHz as default base (MSIRGSEL=1, MSISRANGE=4MHz) unsafe { rcc.icscr1.modify(|_, w| { w.msirgsel().set_bit(); w.msisrange().bits(MsiFreq::MSI4.to_range_bits()); w }); } // If SYSCLK not MSI, force switch to MSI (SW=00) and wait if rcc.cfgr1.read().sws().bits() != 0 { // Reset CFGR1.SW to MSI rcc.cfgr1.modify(|_, w| unsafe { w.sw().bits(0b00) }); while rcc.cfgr1.read().sws().bits() != 0b00 {} } // // 1) Oscillators // // LSI if requested (or if we needed by LSE CSS in future) let lsi_used = if self.lsi { rcc.bdcr.modify(|_, w| w.lsion().set_bit()); while rcc.bdcr.read().lsirdy().bit_is_clear() {} true } else { false }; // LSE if requested if let Some(lse_cfg) = &self.lse { // Enable backup domain write access // NOTE: depends on your Pwr wrapper; mirrors L4 HAL style pwr.cr1.reg().modify(|_, w| w.dbp().set_bit()); // Enable LSE (bypass if set) if lse_cfg.bypass == CrystalBypass::Enable { rcc.bdcr.modify(|_, w| w.lsebyp().set_bit()); } rcc.bdcr.modify(|_, w| w.lseon().set_bit()); while rcc.bdcr.read().lserdy().bit_is_clear() {} // Skipped: LSESYS and LSE CSS/glitch filter } // HSE if requested if let Some(hse_cfg) = &self.hse { rcc.cr.modify(|_, w| { if hse_cfg.bypass == CrystalBypass::Enable { w.hsebyp().set_bit(); } w.hseon().set_bit() }); while rcc.cr.read().hserdy().bit_is_clear() {} if hse_cfg.css == ClockSecuritySystem::Enable { // Enable CSS on HSE (CR.CSSON) rcc.cr.modify(|_, w| w.csson().set_bit()); } } // MSI range if set explicitly if let Some(msi) = self.msi { unsafe { rcc.icscr1.modify(|_, w| { w.msirgsel().set_bit(); w.msisrange().bits(msi.to_range_bits()); w }) }; while rcc.cr.read().msisrdy().bit_is_clear() {} } // HSI48 if self.hsi48 { rcc.cr.modify(|_, w| w.hsi48on().set_bit()); while rcc.cr.read().hsi48rdy().bit_is_clear() {} } // // 2) PLL1 setup (optional) // // Decide PLL1 source let (src_freq, pll_src) = if let Some(src) = self.pll_source { match src { PllSource::HSE => { let hse = self .hse .as_ref() .expect("HSE selected as PLL source, but not enabled"); (hse.speed, src) } PllSource::HSI16 => (HSI, src), PllSource::MSI => { let msi = self.msi.expect("MSI selected as PLL source, but not enabled"); (msi.to_hertz().raw(), src) } } } else { if let Some(hse) = &self.hse { (hse.speed, PllSource::HSE) } else if let Some(msi) = self.msi { (msi.to_hertz().raw(), PllSource::MSI) } else { (HSI, PllSource::HSI16) } }; // If we need HSI for PLL1 and it's not on, turn it on if pll_src == PllSource::HSI16 { rcc.cr.modify(|_, w| w.hsion().set_bit()); while rcc.cr.read().hsirdy().bit_is_clear() {} } let want_sysclk = match (self.sysclk, self.msi) { (Some(sysclk), _) => sysclk, (None, Some(msi)) => msi.to_hertz().raw(), (None, None) => MsiFreq::MSI4.to_hertz().raw(), }; assert!(want_sysclk <= 80_000_000); // Auto PLL config if not provided (simple best-effort: M=1, R=2) let pllconf = if self.pll_config.is_none() { if self.sysclk.is_some() { let m = 1u8; let r = 2u8; // want_sysclk = (src/m)*N / r => N = want_sysclk * r / (src/m) let n = ((want_sysclk as u64) * (r as u64) / (src_freq as u64)) as u16; Some(PllConfig::new(m, n, r)) } else { None } } else { self.pll_config }; // Compute prescalers from requested hclk/pclk1/pclk2 // HPRE (CFGR2.HPRE), PPRE1/PPRE2 (CFGR2.PPRE1/PPRE2) let (hpre_bits, hpre_div) = self .hclk .map(|hclk| match want_sysclk / hclk { 0 => unreachable!(), 1 => (0b0000, 1), 2 => (0b1000, 2), 3..=5 => (0b1001, 4), 6..=11 => (0b1010, 8), 12..=39 => (0b1011, 16), 40..=95 => (0b1100, 64), 96..=191 => (0b1101, 128), 192..=383 => (0b1110, 256), _ => (0b1111, 512), }) .unwrap_or((0b0000, 1)); let hclk = want_sysclk / hpre_div; let (ppre1_bits, ppre1_div) = self .pclk1 .map(|pclk1| match hclk / pclk1 { 0 => unreachable!(), 1 => (0b000, 1), 2 => (0b100, 2), 3..=5 => (0b101, 4), 6..=11 => (0b110, 8), _ => (0b111, 16), }) .unwrap_or((0b000, 1)); let pclk1 = hclk / (ppre1_div as u32); let (ppre2_bits, ppre2_div) = self .pclk2 .map(|pclk2| match hclk / pclk2 { 0 => unreachable!(), 1 => (0b000, 1), 2 => (0b100, 2), 3..=5 => (0b101, 4), 6..=11 => (0b110, 8), _ => (0b111, 16), }) .unwrap_or((0b000, 1)); let pclk2 = hclk / (ppre2_div as u32); // Timer clocks double if APB prescaler != 1 let timclk1 = if ppre1_div == 1 { pclk1 } else { 2 * pclk1 }; let timclk2 = if ppre2_div == 1 { pclk2 } else { 2 * pclk2 }; // Adjust Flash latency (simple L4-like thresholds for <=80 MHz) unsafe { acr.acr().write(|w| { w.latency().bits(if hclk <= 16_000_000 { 0b000 } else if hclk <= 32_000_000 { 0b001 } else if hclk <= 48_000_000 { 0b010 } else if hclk <= 64_000_000 { 0b011 } else { 0b100 }) }) } // Program prescalers first (CFGR2: HPRE/PPRE1/PPRE2) rcc.cfgr2.modify(|_, w| unsafe { w.hpre().bits(hpre_bits); w.ppre1().bits(ppre1_bits); w.ppre2().bits(ppre2_bits) }); let mut used_msi = self.msi; let sysclk_src_bits; if let Some(pll) = pllconf { // Basic checks for VCO ranges (very simplified; FRACN/MBOOST ignored) let vco_in = (src_freq as u32) / (pll.m as u32); assert!(vco_in >= 1_000_000 && vco_in <= 16_000_000); let vco = (vco_in as u64) * (pll.n as u64); assert!(vco >= 128_000_000 && vco <= 544_000_000); let sysclk_calc = (vco / (pll.r as u64)) as u32; assert!(sysclk_calc <= 80_000_000); // Disable PLL1 before reconfig rcc.cr.modify(|_, w| w.pll1on().clear_bit()); while rcc.cr.read().pll1rdy().bit_is_set() {} // Set PLL source let src_bits = pll_src.to_pllsrc_bits(); rcc.pll1cfgr.modify(|_, w| unsafe { w.pll1src().bits(src_bits); // VCI range selection (very rough): 4..8 => range0, 8..16 => range1 let rge = if vco_in < 8_000_000 { 0b00 } else { 0b11 }; w.pll1rge().bits(rge); // M w.pll1m().bits(pll.m - 1); // Enable only R output w.pll1ren().clear_bit(); // clear first; will set after lock w }); // Program N, R in PLL1DIVR rcc.pll1divr.modify(|_, w| unsafe { w.pll1n().bits((pll.n - 1) as u16); w.pll1r().bits(pll.r - 1); // Leave P/Q untouched/disabled (skipped) w }); // Enable PLL1 rcc.cr.modify(|_, w| w.pll1on().set_bit()); while rcc.cr.read().pll1rdy().bit_is_clear() {} // Enable PLL1 R output rcc.pll1cfgr.modify(|_, w| w.pll1ren().set_bit()); // Switch SYSCLK to PLL1 (CFGR1.SW = 11) sysclk_src_bits = 0b11; rcc.cfgr1.modify(|_, w| unsafe { w.sw().bits(sysclk_src_bits) }); } else { // Keep MSI as SYSCLK (CFGR1.SW = 00) sysclk_src_bits = 0b00; if used_msi.is_none() { used_msi = Some(MsiFreq::MSI4); } rcc.cfgr1.modify(|_, w| unsafe { w.sw().bits(sysclk_src_bits) }); } // Wait for SWS while rcc.cfgr1.read().sws().bits() != sysclk_src_bits {} // If we ended up on PLL, and MSI wasn't requested for other purposes, we can switch MSI off if used_msi.is_none() && sysclk_src_bits == 0b11 { rcc.cr.modify(|_, w| w.msison().clear_bit()); } Clocks { hclk: hclk.Hz(), lsi: lsi_used, lse: self.lse.is_some(), msi: used_msi, hsi48: self.hsi48, pclk1: pclk1.Hz(), pclk2: pclk2.Hz(), ppre1: ppre1_div as u8, ppre2: ppre2_div as u8, sysclk: want_sysclk.Hz(), timclk1: timclk1.Hz(), timclk2: timclk2.Hz(), pll_source: if pllconf.is_some() { Some(pll_src) } else { None }, } } } /// Frozen clock frequencies (indicate RCC config is locked) #[derive(Clone, Copy, Debug)] pub struct Clocks { hclk: Hertz, hsi48: bool, msi: Option, lsi: bool, lse: bool, pclk1: Hertz, pclk2: Hertz, ppre1: u8, ppre2: u8, sysclk: Hertz, timclk1: Hertz, timclk2: Hertz, pll_source: Option, } impl Clocks { pub fn hclk(&self) -> Hertz { self.hclk } pub fn hsi48(&self) -> bool { self.hsi48 } pub fn msi(&self) -> Option { self.msi } pub fn lsi(&self) -> bool { self.lsi } pub fn lse(&self) -> bool { self.lse } pub fn pclk1(&self) -> Hertz { self.pclk1 } pub fn pclk2(&self) -> Hertz { self.pclk2 } pub fn pll_source(&self) -> Option { self.pll_source } #[allow(dead_code)] pub(crate) fn ppre1(&self) -> u8 { self.ppre1 } #[allow(dead_code)] pub(crate) fn ppre2(&self) -> u8 { self.ppre2 } pub fn sysclk(&self) -> Hertz { self.sysclk } pub fn timclk1(&self) -> Hertz { self.timclk1 } pub fn timclk2(&self) -> Hertz { self.timclk2 } }