Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move the PL011 driver into the main workspace #1040

Merged
merged 14 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 8 additions & 6 deletions kernel/serial_port/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ pub fn init_serial_port(
serial_port_address: SerialPortAddress,
serial_port: SerialPortBasic,
) -> Option<&'static Arc<IrqSafeMutex<SerialPort>>> {
// Note: if we're called by device_manager, we cannot log (as we're modifying the logger config)

#[cfg(target_arch = "aarch64")]
if serial_port_address != SerialPortAddress::COM1 {
return None;
Expand Down Expand Up @@ -309,7 +311,7 @@ fn serial_port_receive_deferred(
let mut buf = DataChunk::empty();
let bytes_read;
let base_port;

let mut input_was_ignored = false;
let mut send_result = Ok(());

Expand All @@ -331,9 +333,6 @@ fn serial_port_receive_deferred(
// other than data being received, which is the only one we currently care about.
return Ok(());
}

#[cfg(target_arch = "aarch64")]
sp.enable_interrupt(SerialPortInterruptEvent::DataReceived, true);
}

if let Err(e) = send_result {
Expand Down Expand Up @@ -387,15 +386,18 @@ static INTERRUPT_ACTION_COM2_COM4: Once<Box<dyn Fn() + Send + Sync>> = Once::new

// Cross-platform interrupt handler for COM1 and COM3 (IRQ 0x24 on x86_64).
interrupt_handler!(com1_com3_interrupt_handler, Some(interrupts::IRQ_BASE_OFFSET + 0x4), _stack_frame, {
// trace!("COM1/COM3 serial handler");
// log::trace!("COM1/COM3 serial handler");

#[cfg(target_arch = "aarch64")] {
let mut sp = COM1_SERIAL_PORT.get().unwrap().as_ref().lock();
sp.enable_interrupt(SerialPortInterruptEvent::DataReceived, false);
sp.acknowledge_interrupt(SerialPortInterruptEvent::DataReceived);
}

if let Some(func) = INTERRUPT_ACTION_COM1_COM3.get() {
func()
}

// log::trace!("COM1/COM3 serial handler done");
EoiBehaviour::HandlerDidNotSendEoi
});

Expand Down
3 changes: 1 addition & 2 deletions kernel/serial_port_basic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ spin = "0.9.4"
port_io = { path = "../../libs/port_io" }

[target.'cfg(target_arch = "aarch64")'.dependencies]
pl011 = { git = "https://github.com/theseus-os/pl011/", rev = "464dbf22" }
uart_pl011 = { path = "../uart_pl011" }
arm_boards = { path = "../arm_boards" }
memory = { path = "../memory" }

[lib]
crate-type = ["rlib"]
29 changes: 14 additions & 15 deletions kernel/serial_port_basic/src/aarch64.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use memory::{MappedPages, PAGE_SIZE, map_frame_range, MMIO_FLAGS};
use super::{TriState, SerialPortInterruptEvent};
use arm_boards::BOARD_CONFIG;
use pl011::PL011;
use uart_pl011::Pl011;
use core::fmt;

/// The base port I/O addresses for COM serial ports.
Expand All @@ -19,11 +18,10 @@ pub enum SerialPortAddress {
}

/// A serial port and its various data and control registers.
#[derive(Debug)]
pub struct SerialPort {
port_address: SerialPortAddress,
inner: Option<PL011>,
// Owner of the MMIO frames for the PL011 registers
_mapped_pages: Option<MappedPages>,
inner: Option<Pl011>,
}

impl Drop for SerialPort {
Expand All @@ -33,7 +31,6 @@ impl Drop for SerialPort {
if let TriState::Taken = &*sp_locked {
let dummy = SerialPort {
inner: None,
_mapped_pages: None,
port_address: self.port_address,
};
let dropped = core::mem::replace(self, dummy);
Expand All @@ -57,19 +54,12 @@ impl SerialPort {
None => panic!("Board doesn't have {:?}", serial_port_address),
};

let mapped_pages = map_frame_range(*mmio_base, PAGE_SIZE, MMIO_FLAGS)
.expect("serial_port_basic: couldn't map the UART interface");
let addr = mapped_pages.start_address().value();
let mut pl011 = PL011::new(addr as *mut _);

pl011.enable_rx_interrupt(true);
pl011.set_fifo_mode(false);
// pl011.log_status();
let pl011 = Pl011::new(*mmio_base)
.expect("SerialPort::new: Couldn't initialize PL011 UART");

SerialPort {
port_address: serial_port_address,
inner: Some(pl011),
_mapped_pages: Some(mapped_pages),
}
}

Expand All @@ -84,6 +74,15 @@ impl SerialPort {
}
}

/// Clears an interrupt in the serial port controller
pub fn acknowledge_interrupt(&mut self, event: SerialPortInterruptEvent) {
if matches!(event, SerialPortInterruptEvent::DataReceived) {
self.inner.as_mut().unwrap().acknowledge_rx_interrupt();
} else {
unimplemented!()
}
}

/// Write the given string to the serial port, blocking until data can be transmitted.
///
/// # Special characters
Expand Down
5 changes: 5 additions & 0 deletions kernel/serial_port_basic/src/x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ impl SerialPort {
}
}

/// Clears an interrupt in the serial port controller
pub fn acknowledge_interrupt(&mut self, _event: SerialPortInterruptEvent) {
// no-op on x86_64
}

/// Write the given string to the serial port, blocking until data can be transmitted.
///
/// # Special characters
Expand Down
12 changes: 12 additions & 0 deletions kernel/uart_pl011/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "uart_pl011"
authors = ["Nathan Royer <[email protected]>"]
description = "Simple Driver for PL011 UARTs"
version = "0.1.0"
edition = "2021"

[dependencies]
log = "0.4.8"
volatile = "0.2.7"
zerocopy = "0.5.0"
memory = { path = "../memory" }
192 changes: 192 additions & 0 deletions kernel/uart_pl011/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
//! Driver for pl011 UARTs

#![no_std]
use core::fmt;
use zerocopy::FromBytes;
use volatile::{Volatile, ReadOnly, WriteOnly};
use memory::{BorrowedMappedPages, Mutable, PhysicalAddress, PAGE_SIZE, map_frame_range, MMIO_FLAGS};

/// Struct representing Pl011 registers. Not intended to be directly used
#[derive(Debug, FromBytes)]
#[repr(C)]
pub struct Pl011_Regs {
/// Data Register
pub uartdr: Volatile<u32>,
/// receive status / error clear
pub uartrsr: Volatile<u32>,
reserved0: [u32; 4],
/// flag register
pub uartfr: ReadOnly<u32>,
reserved1: u32,
/// IrDA Low power counter register
pub uartilpr: Volatile<u32>,
/// integer baud rate
pub uartibrd: Volatile<u32>,
/// fractional baud rate
pub uartfbrd: Volatile<u32>,
/// line control
pub uartlcr_h: Volatile<u32>,
/// control
pub uartcr: Volatile<u32>,
/// interrupt fifo level select
pub uartifls: Volatile<u32>,
/// interrupt mask set/clear
pub uartimsc: Volatile<u32>,
/// raw interrupt status
pub uartris: ReadOnly<u32>,
/// masked interrupt status
pub uartmis: ReadOnly<u32>,
/// interrupt clear
pub uarticr: WriteOnly<u32>,
/// dma control
pub uartdmacr: Volatile<u32>,
reserved2: [u32; 997],
/// UART Periph ID0
pub uartperiphid0: ReadOnly<u32>,
/// UART Periph ID1
pub uartperiphid1: ReadOnly<u32>,
/// UART Periph ID2
pub uartperiphid2: ReadOnly<u32>,
/// UART Periph ID3
pub uartperiphid3: ReadOnly<u32>,
/// UART PCell ID0
pub uartpcellid0: ReadOnly<u32>,
/// UART PCell ID1
pub uartpcellid1: ReadOnly<u32>,
/// UART PCell ID2
pub uartpcellid2: ReadOnly<u32>,
/// UART PCell ID3
pub uartpcellid3: ReadOnly<u32>,
}

const UARTIMSC_RXIM: u32 = 1 << 4;
const UARTUCR_RXIC: u32 = 1 << 4;

const UARTLCR_FEN: u32 = 1 << 4;

const UARTCR_RX_ENABLED: u32 = 1 << 9;
const UARTCR_TX_ENABLED: u32 = 1 << 8;
const UARTCR_UART_ENABLED: u32 = 1 << 0;

const UARTFR_RX_BUF_EMPTY: u32 = 1 << 4;
const UARTFR_TX_BUF_FULL: u32 = 1 << 5;

/// A Pl011 Single-Serial-Port Controller.
pub struct Pl011 {
regs: BorrowedMappedPages<Pl011_Regs, Mutable>
}

impl core::fmt::Debug for Pl011 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.regs.fmt(f)
}
}

/// Generic methods
impl Pl011 {
/// Initialize a UART driver.
pub fn new(base: PhysicalAddress) -> Result<Self, &'static str> {
let mapped_pages = map_frame_range(base, PAGE_SIZE, MMIO_FLAGS)?;

let mut this = Self {
regs: mapped_pages.into_borrowed_mut(0).map_err(|(_, e)| e)?,
};

this.enable_rx_interrupt(true);
this.set_fifo_mode(false);

Ok(this)
}

/// Enable on-receive interrupt
pub fn enable_rx_interrupt(&mut self, enable: bool) {
let mut reg = self.regs.uartimsc.read();

match enable {
true => reg |= UARTIMSC_RXIM,
false => reg &= !UARTIMSC_RXIM,
};

self.regs.uartimsc.write(reg);
}

pub fn acknowledge_rx_interrupt(&mut self) {
self.regs.uarticr.write(UARTUCR_RXIC);
}

/// Set FIFO mode
pub fn set_fifo_mode(&mut self, enable: bool) {
let mut reg = self.regs.uartlcr_h.read();

match enable {
true => reg |= UARTLCR_FEN,
false => reg &= !UARTLCR_FEN,
};

self.regs.uartlcr_h.write(reg);
}

/// Outputs a summary of the state of the controller using `log::info!()`
pub fn log_status(&self) {
let reg = self.regs.uartcr.read();
log::info!("RX enabled: {}", (reg & UARTCR_RX_ENABLED) > 0);
log::info!("TX enabled: {}", (reg & UARTCR_TX_ENABLED) > 0);
log::info!("UART enabled: {}", (reg & UARTCR_UART_ENABLED) > 0);
}

/// Returns true if the receive-buffer-empty flag is clear.
pub fn has_incoming_data(&self) -> bool {
let uartfr = self.regs.uartfr.read();
uartfr & UARTFR_RX_BUF_EMPTY == 0
}

/// Reads a single byte out the uart
///
/// Spins until a byte is available in the fifo.
pub fn read_byte(&self) -> u8 {
while !self.has_incoming_data() {}
self.regs.uartdr.read() as u8
}

/// Reads bytes into a slice until there is none available.
pub fn read_bytes(&self, bytes: &mut [u8]) -> usize {
let mut read = 0;

while read < bytes.len() && self.has_incoming_data() {
bytes[read] = self.read_byte();
read += 1;
}

read
}

/// Returns true if the transmit-buffer-full flag is clear.
pub fn is_writeable(&self) -> bool {
let uartfr = self.regs.uartfr.read();
uartfr & UARTFR_TX_BUF_FULL == 0
}

/// Writes a single byte out the uart.
///
/// Spins until space is available in the fifo.
pub fn write_byte(&mut self, data: u8) {
while !self.is_writeable() {}
self.regs.uartdr.write(data as u32);
}

/// Writes a byte slice out the uart.
///
/// Spins until space is available in the fifo.
pub fn write_bytes(&mut self, bytes: &[u8]) {
for b in bytes {
self.write_byte(*b);
}
}
}

impl fmt::Write for Pl011 {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write_bytes(s.as_bytes());
Ok(())
}
}