From d12c0961402d09b08eef483d55ffa01eab0c2f88 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 27 Dec 2024 11:09:58 -0800 Subject: [PATCH] implement timer 0 rollover 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. --- platforms/allwinner-d1/d1-core/src/timer.rs | 63 ++++++++++++++++++--- platforms/allwinner-d1/src/lib.rs | 2 +- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/platforms/allwinner-d1/d1-core/src/timer.rs b/platforms/allwinner-d1/d1-core/src/timer.rs index 24bf6cdf..eab9e399 100644 --- a/platforms/allwinner-d1/d1-core/src/timer.rs +++ b/platforms/allwinner-d1/d1-core/src/timer.rs @@ -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, }; @@ -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 { @@ -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: () } } diff --git a/platforms/allwinner-d1/src/lib.rs b/platforms/allwinner-d1/src/lib.rs index b5401d27..7839a8c0 100644 --- a/platforms/allwinner-d1/src/lib.rs +++ b/platforms/allwinner-d1/src/lib.rs @@ -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"),