126 lines
3.0 KiB
Rust
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)) }))
|
|
}
|
|
}
|