//! 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), // DLAB=0: Interrupt enable register // DLAB=1: Divisor latch high (0x004 => IER: ReadWrite), // Read: interrupt identification register/Write: frame control register (0x008 => IIR: ReadWrite), // Line control register (0x00C => LCR: ReadWrite), // Modem control register (0x010 => MCR: ReadWrite), // Line status register (0x014 => LSR: ReadOnly), // Modem status register (0x018 => MSR: ReadOnly), // Scratchpad (0x01C => SCR: ReadWrite), // Low-power divisor latch low (0x020 => LPDLL: ReadWrite), // Low-power divisor latch high (0x024 => LPDLH: ReadWrite), (0x028 => _0), // Shadow receive/transmit buffer (0x030 => SDR: [ReadWrite; 16]), (0x070 => FAR: ReadWrite), (0x074 => TFR: ReadOnly), (0x078 => RFW: WriteOnly), (0x07C => USR: ReadOnly), (0x080 => TFL: ReadOnly), (0x084 => RFL: ReadOnly), (0x088 => SRR: WriteOnly), (0x08C => SRTS: ReadWrite), (0x090 => SBCR: ReadWrite), (0x094 => SDMAM: ReadWrite), (0x098 => SFE: ReadWrite), (0x09C => SRT: ReadWrite), (0x0A0 => STET: ReadWrite), (0x0A4 => HTX: ReadWrite), (0x0A8 => DMASA: WriteOnly), (0x0AC => _1), (0x0F4 => CPR: ReadOnly), (0x0F8 => UCV: ReadOnly), (0x0FC => CTR: ReadOnly), (0x100 => @END), } } struct Io { regs: DeviceMemoryIo<'static, Regs>, } struct Inner { io: IrqSafeSpinlock, } /// Synopsys DesignWare 8250 UART pub struct DwUart { base: PhysicalAddress, irq: FullIrq, inner: OneTimeInit>>, } 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 { 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, _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, _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) -> 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 { 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, context: &ProbeContext) -> Option> { let base = node.map_base(context, 0)?; let irq = node.interrupt(0)?; Some(Arc::new(DwUart { base, irq, inner: OneTimeInit::new() })) } } }