97 lines
2.4 KiB
Rust
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)) }))
|
|
}
|
|
}
|