| 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 | |