use aarch64_cpu::registers::*;
use axplat::time::TimeIf;
use lazyinit::LazyInit;
use rdrive::{IrqConfig, PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo};
static TIMER_IRQ_CONFIG: LazyInit<IrqConfig> = LazyInit::new();
struct TimeIfImpl;
#[impl_plat_interface]
impl TimeIf for TimeIfImpl {
fn current_ticks() -> u64 {
CNTPCT_EL0.get()
}
fn ticks_to_nanos(ticks: u64) -> u64 {
let freq = CNTFRQ_EL0.get();
(ticks * axplat::time::NANOS_PER_SEC) / freq
}
fn nanos_to_ticks(nanos: u64) -> u64 {
let freq = CNTFRQ_EL0.get();
(nanos * freq) / axplat::time::NANOS_PER_SEC
}
fn epochoffset_nanos() -> u64 {
0
}
#[cfg(feature = "irq")]
fn irq_num() -> usize {
TIMER_IRQ_CONFIG.irq.into()
}
#[cfg(feature = "irq")]
fn set_oneshot_timer(deadline_ns: u64) {
let cnptct = CNTPCT_EL0.get();
let cnptct_deadline = Self::nanos_to_ticks(deadline_ns);
if cnptct < cnptct_deadline {
let interval = cnptct_deadline - cnptct;
debug_assert!(interval <= u32::MAX as u64);
set_tval(interval);
} else {
set_tval(0);
}
}
}
fn set_tval(tval: u64) {
#[cfg(feature = "hv")]
unsafe {
core::arch::asm!("msr CNTHP_TVAL_EL2, {0:x}", in(reg) tval);
}
#[cfg(not(feature = "hv"))]
CNTP_TVAL_EL0.set(tval);
}
#[cfg(feature = "hv")]
pub fn enable() {
CNTHP_CTL_EL2.write(CNTHP_CTL_EL2::ENABLE::SET);
set_tval(0);
}
#[cfg(not(feature = "hv"))]
pub fn enable() {
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
set_tval(0);
}
#[cfg(feature = "irq")]
pub fn enable_irqs() {
let irq_raw: usize = TIMER_IRQ_CONFIG.irq.into();
crate::irq::set_enable(irq_raw, true);
}
module_driver!(
name: "ARMv8 Timer",
level: ProbeLevel::PreKernel,
priority: ProbePriority::DEFAULT,
probe_kinds: &[
ProbeKind::Fdt {
compatibles: &["arm,armv8-timer"],
on_probe: probe
}
],
);
fn probe(_fdt: FdtInfo<'_>, _dev: PlatformDevice) -> Result<(), OnProbeError> {
#[cfg(not(feature = "irq"))]
let irq = IrqConfig {
irq: 0.into(),
trigger: rdif_intc::Trigger::EdgeBoth,
is_private: true,
};
#[cfg(feature = "irq")]
let irq = {
#[cfg(not(feature = "hv"))]
let irq_idx = 1;
#[cfg(feature = "hv")]
let irq_idx = 3;
crate::irq::parse_fdt_irqs(&_fdt.interrupts()[irq_idx])
};
TIMER_IRQ_CONFIG.call_once(|| irq);
Ok(())
}