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;
/// Allowed interrupts (ref: peripherals 7.5, page 113)
#[repr(u8)]
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Interrupt {
Timer1 = 1,
@ -30,6 +31,24 @@ struct Registers {
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
/// check if an interrupt is pending.
pub struct Controller {
@ -38,6 +57,7 @@ pub struct Controller {
impl Controller {
/// Returns a new handle to the interrupt controller.
#[inline]
pub fn new() -> Controller {
Controller {
registers: unsafe { &mut *(INT_BASE as *mut Registers) },
@ -58,4 +78,11 @@ impl Controller {
pub fn is_pending(&self, int: Interrupt) -> bool {
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.
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.
#[repr(u8)]
enum LsrStatus {
@ -15,7 +22,7 @@ enum LsrStatus {
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)]
#[allow(non_snake_case)]
struct Registers {
@ -62,23 +69,25 @@ impl MiniUart {
&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 {
registers: registers,
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.
pub fn set_read_timeout(&mut self, milliseconds: u32) {
self.timeout = Some(milliseconds)
@ -111,8 +120,13 @@ impl MiniUart {
}
/// 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() {}
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.
/// The returned value is the number of elapsed microseconds.
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
}
@ -66,7 +66,7 @@ impl Timer {
/// 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, 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);
}

@ -1,11 +1,31 @@
use crate::arch::interrupt::TrapFrame;
use bcm2837::timer::Timer;
use bcm2837::interrupt::{Controller, Interrupt};
use bcm2837::interrupt::Controller;
use log::*;
pub use bcm2837::interrupt::Interrupt;
static IRQ_HANDLERS: &'static [Option<fn()>; 64] = &[None; 64];
pub fn handle_irq(tf: &mut TrapFrame) {
let controller = Timer::new();
let controller = bcm2837::timer::Timer::new();
if controller.is_pending() {
super::timer::set_next();
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 spin::Mutex;
use once::*;
/// Struct to get a global SerialPort interface
pub struct SerialPort {
mu: Option<MiniUart>,
mu: MiniUart,
}
pub trait SerialRead {
@ -14,30 +14,31 @@ pub trait SerialRead {
impl SerialPort {
/// Creates a new instance of `SerialPort`.
const fn new() -> SerialPort {
SerialPort { mu: None }
fn new() -> SerialPort {
SerialPort {
mu: MiniUart::new(),
}
}
/// Init a newly created SerialPort, can only be called once.
pub fn init(&mut self) {
assert_has_not_been_called!("SerialPort::init must be called only once");
self.mu = Some(MiniUart::new());
self.mu.init();
super::irq::register_irq(super::irq::Interrupt::Aux, handle_serial_irq);
}
/// Writes the byte `byte` to the UART device.
pub fn write_byte(&mut self, byte: u8) {
match &mut self.mu {
Some(mu) => mu.write_byte(byte),
None => panic!("SerialPort is not initialized"),
}
fn write_byte(&mut self, byte: u8) {
self.mu.write_byte(byte)
}
/// Reads a byte from the UART device, blocking until a byte is available.
pub fn read_byte(&mut self) -> u8 {
match &mut self.mu {
Some(mu) => return mu.read_byte(),
None => panic!("SerialPort is not initialized"),
}
fn read_byte(&self) -> u8 {
self.mu.read_byte()
}
// 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::interrupt::{Controller, Interrupt};
use log::*;
pub fn init() {

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

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

Loading…
Cancel
Save