aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/rcc/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-stm32/src/rcc/mod.rs')
-rw-r--r--embassy-stm32/src/rcc/mod.rs250
1 files changed, 221 insertions, 29 deletions
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index c41f81816..d25c922d8 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -4,6 +4,7 @@
4#![allow(missing_docs)] // TODO 4#![allow(missing_docs)] // TODO
5 5
6use core::mem::MaybeUninit; 6use core::mem::MaybeUninit;
7use core::ops;
7 8
8mod bd; 9mod bd;
9pub use bd::*; 10pub use bd::*;
@@ -11,6 +12,7 @@ pub use bd::*;
11#[cfg(any(mco, mco1, mco2))] 12#[cfg(any(mco, mco1, mco2))]
12mod mco; 13mod mco;
13use critical_section::CriticalSection; 14use critical_section::CriticalSection;
15use embassy_hal_internal::{Peri, PeripheralType};
14#[cfg(any(mco, mco1, mco2))] 16#[cfg(any(mco, mco1, mco2))]
15pub use mco::*; 17pub use mco::*;
16 18
@@ -28,12 +30,13 @@ pub use hsi48::*;
28#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")] 30#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")]
29#[cfg_attr(stm32u5, path = "u5.rs")] 31#[cfg_attr(stm32u5, path = "u5.rs")]
30#[cfg_attr(stm32wba, path = "wba.rs")] 32#[cfg_attr(stm32wba, path = "wba.rs")]
33#[cfg_attr(stm32n6, path = "n6.rs")]
31mod _version; 34mod _version;
32 35
33pub use _version::*; 36pub use _version::*;
34use stm32_metapac::RCC; 37use stm32_metapac::RCC;
35 38
36pub use crate::_generated::{mux, Clocks}; 39pub use crate::_generated::{Clocks, mux};
37use crate::time::Hertz; 40use crate::time::Hertz;
38 41
39#[cfg(feature = "low-power")] 42#[cfg(feature = "low-power")]
@@ -48,6 +51,12 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0;
48/// May be read without a critical section 51/// May be read without a critical section
49pub(crate) static mut REFCOUNT_STOP2: u32 = 0; 52pub(crate) static mut REFCOUNT_STOP2: u32 = 0;
50 53
54#[cfg(feature = "low-power")]
55pub(crate) static mut RCC_CONFIG: Option<Config> = None;
56
57#[cfg(backup_sram)]
58pub(crate) static mut BKSRAM_RETAINED: bool = false;
59
51#[cfg(not(feature = "_dual-core"))] 60#[cfg(not(feature = "_dual-core"))]
52/// Frozen clock frequencies 61/// Frozen clock frequencies
53/// 62///
@@ -104,6 +113,32 @@ pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clo
104 unsafe { get_freqs() } 113 unsafe { get_freqs() }
105} 114}
106 115
116#[cfg(feature = "low-power")]
117fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) {
118 match stop_mode {
119 StopMode::Standby => {}
120 StopMode::Stop2 => unsafe {
121 REFCOUNT_STOP2 += 1;
122 },
123 StopMode::Stop1 => unsafe {
124 REFCOUNT_STOP1 += 1;
125 },
126 }
127}
128
129#[cfg(feature = "low-power")]
130fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) {
131 match stop_mode {
132 StopMode::Standby => {}
133 StopMode::Stop2 => unsafe {
134 REFCOUNT_STOP2 -= 1;
135 },
136 StopMode::Stop1 => unsafe {
137 REFCOUNT_STOP1 -= 1;
138 },
139 }
140}
141
107pub(crate) trait SealedRccPeripheral { 142pub(crate) trait SealedRccPeripheral {
108 fn frequency() -> Hertz; 143 fn frequency() -> Hertz;
109 #[allow(dead_code)] 144 #[allow(dead_code)]
@@ -134,14 +169,27 @@ pub(crate) struct RccInfo {
134 stop_mode: StopMode, 169 stop_mode: StopMode,
135} 170}
136 171
172/// Specifies a limit for the stop mode of the peripheral or the stop mode to be entered.
173/// E.g. if `StopMode::Stop1` is selected, the peripheral prevents the chip from entering Stop1 mode.
137#[cfg(feature = "low-power")] 174#[cfg(feature = "low-power")]
138#[allow(dead_code)] 175#[allow(dead_code)]
139pub(crate) enum StopMode { 176#[derive(Debug, Clone, Copy, PartialEq, Default, defmt::Format)]
140 Standby, 177pub enum StopMode {
141 Stop2, 178 #[default]
179 /// Peripheral prevents chip from entering Stop1 or executor will enter Stop1
142 Stop1, 180 Stop1,
181 /// Peripheral prevents chip from entering Stop2 or executor will enter Stop2
182 Stop2,
183 /// Peripheral does not prevent chip from entering Stop
184 Standby,
143} 185}
144 186
187#[cfg(feature = "low-power")]
188type BusyRccPeripheral = BusyPeripheral<StopMode>;
189
190#[cfg(not(feature = "low-power"))]
191type BusyRccPeripheral = ();
192
145impl RccInfo { 193impl RccInfo {
146 /// Safety: 194 /// Safety:
147 /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit 195 /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit
@@ -194,17 +242,6 @@ impl RccInfo {
194 } 242 }
195 } 243 }
196 244
197 #[cfg(feature = "low-power")]
198 match self.stop_mode {
199 StopMode::Standby => {}
200 StopMode::Stop2 => unsafe {
201 REFCOUNT_STOP2 += 1;
202 },
203 StopMode::Stop1 => unsafe {
204 REFCOUNT_STOP1 += 1;
205 },
206 }
207
208 // set the xxxRST bit 245 // set the xxxRST bit
209 let reset_ptr = self.reset_ptr(); 246 let reset_ptr = self.reset_ptr();
210 if let Some(reset_ptr) = reset_ptr { 247 if let Some(reset_ptr) = reset_ptr {
@@ -260,17 +297,6 @@ impl RccInfo {
260 } 297 }
261 } 298 }
262 299
263 #[cfg(feature = "low-power")]
264 match self.stop_mode {
265 StopMode::Standby => {}
266 StopMode::Stop2 => unsafe {
267 REFCOUNT_STOP2 -= 1;
268 },
269 StopMode::Stop1 => unsafe {
270 REFCOUNT_STOP1 -= 1;
271 },
272 }
273
274 // clear the xxxEN bit 300 // clear the xxxEN bit
275 let enable_ptr = self.enable_ptr(); 301 let enable_ptr = self.enable_ptr();
276 unsafe { 302 unsafe {
@@ -279,14 +305,85 @@ impl RccInfo {
279 } 305 }
280 } 306 }
281 307
308 #[allow(dead_code)]
309 pub(crate) fn increment_stop_refcount_with_cs(&self, _cs: CriticalSection) {
310 #[cfg(feature = "low-power")]
311 increment_stop_refcount(_cs, self.stop_mode);
312 }
313
314 #[allow(dead_code)]
315 fn increment_minimum_stop_refcount_with_cs(&self, _cs: CriticalSection) {
316 #[cfg(all(stm32wlex, feature = "low-power"))]
317 match self.stop_mode {
318 StopMode::Stop1 | StopMode::Stop2 => increment_stop_refcount(_cs, StopMode::Stop2),
319 _ => {}
320 }
321 }
322
323 #[allow(dead_code)]
324 pub(crate) fn increment_stop_refcount(&self) {
325 #[cfg(feature = "low-power")]
326 critical_section::with(|cs| self.increment_stop_refcount_with_cs(cs))
327 }
328
329 #[allow(dead_code)]
330 pub(crate) fn decrement_stop_refcount_with_cs(&self, _cs: CriticalSection) {
331 #[cfg(feature = "low-power")]
332 decrement_stop_refcount(_cs, self.stop_mode);
333 }
334
335 #[allow(dead_code)]
336 fn decrement_minimum_stop_refcount_with_cs(&self, _cs: CriticalSection) {
337 #[cfg(all(stm32wlex, feature = "low-power"))]
338 match self.stop_mode {
339 StopMode::Stop1 | StopMode::Stop2 => decrement_stop_refcount(_cs, StopMode::Stop2),
340 _ => {}
341 }
342 }
343
344 #[allow(dead_code)]
345 pub(crate) fn decrement_stop_refcount(&self) {
346 #[cfg(feature = "low-power")]
347 critical_section::with(|cs| self.decrement_stop_refcount_with_cs(cs))
348 }
349
282 // TODO: should this be `unsafe`? 350 // TODO: should this be `unsafe`?
283 pub(crate) fn enable_and_reset(&self) { 351 pub(crate) fn enable_and_reset(&self) {
284 critical_section::with(|cs| self.enable_and_reset_with_cs(cs)) 352 critical_section::with(|cs| {
353 self.enable_and_reset_with_cs(cs);
354 self.increment_stop_refcount_with_cs(cs);
355 })
356 }
357
358 #[allow(dead_code)]
359 pub(crate) fn enable_and_reset_without_stop(&self) {
360 critical_section::with(|cs| {
361 self.enable_and_reset_with_cs(cs);
362 self.increment_minimum_stop_refcount_with_cs(cs);
363 })
285 } 364 }
286 365
287 // TODO: should this be `unsafe`? 366 // TODO: should this be `unsafe`?
288 pub(crate) fn disable(&self) { 367 pub(crate) fn disable(&self) {
289 critical_section::with(|cs| self.disable_with_cs(cs)) 368 critical_section::with(|cs| {
369 self.disable_with_cs(cs);
370 self.decrement_stop_refcount_with_cs(cs);
371 })
372 }
373
374 // TODO: should this be `unsafe`?
375 #[allow(dead_code)]
376 pub(crate) fn disable_without_stop(&self) {
377 critical_section::with(|cs| {
378 self.disable_with_cs(cs);
379 self.decrement_minimum_stop_refcount_with_cs(cs);
380 })
381 }
382
383 #[allow(dead_code)]
384 pub(crate) fn block_stop(&self) -> BusyRccPeripheral {
385 #[cfg(feature = "low-power")]
386 BusyPeripheral::new(self.stop_mode)
290 } 387 }
291 388
292 fn reset_ptr(&self) -> Option<*mut u32> { 389 fn reset_ptr(&self) -> Option<*mut u32> {
@@ -302,6 +399,60 @@ impl RccInfo {
302 } 399 }
303} 400}
304 401
402pub(crate) trait StoppablePeripheral {
403 #[cfg(feature = "low-power")]
404 #[allow(dead_code)]
405 fn stop_mode(&self) -> StopMode;
406}
407
408#[cfg(feature = "low-power")]
409impl StoppablePeripheral for StopMode {
410 fn stop_mode(&self) -> StopMode {
411 *self
412 }
413}
414
415impl<'a, T: StoppablePeripheral + PeripheralType> StoppablePeripheral for Peri<'a, T> {
416 #[cfg(feature = "low-power")]
417 fn stop_mode(&self) -> StopMode {
418 T::stop_mode(&self)
419 }
420}
421
422pub(crate) struct BusyPeripheral<T: StoppablePeripheral> {
423 peripheral: T,
424}
425
426impl<T: StoppablePeripheral> BusyPeripheral<T> {
427 pub fn new(peripheral: T) -> Self {
428 #[cfg(feature = "low-power")]
429 critical_section::with(|cs| increment_stop_refcount(cs, peripheral.stop_mode()));
430
431 Self { peripheral }
432 }
433}
434
435impl<T: StoppablePeripheral> Drop for BusyPeripheral<T> {
436 fn drop(&mut self) {
437 #[cfg(feature = "low-power")]
438 critical_section::with(|cs| decrement_stop_refcount(cs, self.peripheral.stop_mode()));
439 }
440}
441
442impl<T: StoppablePeripheral> ops::Deref for BusyPeripheral<T> {
443 type Target = T;
444
445 fn deref(&self) -> &Self::Target {
446 &self.peripheral
447 }
448}
449
450impl<T: StoppablePeripheral> ops::DerefMut for BusyPeripheral<T> {
451 fn deref_mut(&mut self) -> &mut Self::Target {
452 &mut self.peripheral
453 }
454}
455
305#[allow(unused)] 456#[allow(unused)]
306mod util { 457mod util {
307 use crate::time::Hertz; 458 use crate::time::Hertz;
@@ -371,6 +522,16 @@ pub fn enable_and_reset<T: RccPeripheral>() {
371 T::RCC_INFO.enable_and_reset(); 522 T::RCC_INFO.enable_and_reset();
372} 523}
373 524
525/// Enables and resets peripheral `T` without incrementing the stop refcount.
526///
527/// # Safety
528///
529/// Peripheral must not be in use.
530// TODO: should this be `unsafe`?
531pub fn enable_and_reset_without_stop<T: RccPeripheral>() {
532 T::RCC_INFO.enable_and_reset_without_stop();
533}
534
374/// Disables peripheral `T`. 535/// Disables peripheral `T`.
375/// 536///
376/// # Safety 537/// # Safety
@@ -390,7 +551,7 @@ pub fn disable<T: RccPeripheral>() {
390/// 551///
391/// This should only be called after `init`. 552/// This should only be called after `init`.
392#[cfg(not(feature = "_dual-core"))] 553#[cfg(not(feature = "_dual-core"))]
393pub fn reinit<'a>(config: Config, _rcc: &'a mut crate::Peri<'a, crate::peripherals::RCC>) { 554pub fn reinit(config: Config, _rcc: &'_ mut crate::Peri<'_, crate::peripherals::RCC>) {
394 critical_section::with(|cs| init_rcc(cs, config)) 555 critical_section::with(|cs| init_rcc(cs, config))
395} 556}
396 557
@@ -404,8 +565,39 @@ pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) {
404 565
405 #[cfg(feature = "low-power")] 566 #[cfg(feature = "low-power")]
406 { 567 {
568 RCC_CONFIG = Some(config);
407 REFCOUNT_STOP2 = 0; 569 REFCOUNT_STOP2 = 0;
408 REFCOUNT_STOP1 = 0; 570 REFCOUNT_STOP1 = 0;
409 } 571 }
410 } 572 }
411} 573}
574
575/// Calculate intermediate prescaler number used to calculate peripheral prescalers
576///
577/// This function is intended to calculate a number indicating a minimum division
578/// necessary to result in a frequency lower than the provided `freq_max`.
579///
580/// The returned value indicates the `val + 1` divider is necessary to result in
581/// the output frequency that is below the maximum provided.
582///
583/// For example:
584/// 0 = divider of 1 => no division necessary as the input frequency is below max
585/// 1 = divider of 2 => division by 2 necessary
586/// ...
587///
588/// The provided max frequency is inclusive. So if `freq_in == freq_max` the result
589/// will be 0, indicating that no division is necessary. To accomplish that we subtract
590/// 1 from the input frequency so that the integer rounding plays in our favor.
591///
592/// For example:
593/// Let the input frequency be 110 and the max frequency be 55.
594/// If we naiively do `110/55 = 2` the renult will indicate that we need a divider by 3
595/// which in reality will be rounded up to 4 as usually a 3 division is not available.
596/// In either case the resulting frequency will be either 36 or 27 which is lower than
597/// what we would want. The result should be 1.
598/// If we do the following instead `109/55 = 1` indicating that we need a divide by 2
599/// which will result in the correct 55.
600#[allow(unused)]
601pub(crate) fn raw_prescaler(freq_in: u32, freq_max: u32) -> u32 {
602 freq_in.saturating_sub(1) / freq_max
603}