From 517a78d1144b9342ec93bde821a30a29abeee8cc Mon Sep 17 00:00:00 2001 From: equation314 Date: Mon, 29 Oct 2018 13:25:29 +0800 Subject: [PATCH] aarch64: implement mini UART for bcm2837 --- crate/bcm2837/src/asm.rs | 4 +- crate/bcm2837/src/mini_uart.rs | 116 +++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/crate/bcm2837/src/asm.rs b/crate/bcm2837/src/asm.rs index 1c2b449..a496d92 100644 --- a/crate/bcm2837/src/asm.rs +++ b/crate/bcm2837/src/asm.rs @@ -2,9 +2,9 @@ /// delay for some clocks #[inline] -pub unsafe fn delay(clock: u32) { +pub unsafe fn delay(_clock: u32) { #[cfg(target_arch = "aarch64")] asm!("mov x1, x0; 1: subs x1, x1, #1; bne 1b;" - :: "{x0}"(clock) + :: "{x0}"(_clock) :: "volatile"); } diff --git a/crate/bcm2837/src/mini_uart.rs b/crate/bcm2837/src/mini_uart.rs index db28245..d4dcf35 100644 --- a/crate/bcm2837/src/mini_uart.rs +++ b/crate/bcm2837/src/mini_uart.rs @@ -1 +1,117 @@ use super::IO_BASE; +use gpio::{Function, Gpio}; +use volatile::{ReadOnly, Volatile}; + +/// The base address for the `MU` registers. +const MU_REG_BASE: usize = IO_BASE + 0x215040; + +/// The `AUXENB` register from page 9 of the BCM2837 documentation. +const AUX_ENABLES: *mut Volatile = (IO_BASE + 0x215004) as *mut Volatile; + +/// Enum representing bit fields of the `AUX_MU_LSR_REG` register. +#[repr(u8)] +enum LsrStatus { + DataReady = 1, + TxAvailable = 1 << 5, +} + +#[repr(C)] +#[allow(non_snake_case)] +struct Registers { + AUX_MU_IO_REG: Volatile, + __r0: [u8; 3], + AUX_MU_IER_REG: Volatile, + __r1: [u8; 3], + AUX_MU_IIR_REG: Volatile, + __r2: [u8; 3], + AUX_MU_LCR_REG: Volatile, + __r3: [u8; 3], + AUX_MU_MCR_REG: Volatile, + __r4: [u8; 3], + AUX_MU_LSR_REG: ReadOnly, + __r5: [u8; 3], + AUX_MU_MSR_REG: ReadOnly, + __r6: [u8; 3], + AUX_MU_SCRATCH: Volatile, + __r7: [u8; 3], + AUX_MU_CNTL_REG: Volatile, + __r8: [u8; 3], + AUX_MU_STAT_REG: ReadOnly, + AUX_MU_BAUD_REG: Volatile, +} + +/// The Raspberry Pi's "mini UART". +pub struct MiniUart { + registers: &'static mut Registers, + timeout: Option, +} + +impl MiniUart { + /// Initializes the mini UART by enabling it as an auxiliary peripheral, + /// setting the data size to 8 bits, setting the BAUD rate to ~115200 (baud + /// divider of 270), setting GPIO pins 14 and 15 to alternative function 5 + /// (TXD1/RDXD1), and finally enabling the UART transmitter and receiver. + /// + /// By default, reads will never time out. To set a read timeout, use + /// `set_read_timeout()`. + pub fn new() -> MiniUart { + let registers = unsafe { + // Enable the mini UART as an auxiliary device. + (*AUX_ENABLES).write(1); + &mut *(MU_REG_BASE as *mut Registers) + }; + + Gpio::new(14).into_alt(Function::Alt5).set_gpio_pd(0); + Gpio::new(15).into_alt(Function::Alt5).set_gpio_pd(0); + + registers.AUX_MU_CNTL_REG.write(0); // Disable auto flow control and disable receiver and transmitter (for now) + registers.AUX_MU_IER_REG.write(0); // Disable receive and transmit interrupts + registers.AUX_MU_LCR_REG.write(3); // Enable 8 bit mode + registers.AUX_MU_MCR_REG.write(0); // Set RTS line to be always high + registers.AUX_MU_BAUD_REG.write(270); // Set baud rate to 115200 + + registers.AUX_MU_CNTL_REG.write(3); // Finally, enable transmitter and receiver + + MiniUart { + registers: registers, + timeout: None, + } + } + + /// Set the read timeout to `milliseconds` milliseconds. + pub fn set_read_timeout(&mut self, milliseconds: u32) { + self.timeout = Some(milliseconds) + } + + /// Write the byte `byte`. This method blocks until there is space available + /// in the output FIFO. + pub fn write_byte(&mut self, byte: u8) { + while self.registers.AUX_MU_LSR_REG.read() & (LsrStatus::TxAvailable as u8) == 0 {} + self.registers.AUX_MU_IO_REG.write(byte); + } + + /// Returns `true` if there is at least one byte ready to be read. If this + /// method returns `true`, a subsequent call to `read_byte` is guaranteed to + /// return immediately. This method does not block. + pub fn has_byte(&self) -> bool { + self.registers.AUX_MU_LSR_REG.read() & (LsrStatus::DataReady as u8) != 0 + } + + /// Blocks until there is a byte ready to read. If a read timeout is set, + /// this method blocks for at most that amount of time. Otherwise, this + /// method blocks indefinitely until there is a byte to read. + /// + /// Returns `Ok(())` if a byte is ready to read. Returns `Err(())` if the + /// timeout expired while waiting for a byte to be ready. If this method + /// returns `Ok(())`, a subsequent call to `read_byte` is guaranteed to + /// return immediately. + pub fn wait_for_byte(&self) -> Result<(), ()> { + unimplemented!() + } + + /// Reads a byte. Blocks indefinitely until a byte is ready to be read. + pub fn read_byte(&mut self) -> u8 { + while !self.has_byte() {} + self.registers.AUX_MU_IO_REG.read() + } +}