97 lines
2.4 KiB
Rust

//! AArch64 Generic Timer
use core::time::Duration;
use aarch64_cpu::registers::{CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0};
use abi::error::Error;
use alloc::boxed::Box;
use device_api::{
interrupt::{InterruptHandler, Irq},
timer::MonotonicTimestampProviderDevice,
Device,
};
use device_tree::device_tree_driver;
use kernel_arch::task::Scheduler;
use libk::{
arch::Cpu,
device::{external_interrupt_controller, register_monotonic_timestamp_provider},
task::runtime,
};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
/// ARM Generic Timer driver
pub struct ArmTimer {
irq: Irq,
}
/// ARM timer tick interval (in some time units?)
pub const TICK_INTERVAL: u64 = 250000;
impl InterruptHandler for ArmTimer {
fn handle_irq(&self, _vector: Option<usize>) -> bool {
CNTP_TVAL_EL0.set(TICK_INTERVAL);
let now = self.monotonic_timestamp().unwrap();
runtime::tick(now);
unsafe {
Cpu::local().scheduler().yield_cpu();
}
true
}
}
impl MonotonicTimestampProviderDevice for ArmTimer {
fn monotonic_timestamp(&self) -> Result<Duration, Error> {
let count = CNTPCT_EL0.get() * 1_000_000;
let freq = CNTFRQ_EL0.get();
Ok(Duration::from_nanos((count / freq) * 1_000))
}
}
impl Device for ArmTimer {
fn display_name(&self) -> &'static str {
"ARM Generic Timer"
}
unsafe fn init(&'static self) -> Result<(), Error> {
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
register_monotonic_timestamp_provider(self);
Ok(())
}
unsafe fn init_irq(&'static self) -> Result<(), Error> {
let intc = external_interrupt_controller();
intc.register_irq(self.irq, Default::default(), self)?;
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::IMASK::CLEAR);
CNTP_TVAL_EL0.set(TICK_INTERVAL);
intc.enable_irq(self.irq)?;
Ok(())
}
}
impl ArmTimer {
/// Constructs an instance of ARM generic timer.
///
/// # Safety
///
/// The caller must ensure the function has not been called before.
pub const unsafe fn new(irq: Irq) -> Self {
Self { irq }
}
}
device_tree_driver! {
compatible: ["arm,armv8-timer"],
probe(_dt) => {
// TODO actually get info from the dt
Some(Box::new(unsafe { ArmTimer::new(Irq::Private(14)) }))
}
}