#![macro_use]
#![allow(missing_docs)]
use core::mem::MaybeUninit;
mod bd;
pub use bd::*;
#[cfg(any(mco, mco1, mco2))]
mod mco;
use critical_section::CriticalSection;
#[cfg(any(mco, mco1, mco2))]
pub use mco::*;
#[cfg(crs)]
mod hsi48;
#[cfg(crs)]
pub use hsi48::*;
#[cfg_attr(any(stm32f0, stm32f1, stm32f3), path = "f013.rs")]
#[cfg_attr(any(stm32f2, stm32f4, stm32f7), path = "f247.rs")]
#[cfg_attr(stm32c0, path = "c0.rs")]
#[cfg_attr(stm32g0, path = "g0.rs")]
#[cfg_attr(stm32g4, path = "g4.rs")]
#[cfg_attr(any(stm32h5, stm32h7, stm32h7rs), path = "h.rs")]
#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")]
#[cfg_attr(stm32u3, path = "u3.rs")]
#[cfg_attr(stm32u5, path = "u5.rs")]
#[cfg_attr(stm32wba, path = "wba.rs")]
#[cfg_attr(stm32n6, path = "n6.rs")]
mod _version;
pub use _version::*;
use stm32_metapac::RCC;
pub use crate::_generated::{Clocks, mux};
use crate::time::Hertz;
#[cfg(feature = "low-power")]
pub(crate) static mut REFCOUNT_STOP1: u32 = 0;
#[cfg(feature = "low-power")]
pub(crate) static mut REFCOUNT_STOP2: u32 = 0;
#[cfg(all(feature = "low-power", not(feature = "_dual-core")))]
pub(crate) static mut RCC_CONFIG: Option<Config> = None;
#[cfg(all(feature = "low-power", feature = "_dual-core"))]
static RCC_CONFIG_PTR: core::sync::atomic::AtomicPtr<MaybeUninit<Option<Config>>> =
core::sync::atomic::AtomicPtr::new(core::ptr::null_mut());
#[cfg(backup_sram)]
pub(crate) static mut BKSRAM_RETAINED: bool = false;
#[cfg(not(feature = "_dual-core"))]
static mut CLOCK_FREQS: MaybeUninit<Clocks> = MaybeUninit::uninit();
#[cfg(feature = "_dual-core")]
static CLOCK_FREQS_PTR: core::sync::atomic::AtomicPtr<MaybeUninit<Clocks>> =
core::sync::atomic::AtomicPtr::new(core::ptr::null_mut());
#[cfg(feature = "_dual-core")]
pub(crate) fn set_freqs_ptr(freqs: *mut MaybeUninit<Clocks>) {
CLOCK_FREQS_PTR.store(freqs, core::sync::atomic::Ordering::SeqCst);
}
#[cfg(all(feature = "low-power", feature = "_dual-core"))]
pub(crate) fn set_rcc_config_ptr(config: *mut MaybeUninit<Option<Config>>) {
RCC_CONFIG_PTR.store(config, core::sync::atomic::Ordering::SeqCst);
}
#[cfg(not(feature = "_dual-core"))]
pub(crate) unsafe fn set_freqs(freqs: Clocks) {
debug!("rcc: {:?}", freqs);
CLOCK_FREQS = MaybeUninit::new(freqs);
}
#[cfg(feature = "_dual-core")]
pub(crate) unsafe fn set_freqs(freqs: Clocks) {
debug!("rcc: {:?}", freqs);
CLOCK_FREQS_PTR
.load(core::sync::atomic::Ordering::SeqCst)
.write(MaybeUninit::new(freqs));
}
#[cfg(not(feature = "_dual-core"))]
pub(crate) unsafe fn get_freqs() -> &'static Clocks {
(*core::ptr::addr_of_mut!(CLOCK_FREQS)).assume_init_ref()
}
#[cfg(feature = "_dual-core")]
pub(crate) unsafe fn get_freqs() -> &'static Clocks {
unwrap!(CLOCK_FREQS_PTR.load(core::sync::atomic::Ordering::SeqCst).as_ref()).assume_init_ref()
}
#[cfg(all(feature = "low-power", not(feature = "_dual-core")))]
pub(crate) unsafe fn get_rcc_config() -> Option<Config> {
RCC_CONFIG
}
#[cfg(all(feature = "low-power", feature = "_dual-core"))]
pub(crate) unsafe fn get_rcc_config() -> Option<Config> {
unwrap!(RCC_CONFIG_PTR.load(core::sync::atomic::Ordering::SeqCst).as_ref()).assume_init()
}
#[cfg(all(feature = "low-power", not(feature = "_dual-core")))]
unsafe fn set_rcc_config(config: Option<Config>) {
RCC_CONFIG = config;
}
#[cfg(all(feature = "low-power", feature = "_dual-core"))]
unsafe fn set_rcc_config(config: Option<Config>) {
RCC_CONFIG_PTR
.load(core::sync::atomic::Ordering::SeqCst)
.write(MaybeUninit::new(config));
}
pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clocks {
unsafe { get_freqs() }
}
#[cfg(feature = "low-power")]
pub fn get_stop_mode(_cs: CriticalSection) -> Option<StopMode> {
unsafe { get_rcc_config()? };
if unsafe { REFCOUNT_STOP2 == 0 && REFCOUNT_STOP1 == 0 } {
Some(StopMode::Stop2)
} else if unsafe { REFCOUNT_STOP1 == 0 } {
Some(StopMode::Stop1)
} else {
None
}
}
#[cfg(feature = "low-power")]
#[allow(dead_code)]
pub(crate) unsafe fn reset_stop_refcount(_cs: CriticalSection) {
REFCOUNT_STOP2 = 0;
REFCOUNT_STOP1 = 0;
}
#[cfg(feature = "low-power")]
fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) {
match stop_mode {
StopMode::Standby => {}
StopMode::Stop2 => unsafe {
REFCOUNT_STOP2 += 1;
},
StopMode::Stop1 => unsafe {
REFCOUNT_STOP1 += 1;
},
}
}
#[cfg(feature = "low-power")]
fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) {
match stop_mode {
StopMode::Standby => {}
StopMode::Stop2 => unsafe {
REFCOUNT_STOP2 -= 1;
},
StopMode::Stop1 => unsafe {
REFCOUNT_STOP1 -= 1;
},
}
}
pub(crate) trait SealedRccPeripheral {
fn frequency() -> Hertz;
#[allow(dead_code)]
fn bus_frequency() -> Hertz;
const RCC_INFO: RccInfo;
}
#[allow(private_bounds)]
pub trait RccPeripheral: SealedRccPeripheral + 'static {}
pub(crate) struct RccInfo {
reset_offset_or_0xff: u8,
reset_bit: u8,
enable_offset: u8,
enable_bit: u8,
refcount_idx_or_0xff: u8,
#[cfg(feature = "low-power")]
stop_mode: StopMode,
}
#[cfg(feature = "low-power")]
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum StopMode {
#[default]
Stop1,
Stop2,
Standby,
}
#[cfg(feature = "low-power")]
impl StopMode {
pub const fn at_least(&self, other: StopMode) -> bool {
match other {
Self::Stop1 => true,
Self::Stop2 => match *self {
Self::Stop2 | Self::Standby => true,
_ => false,
},
Self::Standby => match *self {
Self::Standby => true,
_ => false,
},
}
}
}
impl RccInfo {
pub(crate) const unsafe fn new(
reset_offset_and_bit: Option<(u8, u8)>,
enable_offset_and_bit: (u8, u8),
refcount_idx: Option<u8>,
#[cfg(feature = "low-power")] stop_mode: StopMode,
) -> Self {
let (reset_offset_or_0xff, reset_bit) = match reset_offset_and_bit {
Some((offset, bit)) => (offset, bit),
None => (0xff, 0xff),
};
let (enable_offset, enable_bit) = enable_offset_and_bit;
let refcount_idx_or_0xff = match refcount_idx {
Some(idx) => idx,
None => 0xff,
};
Self {
reset_offset_or_0xff,
reset_bit,
enable_offset,
enable_bit,
refcount_idx_or_0xff,
#[cfg(feature = "low-power")]
stop_mode,
}
}
pub(crate) fn enable_and_reset_with_cs(&self, cs: CriticalSection) {
if self.refcount_idx_or_0xff != 0xff {
let refcount_idx = self.refcount_idx_or_0xff as usize;
if let Some(refcount) =
unsafe { (*core::ptr::addr_of_mut!(crate::_generated::REFCOUNTS)).get_mut(refcount_idx) }
{
*refcount += 1;
if *refcount > 1 {
return;
}
} else {
panic!("refcount_idx out of bounds: {}", refcount_idx)
}
}
let reset_ptr = self.reset_ptr();
if let Some(reset_ptr) = reset_ptr {
#[cfg(not(stm32wl5x))]
unsafe {
let val = reset_ptr.read_volatile();
reset_ptr.write_volatile(val | 1u32 << self.reset_bit);
}
#[cfg(stm32wl5x)]
unsafe {
let _lock = crate::hsem::get_hsem(3).blocking_lock(0);
if !self.is_enabled_by_other_core() {
let val = reset_ptr.read_volatile();
reset_ptr.write_volatile(val | 1u32 << self.reset_bit);
trace!("rcc: reset 0x{:x}:{}", self.enable_offset, self.enable_bit);
}
}
}
self.enable_with_cs(cs);
}
pub(crate) fn enable_with_cs(&self, _cs: CriticalSection) {
let enable_ptr = self.enable_ptr();
unsafe {
#[cfg(not(all(stm32wl5x, feature = "_core-cm0p")))]
{
let val = enable_ptr.read_volatile();
enable_ptr.write_volatile(val | 1u32 << self.enable_bit);
}
#[cfg(all(stm32wl5x, feature = "_core-cm0p"))]
{
let enable_ptr = enable_ptr.add(0x100 / 4);
let val = enable_ptr.read_volatile();
enable_ptr.write_volatile(val | 1u32 << self.enable_bit);
}
trace!("rcc: enabled 0x{:x}:{}", self.enable_offset, self.enable_bit);
}
let _ = unsafe { enable_ptr.read_volatile() };
cortex_m::asm::dsb();
let reset_ptr = self.reset_ptr();
if let Some(reset_ptr) = reset_ptr {
unsafe {
let val = reset_ptr.read_volatile();
reset_ptr.write_volatile(val & !(1u32 << self.reset_bit));
}
}
}
pub(crate) fn disable_with_cs(&self, _cs: CriticalSection) {
if self.refcount_idx_or_0xff != 0xff {
let refcount_idx = self.refcount_idx_or_0xff as usize;
if let Some(refcount) =
unsafe { (*core::ptr::addr_of_mut!(crate::_generated::REFCOUNTS)).get_mut(refcount_idx) }
{
*refcount -= 1;
if *refcount > 0 {
return;
}
} else {
panic!("refcount_idx out of bounds: {}", refcount_idx)
}
}
let enable_ptr = self.enable_ptr();
unsafe {
#[cfg(not(all(stm32wl5x, feature = "_core-cm0p")))]
{
let val = enable_ptr.read_volatile();
enable_ptr.write_volatile(val & !(1u32 << self.enable_bit));
}
#[cfg(all(stm32wl5x, feature = "_core-cm0p"))]
{
let enable_ptr = enable_ptr.add(0x100 / 4);
let val = enable_ptr.read_volatile();
enable_ptr.write_volatile(val & !(1u32 << self.enable_bit));
}
trace!("rcc: disabled 0x{:x}:{}", self.enable_offset, self.enable_bit);
}
}
#[allow(dead_code)]
pub(crate) fn increment_stop_refcount_with_cs(&self, _cs: CriticalSection) {
#[cfg(feature = "low-power")]
increment_stop_refcount(_cs, self.stop_mode);
}
#[allow(dead_code)]
fn increment_minimum_stop_refcount_with_cs(&self, _cs: CriticalSection) {
#[cfg(all(any(stm32wl, stm32wb), feature = "low-power"))]
match self.stop_mode {
StopMode::Stop1 | StopMode::Stop2 => increment_stop_refcount(_cs, StopMode::Stop2),
_ => {}
}
}
#[allow(dead_code)]
pub(crate) fn increment_stop_refcount(&self) {
#[cfg(feature = "low-power")]
critical_section::with(|cs| self.increment_stop_refcount_with_cs(cs))
}
#[allow(dead_code)]
pub(crate) fn decrement_stop_refcount_with_cs(&self, _cs: CriticalSection) {
#[cfg(feature = "low-power")]
decrement_stop_refcount(_cs, self.stop_mode);
}
#[allow(dead_code)]
fn decrement_minimum_stop_refcount_with_cs(&self, _cs: CriticalSection) {
#[cfg(all(any(stm32wl, stm32wb), feature = "low-power"))]
match self.stop_mode {
StopMode::Stop1 | StopMode::Stop2 => decrement_stop_refcount(_cs, StopMode::Stop2),
_ => {}
}
}
#[allow(dead_code)]
pub(crate) fn decrement_stop_refcount(&self) {
#[cfg(feature = "low-power")]
critical_section::with(|cs| self.decrement_stop_refcount_with_cs(cs))
}
pub(crate) fn enable_and_reset(&self) {
critical_section::with(|cs| {
self.enable_and_reset_with_cs(cs);
self.increment_stop_refcount_with_cs(cs);
})
}
#[allow(dead_code)]
pub(crate) fn enable_and_reset_without_stop(&self) {
critical_section::with(|cs| {
self.enable_and_reset_with_cs(cs);
self.increment_minimum_stop_refcount_with_cs(cs);
})
}
pub(crate) fn disable(&self) {
critical_section::with(|cs| {
self.disable_with_cs(cs);
self.decrement_stop_refcount_with_cs(cs);
})
}
#[allow(dead_code)]
pub(crate) fn disable_without_stop(&self) {
critical_section::with(|cs| {
self.disable_with_cs(cs);
self.decrement_minimum_stop_refcount_with_cs(cs);
})
}
#[allow(dead_code)]
pub(crate) fn wake_guard(&self) -> WakeGuard {
WakeGuard::new(
#[cfg(feature = "low-power")]
self.stop_mode,
)
}
fn reset_ptr(&self) -> Option<*mut u32> {
if self.reset_offset_or_0xff != 0xff {
Some(unsafe { (RCC.as_ptr() as *mut u32).add(self.reset_offset_or_0xff as _) })
} else {
None
}
}
fn enable_ptr(&self) -> *mut u32 {
unsafe { (RCC.as_ptr() as *mut u32).add(self.enable_offset as _) }
}
#[cfg(stm32wl5x)]
unsafe fn is_enabled_by_other_core(&self) -> bool {
let ptr = self.enable_ptr();
#[cfg(feature = "_core-cm4")]
let ptr = ptr.add(0x100);
(ptr.read_volatile() & (1u32 << self.enable_bit)) != 0
}
}
pub struct WakeGuard {
#[cfg(feature = "low-power")]
stop_mode: StopMode,
}
impl WakeGuard {
pub fn new(#[cfg(feature = "low-power")] stop_mode: StopMode) -> Self {
#[cfg(feature = "low-power")]
critical_section::with(|cs| increment_stop_refcount(cs, stop_mode));
Self {
#[cfg(feature = "low-power")]
stop_mode,
}
}
}
impl Drop for WakeGuard {
fn drop(&mut self) {
#[cfg(feature = "low-power")]
critical_section::with(|cs| decrement_stop_refcount(cs, self.stop_mode));
}
}
#[allow(unused)]
mod util {
use crate::time::Hertz;
pub fn calc_pclk<D>(hclk: Hertz, ppre: D) -> (Hertz, Hertz)
where
Hertz: core::ops::Div<D, Output = Hertz>,
{
let pclk = hclk / ppre;
let pclk_tim = if hclk == pclk { pclk } else { pclk * 2u32 };
(pclk, pclk_tim)
}
pub fn all_equal<T: Eq>(mut iter: impl Iterator<Item = T>) -> bool {
let Some(x) = iter.next() else { return true };
if !iter.all(|y| y == x) {
return false;
}
true
}
pub fn get_equal<T: Eq>(mut iter: impl Iterator<Item = T>) -> Result<Option<T>, ()> {
let Some(x) = iter.next() else { return Ok(None) };
if !iter.all(|y| y == x) {
return Err(());
}
Ok(Some(x))
}
}
pub fn frequency<T: RccPeripheral>() -> Hertz {
T::frequency()
}
pub fn enable_and_reset_with_cs<T: RccPeripheral>(cs: CriticalSection) {
T::RCC_INFO.enable_and_reset_with_cs(cs);
}
pub fn enable_with_cs<T: RccPeripheral>(cs: CriticalSection) {
T::RCC_INFO.enable_with_cs(cs);
}
pub fn disable_with_cs<T: RccPeripheral>(cs: CriticalSection) {
T::RCC_INFO.disable_with_cs(cs);
}
pub fn enable_and_reset<T: RccPeripheral>() {
T::RCC_INFO.enable_and_reset();
}
pub fn enable_and_reset_without_stop<T: RccPeripheral>() {
T::RCC_INFO.enable_and_reset_without_stop();
}
pub fn disable<T: RccPeripheral>() {
T::RCC_INFO.disable();
}
#[cfg(not(feature = "_dual-core"))]
pub fn reinit(config: Config, _rcc: &'_ mut crate::Peri<'_, crate::peripherals::RCC>) {
critical_section::with(|cs| {
init_rcc(cs, config);
#[cfg(feature = "_time-driver")]
crate::time_driver::init(cs);
})
}
#[cfg(feature = "low-power")]
#[allow(dead_code)]
pub(crate) fn reinit_saved(_cs: CriticalSection) {
unsafe { init(get_rcc_config().unwrap()) };
}
pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) {
unsafe {
#[cfg(feature = "_lp-time-driver")]
let config = {
use crate::pac::rcc::vals::Lptimsel;
let mut config = config;
#[cfg(time_driver_lptim1)]
{
match config.mux.lptim1sel {
Lptimsel::PCLK1 => {
config.mux.lptim1sel = Lptimsel::LSI;
config.ls.lsi = true;
}
Lptimsel::LSI => {
config.ls.lsi = true;
}
Lptimsel::HSI => {
config.hsi = true;
}
Lptimsel::LSE => {
if let Some(mut lse_config) = config.ls.lse {
lse_config.peripherals_clocked = true;
config.ls.lse = Some(lse_config);
} else {
panic!("LSE is not not configured, but selected for time_driver!!!");
}
}
}
}
#[cfg(time_driver_lptim2)]
{
match config.mux.lptim2sel {
Lptimsel::PCLK1 => {
config.mux.lptim2sel = Lptimsel::LSI;
config.ls.lsi = true;
}
Lptimsel::LSI => {
config.ls.lsi = true;
}
Lptimsel::HSI => {
config.hsi = true;
}
Lptimsel::LSE => {
if let Some(mut lse_config) = config.ls.lse {
lse_config.peripherals_clocked = true;
config.ls.lse = Some(lse_config);
} else {
panic!("LSE is not not configured, but selected for time_driver!!!");
}
}
}
}
#[cfg(time_driver_lptim3)]
{
match config.mux.lptim3sel {
Lptimsel::PCLK1 => {
config.mux.lptim3sel = Lptimsel::LSI;
config.ls.lsi = true;
}
Lptimsel::LSI => {
config.ls.lsi = true;
}
Lptimsel::HSI => {
config.hsi = true;
}
Lptimsel::LSE => {
if let Some(mut lse_config) = config.ls.lse {
lse_config.peripherals_clocked = true;
config.ls.lse = Some(lse_config);
} else {
panic!("LSE is not not configured, but selected for time_driver!!!");
}
}
}
}
config
};
init(config);
#[cfg(feature = "low-power")]
set_rcc_config(Some(config));
}
}
#[allow(unused)]
pub(crate) fn raw_prescaler(freq_in: u32, freq_max: u32) -> u32 {
freq_in.saturating_sub(1) / freq_max
}