ajhahn.de
← FlashOS
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)
}