diff --git a/kernel/src/drivers/serial/mod.rs b/kernel/src/drivers/serial/mod.rs index daaaed0..dcca7ac 100644 --- a/kernel/src/drivers/serial/mod.rs +++ b/kernel/src/drivers/serial/mod.rs @@ -3,4 +3,8 @@ use crate::plat::console::ConsoleDevice; pub trait SerialDriver: Driver + ConsoleDevice {} -pub mod uart16550; +mod sifive; +mod uart16550; + +pub use sifive::UartSifive; +pub use uart16550::Uart16550; diff --git a/kernel/src/drivers/serial/sifive.rs b/kernel/src/drivers/serial/sifive.rs new file mode 100644 index 0000000..8d9e465 --- /dev/null +++ b/kernel/src/drivers/serial/sifive.rs @@ -0,0 +1,172 @@ +use crate::drivers::serial::SerialDriver; +use crate::drivers::Driver; +use crate::plat::console::ConsoleDevice; +use core::sync::atomic::{AtomicPtr, Ordering}; +use fdt::node::FdtNode; + +// https://static.dev.sifive.com/FU540-C000-v1.0.pdf + +pub struct UartSifive +where UartSifive: SerialDriver +{ + tx_data: AtomicPtr, + rx_data: AtomicPtr, + tx_ctrl: AtomicPtr, + rx_ctrl: AtomicPtr, + ie: AtomicPtr, + ip: AtomicPtr, + div: AtomicPtr, +} + +impl UartSifive { + fn tx_write(&self, val: u8) { + unsafe { self.tx_data.load(Ordering::Relaxed).write(val as u32) } + } + + fn tx_full(&self) -> bool { + unsafe { (self.tx_data.load(Ordering::Relaxed).read() >> 31) == 1 } + } + + fn rx_read(&self) -> u8 { + unsafe { self.rx_data.load(Ordering::Relaxed).read() as u8 } + } + + fn rx_full(&self) -> bool { + unsafe { (self.rx_data.load(Ordering::Relaxed).read() >> 31) == 0 } + } + + fn rx_try_read(&self) -> Option { + unsafe { + let val = self.rx_data.load(Ordering::Relaxed).read(); + if (val >> 31) == 0 { + Some(val as u8) + } else { + None + } + } + } + + fn tx_ctrl_enable(&self, enable: bool) { + unsafe { + let ctrl = self.tx_ctrl.load(Ordering::Relaxed); + let val = ctrl.read() & !1 | (enable as u32); + ctrl.write(val); + } + } + + fn tx_ctrl_stop_bit(&self, two: bool) { + unsafe { + let ctrl = self.tx_ctrl.load(Ordering::Relaxed); + let val = ctrl.read() & !2 | (two as u32) << 1; + ctrl.write(val); + } + } + + fn tx_ctrl_watermark(&self, watermark: u8) { + unsafe { + let ctrl = self.tx_ctrl.load(Ordering::Relaxed); + let val = ctrl.read() & !(0b111 << 16) | ((watermark as u32 & 0b111) << 16); + ctrl.write(val); + } + } + + fn rx_ctrl_enable(&self, enable: bool) { + unsafe { + let ctrl = self.rx_ctrl.load(Ordering::Relaxed); + let val = ctrl.read() & !1 | (enable as u32); + ctrl.write(val); + } + } + + fn rx_ctrl_watermark(&self, watermark: u8) { + unsafe { + let ctrl = self.rx_ctrl.load(Ordering::Relaxed); + let val = ctrl.read() & !(0b111 << 16) | ((watermark as u32 & 0b111) << 16); + ctrl.write(val); + } + } + + fn interrupt_tx_enable(&self, enable: bool) { + unsafe { + let ie = self.ie.load(Ordering::Relaxed); + let val = ie.read() & !1 | (enable as u32); + ie.write(val); + } + } + + fn interrupt_rx_enable(&self, enable: bool) { + unsafe { + let ie = self.ie.load(Ordering::Relaxed); + let val = ie.read() & !2 | (enable as u32) << 1; + ie.write(val); + } + } + + fn interrupt_tx_pending(&self) -> bool { + unsafe { (self.ip.load(Ordering::Relaxed).read() & 1) == 1 } + } + + fn interrupt_rx_pending(&self) -> bool { + unsafe { (self.ip.load(Ordering::Relaxed).read() & 2) == 2 } + } + + fn div_write(&self, val: u32) { + unsafe { self.div.load(Ordering::Relaxed).write(val & 0xffff) } + } +} + +impl Driver for UartSifive { + fn compatible() -> &'static [&'static str] { + &["sifive,uart0"] + } + + fn setup(fdt: FdtNode) -> Self { + let addr = fdt.reg().unwrap().next().unwrap().starting_address as *mut u32; + let uart = unsafe { + UartSifive { + tx_data: AtomicPtr::new(addr), + rx_data: AtomicPtr::new(addr.add(1)), + tx_ctrl: AtomicPtr::new(addr.add(2)), + rx_ctrl: AtomicPtr::new(addr.add(3)), + ie: AtomicPtr::new(addr.add(4)), + ip: AtomicPtr::new(addr.add(5)), + div: AtomicPtr::new(addr.add(6)), + } + }; + + // TODO: no interrupt & baud rate support now + uart.interrupt_tx_enable(false); + uart.interrupt_rx_enable(false); + uart.tx_ctrl_stop_bit(false); + + // Enable TX, RX + uart.tx_ctrl_enable(true); + uart.rx_ctrl_enable(true); + + uart + } +} + +impl ConsoleDevice for UartSifive { + fn read(&mut self) -> u8 { + loop { + if let Some(ch) = self.rx_try_read() { + return ch; + } + } + } + + fn try_read(&mut self) -> Option { + self.rx_try_read() + } + + fn write(&mut self, n: u8) { + while self.tx_full() { + core::hint::spin_loop(); + } + + self.tx_write(n); + } +} + +impl SerialDriver for UartSifive {} diff --git a/kernel/src/plat/console.rs b/kernel/src/plat/console.rs index b916a59..edee347 100644 --- a/kernel/src/plat/console.rs +++ b/kernel/src/plat/console.rs @@ -1,7 +1,8 @@ use crate::arch::EarlyConsole; -use crate::drivers::serial::uart16550::Uart16550; +use crate::drivers::serial::{Uart16550, UartSifive}; use crate::drivers::Driver; use fdt::node::FdtNode; +use log::debug; use spin::Mutex; macro_rules! create_console_driver { @@ -20,6 +21,7 @@ macro_rules! create_console_driver { .all() .any(|s| $dynamic_driver::compatible().contains(&s)) { + debug!("Console: Using driver: {}", stringify!($dynamic_driver)); return Some(ConsoleDriver::$dynamic_driver($dynamic_driver::setup(fdt))) } )+ @@ -60,7 +62,7 @@ macro_rules! create_console_driver { }; } -create_console_driver!((Uart16550), (SilenceConsole, EarlyConsole)); +create_console_driver!((Uart16550, UartSifive), (SilenceConsole, EarlyConsole)); impl core::fmt::Write for ConsoleDriver { fn write_str(&mut self, s: &str) -> core::fmt::Result {