py32_hal/rcc/
mod.rs

1/// Reset and Clock Control (RCC)
2
3// The following code is modified from embassy-stm32
4// https://github.com/embassy-rs/embassy/tree/main/embassy-stm32
5// Special thanks to the Embassy Project and its contributors for their work!
6
7use core::mem::MaybeUninit;
8
9#[cfg(mco)]
10mod mco;
11use critical_section::CriticalSection;
12#[cfg(mco)]
13pub use mco::*;
14
15use crate::pac::RCC;
16// pub use crate::_generated::{mux, Clocks};
17pub use crate::_generated::mux;
18use crate::time::Hertz;
19
20#[cfg_attr(rcc_f002b, path = "f002b.rs")]
21#[cfg_attr(not(rcc_f002b), path = "f0.rs")]
22mod _version;
23pub use _version::*;
24
25#[derive(Clone, Copy, Debug)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27pub struct Clocks {
28    pub hclk1: crate::time::MaybeHertz,
29    pub pclk1: crate::time::MaybeHertz,
30    pub pclk1_tim: crate::time::MaybeHertz,
31    // pub pclk2: crate::time::MaybeHertz,
32    // pub pclk2_tim: crate::time::MaybeHertz,
33    pub sys: crate::time::MaybeHertz,
34
35    pub hsi: crate::time::MaybeHertz,
36    pub lse: crate::time::MaybeHertz,
37    #[cfg(not(rcc_f002b))]
38    pub pll: crate::time::MaybeHertz,
39    // pub rtc: crate::time::MaybeHertz,
40    // pub sys: Option<crate::time::Hertz>,
41    // pub usb: Option<crate::time::Hertz>,
42}
43
44// #[cfg(feature = "low-power")]
45// /// Must be written within a critical section
46// ///
47// /// May be read without a critical section
48// pub(crate) static mut REFCOUNT_STOP1: u32 = 0;
49
50// #[cfg(feature = "low-power")]
51// /// Must be written within a critical section
52// ///
53// /// May be read without a critical section
54// pub(crate) static mut REFCOUNT_STOP2: u32 = 0;
55
56/// Frozen clock frequencies
57///
58/// The existence of this value indicates that the clock configuration can no longer be changed
59static mut CLOCK_FREQS: MaybeUninit<Clocks> = MaybeUninit::uninit();
60
61/// Sets the clock frequencies
62///
63/// Safety: Sets a mutable global.
64pub(crate) unsafe fn set_freqs(freqs: Clocks) {
65    debug!("rcc: {:?}", freqs);
66    CLOCK_FREQS = MaybeUninit::new(freqs);
67}
68
69/// Safety: Reads a mutable global.
70pub(crate) unsafe fn get_freqs() -> &'static Clocks {
71    (*core::ptr::addr_of_mut!(CLOCK_FREQS)).assume_init_ref()
72}
73
74pub(crate) trait SealedRccPeripheral {
75    fn frequency() -> Hertz;
76    const RCC_INFO: RccInfo;
77}
78
79#[allow(private_bounds)]
80pub trait RccPeripheral: SealedRccPeripheral + 'static {}
81
82/// Runtime information necessary to reset, enable and disable a peripheral.
83pub(crate) struct RccInfo {
84    /// Offset in 32-bit words of the xxxRSTR register into the RCC register block, or 0xff if the
85    /// peripheral has no reset bit (we don't use an `Option` to save one byte of storage).
86    reset_offset_or_0xff: u8,
87    /// Position of the xxxRST bit within the xxxRSTR register (0..=31).
88    reset_bit: u8,
89    /// Offset in 32-bit words of the xxxENR register into the RCC register block.
90    enable_offset: u8,
91    /// Position of the xxxEN bit within the xxxENR register (0..=31).
92    enable_bit: u8,
93    /// If this peripheral shares the same xxxRSTR bit and xxxEN bit with other peripherals, we
94    /// maintain a refcount in `crate::_generated::REFCOUNTS` at this index. If the bit is not
95    /// shared, this is 0xff (we don't use an `Option` to save one byte of storage).
96    refcount_idx_or_0xff: u8,
97    // /// Stop mode of the peripheral, used to maintain `REFCOUNT_STOP1` and `REFCOUNT_STOP2`.
98    // #[cfg(feature = "low-power")]
99    // stop_mode: StopMode,
100}
101
102// #[cfg(feature = "low-power")]
103// #[allow(dead_code)]
104// pub(crate) enum StopMode {
105//     Standby,
106//     Stop2,
107//     Stop1,
108// }
109
110impl RccInfo {
111    /// Safety:
112    /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit
113    /// - `enable_offset_and_bit` must correspond to valid xxxEN bit
114    /// - `refcount_idx`, if set, must correspond to valid refcount in `_generated::REFCOUNTS`
115    /// - `stop_mode` must be valid
116    pub(crate) const unsafe fn new(
117        reset_offset_and_bit: Option<(u8, u8)>,
118        enable_offset_and_bit: (u8, u8),
119        refcount_idx: Option<u8>,
120        // #[cfg(feature = "low-power")] stop_mode: StopMode,
121    ) -> Self {
122        let (reset_offset_or_0xff, reset_bit) = match reset_offset_and_bit {
123            Some((offset, bit)) => (offset, bit),
124            None => (0xff, 0xff),
125        };
126        let (enable_offset, enable_bit) = enable_offset_and_bit;
127        let refcount_idx_or_0xff = match refcount_idx {
128            Some(idx) => idx,
129            None => 0xff,
130        };
131        Self {
132            reset_offset_or_0xff,
133            reset_bit,
134            enable_offset,
135            enable_bit,
136            refcount_idx_or_0xff,
137            // #[cfg(feature = "low-power")]
138            // stop_mode,
139        }
140    }
141
142    // TODO: should this be `unsafe`?
143    pub(crate) fn enable_and_reset_with_cs(&self, _cs: CriticalSection) {
144        if self.refcount_idx_or_0xff != 0xff {
145            let refcount_idx = self.refcount_idx_or_0xff as usize;
146
147            // Use .get_mut instead of []-operator so that we control how bounds checks happen.
148            // Otherwise, core::fmt will be pulled in here in order to format the integer in the
149            // out-of-bounds error.
150            if let Some(refcount) = unsafe {
151                (*core::ptr::addr_of_mut!(crate::_generated::REFCOUNTS)).get_mut(refcount_idx)
152            } {
153                *refcount += 1;
154                if *refcount > 1 {
155                    return;
156                }
157            } else {
158                panic!("refcount_idx out of bounds: {}", refcount_idx)
159            }
160        }
161
162        // #[cfg(feature = "low-power")]
163        // match self.stop_mode {
164        //     StopMode::Standby => {}
165        //     StopMode::Stop2 => unsafe {
166        //         REFCOUNT_STOP2 += 1;
167        //     },
168        //     StopMode::Stop1 => unsafe {
169        //         REFCOUNT_STOP1 += 1;
170        //     },
171        // }
172
173        // set the xxxRST bit
174        let reset_ptr = self.reset_ptr();
175        if let Some(reset_ptr) = reset_ptr {
176            unsafe {
177                let val = reset_ptr.read_volatile();
178                reset_ptr.write_volatile(val | 1u32 << self.reset_bit);
179            }
180        }
181
182        // set the xxxEN bit
183        let enable_ptr = self.enable_ptr();
184        unsafe {
185            let val = enable_ptr.read_volatile();
186            enable_ptr.write_volatile(val | 1u32 << self.enable_bit);
187        }
188
189        // we must wait two peripheral clock cycles before the clock is active
190        // this seems to work, but might be incorrect
191        // see http://efton.sk/STM32/gotcha/g183.html
192
193        // dummy read (like in the ST HALs)
194        let _ = unsafe { enable_ptr.read_volatile() };
195
196        // DSB for good measure
197        cortex_m::asm::dsb();
198
199        // clear the xxxRST bit
200        if let Some(reset_ptr) = reset_ptr {
201            unsafe {
202                let val = reset_ptr.read_volatile();
203                reset_ptr.write_volatile(val & !(1u32 << self.reset_bit));
204            }
205        }
206    }
207
208    // TODO: should this be `unsafe`?
209    pub(crate) fn disable_with_cs(&self, _cs: CriticalSection) {
210        if self.refcount_idx_or_0xff != 0xff {
211            let refcount_idx = self.refcount_idx_or_0xff as usize;
212
213            // Use .get_mut instead of []-operator so that we control how bounds checks happen.
214            // Otherwise, core::fmt will be pulled in here in order to format the integer in the
215            // out-of-bounds error.
216            if let Some(refcount) = unsafe {
217                (*core::ptr::addr_of_mut!(crate::_generated::REFCOUNTS)).get_mut(refcount_idx)
218            } {
219                *refcount -= 1;
220                if *refcount > 0 {
221                    return;
222                }
223            } else {
224                panic!("refcount_idx out of bounds: {}", refcount_idx)
225            }
226        }
227
228        // #[cfg(feature = "low-power")]
229        // match self.stop_mode {
230        //     StopMode::Standby => {}
231        //     StopMode::Stop2 => unsafe {
232        //         REFCOUNT_STOP2 -= 1;
233        //     },
234        //     StopMode::Stop1 => unsafe {
235        //         REFCOUNT_STOP1 -= 1;
236        //     },
237        // }
238
239        // clear the xxxEN bit
240        let enable_ptr = self.enable_ptr();
241        unsafe {
242            let val = enable_ptr.read_volatile();
243            enable_ptr.write_volatile(val & !(1u32 << self.enable_bit));
244        }
245    }
246
247    // TODO: should this be `unsafe`?
248    pub(crate) fn enable_and_reset(&self) {
249        critical_section::with(|cs| self.enable_and_reset_with_cs(cs))
250    }
251
252    // TODO: should this be `unsafe`?
253    pub(crate) fn disable(&self) {
254        critical_section::with(|cs| self.disable_with_cs(cs))
255    }
256
257    fn reset_ptr(&self) -> Option<*mut u32> {
258        if self.reset_offset_or_0xff != 0xff {
259            Some(unsafe { (RCC.as_ptr() as *mut u32).add(self.reset_offset_or_0xff as _) })
260        } else {
261            None
262        }
263    }
264
265    fn enable_ptr(&self) -> *mut u32 {
266        unsafe { (RCC.as_ptr() as *mut u32).add(self.enable_offset as _) }
267    }
268}
269
270#[allow(unused)]
271mod util {
272    use crate::time::Hertz;
273
274    pub fn calc_pclk<D>(hclk: Hertz, ppre: D) -> (Hertz, Hertz)
275    where
276        Hertz: core::ops::Div<D, Output = Hertz>,
277    {
278        let pclk = hclk / ppre;
279        let pclk_tim = if hclk == pclk { pclk } else { pclk * 2u32 };
280        (pclk, pclk_tim)
281    }
282
283    pub fn all_equal<T: Eq>(mut iter: impl Iterator<Item = T>) -> bool {
284        let Some(x) = iter.next() else { return true };
285        if !iter.all(|y| y == x) {
286            return false;
287        }
288        true
289    }
290
291    pub fn get_equal<T: Eq>(mut iter: impl Iterator<Item = T>) -> Result<Option<T>, ()> {
292        let Some(x) = iter.next() else {
293            return Ok(None);
294        };
295        if !iter.all(|y| y == x) {
296            return Err(());
297        }
298        Ok(Some(x))
299    }
300}
301
302/// Get the kernel clock frequency of the peripheral `T`.
303///
304/// # Panics
305///
306/// Panics if the clock is not active.
307pub fn frequency<T: RccPeripheral>() -> Hertz {
308    T::frequency()
309}
310
311/// Enables and resets peripheral `T`.
312///
313/// # Safety
314///
315/// Peripheral must not be in use.
316// TODO: should this be `unsafe`?
317pub fn enable_and_reset_with_cs<T: RccPeripheral>(cs: CriticalSection) {
318    T::RCC_INFO.enable_and_reset_with_cs(cs);
319}
320
321/// Disables peripheral `T`.
322///
323/// # Safety
324///
325/// Peripheral must not be in use.
326// TODO: should this be `unsafe`?
327pub fn disable_with_cs<T: RccPeripheral>(cs: CriticalSection) {
328    T::RCC_INFO.disable_with_cs(cs);
329}
330
331/// Enables and resets peripheral `T`.
332///
333/// # Safety
334///
335/// Peripheral must not be in use.
336// TODO: should this be `unsafe`?
337pub fn enable_and_reset<T: RccPeripheral>() {
338    T::RCC_INFO.enable_and_reset();
339}
340
341/// Disables peripheral `T`.
342///
343/// # Safety
344///
345/// Peripheral must not be in use.
346// TODO: should this be `unsafe`?
347pub fn disable<T: RccPeripheral>() {
348    T::RCC_INFO.disable();
349}