use core::{
arch::asm,
sync::atomic::{AtomicU64, Ordering},
};
use spin::Once;
use crate::{
arch::{self, boot::DEVICE_TREE, cpu::extension::IsaExtensions, trap::TrapFrame},
irq::IrqLine,
timer::TIMER_FREQ,
};
pub(super) static TIMER_IRQ: Once<IrqLine> = Once::new();
static TIMEBASE_FREQ: AtomicU64 = AtomicU64::new(0);
static TIMER_INTERVAL: AtomicU64 = AtomicU64::new(0);
pub(super) unsafe fn init_on_bsp() {
TIMEBASE_FREQ.store(
DEVICE_TREE
.get()
.unwrap()
.cpus()
.next()
.unwrap()
.timebase_frequency() as u64,
Ordering::Relaxed,
);
TIMER_INTERVAL.store(
TIMEBASE_FREQ.load(Ordering::Relaxed) / TIMER_FREQ,
Ordering::Relaxed,
);
if is_sstc_enabled() {
unsafe {
SET_NEXT_TIMER_FN = set_next_timer_sstc;
}
}
TIMER_IRQ.call_once(|| {
let mut timer_irq = IrqLine::alloc().unwrap();
timer_irq.on_active(timer_callback);
timer_irq
});
unsafe { init_current_hart() };
}
pub(super) unsafe fn init_on_ap() {
unsafe { init_current_hart() };
}
unsafe fn init_current_hart() {
set_next_timer();
unsafe {
riscv::register::sie::set_stimer();
}
}
fn timer_callback(trapframe: &TrapFrame) {
crate::timer::call_timer_callback_functions(trapframe);
set_next_timer();
}
fn set_next_timer() {
unsafe {
SET_NEXT_TIMER_FN();
}
}
static mut SET_NEXT_TIMER_FN: fn() = set_next_timer_sbi;
fn set_next_timer_sbi() {
sbi_rt::set_timer(get_next_when());
}
fn set_next_timer_sstc() {
unsafe {
asm!("csrrw {}, stimecmp, {}", out(reg) _, in(reg) get_next_when());
}
}
fn is_sstc_enabled() -> bool {
arch::cpu::extension::has_extensions(IsaExtensions::SSTC)
}
fn get_next_when() -> u64 {
let current = riscv::register::time::read64();
let interval = TIMER_INTERVAL.load(Ordering::Relaxed);
current + interval
}
pub(crate) fn get_timebase_freq() -> u64 {
TIMEBASE_FREQ.load(Ordering::Relaxed)
}