use core::ptr::NonNull;
use tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
register_structs! {
Pl011UartRegs {
(0x00 => dr: ReadWrite<u32>),
(0x04 => _reserved0),
(0x18 => fr: ReadOnly<u32>),
(0x1c => _reserved1),
(0x30 => cr: ReadWrite<u32>),
(0x34 => ifls: ReadWrite<u32>),
(0x38 => imsc: ReadWrite<u32>),
(0x3c => ris: ReadOnly<u32>),
(0x40 => mis: ReadOnly<u32>),
(0x44 => icr: WriteOnly<u32>),
(0x48 => @END),
}
}
pub struct Pl011Uart {
base: NonNull<Pl011UartRegs>,
}
unsafe impl Send for Pl011Uart {}
unsafe impl Sync for Pl011Uart {}
impl Pl011Uart {
pub const fn new(base: *mut u8) -> Self {
Self {
base: NonNull::new(base).unwrap().cast(),
}
}
const fn regs(&self) -> &Pl011UartRegs {
unsafe { self.base.as_ref() }
}
pub fn init(&mut self) {
self.regs().icr.set(0x7ff);
self.regs().ifls.set(0);
self.regs().imsc.set(1 << 4);
self.regs().cr.set((1 << 0) | (1 << 8) | (1 << 9)); }
pub fn putchar(&mut self, c: u8) {
while !self.try_putchar(c) {}
}
pub fn tx_fifo_full(&self) -> bool {
self.regs().fr.get() & (1 << 5) != 0
}
pub fn try_putchar(&mut self, c: u8) -> bool {
if self.tx_fifo_full() {
false
} else {
self.regs().dr.set(c as u32);
true
}
}
pub fn getchar(&mut self) -> Option<u8> {
if self.regs().fr.get() & (1 << 4) == 0 {
Some(self.regs().dr.get() as u8)
} else {
None
}
}
pub fn is_receive_interrupt(&self) -> bool {
let pending = self.regs().mis.get();
pending & (1 << 4) != 0
}
pub fn ack_interrupts(&mut self) {
self.regs().icr.set(0x7ff);
}
}
#[cfg(test)]
mod tests {
use super::*;
const REGS_WORDS: usize = 0x48 / size_of::<u32>();
const FR_WORD: usize = 0x18 / size_of::<u32>();
const DR_WORD: usize = 0x00 / size_of::<u32>();
const FR_TXFF: u32 = 1 << 5;
fn uart_at(regs: &mut [u32; REGS_WORDS]) -> Pl011Uart {
Pl011Uart::new(regs.as_mut_ptr() as *mut u8)
}
#[test]
fn tx_fifo_full_follows_fr_txff() {
let mut regs = [0u32; REGS_WORDS];
let uart = uart_at(&mut regs);
assert!(!uart.tx_fifo_full());
regs[FR_WORD] = FR_TXFF;
assert!(uart.tx_fifo_full());
}
#[test]
fn try_putchar_writes_when_fifo_has_space() {
let mut regs = [0u32; REGS_WORDS];
let mut uart = uart_at(&mut regs);
assert!(uart.try_putchar(b'A'));
assert_eq!(regs[DR_WORD], u32::from(b'A'));
}
#[test]
fn try_putchar_fails_without_writing_when_fifo_full() {
let mut regs = [0u32; REGS_WORDS];
regs[FR_WORD] = FR_TXFF;
let mut uart = uart_at(&mut regs);
assert!(!uart.try_putchar(b'A'));
assert_eq!(regs[DR_WORD], 0);
}
#[test]
fn putchar_writes_when_fifo_has_space() {
let mut regs = [0u32; REGS_WORDS];
let mut uart = uart_at(&mut regs);
uart.putchar(b'A');
assert_eq!(regs[DR_WORD], u32::from(b'A'));
}
}