kernel/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
//! PL011 UART driver.
//!
//! # Resources
//!
//! - <https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf>
//! - <https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf>
//! - <https://datasheets.raspberrypi.com/rp1/rp1-peripherals.pdf>
//! - <https://developer.arm.com/documentation/ddi0183/latest>
//----------------------------------------------------------------------------
// Private Definitions
//----------------------------------------------------------------------------
use crate::{
bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization,
synchronization::NullLock,
};
use core::fmt;
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
// PL011 UART registers.
//
// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
register_bitfields! {
u32,
/// Flag Registers.
FR [
/// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// Line Control Register, LCR_H
///
/// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
/// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
/// - This bit does not indicate if there is data in the transmit shift register.
TXFE OFFSET(7) NUMBITS(1) [],
/// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
/// LCR_H Register.
///
/// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
/// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// LCR_H Register.
///
/// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
/// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) [],
/// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
/// set until the complete byte, including all the stop bits, has been sent from the shift
/// register.
///
/// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
/// the UART is enabled or not.
BUSY OFFSET(3) NUMBITS(1) []
],
/// Integer Baud Rate Divisor.
IBRD [
/// The integer baud rate divisor.
BAUD_DIVINT OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud Rate Divisor.
FBRD [
/// The fractional baud rate divisor.
BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
],
/// Line Control Register.
LCR_H [
/// Word length. These bits indicate the number of data bits transmitted or received in a
/// frame.
#[allow(clippy::enum_variant_names)]
WLEN OFFSET(5) NUMBITS(2) [
FiveBit = 0b00,
SixBit = 0b01,
SevenBit = 0b10,
EightBit = 0b11
],
/// Enable FIFOs:
///
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs beocme 1-bite-deep holding
/// register.
///
/// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
FEN OFFSET(4) NUMBITS(1) [
FifosDisabled = 0,
FifosEnabled = 1
]
],
/// Control Register.
CR [
/// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
/// Data reception occurs for either UART signals or SIR signals depending on the setting of
/// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
/// current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
/// Data transmission occurs for either UART signals, or SIR signals depending on the
/// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
/// completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable:
///
/// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
/// reception, it completes the current character before stopping.
///
/// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
/// or SIR signals depending on the setting of the SIREN bit
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission or reception, it completes the
/// current character before stopping.
Disabled = 0,
Enabled = 1
]
],
/// Interrupt Clear Registry.
ICR [
/// Meta field for all pending interrupts.
ALL OFFSET(0) NUMBITS(11) []
]
}
register_structs! {
#[allow(non_snake_case)]
pub RegisterBlock {
(0x00 => DR: ReadWrite<u32>),
(0x04 => _reserved1),
(0x18 => FR: ReadOnly<u32, FR::Register>),
(0x1c => _reserved2),
(0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
(0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
(0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
(0x30 => CR: WriteOnly<u32, CR::Register>),
(0x34 => _reserved3),
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
(0x48 => @END),
}
}
/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
struct PL011UartInner {
registers: Registers,
chars_written: usize,
chars_read: usize,
}
//----------------------------------------------------------------------------
// Public Definitions
//----------------------------------------------------------------------------
/// Representation of the UART.
pub struct PL011Uart {
inner: NullLock<PL011UartInner>,
}
//----------------------------------------------------------------------------
// Private Code
//----------------------------------------------------------------------------
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
impl PL011UartInner {
pub const unsafe fn new(mmio_start_addr: usize) -> Self {
Self {
registers: Registers::new(mmio_start_addr),
chars_written: 0,
chars_read: 0,
}
}
/// Set up baud rate and characteristics.
///
/// This result in 8N1 and 921_600 baud.
///
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
///
/// This means the integer part is `3` and goes into the `IBRD`.
/// The fractional part is `0.2552083`.
///
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
///
/// Therefore, the generated baud rate divdier is: `3 + 16/64 = 3.25`. Which results in a
/// generated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
pub fn init(&mut self) {
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
//
// For example, this can happen during runtime on a call to panic!(), because panic!()
// initializes its own UART instance and calls init().
//
// Hence, flush first to ensure all pending characters are transmitted.
self.flush();
// Turn the UART off temporarily.
self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
// From the PL011 Technical Reference Manual:
//
// The LCR_H, IBRD and FBRD registers form the single 30-bit wide LCR Register that is
// updated on a single write strobe generated by a LCR_H write. So. to internally update the
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
//
// Set the baud rate, 8N1 and FIFO enabled.
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
self.registers
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
// Turn the UART on.
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
}
/// Send a character.
fn write_char(&mut self, c: char) {
// Spin while TX FIFO full is set, waiting for an empty slot.
while self.registers.FR.matches_all(FR::TXFF::SET) {
cpu::nop();
}
// Write the character to the buffer.
self.registers.DR.set(c as u32);
self.chars_written += 1;
}
/// Block execution until the last bufferred character has been physically put on the TX wire.
fn flush(&self) {
while self.registers.FR.matches_all(FR::BUSY::SET) {
cpu::nop();
}
}
/// Retrieve a character.
fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<char> {
// If RX FIFO is empty,
if self.registers.FR.matches_all(FR::RXFE::SET) {
// immediately return in non-blocking mode.
if blocking_mode == BlockingMode::NonBlocking {
return None;
}
// Otherwise, wait until a char was received.
while self.registers.FR.matches_all(FR::RXFE::SET) {
cpu::nop();
}
}
// Read one character.
let mut ret = self.registers.DR.get() as u8 as char;
// Convert carrige return to newline.
if ret == '\r' {
ret = '\n'
}
// Update statistics.
self.chars_read += 1;
Some(ret)
}
}
/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are
/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`,
/// we get `write_fmt()` automatically.
///
/// The function takes an `&mut self`, so it must be implemented for the inner struct.
///
/// See [`src/print.rs`].
///
/// [`src/print.rs`]: ../../print/index.html
impl fmt::Write for PL011UartInner {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
self.write_char(c);
}
Ok(())
}
}
//----------------------------------------------------------------------------
// Public Code
//----------------------------------------------------------------------------
impl PL011Uart {
pub const COMPATIBL: &'static str = "BCM PL011 UART";
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
pub const unsafe fn new(mmio_start_addr: usize) -> Self {
Self {
inner: NullLock::new(PL011UartInner::new(mmio_start_addr)),
}
}
}
//----------------------------------------------------------------------------
// Operating System Interface Code
//----------------------------------------------------------------------------
use synchronization::interface::Mutex;
impl driver::interface::DeviceDriver for PL011Uart {
fn compatible(&self) -> &'static str {
Self::COMPATIBL
}
unsafe fn init(&self) -> Result<(), &'static str> {
self.inner.lock(|inner| inner.init());
Ok(())
}
}
impl console::interface::Write for PL011Uart {
/// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to
/// serialize access.
fn write_char(&self, c: char) {
self.inner.lock(|inner| inner.write_char(c));
}
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result {
// Fully qualified syntax for the call to `core::fmt::Write::write_fmt()` to increase
// readability.
self.inner.lock(|inner| fmt::Write::write_fmt(inner, args))
}
fn flush(&self) {
// Spin until TX FIFO empty is set.
self.inner.lock(|inner| inner.flush())
}
}
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner
.lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
}
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
.lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
.is_some()
{}
}
}
impl console::interface::Statistics for PL011Uart {
fn chars_written(&self) -> usize {
self.inner.lock(|inner| inner.chars_written)
}
fn chars_read(&self) -> usize {
self.inner.lock(|inner| inner.chars_read)
}
}
impl console::interface::All for PL011Uart {}