use core::{num::NonZeroU32, ptr::NonNull};
use rdif_serial::{
InterruptMask, IrqSnapshot, IrqSource, RawUart, RxFlag, RxSample, SerialDirection, SerialEvent,
TransBytesError, TransferError,
};
use tock_registers::{
LocalRegisterCopy, interfaces::*, register_bitfields, register_structs, registers::*,
};
use crate::{Config, ConfigError, DataBits, Parity, StopBits};
register_bitfields! [
u32,
UARTDR [
DATA OFFSET(0) NUMBITS(8) [],
FE OFFSET(8) NUMBITS(1) [],
PE OFFSET(9) NUMBITS(1) [],
BE OFFSET(10) NUMBITS(1) [],
OE OFFSET(11) NUMBITS(1) []
],
UARTRSR_ECR [
FE OFFSET(0) NUMBITS(1) [],
PE OFFSET(1) NUMBITS(1) [],
BE OFFSET(2) NUMBITS(1) [],
OE OFFSET(3) NUMBITS(1) []
],
UARTFR [
CTS OFFSET(0) NUMBITS(1) [],
DSR OFFSET(1) NUMBITS(1) [],
DCD OFFSET(2) NUMBITS(1) [],
BUSY OFFSET(3) NUMBITS(1) [],
RXFE OFFSET(4) NUMBITS(1) [],
TXFF OFFSET(5) NUMBITS(1) [],
RXFF OFFSET(6) NUMBITS(1) [],
TXFE OFFSET(7) NUMBITS(1) [],
RI OFFSET(8) NUMBITS(1) []
],
UARTIBRD [
BAUD_DIVINT OFFSET(0) NUMBITS(16) []
],
UARTFBRD [
BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
],
UARTLCR_H [
BRK OFFSET(0) NUMBITS(1) [],
PEN OFFSET(1) NUMBITS(1) [],
EPS OFFSET(2) NUMBITS(1) [],
STP2 OFFSET(3) NUMBITS(1) [],
FEN OFFSET(4) NUMBITS(1) [],
WLEN OFFSET(5) NUMBITS(2) [
FiveBit = 0,
SixBit = 1,
SevenBit = 2,
EightBit = 3
],
SPS OFFSET(7) NUMBITS(1) []
],
UARTCR [
UARTEN OFFSET(0) NUMBITS(1) [],
SIREN OFFSET(1) NUMBITS(1) [],
SIRLP OFFSET(2) NUMBITS(1) [],
LBE OFFSET(7) NUMBITS(1) [],
TXE OFFSET(8) NUMBITS(1) [],
RXE OFFSET(9) NUMBITS(1) [],
DTR OFFSET(10) NUMBITS(1) [],
RTS OFFSET(11) NUMBITS(1) [],
OUT1 OFFSET(12) NUMBITS(1) [],
OUT2 OFFSET(13) NUMBITS(1) [],
RTSEN OFFSET(14) NUMBITS(1) [],
CTSEN OFFSET(15) NUMBITS(1) []
],
UARTIFLS [
TXIFLSEL OFFSET(0) NUMBITS(3) [],
RXIFLSEL OFFSET(3) NUMBITS(3) []
],
UARTIS [
RIM OFFSET(0) NUMBITS(1) [],
CTSM OFFSET(1) NUMBITS(1) [],
DCDM OFFSET(2) NUMBITS(1) [],
DSRM OFFSET(3) NUMBITS(1) [],
RX OFFSET(4) NUMBITS(1) [],
TX OFFSET(5) NUMBITS(1) [],
RT OFFSET(6) NUMBITS(1) [],
FE OFFSET(7) NUMBITS(1) [],
PE OFFSET(8) NUMBITS(1) [],
BE OFFSET(9) NUMBITS(1) [],
OE OFFSET(10) NUMBITS(1) []
],
UARTDMACR [
RXDMAE OFFSET(0) NUMBITS(1) [],
TXDMAE OFFSET(1) NUMBITS(1) [],
DMAONERR OFFSET(2) NUMBITS(1) []
]
];
register_structs! {
pub Pl011Registers {
(0x000 => uartdr: ReadWrite<u32, UARTDR::Register>), (0x004 => uartrsr_ecr: ReadWrite<u32, UARTRSR_ECR::Register>), (0x008 => _reserved1), (0x018 => uartfr: ReadOnly<u32, UARTFR::Register>), (0x01c => _reserved2), (0x020 => uartilpr: ReadWrite<u32>), (0x024 => uartibrd: ReadWrite<u32, UARTIBRD::Register>), (0x028 => uartfbrd: ReadWrite<u32, UARTFBRD::Register>), (0x02c => uartlcr_h: ReadWrite<u32, UARTLCR_H::Register>), (0x030 => uartcr: ReadWrite<u32, UARTCR::Register>), (0x034 => uartifls: ReadWrite<u32, UARTIFLS::Register>), (0x038 => uartimsc: ReadWrite<u32, UARTIS::Register>), (0x03c => uartris: ReadOnly<u32, UARTIS::Register>), (0x040 => uartmis: ReadOnly<u32, UARTIS::Register>), (0x044 => uarticr: WriteOnly<u32, UARTIS::Register>), (0x048 => uartdmacr: ReadWrite<u32, UARTDMACR::Register>), (0x04c => _reserved3), (0x1000 => @END),
}
}
unsafe impl Sync for Pl011Registers {}
pub struct Pl011 {
base: Reg,
clock_freq: u32,
saved_rx_status: Pl011RxStatus,
}
impl Pl011 {
pub fn new_no_clock(base: NonNull<u8>) -> Self {
let clock_freq = Self::detect_clock_frequency(base.as_ptr() as usize);
Self::new(base, clock_freq)
}
pub fn new(base: NonNull<u8>, clock_freq: u32) -> Self {
let base = Reg(base.cast());
Self {
base,
clock_freq,
saved_rx_status: Pl011RxStatus::empty(),
}
}
fn registers(&self) -> &Pl011Registers {
unsafe { &*self.base.0.as_ptr() }
}
fn detect_clock_frequency(base: usize) -> u32 {
let registers = unsafe { &*(base as *const Pl011Registers) };
use tock_registers::interfaces::Readable;
let ibrd = registers.uartibrd.read(UARTIBRD::BAUD_DIVINT);
if ibrd > 0 && ibrd <= 0xFFFF {
let estimated_clock = 16 * ibrd * 115200;
if (1_000_000..=100_000_000).contains(&estimated_clock) {
return estimated_clock;
}
}
24_000_000
}
fn set_baudrate_internal(&self, baudrate: u32) -> Result<(), ConfigError> {
let bauddiv = self.clock_freq / (16 * baudrate);
let remainder = self.clock_freq % (16 * baudrate);
let fbrd = (remainder * 64 + (16 * baudrate / 2)) / (16 * baudrate);
if bauddiv == 0 || bauddiv > 0xFFFF {
return Err(ConfigError::InvalidBaudrate);
}
self.registers()
.uartibrd
.write(UARTIBRD::BAUD_DIVINT.val(bauddiv));
self.registers()
.uartfbrd
.write(UARTFBRD::BAUD_DIVFRAC.val(fbrd));
Ok(())
}
fn set_data_bits_internal(&self, bits: DataBits) -> Result<(), ConfigError> {
let wlen = match bits {
DataBits::Five => UARTLCR_H::WLEN::FiveBit,
DataBits::Six => UARTLCR_H::WLEN::SixBit,
DataBits::Seven => UARTLCR_H::WLEN::SevenBit,
DataBits::Eight => UARTLCR_H::WLEN::EightBit,
};
self.registers().uartlcr_h.modify(wlen);
Ok(())
}
fn set_stop_bits_internal(&self, bits: StopBits) -> Result<(), ConfigError> {
match bits {
StopBits::One => self.registers().uartlcr_h.modify(UARTLCR_H::STP2::CLEAR),
StopBits::Two => self.registers().uartlcr_h.modify(UARTLCR_H::STP2::SET),
}
Ok(())
}
fn set_parity_internal(&self, parity: Parity) -> Result<(), ConfigError> {
match parity {
Parity::None => {
self.registers().uartlcr_h.modify(UARTLCR_H::PEN::CLEAR);
}
Parity::Odd => {
self.registers()
.uartlcr_h
.modify(UARTLCR_H::PEN::SET + UARTLCR_H::EPS::CLEAR + UARTLCR_H::SPS::CLEAR);
}
Parity::Even => {
self.registers()
.uartlcr_h
.modify(UARTLCR_H::PEN::SET + UARTLCR_H::EPS::SET + UARTLCR_H::SPS::CLEAR);
}
Parity::Mark => {
self.registers()
.uartlcr_h
.modify(UARTLCR_H::PEN::SET + UARTLCR_H::EPS::CLEAR + UARTLCR_H::SPS::SET);
}
Parity::Space => {
self.registers()
.uartlcr_h
.modify(UARTLCR_H::PEN::SET + UARTLCR_H::EPS::SET + UARTLCR_H::SPS::SET);
}
}
Ok(())
}
pub fn open(&mut self) {
self.registers().uartcr.modify(UARTCR::UARTEN::CLEAR);
while self.registers().uartfr.is_set(UARTFR::BUSY) {
core::hint::spin_loop();
}
self.registers().uartlcr_h.modify(UARTLCR_H::FEN::CLEAR);
self.registers().uartlcr_h.modify(UARTLCR_H::FEN::SET);
#[cfg(debug_assertions)]
{
let ifls = self.registers().uartifls.get();
let lcr_h = self.registers().uartlcr_h.get();
log::debug!("UART IFLS: 0x{:02x}, LCR_H: 0x{:02x}", ifls, lcr_h);
log::debug!(" FIFO enabled: {}", lcr_h & (1 << 4) != 0);
log::debug!(" RX trigger level: 1/8");
log::debug!(" TX trigger level: 1/2");
}
self.registers().uartimsc.set(0); self.registers()
.uartcr
.modify(UARTCR::UARTEN::SET + UARTCR::TXE::SET + UARTCR::RXE::SET);
}
pub fn set_irq_mask(&mut self, mask: InterruptMask) {
let mut imsc = 0;
if mask.intersects(InterruptMask::RX) {
imsc |= UARTIS::RX::SET.value
| UARTIS::RT::SET.value
| UARTIS::FE::SET.value
| UARTIS::PE::SET.value
| UARTIS::BE::SET.value
| UARTIS::OE::SET.value;
}
if mask.contains(InterruptMask::TX_SPACE) {
imsc |= UARTIS::TX::SET.value;
}
self.registers().uartimsc.set(imsc);
}
pub fn get_irq_mask(&self) -> InterruptMask {
let imsc = self.registers().uartimsc.extract();
let mut mask = InterruptMask::empty();
if imsc.is_set(UARTIS::RX)
|| imsc.is_set(UARTIS::RT)
|| imsc.is_set(UARTIS::FE)
|| imsc.is_set(UARTIS::PE)
|| imsc.is_set(UARTIS::BE)
|| imsc.is_set(UARTIS::OE)
{
mask |= InterruptMask::RX;
}
if imsc.is_set(UARTIS::TX) {
mask |= InterruptMask::TX_SPACE;
}
mask
}
pub fn pending(&mut self, direction: SerialDirection) -> bool {
match direction {
SerialDirection::Input => !self.registers().uartfr.is_set(UARTFR::RXFE),
SerialDirection::Output => !self.registers().uartfr.is_set(UARTFR::TXFF),
}
}
pub fn poll_status(&mut self) -> SerialEvent {
let mut event = SerialEvent::empty();
let fr = self.registers().uartfr.extract();
if !fr.is_set(UARTFR::RXFE) {
event |= SerialEvent::RX_READY;
}
if !fr.is_set(UARTFR::TXFF) {
event |= SerialEvent::TX_READY;
}
let status =
self.saved_rx_status | Pl011RxStatus::from_rsr(self.registers().uartrsr_ecr.extract());
if status.intersects(Pl011RxStatus::FRAMING | Pl011RxStatus::PARITY | Pl011RxStatus::BREAK)
{
event |= SerialEvent::RX_ERROR;
}
if status.contains(Pl011RxStatus::OVERRUN) {
event |= SerialEvent::RX_ERROR | SerialEvent::OVERRUN;
}
event
}
pub fn try_write(&mut self, bytes: &[u8]) -> usize {
let mut written = 0;
for &byte in bytes {
let status = self.poll_status();
if !status.tx_ready() {
break;
}
self.write_byte(byte);
written += 1;
}
written
}
pub fn try_read(&mut self, bytes: &mut [u8]) -> Result<usize, TransBytesError> {
let mut count = 0;
for byte in bytes.iter_mut() {
let status = self.poll_status();
if !status.rx_ready() && !status.rx_error() {
break;
}
match self.read_byte(status) {
Some(Ok(b)) => {
*byte = b;
}
Some(Err(TransferError::Overrun(b))) => {
*byte = b;
count += 1;
return Err(TransBytesError {
bytes_transferred: count,
kind: TransferError::Overrun(b),
});
}
Some(Err(e)) => {
return Err(TransBytesError {
bytes_transferred: count,
kind: e,
});
}
None => break,
}
count += 1;
}
Ok(count)
}
pub fn handle_irq(&mut self) -> SerialEvent {
serial_event_from_snapshot(self.take_irq_snapshot())
}
pub fn write_byte(&mut self, byte: u8) {
self.registers().uartdr.set(byte as _);
}
pub fn read_byte(&mut self, status: SerialEvent) -> Option<Result<u8, TransferError>> {
if !status.rx_ready() && !status.rx_error() {
return None;
}
let sample = self.read_rx()?;
if sample.overrun {
return Some(Err(TransferError::Overrun(sample.byte.unwrap_or(0))));
}
match sample.flag {
RxFlag::Normal => sample.byte.map(Ok),
RxFlag::Break => Some(Err(TransferError::Break)),
RxFlag::Parity => Some(Err(TransferError::Parity)),
RxFlag::Framing => Some(Err(TransferError::Framing)),
}
}
pub fn take_irq_snapshot(&mut self) -> IrqSnapshot {
let mis = self.registers().uartmis.extract();
let active = mis.get();
if active == 0 {
return IrqSnapshot::default();
}
let mut sources = IrqSource::empty();
if mis.is_set(UARTIS::RX) {
sources |= IrqSource::RX_DATA;
}
if mis.is_set(UARTIS::RT) {
sources |= IrqSource::RX_TIMEOUT;
}
if mis.is_set(UARTIS::FE)
|| mis.is_set(UARTIS::PE)
|| mis.is_set(UARTIS::BE)
|| mis.is_set(UARTIS::OE)
{
sources |= IrqSource::RX_STATUS;
self.saved_rx_status |= Pl011RxStatus::from_irq_status(mis);
}
if mis.is_set(UARTIS::TX) {
sources |= IrqSource::TX_SPACE;
}
if mis.is_set(UARTIS::CTSM)
|| mis.is_set(UARTIS::DSRM)
|| mis.is_set(UARTIS::DCDM)
|| mis.is_set(UARTIS::RIM)
{
sources |= IrqSource::MODEM_STATUS;
}
self.registers()
.uarticr
.set(active & !(UARTIS::RX::SET.value | UARTIS::RT::SET.value));
IrqSnapshot {
claimed: true,
sources,
}
}
pub fn read_rx(&mut self) -> Option<RxSample> {
if self.registers().uartfr.is_set(UARTFR::RXFE) {
self.saved_rx_status |= Pl011RxStatus::from_rsr(self.registers().uartrsr_ecr.extract());
return self.saved_rx_status.take_status_sample();
}
let dr = self.registers().uartdr.extract();
let data = dr.read(UARTDR::DATA) as u8;
let status = Pl011RxStatus::from_data(dr);
if !status.is_empty() {
self.saved_rx_status.remove(status);
}
let flag = if status.contains(Pl011RxStatus::BREAK) {
RxFlag::Break
} else if status.contains(Pl011RxStatus::PARITY) {
RxFlag::Parity
} else if status.contains(Pl011RxStatus::FRAMING) {
RxFlag::Framing
} else {
RxFlag::Normal
};
Some(RxSample {
byte: Some(data),
flag,
overrun: status.contains(Pl011RxStatus::OVERRUN),
})
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
struct Pl011RxStatus: u32 {
const FRAMING = 1 << 0;
const PARITY = 1 << 1;
const BREAK = 1 << 2;
const OVERRUN = 1 << 3;
}
}
impl Pl011RxStatus {
fn from_data(dr: LocalRegisterCopy<u32, UARTDR::Register>) -> Self {
let mut status = Self::empty();
if dr.is_set(UARTDR::FE) {
status |= Self::FRAMING;
}
if dr.is_set(UARTDR::PE) {
status |= Self::PARITY;
}
if dr.is_set(UARTDR::BE) {
status |= Self::BREAK;
}
if dr.is_set(UARTDR::OE) {
status |= Self::OVERRUN;
}
status
}
fn from_irq_status(mis: LocalRegisterCopy<u32, UARTIS::Register>) -> Self {
let mut status = Self::empty();
if mis.is_set(UARTIS::FE) {
status |= Self::FRAMING;
}
if mis.is_set(UARTIS::PE) {
status |= Self::PARITY;
}
if mis.is_set(UARTIS::BE) {
status |= Self::BREAK;
}
if mis.is_set(UARTIS::OE) {
status |= Self::OVERRUN;
}
status
}
fn from_rsr(rsr: LocalRegisterCopy<u32, UARTRSR_ECR::Register>) -> Self {
let mut status = Self::empty();
if rsr.is_set(UARTRSR_ECR::FE) {
status |= Self::FRAMING;
}
if rsr.is_set(UARTRSR_ECR::PE) {
status |= Self::PARITY;
}
if rsr.is_set(UARTRSR_ECR::BE) {
status |= Self::BREAK;
}
if rsr.is_set(UARTRSR_ECR::OE) {
status |= Self::OVERRUN;
}
status
}
fn flag(self) -> RxFlag {
if self.contains(Self::BREAK) {
RxFlag::Break
} else if self.contains(Self::PARITY) {
RxFlag::Parity
} else if self.contains(Self::FRAMING) {
RxFlag::Framing
} else {
RxFlag::Normal
}
}
fn take_status_sample(&mut self) -> Option<RxSample> {
if self.is_empty() {
return None;
}
let status = *self;
*self = Self::empty();
Some(RxSample {
byte: None,
flag: status.flag(),
overrun: status.contains(Self::OVERRUN),
})
}
}
fn serial_event_from_snapshot(snapshot: IrqSnapshot) -> SerialEvent {
let mut event = SerialEvent::empty();
if !snapshot.claimed {
return event;
}
if snapshot
.sources
.intersects(IrqSource::RX_DATA | IrqSource::RX_TIMEOUT)
{
event |= SerialEvent::RX_READY;
}
if snapshot.sources.contains(IrqSource::RX_STATUS) {
event |= SerialEvent::RX_ERROR;
}
if snapshot.sources.contains(IrqSource::TX_SPACE) {
event |= SerialEvent::TX_READY;
}
if snapshot.sources.contains(IrqSource::MODEM_STATUS) {
event |= SerialEvent::MODEM_STATUS;
}
event
}
#[derive(Clone, Copy, PartialEq, Eq)]
struct Reg(NonNull<Pl011Registers>);
unsafe impl Send for Reg {}
unsafe impl Sync for Reg {}
impl RawUart for Pl011 {
fn name(&self) -> &'static str {
"PL011 UART"
}
fn base_addr(&self) -> usize {
self.base.0.as_ptr() as usize
}
fn clock_freq(&self) -> Option<NonZeroU32> {
self.clock_freq.try_into().ok()
}
fn startup(&mut self, config: &Config) -> Result<(), ConfigError> {
self.open();
self.set_config(config)?;
self.set_irq_mask(InterruptMask::empty());
Ok(())
}
fn shutdown(&mut self) {
self.registers().uartimsc.set(0);
self.registers().uartcr.modify(UARTCR::UARTEN::CLEAR);
}
fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
use tock_registers::interfaces::Readable;
let original_cr = self.registers().uartcr.extract(); self.registers().uartcr.modify(UARTCR::UARTEN::CLEAR);
while self.registers().uartfr.is_set(UARTFR::BUSY) {
core::hint::spin_loop();
}
self.registers().uartlcr_h.modify(UARTLCR_H::FEN::CLEAR);
if let Some(baudrate) = config.baudrate {
self.set_baudrate_internal(baudrate)?;
}
if let Some(data_bits) = config.data_bits {
self.set_data_bits_internal(data_bits)?;
}
if let Some(stop_bits) = config.stop_bits {
self.set_stop_bits_internal(stop_bits)?;
}
if let Some(parity) = config.parity {
self.set_parity_internal(parity)?;
}
self.registers().uartlcr_h.modify(UARTLCR_H::FEN::SET);
if original_cr.is_set(UARTCR::UARTEN) {
self.registers().uartcr.modify(
UARTCR::UARTEN.val(original_cr.read(UARTCR::UARTEN))
+ UARTCR::TXE.val(original_cr.read(UARTCR::TXE))
+ UARTCR::RXE.val(original_cr.read(UARTCR::RXE)),
);
}
Ok(())
}
fn baudrate(&self) -> u32 {
let ibrd = self.registers().uartibrd.read(UARTIBRD::BAUD_DIVINT);
let fbrd = self.registers().uartfbrd.read(UARTFBRD::BAUD_DIVFRAC);
let divisor = ibrd * 64 + fbrd;
if divisor == 0 {
return 0;
}
self.clock_freq * 64 / (16 * divisor)
}
fn data_bits(&self) -> DataBits {
let wlen = self.registers().uartlcr_h.read(UARTLCR_H::WLEN);
match wlen {
0 => DataBits::Five,
1 => DataBits::Six,
2 => DataBits::Seven,
3 => DataBits::Eight,
_ => DataBits::Eight, }
}
fn stop_bits(&self) -> StopBits {
if self.registers().uartlcr_h.is_set(UARTLCR_H::STP2) {
StopBits::Two
} else {
StopBits::One
}
}
fn parity(&self) -> Parity {
if !self.registers().uartlcr_h.is_set(UARTLCR_H::PEN) {
Parity::None
} else if self.registers().uartlcr_h.is_set(UARTLCR_H::SPS) {
if self.registers().uartlcr_h.is_set(UARTLCR_H::EPS) {
Parity::Space
} else {
Parity::Mark
}
} else {
if self.registers().uartlcr_h.is_set(UARTLCR_H::EPS) {
Parity::Even
} else {
Parity::Odd
}
}
}
fn enable_loopback(&mut self) {
self.registers().uartcr.modify(UARTCR::LBE::SET);
}
fn disable_loopback(&mut self) {
self.registers().uartcr.modify(UARTCR::LBE::CLEAR);
}
fn is_loopback_enabled(&self) -> bool {
self.registers().uartcr.is_set(UARTCR::LBE)
}
fn set_irq_mask(&mut self, mask: InterruptMask) {
Pl011::set_irq_mask(self, mask);
}
fn take_irq_snapshot(&mut self) -> IrqSnapshot {
Pl011::take_irq_snapshot(self)
}
fn read_rx(&mut self) -> Option<RxSample> {
Pl011::read_rx(self)
}
fn tx_ready(&mut self) -> bool {
!self.registers().uartfr.is_set(UARTFR::TXFF)
}
fn write_tx(&mut self, byte: u8) {
Pl011::write_byte(self, byte);
}
fn tx_load_size(&self) -> usize {
16
}
fn tx_idle(&mut self) -> bool {
let fr = self.registers().uartfr.extract();
!fr.is_set(UARTFR::BUSY) && !fr.is_set(UARTFR::TXFF)
}
fn poll_status(&mut self) -> SerialEvent {
Pl011::poll_status(self)
}
fn write_byte(&mut self, byte: u8) {
Pl011::write_byte(self, byte)
}
fn read_byte(&mut self, status: SerialEvent) -> Option<Result<u8, TransferError>> {
Pl011::read_byte(self, status)
}
}
impl Pl011 {
pub fn enable_fifo(&self, enable: bool) {
if enable {
self.registers().uartlcr_h.modify(UARTLCR_H::FEN::SET);
} else {
self.registers().uartlcr_h.modify(UARTLCR_H::FEN::CLEAR);
}
}
pub fn set_fifo_trigger_level(&self, rx_level: u8, tx_level: u8) {
let rx_iflsel = match rx_level {
0..=2 => 0b000, 3..=4 => 0b001, 5..=8 => 0b010, 9..=12 => 0b011, _ => 0b100, };
let tx_iflsel = match tx_level {
0..=2 => 0b000, 3..=4 => 0b001, 5..=8 => 0b010, 9..=12 => 0b011, _ => 0b100, };
self.registers()
.uartifls
.write(UARTIFLS::RXIFLSEL.val(rx_iflsel) + UARTIFLS::TXIFLSEL.val(tx_iflsel));
}
}
#[cfg(test)]
mod tests {
use core::ptr::NonNull;
use std::boxed::Box;
use rdif_serial::{OwnerId, OwnerLease, SerialParts, SerialPort};
use super::*;
fn pl011_with_registers() -> (Box<Pl011Registers>, Pl011) {
let mut regs = Box::new(unsafe { core::mem::zeroed::<Pl011Registers>() });
let ptr = NonNull::from(regs.as_mut()).cast::<u8>();
let uart = Pl011::new(ptr, 24_000_000);
(regs, uart)
}
fn pl011_with_overrun_data() -> (Box<Pl011Registers>, Pl011) {
let (regs, uart) = pl011_with_registers();
regs.uartdr
.set((UARTDR::DATA.val(0xab) + UARTDR::OE::SET).into());
(regs, uart)
}
fn write_test_reg(regs: &mut Pl011Registers, offset: usize, value: u32) {
unsafe {
(regs as *mut Pl011Registers)
.cast::<u32>()
.add(offset / core::mem::size_of::<u32>())
.write_volatile(value);
}
}
fn read_test_reg(regs: &Pl011Registers, offset: usize) -> u32 {
unsafe {
(regs as *const Pl011Registers)
.cast::<u32>()
.add(offset / core::mem::size_of::<u32>())
.read_volatile()
}
}
fn owner_lease() -> OwnerLease<'static> {
unsafe { OwnerLease::new_unchecked(OwnerId(0)) }
}
fn started_parts(uart: Pl011) -> SerialParts<64, 64> {
let parts = SerialPort::<64, 64>::split(uart, OwnerId(0));
parts.port.startup(owner_lease(), &Config::new()).unwrap();
parts
}
#[test]
fn raw_rx_reports_overrun_instead_of_swallowing_it() {
let (_regs, mut uart) = pl011_with_overrun_data();
let mut buf = [0];
let err = uart
.try_read(&mut buf)
.expect_err("overrun must be reported to the caller");
assert_eq!(buf[0], 0xab);
assert_eq!(err.bytes_transferred, 1);
assert_eq!(err.kind, TransferError::Overrun(0xab));
}
#[test]
fn raw_rx_sample_reports_overrun_instead_of_swallowing_it() {
let (mut regs, uart) = pl011_with_overrun_data();
let mut uart = uart;
write_test_reg(&mut regs, 0x040, UARTIS::OE::SET.value);
let snapshot = uart.take_irq_snapshot();
assert!(snapshot.claimed);
assert!(snapshot.sources.contains(IrqSource::RX_STATUS));
let sample = uart.read_rx().expect("RX sample should be available");
assert_eq!(sample.byte, Some(0xab));
assert_eq!(sample.flag, RxFlag::Normal);
assert!(sample.overrun);
}
#[test]
fn irq_status_without_rx_byte_is_preserved_after_irq_ack() {
let (mut regs, mut uart) = pl011_with_registers();
write_test_reg(
&mut regs,
0x040,
UARTIS::OE::SET.value | UARTIS::PE::SET.value,
);
write_test_reg(&mut regs, 0x018, UARTFR::RXFE::SET.value);
let snapshot = uart.take_irq_snapshot();
assert!(snapshot.claimed);
assert!(snapshot.sources.contains(IrqSource::RX_STATUS));
let sample = uart.read_rx().expect("saved RX status should be available");
assert_eq!(sample.byte, None);
assert_eq!(sample.flag, RxFlag::Parity);
assert!(sample.overrun);
assert!(uart.read_rx().is_none());
}
#[test]
fn serial_core_tx_irq_drains_software_fifo() {
let (mut regs, uart) = pl011_with_registers();
let parts = started_parts(uart);
let mut tx = parts.tx;
let mut irq = parts.irq;
write_test_reg(&mut regs, 0x018, UARTFR::TXFF::SET.value);
assert_eq!(tx.submit(b"x").accepted, 1);
assert_eq!(tx.chars_in_buffer(), 1);
write_test_reg(&mut regs, 0x018, 0);
write_test_reg(&mut regs, 0x040, UARTIS::TX::SET.value);
let outcome = irq.handle(owner_lease());
assert!(outcome.claimed);
assert_eq!(outcome.tx_sent, 1);
assert_eq!(regs.uartdr.get() as u8, b'x');
assert_eq!(tx.chars_in_buffer(), 0);
}
#[test]
fn tx_irq_snapshot_acknowledges_tx_interrupt() {
let (mut regs, mut uart) = pl011_with_registers();
write_test_reg(&mut regs, 0x040, UARTIS::TX::SET.value);
let snapshot = uart.take_irq_snapshot();
assert!(snapshot.claimed);
assert!(snapshot.sources.contains(IrqSource::TX_SPACE));
assert_eq!(
read_test_reg(®s, 0x044) & UARTIS::TX::SET.value,
UARTIS::TX::SET.value
);
}
#[test]
fn set_config_preserves_enabled_tx_and_rx_paths() {
let (regs, mut uart) = pl011_with_registers();
regs.uartcr
.write(UARTCR::UARTEN::SET + UARTCR::TXE::SET + UARTCR::RXE::SET);
uart.set_config(&Config::new()).unwrap();
let cr = regs.uartcr.extract();
assert!(cr.is_set(UARTCR::UARTEN));
assert!(cr.is_set(UARTCR::TXE));
assert!(cr.is_set(UARTCR::RXE));
}
#[test]
fn rx_available_mask_enables_timeout_and_error_interrupts() {
let (regs, mut uart) = pl011_with_registers();
uart.set_irq_mask(InterruptMask::RX_AVAILABLE);
let imsc = regs.uartimsc.extract();
assert!(imsc.is_set(UARTIS::RX));
assert!(imsc.is_set(UARTIS::RT));
assert!(imsc.is_set(UARTIS::FE));
assert!(imsc.is_set(UARTIS::PE));
assert!(imsc.is_set(UARTIS::BE));
assert!(imsc.is_set(UARTIS::OE));
assert_eq!(uart.get_irq_mask(), InterruptMask::RX_AVAILABLE);
}
#[test]
fn hard_irq_does_not_claim_rx_ready_without_mis() {
let (mut regs, mut uart) = pl011_with_registers();
uart.set_irq_mask(InterruptMask::RX_AVAILABLE);
write_test_reg(&mut regs, 0x040, 0);
write_test_reg(&mut regs, 0x018, 0);
assert!(uart.handle_irq().is_empty());
}
#[test]
fn raw_rx_ready_is_visible_without_irq_snapshot() {
let (mut regs, mut uart) = pl011_with_registers();
uart.set_irq_mask(InterruptMask::RX_AVAILABLE);
write_test_reg(&mut regs, 0x040, 0);
write_test_reg(&mut regs, 0x018, 0);
regs.uartdr.set(UARTDR::DATA.val(b'r' as u32).into());
let status = uart.poll_status();
assert!(status.rx_ready());
let sample = uart.read_rx().expect("RX sample should be available");
assert_eq!(sample.byte, Some(b'r'));
assert_eq!(sample.flag, RxFlag::Normal);
}
}