blob: c3bcefbd1989739a491774ecff885bb96cbd9239 [file] [log] [blame]
//! Matcha hardware mailbox.
use core::mem::transmute;
use kernel::common::registers::{register_structs, ReadOnly, ReadWrite, WriteOnly};
use kernel::common::StaticRef;
use crate::*;
// TODO(aappleby): These constants are hardcoded pending the merge of the
// mailbox RTL.
pub const MAILBOX0_BASE: u32 = 0x400F0000;
pub const MAILBOX1_BASE: u32 = 0x400F1000;
pub const MAILBOX_SIZE_DWORDS: u32 = 8;
pub const MAILBOX_STATUS_HIWATER: u32 = 0b0100;
pub const MAILBOX_STATUS_LOWATER: u32 = 0b1000;
pub const MAILBOX0_HIWATER_IRQ: isize = 100;
pub const MAILBOX0_LOWATER_IRQ: isize = 101;
pub const MAILBOX1_HIWATER_IRQ: isize = 102;
pub const MAILBOX1_LOWATER_IRQ: isize = 103;
pub const IRQ_ERROR : u32 = 0b001;
pub const IRQ_HIWATER : u32 = 0b010;
pub const IRQ_LOWATER : u32 = 0b100;
register_structs! {
pub MailboxRegisters {
(0x000 => reg_istate: ReadWrite<u32>),
(0x004 => reg_ienable: ReadWrite<u32>),
(0x008 => reg_itest: ReadWrite<u32>),
(0x00C => reg_write: WriteOnly<u32>),
(0x010 => reg_read: ReadOnly<u32>),
(0x014 => reg_status: ReadOnly<u32>),
(0x018 => reg_error: ReadOnly<u32>),
(0x01C => reg_hiwater: ReadWrite<u32>),
(0x020 => reg_lowater: ReadWrite<u32>),
(0x024 => reg_flush: WriteOnly<u32>),
(0x028 => @END),
}
}
pub struct Mailbox {
regs: StaticRef<MailboxRegisters>,
plic_irq_hiwater: isize,
plic_irq_lowater: isize,
}
pub const MAILBOX0: Mailbox = Mailbox {
regs: unsafe { StaticRef::new(MAILBOX0_BASE as *mut MailboxRegisters) },
plic_irq_hiwater: MAILBOX0_HIWATER_IRQ,
plic_irq_lowater: MAILBOX0_LOWATER_IRQ,
};
pub const MAILBOX1: Mailbox = Mailbox {
regs: unsafe { StaticRef::new(MAILBOX1_BASE as *mut MailboxRegisters) },
plic_irq_hiwater: MAILBOX1_HIWATER_IRQ,
plic_irq_lowater: MAILBOX1_LOWATER_IRQ,
};
// 32-bit-word bitfield helpers for the PLIC's irq enable lines.
pub unsafe fn set_bit(base: u32, bit_index: isize) {
let buf: *mut u32 = transmute(base);
let mut bits = buf.offset(bit_index >> 5).read_volatile();
bits |= 1 << (bit_index & 31);
buf.offset(bit_index >> 5).write_volatile(bits);
}
pub unsafe fn clear_bit(base: u32, bit_index: isize) {
let buf: *mut u32 = transmute(base);
let mut bits = buf.offset(bit_index >> 5).read_volatile();
bits &= !(1 << (bit_index & 31));
buf.offset(bit_index >> 5).write_volatile(bits);
}
impl Mailbox {
pub unsafe fn send_message(&self, message: &[u32]) {
self.regs.reg_lowater.set(MAILBOX_SIZE_DWORDS - 1);
self.regs.reg_ienable.set(IRQ_LOWATER);
for i in 0..message.len() {
self.wait_irq_lowater();
self.regs.reg_write.set(message[i]);
}
self.regs.reg_ienable.set(0);
self.regs.reg_lowater.set(0);
}
pub unsafe fn recv_message(&self, message: &mut [u32]) {
self.regs.reg_hiwater.set(1);
self.regs.reg_ienable.set(IRQ_HIWATER);
for i in 0..message.len() {
self.wait_irq_hiwater();
message[i] = self.regs.reg_read.get();
}
self.regs.reg_ienable.set(0);
self.regs.reg_hiwater.set(0);
}
pub unsafe fn wait_hiwater(&self, hiwater : u32) {
self.regs.reg_hiwater.set(hiwater);
self.regs.reg_ienable.set(IRQ_HIWATER);
self.wait_irq_hiwater();
self.regs.reg_ienable.set(0);
self.regs.reg_hiwater.set(0);
}
pub unsafe fn wait_lowater(&self, lowater : u32) {
self.regs.reg_lowater.set(lowater);
self.regs.reg_ienable.set(IRQ_LOWATER);
self.wait_irq_lowater();
self.regs.reg_ienable.set(0);
self.regs.reg_lowater.set(0);
}
// This requires a small bit of explanation - if an interrupt were to happen
// between the 'if (*flag == 0)' and 'asm("wfi")' statements, we could end up
// waiting forever for an interrupt that had already arrived. To prevent that,
// we have to globally disable interrupts around those statements. Once
// an interrupt has been triggered, 'wfi' will continue and the isr will
// execute immediately after global interrupts are re-enabled.
unsafe fn wait_irq_hiwater(&self) {
while (self.regs.reg_status.get() & MAILBOX_STATUS_HIWATER) == 0 {
clear_mstatus_bits(MIE_BIT | SIE_BIT);
set_bit(plic::PLIC_EN0, self.plic_irq_hiwater);
if (self.regs.reg_status.get() & MAILBOX_STATUS_HIWATER) == 0 {
asm!("wfi");
}
clear_bit(plic::PLIC_EN0, self.plic_irq_hiwater);
set_mstatus_bits(MIE_BIT | SIE_BIT);
}
self.regs.reg_istate.set(IRQ_HIWATER);
}
unsafe fn wait_irq_lowater(&self) {
while (self.regs.reg_status.get() & MAILBOX_STATUS_LOWATER) == 0 {
clear_mstatus_bits(MIE_BIT | SIE_BIT);
set_bit(plic::PLIC_EN0, self.plic_irq_lowater);
if (self.regs.reg_status.get() & MAILBOX_STATUS_LOWATER) == 0 {
asm!("wfi");
}
clear_bit(plic::PLIC_EN0, self.plic_irq_lowater);
set_mstatus_bits(MIE_BIT | SIE_BIT);
}
self.regs.reg_istate.set(IRQ_LOWATER);
}
}