1 | #![allow (non_snake_case)] |
2 | |
3 | use core::cell::{Cell, RefCell}; |
4 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; |
5 | |
6 | use critical_section::CriticalSection; |
7 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
8 | use embassy_sync::blocking_mutex::Mutex; |
9 | use embassy_time_driver::{Driver, TICK_HZ}; |
10 | use embassy_time_queue_utils::Queue; |
11 | use stm32_metapac::timer::{regs, TimGp16}; |
12 | |
13 | use crate::interrupt::typelevel::Interrupt; |
14 | use crate::pac::timer::vals; |
15 | use crate::rcc::{self, SealedRccPeripheral}; |
16 | #[cfg (feature = "low-power" )] |
17 | use crate::rtc::Rtc; |
18 | use crate::timer::{CoreInstance, GeneralInstance1Channel}; |
19 | use crate::{interrupt, peripherals}; |
20 | |
21 | // NOTE regarding ALARM_COUNT: |
22 | // |
23 | // As of 2023-12-04, this driver is implemented using CC1 as the halfway rollover interrupt, and any |
24 | // additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST |
25 | // one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not |
26 | // candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.) |
27 | |
28 | #[cfg (time_driver_tim1)] |
29 | type T = peripherals::TIM1; |
30 | #[cfg (time_driver_tim2)] |
31 | type T = peripherals::TIM2; |
32 | #[cfg (time_driver_tim3)] |
33 | type T = peripherals::TIM3; |
34 | #[cfg (time_driver_tim4)] |
35 | type T = peripherals::TIM4; |
36 | #[cfg (time_driver_tim5)] |
37 | type T = peripherals::TIM5; |
38 | #[cfg (time_driver_tim8)] |
39 | type T = peripherals::TIM8; |
40 | #[cfg (time_driver_tim9)] |
41 | type T = peripherals::TIM9; |
42 | #[cfg (time_driver_tim12)] |
43 | type T = peripherals::TIM12; |
44 | #[cfg (time_driver_tim15)] |
45 | type T = peripherals::TIM15; |
46 | #[cfg (time_driver_tim20)] |
47 | type T = peripherals::TIM20; |
48 | #[cfg (time_driver_tim21)] |
49 | type T = peripherals::TIM21; |
50 | #[cfg (time_driver_tim22)] |
51 | type T = peripherals::TIM22; |
52 | #[cfg (time_driver_tim23)] |
53 | type T = peripherals::TIM23; |
54 | #[cfg (time_driver_tim24)] |
55 | type T = peripherals::TIM24; |
56 | |
57 | foreach_interrupt! { |
58 | (TIM1, timer, $block:ident, CC, $irq:ident) => { |
59 | #[cfg(time_driver_tim1)] |
60 | #[cfg(feature = "rt" )] |
61 | #[interrupt] |
62 | fn $irq() { |
63 | DRIVER.on_interrupt() |
64 | } |
65 | }; |
66 | (TIM2, timer, $block:ident, CC, $irq:ident) => { |
67 | #[cfg(time_driver_tim2)] |
68 | #[cfg(feature = "rt" )] |
69 | #[interrupt] |
70 | fn $irq() { |
71 | DRIVER.on_interrupt() |
72 | } |
73 | }; |
74 | (TIM3, timer, $block:ident, CC, $irq:ident) => { |
75 | #[cfg(time_driver_tim3)] |
76 | #[cfg(feature = "rt" )] |
77 | #[interrupt] |
78 | fn $irq() { |
79 | DRIVER.on_interrupt() |
80 | } |
81 | }; |
82 | (TIM4, timer, $block:ident, CC, $irq:ident) => { |
83 | #[cfg(time_driver_tim4)] |
84 | #[cfg(feature = "rt" )] |
85 | #[interrupt] |
86 | fn $irq() { |
87 | DRIVER.on_interrupt() |
88 | } |
89 | }; |
90 | (TIM5, timer, $block:ident, CC, $irq:ident) => { |
91 | #[cfg(time_driver_tim5)] |
92 | #[cfg(feature = "rt" )] |
93 | #[interrupt] |
94 | fn $irq() { |
95 | DRIVER.on_interrupt() |
96 | } |
97 | }; |
98 | (TIM8, timer, $block:ident, CC, $irq:ident) => { |
99 | #[cfg(time_driver_tim8)] |
100 | #[cfg(feature = "rt" )] |
101 | #[interrupt] |
102 | fn $irq() { |
103 | DRIVER.on_interrupt() |
104 | } |
105 | }; |
106 | (TIM9, timer, $block:ident, CC, $irq:ident) => { |
107 | #[cfg(time_driver_tim9)] |
108 | #[cfg(feature = "rt" )] |
109 | #[interrupt] |
110 | fn $irq() { |
111 | DRIVER.on_interrupt() |
112 | } |
113 | }; |
114 | (TIM12, timer, $block:ident, CC, $irq:ident) => { |
115 | #[cfg(time_driver_tim12)] |
116 | #[cfg(feature = "rt" )] |
117 | #[interrupt] |
118 | fn $irq() { |
119 | DRIVER.on_interrupt() |
120 | } |
121 | }; |
122 | (TIM15, timer, $block:ident, CC, $irq:ident) => { |
123 | #[cfg(time_driver_tim15)] |
124 | #[cfg(feature = "rt" )] |
125 | #[interrupt] |
126 | fn $irq() { |
127 | DRIVER.on_interrupt() |
128 | } |
129 | }; |
130 | (TIM20, timer, $block:ident, CC, $irq:ident) => { |
131 | #[cfg(time_driver_tim20)] |
132 | #[cfg(feature = "rt" )] |
133 | #[interrupt] |
134 | fn $irq() { |
135 | DRIVER.on_interrupt() |
136 | } |
137 | }; |
138 | (TIM21, timer, $block:ident, CC, $irq:ident) => { |
139 | #[cfg(time_driver_tim21)] |
140 | #[cfg(feature = "rt" )] |
141 | #[interrupt] |
142 | fn $irq() { |
143 | DRIVER.on_interrupt() |
144 | } |
145 | }; |
146 | (TIM22, timer, $block:ident, CC, $irq:ident) => { |
147 | #[cfg(time_driver_tim22)] |
148 | #[cfg(feature = "rt" )] |
149 | #[interrupt] |
150 | fn $irq() { |
151 | DRIVER.on_interrupt() |
152 | } |
153 | }; |
154 | (TIM23, timer, $block:ident, CC, $irq:ident) => { |
155 | #[cfg(time_driver_tim23)] |
156 | #[cfg(feature = "rt" )] |
157 | #[interrupt] |
158 | fn $irq() { |
159 | DRIVER.on_interrupt() |
160 | } |
161 | }; |
162 | (TIM24, timer, $block:ident, CC, $irq:ident) => { |
163 | #[cfg(time_driver_tim24)] |
164 | #[cfg(feature = "rt" )] |
165 | #[interrupt] |
166 | fn $irq() { |
167 | DRIVER.on_interrupt() |
168 | } |
169 | }; |
170 | } |
171 | |
172 | fn regs_gp16() -> TimGp16 { |
173 | unsafe { TimGp16::from_ptr(T::regs()) } |
174 | } |
175 | |
176 | // Clock timekeeping works with something we call "periods", which are time intervals |
177 | // of 2^15 ticks. The Clock counter value is 16 bits, so one "overflow cycle" is 2 periods. |
178 | // |
179 | // A `period` count is maintained in parallel to the Timer hardware `counter`, like this: |
180 | // - `period` and `counter` start at 0 |
181 | // - `period` is incremented on overflow (at counter value 0) |
182 | // - `period` is incremented "midway" between overflows (at counter value 0x8000) |
183 | // |
184 | // Therefore, when `period` is even, counter is in 0..0x7FFF. When odd, counter is in 0x8000..0xFFFF |
185 | // This allows for now() to return the correct value even if it races an overflow. |
186 | // |
187 | // To get `now()`, `period` is read first, then `counter` is read. If the counter value matches |
188 | // the expected range for the `period` parity, we're done. If it doesn't, this means that |
189 | // a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value |
190 | // corresponds to the next period. |
191 | // |
192 | // `period` is a 32bit integer, so It overflows on 2^32 * 2^15 / 32768 seconds of uptime, which is 136 years. |
193 | fn calc_now(period: u32, counter: u16) -> u64 { |
194 | ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64) |
195 | } |
196 | |
197 | struct AlarmState { |
198 | timestamp: Cell<u64>, |
199 | } |
200 | |
201 | unsafe impl Send for AlarmState {} |
202 | |
203 | impl AlarmState { |
204 | const fn new() -> Self { |
205 | Self { |
206 | timestamp: Cell::new(u64::MAX), |
207 | } |
208 | } |
209 | } |
210 | |
211 | pub(crate) struct RtcDriver { |
212 | /// Number of 2^15 periods elapsed since boot. |
213 | period: AtomicU32, |
214 | alarm: Mutex<CriticalSectionRawMutex, AlarmState>, |
215 | #[cfg (feature = "low-power" )] |
216 | rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>, |
217 | queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>, |
218 | } |
219 | |
220 | embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { |
221 | period: AtomicU32::new(0), |
222 | alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), |
223 | #[cfg (feature = "low-power" )] |
224 | rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), |
225 | queue: Mutex::new(RefCell::new(Queue::new())) |
226 | }); |
227 | |
228 | impl RtcDriver { |
229 | fn init(&'static self, cs: critical_section::CriticalSection) { |
230 | let r = regs_gp16(); |
231 | |
232 | rcc::enable_and_reset_with_cs::<T>(cs); |
233 | |
234 | let timer_freq = T::frequency(); |
235 | |
236 | r.cr1().modify(|w| w.set_cen(false)); |
237 | r.cnt().write(|w| w.set_cnt(0)); |
238 | |
239 | let psc = timer_freq.0 / TICK_HZ as u32 - 1; |
240 | let psc: u16 = match psc.try_into() { |
241 | Err(_) => panic!("psc division overflow: {}" , psc), |
242 | Ok(n) => n, |
243 | }; |
244 | |
245 | r.psc().write_value(psc); |
246 | r.arr().write(|w| w.set_arr(u16::MAX)); |
247 | |
248 | // Set URS, generate update and clear URS |
249 | r.cr1().modify(|w| w.set_urs(vals::Urs::COUNTER_ONLY)); |
250 | r.egr().write(|w| w.set_ug(true)); |
251 | r.cr1().modify(|w| w.set_urs(vals::Urs::ANY_EVENT)); |
252 | |
253 | // Mid-way point |
254 | r.ccr(0).write(|w| w.set_ccr(0x8000)); |
255 | |
256 | // Enable overflow and half-overflow interrupts |
257 | r.dier().write(|w| { |
258 | w.set_uie(true); |
259 | w.set_ccie(0, true); |
260 | }); |
261 | |
262 | <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend(); |
263 | unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() }; |
264 | |
265 | r.cr1().modify(|w| w.set_cen(true)); |
266 | } |
267 | |
268 | fn on_interrupt(&self) { |
269 | let r = regs_gp16(); |
270 | |
271 | critical_section::with(|cs| { |
272 | let sr = r.sr().read(); |
273 | let dier = r.dier().read(); |
274 | |
275 | // Clear all interrupt flags. Bits in SR are "write 0 to clear", so write the bitwise NOT. |
276 | // Other approaches such as writing all zeros, or RMWing won't work, they can |
277 | // miss interrupts. |
278 | r.sr().write_value(regs::SrGp16(!sr.0)); |
279 | |
280 | // Overflow |
281 | if sr.uif() { |
282 | self.next_period(); |
283 | } |
284 | |
285 | // Half overflow |
286 | if sr.ccif(0) { |
287 | self.next_period(); |
288 | } |
289 | |
290 | let n = 0; |
291 | if sr.ccif(n + 1) && dier.ccie(n + 1) { |
292 | self.trigger_alarm(cs); |
293 | } |
294 | }) |
295 | } |
296 | |
297 | fn next_period(&self) { |
298 | let r = regs_gp16(); |
299 | |
300 | // We only modify the period from the timer interrupt, so we know this can't race. |
301 | let period = self.period.load(Ordering::Relaxed) + 1; |
302 | self.period.store(period, Ordering::Relaxed); |
303 | let t = (period as u64) << 15; |
304 | |
305 | critical_section::with(move |cs| { |
306 | r.dier().modify(move |w| { |
307 | let n = 0; |
308 | let alarm = self.alarm.borrow(cs); |
309 | let at = alarm.timestamp.get(); |
310 | |
311 | if at < t + 0xc000 { |
312 | // just enable it. `set_alarm` has already set the correct CCR val. |
313 | w.set_ccie(n + 1, true); |
314 | } |
315 | }) |
316 | }) |
317 | } |
318 | |
319 | fn trigger_alarm(&self, cs: CriticalSection) { |
320 | let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); |
321 | while !self.set_alarm(cs, next) { |
322 | next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); |
323 | } |
324 | } |
325 | |
326 | /* |
327 | Low-power private functions: all operate within a critical seciton |
328 | */ |
329 | |
330 | #[cfg (feature = "low-power" )] |
331 | /// Compute the approximate amount of time until the next alarm |
332 | fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration { |
333 | let now = self.now() + 32; |
334 | |
335 | embassy_time::Duration::from_ticks(self.alarm.borrow(cs).timestamp.get().saturating_sub(now)) |
336 | } |
337 | |
338 | #[cfg (feature = "low-power" )] |
339 | /// Add the given offset to the current time |
340 | fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { |
341 | let offset = offset.as_ticks(); |
342 | let cnt = regs_gp16().cnt().read().cnt() as u32; |
343 | let period = self.period.load(Ordering::SeqCst); |
344 | |
345 | // Correct the race, if it exists |
346 | let period = if period & 1 == 1 && cnt < u16::MAX as u32 / 2 { |
347 | period + 1 |
348 | } else { |
349 | period |
350 | }; |
351 | |
352 | // Normalize to the full overflow |
353 | let period = (period / 2) * 2; |
354 | |
355 | // Add the offset |
356 | let period = period + 2 * (offset / u16::MAX as u64) as u32; |
357 | let cnt = cnt + (offset % u16::MAX as u64) as u32; |
358 | |
359 | let (cnt, period) = if cnt > u16::MAX as u32 { |
360 | (cnt - u16::MAX as u32, period + 2) |
361 | } else { |
362 | (cnt, period) |
363 | }; |
364 | |
365 | let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period }; |
366 | |
367 | self.period.store(period, Ordering::SeqCst); |
368 | regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); |
369 | |
370 | // Now, recompute alarm |
371 | let alarm = self.alarm.borrow(cs); |
372 | |
373 | if !self.set_alarm(cs, alarm.timestamp.get()) { |
374 | // If the alarm timestamp has passed, we need to trigger it |
375 | self.trigger_alarm(cs); |
376 | } |
377 | } |
378 | |
379 | #[cfg (feature = "low-power" )] |
380 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset |
381 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { |
382 | if let Some(offset) = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm(cs) { |
383 | self.add_time(offset, cs); |
384 | } |
385 | } |
386 | |
387 | /* |
388 | Low-power public functions: all create a critical section |
389 | */ |
390 | #[cfg (feature = "low-power" )] |
391 | /// Set the rtc but panic if it's already been set |
392 | pub(crate) fn set_rtc(&self, rtc: &'static Rtc) { |
393 | critical_section::with(|cs| { |
394 | rtc.stop_wakeup_alarm(cs); |
395 | |
396 | assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()) |
397 | }); |
398 | } |
399 | |
400 | #[cfg (feature = "low-power" )] |
401 | /// The minimum pause time beyond which the executor will enter a low-power state. |
402 | pub(crate) const MIN_STOP_PAUSE: embassy_time::Duration = embassy_time::Duration::from_millis(250); |
403 | |
404 | #[cfg (feature = "low-power" )] |
405 | /// Pause the timer if ready; return err if not |
406 | pub(crate) fn pause_time(&self) -> Result<(), ()> { |
407 | critical_section::with(|cs| { |
408 | /* |
409 | If the wakeup timer is currently running, then we need to stop it and |
410 | add the elapsed time to the current time, as this will impact the result |
411 | of `time_until_next_alarm`. |
412 | */ |
413 | self.stop_wakeup_alarm(cs); |
414 | |
415 | let time_until_next_alarm = self.time_until_next_alarm(cs); |
416 | if time_until_next_alarm < Self::MIN_STOP_PAUSE { |
417 | Err(()) |
418 | } else { |
419 | self.rtc |
420 | .borrow(cs) |
421 | .get() |
422 | .unwrap() |
423 | .start_wakeup_alarm(time_until_next_alarm, cs); |
424 | |
425 | regs_gp16().cr1().modify(|w| w.set_cen(false)); |
426 | |
427 | Ok(()) |
428 | } |
429 | }) |
430 | } |
431 | |
432 | #[cfg (feature = "low-power" )] |
433 | /// Resume the timer with the given offset |
434 | pub(crate) fn resume_time(&self) { |
435 | if regs_gp16().cr1().read().cen() { |
436 | // Time isn't currently stopped |
437 | |
438 | return; |
439 | } |
440 | |
441 | critical_section::with(|cs| { |
442 | self.stop_wakeup_alarm(cs); |
443 | |
444 | regs_gp16().cr1().modify(|w| w.set_cen(true)); |
445 | }) |
446 | } |
447 | |
448 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { |
449 | let r = regs_gp16(); |
450 | |
451 | let n = 0; |
452 | self.alarm.borrow(cs).timestamp.set(timestamp); |
453 | |
454 | let t = self.now(); |
455 | if timestamp <= t { |
456 | // If alarm timestamp has passed the alarm will not fire. |
457 | // Disarm the alarm and return `false` to indicate that. |
458 | r.dier().modify(|w| w.set_ccie(n + 1, false)); |
459 | |
460 | self.alarm.borrow(cs).timestamp.set(u64::MAX); |
461 | |
462 | return false; |
463 | } |
464 | |
465 | // Write the CCR value regardless of whether we're going to enable it now or not. |
466 | // This way, when we enable it later, the right value is already set. |
467 | r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16)); |
468 | |
469 | // Enable it if it'll happen soon. Otherwise, `next_period` will enable it. |
470 | let diff = timestamp - t; |
471 | r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)); |
472 | |
473 | // Reevaluate if the alarm timestamp is still in the future |
474 | let t = self.now(); |
475 | if timestamp <= t { |
476 | // If alarm timestamp has passed since we set it, we have a race condition and |
477 | // the alarm may or may not have fired. |
478 | // Disarm the alarm and return `false` to indicate that. |
479 | // It is the caller's responsibility to handle this ambiguity. |
480 | r.dier().modify(|w| w.set_ccie(n + 1, false)); |
481 | |
482 | self.alarm.borrow(cs).timestamp.set(u64::MAX); |
483 | |
484 | return false; |
485 | } |
486 | |
487 | // We're confident the alarm will ring in the future. |
488 | true |
489 | } |
490 | } |
491 | |
492 | impl Driver for RtcDriver { |
493 | fn now(&self) -> u64 { |
494 | let r: TimGp16 = regs_gp16(); |
495 | |
496 | let period: u32 = self.period.load(order:Ordering::Relaxed); |
497 | compiler_fence(order:Ordering::Acquire); |
498 | let counter: u16 = r.cnt().read().cnt(); |
499 | calc_now(period, counter) |
500 | } |
501 | |
502 | fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { |
503 | critical_section::with(|cs: CriticalSection<'_>| { |
504 | let mut queue: RefMut<'_, Queue> = self.queue.borrow(cs).borrow_mut(); |
505 | |
506 | if queue.schedule_wake(at, waker) { |
507 | let mut next: u64 = queue.next_expiration(self.now()); |
508 | while !self.set_alarm(cs, timestamp:next) { |
509 | next = queue.next_expiration(self.now()); |
510 | } |
511 | } |
512 | }) |
513 | } |
514 | } |
515 | |
516 | #[cfg (feature = "low-power" )] |
517 | pub(crate) fn get_driver() -> &'static RtcDriver { |
518 | &DRIVER |
519 | } |
520 | |
521 | pub(crate) fn init(cs: CriticalSection) { |
522 | DRIVER.init(cs) |
523 | } |
524 | |