use core::arch::asm;
const LOCAL_TIMER_IRQCTL_BASE: usize = 0x4000_0040;
const CNTPNSIRQ: u32 = 1 << 1;
const CTL_ENABLE: u32 = 1 << 0;
const CTL_ISTATUS: u32 = 1 << 2;
pub struct GenericTimer {
_private: (),
}
impl GenericTimer {
pub fn new() -> Self {
Self { _private: () }
}
pub fn frequency(&self) -> u32 {
read_cntfrq()
}
pub fn now(&self) -> u64 {
read_cntpct()
}
pub fn delay_us(&self, us: u32) {
self.delay_ticks((us as u64 * self.frequency() as u64) / 1_000_000);
}
pub fn delay_ms(&self, ms: u32) {
self.delay_ticks((ms as u64 * self.frequency() as u64) / 1_000);
}
pub fn arm_after_us(&self, us: u32) {
let ticks = (us as u64 * self.frequency() as u64) / 1_000_000;
write_cntp_tval(ticks as u32);
write_cntp_ctl(CTL_ENABLE);
}
pub fn set_deadline(&self, deadline: u64) {
write_cntp_cval(deadline);
write_cntp_ctl(CTL_ENABLE);
}
pub fn is_pending(&self) -> bool {
read_cntp_ctl() & CTL_ISTATUS != 0
}
pub fn disable(&self) {
write_cntp_ctl(0);
}
pub fn route_irq(&self) {
modify_local_timer_ctl(|v| v | CNTPNSIRQ);
}
pub fn mask_irq(&self) {
modify_local_timer_ctl(|v| v & !CNTPNSIRQ);
}
fn delay_ticks(&self, ticks: u64) {
let target = self.now() + ticks;
while self.now() < target {}
}
}
impl Default for GenericTimer {
fn default() -> Self {
Self::new()
}
}
impl embedded_hal::delay::DelayNs for GenericTimer {
fn delay_ns(&mut self, ns: u32) {
self.delay_ticks((ns as u64 * self.frequency() as u64).div_ceil(1_000_000_000));
}
fn delay_us(&mut self, us: u32) {
GenericTimer::delay_us(self, us);
}
fn delay_ms(&mut self, ms: u32) {
GenericTimer::delay_ms(self, ms);
}
}
fn modify_local_timer_ctl(f: impl FnOnce(u32) -> u32) {
let reg = (LOCAL_TIMER_IRQCTL_BASE + 4 * crate::cpu::core_id()) as *mut u32;
unsafe {
let cur = core::ptr::read_volatile(reg);
core::ptr::write_volatile(reg, f(cur));
}
}
#[cfg(target_arch = "arm")]
#[inline(always)]
fn read_cntfrq() -> u32 {
let freq;
unsafe { asm!("mrc p15, 0, {}, c14, c0, 0", out(reg) freq) };
freq
}
#[cfg(target_arch = "arm")]
#[inline(always)]
fn read_cntpct() -> u64 {
let (lo, hi): (u32, u32);
unsafe { asm!("isb", "mrrc p15, 0, {0}, {1}, c14", out(reg) lo, out(reg) hi) };
((hi as u64) << 32) | lo as u64
}
#[cfg(target_arch = "arm")]
#[inline(always)]
fn write_cntp_tval(v: u32) {
unsafe { asm!("mcr p15, 0, {}, c14, c2, 0", in(reg) v) };
}
#[cfg(target_arch = "arm")]
#[inline(always)]
fn write_cntp_cval(v: u64) {
let (lo, hi) = (v as u32, (v >> 32) as u32);
unsafe { asm!("mcrr p15, 2, {0}, {1}, c14", in(reg) lo, in(reg) hi) };
}
#[cfg(target_arch = "arm")]
#[inline(always)]
fn write_cntp_ctl(v: u32) {
unsafe { asm!("mcr p15, 0, {}, c14, c2, 1", in(reg) v) };
}
#[cfg(target_arch = "arm")]
#[inline(always)]
fn read_cntp_ctl() -> u32 {
let ctl;
unsafe { asm!("mrc p15, 0, {}, c14, c2, 1", out(reg) ctl) };
ctl
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
fn read_cntfrq() -> u32 {
let freq: u64;
unsafe { asm!("mrs {}, cntfrq_el0", out(reg) freq) };
freq as u32
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
fn read_cntpct() -> u64 {
let count: u64;
unsafe { asm!("isb", "mrs {}, cntpct_el0", out(reg) count) };
count
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
fn write_cntp_tval(v: u32) {
let v = v as u64;
unsafe { asm!("msr cntp_tval_el0, {}", in(reg) v) };
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
fn write_cntp_cval(v: u64) {
unsafe { asm!("msr cntp_cval_el0, {}", in(reg) v) };
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
fn write_cntp_ctl(v: u32) {
let v = v as u64;
unsafe { asm!("msr cntp_ctl_el0, {}", in(reg) v) };
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
fn read_cntp_ctl() -> u32 {
let ctl: u64;
unsafe { asm!("mrs {}, cntp_ctl_el0", out(reg) ctl) };
ctl as u32
}