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}