blob: d4acf4bf6d2466538056d97f0e4b798385da6a81 [file] [log] [blame]
//! Matcha hardware mailbox.
// We want constants & registers to stay together even if they're not currently
// used in this file.
#![allow(dead_code)]
use core::mem::transmute;
use kernel::common::registers::{register_structs, ReadOnly, ReadWrite, WriteOnly};
use kernel::common::StaticRef;
use crate::*;
const MAILBOX_BASE: u32 = 0x40800000; // TOP_MATCHA_MAILBOX_SEC_BASE_ADDR
const MAILBOX_WTIRQ: u32 = 181; // kTopMatchaPlicIrqIdMailboxSecWtirq
const MAILBOX_RTIRQ: u32 = 182; // kTopMatchaPlicIrqIdMailboxSecRtirq
const MAILBOX_EIRQ: u32 = 183; // kTopMatchaPlicIrqIdMailboxSecEirq
const MAILBOX_SIZE_DWORDS: u32 = 8;
const INTR_STATE_BIT_WTIRQ: u32 = 0b001;
const INTR_STATE_BIT_RTIRQ: u32 = 0b010;
const INTR_STATE_BIT_EIRQ: u32 = 0b100;
const INTR_ENABLE_BIT_WTIRQ: u32 = 0b001;
const INTR_ENABLE_BIT_RTIRQ: u32 = 0b010;
const INTR_ENABLE_BIT_EIRQ: u32 = 0b100;
const STATUS_BIT_EMPTY: u32 = 0b0001;
const STATUS_BIT_FULL: u32 = 0b0010;
const STATUS_BIT_WFIFOL: u32 = 0b0100;
const STATUS_BIT_RFIFOL: u32 = 0b1000;
register_structs! {
pub MailboxRegisters {
(0x000 => reg_intr_state: ReadWrite<u32>),
(0x004 => reg_intr_enable: ReadWrite<u32>),
(0x008 => reg_intr_test: ReadWrite<u32>),
(0x00C => reg_mboxw: WriteOnly<u32>),
(0x010 => reg_mboxr: ReadOnly<u32>),
(0x014 => reg_status: ReadOnly<u32>),
(0x018 => reg_error: ReadOnly<u32>),
(0x01C => reg_wirqt: ReadWrite<u32>),
(0x020 => reg_rirqt: ReadWrite<u32>),
(0x024 => reg_ctrl: WriteOnly<u32>),
(0x028 => @END),
}
}
pub struct Mailbox {
regs: StaticRef<MailboxRegisters>,
plic_wtirq: u32,
plic_rtirq: u32,
plic_eirq: u32,
}
pub const MAILBOX: Mailbox = Mailbox {
regs: unsafe { StaticRef::new(MAILBOX_BASE as *mut MailboxRegisters) },
plic_wtirq: MAILBOX_WTIRQ,
plic_rtirq: MAILBOX_RTIRQ,
plic_eirq: MAILBOX_EIRQ,
};
// 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 {
// Synchronously send a message. Blocks the caller if there's no room in the
// outbox.
pub fn send_message_sync(&self, message: &[u32]) {
for i in 0..message.len() {
// NOTE: The spec for the TLUL mailbox says that the write interrupt
// fires when the FIFO is _above_ the threshold, which is useless
// for us as we need to wait for it to fall _below_ a threshold.
// So, we have to busy-wait here, which is slightly annoying.
while (self.regs.reg_status.get() & STATUS_BIT_FULL) != 0 {}
self.regs.reg_mboxw.set(message[i]);
}
}
// Synchronously wait on a message to arrive. Blocks the caller if no
// message is in the inbox.
pub fn wait_recv_sync(&self) {
// Set the mailbox to fire the read interrupt when there's more than
// zero items in it and enable the corresponding IRQ line in the PLIC so
// the interrupt will make it through to the CPU.
self.regs.reg_rirqt.set(0);
self.regs.reg_intr_enable.set(INTR_ENABLE_BIT_RTIRQ);
unsafe {
set_bit(plic::PLIC_EN0, self.plic_rtirq as isize);
}
// Wait for the read interrupt to fire.
while (self.regs.reg_intr_state.get() & INTR_STATE_BIT_RTIRQ) == 0 {
unsafe {
asm!("wfi");
}
}
// NOTE: We don't need to deal with the PLIC claim/complete registers here
// as we're disabling the interrupt immediately after it arrives and before
// any other bit of code has a chance to claim it.
// Turn the interrupt and IRQ line back off.
unsafe {
clear_bit(plic::PLIC_EN0, self.plic_rtirq as isize);
}
self.regs.reg_intr_enable.set(0);
// Ack it in the mailbox so it doesn't re-fire later.
self.regs.reg_intr_state.set(INTR_STATE_BIT_RTIRQ);
}
// Synchronously receive a message. Blocks the caller if no message is in
// the inbox.
pub unsafe fn recv_message_sync(&self, message: &mut [u32]) {
for i in 0..message.len() {
while (self.regs.reg_status.get() & STATUS_BIT_EMPTY) != 0 {
self.wait_recv_sync();
}
message[i] = self.regs.reg_mboxr.get();
}
}
}