227 lines
5.9 KiB
Rust

//! ARM PL011 driver
use abi::{error::Error, io::DeviceRequest};
use alloc::boxed::Box;
use device_api::{interrupt::InterruptHandler, serial::SerialDevice, Device};
use kernel_util::util::OneTimeInit;
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use vfs::CharDevice;
use crate::{
arch::{aarch64::IrqNumber, Architecture, ARCHITECTURE},
block,
debug::{self, DebugSink, LogLevel},
device::{
devtree::{self, DevTreeIndexPropExt},
tty::{TtyContext, TtyDevice},
},
device_tree_driver,
fs::devfs::{self, CharDeviceType},
mem::{address::FromRaw, device::DeviceMemoryIo, PhysicalAddress},
sync::IrqSafeSpinlock,
task::process::ProcessId,
};
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 Pl011Inner {
regs: DeviceMemoryIo<'static, Regs>,
}
/// PL011 device instance
pub struct Pl011 {
inner: OneTimeInit<IrqSafeSpinlock<Pl011Inner>>,
base: PhysicalAddress,
irq: IrqNumber,
context: TtyContext,
}
impl Pl011Inner {
fn send_byte(&mut self, b: u8) -> Result<(), Error> {
while self.regs.FR.matches_all(FR::TXFF::SET) {
core::hint::spin_loop();
}
self.regs.DR.set(b as u32);
Ok(())
}
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 DebugSink for Pl011 {
fn putc(&self, byte: u8) -> Result<(), Error> {
self.send(byte)
}
fn supports_control_sequences(&self) -> bool {
true
}
}
impl TtyDevice for Pl011 {
fn context(&self) -> &TtyContext {
&self.context
}
}
impl CharDevice for Pl011 {
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Error> {
assert!(blocking);
self.line_write(data)
}
fn read(&'static self, blocking: bool, data: &mut [u8]) -> Result<usize, Error> {
assert!(blocking);
match block! {
self.line_read(data).await
} {
Ok(res) => res,
Err(err) => Err(err),
}
}
fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
match req {
&mut DeviceRequest::SetTerminalGroup(id) => {
self.set_signal_group(ProcessId::from(id));
Ok(())
}
DeviceRequest::SetTerminalOptions(config) => self.context.set_config(config),
DeviceRequest::GetTerminalOptions(config) => {
config.write(self.context.config());
Ok(())
}
_ => Err(Error::InvalidArgument),
}
}
}
impl SerialDevice for Pl011 {
fn send(&self, byte: u8) -> Result<(), Error> {
self.inner.get().lock().send_byte(byte)
}
}
impl InterruptHandler for Pl011 {
fn handle_irq(&self) -> bool {
let inner = self.inner.get().lock();
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 Device for Pl011 {
fn display_name(&self) -> &'static str {
"Primecell PL011 UART"
}
unsafe fn init(&'static self) -> Result<(), Error> {
let mut inner = Pl011Inner {
regs: DeviceMemoryIo::map(self.base)?,
};
inner.init();
self.inner.init(IrqSafeSpinlock::new(inner));
debug::add_sink(self, LogLevel::Debug);
devfs::add_char_device(self, CharDeviceType::TtySerial)?;
Ok(())
}
unsafe fn init_irq(&'static self) -> Result<(), Error> {
let intc = ARCHITECTURE.external_interrupt_controller();
intc.register_irq(self.irq, Default::default(), self)?;
self.inner.get().lock().regs.IMSC.modify(IMSC::RXIM::SET);
intc.enable_irq(self.irq)?;
Ok(())
}
}
device_tree_driver! {
compatible: ["arm,pl011"],
probe(of) => {
let reg = devtree::find_prop(&of.node, "reg")?;
let (base, _) = reg.cell2_array_item(0, of.address_cells, of.size_cells)?;
Some(Box::new(Pl011 {
inner: OneTimeInit::new(),
// TODO obtain IRQ from dt
irq: IrqNumber::Shared(1),
context: TtyContext::new(),
base: PhysicalAddress::from_raw(base)
}))
}
}