Skip to content

Commit

Permalink
implement timer 0 rollover
Browse files Browse the repository at this point in the history
This implements an ancient TODO about how we ought to handle 32-bit
timer 0 rollovers. I also abstracted a bunch more of the timer 0
configuration behind the timer module, so the rest of the kernel doesn't
have to know about it.
  • Loading branch information
hawkw committed Dec 27, 2024
1 parent d7df560 commit d12c096
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 10 deletions.
63 changes: 54 additions & 9 deletions platforms/allwinner-d1/d1-core/src/timer.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::plic::{Plic, Priority};
use core::sync::atomic::{AtomicU32, Ordering};
pub use d1_pac::timer::tmr_ctrl::{
TMR_CLK_PRES_A as TimerPrescaler, TMR_CLK_SRC_A as TimerSource, TMR_MODE_A as TimerMode,
};
Expand All @@ -9,19 +11,27 @@ pub struct Timers {
pub timer1: Timer1,
}

static TIMER0_ROLLOVERS: AtomicU32 = AtomicU32::new(0);

impl Timer0 {
pub fn into_maitake_clock(mut self) -> Clock {
const INITIAL_VALUE: u32 = u32::MAX;

pub fn into_maitake_clock(mut self, plic: &Plic) -> Clock {
use d1_pac::Interrupt;
self.set_prescaler(TimerPrescaler::P8); // 24M / 8: 3.00M ticks/s
self.set_mode(TimerMode::PERIODIC);

// Clear any previous interrupt flag.
let _ = self.get_and_clear_interrupt();

// Start the timer counting down from 0xffff_ffff_ffff_ffff.
// Register the interrupt handler for when the timer rolls over.
unsafe {
plic.register(Interrupt::TIMER0, Self::maitake_timer_interrupt);
plic.activate(Interrupt::TIMER0, Priority::P1).unwrap();
}

// TODO(eliza): if it's zero, handle that lol --- when the IRQ fires
// we need to increment some higher half counter and then reset the
// timer to u32::MAX?
self.start_counter(u32::MAX);
// Start the timer counting down from u32::MAX;.
self.reset();

Clock::new(core::time::Duration::from_nanos(333), || {
let timer0 = unsafe {
Expand All @@ -30,13 +40,48 @@ impl Timer0 {
Self::steal()
};
// Since timer 0 is counting *down*, we have to subtract its current
// value from the intial value to get an increasing timestamp for
// Maitake.
(u32::MAX - timer0.current_value()) as u64
// value from the intial value to get an i[ncreasing timestamp for
// Maitake. As timer 0 is a 32-bit timer, this forms the lower half
// of our 64-bit timestamp.
let lo = (Self::INITIAL_VALUE - timer0.current_value()) as u64;
// The higher half of the 64-bit timestamp is the current value of
// the 32-bit timer rollover counter --- i.e. the number of times
// that timer 0 has counted down to 0.
let hi = TIMER0_ROLLOVERS.load(Ordering::Relaxed) as u64;
// Combine the two halves to form the full 64-bit timestamp.
(hi << 32) | lo
})
.named("CLOCK_D1_TIMER0")
}

fn reset(&mut self) {
self.start_counter(Self::INITIAL_VALUE);
// N.B. that we probably don't *have* to reset the IRQ_EN bit every time
// it fires, but let's make sure it's enabled just in case...
self.set_interrupt_en(true);
}

/// Handle a TIMER0 interrupt when TIMER0 is used as the maitake timer.
fn maitake_timer_interrupt() {
let mut timer0 = unsafe {
// Safety: we need to do this to be an ISR lol
Self::steal()
};

// Clear the interrupt flag
let _ = timer0.get_and_clear_interrupt();

// Increment the rollover counter
TIMER0_ROLLOVERS.fetch_add(1, Ordering::Relaxed);

// Wait for the interrupt to clear to avoid repeat interrupts
let timers = unsafe { &*TIMER::PTR };
while timers.tmr_irq_sta.read().tmr0_irq_pend().bit_is_set() {}

// RESET THE CLOCK!
timer0.reset();
}

unsafe fn steal() -> Self {
Self { _x: () }
}
Expand Down
2 changes: 1 addition & 1 deletion platforms/allwinner-d1/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ impl D1 {
kernel_settings: KernelSettings,
service_settings: KernelServiceSettings,
) -> Self {
let timer0_clock = timers.timer0.into_maitake_clock();
let timer0_clock = timers.timer0.into_maitake_clock(&plic);
let k = unsafe {
Box::into_raw(
Kernel::new(kernel_settings, timer0_clock).expect("cannot initialize kernel"),
Expand Down

0 comments on commit d12c096

Please sign in to comment.