Skip to main content

axplat_aarch64_peripherals/
generic_timer.rs

1//! ARM Generic Timer.
2
3use aarch64_cpu::registers::{CNTFRQ_EL0, CNTP_TVAL_EL0, CNTPCT_EL0};
4use aarch64_cpu::registers::{Readable, Writeable};
5use int_ratio::Ratio;
6
7static mut CNTPCT_TO_NANOS_RATIO: Ratio = Ratio::zero();
8static mut NANOS_TO_CNTPCT_RATIO: Ratio = Ratio::zero();
9
10/// Returns the current clock time in hardware ticks.
11#[inline]
12pub fn current_ticks() -> u64 {
13    CNTPCT_EL0.get()
14}
15
16/// Converts hardware ticks to nanoseconds.
17#[inline]
18pub fn ticks_to_nanos(ticks: u64) -> u64 {
19    unsafe { CNTPCT_TO_NANOS_RATIO.mul_trunc(ticks) }
20}
21
22/// Converts nanoseconds to hardware ticks.
23#[inline]
24pub fn nanos_to_ticks(nanos: u64) -> u64 {
25    unsafe { NANOS_TO_CNTPCT_RATIO.mul_trunc(nanos) }
26}
27
28/// Set a one-shot timer.
29///
30/// A timer interrupt will be triggered at the specified monotonic time deadline (in nanoseconds).
31pub fn set_oneshot_timer(deadline_ns: u64) {
32    let cnptct = CNTPCT_EL0.get();
33    let cnptct_deadline = nanos_to_ticks(deadline_ns);
34    if cnptct < cnptct_deadline {
35        let interval = cnptct_deadline - cnptct;
36        debug_assert!(interval <= u32::MAX as u64);
37        CNTP_TVAL_EL0.set(interval);
38    } else {
39        CNTP_TVAL_EL0.set(0);
40    }
41}
42
43/// Early stage initialization: stores the timer frequency.
44pub fn init_early() {
45    let freq = CNTFRQ_EL0.get();
46    unsafe {
47        CNTPCT_TO_NANOS_RATIO = Ratio::new(axplat::time::NANOS_PER_SEC as u32, freq as u32);
48        NANOS_TO_CNTPCT_RATIO = CNTPCT_TO_NANOS_RATIO.inverse();
49    }
50}
51
52/// Enable timer interrupts.
53///
54/// It should be called on all CPUs, as the timer interrupt is a PPI (Private
55/// Peripheral Interrupt).
56#[cfg(feature = "irq")]
57pub fn enable_irqs(timer_irq_num: usize) {
58    use aarch64_cpu::registers::CNTP_CTL_EL0;
59    CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
60    CNTP_TVAL_EL0.set(0);
61    axplat::irq::set_enable(timer_irq_num, true);
62}
63
64/// Default implementation of [`axplat::time::TimeIf`] using the generic
65/// timer.
66#[macro_export]
67macro_rules! time_if_impl {
68    ($name:ident) => {
69        struct $name;
70
71        #[impl_plat_interface]
72        impl axplat::time::TimeIf for $name {
73            /// Returns the current clock time in hardware ticks.
74            fn current_ticks() -> u64 {
75                $crate::generic_timer::current_ticks()
76            }
77
78            /// Converts hardware ticks to nanoseconds.
79            fn ticks_to_nanos(ticks: u64) -> u64 {
80                $crate::generic_timer::ticks_to_nanos(ticks)
81            }
82
83            /// Converts nanoseconds to hardware ticks.
84            fn nanos_to_ticks(nanos: u64) -> u64 {
85                $crate::generic_timer::nanos_to_ticks(nanos)
86            }
87
88            /// Return epoch offset in nanoseconds (wall time offset to monotonic
89            /// clock start).
90            fn epochoffset_nanos() -> u64 {
91                $crate::pl031::epochoffset_nanos()
92            }
93
94            /// Set a one-shot timer.
95            ///
96            /// A timer interrupt will be triggered at the specified monotonic time
97            /// deadline (in nanoseconds).
98            #[cfg(feature = "irq")]
99            fn set_oneshot_timer(deadline_ns: u64) {
100                $crate::generic_timer::set_oneshot_timer(deadline_ns)
101            }
102        }
103    };
104}