aarch64: implement IRQ register & serial IRQ

master
equation314 6 years ago
parent 28d872064d
commit 5a165178cc

@ -4,6 +4,7 @@ use volatile::{ReadOnly, Volatile};
const INT_BASE: usize = IO_BASE + 0xB000 + 0x200; const INT_BASE: usize = IO_BASE + 0xB000 + 0x200;
/// Allowed interrupts (ref: peripherals 7.5, page 113) /// Allowed interrupts (ref: peripherals 7.5, page 113)
#[repr(u8)]
#[derive(Copy, Clone, PartialEq, Debug)] #[derive(Copy, Clone, PartialEq, Debug)]
pub enum Interrupt { pub enum Interrupt {
Timer1 = 1, Timer1 = 1,
@ -30,6 +31,24 @@ struct Registers {
DisableBasicIRQ: Volatile<u32>, DisableBasicIRQ: Volatile<u32>,
} }
/// Pending interrupts
pub struct PendingInterrupts(u64);
impl Iterator for PendingInterrupts {
type Item = usize;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let int = self.0.trailing_zeros();
if int < 64 {
self.0 &= !(1 << int);
Some(int as usize)
} else {
None
}
}
}
/// An interrupt controller. Used to enable and disable interrupts as well as to /// An interrupt controller. Used to enable and disable interrupts as well as to
/// check if an interrupt is pending. /// check if an interrupt is pending.
pub struct Controller { pub struct Controller {
@ -38,6 +57,7 @@ pub struct Controller {
impl Controller { impl Controller {
/// Returns a new handle to the interrupt controller. /// Returns a new handle to the interrupt controller.
#[inline]
pub fn new() -> Controller { pub fn new() -> Controller {
Controller { Controller {
registers: unsafe { &mut *(INT_BASE as *mut Registers) }, registers: unsafe { &mut *(INT_BASE as *mut Registers) },
@ -58,4 +78,11 @@ impl Controller {
pub fn is_pending(&self, int: Interrupt) -> bool { pub fn is_pending(&self, int: Interrupt) -> bool {
self.registers.IRQPending[int as usize / 32].read() & (1 << (int as usize) % 32) != 0 self.registers.IRQPending[int as usize / 32].read() & (1 << (int as usize) % 32) != 0
} }
/// Return all pending interrupts.
pub fn pending_interrupts(&self) -> PendingInterrupts {
let irq1 = self.registers.IRQPending[0].read() as u64;
let irq2 = self.registers.IRQPending[1].read() as u64;
PendingInterrupts((irq2 << 32) | irq1)
}
} }

@ -8,6 +8,13 @@ const MU_REG_BASE: usize = IO_BASE + 0x215040;
/// The `AUXENB` register from page 9 of the BCM2837 documentation. /// The `AUXENB` register from page 9 of the BCM2837 documentation.
const AUX_ENABLES: *mut Volatile<u8> = (IO_BASE + 0x215004) as *mut Volatile<u8>; const AUX_ENABLES: *mut Volatile<u8> = (IO_BASE + 0x215004) as *mut Volatile<u8>;
/// Enum representing bit fields of the `AUX_MU_IIR_REG` register.
#[repr(u8)]
pub enum MiniUartInterruptId {
Transmit = 0b010,
Recive = 0b100,
}
/// Enum representing bit fields of the `AUX_MU_LSR_REG` register. /// Enum representing bit fields of the `AUX_MU_LSR_REG` register.
#[repr(u8)] #[repr(u8)]
enum LsrStatus { enum LsrStatus {
@ -15,7 +22,7 @@ enum LsrStatus {
TxAvailable = 1 << 5, TxAvailable = 1 << 5,
} }
/// MU registers starting from `AUX_ENABLES` (ref: peripherals 2.1, page 8) /// MU registers starting from `MU_REG_BASE` (ref: peripherals 2.1, page 8)
#[repr(C)] #[repr(C)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
struct Registers { struct Registers {
@ -62,23 +69,25 @@ impl MiniUart {
&mut *(MU_REG_BASE as *mut Registers) &mut *(MU_REG_BASE as *mut Registers)
}; };
Gpio::new(14).into_alt(Function::Alt5).set_gpio_pd(0);
Gpio::new(15).into_alt(Function::Alt5).set_gpio_pd(0);
registers.AUX_MU_CNTL_REG.write(0); // Disable auto flow control and disable receiver and transmitter (for now)
registers.AUX_MU_IER_REG.write(0); // Disable receive and transmit interrupts
registers.AUX_MU_LCR_REG.write(3); // Enable 8 bit mode
registers.AUX_MU_MCR_REG.write(0); // Set RTS line to be always high
registers.AUX_MU_BAUD_REG.write(270); // Set baud rate to 115200
registers.AUX_MU_CNTL_REG.write(3); // Finally, enable transmitter and receiver
MiniUart { MiniUart {
registers: registers, registers: registers,
timeout: None, timeout: None,
} }
} }
pub fn init(&mut self) {
Gpio::new(14).into_alt(Function::Alt5).set_gpio_pd(0);
Gpio::new(15).into_alt(Function::Alt5).set_gpio_pd(0);
self.registers.AUX_MU_CNTL_REG.write(0); // Disable auto flow control and disable receiver and transmitter (for now)
self.registers.AUX_MU_IER_REG.write(1); // Enable receive interrupts and disable transmit interrupts
self.registers.AUX_MU_LCR_REG.write(3); // Enable 8 bit mode
self.registers.AUX_MU_MCR_REG.write(0); // Set RTS line to be always high
self.registers.AUX_MU_BAUD_REG.write(270); // Set baud rate to 115200
self.registers.AUX_MU_CNTL_REG.write(3); // Finally, enable transmitter and receiver
}
/// Set the read timeout to `milliseconds` milliseconds. /// Set the read timeout to `milliseconds` milliseconds.
pub fn set_read_timeout(&mut self, milliseconds: u32) { pub fn set_read_timeout(&mut self, milliseconds: u32) {
self.timeout = Some(milliseconds) self.timeout = Some(milliseconds)
@ -111,8 +120,13 @@ impl MiniUart {
} }
/// Reads a byte. Blocks indefinitely until a byte is ready to be read. /// Reads a byte. Blocks indefinitely until a byte is ready to be read.
pub fn read_byte(&mut self) -> u8 { pub fn read_byte(&self) -> u8 {
while !self.has_byte() {} while !self.has_byte() {}
self.registers.AUX_MU_IO_REG.read() self.registers.AUX_MU_IO_REG.read()
} }
// Read `AUX_MU_IIR_REG` and determine if the interrupt `id` is pending.
pub fn interrupt_is_pending(&self, id: MiniUartInterruptId) -> bool {
self.registers.AUX_MU_IIR_REG.read() & 0b110 == id as u8
}
} }

@ -58,7 +58,7 @@ impl Timer {
/// Reads the generic timer's counter and returns the 64-bit counter value. /// Reads the generic timer's counter and returns the 64-bit counter value.
/// The returned value is the number of elapsed microseconds. /// The returned value is the number of elapsed microseconds.
pub fn read(&self) -> u64 { pub fn read(&self) -> u64 {
let cntfrq = CNTFRQ_EL0.get(); let cntfrq = CNTFRQ_EL0.get(); // 62500000
(CNTPCT_EL0.get() * 1000000 / (cntfrq as u64)) as u64 (CNTPCT_EL0.get() * 1000000 / (cntfrq as u64)) as u64
} }
@ -66,7 +66,7 @@ impl Timer {
/// interrupts for timer 1 are enabled and IRQs are unmasked, then a timer /// interrupts for timer 1 are enabled and IRQs are unmasked, then a timer
/// interrupt will be issued in `us` microseconds. /// interrupt will be issued in `us` microseconds.
pub fn tick_in(&mut self, us: u32) { pub fn tick_in(&mut self, us: u32) {
let cntfrq = CNTFRQ_EL0.get(); let cntfrq = CNTFRQ_EL0.get(); // 62500000
CNTP_TVAL_EL0.set(((cntfrq as f64) * (us as f64) / 1000000.0) as u32); CNTP_TVAL_EL0.set(((cntfrq as f64) * (us as f64) / 1000000.0) as u32);
} }

@ -1,11 +1,31 @@
use crate::arch::interrupt::TrapFrame; use crate::arch::interrupt::TrapFrame;
use bcm2837::timer::Timer; use bcm2837::interrupt::Controller;
use bcm2837::interrupt::{Controller, Interrupt}; use log::*;
pub use bcm2837::interrupt::Interrupt;
static IRQ_HANDLERS: &'static [Option<fn()>; 64] = &[None; 64];
pub fn handle_irq(tf: &mut TrapFrame) { pub fn handle_irq(tf: &mut TrapFrame) {
let controller = Timer::new(); let controller = bcm2837::timer::Timer::new();
if controller.is_pending() { if controller.is_pending() {
super::timer::set_next(); super::timer::set_next();
crate::trap::timer(); crate::trap::timer();
} }
for int in Controller::new().pending_interrupts() {
if let Some(handler) = IRQ_HANDLERS[int] {
handler();
} else {
error!("Unregistered IRQ {}", int);
crate::trap::error(tf);
}
}
}
pub fn register_irq(int: Interrupt, handler: fn()) {
unsafe {
*(&IRQ_HANDLERS[int as usize] as *const _ as *mut Option<fn()>) = Some(handler);
}
Controller::new().enable(int);
} }

@ -1,11 +1,11 @@
use bcm2837::mini_uart::MiniUart; use bcm2837::mini_uart::{MiniUart, MiniUartInterruptId};
use lazy_static::lazy_static;
use core::fmt; use core::fmt;
use spin::Mutex; use spin::Mutex;
use once::*;
/// Struct to get a global SerialPort interface /// Struct to get a global SerialPort interface
pub struct SerialPort { pub struct SerialPort {
mu: Option<MiniUart>, mu: MiniUart,
} }
pub trait SerialRead { pub trait SerialRead {
@ -14,30 +14,31 @@ pub trait SerialRead {
impl SerialPort { impl SerialPort {
/// Creates a new instance of `SerialPort`. /// Creates a new instance of `SerialPort`.
const fn new() -> SerialPort { fn new() -> SerialPort {
SerialPort { mu: None } SerialPort {
mu: MiniUart::new(),
}
} }
/// Init a newly created SerialPort, can only be called once. /// Init a newly created SerialPort, can only be called once.
pub fn init(&mut self) { pub fn init(&mut self) {
assert_has_not_been_called!("SerialPort::init must be called only once"); self.mu.init();
self.mu = Some(MiniUart::new()); super::irq::register_irq(super::irq::Interrupt::Aux, handle_serial_irq);
} }
/// Writes the byte `byte` to the UART device. /// Writes the byte `byte` to the UART device.
pub fn write_byte(&mut self, byte: u8) { fn write_byte(&mut self, byte: u8) {
match &mut self.mu { self.mu.write_byte(byte)
Some(mu) => mu.write_byte(byte),
None => panic!("SerialPort is not initialized"),
}
} }
/// Reads a byte from the UART device, blocking until a byte is available. /// Reads a byte from the UART device, blocking until a byte is available.
pub fn read_byte(&mut self) -> u8 { fn read_byte(&self) -> u8 {
match &mut self.mu { self.mu.read_byte()
Some(mu) => return mu.read_byte(),
None => panic!("SerialPort is not initialized"),
} }
// Whether the interrupt `id` is pending.
fn interrupt_is_pending(&self, id: MiniUartInterruptId) -> bool {
self.mu.interrupt_is_pending(id)
} }
} }
@ -70,4 +71,13 @@ impl fmt::Write for SerialPort {
} }
} }
pub static SERIAL_PORT: Mutex<SerialPort> = Mutex::new(SerialPort::new()); fn handle_serial_irq() {
let serial = SERIAL_PORT.lock();
if serial.interrupt_is_pending(MiniUartInterruptId::Recive) {
crate::trap::serial(serial.read_byte() as char)
}
}
lazy_static!{
pub static ref SERIAL_PORT: Mutex<SerialPort> = Mutex::new(SerialPort::new());
}

@ -1,5 +1,4 @@
use bcm2837::timer; use bcm2837::timer;
use bcm2837::interrupt::{Controller, Interrupt};
use log::*; use log::*;
pub fn init() { pub fn init() {

@ -1,7 +1,7 @@
//! Serial driver for aarch64. //! Serial driver for aarch64.
use core::fmt::{Arguments, Write}; use core::fmt::{Arguments, Write};
use super::board::serial::{SerialRead, SERIAL_PORT}; use super::board::serial::*;
pub fn getchar() -> char { pub fn getchar() -> char {
unsafe { SERIAL_PORT.force_unlock(); } unsafe { SERIAL_PORT.force_unlock(); }

@ -77,13 +77,12 @@ impl Stdin {
pub fn pop(&self) -> char { pub fn pop(&self) -> char {
// QEMU v3.0 don't support M-mode external interrupt (bug?) // QEMU v3.0 don't support M-mode external interrupt (bug?)
// So we have to use polling. // So we have to use polling.
// TODO: serial interrupt on aarch64 #[cfg(feature = "m_mode")]
#[cfg(any(feature = "m_mode", target_arch = "aarch64"))]
loop { loop {
let c = crate::arch::io::getchar(); let c = crate::arch::io::getchar();
if c != '\0' { return c; } if c != '\0' { return c; }
} }
#[cfg(not(any(feature = "m_mode", target_arch = "aarch64")))] #[cfg(not(feature = "m_mode"))]
loop { loop {
let ret = self.buf.lock().pop_front(); let ret = self.buf.lock().pop_front();
match ret { match ret {

Loading…
Cancel
Save