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(); } }