//! Allwinner (H6) UART implementation use abi::{error::Error, io::DeviceRequest}; use alloc::boxed::Box; use device_api::{interrupt::InterruptHandler, serial::SerialDevice, Device}; use libk::util::OneTimeInit; use tock_registers::{ interfaces::{ReadWriteable, Readable, Writeable}, register_bitfields, register_structs, registers::{ReadOnly, ReadWrite}, }; use vfs::CharDevice; use crate::{ arch::{aarch64::IrqNumber, Architecture, PLATFORM}, debug::{self, DebugSink, LogLevel}, device::{ devtree::{self, DevTreeIndexPropExt}, tty::{CharRing, TtyDevice}, }, device_tree_driver, fs::devfs::{self, CharDeviceType}, mem::device::DeviceMemoryIo, sync::IrqSafeSpinlock, }; register_bitfields! { u32, USR [ TFE OFFSET(2) NUMBITS(1) [], TFNF OFFSET(1) NUMBITS(1) [] ], IER [ ERBFI OFFSET(0) NUMBITS(1) [], ], IIR [ IID OFFSET(0) NUMBITS(4) [ RecvDataAvailable = 0b0100, ] ] } register_structs! { #[allow(non_snake_case)] Regs { (0x00 => DLL: ReadWrite), (0x04 => IER: ReadWrite), (0x08 => IIR: ReadWrite), (0x0C => _0), (0x7C => USR: ReadOnly), (0x80 => @END), } } struct Inner { regs: DeviceMemoryIo, } struct SunxiUart { inner: OneTimeInit>, base: usize, irq: IrqNumber, ring: CharRing<16>, } impl DebugSink for SunxiUart { fn putc(&self, c: u8) -> Result<(), Error> { self.send(c) } fn supports_control_sequences(&self) -> bool { true } } impl CharDevice for SunxiUart { fn read(&'static self, _blocking: bool, data: &mut [u8]) -> Result { self.line_read(data) } fn write(&self, _blocking: bool, data: &[u8]) -> Result { self.line_write(data) } fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> { match req { &mut DeviceRequest::SetTerminalGroup(id) => { self.set_signal_group(id as _); Ok(()) } _ => Err(Error::InvalidArgument), } } } impl TtyDevice<16> for SunxiUart { fn ring(&self) -> &CharRing<16> { &self.ring } } impl InterruptHandler for SunxiUart { fn handle_irq(&self) -> bool { let inner = self.inner.get().lock(); if inner.regs.IIR.matches_all(IIR::IID::RecvDataAvailable) { let byte = inner.regs.DLL.get(); drop(inner); if byte == b'\x1b' as u32 { panic!("RESET TRIGGERED"); } self.recv_byte(byte as u8); } // inner.regs.ICR.write(ICR::ALL::CLEAR); // let byte = inner.regs.DR.get(); // drop(inner); // if byte == b'\x1b' as u32 { // use crate::task::sched::CpuQueue; // for (i, queue) in CpuQueue::all().enumerate() { // log_print_raw!(LogLevel::Fatal, "queue{}:\n", i); // let lock = unsafe { queue.grab() }; // for item in lock.iter() { // log_print_raw!( // LogLevel::Fatal, // "* {} {:?} {:?}\n", // item.id(), // item.name(), // item.state() // ); // } // } // } else { // self.recv_byte(byte as u8); // } true } } impl SerialDevice for SunxiUart { fn send(&self, byte: u8) -> Result<(), Error> { let inner = self.inner.get().lock(); if byte == b'\n' { while inner.regs.USR.matches_all(USR::TFE::CLEAR) { core::hint::spin_loop(); } inner.regs.DLL.set(b'\r' as u32); } while inner.regs.USR.matches_all(USR::TFE::CLEAR) { core::hint::spin_loop(); } inner.regs.DLL.set(byte as u32); Ok(()) } } impl Device for SunxiUart { fn display_name(&self) -> &'static str { "Allwinner UART" } unsafe fn init(&'static self) -> Result<(), Error> { let regs = DeviceMemoryIo::::map("sunxi-uart", self.base)?; self.inner.init(IrqSafeSpinlock::new(Inner { regs })); debug::add_sink(self, LogLevel::Debug); devfs::add_char_device(self, CharDeviceType::TtySerial)?; Ok(()) } unsafe fn init_irq(&'static self) -> Result<(), Error> { let intc = PLATFORM.external_interrupt_controller(); intc.register_irq(self.irq, Default::default(), self)?; intc.enable_irq(self.irq)?; let inner = self.inner.get().lock(); inner.regs.IER.modify(IER::ERBFI::SET); Ok(()) } } device_tree_driver! { compatible: ["snps,dw-apb-uart"], probe(of) => { let reg = devtree::find_prop(&of.node, "reg")?; let (base, _) = reg.cell2_array_item(0, of.address_cells, of.size_cells)?; if base == 0x05000000 { Some(Box::new(SunxiUart { inner: OneTimeInit::new(), ring: CharRing::new(), irq: IrqNumber::Shared(0), base: base as usize })) } else { // TODO don't just hardcode and ignore other UARTs None } } }