feat: add sifive serial driver

This commit is contained in:
Paul Pan 2024-03-24 21:24:33 +08:00
parent 5032711022
commit ea15acb8f6
3 changed files with 181 additions and 3 deletions

View File

@ -3,4 +3,8 @@ use crate::plat::console::ConsoleDevice;
pub trait SerialDriver: Driver + ConsoleDevice {} pub trait SerialDriver: Driver + ConsoleDevice {}
pub mod uart16550; mod sifive;
mod uart16550;
pub use sifive::UartSifive;
pub use uart16550::Uart16550;

View File

@ -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<u32>,
rx_data: AtomicPtr<u32>,
tx_ctrl: AtomicPtr<u32>,
rx_ctrl: AtomicPtr<u32>,
ie: AtomicPtr<u32>,
ip: AtomicPtr<u32>,
div: AtomicPtr<u32>,
}
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<u8> {
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<u8> {
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 {}

View File

@ -1,7 +1,8 @@
use crate::arch::EarlyConsole; use crate::arch::EarlyConsole;
use crate::drivers::serial::uart16550::Uart16550; use crate::drivers::serial::{Uart16550, UartSifive};
use crate::drivers::Driver; use crate::drivers::Driver;
use fdt::node::FdtNode; use fdt::node::FdtNode;
use log::debug;
use spin::Mutex; use spin::Mutex;
macro_rules! create_console_driver { macro_rules! create_console_driver {
@ -20,6 +21,7 @@ macro_rules! create_console_driver {
.all() .all()
.any(|s| $dynamic_driver::compatible().contains(&s)) .any(|s| $dynamic_driver::compatible().contains(&s))
{ {
debug!("Console: Using driver: {}", stringify!($dynamic_driver));
return Some(ConsoleDriver::$dynamic_driver($dynamic_driver::setup(fdt))) 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 { impl core::fmt::Write for ConsoleDriver {
fn write_str(&mut self, s: &str) -> core::fmt::Result { fn write_str(&mut self, s: &str) -> core::fmt::Result {