Add mini_uart implementation
This commit is contained in:
		
							parent
							
								
									7a46d7d993
								
							
						
					
					
						commit
						cecddea41e
					
				|  | @ -12,6 +12,12 @@ version = "1.2.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "cfg-if" | ||||
| version = "1.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "cortex-a" | ||||
| version = "3.0.5" | ||||
|  | @ -71,6 +77,7 @@ version = "0.0.1" | |||
| dependencies = [ | ||||
|  "bit_field", | ||||
|  "bitflags", | ||||
|  "cfg-if", | ||||
|  "cortex-a", | ||||
|  "qemu-exit", | ||||
|  "r0", | ||||
|  |  | |||
|  | @ -30,3 +30,4 @@ usize_conversions = "0.2.0" | |||
| bit_field = "0.10.0" | ||||
| register = "0.5.1" | ||||
| bitflags = "1.2.1" | ||||
| cfg-if = "1.0" | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| 
 | ||||
| #![allow(dead_code)] | ||||
| 
 | ||||
| use crate::platform; | ||||
| use core::fmt; | ||||
| 
 | ||||
| /// A trait that must be implemented by devices that are candidates for the
 | ||||
|  | @ -28,6 +29,7 @@ impl ConsoleOps for NullConsole {} | |||
| /// Possible outputs which the console can store.
 | ||||
| pub enum Output { | ||||
|     None(NullConsole), | ||||
|     MiniUart(platform::rpi3::mini_uart::PreparedMiniUart), | ||||
| } | ||||
| 
 | ||||
| /// Generate boilerplate for converting into one of Output enum values
 | ||||
|  | @ -40,6 +42,8 @@ macro output_from($name:ty, $optname:ident) { | |||
| } | ||||
| 
 | ||||
| output_from!(NullConsole, None); | ||||
| output_from!(platform::rpi3::mini_uart::PreparedMiniUart, MiniUart); | ||||
| 
 | ||||
| pub struct Console { | ||||
|     output: Output, | ||||
| } | ||||
|  | @ -63,6 +67,7 @@ impl Console { | |||
|     fn current_ptr(&self) -> &dyn ConsoleOps { | ||||
|         match &self.output { | ||||
|             Output::None(i) => i, | ||||
|             Output::MiniUart(i) => i, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| 
 | ||||
| use { | ||||
|     super::BcmHost, | ||||
|     crate::arch::loop_delay, | ||||
|     core::{marker::PhantomData, ops}, | ||||
|     register::{ | ||||
|         mmio::{ReadOnly, ReadWrite, WriteOnly}, | ||||
|  | @ -130,6 +131,19 @@ impl GPIO { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn enable_uart_pins(gpio: &GPIO) { | ||||
|     gpio.PUD.set(0); | ||||
| 
 | ||||
|     loop_delay(150); | ||||
| 
 | ||||
|     // enable pins 14 and 15
 | ||||
|     gpio.PUDCLK[0].write(PUDCLK0::PUDCLK14::AssertClock + PUDCLK0::PUDCLK15::AssertClock); | ||||
| 
 | ||||
|     loop_delay(150); | ||||
| 
 | ||||
|     gpio.PUDCLK[0].set(0); | ||||
| } | ||||
| 
 | ||||
| /// An alternative GPIO function.
 | ||||
| #[repr(u8)] | ||||
| pub enum Function { | ||||
|  |  | |||
|  | @ -0,0 +1,278 @@ | |||
| /* | ||||
|  * SPDX-License-Identifier: MIT OR BlueOak-1.0.0 | ||||
|  * Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com> | ||||
|  * Copyright (c) Berkus Decker <berkus+vesper@metta.systems> | ||||
|  * Original code distributed under MIT, additional changes are under BlueOak-1.0.0 | ||||
|  */ | ||||
| 
 | ||||
| use { | ||||
|     super::{gpio, BcmHost}, | ||||
|     crate::devices::ConsoleOps, | ||||
|     cfg_if::cfg_if, | ||||
|     core::{convert::From, fmt, ops}, | ||||
|     register::{mmio::*, register_bitfields}, | ||||
| }; | ||||
| 
 | ||||
| // Auxiliary mini UART registers
 | ||||
| //
 | ||||
| // Descriptions taken from
 | ||||
| // https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
 | ||||
| register_bitfields! { | ||||
|     u32, | ||||
| 
 | ||||
|     /// Auxiliary enables
 | ||||
|     AUX_ENABLES [ | ||||
|         /// If set the mini UART is enabled. The UART will immediately
 | ||||
|         /// start receiving data, especially if the UART1_RX line is
 | ||||
|         /// low.
 | ||||
|         /// If clear the mini UART is disabled. That also disables any
 | ||||
|         /// mini UART register access
 | ||||
|         MINI_UART_ENABLE OFFSET(0) NUMBITS(1) [] | ||||
|     ], | ||||
| 
 | ||||
|     /// Mini Uart Interrupt Identify
 | ||||
|     AUX_MU_IIR [ | ||||
|         /// Writing with bit 1 set will clear the receive FIFO
 | ||||
|         /// Writing with bit 2 set will clear the transmit FIFO
 | ||||
|         FIFO_CLEAR OFFSET(1) NUMBITS(2) [ | ||||
|             Rx = 0b01, | ||||
|             Tx = 0b10, | ||||
|             All = 0b11 | ||||
|         ] | ||||
|     ], | ||||
| 
 | ||||
|     /// Mini Uart Line Control
 | ||||
|     AUX_MU_LCR [ | ||||
|         /// Mode the UART works in
 | ||||
|         DATA_SIZE OFFSET(0) NUMBITS(2) [ | ||||
|             SevenBit = 0b00, | ||||
|             EightBit = 0b11 | ||||
|         ] | ||||
|     ], | ||||
| 
 | ||||
|     /// Mini Uart Line Status
 | ||||
|     AUX_MU_LSR [ | ||||
|         /// This bit is set if the transmit FIFO is empty and the transmitter is
 | ||||
|         /// idle. (Finished shifting out the last bit).
 | ||||
|         TX_IDLE    OFFSET(6) NUMBITS(1) [], | ||||
| 
 | ||||
|         /// This bit is set if the transmit FIFO can accept at least
 | ||||
|         /// one byte.
 | ||||
|         TX_EMPTY   OFFSET(5) NUMBITS(1) [], | ||||
| 
 | ||||
|         /// This bit is set if the receive FIFO holds at least 1
 | ||||
|         /// symbol.
 | ||||
|         DATA_READY OFFSET(0) NUMBITS(1) [] | ||||
|     ], | ||||
| 
 | ||||
|     /// Mini Uart Extra Control
 | ||||
|     AUX_MU_CNTL [ | ||||
|         /// If this bit is set the mini UART transmitter is enabled.
 | ||||
|         /// If this bit is clear the mini UART transmitter is disabled.
 | ||||
|         TX_EN OFFSET(1) NUMBITS(1) [ | ||||
|             Disabled = 0, | ||||
|             Enabled = 1 | ||||
|         ], | ||||
| 
 | ||||
|         /// If this bit is set the mini UART receiver is enabled.
 | ||||
|         /// If this bit is clear the mini UART receiver is disabled.
 | ||||
|         RX_EN OFFSET(0) NUMBITS(1) [ | ||||
|             Disabled = 0, | ||||
|             Enabled = 1 | ||||
|         ] | ||||
|     ], | ||||
| 
 | ||||
|     /// Mini Uart Baudrate
 | ||||
|     AUX_MU_BAUD [ | ||||
|         /// Mini UART baudrate counter
 | ||||
|         RATE OFFSET(0) NUMBITS(16) [] | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| #[allow(non_snake_case)] | ||||
| #[repr(C)] | ||||
| pub struct RegisterBlock { | ||||
|     __reserved_0: u32,                                  // 0x00 - AUX_IRQ?
 | ||||
|     AUX_ENABLES: ReadWrite<u32, AUX_ENABLES::Register>, // 0x04
 | ||||
|     __reserved_1: [u32; 14],                            // 0x08
 | ||||
|     AUX_MU_IO: ReadWrite<u32>,                          // 0x40 - Mini Uart I/O Data
 | ||||
|     AUX_MU_IER: WriteOnly<u32>,                         // 0x44 - Mini Uart Interrupt Enable
 | ||||
|     AUX_MU_IIR: WriteOnly<u32, AUX_MU_IIR::Register>,   // 0x48
 | ||||
|     AUX_MU_LCR: WriteOnly<u32, AUX_MU_LCR::Register>,   // 0x4C
 | ||||
|     AUX_MU_MCR: WriteOnly<u32>,                         // 0x50
 | ||||
|     AUX_MU_LSR: ReadOnly<u32, AUX_MU_LSR::Register>,    // 0x54
 | ||||
|     __reserved_2: [u32; 2],                             // 0x58 - AUX_MU_MSR, AUX_MU_SCRATCH
 | ||||
|     AUX_MU_CNTL: WriteOnly<u32, AUX_MU_CNTL::Register>, // 0x60
 | ||||
|     __reserved_3: u32,                                  // 0x64 - AUX_MU_STAT
 | ||||
|     AUX_MU_BAUD: WriteOnly<u32, AUX_MU_BAUD::Register>, // 0x68
 | ||||
| } | ||||
| 
 | ||||
| pub struct MiniUart { | ||||
|     base_addr: usize, | ||||
| } | ||||
| 
 | ||||
| pub struct PreparedMiniUart(MiniUart); | ||||
| 
 | ||||
| /// Divisor values for common baud rates
 | ||||
| pub enum Rate { | ||||
|     Baud115200 = 270, | ||||
| } | ||||
| 
 | ||||
| impl From<Rate> for u32 { | ||||
|     fn from(r: Rate) -> Self { | ||||
|         r as u32 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Deref to RegisterBlock
 | ||||
| ///
 | ||||
| /// Allows writing
 | ||||
| /// ```
 | ||||
| /// self.MU_IER.read()
 | ||||
| /// ```
 | ||||
| /// instead of something along the lines of
 | ||||
| /// ```
 | ||||
| /// unsafe { (*MiniUart::ptr()).MU_IER.read() }
 | ||||
| /// ```
 | ||||
| impl ops::Deref for MiniUart { | ||||
|     type Target = RegisterBlock; | ||||
| 
 | ||||
|     fn deref(&self) -> &Self::Target { | ||||
|         unsafe { &*self.ptr() } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // [temporary] Used in mmu.rs to set up local paging
 | ||||
| pub const UART1_BASE: usize = BcmHost::get_peripheral_address() + 0x21_5000; | ||||
| 
 | ||||
| impl Default for MiniUart { | ||||
|     fn default() -> MiniUart { | ||||
|         MiniUart::new(UART1_BASE) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl MiniUart { | ||||
|     pub fn new(base_addr: usize) -> MiniUart { | ||||
|         MiniUart { base_addr } | ||||
|     } | ||||
| 
 | ||||
|     /// Returns a pointer to the register block
 | ||||
|     fn ptr(&self) -> *const RegisterBlock { | ||||
|         self.base_addr as *const _ | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl MiniUart { | ||||
|     cfg_if! { | ||||
|         if #[cfg(not(feature = "noserial"))] { | ||||
|             /// Set baud rate and characteristics (115200 8N1) and map to GPIO
 | ||||
|             pub fn prepare(self, gpio: &gpio::GPIO) -> PreparedMiniUart { | ||||
|                 // initialize UART
 | ||||
|                 self.AUX_ENABLES.modify(AUX_ENABLES::MINI_UART_ENABLE::SET); | ||||
|                 self.AUX_MU_IER.set(0); | ||||
|                 self.AUX_MU_CNTL.set(0); | ||||
|                 self.AUX_MU_LCR.write(AUX_MU_LCR::DATA_SIZE::EightBit); | ||||
|                 self.AUX_MU_MCR.set(0); | ||||
|                 self.AUX_MU_IER.set(0); | ||||
|                 self.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All); | ||||
|                 self.AUX_MU_BAUD | ||||
|                     .write(AUX_MU_BAUD::RATE.val(Rate::Baud115200.into())); | ||||
| 
 | ||||
|                 // Pin 14
 | ||||
|                 const MINI_UART_TXD: gpio::Function = gpio::Function::Alt5; | ||||
|                 // Pin 15
 | ||||
|                 const MINI_UART_RXD: gpio::Function = gpio::Function::Alt5; | ||||
| 
 | ||||
|                 // map UART1 to GPIO pins
 | ||||
|                 gpio.get_pin(14).into_alt(MINI_UART_TXD); | ||||
|                 gpio.get_pin(15).into_alt(MINI_UART_RXD); | ||||
| 
 | ||||
|                 gpio::enable_uart_pins(gpio); | ||||
| 
 | ||||
|                 self.AUX_MU_CNTL | ||||
|                     .write(AUX_MU_CNTL::RX_EN::Enabled + AUX_MU_CNTL::TX_EN::Enabled); | ||||
| 
 | ||||
|                 // Clear FIFOs before using the device
 | ||||
|                 self.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All); | ||||
| 
 | ||||
|                 PreparedMiniUart(self) | ||||
|             } | ||||
|         } else { | ||||
|             pub fn prepare(self, _gpio: &gpio::GPIO) -> PreparedMiniUart { | ||||
|                 PreparedMiniUart(self) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Drop for PreparedMiniUart { | ||||
|     fn drop(&mut self) { | ||||
|         self.0 | ||||
|             .AUX_ENABLES | ||||
|             .modify(AUX_ENABLES::MINI_UART_ENABLE::CLEAR); | ||||
|         // @todo disable gpio.PUD ?
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ConsoleOps for PreparedMiniUart { | ||||
|     cfg_if! { | ||||
|         if #[cfg(not(feature = "noserial"))] { | ||||
|             /// Send a character
 | ||||
|             fn putc(&self, c: char) { | ||||
|                 // wait until we can send
 | ||||
|                 crate::arch::loop_until(|| self.0.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY)); | ||||
| 
 | ||||
|                 // write the character to the buffer
 | ||||
|                 self.0.AUX_MU_IO.set(c as u32); | ||||
|             } | ||||
| 
 | ||||
|             /// Display a string
 | ||||
|             fn puts(&self, string: &str) { | ||||
|                 for c in string.chars() { | ||||
|                     // convert newline to carriage return + newline
 | ||||
|                     if c == '\n' { | ||||
|                         self.putc('\r') | ||||
|                     } | ||||
| 
 | ||||
|                     self.putc(c); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             /// Receive a character
 | ||||
|             fn getc(&self) -> char { | ||||
|                 // wait until something is in the buffer
 | ||||
|                 crate::arch::loop_until(|| self.0.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY)); | ||||
| 
 | ||||
|                 // read it and return
 | ||||
|                 let mut ret = self.0.AUX_MU_IO.get() as u8 as char; | ||||
| 
 | ||||
|                 // convert carriage return to newline
 | ||||
|                 if ret == '\r' { | ||||
|                     ret = '\n' | ||||
|                 } | ||||
| 
 | ||||
|                 ret | ||||
|             } | ||||
| 
 | ||||
|             /// Wait until the TX FIFO is empty, aka all characters have been put on the
 | ||||
|             /// line.
 | ||||
|             fn flush(&self) { | ||||
|                 crate::arch::loop_until(|| self.0.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_IDLE)); | ||||
|             } | ||||
|         } else { | ||||
|             fn putc(&self, _c: char) {} | ||||
|             fn puts(&self, _string: &str) {} | ||||
|             fn getc(&self) -> char { | ||||
|                 '\n' | ||||
|             } | ||||
|             fn flush(&self) {} | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Write for PreparedMiniUart { | ||||
|     fn write_str(&mut self, s: &str) -> fmt::Result { | ||||
|         self.puts(s); | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | @ -8,6 +8,7 @@ | |||
| pub mod fb; | ||||
| pub mod gpio; | ||||
| pub mod mailbox; | ||||
| pub mod mini_uart; | ||||
| 
 | ||||
| /// See BCM2835-ARM-Peripherals.pdf
 | ||||
| /// See https://www.raspberrypi.org/forums/viewtopic.php?t=186090 for more details.
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue