yggdrasil/kernel/src/device/serial/snps_dw_apb_uart.rs

231 lines
6.3 KiB
Rust
Raw Normal View History

//! Synopsys DesignWare 8250 driver
use abi::{error::Error, io::TerminalOptions};
use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
interrupt::{FullIrq, InterruptHandler, IrqVector},
};
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,
IER [
PTIME OFFSET(7) NUMBITS(1) [],
EDSSI OFFSET(3) NUMBITS(1) [],
ELSI OFFSET(2) NUMBITS(1) [],
// Transmit buffer available
ETBEI OFFSET(1) NUMBITS(1) [],
// Receive data available
ERBFI OFFSET(0) NUMBITS(1) [],
],
LSR [
// Data ready bit
DR OFFSET(0) NUMBITS(1) [],
// Transmitter holding register empty
THRE OFFSET(5) NUMBITS(1) [],
]
}
register_structs! {
#[allow(non_snake_case)]
Regs {
// DLAB=0, Write: transmitter holding register/Read: receiver buffer register
// DLAB=1, Read/Write: divisor latch low
(0x000 => DR: ReadWrite<u32>),
// DLAB=0: Interrupt enable register
// DLAB=1: Divisor latch high
(0x004 => IER: ReadWrite<u32, IER::Register>),
// Read: interrupt identification register/Write: frame control register
(0x008 => IIR: ReadWrite<u32>),
// Line control register
(0x00C => LCR: ReadWrite<u32>),
// Modem control register
(0x010 => MCR: ReadWrite<u32>),
// Line status register
(0x014 => LSR: ReadOnly<u32, LSR::Register>),
// Modem status register
(0x018 => MSR: ReadOnly<u32>),
// Scratchpad
(0x01C => SCR: ReadWrite<u32>),
// Low-power divisor latch low
(0x020 => LPDLL: ReadWrite<u32>),
// Low-power divisor latch high
(0x024 => LPDLH: ReadWrite<u32>),
(0x028 => _0),
// Shadow receive/transmit buffer
(0x030 => SDR: [ReadWrite<u32>; 16]),
(0x070 => FAR: ReadWrite<u32>),
(0x074 => TFR: ReadOnly<u32>),
(0x078 => RFW: WriteOnly<u32>),
(0x07C => USR: ReadOnly<u32>),
(0x080 => TFL: ReadOnly<u32>),
(0x084 => RFL: ReadOnly<u32>),
(0x088 => SRR: WriteOnly<u32>),
(0x08C => SRTS: ReadWrite<u32>),
(0x090 => SBCR: ReadWrite<u32>),
(0x094 => SDMAM: ReadWrite<u32>),
(0x098 => SFE: ReadWrite<u32>),
(0x09C => SRT: ReadWrite<u32>),
(0x0A0 => STET: ReadWrite<u32>),
(0x0A4 => HTX: ReadWrite<u32>),
(0x0A8 => DMASA: WriteOnly<u32>),
(0x0AC => _1),
(0x0F4 => CPR: ReadOnly<u32>),
(0x0F8 => UCV: ReadOnly<u32>),
(0x0FC => CTR: ReadOnly<u32>),
(0x100 => @END),
}
}
struct Io {
regs: DeviceMemoryIo<'static, Regs>,
}
struct Inner {
io: IrqSafeSpinlock<Io>,
}
/// Synopsys DesignWare 8250 UART
pub struct DwUart {
base: PhysicalAddress,
irq: FullIrq,
inner: OneTimeInit<Arc<Terminal<Inner>>>,
}
impl Io {
fn send(&mut self, byte: u8) {
// TODO
if byte == b'\n' {
self.send(b'\r');
}
while !self.regs.LSR.matches_all(LSR::THRE::SET) {
core::hint::spin_loop();
}
self.regs.DR.set(byte as u32);
}
fn init(&mut self) {
self.regs.IER.set(0);
}
fn handle_irq(&mut self) -> Option<u8> {
let status = self.regs.IIR.get();
if status & 0xF == 4 {
Some(self.regs.DR.get() as u8)
} else {
None
}
}
}
impl InterruptHandler for DwUart {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let inner = self.inner.get();
let output = inner.output();
let byte = output.io.lock().handle_irq();
if let Some(byte) = byte {
inner.write_to_input(byte);
true
} else {
false
}
}
}
impl Device for DwUart {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let regs = DeviceMemoryIo::map(self.base, Default::default())?;
let mut io = Io { regs };
io.init();
let input = TerminalInput::with_capacity(64)?;
let output = Inner {
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, Default::default(), self.clone())?;
intc.enable_irq(self.irq.irq)?;
let output = self.inner.get().output();
let io = output.io.lock();
io.regs.IER.modify(IER::ERBFI::SET);
Ok(())
}
fn display_name(&self) -> &str {
"Synopsys DesignWare 8250 UART"
}
}
impl DebugSink for DwUart {
fn putc(&self, c: u8) -> Result<(), Error> {
self.inner.get().putc_to_output(c)
}
fn supports_control_sequences(&self) -> bool {
true
}
}
impl TerminalOutput for Inner {
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())
}
}
device_tree_driver! {
compatible: ["snps,dw-apb-uart"],
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(DwUart {
base,
irq,
inner: OneTimeInit::new()
}))
}
}
}