1//! Reset and Clock Control (RCC)
2
3#![macro_use]
4#![allow(missing_docs)] // TODO
5
6use core::mem::MaybeUninit;
7
8mod bd;
9pub use bd::*;
10
11#[cfg(any(mco, mco1, mco2))]
12mod mco;
13use critical_section::CriticalSection;
14#[cfg(any(mco, mco1, mco2))]
15pub use mco::*;
16
17#[cfg(crs)]
18mod hsi48;
19#[cfg(crs)]
20pub 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")]
31mod _version;
32
33pub use _version::*;
34use stm32_metapac::RCC;
35
36pub use crate::_generated::{mux, Clocks};
37use 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
43pub(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
49pub(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
55static mut CLOCK_FREQS: MaybeUninit<Clocks> = MaybeUninit::uninit();
56
57#[cfg(feature = "_dual-core")]
58static 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")]
62pub(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.
70pub(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.
79pub(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.
88pub(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.
94pub(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
98pub(crate) trait SealedRccPeripheral {
99 fn frequency() -> Hertz;
100 const RCC_INFO: RccInfo;
101}
102
103#[allow(private_bounds)]
104pub trait RccPeripheral: SealedRccPeripheral + 'static {}
105
106/// Runtime information necessary to reset, enable and disable a peripheral.
107pub(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)]
128pub(crate) enum StopMode {
129 Standby,
130 Stop2,
131 Stop1,
132}
133
134impl 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)]
295mod 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.
329pub 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`?
339pub 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`?
349pub 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`?
359pub 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`?
369pub fn disable<T: RccPeripheral>() {
370 T::RCC_INFO.disable();
371}
372