use super::*;
pub struct Pl011 {
pub(super) base: Reg,
clock_freq: u32,
pub(super) 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(),
}
}
pub(super) fn registers(&self) -> &Pl011Registers {
unsafe { &*self.base.0.as_ptr() }
}
pub(super) fn current_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 {
0
} else {
self.clock_freq * 64 / (16 * divisor)
}
}
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
}
pub(super) fn set_baudrate_internal(&self, baudrate: u32) -> Result<(), ConfigError> {
let scaled_baudrate = baudrate
.checked_mul(16)
.filter(|scaled| *scaled != 0)
.ok_or(ConfigError::InvalidBaudrate)?;
let bauddiv = self.clock_freq / scaled_baudrate;
let remainder = self.clock_freq % scaled_baudrate;
let fbrd = (remainder * 64 + (scaled_baudrate / 2)) / scaled_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(())
}
pub(super) fn wait_until_not_busy(&self) -> Result<(), ConfigError> {
for _ in 0..BUSY_POLL_BUDGET {
if !self.registers().uartfr.is_set(UARTFR::BUSY) {
return Ok(());
}
core::hint::spin_loop();
}
Err(ConfigError::Timeout)
}
pub(super) 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(())
}
pub(super) 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(())
}
pub(super) 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) -> Result<(), ConfigError> {
let original_cr = self.registers().uartcr.get();
self.registers().uartcr.modify(UARTCR::UARTEN::CLEAR);
if let Err(error) = self.wait_until_not_busy() {
self.registers().uartcr.set(original_cr);
return Err(error);
}
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);
Ok(())
}
pub fn set_irq_mask(&mut self, events: SerialEventSet) {
self.registers().uartimsc.set(imsc_for_events(events));
}
pub fn get_irq_mask(&self) -> SerialEventSet {
let imsc = self.registers().uartimsc.extract();
let mut events = SerialEventSet::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)
{
events |= SerialEventSet::RX;
}
if imsc.is_set(UARTIS::TX) {
events |= SerialEventSet::TX_SPACE;
}
events
}
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 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 read_rx(&mut self) -> Option<RxSample> {
let base = self.base;
let registers = unsafe { &*base.0.as_ptr() };
read_rx_sample(registers, &mut self.saved_rx_status)
}
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));
}
}