aboutsummaryrefslogtreecommitdiff
path: root/embassy-mcxa/src/ostimer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-mcxa/src/ostimer.rs')
-rw-r--r--embassy-mcxa/src/ostimer.rs745
1 files changed, 745 insertions, 0 deletions
diff --git a/embassy-mcxa/src/ostimer.rs b/embassy-mcxa/src/ostimer.rs
new file mode 100644
index 000000000..c51812e3d
--- /dev/null
+++ b/embassy-mcxa/src/ostimer.rs
@@ -0,0 +1,745 @@
1//! # OSTIMER Driver with Robustness Features
2//!
3//! This module provides an async timer driver for the NXP MCXA276 OSTIMER peripheral
4//! with protection against race conditions and timer rollover issues.
5//!
6//! ## Features
7//!
8//! - Async timing with embassy-time integration
9//! - Gray code counter handling (42-bit counter)
10//! - Interrupt-driven wakeups
11//! - Configurable interrupt priority
12//! - **Race condition protection**: Critical sections and atomic operations
13//! - **Timer rollover handling**: Bounds checking and rollover prevention
14//!
15//! ## Clock Frequency Configuration
16//!
17//! The OSTIMER frequency depends on your system's clock configuration. You must provide
18//! the actual frequency when calling `time_driver::init()`.
19//!
20//! ## Race Condition Protection
21//! - Critical sections in interrupt handlers prevent concurrent access
22//! - Atomic register operations with memory barriers
23//! - Proper interrupt flag clearing and validation
24//!
25//! ## Timer Rollover Handling
26//! - Bounds checking prevents scheduling beyond timer capacity
27//! - Immediate wake for timestamps that would cause rollover issues
28#![allow(dead_code)]
29
30use core::sync::atomic::{AtomicBool, Ordering};
31
32use embassy_hal_internal::{Peri, PeripheralType};
33
34use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel};
35use crate::clocks::{assert_reset, enable_and_reset, is_reset_released, release_reset, Gate, PoweredClock};
36use crate::interrupt::InterruptExt;
37use crate::pac;
38
39// PAC defines the shared RegisterBlock under `ostimer0`.
40type Regs = pac::ostimer0::RegisterBlock;
41
42// OSTIMER EVTIMER register layout constants
43/// Total width of the EVTIMER counter in bits (42 bits total)
44const EVTIMER_TOTAL_BITS: u32 = 42;
45/// Width of the low part of EVTIMER (bits 31:0)
46const EVTIMER_LO_BITS: u32 = 32;
47/// Width of the high part of EVTIMER (bits 41:32)
48const EVTIMER_HI_BITS: u32 = 10;
49/// Bit position where high part starts in the combined 64-bit value
50const EVTIMER_HI_SHIFT: u32 = 32;
51
52/// Bit mask for the high part of EVTIMER
53const EVTIMER_HI_MASK: u16 = (1 << EVTIMER_HI_BITS) - 1;
54
55/// Maximum value for MATCH_L register (32-bit)
56const MATCH_L_MAX: u32 = u32::MAX;
57/// Maximum value for MATCH_H register (10-bit)
58const MATCH_H_MAX: u16 = EVTIMER_HI_MASK;
59
60/// Bit mask for extracting the low 32 bits from a 64-bit value
61const LOW_32_BIT_MASK: u64 = u32::MAX as u64;
62
63/// Gray code conversion bit shifts (most significant to least)
64const GRAY_CONVERSION_SHIFTS: [u32; 6] = [32, 16, 8, 4, 2, 1];
65
66/// Maximum timer value before rollover (2^42 - 1 ticks)
67/// Actual rollover time depends on the configured clock frequency
68const TIMER_MAX_VALUE: u64 = (1u64 << EVTIMER_TOTAL_BITS) - 1;
69
70/// Threshold for detecting timer rollover in comparisons (1 second at 1MHz)
71const TIMER_ROLLOVER_THRESHOLD: u64 = 1_000_000;
72
73/// Common default interrupt priority for OSTIMER
74const DEFAULT_INTERRUPT_PRIORITY: u8 = 3;
75
76// Global alarm state for interrupt handling
77static ALARM_ACTIVE: AtomicBool = AtomicBool::new(false);
78static mut ALARM_CALLBACK: Option<fn()> = None;
79static mut ALARM_FLAG: Option<*const AtomicBool> = None;
80static mut ALARM_TARGET_TIME: u64 = 0;
81
82/// Number of tight spin iterations between elapsed time checks while waiting for MATCH writes to return to the idle (0) state.
83const MATCH_WRITE_READY_SPINS: usize = 512;
84/// Maximum time (in OSTIMER ticks) to wait for MATCH registers to become writable (~5 ms at 1 MHz).
85const MATCH_WRITE_READY_TIMEOUT_TICKS: u64 = 5_000;
86/// Short stabilization delay executed after toggling the MRCC reset line to let the OSTIMER bus interface settle.
87const RESET_STABILIZE_SPINS: usize = 512;
88
89pub(super) fn wait_for_match_write_ready(r: &Regs) -> bool {
90 let start = now_ticks_read();
91 let mut spin_budget = 0usize;
92
93 loop {
94 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
95 return true;
96 }
97
98 cortex_m::asm::nop();
99 spin_budget += 1;
100
101 if spin_budget >= MATCH_WRITE_READY_SPINS {
102 spin_budget = 0;
103
104 let elapsed = now_ticks_read().wrapping_sub(start);
105 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
106 return false;
107 }
108 }
109 }
110}
111
112pub(super) fn wait_for_match_write_complete(r: &Regs) -> bool {
113 let start = now_ticks_read();
114 let mut spin_budget = 0usize;
115
116 loop {
117 if r.osevent_ctrl().read().match_wr_rdy().bit_is_clear() {
118 return true;
119 }
120
121 cortex_m::asm::nop();
122 spin_budget += 1;
123
124 if spin_budget >= MATCH_WRITE_READY_SPINS {
125 spin_budget = 0;
126
127 let elapsed = now_ticks_read().wrapping_sub(start);
128 if elapsed >= MATCH_WRITE_READY_TIMEOUT_TICKS {
129 return false;
130 }
131 }
132 }
133}
134
135fn prime_match_registers(r: &Regs) {
136 // Disable the interrupt, clear any pending flag, then wait until the MATCH registers are writable.
137 r.osevent_ctrl()
138 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
139
140 if wait_for_match_write_ready(r) {
141 r.match_l().write(|w| unsafe { w.match_value().bits(MATCH_L_MAX) });
142 r.match_h().write(|w| unsafe { w.match_value().bits(MATCH_H_MAX) });
143 let _ = wait_for_match_write_complete(r);
144 }
145}
146
147/// Single-shot alarm functionality for OSTIMER
148pub struct Alarm<'d> {
149 /// Whether the alarm is currently active
150 active: AtomicBool,
151 /// Callback to execute when alarm expires (optional)
152 callback: Option<fn()>,
153 /// Flag that gets set when alarm expires (optional)
154 flag: Option<&'d AtomicBool>,
155 _phantom: core::marker::PhantomData<&'d mut ()>,
156}
157
158impl<'d> Default for Alarm<'d> {
159 fn default() -> Self {
160 Self::new()
161 }
162}
163
164impl<'d> Alarm<'d> {
165 /// Create a new alarm instance
166 pub fn new() -> Self {
167 Self {
168 active: AtomicBool::new(false),
169 callback: None,
170 flag: None,
171 _phantom: core::marker::PhantomData,
172 }
173 }
174
175 /// Set a callback that will be executed when the alarm expires
176 /// Note: Due to interrupt handler constraints, callbacks must be static function pointers
177 pub fn with_callback(mut self, callback: fn()) -> Self {
178 self.callback = Some(callback);
179 self
180 }
181
182 /// Set a flag that will be set to true when the alarm expires
183 pub fn with_flag(mut self, flag: &'d AtomicBool) -> Self {
184 self.flag = Some(flag);
185 self
186 }
187
188 /// Check if the alarm is currently active
189 pub fn is_active(&self) -> bool {
190 self.active.load(Ordering::Acquire)
191 }
192
193 /// Cancel the alarm if it's active
194 pub fn cancel(&self) {
195 self.active.store(false, Ordering::Release);
196 }
197}
198
199/// Configuration for Ostimer::new()
200#[derive(Copy, Clone)]
201pub struct Config {
202 /// Initialize MATCH registers to their max values and mask/clear the interrupt flag.
203 pub init_match_max: bool,
204 pub power: PoweredClock,
205 pub source: OstimerClockSel,
206}
207
208impl Default for Config {
209 fn default() -> Self {
210 Self {
211 init_match_max: true,
212 power: PoweredClock::NormalEnabledDeepSleepDisabled,
213 source: OstimerClockSel::Clk1M,
214 }
215 }
216}
217
218/// OSTIMER peripheral instance
219pub struct Ostimer<'d, I: Instance> {
220 _inst: core::marker::PhantomData<I>,
221 clock_frequency_hz: u64,
222 _phantom: core::marker::PhantomData<&'d mut ()>,
223}
224
225impl<'d, I: Instance> Ostimer<'d, I> {
226 /// Construct OSTIMER handle.
227 /// Requires clocks for the instance to be enabled by the board before calling.
228 /// Does not enable NVIC or INTENA; use time_driver::init() for async operation.
229 pub fn new(_inst: Peri<'d, I>, cfg: Config) -> Self {
230 let clock_freq = unsafe {
231 enable_and_reset::<I>(&OsTimerConfig {
232 power: cfg.power,
233 source: cfg.source,
234 })
235 .expect("Enabling OsTimer clock should not fail")
236 };
237
238 assert!(clock_freq > 0, "OSTIMER frequency must be greater than 0");
239
240 if cfg.init_match_max {
241 let r: &Regs = unsafe { &*I::ptr() };
242 // Mask INTENA, clear pending flag, and set MATCH to max so no spurious IRQ fires.
243 prime_match_registers(r);
244 }
245
246 Self {
247 _inst: core::marker::PhantomData,
248 clock_frequency_hz: clock_freq as u64,
249 _phantom: core::marker::PhantomData,
250 }
251 }
252
253 /// Get the configured clock frequency in Hz
254 pub fn clock_frequency_hz(&self) -> u64 {
255 self.clock_frequency_hz
256 }
257
258 /// Read the current timer counter value in timer ticks
259 ///
260 /// # Returns
261 /// Current timer counter value as a 64-bit unsigned integer
262 pub fn now(&self) -> u64 {
263 now_ticks_read()
264 }
265
266 /// Reset the timer counter to zero
267 ///
268 /// This performs a hardware reset of the OSTIMER peripheral, which will reset
269 /// the counter to zero and clear any pending interrupts. Note that this will
270 /// affect all timer operations including embassy-time.
271 ///
272 /// # Safety
273 /// This operation will reset the entire OSTIMER peripheral. Any active alarms
274 /// or time_driver operations will be disrupted. Use with caution.
275 pub fn reset(&self, _peripherals: &crate::pac::Peripherals) {
276 critical_section::with(|_| {
277 let r: &Regs = unsafe { &*I::ptr() };
278
279 // Mask the peripheral interrupt flag before we toggle the reset line so that
280 // no new NVIC activity races with the reset sequence.
281 r.osevent_ctrl()
282 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
283
284 unsafe {
285 assert_reset::<I>();
286
287 for _ in 0..RESET_STABILIZE_SPINS {
288 cortex_m::asm::nop();
289 }
290
291 release_reset::<I>();
292
293 while !is_reset_released::<I>() {
294 cortex_m::asm::nop();
295 }
296 }
297
298 for _ in 0..RESET_STABILIZE_SPINS {
299 cortex_m::asm::nop();
300 }
301
302 // Clear alarm bookkeeping before re-arming MATCH registers.
303 ALARM_ACTIVE.store(false, Ordering::Release);
304 unsafe {
305 ALARM_TARGET_TIME = 0;
306 ALARM_CALLBACK = None;
307 ALARM_FLAG = None;
308 }
309
310 prime_match_registers(r);
311 });
312
313 // Ensure no stale OS_EVENT request remains pending after the reset sequence.
314 crate::interrupt::OS_EVENT.unpend();
315 }
316
317 /// Schedule a single-shot alarm to expire after the specified delay in microseconds
318 ///
319 /// # Parameters
320 /// * `alarm` - The alarm instance to schedule
321 /// * `delay_us` - Delay in microseconds from now
322 ///
323 /// # Returns
324 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
325 pub fn schedule_alarm_delay(&self, alarm: &Alarm, delay_us: u64) -> bool {
326 let delay_ticks = (delay_us * self.clock_frequency_hz) / 1_000_000;
327 let target_time = now_ticks_read() + delay_ticks;
328 self.schedule_alarm_at(alarm, target_time)
329 }
330
331 /// Schedule a single-shot alarm to expire at the specified absolute time in timer ticks
332 ///
333 /// # Parameters
334 /// * `alarm` - The alarm instance to schedule
335 /// * `target_ticks` - Absolute time in timer ticks when the alarm should expire
336 ///
337 /// # Returns
338 /// `true` if the alarm was scheduled successfully, `false` if it would exceed timer capacity
339 pub fn schedule_alarm_at(&self, alarm: &Alarm, target_ticks: u64) -> bool {
340 let now = now_ticks_read();
341
342 // Check if target time is in the past
343 if target_ticks <= now {
344 // Execute callback immediately if alarm was supposed to be active
345 if alarm.active.load(Ordering::Acquire) {
346 alarm.active.store(false, Ordering::Release);
347 if let Some(callback) = alarm.callback {
348 callback();
349 }
350 if let Some(flag) = &alarm.flag {
351 flag.store(true, Ordering::Release);
352 }
353 }
354 return true;
355 }
356
357 // Check for timer rollover
358 let max_future = now + TIMER_MAX_VALUE;
359 if target_ticks > max_future {
360 return false; // Would exceed timer capacity
361 }
362
363 // Program the timer
364 let r: &Regs = unsafe { &*I::ptr() };
365
366 critical_section::with(|_| {
367 // Disable interrupt and clear flag
368 r.osevent_ctrl()
369 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
370
371 if !wait_for_match_write_ready(r) {
372 prime_match_registers(r);
373
374 if !wait_for_match_write_ready(r) {
375 alarm.active.store(false, Ordering::Release);
376 ALARM_ACTIVE.store(false, Ordering::Release);
377 unsafe {
378 ALARM_TARGET_TIME = 0;
379 ALARM_CALLBACK = None;
380 ALARM_FLAG = None;
381 }
382 return false;
383 }
384 }
385
386 // Mark alarm as active now that we know the MATCH registers are writable
387 alarm.active.store(true, Ordering::Release);
388
389 // Set global alarm state for interrupt handler
390 ALARM_ACTIVE.store(true, Ordering::Release);
391 unsafe {
392 ALARM_TARGET_TIME = target_ticks;
393 ALARM_CALLBACK = alarm.callback;
394 ALARM_FLAG = alarm.flag.map(|f| f as *const AtomicBool);
395 }
396
397 // Program MATCH registers (Gray-coded)
398 let gray = bin_to_gray(target_ticks);
399 let l = (gray & LOW_32_BIT_MASK) as u32;
400 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
401
402 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
403 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
404
405 if !wait_for_match_write_complete(r) {
406 alarm.active.store(false, Ordering::Release);
407 ALARM_ACTIVE.store(false, Ordering::Release);
408 unsafe {
409 ALARM_TARGET_TIME = 0;
410 ALARM_CALLBACK = None;
411 ALARM_FLAG = None;
412 }
413 return false;
414 }
415
416 let now_after_program = now_ticks_read();
417 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
418 if now_after_program >= target_ticks && !intrflag_set {
419 alarm.active.store(false, Ordering::Release);
420 ALARM_ACTIVE.store(false, Ordering::Release);
421 unsafe {
422 ALARM_TARGET_TIME = 0;
423 ALARM_CALLBACK = None;
424 ALARM_FLAG = None;
425 }
426 return false;
427 }
428
429 // Enable interrupt
430 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
431
432 true
433 })
434 }
435
436 /// Cancel any active alarm
437 pub fn cancel_alarm(&self, alarm: &Alarm) {
438 critical_section::with(|_| {
439 alarm.cancel();
440
441 // Clear global alarm state
442 ALARM_ACTIVE.store(false, Ordering::Release);
443 unsafe { ALARM_TARGET_TIME = 0 };
444
445 // Reset MATCH registers to maximum values to prevent spurious interrupts
446 let r: &Regs = unsafe { &*I::ptr() };
447 prime_match_registers(r);
448 });
449 }
450
451 /// Check if an alarm has expired (call this from your interrupt handler)
452 /// Returns true if the alarm was active and has now expired
453 pub fn check_alarm_expired(&self, alarm: &Alarm) -> bool {
454 if alarm.active.load(Ordering::Acquire) {
455 alarm.active.store(false, Ordering::Release);
456
457 // Execute callback
458 if let Some(callback) = alarm.callback {
459 callback();
460 }
461
462 // Set flag
463 if let Some(flag) = &alarm.flag {
464 flag.store(true, Ordering::Release);
465 }
466
467 true
468 } else {
469 false
470 }
471 }
472}
473
474/// Read current EVTIMER (Gray-coded) and convert to binary ticks.
475#[inline(always)]
476fn now_ticks_read() -> u64 {
477 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
478
479 // Read high then low to minimize incoherent snapshots
480 let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64);
481 let lo = r.evtimerl().read().evtimer_count_value().bits() as u64;
482 // Combine and convert from Gray code to binary
483 let gray = lo | (hi << EVTIMER_HI_SHIFT);
484 gray_to_bin(gray)
485}
486
487// Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance
488pub trait Instance: Gate<MrccPeriphConfig = OsTimerConfig> + PeripheralType {
489 fn ptr() -> *const Regs;
490}
491
492#[cfg(not(feature = "time"))]
493impl Instance for crate::peripherals::OSTIMER0 {
494 #[inline(always)]
495 fn ptr() -> *const Regs {
496 pac::Ostimer0::ptr()
497 }
498}
499
500#[inline(always)]
501fn bin_to_gray(x: u64) -> u64 {
502 x ^ (x >> 1)
503}
504
505#[inline(always)]
506fn gray_to_bin(gray: u64) -> u64 {
507 // More efficient iterative conversion using predefined shifts
508 let mut bin = gray;
509 for &shift in &GRAY_CONVERSION_SHIFTS {
510 bin ^= bin >> shift;
511 }
512 bin
513}
514
515#[cfg(feature = "time")]
516pub mod time_driver {
517 use core::sync::atomic::Ordering;
518 use core::task::Waker;
519
520 use embassy_sync::waitqueue::AtomicWaker;
521 use embassy_time_driver as etd;
522
523 use super::{
524 bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME,
525 EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK,
526 };
527 use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel};
528 use crate::clocks::{enable_and_reset, PoweredClock};
529 use crate::pac;
530
531 #[allow(non_camel_case_types)]
532 pub(crate) struct _OSTIMER0_TIME_DRIVER {
533 _x: (),
534 }
535
536 // #[cfg(feature = "time")]
537 // impl_cc_gate!(_OSTIMER0_TIME_DRIVER, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig);
538
539 impl crate::clocks::Gate for _OSTIMER0_TIME_DRIVER {
540 type MrccPeriphConfig = crate::clocks::periph_helpers::OsTimerConfig;
541
542 #[inline]
543 unsafe fn enable_clock() {
544 let mrcc = unsafe { pac::Mrcc0::steal() };
545 mrcc.mrcc_glb_cc1().modify(|_, w| w.ostimer0().enabled());
546 }
547
548 #[inline]
549 unsafe fn disable_clock() {
550 let mrcc = unsafe { pac::Mrcc0::steal() };
551 mrcc.mrcc_glb_cc1().modify(|_r, w| w.ostimer0().disabled());
552 }
553
554 #[inline]
555 fn is_clock_enabled() -> bool {
556 let mrcc = unsafe { pac::Mrcc0::steal() };
557 mrcc.mrcc_glb_cc1().read().ostimer0().is_enabled()
558 }
559
560 #[inline]
561 unsafe fn release_reset() {
562 let mrcc = unsafe { pac::Mrcc0::steal() };
563 mrcc.mrcc_glb_rst1().modify(|_, w| w.ostimer0().enabled());
564 }
565
566 #[inline]
567 unsafe fn assert_reset() {
568 let mrcc = unsafe { pac::Mrcc0::steal() };
569 mrcc.mrcc_glb_rst1().modify(|_, w| w.ostimer0().disabled());
570 }
571
572 #[inline]
573 fn is_reset_released() -> bool {
574 let mrcc = unsafe { pac::Mrcc0::steal() };
575 mrcc.mrcc_glb_rst1().read().ostimer0().is_enabled()
576 }
577 }
578
579 pub struct Driver;
580 static TIMER_WAKER: AtomicWaker = AtomicWaker::new();
581
582 impl etd::Driver for Driver {
583 fn now(&self) -> u64 {
584 // Use the hardware counter (frequency configured in init)
585 super::now_ticks_read()
586 }
587
588 fn schedule_wake(&self, timestamp: u64, waker: &Waker) {
589 let now = self.now();
590
591 // If timestamp is in the past or very close to now, wake immediately
592 if timestamp <= now {
593 waker.wake_by_ref();
594 return;
595 }
596
597 // Prevent scheduling too far in the future (beyond timer rollover)
598 // This prevents wraparound issues
599 let max_future = now + super::TIMER_MAX_VALUE;
600 if timestamp > max_future {
601 // For very long timeouts, wake immediately to avoid rollover issues
602 waker.wake_by_ref();
603 return;
604 }
605
606 // Register the waker first so any immediate wake below is observed by the executor.
607 TIMER_WAKER.register(waker);
608
609 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
610
611 critical_section::with(|_| {
612 // Mask INTENA and clear flag
613 r.osevent_ctrl()
614 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
615
616 // Read back to ensure W1C took effect on hardware
617 let _ = r.osevent_ctrl().read().ostimer_intrflag().bit();
618
619 if !super::wait_for_match_write_ready(r) {
620 super::prime_match_registers(r);
621
622 if !super::wait_for_match_write_ready(r) {
623 // If we can't safely program MATCH, wake immediately and leave INTENA masked.
624 waker.wake_by_ref();
625 return;
626 }
627 }
628
629 // Program MATCH (Gray-coded). Write low then high, then fence.
630 let gray = bin_to_gray(timestamp);
631 let l = (gray & LOW_32_BIT_MASK) as u32;
632
633 let h = (((gray >> EVTIMER_HI_SHIFT) as u16) & EVTIMER_HI_MASK) as u16;
634
635 r.match_l().write(|w| unsafe { w.match_value().bits(l) });
636 r.match_h().write(|w| unsafe { w.match_value().bits(h) });
637
638 if !super::wait_for_match_write_complete(r) {
639 waker.wake_by_ref();
640 return;
641 }
642
643 let now_after_program = super::now_ticks_read();
644 let intrflag_set = r.osevent_ctrl().read().ostimer_intrflag().bit_is_set();
645 if now_after_program >= timestamp && !intrflag_set {
646 waker.wake_by_ref();
647 return;
648 }
649
650 // Enable peripheral interrupt
651 r.osevent_ctrl().write(|w| w.ostimer_intena().set_bit());
652 });
653 }
654 }
655
656 /// Install the global embassy-time driver and configure NVIC priority for OS_EVENT.
657 ///
658 /// # Parameters
659 /// * `priority` - Interrupt priority for the OSTIMER interrupt
660 /// * `frequency_hz` - Actual OSTIMER clock frequency in Hz (stored for future use)
661 ///
662 /// Note: The frequency parameter is currently accepted for API compatibility.
663 /// The embassy_time_driver macro handles driver registration automatically.
664 pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) {
665 let _clock_freq = unsafe {
666 enable_and_reset::<_OSTIMER0_TIME_DRIVER>(&OsTimerConfig {
667 power: PoweredClock::AlwaysEnabled,
668 source: OstimerClockSel::Clk1M,
669 })
670 .expect("Enabling OsTimer clock should not fail")
671 };
672
673 // Mask/clear at peripheral and set default MATCH
674 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
675 super::prime_match_registers(r);
676
677 // Configure NVIC for timer operation
678 crate::interrupt::OS_EVENT.configure_for_timer(priority);
679
680 // Note: The embassy_time_driver macro automatically registers the driver
681 // The frequency parameter is accepted for future compatibility
682 let _ = frequency_hz; // Suppress unused parameter warning
683 }
684
685 // Export the global time driver expected by embassy-time
686 embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver);
687
688 /// To be called from the OS_EVENT IRQ.
689 pub fn on_interrupt() {
690 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
691
692 // Critical section to prevent races with schedule_wake
693 critical_section::with(|_| {
694 // Check if interrupt is actually pending and handle it atomically
695 if r.osevent_ctrl().read().ostimer_intrflag().bit_is_set() {
696 // Clear flag and disable interrupt atomically
697 r.osevent_ctrl().write(|w| {
698 w.ostimer_intrflag()
699 .clear_bit_by_one() // Write-1-to-clear using safe helper
700 .ostimer_intena()
701 .clear_bit()
702 });
703
704 // Wake the waiting task
705 TIMER_WAKER.wake();
706
707 // Handle alarm callback if active and this interrupt is for the alarm
708 if ALARM_ACTIVE.load(Ordering::SeqCst) {
709 let current_time = now_ticks_read();
710 let target_time = unsafe { ALARM_TARGET_TIME };
711
712 // Check if current time is close to alarm target time (within 1000 ticks for timing variations)
713 if current_time >= target_time && current_time <= target_time + 1000 {
714 ALARM_ACTIVE.store(false, Ordering::SeqCst);
715 unsafe { ALARM_TARGET_TIME = 0 };
716
717 // Execute callback if set
718 unsafe {
719 if let Some(callback) = ALARM_CALLBACK {
720 callback();
721 }
722 }
723
724 // Set flag if provided
725 unsafe {
726 if let Some(flag) = ALARM_FLAG {
727 (*flag).store(true, Ordering::SeqCst);
728 }
729 }
730 }
731 }
732 }
733 });
734 }
735}
736
737#[cfg(feature = "time")]
738use crate::pac::interrupt;
739
740#[cfg(feature = "time")]
741#[allow(non_snake_case)]
742#[interrupt]
743fn OS_EVENT() {
744 time_driver::on_interrupt()
745}