Flash 46 lines
// generic_timer: generic ARM timer driver.
//
// The tick deadline is maintained as an absolute CNTP_CVAL value and advanced
// by a fixed period each interrupt. Re-arming relative to the current counter
// (CNTP_TVAL) would rebase every deadline to handler-entry time, letting
// interrupt latency accumulate into the tick period and drift the cadence on
// real hardware.
const SYS_FREQ u32 = 54_000_000
extern fn setup_CNTP_CTL() void
extern fn set_CNTP_CVAL(cval u64) void
extern fn get_sys_count() u64
extern fn get_sys_freq() u64
var next_deadline u64 = 0
// Seconds since boot, for SYS_UPTIME. Divides the architectural counter
// by its runtime frequency (CNTFRQ_EL0) — deliberately NOT the SYS_FREQ
// tick constant, which is only the re-arm period and can differ from the
// counter rate on another board. A zero CNTFRQ would be a firmware bug;
// guard the divide so a misconfigured board reports 0 instead of faulting.
export fn uptime_seconds() u64 {
const freq = get_sys_freq()
if freq == 0 { return 0 }
return get_sys_count() / freq
}
export fn generic_timer_init() void {
setup_CNTP_CTL()
next_deadline = get_sys_count() +% SYS_FREQ
set_CNTP_CVAL(next_deadline)
}
export fn handle_generic_timer() void {
next_deadline +%= SYS_FREQ
// If the new deadline already lies in the past (the handler ran very
// late), rebase from the current counter so the timer does not refire
// immediately for every missed period in a burst.
const now = get_sys_count()
if (#as(i64, #bitCast(next_deadline -% now)) <= 0) {
next_deadline = now +% SYS_FREQ
}
set_CNTP_CVAL(next_deadline)
}