126 lines
3.0 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, timer::MonotonicTimestampProviderDevice, Device};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use crate::{
arch::{aarch64::IrqNumber, Architecture, ARCHITECTURE},
device_tree_driver,
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 InterruptHandler for ArmTimer {
fn handle_irq(&self) -> bool {
CNTP_TVAL_EL0.set(TICK_INTERVAL);
let now = self.monotonic_timestamp().unwrap();
wait::tick(now);
tasklet::tick(now);
unsafe {
Cpu::local().queue().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);
ARCHITECTURE.register_monotonic_timer(self)?;
Ok(())
}
unsafe fn init_irq(&'static self) -> Result<(), Error> {
let intc = ARCHITECTURE.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 TimestampSource for ArmTimer {
// fn timestamp(&self) -> Result<Duration, Error> {
// }
// }
// 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)?;
// 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 }
}
}
device_tree_driver! {
compatible: ["arm,armv8-timer"],
probe(_dt) => {
// TODO actually get info from the dt
Some(Box::new(unsafe { ArmTimer::new(IrqNumber::Private(14)) }))
}
}