git-subtree-dir: kernel git-subtree-mainline: 817f71f90f97270dd569fd44246bf74e57636552 git-subtree-split: 7f1f6b73377367db17f98a740316b904c37ce3b1
207 lines
5.4 KiB
Rust
207 lines
5.4 KiB
Rust
//! 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<u32>),
|
|
(0x04 => IER: ReadWrite<u32, IER::Register>),
|
|
(0x08 => IIR: ReadWrite<u32, IIR::Register>),
|
|
(0x0C => _0),
|
|
(0x7C => USR: ReadOnly<u32, USR::Register>),
|
|
(0x80 => @END),
|
|
}
|
|
}
|
|
|
|
struct Inner {
|
|
regs: DeviceMemoryIo<Regs>,
|
|
}
|
|
|
|
struct SunxiUart {
|
|
inner: OneTimeInit<IrqSafeSpinlock<Inner>>,
|
|
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<usize, Error> {
|
|
self.line_read(data)
|
|
}
|
|
|
|
fn write(&self, _blocking: bool, data: &[u8]) -> Result<usize, Error> {
|
|
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::<Regs>::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
|
|
}
|
|
}
|
|
}
|