use bsp_define::{
clocksource::{ClockImpl, SetNextEventFunc, SetStateStoppedFunc},
irqchip::IrqType,
};
use semx_bitops::make_64bit_mask;
use crate::{
irq::{
IrqDevData, IrqHandlerRet,
irqctl::{irq_enable, irq_set_type},
request_percpu_irq,
},
processor::isb,
time::{
clockevents::clockevent_handler,
clocksource::{ClockSource, clocksource_init},
},
};
#[inline(always)]
fn p_read() -> u64 {
let tick: u64;
isb();
unsafe {
core::arch::asm!("mrs {0}, cntpct_el0", out(reg) tick);
}
tick
}
#[inline(always)]
fn v_read() -> u64 {
let tick: u64;
isb();
unsafe {
core::arch::asm!("mrs {0}, cntvct_el0", out(reg) tick);
}
tick
}
#[derive(PartialEq, PartialOrd)]
pub enum ArmClockType {
Phys,
Virt,
}
pub struct ArmClock {
clktype: ArmClockType,
irqtype: IrqType,
hwirq: usize,
}
impl ArmClock {
pub const fn create(clktype: ArmClockType, irqtype: IrqType, hwirq: usize) -> Self {
Self { clktype, irqtype, hwirq }
}
fn timer_setup(&self) {
let timer_shutdown = self.get_set_state_stopped();
timer_shutdown();
let mut cntkctl: u32;
unsafe {
core::arch::asm!(
"mrs {0:x}, cntkctl_el1",
out(reg) cntkctl
);
}
cntkctl &= !(ARCH_TIMER_USR_PT_ACCESS_EN
| ARCH_TIMER_USR_VT_ACCESS_EN
| ARCH_TIMER_USR_VCT_ACCESS_EN
| ARCH_TIMER_VIRT_EVT_EN
| ARCH_TIMER_USR_PCT_ACCESS_EN);
cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN;
unsafe {
core::arch::asm!(
"msr cntkctl_el1, {0:x}",
in(reg) cntkctl
);
}
isb();
}
}
const ARCH_TIMER_USR_PCT_ACCESS_EN: u32 = 1 << 0;
const ARCH_TIMER_USR_VCT_ACCESS_EN: u32 = 1 << 1;
const ARCH_TIMER_VIRT_EVT_EN: u32 = 1 << 2;
const ARCH_TIMER_USR_VT_ACCESS_EN: u32 = 1 << 8;
const ARCH_TIMER_USR_PT_ACCESS_EN: u32 = 1 << 9;
impl ClockImpl for ArmClock {
fn init(&self) {
let rate = self.rate();
let mask = make_64bit_mask(0, 56);
let name;
let read: fn() -> u64;
if self.clktype == ArmClockType::Phys {
name = "arm_arch_timer(phys)";
read = p_read;
} else {
name = "arm_arch_timer(virt)";
read = v_read;
}
let clocksource = ClockSource::create(read, mask, name);
clocksource_init(clocksource, rate);
let f = if self.clktype == ArmClockType::Phys {
arch_timer_handler_phys
} else {
arch_timer_handler_virt
};
request_percpu_irq(self.hwirq as u32, self.irqtype, f, None);
self.timer_setup();
}
fn init_cpu(&self) {
self.timer_setup();
irq_set_type(self.hwirq as u32, self.irqtype).unwrap();
irq_enable(self.hwirq as u32);
}
fn get_set_next_event(&self) -> SetNextEventFunc {
if self.clktype == ArmClockType::Phys {
arch_timer_set_next_event_phys
} else {
arch_timer_set_next_event_virt
}
}
fn get_set_state_stopped(&self) -> SetStateStoppedFunc {
if self.clktype == ArmClockType::Phys {
arch_timer_shutdown_phys
} else {
arch_timer_shutdown_virt
}
}
#[inline(always)]
fn rate(&self) -> usize {
let rate: usize;
unsafe {
core::arch::asm!(
"mrs {0}, cntfrq_el0",
out(reg) rate
);
}
rate
}
#[inline(always)]
fn min_delta(&self) -> u64 {
0xf
}
#[inline(always)]
fn max_delta(&self) -> u64 {
0x7fffffff
}
}
#[derive(Clone, Copy)]
enum ArchTimerReg {
Ctrl,
Tval,
}
#[inline(always)]
fn arch_timer_reg_read(access: TimerAccess, reg: ArchTimerReg) -> u32 {
unsafe {
let val;
match access {
TimerAccess::PhysAccess => match reg {
ArchTimerReg::Ctrl => {
core::arch::asm!(
"mrs {0:x}, cntp_ctl_el0",
out(reg) val
);
val
},
ArchTimerReg::Tval => {
core::arch::asm!(
"mrs {0:x}, cntp_tval_el0",
out(reg) val
);
val
},
},
TimerAccess::VirtAccess => match reg {
ArchTimerReg::Ctrl => {
core::arch::asm!(
"mrs {0:x}, cntv_ctl_el0",
out(reg) val
);
val
},
ArchTimerReg::Tval => {
core::arch::asm!(
"mrs {0:x}, cntv_tval_el0",
out(reg) val
);
val
},
},
}
}
}
#[inline(always)]
fn arch_timer_reg_write(access: TimerAccess, reg: ArchTimerReg, val: u32) {
unsafe {
match access {
TimerAccess::PhysAccess => match reg {
ArchTimerReg::Ctrl => {
core::arch::asm!(
"msr cntp_ctl_el0, {0:x}",
in(reg) val
);
},
ArchTimerReg::Tval => {
core::arch::asm!(
"msr cntp_tval_el0, {0:x}",
in(reg) val
);
},
},
TimerAccess::VirtAccess => match reg {
ArchTimerReg::Ctrl => {
core::arch::asm!(
"msr cntv_ctl_el0, {0:x}",
in(reg) val
);
},
ArchTimerReg::Tval => {
core::arch::asm!(
"msr cntv_tval_el0, {0:x}",
in(reg) val
);
},
},
}
isb();
}
}
#[derive(Clone, Copy)]
enum TimerAccess {
PhysAccess,
VirtAccess,
}
const ARCH_TIMER_CTRL_ENABLE: u32 = 1 << 0;
const ARCH_TIMER_CTRL_IT_MASK: u32 = 1 << 1;
const ARCH_TIMER_CTRL_IT_STAT: u32 = 1 << 2;
#[inline(always)]
fn timer_handler(access: TimerAccess) -> IrqHandlerRet {
let mut ctrl = arch_timer_reg_read(access, ArchTimerReg::Ctrl);
if ctrl & ARCH_TIMER_CTRL_IT_STAT != 0 {
ctrl |= ARCH_TIMER_CTRL_IT_MASK;
arch_timer_reg_write(access, ArchTimerReg::Ctrl, ctrl);
clockevent_handler();
}
IrqHandlerRet::IrqHandled
}
#[allow(clippy::ref_option)]
fn arch_timer_handler_virt(_: u32, _: &Option<IrqDevData>) -> IrqHandlerRet {
timer_handler(TimerAccess::VirtAccess)
}
#[allow(clippy::ref_option)]
fn arch_timer_handler_phys(_: u32, _: &Option<IrqDevData>) -> IrqHandlerRet {
timer_handler(TimerAccess::PhysAccess)
}
#[inline(always)]
fn set_next_event(access: TimerAccess, evt: u64) {
let mut ctrl = arch_timer_reg_read(access, ArchTimerReg::Ctrl);
ctrl |= ARCH_TIMER_CTRL_ENABLE;
ctrl &= !ARCH_TIMER_CTRL_IT_MASK;
arch_timer_reg_write(access, ArchTimerReg::Tval, evt as u32);
arch_timer_reg_write(access, ArchTimerReg::Ctrl, ctrl);
}
fn arch_timer_set_next_event_phys(evt: u64) -> bool {
set_next_event(TimerAccess::PhysAccess, evt);
true
}
fn arch_timer_set_next_event_virt(evt: u64) -> bool {
set_next_event(TimerAccess::VirtAccess, evt);
true
}
#[inline(always)]
fn timer_shutdown(access: TimerAccess) {
let mut ctrl = arch_timer_reg_read(access, ArchTimerReg::Ctrl);
ctrl &= !ARCH_TIMER_CTRL_ENABLE;
arch_timer_reg_write(access, ArchTimerReg::Ctrl, ctrl);
}
fn arch_timer_shutdown_phys() {
timer_shutdown(TimerAccess::PhysAccess);
}
fn arch_timer_shutdown_virt() {
timer_shutdown(TimerAccess::VirtAccess);
}