blob: f28a76f6e06f23bf20ba2706878edffa4853120d [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::cell::Cell;
use core::mem::transmute;
use core::slice;
use kernel::common::registers::{register_structs, ReadOnly, ReadWrite, WriteOnly};
use kernel::common::StaticRef;
use kernel::{AppSlice, Shared};
use crate::*;
//------------------------------------------------------------------------------
// The high bit of the message header is used to distinguish between "inline"
// messages that fit in the mailbox and "long" messages that contain the
// physical address of a memory page containing the message.
const HEADER_FLAG_LONG_MESSAGE: u32 = 0x80000000;
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_STATE_MASK: u32 = 0b111;
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 => reg_fifor: ReadOnly<u32>),
(0x02C => reg_fifow: ReadOnly<u32>),
(0x030 => @END),
}
}
//------------------------------------------------------------------------------
pub trait MailboxISR {
fn on_wtirq(&self);
fn on_rtirq(&self);
fn on_eirq(&self);
}
//------------------------------------------------------------------------------
pub trait MailboxHAL {
fn get_message_sync(&self, buf: &mut [u8]) -> Result<usize, usize>;
fn send_message_sync(&self, message_size: usize, buf: &[u8]) -> Result<usize, usize>;
fn get_message_slice_sync(&self, slice: &AppSlice<Shared, u8>) -> Result<usize, usize>;
fn send_message_slice_sync(
&self,
message_len: usize,
slice: &AppSlice<Shared, u8>,
) -> Result<usize, usize>;
}
//------------------------------------------------------------------------------
pub struct MailboxImpl {
regs: StaticRef<MailboxRegisters>,
// PLIC line for write interrupt
wtirq: u32,
// PLIC line for read interrupt
rtirq: u32,
// PLIC line for error interrupt
eirq: u32,
// Client interrupt callback
client_isr: Cell<Option<&'static dyn MailboxISR>>,
// Physical address of the memory page used to send the last message, if it
// was a long message.
message_page: Cell<u32>,
}
//------------------------------------------------------------------------------
impl MailboxImpl {
pub fn new(mailbox_base_addr: u32, wtirq: u32, rtirq: u32, eirq: u32) -> Self {
let inst = Self {
regs: unsafe { StaticRef::new(mailbox_base_addr as *mut MailboxRegisters) },
wtirq: wtirq,
rtirq: rtirq,
eirq: eirq,
client_isr: Cell::new(None),
message_page: Cell::new(0),
};
// Clear any old interrupts and enable receive and error interrupts.
inst.regs.reg_wirqt.set(0);
inst.regs.reg_rirqt.set(0);
inst.regs.reg_intr_state.set(INTR_STATE_MASK); // W1C
inst.regs
.reg_intr_enable
.set(INTR_ENABLE_BIT_RTIRQ | INTR_ENABLE_BIT_EIRQ);
unsafe {
MailboxImpl::set_bit(plic_hal::PLIC_EN0, inst.rtirq as isize);
MailboxImpl::set_bit(plic_hal::PLIC_EN0, inst.eirq as isize);
}
return inst;
}
pub fn set_client_isr(&self, client: &'static dyn MailboxISR) {
self.client_isr.set(Some(client));
}
//--------------------------------------------------------------------------
// 32-bit-word bitfield helpers for the PLIC's irq enable lines.
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);
}
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);
}
fn enqueue_u32(&self, x: u32) {
while (self.regs.reg_status.get() & STATUS_BIT_FULL) == STATUS_BIT_FULL {}
self.regs.reg_mboxw.set(x);
}
fn dequeue_u32(&self) -> u32 {
while (self.regs.reg_status.get() & STATUS_BIT_EMPTY) == STATUS_BIT_EMPTY {}
return self.regs.reg_mboxr.get();
}
pub fn drain_read_fifo(&self) {
while (self.regs.reg_status.get() & STATUS_BIT_EMPTY) == 0 {
let _ = self.regs.reg_mboxr.get();
}
}
pub fn dump(&self) {
dprintf!("reg_intr_state {:x}\n", self.regs.reg_intr_state.get());
dprintf!("reg_intr_enable {:x}\n", self.regs.reg_intr_enable.get());
dprintf!("reg_intr_test {:x}\n", self.regs.reg_intr_test.get());
dprintf!("reg_status {:x}\n", self.regs.reg_status.get());
dprintf!("reg_error {:x}\n", self.regs.reg_error.get());
dprintf!("reg_wirqt {:x}\n", self.regs.reg_wirqt.get());
dprintf!("reg_rirqt {:x}\n", self.regs.reg_rirqt.get());
dprintf!("reg_fifor {:x}\n", self.regs.reg_fifor.get());
dprintf!("reg_fifow {:x}\n", self.regs.reg_fifow.get());
}
}
//------------------------------------------------------------------------------
impl MailboxHAL for MailboxImpl {
fn get_message_sync(&self, buf: &mut [u8]) -> Result<usize, usize> {
let header = self.dequeue_u32();
let message_size = header & !HEADER_FLAG_LONG_MESSAGE;
if (header & HEADER_FLAG_LONG_MESSAGE) != 0 {
// Long message, copy from message page to result buffer.
let message_page = self.dequeue_u32();
self.message_page.set(message_page);
unsafe {
let src: *const u8 = transmute(message_page);
let dst: *mut u8 = transmute(buf.as_ptr());
core::ptr::copy_nonoverlapping(src, dst, message_size as usize);
}
} else {
// Short message, copy from mailbox to result buffer.
unsafe {
let dst: *mut u32 = transmute(buf.as_ptr());
let message_dwords = message_size / 4;
for i in 0..message_dwords {
dst.offset(i as isize).write(self.dequeue_u32());
}
}
}
return Ok(message_size as usize);
}
fn send_message_sync(&self, message_size: usize, buf: &[u8]) -> Result<usize, usize> {
unsafe {
let message_page = self.message_page.get();
if message_page != 0 {
// Long message, reply using the same physical page as the request.
let src: *const u8 = transmute(buf.as_ptr());
let dst: *mut u8 = transmute(message_page);
core::ptr::copy_nonoverlapping(src, dst, message_size);
self.enqueue_u32(0x80000000 | message_size as u32);
self.enqueue_u32(message_page);
// Response sent, clear cached page address
self.message_page.set(0);
} else {
// Short message, respond via the mailbox.
let src: *const u32 = transmute(buf.as_ptr());
let message_dwords = message_size / 4;
self.enqueue_u32(message_size as u32);
for i in 0..message_dwords {
self.enqueue_u32(src.offset(i as isize).read());
}
}
}
return Ok(message_size);
}
fn get_message_slice_sync(&self, slice: &AppSlice<Shared, u8>) -> Result<usize, usize> {
unsafe {
let buf: &mut [u8] = slice::from_raw_parts_mut(slice.ptr() as *mut u8, slice.len());
return self.get_message_sync(buf);
}
}
fn send_message_slice_sync(
&self,
message_len: usize,
slice: &AppSlice<Shared, u8>,
) -> Result<usize, usize> {
unsafe {
let buf: &[u8] = slice::from_raw_parts(slice.ptr() as *const u8, slice.len());
return self.send_message_sync(message_len, buf);
}
}
}
//------------------------------------------------------------------------------
// The interrupt handler in platform/src/chip.rs will call these handlers, which
// in turn call the capsule-level handlers and then clear the interrupt.
impl MailboxISR for MailboxImpl {
fn on_wtirq(&self) {
self.client_isr.get().map(|c| c.on_wtirq());
self.regs.reg_intr_state.set(INTR_STATE_BIT_WTIRQ);
}
fn on_rtirq(&self) {
self.client_isr.get().map(|c| c.on_rtirq());
if (self.regs.reg_status.get() & STATUS_BIT_EMPTY) == 0 {
self.drain_read_fifo();
}
self.regs.reg_intr_state.set(INTR_STATE_BIT_RTIRQ);
}
fn on_eirq(&self) {
dprintf!("MailboxImpl::on_eirq fired - this is probably bad\n");
self.client_isr.get().map(|c| c.on_eirq());
self.regs.reg_intr_state.set(INTR_STATE_BIT_EIRQ);
}
}
//------------------------------------------------------------------------------