diff options
| author | Raul Alimbekov <[email protected]> | 2025-12-16 09:05:22 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-16 09:05:22 +0300 |
| commit | c9a04b4b732b7a3b696eb8223664c1a7942b1875 (patch) | |
| tree | 6dbe5c02e66eed8d8762f13f95afd24f8db2b38c /embassy-stm32/src/rcc/mod.rs | |
| parent | cde24a3ef1117653ba5ed4184102b33f745782fb (diff) | |
| parent | 5ae6e060ec1c90561719aabdc29d5b6e7b8b0a82 (diff) | |
Merge branch 'main' into main
Diffstat (limited to 'embassy-stm32/src/rcc/mod.rs')
| -rw-r--r-- | embassy-stm32/src/rcc/mod.rs | 250 |
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 | ||
| 6 | use core::mem::MaybeUninit; | 6 | use core::mem::MaybeUninit; |
| 7 | use core::ops; | ||
| 7 | 8 | ||
| 8 | mod bd; | 9 | mod bd; |
| 9 | pub use bd::*; | 10 | pub use bd::*; |
| @@ -11,6 +12,7 @@ pub use bd::*; | |||
| 11 | #[cfg(any(mco, mco1, mco2))] | 12 | #[cfg(any(mco, mco1, mco2))] |
| 12 | mod mco; | 13 | mod mco; |
| 13 | use critical_section::CriticalSection; | 14 | use critical_section::CriticalSection; |
| 15 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 14 | #[cfg(any(mco, mco1, mco2))] | 16 | #[cfg(any(mco, mco1, mco2))] |
| 15 | pub use mco::*; | 17 | pub 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")] | ||
| 31 | mod _version; | 34 | mod _version; |
| 32 | 35 | ||
| 33 | pub use _version::*; | 36 | pub use _version::*; |
| 34 | use stm32_metapac::RCC; | 37 | use stm32_metapac::RCC; |
| 35 | 38 | ||
| 36 | pub use crate::_generated::{mux, Clocks}; | 39 | pub use crate::_generated::{Clocks, mux}; |
| 37 | use crate::time::Hertz; | 40 | use 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 |
| 49 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; | 52 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; |
| 50 | 53 | ||
| 54 | #[cfg(feature = "low-power")] | ||
| 55 | pub(crate) static mut RCC_CONFIG: Option<Config> = None; | ||
| 56 | |||
| 57 | #[cfg(backup_sram)] | ||
| 58 | pub(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")] | ||
| 117 | fn 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")] | ||
| 130 | fn 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 | |||
| 107 | pub(crate) trait SealedRccPeripheral { | 142 | pub(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)] |
| 139 | pub(crate) enum StopMode { | 176 | #[derive(Debug, Clone, Copy, PartialEq, Default, defmt::Format)] |
| 140 | Standby, | 177 | pub 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")] | ||
| 188 | type BusyRccPeripheral = BusyPeripheral<StopMode>; | ||
| 189 | |||
| 190 | #[cfg(not(feature = "low-power"))] | ||
| 191 | type BusyRccPeripheral = (); | ||
| 192 | |||
| 145 | impl RccInfo { | 193 | impl 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 | ||
| 402 | pub(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")] | ||
| 409 | impl StoppablePeripheral for StopMode { | ||
| 410 | fn stop_mode(&self) -> StopMode { | ||
| 411 | *self | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | impl<'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 | |||
| 422 | pub(crate) struct BusyPeripheral<T: StoppablePeripheral> { | ||
| 423 | peripheral: T, | ||
| 424 | } | ||
| 425 | |||
| 426 | impl<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 | |||
| 435 | impl<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 | |||
| 442 | impl<T: StoppablePeripheral> ops::Deref for BusyPeripheral<T> { | ||
| 443 | type Target = T; | ||
| 444 | |||
| 445 | fn deref(&self) -> &Self::Target { | ||
| 446 | &self.peripheral | ||
| 447 | } | ||
| 448 | } | ||
| 449 | |||
| 450 | impl<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)] |
| 306 | mod util { | 457 | mod 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`? | ||
| 531 | pub 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"))] |
| 393 | pub fn reinit<'a>(config: Config, _rcc: &'a mut crate::Peri<'a, crate::peripherals::RCC>) { | 554 | pub 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)] | ||
| 601 | pub(crate) fn raw_prescaler(freq_in: u32, freq_max: u32) -> u32 { | ||
| 602 | freq_in.saturating_sub(1) / freq_max | ||
| 603 | } | ||
