blob: 03b28c3d5ac72cb7ccccd82f0858d1c69a439f34 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! UART driver.
#![no_std]
//error[E0658]: dereferencing raw mutable pointers in statics is unstable
#![feature(const_mut_refs)]
#[allow(dead_code)]
mod uart;
use uart::*;
use cantrip_os_common::camkes;
use cantrip_os_common::logger;
use cantrip_os_common::sel4_sys;
use spin::Mutex;
use uart_interface::*;
use camkes::*;
use logger::*;
use sel4_sys::seL4_PageBits;
// TODO(chrisphan): Use ringbuf crate instead.
use circular_buffer::Buffer;
// Frequency of the primary clock clk_i.
// https://docs.opentitan.org/hw/ip/clkmgr/doc/
use reg_constants::platform::TOP_MATCHA_SMC_UART_CLOCK_FREQ_PERIPHERAL_HZ as CLK_FIXED_FREQ_HZ;
// The TX/RX Fifo capacity mentioned in the programming guide.
const UART_FIFO_CAPACITY: u32 = 32;
const BAUD_RATE: u64 = 115200;
// This is the default in CAmkES 2 and the configurable default in CAmkES 3.
const TX_RX_DATAPORT_CAPACITY: usize = 1 << seL4_PageBits;
// Driver-owned circular buffer to receive more than the FIFO size before the
// received data is consumed by rx_update.
static RX_BUFFER: Mutex<Buffer> = Mutex::new(Buffer::new());
// Driver-owned circular buffer to buffer more transmitted bytes than can fit
// in the transmit FIFO.
static TX_BUFFER: Mutex<Buffer> = Mutex::new(Buffer::new());
// Generated code...
mod generated {
include!(concat!(env!("SEL4_OUT_DIR"), "/../uart_driver/camkes.rs"));
}
use generated::*;
struct UartDriverControlThread;
impl CamkesThreadInterface for UartDriverControlThread {
fn pre_init() {
// XXX how to handle "maybe" inclusion
static_logger!(logger);
// Computes NCO value corresponding to baud rate.
// nco = 2^20 * baud / fclk (assuming NCO width is 16-bit)
let ctrl_nco: u64 = (BAUD_RATE << 20) / CLK_FIXED_FREQ_HZ;
debug_assert!(ctrl_nco <= reg_constants::uart::UART_CTRL_NCO_MASK as u64);
// Set baud rate and enable TX and RX.
set_ctrl(
Ctrl::new()
.with_nco(ctrl_nco as u16)
.with_tx(true)
.with_rx(true),
);
// Reset TX and RX FIFOs.
set_fifo_ctrl(get_fifo_ctrl().with_rxrst(true).with_txrst(true));
set_fifo_watermarks();
}
// NB: use control thread to handle IRQ's. We could put the handlers
// here but leave them in interface threads in case we want to split
// them back into dedicated handler threads.
fn run() {
shared_irq_loop!(
irq,
tx_watermark => TxWatermarkInterfaceThread::handler,
tx_empty => TxEmptyInterfaceThread::handler
);
}
}
// Handles a tx_watermark interrupt.
//
// These happen when the transmit FIFO is half-empty. This refills the FIFO to
// prevent stalling, stopping early if TX_BUFFER becomes empty, and then signals
// any tx_update that might be waiting for TX_BUFFER to not be full.
struct TxWatermarkInterfaceThread;
impl CamkesThreadInterface for TxWatermarkInterfaceThread {
fn run() {
dedicated_irq_loop!(tx_watermark, Self::handler);
}
}
impl TxWatermarkInterfaceThread {
fn handler() {
fill_tx_fifo();
// Clears INTR_STATE for tx_watermark. (INTR_STATE is write-1-to-clear.) No
// similar check to the one in tx_empty_handle is necessary here, since
// tx_empty will eventually assert and cause anything left in TX_BUFFER
// to be flushed out.
set_intr_state(IntrState::new().with_tx_watermark(true));
}
}
// Handles an rx_watermark interrupt.
//
// Reads any bytes currently pending in the receive FIFO into RX_BUFFER,
// stopping early if RX_BUFFER becomes full and then signals any call
// rx_update that may be waiting on the condition that RX_BUFFER not be empty.
struct RxWatermarkInterfaceThread;
impl CamkesThreadInterface for RxWatermarkInterfaceThread {
fn run() {
dedicated_irq_loop!(rx_watermark, Self::handler);
}
}
impl RxWatermarkInterfaceThread {
fn handler() {
let mut buf = RX_BUFFER.lock();
while rx_fifo_level() > 0 {
if buf.available_data() == 0 {
// The buffer is full.
//
// We want to stay in this invocation of the interrupt handler until
// the RX FIFO is empty, since the rx_watermark interrupt will not
// fire again until the RX FIFO level crosses from 0 to 1. Therefore
// we unblock any pending reads and wait for RX_BUFFER to drain.
RX_NONEMPTY.post();
drop(buf);
RX_EMPTY.wait();
buf = RX_BUFFER.lock();
continue;
}
while rx_fifo_level() > 0 && buf.available_data() > 0 {
let _ = buf.push(uart_getchar());
}
}
// NB: RX_BUFFER must be unlocked before posting RX_NONEMPTY (see read_request)
drop(buf);
RX_NONEMPTY.post();
set_intr_state(IntrState::new().with_rx_watermark(true));
}
}
// Handles a tx_empty interrupt.
//
// This copies TX_BUFFER into the hardware transmit FIFO, stopping early
// if TX_BUFFER becomes empty, and then signals any tx_update that might
// be waiting for TX_BUFFER to not be full.
struct TxEmptyInterfaceThread;
impl CamkesThreadInterface for TxEmptyInterfaceThread {
fn run() {
dedicated_irq_loop!(tx_empty, Self::handler);
}
}
impl TxEmptyInterfaceThread {
fn handler() {
fill_tx_fifo();
let buf = TX_BUFFER.lock();
if buf.is_empty() {
// Clears INTR_STATE for tx_empty. (INTR_STATE is write-1-to-clear.) We
// only do this if TX_BUFFER is empty, since the TX FIFO might have
// become empty in the time from fill_tx_fifo having sent the last
// character until here. In that case, we want the interrupt to
// reassert.
set_intr_state(IntrState::new().with_tx_empty(true));
}
drop(buf); // XXX drop on block exit?
}
}
/// Initializes watermarks.
fn set_fifo_watermarks() {
// Set FIFO watermarks:
//
// RX watermark to 1.
// This enables calls that block on a single byte at a time, like the one
// the shell does when reading a line of input, to return immediately when
// that byte is received.
//
// Note that this high watermark is only a threshold for when to be informed
// that bytes have been received. The FIFO can still fill to its full
// capacity (32) independent of how this is set.
//
// Although a higher watermark in combination with rx_timeout might be
// preferable, Renode simulation does not yet support the rx_timeout
// interrupt.
//
// TX watermark to 16 (half full).
set_fifo_ctrl(
get_fifo_ctrl()
.with_rxilvl(RxILvl::Level1)
.with_txilvl(TxILvl::Level16),
);
// Enables interrupts.
set_intr_enable(
IntrEnable::new()
.with_tx_watermark(true)
.with_rx_watermark(true)
.with_tx_empty(true),
);
}
struct ReadInterfaceThread;
impl CamkesThreadInterface for ReadInterfaceThread {
fn run() {
rpc_basic_recv!(Read, READ_REQUEST_DATA_SIZE, UartDriverError::Success);
}
}
impl ReadInterfaceThread {
fn dispatch(
_client_badge: usize,
request_buffer: &[u8],
reply_buffer: &mut [u8],
) -> Result<usize, UartDriverError> {
let request = match postcard::from_bytes::<ReadRequest>(request_buffer) {
Ok(request) => request,
Err(_) => return Err(UartDriverError::DeserializeFailed),
};
match request {
ReadRequest::Read(limit) => Self::read_request(limit, reply_buffer),
}
}
/// Implements Rust Read::read().
///
/// Reads up to a given limit of bytes into the CAmkES RX_DATAPORT, blocking
/// until at least one byte is available.
fn read_request(limit: usize, reply_buffer: &mut [u8]) -> Result<usize, UartDriverError> {
if limit > TX_RX_DATAPORT_CAPACITY {
// XXX why not just truncate
return Err(UartDriverError::BadLimit);
}
let mut buf = RX_BUFFER.lock();
while buf.is_empty() {
drop(buf);
RX_NONEMPTY.wait();
buf = RX_BUFFER.lock();
}
let mut num_read = 0;
let dataport = unsafe { &mut RX_DATAPORT.data[..limit] };
while num_read < limit {
if let Some(result) = buf.pop() {
dataport[num_read] = result;
} else {
break;
}
num_read += 1;
}
let is_empty = buf.is_empty();
drop(buf);
if is_empty {
RX_EMPTY.post();
}
// TODO: Return error code if num_read == 0.
let reply_slice = postcard::to_slice(&ReadResponse { num_read }, reply_buffer)
.or(Err(UartDriverError::SerializeFailed))?;
Ok(reply_slice.len())
}
}
struct WriteInterfaceThread;
impl CamkesThreadInterface for WriteInterfaceThread {
fn run() {
rpc_basic_recv!(write, WRITE_REQUEST_DATA_SIZE, UartDriverError::Success);
}
}
impl WriteInterfaceThread {
fn dispatch(
_client_badge: usize,
request_buffer: &[u8],
reply_buffer: &mut [u8],
) -> Result<usize, UartDriverError> {
let request = match postcard::from_bytes::<WriteRequest>(request_buffer) {
Ok(request) => request,
Err(_) => return Err(UartDriverError::DeserializeFailed),
};
match request {
WriteRequest::Write(available) => Self::write_request(available, reply_buffer),
WriteRequest::Flush => Self::flush_request(),
}
}
/// Implements Rust Write::write().
///
/// Writes as many bytes from TX_DATAPORT as the hardware will accept, but not
/// more than the number available (specified by the argument). Returns the
/// number of bytes written or a negative value if there is any error.
fn write_request(available: usize, reply_buffer: &mut [u8]) -> Result<usize, UartDriverError> {
if available > TX_RX_DATAPORT_CAPACITY {
// XXX why not just truncate
return Err(UartDriverError::BadLimit);
}
let mut num_written = 0;
let dataport = unsafe { &TX_DATAPORT.data[..available] };
while num_written < available {
let mut buf = TX_BUFFER.lock();
if !buf.push(dataport[num_written]) {
break;
}
num_written += 1;
}
fill_tx_fifo();
// TODO: Return error code if num_written == 0.
let reply_slice = postcard::to_slice(&WriteResponse { num_written }, reply_buffer)
.or(Err(UartDriverError::SerializeFailed))?;
Ok(reply_slice.len())
}
/// Implements Rust Write::flush().
///
/// Drains TX_BUFFER and tx_fifo. Blocks until buffer is empty.
fn flush_request() -> Result<usize, UartDriverError> {
let buf = TX_BUFFER.lock();
while !buf.is_empty() {
fill_tx_fifo();
}
Ok(0)
}
}
/// Copies from TX_BUFFER into the transmit FIFO.
///
/// This stops when the transmit FIFO is full or when TX_BUFFER is empty,
/// whichever comes first.
fn tx_fifo_level() -> u32 { get_fifo_status().txlvl().into() }
/// Copies from TX_BUFFER into the transmit FIFO.
///
/// This stops when the transmit FIFO is full or when TX_BUFFER is empty,
/// whichever comes first.
fn fill_tx_fifo() {
let mut buf = TX_BUFFER.lock();
while tx_fifo_level() < UART_FIFO_CAPACITY {
if let Some(result) = buf.pop() {
set_wdata(result);
} else {
break;
}
}
// drop(buf) (happens on block exit)
}
/// Gets the number of unread bytes in the RX FIFO from hardware MMIO.
fn rx_fifo_level() -> u32 { get_fifo_status().rxlvl().into() }
/// Reads one byte from the hardware read data register.
///
/// Callers should first ensure the receive FIFO is not empty rather than rely
/// on any particular magic value to indicate that.
fn uart_getchar() -> u8 { get_rdata() }