| //! 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(); |
| } |
| } |
| } |