146 lines
4.0 KiB
Rust
146 lines
4.0 KiB
Rust
use alloc::boxed::Box;
|
|
|
|
use super::ic_for_chip;
|
|
use crate::{
|
|
arch::device::cpu::registers::control_regs,
|
|
context::{self, timeout},
|
|
dtb::{
|
|
get_interrupt,
|
|
irqchip::{register_irq, InterruptHandler, IRQ_CHIP},
|
|
},
|
|
scheme::irq::irq_trigger,
|
|
sync::CleanLockToken,
|
|
time,
|
|
};
|
|
use fdt::Fdt;
|
|
|
|
bitflags! {
|
|
struct TimerCtrlFlags: u32 {
|
|
const ENABLE = 1 << 0;
|
|
const IMASK = 1 << 1;
|
|
const ISTATUS = 1 << 2;
|
|
}
|
|
}
|
|
|
|
pub unsafe fn init(fdt: &Fdt) {
|
|
unsafe {
|
|
let mut timer = GenericTimer::new();
|
|
timer.init();
|
|
if let Some(node) = fdt.find_compatible(&["arm,armv7-timer"]) {
|
|
let irq = get_interrupt(fdt, &node, 1).unwrap();
|
|
debug!("irq = {:?}", irq);
|
|
if let Some(ic_idx) = ic_for_chip(&fdt, &node) {
|
|
//PHYS_NONSECURE_PPI only
|
|
let virq = IRQ_CHIP.irq_chip_list.chips[ic_idx]
|
|
.ic
|
|
.irq_xlate(irq)
|
|
.unwrap();
|
|
info!("generic_timer virq = {}", virq);
|
|
register_irq(virq as u32, Box::new(timer));
|
|
IRQ_CHIP.irq_enable(virq as u32);
|
|
} else {
|
|
error!("Failed to find irq parent for generic timer");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct GenericTimer {
|
|
pub use_virtual_timer: bool,
|
|
pub clk_freq: u32,
|
|
pub reload_count: u32,
|
|
}
|
|
|
|
impl GenericTimer {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
use_virtual_timer: false,
|
|
clk_freq: 0,
|
|
reload_count: 0,
|
|
}
|
|
}
|
|
pub fn init(&mut self) {
|
|
self.use_virtual_timer = unsafe { !control_regs::vhe_present() };
|
|
debug!(
|
|
"generic_timer use_virtual_timer = {:?}",
|
|
self.use_virtual_timer
|
|
);
|
|
let clk_freq = unsafe { control_regs::cntfrq_el0() };
|
|
self.clk_freq = clk_freq;
|
|
self.reload_count = clk_freq / 100;
|
|
self.reload_count();
|
|
}
|
|
|
|
fn read_tmr_ctrl(&self) -> TimerCtrlFlags {
|
|
TimerCtrlFlags::from_bits_truncate(if self.use_virtual_timer {
|
|
unsafe { control_regs::vtmr_ctrl() }
|
|
} else {
|
|
unsafe { control_regs::ptmr_ctrl() }
|
|
})
|
|
}
|
|
|
|
fn write_tmr_ctrl(&self, ctrl: TimerCtrlFlags) {
|
|
if self.use_virtual_timer {
|
|
unsafe { control_regs::vtmr_ctrl_write(ctrl.bits()) };
|
|
} else {
|
|
unsafe { control_regs::ptmr_ctrl_write(ctrl.bits()) };
|
|
}
|
|
}
|
|
|
|
#[allow(unused)]
|
|
fn disable(&self) {
|
|
let mut ctrl = self.read_tmr_ctrl();
|
|
ctrl.remove(TimerCtrlFlags::ENABLE);
|
|
self.write_tmr_ctrl(ctrl);
|
|
}
|
|
|
|
#[allow(unused)]
|
|
pub fn set_irq(&mut self) {
|
|
let mut ctrl = self.read_tmr_ctrl();
|
|
ctrl.remove(TimerCtrlFlags::IMASK);
|
|
self.write_tmr_ctrl(ctrl);
|
|
}
|
|
|
|
pub fn clear_irq(&mut self) {
|
|
let mut ctrl = self.read_tmr_ctrl();
|
|
|
|
if ctrl.contains(TimerCtrlFlags::ISTATUS) {
|
|
ctrl.insert(TimerCtrlFlags::IMASK);
|
|
self.write_tmr_ctrl(ctrl);
|
|
}
|
|
}
|
|
|
|
pub fn reload_count(&mut self) {
|
|
if self.use_virtual_timer {
|
|
unsafe { control_regs::vtmr_tval_write(self.reload_count) };
|
|
} else {
|
|
unsafe { control_regs::ptmr_tval_write(self.reload_count) };
|
|
}
|
|
let mut ctrl = self.read_tmr_ctrl();
|
|
ctrl.insert(TimerCtrlFlags::ENABLE);
|
|
ctrl.remove(TimerCtrlFlags::IMASK);
|
|
self.write_tmr_ctrl(ctrl);
|
|
}
|
|
}
|
|
|
|
impl InterruptHandler for GenericTimer {
|
|
fn irq_handler(&mut self, irq: u32, token: &mut CleanLockToken) {
|
|
self.clear_irq();
|
|
{
|
|
*time::OFFSET.write(token.token()) += self.clk_freq as u128;
|
|
}
|
|
|
|
timeout::trigger(token);
|
|
context::switch::tick(token);
|
|
|
|
unsafe {
|
|
// FIXME add_irq accepts a u8 as irq number
|
|
// PercpuBlock::current().stats.add_irq(irq);
|
|
|
|
irq_trigger(irq.try_into().unwrap(), token);
|
|
IRQ_CHIP.irq_eoi(irq);
|
|
}
|
|
self.reload_count();
|
|
}
|
|
}
|