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 | |