90 lines
2.1 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 tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use crate::{
arch::{aarch64::gic::IrqNumber, PLATFORM},
device::{
interrupt::InterruptSource, platform::Platform, timer::TimestampSource, Device,
InitializableDevice,
},
proc::wait,
task::tasklet,
};
use super::cpu::Cpu;
/// ARM Generic Timer driver
pub struct ArmTimer {
irq: IrqNumber,
}
/// ARM timer tick interval (in some time units?)
pub const TICK_INTERVAL: u64 = 1000000;
impl Device for ArmTimer {
fn name(&self) -> &'static str {
"ARM Generic Timer"
}
}
impl InitializableDevice for ArmTimer {
type Options = ();
unsafe fn init(&self, _opts: ()) -> Result<(), Error> {
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
Ok(())
}
}
impl TimestampSource for ArmTimer {
fn 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 InterruptSource for ArmTimer {
fn handle_irq(&self) -> Result<bool, Error> {
CNTP_TVAL_EL0.set(TICK_INTERVAL);
let t = self.timestamp()?;
wait::tick(t);
tasklet::tick(t);
unsafe {
Cpu::local().queue().yield_cpu();
}
Ok(true)
}
unsafe fn init_irq(&'static self) -> Result<(), Error> {
let intc = PLATFORM.interrupt_controller();
intc.register_handler(self.irq, 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: IrqNumber) -> Self {
Self { irq }
}
}