| 1 | //! Reset and Clock Control (RCC) |
| 2 | |
| 3 | #![macro_use ] |
| 4 | #![allow (missing_docs)] // TODO |
| 5 | |
| 6 | use core::mem::MaybeUninit; |
| 7 | |
| 8 | mod bd; |
| 9 | pub use bd::*; |
| 10 | |
| 11 | #[cfg (any(mco, mco1, mco2))] |
| 12 | mod mco; |
| 13 | use critical_section::CriticalSection; |
| 14 | #[cfg (any(mco, mco1, mco2))] |
| 15 | pub use mco::*; |
| 16 | |
| 17 | #[cfg (crs)] |
| 18 | mod hsi48; |
| 19 | #[cfg (crs)] |
| 20 | pub use hsi48::*; |
| 21 | |
| 22 | #[cfg_attr (any(stm32f0, stm32f1, stm32f3), path = "f013.rs" )] |
| 23 | #[cfg_attr (any(stm32f2, stm32f4, stm32f7), path = "f247.rs" )] |
| 24 | #[cfg_attr (stm32c0, path = "c0.rs" )] |
| 25 | #[cfg_attr (stm32g0, path = "g0.rs" )] |
| 26 | #[cfg_attr (stm32g4, path = "g4.rs" )] |
| 27 | #[cfg_attr (any(stm32h5, stm32h7, stm32h7rs), path = "h.rs" )] |
| 28 | #[cfg_attr (any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs" )] |
| 29 | #[cfg_attr (stm32u5, path = "u5.rs" )] |
| 30 | #[cfg_attr (stm32wba, path = "wba.rs" )] |
| 31 | mod _version; |
| 32 | |
| 33 | pub use _version::*; |
| 34 | use stm32_metapac::RCC; |
| 35 | |
| 36 | pub use crate::_generated::{mux, Clocks}; |
| 37 | use crate::time::Hertz; |
| 38 | |
| 39 | #[cfg (feature = "low-power" )] |
| 40 | /// Must be written within a critical section |
| 41 | /// |
| 42 | /// May be read without a critical section |
| 43 | pub(crate) static mut REFCOUNT_STOP1: u32 = 0; |
| 44 | |
| 45 | #[cfg (feature = "low-power" )] |
| 46 | /// Must be written within a critical section |
| 47 | /// |
| 48 | /// May be read without a critical section |
| 49 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; |
| 50 | |
| 51 | #[cfg (not(feature = "_dual-core" ))] |
| 52 | /// Frozen clock frequencies |
| 53 | /// |
| 54 | /// The existence of this value indicates that the clock configuration can no longer be changed |
| 55 | static mut CLOCK_FREQS: MaybeUninit<Clocks> = MaybeUninit::uninit(); |
| 56 | |
| 57 | #[cfg (feature = "_dual-core" )] |
| 58 | static CLOCK_FREQS_PTR: core::sync::atomic::AtomicPtr<MaybeUninit<Clocks>> = |
| 59 | core::sync::atomic::AtomicPtr::new(core::ptr::null_mut()); |
| 60 | |
| 61 | #[cfg (feature = "_dual-core" )] |
| 62 | pub(crate) fn set_freqs_ptr(freqs: *mut MaybeUninit<Clocks>) { |
| 63 | CLOCK_FREQS_PTR.store(freqs, core::sync::atomic::Ordering::SeqCst); |
| 64 | } |
| 65 | |
| 66 | #[cfg (not(feature = "_dual-core" ))] |
| 67 | /// Sets the clock frequencies |
| 68 | /// |
| 69 | /// Safety: Sets a mutable global. |
| 70 | pub(crate) unsafe fn set_freqs(freqs: Clocks) { |
| 71 | debug!("rcc: {:?}" , freqs); |
| 72 | CLOCK_FREQS = MaybeUninit::new(val:freqs); |
| 73 | } |
| 74 | |
| 75 | #[cfg (feature = "_dual-core" )] |
| 76 | /// Sets the clock frequencies |
| 77 | /// |
| 78 | /// Safety: Sets a mutable global. |
| 79 | pub(crate) unsafe fn set_freqs(freqs: Clocks) { |
| 80 | debug!("rcc: {:?}" , freqs); |
| 81 | CLOCK_FREQS_PTR |
| 82 | .load(core::sync::atomic::Ordering::SeqCst) |
| 83 | .write(MaybeUninit::new(freqs)); |
| 84 | } |
| 85 | |
| 86 | #[cfg (not(feature = "_dual-core" ))] |
| 87 | /// Safety: Reads a mutable global. |
| 88 | pub(crate) unsafe fn get_freqs() -> &'static Clocks { |
| 89 | (*core::ptr::addr_of_mut!(CLOCK_FREQS)).assume_init_ref() |
| 90 | } |
| 91 | |
| 92 | #[cfg (feature = "_dual-core" )] |
| 93 | /// Safety: Reads a mutable global. |
| 94 | pub(crate) unsafe fn get_freqs() -> &'static Clocks { |
| 95 | unwrap!(CLOCK_FREQS_PTR.load(core::sync::atomic::Ordering::SeqCst).as_ref()).assume_init_ref() |
| 96 | } |
| 97 | |
| 98 | pub(crate) trait SealedRccPeripheral { |
| 99 | fn frequency() -> Hertz; |
| 100 | const RCC_INFO: RccInfo; |
| 101 | } |
| 102 | |
| 103 | #[allow (private_bounds)] |
| 104 | pub trait RccPeripheral: SealedRccPeripheral + 'static {} |
| 105 | |
| 106 | /// Runtime information necessary to reset, enable and disable a peripheral. |
| 107 | pub(crate) struct RccInfo { |
| 108 | /// Offset in 32-bit words of the xxxRSTR register into the RCC register block, or 0xff if the |
| 109 | /// peripheral has no reset bit (we don't use an `Option` to save one byte of storage). |
| 110 | reset_offset_or_0xff: u8, |
| 111 | /// Position of the xxxRST bit within the xxxRSTR register (0..=31). |
| 112 | reset_bit: u8, |
| 113 | /// Offset in 32-bit words of the xxxENR register into the RCC register block. |
| 114 | enable_offset: u8, |
| 115 | /// Position of the xxxEN bit within the xxxENR register (0..=31). |
| 116 | enable_bit: u8, |
| 117 | /// If this peripheral shares the same xxxRSTR bit and xxxEN bit with other peripherals, we |
| 118 | /// maintain a refcount in `crate::_generated::REFCOUNTS` at this index. If the bit is not |
| 119 | /// shared, this is 0xff (we don't use an `Option` to save one byte of storage). |
| 120 | refcount_idx_or_0xff: u8, |
| 121 | /// Stop mode of the peripheral, used to maintain `REFCOUNT_STOP1` and `REFCOUNT_STOP2`. |
| 122 | #[cfg (feature = "low-power" )] |
| 123 | stop_mode: StopMode, |
| 124 | } |
| 125 | |
| 126 | #[cfg (feature = "low-power" )] |
| 127 | #[allow (dead_code)] |
| 128 | pub(crate) enum StopMode { |
| 129 | Standby, |
| 130 | Stop2, |
| 131 | Stop1, |
| 132 | } |
| 133 | |
| 134 | impl RccInfo { |
| 135 | /// Safety: |
| 136 | /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit |
| 137 | /// - `enable_offset_and_bit` must correspond to valid xxxEN bit |
| 138 | /// - `refcount_idx`, if set, must correspond to valid refcount in `_generated::REFCOUNTS` |
| 139 | /// - `stop_mode` must be valid |
| 140 | pub(crate) const unsafe fn new( |
| 141 | reset_offset_and_bit: Option<(u8, u8)>, |
| 142 | enable_offset_and_bit: (u8, u8), |
| 143 | refcount_idx: Option<u8>, |
| 144 | #[cfg (feature = "low-power" )] stop_mode: StopMode, |
| 145 | ) -> Self { |
| 146 | let (reset_offset_or_0xff, reset_bit) = match reset_offset_and_bit { |
| 147 | Some((offset, bit)) => (offset, bit), |
| 148 | None => (0xff, 0xff), |
| 149 | }; |
| 150 | let (enable_offset, enable_bit) = enable_offset_and_bit; |
| 151 | let refcount_idx_or_0xff = match refcount_idx { |
| 152 | Some(idx) => idx, |
| 153 | None => 0xff, |
| 154 | }; |
| 155 | Self { |
| 156 | reset_offset_or_0xff, |
| 157 | reset_bit, |
| 158 | enable_offset, |
| 159 | enable_bit, |
| 160 | refcount_idx_or_0xff, |
| 161 | #[cfg (feature = "low-power" )] |
| 162 | stop_mode, |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | // TODO: should this be `unsafe`? |
| 167 | pub(crate) fn enable_and_reset_with_cs(&self, _cs: CriticalSection) { |
| 168 | if self.refcount_idx_or_0xff != 0xff { |
| 169 | let refcount_idx = self.refcount_idx_or_0xff as usize; |
| 170 | |
| 171 | // Use .get_mut instead of []-operator so that we control how bounds checks happen. |
| 172 | // Otherwise, core::fmt will be pulled in here in order to format the integer in the |
| 173 | // out-of-bounds error. |
| 174 | if let Some(refcount) = |
| 175 | unsafe { (*core::ptr::addr_of_mut!(crate::_generated::REFCOUNTS)).get_mut(refcount_idx) } |
| 176 | { |
| 177 | *refcount += 1; |
| 178 | if *refcount > 1 { |
| 179 | return; |
| 180 | } |
| 181 | } else { |
| 182 | panic!("refcount_idx out of bounds: {}" , refcount_idx) |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | #[cfg (feature = "low-power" )] |
| 187 | match self.stop_mode { |
| 188 | StopMode::Standby => {} |
| 189 | StopMode::Stop2 => unsafe { |
| 190 | REFCOUNT_STOP2 += 1; |
| 191 | }, |
| 192 | StopMode::Stop1 => unsafe { |
| 193 | REFCOUNT_STOP1 += 1; |
| 194 | }, |
| 195 | } |
| 196 | |
| 197 | // set the xxxRST bit |
| 198 | let reset_ptr = self.reset_ptr(); |
| 199 | if let Some(reset_ptr) = reset_ptr { |
| 200 | unsafe { |
| 201 | let val = reset_ptr.read_volatile(); |
| 202 | reset_ptr.write_volatile(val | 1u32 << self.reset_bit); |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | // set the xxxEN bit |
| 207 | let enable_ptr = self.enable_ptr(); |
| 208 | unsafe { |
| 209 | let val = enable_ptr.read_volatile(); |
| 210 | enable_ptr.write_volatile(val | 1u32 << self.enable_bit); |
| 211 | } |
| 212 | |
| 213 | // we must wait two peripheral clock cycles before the clock is active |
| 214 | // this seems to work, but might be incorrect |
| 215 | // see http://efton.sk/STM32/gotcha/g183.html |
| 216 | |
| 217 | // dummy read (like in the ST HALs) |
| 218 | let _ = unsafe { enable_ptr.read_volatile() }; |
| 219 | |
| 220 | // DSB for good measure |
| 221 | cortex_m::asm::dsb(); |
| 222 | |
| 223 | // clear the xxxRST bit |
| 224 | if let Some(reset_ptr) = reset_ptr { |
| 225 | unsafe { |
| 226 | let val = reset_ptr.read_volatile(); |
| 227 | reset_ptr.write_volatile(val & !(1u32 << self.reset_bit)); |
| 228 | } |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | // TODO: should this be `unsafe`? |
| 233 | pub(crate) fn disable_with_cs(&self, _cs: CriticalSection) { |
| 234 | if self.refcount_idx_or_0xff != 0xff { |
| 235 | let refcount_idx = self.refcount_idx_or_0xff as usize; |
| 236 | |
| 237 | // Use .get_mut instead of []-operator so that we control how bounds checks happen. |
| 238 | // Otherwise, core::fmt will be pulled in here in order to format the integer in the |
| 239 | // out-of-bounds error. |
| 240 | if let Some(refcount) = |
| 241 | unsafe { (*core::ptr::addr_of_mut!(crate::_generated::REFCOUNTS)).get_mut(refcount_idx) } |
| 242 | { |
| 243 | *refcount -= 1; |
| 244 | if *refcount > 0 { |
| 245 | return; |
| 246 | } |
| 247 | } else { |
| 248 | panic!("refcount_idx out of bounds: {}" , refcount_idx) |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | #[cfg (feature = "low-power" )] |
| 253 | match self.stop_mode { |
| 254 | StopMode::Standby => {} |
| 255 | StopMode::Stop2 => unsafe { |
| 256 | REFCOUNT_STOP2 -= 1; |
| 257 | }, |
| 258 | StopMode::Stop1 => unsafe { |
| 259 | REFCOUNT_STOP1 -= 1; |
| 260 | }, |
| 261 | } |
| 262 | |
| 263 | // clear the xxxEN bit |
| 264 | let enable_ptr = self.enable_ptr(); |
| 265 | unsafe { |
| 266 | let val = enable_ptr.read_volatile(); |
| 267 | enable_ptr.write_volatile(val & !(1u32 << self.enable_bit)); |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | // TODO: should this be `unsafe`? |
| 272 | pub(crate) fn enable_and_reset(&self) { |
| 273 | critical_section::with(|cs| self.enable_and_reset_with_cs(cs)) |
| 274 | } |
| 275 | |
| 276 | // TODO: should this be `unsafe`? |
| 277 | pub(crate) fn disable(&self) { |
| 278 | critical_section::with(|cs| self.disable_with_cs(cs)) |
| 279 | } |
| 280 | |
| 281 | fn reset_ptr(&self) -> Option<*mut u32> { |
| 282 | if self.reset_offset_or_0xff != 0xff { |
| 283 | Some(unsafe { (RCC.as_ptr() as *mut u32).add(self.reset_offset_or_0xff as _) }) |
| 284 | } else { |
| 285 | None |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | fn enable_ptr(&self) -> *mut u32 { |
| 290 | unsafe { (RCC.as_ptr() as *mut u32).add(self.enable_offset as _) } |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | #[allow (unused)] |
| 295 | mod util { |
| 296 | use crate::time::Hertz; |
| 297 | |
| 298 | pub fn calc_pclk<D>(hclk: Hertz, ppre: D) -> (Hertz, Hertz) |
| 299 | where |
| 300 | Hertz: core::ops::Div<D, Output = Hertz>, |
| 301 | { |
| 302 | let pclk = hclk / ppre; |
| 303 | let pclk_tim = if hclk == pclk { pclk } else { pclk * 2u32 }; |
| 304 | (pclk, pclk_tim) |
| 305 | } |
| 306 | |
| 307 | pub fn all_equal<T: Eq>(mut iter: impl Iterator<Item = T>) -> bool { |
| 308 | let Some(x) = iter.next() else { return true }; |
| 309 | if !iter.all(|y| y == x) { |
| 310 | return false; |
| 311 | } |
| 312 | true |
| 313 | } |
| 314 | |
| 315 | pub fn get_equal<T: Eq>(mut iter: impl Iterator<Item = T>) -> Result<Option<T>, ()> { |
| 316 | let Some(x) = iter.next() else { return Ok(None) }; |
| 317 | if !iter.all(|y| y == x) { |
| 318 | return Err(()); |
| 319 | } |
| 320 | Ok(Some(x)) |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | /// Get the kernel clock frequency of the peripheral `T`. |
| 325 | /// |
| 326 | /// # Panics |
| 327 | /// |
| 328 | /// Panics if the clock is not active. |
| 329 | pub fn frequency<T: RccPeripheral>() -> Hertz { |
| 330 | T::frequency() |
| 331 | } |
| 332 | |
| 333 | /// Enables and resets peripheral `T`. |
| 334 | /// |
| 335 | /// # Safety |
| 336 | /// |
| 337 | /// Peripheral must not be in use. |
| 338 | // TODO: should this be `unsafe`? |
| 339 | pub fn enable_and_reset_with_cs<T: RccPeripheral>(cs: CriticalSection) { |
| 340 | T::RCC_INFO.enable_and_reset_with_cs(cs); |
| 341 | } |
| 342 | |
| 343 | /// Disables peripheral `T`. |
| 344 | /// |
| 345 | /// # Safety |
| 346 | /// |
| 347 | /// Peripheral must not be in use. |
| 348 | // TODO: should this be `unsafe`? |
| 349 | pub fn disable_with_cs<T: RccPeripheral>(cs: CriticalSection) { |
| 350 | T::RCC_INFO.disable_with_cs(cs); |
| 351 | } |
| 352 | |
| 353 | /// Enables and resets peripheral `T`. |
| 354 | /// |
| 355 | /// # Safety |
| 356 | /// |
| 357 | /// Peripheral must not be in use. |
| 358 | // TODO: should this be `unsafe`? |
| 359 | pub fn enable_and_reset<T: RccPeripheral>() { |
| 360 | T::RCC_INFO.enable_and_reset(); |
| 361 | } |
| 362 | |
| 363 | /// Disables peripheral `T`. |
| 364 | /// |
| 365 | /// # Safety |
| 366 | /// |
| 367 | /// Peripheral must not be in use. |
| 368 | // TODO: should this be `unsafe`? |
| 369 | pub fn disable<T: RccPeripheral>() { |
| 370 | T::RCC_INFO.disable(); |
| 371 | } |
| 372 | |