tock: Migrate to using an opentitan UART driver
This brings in the uart driver from lowrisc::uart, but updates it to
use offsets from the opentitan-upstream tree in an effort to get us
off the old tree.
Bug: 228243751
Change-Id: If3b9ce99ee107303d37de46dee591f120577be34
diff --git a/chips/opentitan/Cargo.toml b/chips/opentitan/Cargo.toml
new file mode 100644
index 0000000..a7ef8ba
--- /dev/null
+++ b/chips/opentitan/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "opentitan"
+version = "0.1.0"
+authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
+
+[dependencies]
+rv32i = { path = "../../../tock/arch/rv32i" }
+kernel = { path = "../../../tock/kernel" }
+tock-rt0 = { path = "../../../tock/libraries/tock-rt0" }
diff --git a/chips/opentitan/src/lib.rs b/chips/opentitan/src/lib.rs
new file mode 100644
index 0000000..9622367
--- /dev/null
+++ b/chips/opentitan/src/lib.rs
@@ -0,0 +1,8 @@
+//! Implementations for generic OpenTitan peripherals.
+
+#![feature(const_fn)]
+#![no_std]
+#![crate_name = "opentitan"]
+#![crate_type = "rlib"]
+
+pub mod uart;
diff --git a/chips/opentitan/src/uart.rs b/chips/opentitan/src/uart.rs
new file mode 100644
index 0000000..0c21cad
--- /dev/null
+++ b/chips/opentitan/src/uart.rs
@@ -0,0 +1,348 @@
+//! UART driver.
+
+use core::cell::Cell;
+
+use kernel::common::cells::OptionalCell;
+use kernel::common::cells::TakeCell;
+use kernel::common::registers::{
+ register_bitfields, register_structs, ReadOnly, ReadWrite, WriteOnly,
+};
+use kernel::common::StaticRef;
+use kernel::hil;
+use kernel::hil::uart;
+use kernel::ReturnCode;
+
+// BIG FAT NOTE: if the UART registers change, we're going to have to fix the
+// dprintf hack in dprintf_hal.rs as well, since it bypasses these register
+// definitions. If you fix this, please fix b/239714445 as well.
+register_structs! {
+ pub UartRegisters {
+ (0x000 => intr_state: ReadWrite<u32, intr::Register>),
+ (0x004 => intr_enable: ReadWrite<u32, intr::Register>),
+ (0x008 => intr_test: ReadWrite<u32, intr::Register>),
+ (0x00c => alert_test: ReadWrite<u32, intr::Register>),
+ /// UART control register
+ (0x010 => ctrl: ReadWrite<u32, ctrl::Register>),
+ /// UART live status register
+ (0x014 => status: ReadOnly<u32, status::Register>),
+ /// UART read data)
+ (0x018 => rdata: ReadOnly<u32, rdata::Register>),
+ /// UART write data
+ (0x01c => wdata: WriteOnly<u32, wdata::Register>),
+ /// UART FIFO control register")
+ (0x020 => fifo_ctrl: ReadWrite<u32, fifo_ctrl::Register>),
+ /// UART FIFO status register
+ (0x024 => fifo_status: ReadWrite<u32, fifo_status::Register>),
+ /// TX pin override control. Gives direct SW control over TX pin state
+ (0x028 => ovrd: ReadWrite<u32, ovrd::Register>),
+ /// UART oversampled values
+ (0x02c => val: ReadWrite<u32, val::Register>),
+ /// UART RX timeout control
+ (0x030 => timeout_ctrl: ReadWrite<u32, timeout_ctrl::Register>),
+ (0x034 => @END),
+ }
+}
+
+register_bitfields![u32,
+ intr [
+ tx_watermark OFFSET(0) NUMBITS(1) [],
+ rx_watermark OFFSET(1) NUMBITS(1) [],
+ tx_empty OFFSET(2) NUMBITS(1) [],
+ rx_overflow OFFSET(3) NUMBITS(1) [],
+ rx_frame_err OFFSET(4) NUMBITS(1) [],
+ rx_break_err OFFSET(5) NUMBITS(1) [],
+ rx_timeout OFFSET(6) NUMBITS(1) [],
+ rx_parity_err OFFSET(7) NUMBITS(1) []
+ ],
+ ctrl [
+ tx OFFSET(0) NUMBITS(1) [],
+ rx OFFSET(1) NUMBITS(1) [],
+ nf OFFSET(2) NUMBITS(1) [],
+ slpbk OFFSET(4) NUMBITS(1) [],
+ llpbk OFFSET(5) NUMBITS(1) [],
+ parity_en OFFSET(6) NUMBITS(1) [],
+ parity_odd OFFSET(7) NUMBITS(1) [],
+ rxblvl OFFSET(8) NUMBITS(2) [],
+ nco OFFSET(16) NUMBITS(16) []
+ ],
+ status [
+ txfull OFFSET(0) NUMBITS(1) [],
+ rxfull OFFSET(1) NUMBITS(1) [],
+ txempty OFFSET(2) NUMBITS(1) [],
+ txidle OFFSET(3) NUMBITS(1) [],
+ rxidle OFFSET(4) NUMBITS(1) [],
+ rxempty OFFSET(5) NUMBITS(1) []
+ ],
+ rdata [
+ data OFFSET(0) NUMBITS(8) []
+ ],
+ wdata [
+ data OFFSET(0) NUMBITS(8) []
+ ],
+ fifo_ctrl [
+ rxrst OFFSET(0) NUMBITS(1) [],
+ txrst OFFSET(1) NUMBITS(1) [],
+ rxilvl OFFSET(2) NUMBITS(2) [],
+ txilvl OFFSET(5) NUMBITS(2) []
+ ],
+ fifo_status [
+ txlvl OFFSET(0) NUMBITS(5) [],
+ rxlvl OFFSET(16) NUMBITS(5) []
+ ],
+ ovrd [
+ txen OFFSET(0) NUMBITS(1) [],
+ txval OFFSET(1) NUMBITS(1) []
+ ],
+ val [
+ rx OFFSET(0) NUMBITS(16) []
+ ],
+ timeout_ctrl [
+ val OFFSET(0) NUMBITS(23) [],
+ en OFFSET(31) NUMBITS(1) []
+ ]
+];
+
+pub struct Uart<'a> {
+ registers: StaticRef<UartRegisters>,
+ clock_frequency: u32,
+ tx_client: OptionalCell<&'a dyn hil::uart::TransmitClient>,
+ rx_client: OptionalCell<&'a dyn hil::uart::ReceiveClient>,
+
+ tx_buffer: TakeCell<'static, [u8]>,
+ tx_len: Cell<usize>,
+ tx_index: Cell<usize>,
+
+ rx_buffer: TakeCell<'static, [u8]>,
+ rx_len: Cell<usize>,
+}
+
+#[derive(Copy, Clone)]
+pub struct UartParams {
+ pub baud_rate: u32,
+}
+
+impl<'a> Uart<'a> {
+ pub const fn new(base: StaticRef<UartRegisters>, clock_frequency: u32) -> Uart<'a> {
+ Uart {
+ registers: base,
+ clock_frequency: clock_frequency,
+ tx_client: OptionalCell::empty(),
+ rx_client: OptionalCell::empty(),
+ tx_buffer: TakeCell::empty(),
+ tx_len: Cell::new(0),
+ tx_index: Cell::new(0),
+ rx_buffer: TakeCell::empty(),
+ rx_len: Cell::new(0),
+ }
+ }
+
+ fn set_baud_rate(&self, baud_rate: u32) {
+ let regs = self.registers;
+ let uart_ctrl_nco = ((baud_rate as u64) << 20) / self.clock_frequency as u64;
+
+ regs.ctrl
+ .write(ctrl::nco.val((uart_ctrl_nco & 0xffff) as u32));
+ regs.ctrl.modify(ctrl::tx::SET + ctrl::rx::SET);
+
+ regs.fifo_ctrl
+ .write(fifo_ctrl::rxrst::SET + fifo_ctrl::txrst::SET);
+ }
+
+ fn enable_tx_interrupt(&self) {
+ let regs = self.registers;
+
+ regs.intr_enable.modify(intr::tx_empty::SET);
+ }
+
+ fn disable_tx_interrupt(&self) {
+ let regs = self.registers;
+
+ regs.intr_enable.modify(intr::tx_empty::CLEAR);
+ // Clear the interrupt bit (by writing 1), if it happens to be set
+ regs.intr_state.write(intr::tx_empty::SET);
+ }
+
+ fn enable_rx_interrupt(&self) {
+ let regs = self.registers;
+
+ // Generate an interrupt if we get any value in the RX buffer
+ regs.intr_enable.modify(intr::rx_watermark::SET);
+ regs.fifo_ctrl.write(fifo_ctrl::rxilvl.val(0 as u32));
+ }
+
+ fn disable_rx_interrupt(&self) {
+ let regs = self.registers;
+
+ // Generate an interrupt if we get any value in the RX buffer
+ regs.intr_enable.modify(intr::rx_watermark::CLEAR);
+
+ // Clear the interrupt bit (by writing 1), if it happens to be set
+ regs.intr_state.write(intr::rx_watermark::SET);
+ }
+
+ fn tx_progress(&self) {
+ let regs = self.registers;
+ let idx = self.tx_index.get();
+ let len = self.tx_len.get();
+
+ if idx < len {
+ // If we are going to transmit anything, we first need to enable the
+ // TX interrupt. This ensures that we will get an interrupt, where
+ // we can either call the callback from, or continue transmitting
+ // bytes.
+ self.enable_tx_interrupt();
+
+ // Read from the transmit buffer and send bytes to the UART hardware
+ // until either the buffer is empty or the UART hardware is full.
+ self.tx_buffer.map(|tx_buf| {
+ let tx_len = len - idx;
+
+ for i in 0..tx_len {
+ if regs.status.is_set(status::txfull) {
+ break;
+ }
+ let tx_idx = idx + i;
+ regs.wdata.write(wdata::data.val(tx_buf[tx_idx] as u32));
+ self.tx_index.set(tx_idx + 1)
+ }
+ });
+ }
+ }
+
+ pub fn handle_interrupt(&self) {
+ let regs = self.registers;
+ let intrs = regs.intr_state.extract();
+
+ if intrs.is_set(intr::tx_empty) {
+ self.disable_tx_interrupt();
+
+ if self.tx_index.get() == self.tx_len.get() {
+ // We sent everything to the UART hardware, now from an
+ // interrupt callback we can issue the callback.
+ self.tx_client.map(|client| {
+ self.tx_buffer.take().map(|tx_buf| {
+ client.transmitted_buffer(tx_buf, self.tx_len.get(), ReturnCode::SUCCESS);
+ });
+ });
+ } else {
+ // We have more to transmit, so continue in tx_progress().
+ self.tx_progress();
+ }
+ } else if intrs.is_set(intr::rx_watermark) {
+ self.disable_rx_interrupt();
+
+ self.rx_client.map(|client| {
+ self.rx_buffer.take().map(|rx_buf| {
+ let mut len = 0;
+ let mut return_code = ReturnCode::SUCCESS;
+
+ for i in 0..self.rx_len.get() {
+ rx_buf[i] = regs.rdata.get() as u8;
+ len = i + 1;
+
+ if regs.status.is_set(status::rxempty) {
+ /* RX is empty */
+ return_code = ReturnCode::ESIZE;
+ break;
+ }
+ }
+
+ client.received_buffer(rx_buf, len, return_code, uart::Error::None);
+ });
+ });
+ }
+ }
+
+ pub fn transmit_sync(&self, bytes: &[u8]) {
+ let regs = self.registers;
+ for b in bytes.iter() {
+ while regs.status.is_set(status::txfull) {}
+ regs.wdata.write(wdata::data.val(*b as u32));
+ }
+ }
+}
+
+impl<'a> hil::uart::UartData<'a> for Uart<'a> {}
+impl<'a> hil::uart::Uart<'a> for Uart<'a> {}
+
+impl hil::uart::Configure for Uart<'_> {
+ fn configure(&self, params: hil::uart::Parameters) -> ReturnCode {
+ let regs = self.registers;
+ // We can set the baud rate.
+ self.set_baud_rate(params.baud_rate);
+
+ regs.fifo_ctrl
+ .write(fifo_ctrl::rxrst::SET + fifo_ctrl::txrst::SET);
+
+ // Disable all interrupts for now
+ regs.intr_enable.set(0 as u32);
+
+ ReturnCode::SUCCESS
+ }
+}
+
+impl<'a> hil::uart::Transmit<'a> for Uart<'a> {
+ fn set_transmit_client(&self, client: &'a dyn hil::uart::TransmitClient) {
+ self.tx_client.set(client);
+ }
+
+ fn transmit_buffer(
+ &self,
+ tx_data: &'static mut [u8],
+ tx_len: usize,
+ ) -> (ReturnCode, Option<&'static mut [u8]>) {
+ if tx_len == 0 || tx_len > tx_data.len() {
+ (ReturnCode::ESIZE, Some(tx_data))
+ } else if self.tx_buffer.is_some() {
+ (ReturnCode::EBUSY, Some(tx_data))
+ } else {
+ // Save the buffer so we can keep sending it.
+ self.tx_buffer.replace(tx_data);
+ self.tx_len.set(tx_len);
+ self.tx_index.set(0);
+
+ self.tx_progress();
+ (ReturnCode::SUCCESS, None)
+ }
+ }
+
+ fn transmit_abort(&self) -> ReturnCode {
+ ReturnCode::FAIL
+ }
+
+ fn transmit_word(&self, _word: u32) -> ReturnCode {
+ ReturnCode::FAIL
+ }
+}
+
+/* UART receive is not implemented yet, mostly due to a lack of tests avaliable */
+impl<'a> hil::uart::Receive<'a> for Uart<'a> {
+ fn set_receive_client(&self, client: &'a dyn hil::uart::ReceiveClient) {
+ self.rx_client.set(client);
+ }
+
+ fn receive_buffer(
+ &self,
+ rx_buffer: &'static mut [u8],
+ rx_len: usize,
+ ) -> (ReturnCode, Option<&'static mut [u8]>) {
+ if rx_len == 0 || rx_len > rx_buffer.len() {
+ return (ReturnCode::ESIZE, Some(rx_buffer));
+ }
+
+ self.enable_rx_interrupt();
+
+ self.rx_buffer.replace(rx_buffer);
+ self.rx_len.set(rx_len);
+
+ (ReturnCode::SUCCESS, None)
+ }
+
+ fn receive_abort(&self) -> ReturnCode {
+ ReturnCode::FAIL
+ }
+
+ fn receive_word(&self) -> ReturnCode {
+ ReturnCode::FAIL
+ }
+}
diff --git a/config/src/lib.rs b/config/src/lib.rs
index f7a74cc..83a4ecf 100644
--- a/config/src/lib.rs
+++ b/config/src/lib.rs
@@ -32,4 +32,6 @@
pub const MAILBOX_BASE: u32 = 0x40800000; // TOP_MATCHA_MAILBOX_SEC_BASE_ADDR
pub const MAILBOX_WTIRQ: u32 = 181; // kTopMatchaPlicIrqIdMailboxSecWtirq
pub const MAILBOX_RTIRQ: u32 = 182; // kTopMatchaPlicIrqIdMailboxSecRtirq
-pub const MAILBOX_EIRQ: u32 = 183; // kTopMatchaPlicIrqIdMailboxSecEirq
\ No newline at end of file
+pub const MAILBOX_EIRQ: u32 = 183; // kTopMatchaPlicIrqIdMailboxSecEirq
+
+pub const UART0_BASE_ADDRESS: u32 = 0x40000000;
diff --git a/hal/src/dprintf_hal.rs b/hal/src/dprintf_hal.rs
index d6e5ffb..848a76f 100644
--- a/hal/src/dprintf_hal.rs
+++ b/hal/src/dprintf_hal.rs
@@ -3,12 +3,21 @@
use core2::io::{Cursor, Write};
use core::fmt;
-pub const TX_BUSY: *const u32 = 0x4000_0010 as *const u32;
-pub const TX_PORT: *mut u32 = 0x4000_0018 as *mut u32;
+// TODO(jtgans|aappleby): b/239714445. Use the actual UART registers defined in
+// opentitan/uart.rs instead of hardcoding them here. Or better: get rid of this
+// gross hack and replace with something less gross. :P
+pub const TX_BUSY: *const u32 = 0x4000_0014 as *const u32;
+pub const TX_PORT: *mut u32 = 0x4000_001c as *mut u32;
pub fn send_sync(buf: &[u8], len: usize) {
unsafe {
for i in 0..len {
+ // TODO(jtgans|aappleby): b/239714445. This is a gross hack to
+ // ensure we don't buffer dprintf output for diagnosing early boot
+ // of the system.
+ //
+ // NOTE: if the UART registers change, we're going to have to fix
+ // this here as well!
while (TX_BUSY.read_volatile() & 1) != 0 {}
TX_PORT.write_volatile(buf[i] as u32);
}
diff --git a/platform/Cargo.toml b/platform/Cargo.toml
index e3c1b04..a0ee2e4 100644
--- a/platform/Cargo.toml
+++ b/platform/Cargo.toml
@@ -11,6 +11,7 @@
capsules = { path = "../../tock/capsules" }
kernel = { path = "../../tock/kernel" }
lowrisc = { path = "../../tock/chips/lowrisc" }
+opentitan = { path = "../chips/opentitan" }
blob_fs = { path = "../blob_fs" }
matcha_capsules = { path = "../capsules" }
diff --git a/platform/src/uart.rs b/platform/src/uart.rs
index f9acb19..5ebe18c 100644
--- a/platform/src/uart.rs
+++ b/platform/src/uart.rs
@@ -1,10 +1,10 @@
-//use crate::chip_config::CONFIG;
use kernel::common::StaticRef;
-use lowrisc::uart::{Uart, UartRegisters};
+use opentitan::uart::{Uart, UartRegisters};
+use matcha_config::*;
pub const UART0_BAUDRATE: u32 = crate::chip::CHIP_UART_BPS;
pub static mut UART0: Uart = Uart::new(UART0_BASE, crate::chip::CHIP_PERIPH_FREQ);
const UART0_BASE: StaticRef<UartRegisters> =
- unsafe { StaticRef::new(0x4000_0000 as *const UartRegisters) };
+ unsafe { StaticRef::new(UART0_BASE_ADDRESS as *const UartRegisters) };