diff --git a/crate/bcm2837/src/interrupt.rs b/crate/bcm2837/src/interrupt.rs new file mode 100644 index 0000000..cf340ee --- /dev/null +++ b/crate/bcm2837/src/interrupt.rs @@ -0,0 +1,61 @@ +use super::IO_BASE; +use volatile::{ReadOnly, Volatile}; + +const INT_BASE: usize = IO_BASE + 0xB000 + 0x200; + +/// Allowed interrupts (ref: peripherals 7.5, page 113) +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Interrupt { + Timer1 = 1, + Timer3 = 3, + Usb = 9, + Aux = 29, + Gpio0 = 49, + Gpio1 = 50, + Gpio2 = 51, + Gpio3 = 52, + Uart = 57, +} + +/// Interrupts registers starting from `INT_BASE` (ref: peripherals 7.5, page 112) +#[repr(C)] +#[allow(non_snake_case)] +struct Registers { + IRQBasicPending: ReadOnly, + IRQPending: [ReadOnly; 2], + FIQControl: Volatile, + EnableIRQ: [Volatile; 2], + EnableBasicIRQ: Volatile, + DisableIRQ: [Volatile; 2], + DisableBasicIRQ: Volatile, +} + +/// An interrupt controller. Used to enable and disable interrupts as well as to +/// check if an interrupt is pending. +pub struct Controller { + registers: &'static mut Registers, +} + +impl Controller { + /// Returns a new handle to the interrupt controller. + pub fn new() -> Controller { + Controller { + registers: unsafe { &mut *(INT_BASE as *mut Registers) }, + } + } + + /// Enables the interrupt `int`. + pub fn enable(&mut self, int: Interrupt) { + self.registers.EnableIRQ[int as usize / 32].write(1 << (int as usize) % 32); + } + + /// Disables the interrupt `int`. + pub fn disable(&mut self, int: Interrupt) { + self.registers.DisableIRQ[int as usize / 32].write(1 << (int as usize) % 32); + } + + /// Returns `true` if `int` is pending. Otherwise, returns `false`. + pub fn is_pending(&self, int: Interrupt) -> bool { + self.registers.IRQPending[int as usize / 32].read() & (1 << (int as usize) % 32) != 0 + } +} diff --git a/crate/bcm2837/src/lib.rs b/crate/bcm2837/src/lib.rs index b2611e3..13da9ff 100644 --- a/crate/bcm2837/src/lib.rs +++ b/crate/bcm2837/src/lib.rs @@ -6,6 +6,8 @@ extern crate volatile; mod asm; pub mod gpio; +pub mod timer; pub mod mini_uart; +pub mod interrupt; pub const IO_BASE: usize = 0x3F000000; diff --git a/crate/bcm2837/src/timer.rs b/crate/bcm2837/src/timer.rs new file mode 100644 index 0000000..3e485b8 --- /dev/null +++ b/crate/bcm2837/src/timer.rs @@ -0,0 +1,68 @@ +use super::IO_BASE; +use volatile::{ReadOnly, Volatile}; + +/// The base address for the ARM system timer registers. +const TIMER_REG_BASE: usize = IO_BASE + 0x3000; + +/// System timer registers (ref: peripherals 12.1, page 172) +#[repr(C)] +#[allow(non_snake_case)] +struct Registers { + CS: Volatile, + CLO: ReadOnly, + CHI: ReadOnly, + COMPARE: [Volatile; 4], +} + +#[repr(u8)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum SystemTimer { + Timer0 = 0, + Timer1 = 1, + Timer2 = 2, + Timer3 = 3, +} + +/// The Raspberry Pi ARM system timer. +pub struct Timer { + registers: &'static mut Registers, +} + +impl Timer { + /// Returns a new instance of `Timer`. + pub fn new() -> Timer { + Timer { + registers: unsafe { &mut *(TIMER_REG_BASE as *mut Registers) }, + } + } + + /// Reads the system timer's counter and returns the 64-bit counter value. + /// The returned value is the number of elapsed microseconds. + pub fn read(&self) -> u64 { + let low = self.registers.CLO.read(); + let high = self.registers.CHI.read(); + ((high as u64) << 32) | (low as u64) + } + + /// Sets up a match in timer 1 to occur `us` microseconds from now. If + /// interrupts for timer 1 are enabled and IRQs are unmasked, then a timer + /// interrupt will be issued in `us` microseconds. + pub fn tick_in(&mut self, st: SystemTimer, us: u32) { + let current_low = self.registers.CLO.read(); + let compare = current_low.wrapping_add(us); + self.registers.COMPARE[st as usize].write(compare); + self.registers.CS.write(1 << (st as usize)); // unmask + } +} + +/// Returns the current time in microseconds. +pub fn current_time() -> u64 { + Timer::new().read() +} + +/// Sets up a match in timer 1 to occur `us` microseconds from now. If +/// interrupts for timer 1 are enabled and IRQs are unmasked, then a timer +/// interrupt will be issued in `us` microseconds. +pub fn tick_in(st: SystemTimer, us: u32) { + Timer::new().tick_in(st, us) +} diff --git a/kernel/src/arch/aarch64/board/raspi3/irq.rs b/kernel/src/arch/aarch64/board/raspi3/irq.rs new file mode 100644 index 0000000..5f1fb0d --- /dev/null +++ b/kernel/src/arch/aarch64/board/raspi3/irq.rs @@ -0,0 +1,10 @@ +use super::bcm2837::interrupt::{Controller, Interrupt}; + +pub fn handle_irq() { + let controller = Controller::new(); + if controller.is_pending(Interrupt::Timer1) { + println!("Timer tick..."); + super::timer::set_next(); + // ::trap::timer(); + } +} diff --git a/kernel/src/arch/aarch64/board/raspi3/mod.rs b/kernel/src/arch/aarch64/board/raspi3/mod.rs index 3ff2324..2b32a10 100644 --- a/kernel/src/arch/aarch64/board/raspi3/mod.rs +++ b/kernel/src/arch/aarch64/board/raspi3/mod.rs @@ -2,6 +2,8 @@ extern crate bcm2837; +pub mod irq; +pub mod timer; pub mod serial; pub fn init() { diff --git a/kernel/src/arch/aarch64/board/raspi3/timer.rs b/kernel/src/arch/aarch64/board/raspi3/timer.rs new file mode 100644 index 0000000..b1a0886 --- /dev/null +++ b/kernel/src/arch/aarch64/board/raspi3/timer.rs @@ -0,0 +1,16 @@ +use super::bcm2837::timer; +use super::bcm2837::interrupt::{Controller, Interrupt}; + +pub fn init() { + Controller::new().enable(Interrupt::Timer1); + set_next(); +} + +pub fn get_cycle() -> u64 { + timer::current_time() +} + +pub fn set_next() { + // 1000 ms + timer::tick_in(timer::SystemTimer::Timer1, 1000 * 1000); +} diff --git a/kernel/src/arch/aarch64/interrupt/handler.rs b/kernel/src/arch/aarch64/interrupt/handler.rs index 371d4d1..6a3a905 100644 --- a/kernel/src/arch/aarch64/interrupt/handler.rs +++ b/kernel/src/arch/aarch64/interrupt/handler.rs @@ -1,5 +1,6 @@ //! Trap handler +use arch::board::irq::handle_irq; use super::context::TrapFrame; use super::syndrome::Syndrome; @@ -48,9 +49,7 @@ pub extern "C" fn rust_trap(info: Info, esr: u32, tf: &mut TrapFrame) { _ => ::trap::error(tf), } } - Kind::Irq => { - // TODO - } + Kind::Irq => handle_irq(), _ => ::trap::error(tf), } ::trap::before_return(); diff --git a/kernel/src/arch/aarch64/interrupt/mod.rs b/kernel/src/arch/aarch64/interrupt/mod.rs index 5181cb5..9f6fb0f 100644 --- a/kernel/src/arch/aarch64/interrupt/mod.rs +++ b/kernel/src/arch/aarch64/interrupt/mod.rs @@ -20,13 +20,13 @@ pub fn init() { /// Enable the interrupt. #[inline(always)] pub unsafe fn enable() { - // TODO + asm!("msr daifclr, #2"); } /// Disable the interrupt. #[inline(always)] pub unsafe fn disable() { - // TODO + asm!("msr daifset, #2"); } /// Disable the interrupt and store the status. diff --git a/kernel/src/arch/aarch64/mod.rs b/kernel/src/arch/aarch64/mod.rs index 8353324..8548417 100644 --- a/kernel/src/arch/aarch64/mod.rs +++ b/kernel/src/arch/aarch64/mod.rs @@ -9,6 +9,8 @@ pub mod interrupt; #[path = "board/raspi3/mod.rs"] pub mod board; +pub use self::board::timer; + /// TODO /// The entry point of kernel #[no_mangle] // don't mangle the name of this function @@ -20,6 +22,9 @@ pub extern "C" fn rust_main() -> ! { // FIXME // ::logging::init(); interrupt::init(); + timer::init(); + + unsafe { interrupt::enable(); } super::fs::show_logo(); @@ -39,6 +44,9 @@ pub extern "C" fn rust_main() -> ! { println!("svc 666"); asm!("svc 666"); }, + 't' => unsafe { + println!("{}", timer::get_cycle()); + }, ' '...'\u{7e}' => { print!("{}", c); }