synopsys_dw_uart/
lib.rs

1// Copyright 2025 Google LLC
2// This project is dual-licensed under Apache 2.0 and MIT terms.
3// See LICENSE-APACHE and LICENSE-MIT for details.
4
5//! Driver for a Synopsys DesignWare DW_apb UART.
6
7#![no_std]
8#![deny(clippy::undocumented_unsafe_blocks)]
9#![deny(unsafe_op_in_unsafe_fn)]
10
11#[cfg(feature = "embedded-io")]
12mod embedded_io;
13pub mod registers;
14
15use crate::registers::{Fcr, Lcr, Lsr, Registers, Usr};
16use core::{fmt, hint::spin_loop};
17use safe_mmio::{UniqueMmioPointer, field, field_shared};
18use thiserror::Error;
19
20/// Driver for a Synopsys DesignWare DW_apb UART.
21pub struct SynopsysUart<'a> {
22    registers: UniqueMmioPointer<'a, Registers>,
23}
24
25impl<'a> SynopsysUart<'a> {
26    /// Creates a new instance of the UART driver.
27    pub const fn new(registers: UniqueMmioPointer<'a, Registers>) -> Self {
28        Self { registers }
29    }
30
31    /// Configures the UART with the given baud rate, 8 data bits, no parity, and 1 stop bit.
32    ///
33    /// Also enables the transmit and receive FIFOs.
34    ///
35    /// This first waits until the UART is not busy, so may block.
36    pub fn configure(&mut self, baud_rate: u32, serial_clock: u32) {
37        // Wait until the UART is not busy.
38        while field_shared!(self.registers, usr)
39            .read()
40            .contains(Usr::BUSY)
41        {
42            spin_loop();
43        }
44
45        // Enable divisor latch access.
46        field!(self.registers, lcr).write(Lcr::DLAB);
47
48        // Set the baud rate.
49        let divisor = serial_clock / (16 * baud_rate);
50        let fractional = (serial_clock % (16 * baud_rate)) / baud_rate;
51        field!(self.registers, dlf).write(fractional);
52        field!(self.registers, rbr_thr_dll).write(divisor & 0xff);
53        field!(self.registers, dlh_ier).write(divisor >> 8);
54
55        // Configure 8N1 and disable divisor latch access.
56        field!(self.registers, lcr).write(Lcr::DLS_8);
57
58        // Enable TX and RX FIFOs.
59        field!(self.registers, iir_fcr).write(Fcr::FIFOE.bits());
60    }
61
62    /// Returns whether the TX FIFO is currently full.
63    ///
64    /// If this returns true, `write_word` will block.
65    pub fn is_tx_fifo_full(&self) -> bool {
66        !field_shared!(self.registers, usr)
67            .read()
68            .contains(Usr::TFNF)
69    }
70
71    /// Writes a single byte to the UART.
72    ///
73    /// This blocks until there is room in the transmit FIFO to write the byte, but doesn't wait for
74    /// the byte to be transmitted.
75    pub fn write_word(&mut self, byte: u8) {
76        // Wait until the transmit FIFO has space.
77        while self.is_tx_fifo_full() {
78            spin_loop();
79        }
80
81        field!(self.registers, rbr_thr_dll).write(byte.into());
82    }
83
84    /// Blocks until all previously written bytes have been transmitted.
85    pub fn flush(&self) {
86        while !field_shared!(self.registers, usr).read().contains(Usr::TFE) {
87            spin_loop();
88        }
89    }
90
91    /// Returns true if the RX FIFO is currently empty.
92    ///
93    /// If this returns true then `read_word` will return `None`.
94    pub fn is_rx_fifo_empty(&self) -> bool {
95        !field_shared!(self.registers, usr)
96            .read()
97            .contains(Usr::RFNE)
98    }
99
100    /// Reads a single byte from the UART.
101    ///
102    /// If no data is available to be read this will return `Ok(None)`. If there is an error
103    /// condition then it will be cleared and an error will be returned.
104    pub fn read_word(&mut self) -> Result<Option<u8>, UartError> {
105        let lsr = field!(self.registers, lsr).read();
106
107        // The order of these checks is important. In particular, we must check for BI before FE or
108        // PE, as a framing error will also set those bits.
109        if lsr.contains(Lsr::BI) {
110            Err(UartError::Break)
111        } else if lsr.contains(Lsr::FE) {
112            Err(UartError::Framing)
113        } else if lsr.contains(Lsr::PE) {
114            Err(UartError::Parity)
115        } else if lsr.contains(Lsr::OE) {
116            Err(UartError::Overrun)
117        } else if !lsr.contains(Lsr::DR) {
118            Ok(None)
119        } else {
120            Ok(Some(field!(self.registers, rbr_thr_dll).read() as u8))
121        }
122    }
123}
124
125// SAFETY: A `&SynopsysUart` only allows operations which read registers, which can safely be done
126// from multiple threads simultaneously.
127unsafe impl Sync for SynopsysUart<'_> {}
128
129impl fmt::Write for SynopsysUart<'_> {
130    fn write_str(&mut self, s: &str) -> fmt::Result {
131        for byte in s.as_bytes() {
132            self.write_word(*byte);
133        }
134        Ok(())
135    }
136}
137
138/// A UART read error.
139#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
140pub enum UartError {
141    /// A framing error was detected by the receiver.
142    #[error("Framing error")]
143    Framing,
144    /// A parity error was detected by the receiver.
145    #[error("Parity error")]
146    Parity,
147    /// The receive FIFO overflowed and data was lost.
148    #[error("Overrun")]
149    Overrun,
150    /// A break sequence was detected.
151    #[error("Break")]
152    Break,
153}