blob: 61ddb1787fc459223f24a0547400795534a424ef [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
#![cfg_attr(not(test), no_std)]
use uart::RegisterBlock;
use usart_api::backend::{BackendError, IrqMask, LineStatus, Parity, UsartBackend, UsartConfig};
pub trait UartTx {
fn tx_fifo_full(&self) -> bool;
fn write_byte(&mut self, byte: u8);
}
pub struct UartDriver {
uart: RegisterBlock<ureg::RealMmioMut<'static>>,
}
impl UartDriver {
/// Creates a new `UartDriver` for the UART peripheral at the given base address.
///
/// # Safety
///
/// The caller must ensure that `ptr` points to a valid UART peripheral register block,
/// and that they have exclusive access to it.
pub unsafe fn new(ptr: *mut u32) -> Self {
Self {
uart: unsafe { RegisterBlock::new(ptr) },
}
}
fn rx_fifo_empty(&self) -> bool {
self.uart.status().read().rxempty()
}
fn read_byte(&mut self) -> u8 {
self.uart.rdata().read().rdata() as u8
}
}
impl UartTx for UartDriver {
fn tx_fifo_full(&self) -> bool {
self.uart.status().read().txfull()
}
fn write_byte(&mut self, byte: u8) {
self.uart.wdata().write(|w| w.wdata(byte as u32));
}
}
impl UsartBackend for UartDriver {
fn configure(&mut self, config: UsartConfig) -> Result<(), BackendError> {
if config.stop_bits != 1 {
return Err(BackendError::InvalidConfiguration);
}
let parity_en = config.parity != Parity::None;
let parity_odd = matches!(config.parity, Parity::Odd);
if config.baud_rate != 0 {
// This division will truncate to zero for very low baud rates.
// The peripheral clock is 24 MHz on Earlgrey, so this truncates
// to zero for a configured baud rate of 22 baud.
let nco = (((config.baud_rate as u64) << 20)
/ (earlgrey_clock_domain::PERIPHERAL_CLOCK_HZ)) as u32;
if nco > 0xffff {
return Err(BackendError::InvalidConfiguration);
}
self.uart.ctrl().write(|w| {
w.tx(true)
.rx(true)
.parity_en(parity_en)
.parity_odd(parity_odd)
.nco(nco)
});
} else {
self.uart.ctrl().modify(|w| {
w.tx(true)
.rx(true)
.parity_en(parity_en)
.parity_odd(parity_odd)
});
}
// Reset FIFOs and set RX trigger level to 1 byte
self.uart.fifo_ctrl().write(|w| {
w.txrst(true)
.rxrst(true)
.with_rxilvl(uart::enums::Rxilvl::Rxlvl1)
});
// Configure RX timeout (8 bit times)
self.uart.timeout_ctrl().write(|w| w.val(8).en(true));
Ok(())
}
fn write(&mut self, data: &[u8]) -> Result<usize, BackendError> {
if data.is_empty() {
return Ok(0);
}
if self.tx_fifo_full() {
return Err(BackendError::WouldBlock);
}
let mut written = 0;
for &byte in data {
if self.tx_fifo_full() {
break;
}
self.write_byte(byte);
written += 1;
}
Ok(written)
}
fn read(&mut self, out: &mut [u8]) -> Result<usize, BackendError> {
// Our implementation never blocks. `read` is the same as `try_read`.
self.try_read(out)
}
fn try_read(&mut self, out: &mut [u8]) -> Result<usize, BackendError> {
if self.rx_fifo_empty() {
return Err(BackendError::WouldBlock);
}
// Clear errors and timeout in intr_state before reading.
// Some of these bits are reported by `line_status`. You need
// to call line_status before trying to read.
self.uart.intr_state().write(|w| {
w.rx_overflow_clear()
.rx_frame_err_clear()
.rx_parity_err_clear()
.rx_break_err_clear()
.rx_timeout_clear()
});
let mut read_bytes = 0;
for byte in out.iter_mut() {
if self.rx_fifo_empty() {
break;
}
*byte = self.read_byte();
read_bytes += 1;
}
Ok(read_bytes)
}
fn line_status(&self) -> Result<LineStatus, BackendError> {
let status = self.uart.status().read();
let intr = self.uart.intr_state().read();
// TODO: usart_api::backend::LineStatus is a newtype wrapper around u8. It
// should really be a bitflags definition and we should use named constants.
let mut bits = 0u8;
if !status.rxempty() {
bits |= 0x01; // DataReady
}
if status.txempty() {
bits |= 0x40 | 0x20; // TransmitterEmpty | TransmitterHoldingRegisterEmpty
}
if intr.rx_overflow() {
bits |= 0x02; // OverrunError
}
if intr.rx_parity_err() {
bits |= 0x04; // ParityError
}
if intr.rx_frame_err() {
bits |= 0x08; // FramingError
}
if intr.rx_break_err() {
bits |= 0x10; // BreakInterrupt
}
Ok(LineStatus(bits))
}
fn enable_interrupts(&mut self, mask: IrqMask) -> Result<(), BackendError> {
// Clear pending interrupts first to avoid stale triggers
// The watermark interrupts are level triggered and cannot be cleared in
// the `intr_state` register; they are cleared by addressing the underlying
// condition.
//
// The other bits (timeout, tx_done) are events must be cleared in
// the `intr_state` regsiter.
self.uart.intr_state().write(|w| {
let mut w = w;
if mask.contains(IrqMask::RX_DATA_AVAILABLE) {
w = w.rx_timeout_clear();
}
if mask.contains(IrqMask::TX_IDLE) {
w = w.tx_done_clear();
}
w
});
self.uart.intr_enable().modify(|w| {
let mut w = w;
if mask.contains(IrqMask::RX_DATA_AVAILABLE) {
w = w.rx_watermark(true).rx_timeout(true);
}
if mask.contains(IrqMask::TX_IDLE) {
w = w.tx_done(true);
}
w
});
Ok(())
}
fn disable_interrupts(&mut self, mask: IrqMask) -> Result<(), BackendError> {
self.uart.intr_enable().modify(|w| {
let mut w = w;
if mask.contains(IrqMask::RX_DATA_AVAILABLE) {
w = w.rx_watermark(false).rx_timeout(false);
}
if mask.contains(IrqMask::TX_IDLE) {
w = w.tx_done(false);
}
w
});
Ok(())
}
}