196 lines
4.7 KiB
Rust
196 lines
4.7 KiB
Rust
//! ARM PL011 driver
|
|
use abi::{error::Error, io::TerminalOptions};
|
|
use alloc::sync::Arc;
|
|
use device_api::{
|
|
device::Device,
|
|
interrupt::{FullIrq, InterruptHandler},
|
|
};
|
|
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
|
use libk::{
|
|
debug::DebugSink,
|
|
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
|
|
vfs::{Terminal, TerminalInput, TerminalOutput},
|
|
};
|
|
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
|
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
|
use tock_registers::{
|
|
interfaces::{ReadWriteable, Readable, Writeable},
|
|
register_bitfields, register_structs,
|
|
registers::{ReadOnly, ReadWrite, WriteOnly},
|
|
};
|
|
|
|
register_bitfields! {
|
|
u32,
|
|
FR [
|
|
TXFF OFFSET(5) NUMBITS(1) [],
|
|
RXFE OFFSET(4) NUMBITS(1) [],
|
|
BUSY OFFSET(3) NUMBITS(1) [],
|
|
],
|
|
CR [
|
|
RXE OFFSET(9) NUMBITS(1) [],
|
|
TXE OFFSET(8) NUMBITS(1) [],
|
|
UARTEN OFFSET(0) NUMBITS(1) [],
|
|
],
|
|
ICR [
|
|
ALL OFFSET(0) NUMBITS(11) [],
|
|
],
|
|
IMSC [
|
|
RXIM OFFSET(4) NUMBITS(1) [],
|
|
]
|
|
}
|
|
|
|
register_structs! {
|
|
#[allow(non_snake_case)]
|
|
Regs {
|
|
/// Transmit/receive data register
|
|
(0x00 => DR: ReadWrite<u32>),
|
|
(0x04 => _0),
|
|
(0x18 => FR: ReadOnly<u32, FR::Register>),
|
|
(0x1C => _1),
|
|
(0x2C => LCR_H: ReadWrite<u32>),
|
|
(0x30 => CR: ReadWrite<u32, CR::Register>),
|
|
(0x34 => IFLS: ReadWrite<u32>),
|
|
(0x38 => IMSC: ReadWrite<u32, IMSC::Register>),
|
|
(0x3C => _2),
|
|
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
|
|
(0x48 => @END),
|
|
}
|
|
}
|
|
|
|
struct Io {
|
|
regs: DeviceMemoryIo<'static, Regs>,
|
|
}
|
|
|
|
struct Pl011Inner {
|
|
io: IrqSafeSpinlock<Io>,
|
|
}
|
|
|
|
/// PL011 device instance
|
|
pub struct Pl011 {
|
|
inner: OneTimeInit<Arc<Terminal<Pl011Inner>>>,
|
|
base: PhysicalAddress,
|
|
irq: FullIrq,
|
|
}
|
|
|
|
impl Io {
|
|
fn send(&mut self, byte: u8) {
|
|
while self.regs.FR.matches_all(FR::TXFF::SET) {
|
|
core::hint::spin_loop();
|
|
}
|
|
self.regs.DR.set(byte as u32);
|
|
}
|
|
|
|
unsafe fn init(&mut self) {
|
|
self.regs.CR.set(0);
|
|
self.regs.ICR.write(ICR::ALL::CLEAR);
|
|
self.regs
|
|
.CR
|
|
.write(CR::UARTEN::SET + CR::TXE::SET + CR::RXE::SET);
|
|
}
|
|
}
|
|
|
|
impl TerminalOutput for Pl011Inner {
|
|
fn write(&self, byte: u8) -> Result<(), Error> {
|
|
self.io.lock().send(byte);
|
|
Ok(())
|
|
}
|
|
|
|
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
|
let mut lock = self.io.lock();
|
|
for &byte in bytes {
|
|
lock.send(byte);
|
|
}
|
|
Ok(bytes.len())
|
|
}
|
|
}
|
|
|
|
impl Pl011Inner {
|
|
#[inline]
|
|
fn handle_irq(&self) -> u32 {
|
|
let lock = self.io.lock();
|
|
lock.regs.ICR.write(ICR::ALL::CLEAR);
|
|
lock.regs.DR.get()
|
|
}
|
|
}
|
|
|
|
impl DebugSink for Pl011 {
|
|
fn putc(&self, byte: u8) -> Result<(), Error> {
|
|
self.inner.get().putc_to_output(byte)
|
|
}
|
|
|
|
fn supports_control_sequences(&self) -> bool {
|
|
true
|
|
}
|
|
}
|
|
|
|
impl InterruptHandler for Pl011 {
|
|
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
|
|
let inner = self.inner.get();
|
|
let byte = inner.output().handle_irq();
|
|
|
|
inner.write_to_input(byte as u8);
|
|
|
|
true
|
|
}
|
|
}
|
|
|
|
impl Device for Pl011 {
|
|
fn display_name(&self) -> &'static str {
|
|
"Primecell PL011 UART"
|
|
}
|
|
|
|
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
|
|
let mut io = Io {
|
|
regs: DeviceMemoryIo::map(self.base, Default::default())?,
|
|
};
|
|
io.init();
|
|
|
|
let input = TerminalInput::with_capacity(64)?;
|
|
let output = Pl011Inner {
|
|
io: IrqSafeSpinlock::new(io),
|
|
};
|
|
|
|
let terminal = self.inner.init(Arc::new(Terminal::from_parts(
|
|
TerminalOptions::const_default(),
|
|
input,
|
|
output,
|
|
)));
|
|
|
|
DEVICE_REGISTRY
|
|
.serial_terminal
|
|
.register(terminal.clone(), Some(self.clone()))
|
|
.ok();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
|
let intc = external_interrupt_controller()?;
|
|
|
|
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
|
|
{
|
|
let io = self.inner.get().output().io.lock();
|
|
io.regs.IMSC.modify(IMSC::RXIM::SET);
|
|
}
|
|
intc.enable_irq(self.irq.irq)?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
device_tree_driver! {
|
|
compatible: ["arm,pl011"],
|
|
driver: {
|
|
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>> {
|
|
let base = node.map_base(context, 0)?;
|
|
let irq = node.interrupt(0)?;
|
|
|
|
Some(Arc::new(Pl011 {
|
|
base,
|
|
irq,
|
|
inner: OneTimeInit::new(),
|
|
}))
|
|
}
|
|
}
|
|
}
|