serial: unify 8250 drivers, better dts support
This commit is contained in:
@@ -35,6 +35,7 @@ ygg_driver_ahci = { path = "driver/block/ahci" }
|
||||
ygg_driver_input = { path = "driver/input" }
|
||||
ygg_driver_usb_xhci.path = "driver/usb/xhci"
|
||||
ygg_driver_net_rtl81xx.path = "driver/net/rtl81xx"
|
||||
ygg_driver_serial_8250.path = "driver/serial/uart8250"
|
||||
|
||||
memfs = { path = "driver/fs/memfs" }
|
||||
ext2 = { path = "driver/fs/ext2" }
|
||||
|
||||
@@ -16,7 +16,7 @@ use kernel_arch_interface::{
|
||||
};
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
process::ProcessAddressSpaceManager,
|
||||
process::{PageAttributeUpdate, ProcessAddressSpaceManager},
|
||||
table::{MapAttributes, TableAllocator},
|
||||
};
|
||||
use yggdrasil_abi::{
|
||||
@@ -164,6 +164,14 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
unsafe fn update_page_attributes(
|
||||
&mut self,
|
||||
_address: usize,
|
||||
_update: &PageAttributeUpdate,
|
||||
) -> Result<(), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn translate(&self, _address: usize) -> Result<(PhysicalAddress, MapAttributes), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// TODO baud rate configuration
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
@@ -16,7 +17,10 @@ use tock_registers::{
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use yggdrasil_abi::{error::Error, io::TerminalOptions};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{TerminalOptions, TerminalOutputOptions},
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
@@ -89,14 +93,25 @@ impl Io {
|
||||
}
|
||||
|
||||
impl TerminalOutput for Pl011Inner {
|
||||
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||
self.io.lock().send(byte);
|
||||
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
|
||||
let mut lock = self.io.lock();
|
||||
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
|
||||
lock.send(b'\r');
|
||||
}
|
||||
lock.send(b'\n');
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
fn write_multiple(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
options: &TerminalOutputOptions,
|
||||
) -> Result<usize, Error> {
|
||||
let mut lock = self.io.lock();
|
||||
for &byte in bytes {
|
||||
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
|
||||
lock.send(b'\r');
|
||||
}
|
||||
lock.send(byte);
|
||||
}
|
||||
Ok(bytes.len())
|
||||
@@ -117,6 +132,11 @@ impl DebugSink for Pl011 {
|
||||
self.inner.get().putc_to_output(byte)
|
||||
}
|
||||
|
||||
fn puts(&self, s: &str) -> Result<(), Error> {
|
||||
self.inner.get().write_to_output(s.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn supports_control_sequences(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
@@ -68,10 +68,6 @@ pub struct Bcm2835AuxUart {
|
||||
|
||||
impl Regs {
|
||||
fn write_byte(&self, byte: u8) -> Result<(), Error> {
|
||||
if byte == b'\n' {
|
||||
self.write_byte(b'\r').ok();
|
||||
}
|
||||
|
||||
while !self
|
||||
.AUX_MU_LSR_REG
|
||||
.matches_all(AUX_MU_LSR_REG::TX_EMPTY::SET)
|
||||
@@ -81,22 +77,29 @@ impl Regs {
|
||||
self.AUX_MU_IO_REG.set(byte as u32);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_bytes(&self, bytes: &[u8]) -> Result<(), Error> {
|
||||
for &byte in bytes {
|
||||
self.write_byte(byte)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalOutput for Inner {
|
||||
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||
self.regs.lock().write_byte(byte)
|
||||
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
|
||||
let regs = self.regs.lock();
|
||||
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
|
||||
regs.write_byte(b'\r')?;
|
||||
}
|
||||
regs.write_byte(byte)
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
self.regs.lock().write_bytes(bytes)?;
|
||||
fn write_multiple(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
options: &TerminalOutputOptions,
|
||||
) -> Result<usize, Error> {
|
||||
let regs = self.regs.lock();
|
||||
for &byte in bytes {
|
||||
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
|
||||
regs.write_byte(b'\r')?;
|
||||
}
|
||||
regs.write_byte(byte)?;
|
||||
}
|
||||
Ok(bytes.len())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,16 @@ const SYSCRG_GMAC0_GTXC: usize = 0x1BC / 4;
|
||||
// uart
|
||||
const SYSCRG_UART0_APB: usize = 0x244 / 4;
|
||||
const SYSCRG_UART0_CORE: usize = 0x248 / 4;
|
||||
const SYSCRG_UART1_APB: usize = 0x24C / 4;
|
||||
const SYSCRG_UART1_CORE: usize = 0x250 / 4;
|
||||
const SYSCRG_UART2_APB: usize = 0x254 / 4;
|
||||
const SYSCRG_UART2_CORE: usize = 0x258 / 4;
|
||||
const SYSCRG_UART3_APB: usize = 0x25C / 4;
|
||||
const SYSCRG_UART3_CORE: usize = 0x260 / 4;
|
||||
const SYSCRG_UART4_APB: usize = 0x264 / 4;
|
||||
const SYSCRG_UART4_CORE: usize = 0x268 / 4;
|
||||
const SYSCRG_UART5_APB: usize = 0x26C / 4;
|
||||
const SYSCRG_UART5_CORE: usize = 0x270 / 4;
|
||||
// jtag
|
||||
const SYSCRG_JTAG_CERTIFICATION_TRNG: usize = 0x2F4 / 4;
|
||||
const SYSCRG_CLOCK_COUNT: usize = SYSCRG_JTAG_CERTIFICATION_TRNG + 1;
|
||||
@@ -226,6 +236,16 @@ const SYSCRG_CLOCKS: &[Option<(&'static str, ClockDef)>] = &const {
|
||||
// uart
|
||||
t[SYSCRG_UART0_APB] = Some(("clk_uart0_apb", Gate(SYSCRG_APB0)));
|
||||
t[SYSCRG_UART0_CORE] = Some(("clk_uart0_core", Gate(SYSCRG_OSC)));
|
||||
t[SYSCRG_UART1_APB] = Some(("clk_uart1_apb", Gate(SYSCRG_APB0)));
|
||||
t[SYSCRG_UART1_CORE] = Some(("clk_uart1_core", Gate(SYSCRG_OSC)));
|
||||
t[SYSCRG_UART2_APB] = Some(("clk_uart2_apb", Gate(SYSCRG_APB0)));
|
||||
t[SYSCRG_UART2_CORE] = Some(("clk_uart2_core", Gate(SYSCRG_OSC)));
|
||||
t[SYSCRG_UART3_APB] = Some(("clk_uart3_apb", Gate(SYSCRG_APB0)));
|
||||
t[SYSCRG_UART3_CORE] = Some(("clk_uart3_core", GateDiv(10, SYSCRG_PERH_ROOT)));
|
||||
t[SYSCRG_UART4_APB] = Some(("clk_uart4_apb", Gate(SYSCRG_APB0)));
|
||||
t[SYSCRG_UART4_CORE] = Some(("clk_uart4_core", GateDiv(10, SYSCRG_PERH_ROOT)));
|
||||
t[SYSCRG_UART5_APB] = Some(("clk_uart5_apb", Gate(SYSCRG_APB0)));
|
||||
t[SYSCRG_UART5_CORE] = Some(("clk_uart5_core", GateDiv(10, SYSCRG_PERH_ROOT)));
|
||||
|
||||
t
|
||||
};
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "ygg_driver_serial_8250"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
device-tree.workspace = true
|
||||
device-api.workspace = true
|
||||
yggdrasil-abi.workspace = true
|
||||
libk-mm.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk.workspace = true
|
||||
|
||||
[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies]
|
||||
kernel-arch-x86.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
kernel-arch-x86.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,180 @@
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
clock::{ClockHandle, Hertz, ResetHandle},
|
||||
device::Device,
|
||||
interrupt::{InterruptHandler, IrqHandle},
|
||||
};
|
||||
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
|
||||
use libk::error::Error;
|
||||
use libk_mm::{address::PhysicalAddress, device::RawDeviceMemoryMapping};
|
||||
|
||||
use crate::{Access, Config, PortConfig, Uart8250, Vendor};
|
||||
|
||||
pub struct Fdt8250Access {
|
||||
mapping: RawDeviceMemoryMapping,
|
||||
offset: usize,
|
||||
shift: usize,
|
||||
io_width: usize,
|
||||
}
|
||||
|
||||
pub struct Fdt8250Config {
|
||||
irq: IrqHandle,
|
||||
ports: [Fdt8250PortConfig; 1],
|
||||
}
|
||||
|
||||
pub struct Fdt8250PortConfig {
|
||||
base: PhysicalAddress,
|
||||
reg_offset: usize,
|
||||
reg_shift: usize,
|
||||
reg_io_width: usize,
|
||||
|
||||
resets: Vec<ResetHandle>,
|
||||
clock_frequency: Option<Hertz>,
|
||||
clk_baud: Option<ClockHandle>,
|
||||
clk_apb: Option<ClockHandle>,
|
||||
}
|
||||
|
||||
impl Access for Fdt8250Access {
|
||||
type Config = Fdt8250Config;
|
||||
|
||||
unsafe fn map(config: &Fdt8250PortConfig) -> Result<Self, Error> {
|
||||
let mapping = unsafe {
|
||||
RawDeviceMemoryMapping::map(config.base.into_u64(), 0x1000, Default::default())
|
||||
}?;
|
||||
Ok(Self {
|
||||
offset: config.reg_offset,
|
||||
shift: config.reg_shift,
|
||||
io_width: config.reg_io_width,
|
||||
mapping,
|
||||
})
|
||||
}
|
||||
|
||||
fn read(&self, reg: u8) -> u8 {
|
||||
let addr = ((reg as usize) << self.shift) + self.offset;
|
||||
assert!(addr + self.io_width <= 0x1000);
|
||||
let ptr = unsafe { self.mapping.as_non_null::<u8>().add(addr) };
|
||||
match self.io_width {
|
||||
4 => unsafe { ptr.cast::<u32>().read_volatile() as u8 },
|
||||
1 => unsafe { ptr.cast::<u8>().read_volatile() },
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, reg: u8, val: u8) {
|
||||
let addr = ((reg as usize) << self.shift) + self.offset;
|
||||
assert!(addr + self.io_width <= 0x1000);
|
||||
let ptr = unsafe { self.mapping.as_non_null::<u8>().add(addr) };
|
||||
match self.io_width {
|
||||
4 => unsafe { ptr.cast::<u32>().write_volatile(val as u32) },
|
||||
1 => unsafe { ptr.cast::<u8>().write_volatile(val) },
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
// fn read(&self, reg: usize) -> u32 {
|
||||
// }
|
||||
|
||||
// fn write(&mut self, reg: usize, val: u32) {
|
||||
// }
|
||||
}
|
||||
|
||||
impl PortConfig for Fdt8250PortConfig {
|
||||
fn input_clock_frequency(&self) -> Result<Hertz, Error> {
|
||||
if let Some(clock_frequency) = self.clock_frequency {
|
||||
Ok(clock_frequency)
|
||||
} else if let Some(clk_baud) = self.clk_baud.as_ref() {
|
||||
clk_baud.rate()
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
fn deassert_resets(&self) -> Result<(), Error> {
|
||||
for reset in self.resets.iter() {
|
||||
reset.deassert()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn enable_clocks(&self) -> Result<(), Error> {
|
||||
if let Some(clk_apb) = self.clk_apb.as_ref() {
|
||||
clk_apb.enable()?;
|
||||
}
|
||||
if let Some(clk_baud) = self.clk_baud.as_ref() {
|
||||
clk_baud.enable()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Config for Fdt8250Config {
|
||||
type Port = Fdt8250PortConfig;
|
||||
|
||||
fn ports(&self) -> &[Self::Port] {
|
||||
&self.ports[..]
|
||||
}
|
||||
|
||||
fn enable_interrupt(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
|
||||
self.irq.register(handler)?;
|
||||
self.irq.enable()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Fdt8250Config {
|
||||
fn from_device_tree(node: &Arc<Node>, context: &mut ProbeContext) -> Option<(Self, Vendor)> {
|
||||
let vendor = if node.is_compatible("snps,dw-apb-uart") {
|
||||
Vendor::DwApbUart
|
||||
} else if node.is_compatible("ns16550a") {
|
||||
Vendor::Generic16550
|
||||
} else {
|
||||
Vendor::Generic8250
|
||||
};
|
||||
|
||||
let base = node.map_base(context, 0)?;
|
||||
let irq = node.interrupt(0)?;
|
||||
let resets = node.resets().map(|r| r.collect()).unwrap_or_default();
|
||||
let clock_frequency = node
|
||||
.prop_usize("clock-frequency")
|
||||
.map(|clk| Hertz(clk as _));
|
||||
let clk_baud = node.named_clock("baudclk");
|
||||
let clk_apb = node.named_clock("apb_pclk");
|
||||
|
||||
let reg_io_width = node.prop_usize("reg-io-width").unwrap_or(1);
|
||||
let reg_offset = node.prop_usize("reg-offset").unwrap_or(0);
|
||||
let reg_shift = node.prop_usize("reg-shift").unwrap_or(0);
|
||||
|
||||
Some((
|
||||
Self {
|
||||
irq,
|
||||
ports: [Fdt8250PortConfig {
|
||||
clk_apb,
|
||||
clk_baud,
|
||||
resets,
|
||||
clock_frequency,
|
||||
|
||||
reg_io_width,
|
||||
reg_offset,
|
||||
reg_shift,
|
||||
|
||||
base,
|
||||
}],
|
||||
},
|
||||
vendor,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: [
|
||||
"snps,dw-apb-uart",
|
||||
"ns16550a",
|
||||
],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let (config, vendor) = Fdt8250Config::from_device_tree(node, context)?;
|
||||
let uart = Arc::new(Uart8250::<Fdt8250Access>::new(config, vendor));
|
||||
Some(uart)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::Hertz,
|
||||
interrupt::{InterruptHandler, Irq},
|
||||
};
|
||||
use kernel_arch_x86::intrinsics;
|
||||
use libk::{device::external_interrupt_controller, error::Error};
|
||||
|
||||
use crate::{Access, Config, PortConfig};
|
||||
|
||||
pub struct Io8250Access {
|
||||
io_base: u16,
|
||||
}
|
||||
|
||||
pub struct Io8250Config {
|
||||
ports: [Io8250PortConfig; 2],
|
||||
irq: Irq,
|
||||
}
|
||||
|
||||
pub struct Io8250PortConfig {
|
||||
io_base: u16,
|
||||
}
|
||||
|
||||
impl Io8250Config {
|
||||
pub fn new(port_a: u16, port_b: u16, irq: Irq) -> Self {
|
||||
Self {
|
||||
ports: [
|
||||
Io8250PortConfig { io_base: port_a },
|
||||
Io8250PortConfig { io_base: port_b },
|
||||
],
|
||||
irq,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Access for Io8250Access {
|
||||
type Config = Io8250Config;
|
||||
|
||||
unsafe fn map(config: &Io8250PortConfig) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
io_base: config.io_base,
|
||||
})
|
||||
}
|
||||
|
||||
fn read(&self, reg: u8) -> u8 {
|
||||
unsafe { intrinsics::inb(self.io_base + reg as u16) }
|
||||
}
|
||||
|
||||
fn write(&mut self, reg: u8, val: u8) {
|
||||
unsafe { intrinsics::outb(self.io_base + reg as u16, val) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Config for Io8250Config {
|
||||
type Port = Io8250PortConfig;
|
||||
|
||||
fn ports(&self) -> &[Self::Port] {
|
||||
&self.ports
|
||||
}
|
||||
|
||||
fn enable_interrupt(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
|
||||
let intc = external_interrupt_controller()?;
|
||||
intc.register_irq(self.irq, Default::default(), handler)?;
|
||||
intc.enable_irq(self.irq)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PortConfig for Io8250PortConfig {
|
||||
fn input_clock_frequency(&self) -> Result<Hertz, Error> {
|
||||
Ok(Hertz(115200 * 16))
|
||||
}
|
||||
|
||||
fn enable_clocks(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deassert_resets(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
#![no_std]
|
||||
|
||||
use core::iter;
|
||||
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
clock::Hertz,
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{InterruptHandler, IrqVector},
|
||||
};
|
||||
use libk::error::Error;
|
||||
use libk_util::OneTimeInit;
|
||||
|
||||
use crate::port::Port8250;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(any(rust_analyzer, target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
pub mod fdt;
|
||||
#[cfg(any(rust_analyzer, target_arch = "x86_64", target_arch = "x86"))]
|
||||
pub mod io;
|
||||
pub mod port;
|
||||
|
||||
pub trait Config: Sync + Send {
|
||||
type Port: PortConfig;
|
||||
|
||||
fn ports(&self) -> &[Self::Port];
|
||||
fn enable_interrupt(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub trait PortConfig: Sync + Send {
|
||||
fn input_clock_frequency(&self) -> Result<Hertz, Error>;
|
||||
fn enable_clocks(&self) -> Result<(), Error>;
|
||||
fn deassert_resets(&self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub trait Access: Sized + Send + 'static {
|
||||
type Config: Config;
|
||||
|
||||
unsafe fn map(config: &<Self::Config as Config>::Port) -> Result<Self, Error>;
|
||||
|
||||
fn read(&self, reg: u8) -> u8;
|
||||
fn write(&mut self, reg: u8, val: u8);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Vendor {
|
||||
DwApbUart,
|
||||
Generic8250,
|
||||
Generic16550,
|
||||
}
|
||||
|
||||
pub struct Uart8250<A: Access> {
|
||||
config: A::Config,
|
||||
vendor: Vendor,
|
||||
ports: Vec<Arc<Port8250<A>>>,
|
||||
}
|
||||
|
||||
impl<A: Access> Uart8250<A> {
|
||||
pub fn new(config: A::Config, vendor: Vendor) -> Self {
|
||||
let ports = config
|
||||
.ports()
|
||||
.iter()
|
||||
.map(|_| {
|
||||
Arc::new(Port8250 {
|
||||
vendor,
|
||||
inner: OneTimeInit::new(),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
Self {
|
||||
config,
|
||||
vendor,
|
||||
ports,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn init_inner(self: &Arc<Self>) -> Result<(), Error> {
|
||||
let ports = self.ports.iter();
|
||||
let port_configs = self.config.ports().iter();
|
||||
for (port, config) in iter::zip(ports, port_configs) {
|
||||
port.clone().init(config)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> Device for Uart8250<A> {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
unsafe { self.init_inner() }
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
self.config.enable_interrupt(self.clone())?;
|
||||
for port in self.ports.iter() {
|
||||
port.enable_irq();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
match self.vendor {
|
||||
Vendor::DwApbUart => "Synopsys DesignWare 16550 UART",
|
||||
Vendor::Generic16550 => "16550 UART",
|
||||
Vendor::Generic8250 => "8250 UART",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> InterruptHandler for Uart8250<A> {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
for port in self.ports.iter() {
|
||||
if port.handle_irq() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use device_api::clock::Hertz;
|
||||
use libk::{
|
||||
debug::{self, DebugSink},
|
||||
device::manager::DEVICE_REGISTRY,
|
||||
error::Error,
|
||||
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||
};
|
||||
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
|
||||
use yggdrasil_abi::io::{TerminalOptions, TerminalOutputOptions};
|
||||
|
||||
use crate::{Access, Config, PortConfig, Vendor};
|
||||
|
||||
const REG_RBR: u8 = 0;
|
||||
const REG_THR: u8 = 0;
|
||||
const REG_DLL: u8 = 0;
|
||||
const REG_IER: u8 = 1;
|
||||
const REG_DLH: u8 = 1;
|
||||
const REG_IIR: u8 = 2;
|
||||
const REG_FCR: u8 = 2;
|
||||
const REG_LCR: u8 = 3;
|
||||
const REG_MCR: u8 = 4;
|
||||
const REG_LSR: u8 = 5;
|
||||
|
||||
// Vendor-specific
|
||||
const DW_REG_USR: u8 = 31;
|
||||
const DW_REG_SRR: u8 = 34;
|
||||
const DW_REG_HTX: u8 = 41;
|
||||
|
||||
/// Received Data available interrupt
|
||||
const IER_RXDE: u8 = 1 << 0;
|
||||
/// Received Data available
|
||||
const IIR_RXDA: u8 = 0b100;
|
||||
const IIR_MASK: u8 = 0x7;
|
||||
const FCR_FIFO_ENABLE: u8 = 1 << 0;
|
||||
const FCR_RX_FIFO_RESET: u8 = 1 << 1;
|
||||
const FCR_TX_FIFO_RESET: u8 = 1 << 2;
|
||||
const LCR_BITS_8: u8 = 0x3;
|
||||
const LCR_DLAB: u8 = 1 << 7;
|
||||
const MCR_DTR: u8 = 1 << 0;
|
||||
const MCR_RTS: u8 = 1 << 1;
|
||||
const LSR_TEMT: u8 = 1 << 6;
|
||||
|
||||
pub struct Port8250<A: Access> {
|
||||
pub(crate) vendor: Vendor,
|
||||
pub(crate) inner: OneTimeInit<Arc<Terminal<Inner<A>>>>,
|
||||
}
|
||||
|
||||
struct PortRegs<A: Access> {
|
||||
regs: A,
|
||||
baud_rate: u32,
|
||||
input_clock: Hertz,
|
||||
}
|
||||
|
||||
pub(crate) struct Inner<A: Access> {
|
||||
regs: IrqSafeSpinlock<PortRegs<A>>,
|
||||
}
|
||||
|
||||
impl<A: Access> Port8250<A> {
|
||||
pub(crate) fn init(self: Arc<Self>, config: &<A::Config as Config>::Port) -> Result<(), Error> {
|
||||
let _guard = debug::MuteGuard::acquire();
|
||||
// Deassert reset signal and enable clocks
|
||||
config.enable_clocks()?;
|
||||
config.deassert_resets()?;
|
||||
|
||||
let input_clock = config.input_clock_frequency()?;
|
||||
|
||||
let mut regs = PortRegs {
|
||||
regs: unsafe { A::map(config) }?,
|
||||
baud_rate: 0,
|
||||
input_clock,
|
||||
};
|
||||
|
||||
regs.init(self.vendor, 115200)?;
|
||||
|
||||
let input = TerminalInput::with_capacity(64)?;
|
||||
let output = Inner {
|
||||
regs: IrqSafeSpinlock::new(regs),
|
||||
};
|
||||
|
||||
let terminal = self.inner.init(Arc::new(Terminal::from_parts(
|
||||
TerminalOptions::const_default(),
|
||||
input,
|
||||
output,
|
||||
)));
|
||||
|
||||
DEVICE_REGISTRY
|
||||
.serial_terminal
|
||||
.register(terminal.clone(), Some(self.clone()))
|
||||
.ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn enable_irq(&self) {
|
||||
let mut regs = self.inner.get().output().regs.lock();
|
||||
regs.write(REG_IER, IER_RXDE);
|
||||
}
|
||||
|
||||
pub(crate) fn handle_irq(&self) -> bool {
|
||||
let inner = self.inner.get();
|
||||
let byte = {
|
||||
let regs = inner.output().regs.lock();
|
||||
let iir = regs.read(REG_IIR) & IIR_MASK;
|
||||
if iir == IIR_RXDA {
|
||||
Some(regs.read(REG_RBR) as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(byte) = byte {
|
||||
inner.write_to_input(byte);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> DebugSink for Port8250<A> {
|
||||
fn putc(&self, c: u8) -> Result<(), Error> {
|
||||
self.inner.get().putc_to_output(c)
|
||||
}
|
||||
|
||||
fn supports_control_sequences(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> TerminalOutput for Inner<A> {
|
||||
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> {
|
||||
let mut regs = self.regs.lock();
|
||||
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
|
||||
regs.serial_out(b'\r')?;
|
||||
}
|
||||
regs.serial_out(byte)
|
||||
}
|
||||
|
||||
fn write_multiple(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
options: &TerminalOutputOptions,
|
||||
) -> Result<usize, Error> {
|
||||
let mut regs = self.regs.lock();
|
||||
for &byte in bytes {
|
||||
if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) {
|
||||
regs.serial_out(b'\r')?;
|
||||
}
|
||||
regs.serial_out(byte)?;
|
||||
}
|
||||
Ok(bytes.len())
|
||||
}
|
||||
|
||||
fn set_baud_rate(&self, baud: u32) -> Result<(), Error> {
|
||||
let mut regs = self.regs.lock();
|
||||
regs.set_baud_rate(baud)
|
||||
}
|
||||
|
||||
fn baud_rate(&self) -> u32 {
|
||||
let regs = self.regs.lock();
|
||||
regs.baud_rate
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> PortRegs<A> {
|
||||
fn init(&mut self, vendor: Vendor, baud_rate: u32) -> Result<(), Error> {
|
||||
if vendor == Vendor::DwApbUart {
|
||||
self.write(DW_REG_SRR, 1);
|
||||
delay();
|
||||
self.write(DW_REG_HTX, 0);
|
||||
delay();
|
||||
|
||||
// wait while DW USR.BUSY == 1
|
||||
while self.read(DW_REG_USR) & (1 << 0) != 0 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
self.set_baud_rate(baud_rate)
|
||||
}
|
||||
|
||||
fn serial_out(&mut self, byte: u8) -> Result<(), Error> {
|
||||
let mut timeout = 1000000;
|
||||
while timeout > 0 && self.read(REG_LSR) & LSR_TEMT == 0 {
|
||||
timeout -= 1;
|
||||
}
|
||||
if timeout == 0 {
|
||||
return Err(Error::TimedOut);
|
||||
}
|
||||
self.write(REG_THR, byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_baud_rate(&mut self, baud_rate: u32) -> Result<(), Error> {
|
||||
// TODO check that the divisor actually fits
|
||||
|
||||
self.baud_rate = baud_rate;
|
||||
|
||||
let divisor = (self.input_clock.0 / (baud_rate as u64 * 16)) as u32;
|
||||
|
||||
// Disable interrupts, clear FCR/MCR
|
||||
self.write(REG_IER, 0x00);
|
||||
self.write(REG_MCR, 0x00);
|
||||
self.write(REG_FCR, 0x00);
|
||||
|
||||
// Program divisor
|
||||
// DLAB=1
|
||||
let lcr = self.read(REG_LCR);
|
||||
self.write(REG_LCR, lcr | LCR_DLAB);
|
||||
self.write(REG_DLH, ((divisor >> 8) & 0xFF) as u8);
|
||||
self.write(REG_DLL, (divisor & 0xFF) as u8);
|
||||
delay();
|
||||
let lcr = self.read(REG_LCR);
|
||||
self.write(REG_LCR, lcr & !LCR_DLAB);
|
||||
delay();
|
||||
|
||||
// 8n1 setting
|
||||
let lcr = self.read(REG_LCR);
|
||||
self.write(REG_LCR, lcr | LCR_BITS_8);
|
||||
delay();
|
||||
|
||||
// DTR+RTS
|
||||
self.write(REG_MCR, MCR_DTR | MCR_RTS);
|
||||
|
||||
// Enable and drain FIFO
|
||||
self.write(REG_FCR, FCR_FIFO_ENABLE);
|
||||
delay();
|
||||
let fcr = self.read(REG_FCR);
|
||||
self.write(REG_FCR, fcr | FCR_RX_FIFO_RESET | FCR_TX_FIFO_RESET);
|
||||
delay();
|
||||
// TODO flush Rx
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> Deref for PortRegs<A> {
|
||||
type Target = A;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.regs
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Access> DerefMut for PortRegs<A> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.regs
|
||||
}
|
||||
}
|
||||
|
||||
fn delay() {
|
||||
for _ in 0..10000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
@@ -105,18 +105,18 @@ impl Node {
|
||||
.as_str_list()
|
||||
.find_map(|c| drivers.iter().find(|d| d.matches(c)));
|
||||
|
||||
if libk::config::get().device_tree.log_missing && driver.is_none() {
|
||||
// FIXME don't spam virtio missing stuff
|
||||
if !name.is_some_and(|n| n.starts_with("virtio_mmio")) {
|
||||
for (i, compatible) in compatible.as_str_list().enumerate() {
|
||||
if i == 0 {
|
||||
log::warn!("No driver for {name:?} ({compatible:?})");
|
||||
} else {
|
||||
log::warn!(" also {compatible:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if libk::config::get().device_tree.log_missing && driver.is_none() {
|
||||
// // FIXME don't spam virtio missing stuff
|
||||
// if !name.is_some_and(|n| n.starts_with("virtio_mmio")) {
|
||||
// for (i, compatible) in compatible.as_str_list().enumerate() {
|
||||
// if i == 0 {
|
||||
// log::warn!("No driver for {name:?} ({compatible:?})");
|
||||
// } else {
|
||||
// log::warn!(" also {compatible:?}");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
let driver = driver?;
|
||||
|
||||
// Initialize default pinctrl before probing
|
||||
|
||||
@@ -8,7 +8,11 @@ use libk_util::{
|
||||
StaticVector,
|
||||
sync::{IrqSafeSpinlock, spin_rwlock::IrqSafeRwLock},
|
||||
};
|
||||
use yggdrasil_abi::{error::Error, io::TerminalSize, primitive_enum};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{TerminalOutputOptions, TerminalSize},
|
||||
primitive_enum,
|
||||
};
|
||||
|
||||
use crate::{task::runtime, vfs::TerminalOutput};
|
||||
|
||||
@@ -520,7 +524,8 @@ impl TerminalOutput for ConsoleWrapper {
|
||||
TerminalSize { rows, columns }
|
||||
}
|
||||
|
||||
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||
fn write(&self, byte: u8, _options: &TerminalOutputOptions) -> Result<(), Error> {
|
||||
// TODO handle options
|
||||
self.0.write_char(byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use async_trait::async_trait;
|
||||
use libk_util::{ring::BlockingRingQueue, sync::spin_rwlock::IrqSafeRwLock};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{TerminalOptions, TerminalSize},
|
||||
io::{TerminalOptions, TerminalOutputOptions, TerminalSize},
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -28,11 +28,16 @@ struct PtyOutput {
|
||||
}
|
||||
|
||||
impl TerminalOutput for PtyOutput {
|
||||
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||
// TODO options
|
||||
fn write(&self, byte: u8, _options: &TerminalOutputOptions) -> Result<(), Error> {
|
||||
block!(self.ring.write(byte).await)
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
fn write_multiple(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
_options: &TerminalOutputOptions,
|
||||
) -> Result<usize, Error> {
|
||||
block!(self.ring.write_all(bytes).await)?;
|
||||
Ok(bytes.len())
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ pub struct TerminalInput {
|
||||
|
||||
// Program -> screen, PTY master, terminal, etc.
|
||||
pub trait TerminalOutput: Sync + Send {
|
||||
fn write(&self, byte: u8) -> Result<(), Error>;
|
||||
fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error>;
|
||||
|
||||
fn notify_readers(&self) {}
|
||||
|
||||
@@ -56,10 +56,14 @@ pub trait TerminalOutput: Sync + Send {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
fn write_multiple(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
options: &TerminalOutputOptions,
|
||||
) -> Result<usize, Error> {
|
||||
let mut written = 0;
|
||||
for &byte in bytes {
|
||||
self.write(byte)?;
|
||||
self.write(byte, options)?;
|
||||
written += 1;
|
||||
}
|
||||
Ok(written)
|
||||
@@ -126,12 +130,14 @@ impl<O: TerminalOutput> Terminal<O> {
|
||||
}
|
||||
|
||||
pub fn putc_to_output(&self, byte: u8) -> Result<(), Error> {
|
||||
self.output.write(byte)
|
||||
let options = self.config.read().output;
|
||||
self.output.write(byte, &options)
|
||||
}
|
||||
|
||||
pub fn write_to_output(&self, buffer: &[u8]) -> Result<usize, Error> {
|
||||
let options = self.config.read().output;
|
||||
// TODO handle options
|
||||
self.output.write_multiple(buffer)
|
||||
self.output.write_multiple(buffer, &options)
|
||||
}
|
||||
|
||||
pub fn write_to_input(&self, mut byte: u8) {
|
||||
@@ -151,7 +157,9 @@ impl<O: TerminalOutput> Terminal<O> {
|
||||
buffer.erase_pending() && config.line.contains(TerminalLineOptions::ECHO_ERASE);
|
||||
|
||||
if echo {
|
||||
self.output.write_multiple(b"\x1B[D \x1B[D").ok();
|
||||
self.output
|
||||
.write_multiple(b"\x1B[D \x1B[D", &config.output)
|
||||
.ok();
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -160,17 +168,16 @@ impl<O: TerminalOutput> Terminal<O> {
|
||||
if byte == b'\n' {
|
||||
// TODO NL_TO_CRNL
|
||||
if config.is_echo_newline() {
|
||||
if config.output.contains(TerminalOutputOptions::NL_TO_CRNL) {
|
||||
self.output.write(b'\r').ok();
|
||||
}
|
||||
self.output.write(byte).ok();
|
||||
self.output.write(byte, &config.output).ok();
|
||||
}
|
||||
} else if byte.is_ascii_control() {
|
||||
if config.line.contains(TerminalLineOptions::ECHO) {
|
||||
self.output.write_multiple(&[b'^', byte + 0x40]).ok();
|
||||
self.output
|
||||
.write_multiple(&[b'^', byte + 0x40], &config.output)
|
||||
.ok();
|
||||
}
|
||||
} else if config.line.contains(TerminalLineOptions::ECHO) {
|
||||
self.output.write(byte).ok();
|
||||
self.output.write(byte, &config.output).ok();
|
||||
}
|
||||
|
||||
if byte == config.chars.interrupt {
|
||||
|
||||
@@ -168,6 +168,11 @@ impl Riscv64 {
|
||||
if is_bsp {
|
||||
call_init_array();
|
||||
|
||||
unsafe extern "C" {
|
||||
static __kernel_start: u8;
|
||||
}
|
||||
log::info!("Boot address: {:#x}", (&raw const __kernel_start).addr());
|
||||
|
||||
atomic::compiler_fence(Ordering::SeqCst);
|
||||
|
||||
libk::debug::init();
|
||||
|
||||
+28
-11
@@ -20,8 +20,12 @@ use libk_mm::{
|
||||
phys::{self, PhysicalMemoryRegion},
|
||||
table::EntryLevelExt,
|
||||
};
|
||||
use peripherals::{i8253::I8253, ps2::PS2Controller, rtc::Rtc, serial::ComPort};
|
||||
use peripherals::{i8253::I8253, ps2::PS2Controller, rtc::Rtc};
|
||||
use ygg_driver_pci::PciBusManager;
|
||||
use ygg_driver_serial_8250::{
|
||||
Uart8250,
|
||||
io::{Io8250Access, Io8250Config},
|
||||
};
|
||||
|
||||
use crate::fs::{INITRD_DATA, Initrd};
|
||||
|
||||
@@ -43,8 +47,8 @@ struct ProbeClockSource {
|
||||
}
|
||||
|
||||
pub struct EarlyPlatformDevices {
|
||||
pub com1_3: Arc<ComPort>,
|
||||
|
||||
com1_3: Arc<Uart8250<Io8250Access>>,
|
||||
com2_4: Arc<Uart8250<Io8250Access>>,
|
||||
clock_sources: Vec<ProbeClockSource>,
|
||||
}
|
||||
|
||||
@@ -72,12 +76,18 @@ pub fn init_platform_early(cmdline: &str) -> Result<EarlyPlatformDevices, Error>
|
||||
// Initialize async executor queue
|
||||
runtime::init_task_queue();
|
||||
|
||||
let com1_3 = ComPort::setup(
|
||||
dummy_init_context(),
|
||||
0x3F8,
|
||||
0x3E8,
|
||||
Irq::External(ISA_IRQ_OFFSET + 4),
|
||||
)?;
|
||||
let com1_3_config = Io8250Config::new(0x3F8, 0x3E8, Irq::External(ISA_IRQ_OFFSET + 4));
|
||||
let com2_4_config = Io8250Config::new(0x2F8, 0x2E8, Irq::External(ISA_IRQ_OFFSET + 3));
|
||||
|
||||
let com1_3 = Arc::new(Uart8250::<Io8250Access>::new(
|
||||
com1_3_config,
|
||||
ygg_driver_serial_8250::Vendor::Generic8250,
|
||||
));
|
||||
unsafe { com1_3.init_inner() }?;
|
||||
let com2_4 = Arc::new(Uart8250::<Io8250Access>::new(
|
||||
com2_4_config,
|
||||
ygg_driver_serial_8250::Vendor::Generic8250,
|
||||
));
|
||||
// let i8259 = I8259::setup().expect("Could not initialize i8259 PIC");
|
||||
|
||||
// disable_i8259();
|
||||
@@ -85,6 +95,7 @@ pub fn init_platform_early(cmdline: &str) -> Result<EarlyPlatformDevices, Error>
|
||||
|
||||
Ok(EarlyPlatformDevices {
|
||||
com1_3,
|
||||
com2_4,
|
||||
clock_sources: Vec::new(),
|
||||
})
|
||||
}
|
||||
@@ -116,8 +127,14 @@ pub fn init_platform_devices(early: EarlyPlatformDevices) {
|
||||
if let Err(error) = Rtc::setup() {
|
||||
log::error!("RTC init error: {error:?}");
|
||||
}
|
||||
if let Err(error) = unsafe { early.com1_3.port_a().clone().init_irq() } {
|
||||
log::error!("COM port IRQ init error: {error:?}");
|
||||
if let Err(error) = unsafe { early.com2_4.init_inner() } {
|
||||
log::error!("COM port 2/4 init error: {error:?}");
|
||||
}
|
||||
if let Err(error) = unsafe { early.com1_3.init_irq() } {
|
||||
log::error!("COM port 1/3 IRQ init error: {error:?}");
|
||||
}
|
||||
if let Err(error) = unsafe { early.com2_4.init_irq() } {
|
||||
log::error!("COM port 2/4 IRQ init error: {error:?}");
|
||||
}
|
||||
|
||||
if let Err(error) = PciBusManager::probe_bus_devices() {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
pub mod i8253;
|
||||
pub mod ps2;
|
||||
pub mod rtc;
|
||||
pub mod serial;
|
||||
|
||||
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
||||
pub mod hpet;
|
||||
|
||||
@@ -1,218 +0,0 @@
|
||||
//! Driver for x86 COM ports
|
||||
use abi::{error::Error, io::TerminalOptions};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{InterruptHandler, Irq, IrqVector},
|
||||
};
|
||||
use kernel_arch_x86::intrinsics::{IoPort, IoPortAccess};
|
||||
use libk::{
|
||||
debug::DebugSink,
|
||||
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
|
||||
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||
};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
|
||||
// Single port
|
||||
struct Regs {
|
||||
dr: IoPort<u8>,
|
||||
lsr: IoPort<u8>,
|
||||
lcr: IoPort<u8>,
|
||||
ier: IoPort<u8>,
|
||||
isr: IoPort<u8>,
|
||||
|
||||
baud_rate: u32,
|
||||
}
|
||||
|
||||
struct PortInner {
|
||||
regs: IrqSafeSpinlock<Regs>,
|
||||
}
|
||||
|
||||
/// Single port of the COM port pair
|
||||
pub struct Port {
|
||||
terminal: Arc<Terminal<PortInner>>,
|
||||
irq: Irq,
|
||||
}
|
||||
|
||||
/// COM port pair
|
||||
#[allow(unused)]
|
||||
pub struct ComPort {
|
||||
port_a: Arc<Port>,
|
||||
port_b: Arc<Port>,
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
fn write(&mut self, byte: u8) -> Result<(), Error> {
|
||||
while self.lsr.read() & Port::LSR_THRE == 0 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
self.dr.write(byte);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PortInner {
|
||||
fn handle_irq(&self) -> Option<u8> {
|
||||
let (status, value) = {
|
||||
let inner = self.regs.lock();
|
||||
let status = inner.isr.read();
|
||||
let value = inner.dr.read();
|
||||
(status, value)
|
||||
};
|
||||
|
||||
if status & Port::ISR_IRQ_MASK != 0 {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalOutput for PortInner {
|
||||
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||
self.regs.lock().write(byte)
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
let mut regs = self.regs.lock();
|
||||
for &b in bytes {
|
||||
regs.write(b)?;
|
||||
}
|
||||
Ok(bytes.len())
|
||||
}
|
||||
|
||||
fn baud_rate(&self) -> u32 {
|
||||
self.regs.lock().baud_rate
|
||||
}
|
||||
|
||||
fn set_baud_rate(&self, baud: u32) -> Result<(), Error> {
|
||||
if baud > 115200 {
|
||||
log::warn!("Tried to set baud rate for COM port beyond 115200: {baud}");
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let div = 115200 / baud;
|
||||
|
||||
let mut regs = self.regs.lock();
|
||||
|
||||
regs.lcr.write(regs.lcr.read() | (1 << 7));
|
||||
regs.dr.write(div as u8);
|
||||
regs.ier.write((div >> 8) as u8);
|
||||
regs.lcr.write(regs.lcr.read() & !(1 << 7));
|
||||
|
||||
regs.baud_rate = baud;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugSink for Port {
|
||||
fn putc(&self, c: u8) -> Result<(), Error> {
|
||||
self.terminal.putc_to_output(c).ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn puts(&self, s: &str) -> Result<(), Error> {
|
||||
self.terminal.write_to_output(s.as_bytes()).ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn supports_control_sequences(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Port {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
let inner = self.terminal.output();
|
||||
if let Some(byte) = inner.handle_irq() {
|
||||
self.terminal.write_to_input(byte);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Port {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"COM port"
|
||||
}
|
||||
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
self.terminal.output().set_baud_rate(115200)?;
|
||||
|
||||
DEVICE_REGISTRY
|
||||
.serial_terminal
|
||||
.register(self.terminal.clone(), Some(self.clone()))
|
||||
.ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
let intc = external_interrupt_controller()?;
|
||||
|
||||
// TODO check that the same IRQ is not bound already for another port
|
||||
intc.register_irq(self.irq, Default::default(), self.clone())?;
|
||||
intc.enable_irq(self.irq)?;
|
||||
|
||||
let regs = self.terminal.output().regs.lock();
|
||||
|
||||
regs.ier.modify(|v| v | Self::IER_RXDA);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Port {
|
||||
const LSR_THRE: u8 = 1 << 5;
|
||||
const IER_RXDA: u8 = 1 << 0;
|
||||
const ISR_IRQ_MASK: u8 = 3 << 1;
|
||||
|
||||
fn new(base: u16, irq: Irq) -> Result<Self, Error> {
|
||||
let input = TerminalInput::with_capacity(64)?;
|
||||
let output = PortInner {
|
||||
regs: IrqSafeSpinlock::new(Regs {
|
||||
dr: IoPort::new(base),
|
||||
lsr: IoPort::new(base + 5),
|
||||
ier: IoPort::new(base + 1),
|
||||
isr: IoPort::new(base + 2),
|
||||
lcr: IoPort::new(base + 3),
|
||||
|
||||
baud_rate: 0,
|
||||
}),
|
||||
};
|
||||
let terminal = Terminal::from_parts(TerminalOptions::const_default(), input, output);
|
||||
Ok(Self {
|
||||
terminal: Arc::new(terminal),
|
||||
irq,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ComPort {
|
||||
/// Constructs a COM port pair
|
||||
fn new(port_a: u16, port_b: u16, irq: Irq) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
port_a: Arc::new(Port::new(port_a, irq)?),
|
||||
port_b: Arc::new(Port::new(port_b, irq)?),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn setup(
|
||||
cx: DeviceInitContext,
|
||||
port_a: u16,
|
||||
port_b: u16,
|
||||
irq: Irq,
|
||||
) -> Result<Arc<Self>, Error> {
|
||||
let this = Arc::new(Self::new(port_a, port_b, irq)?);
|
||||
unsafe { this.port_a().clone().init(cx) }?;
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
/// Returns a reference to the A port of this COM pair
|
||||
pub fn port_a(&self) -> &Arc<Port> {
|
||||
&self.port_a
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ pub mod bus;
|
||||
pub mod clock;
|
||||
pub mod display;
|
||||
pub mod power;
|
||||
pub mod serial;
|
||||
// pub mod timer;
|
||||
|
||||
#[cfg(any(rust_analyzer, not(target_arch = "x86_64")))]
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
//! Serial device interfaces
|
||||
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod ns16550a;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod snps_dw_apb_uart;
|
||||
@@ -1,204 +0,0 @@
|
||||
//! 16550-style UART device driver
|
||||
use abi::{error::Error, io::TerminalOptions};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
|
||||
use libk::{
|
||||
debug::DebugSink,
|
||||
device::manager::DEVICE_REGISTRY,
|
||||
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
|
||||
register_bitfields!(
|
||||
u8,
|
||||
IER [
|
||||
/// Received data ready
|
||||
RDR OFFSET(0) NUMBITS(1) [],
|
||||
/// Trasmitter holding register empty
|
||||
THRE OFFSET(1) NUMBITS(1) [],
|
||||
/// Receiver line status
|
||||
RLS OFFSET(2) NUMBITS(1) [],
|
||||
/// Modem status
|
||||
MS OFFSET(3) NUMBITS(1) [],
|
||||
],
|
||||
LSR [
|
||||
/// Data ready indicator
|
||||
DR OFFSET(0) NUMBITS(1) [],
|
||||
/// Transmitter FIFO empty
|
||||
TFE OFFSET(5) NUMBITS(1) [],
|
||||
],
|
||||
LCR [
|
||||
BITS OFFSET(0) NUMBITS(2) [
|
||||
Bits8 = 3
|
||||
],
|
||||
STOPBITS OFFSET(2) NUMBITS(1) [],
|
||||
PARITY OFFSET(3) NUMBITS(1) [],
|
||||
PARITY_EVEN OFFSET(4) NUMBITS(1) [],
|
||||
PARITY_STICK OFFSET(5) NUMBITS(1) [],
|
||||
BREAK OFFSET(6) NUMBITS(1) [],
|
||||
DLAB OFFSET(7) NUMBITS(1) [],
|
||||
]
|
||||
);
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
// Read: receive buffer, write: transmit buffer
|
||||
(0x00 => DR: ReadWrite<u8>),
|
||||
(0x01 => IER: ReadWrite<u8, IER::Register>),
|
||||
// Read: interrupt idenditication, write: FIFO control
|
||||
(0x02 => FCR: ReadWrite<u8>),
|
||||
(0x03 => LCR: ReadWrite<u8, LCR::Register>),
|
||||
(0x04 => MCR: WriteOnly<u8>),
|
||||
(0x05 => LSR: ReadOnly<u8, LSR::Register>),
|
||||
(0x06 => MSR: ReadOnly<u8>),
|
||||
(0x07 => _0),
|
||||
(0x08 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct Io {
|
||||
regs: DeviceMemoryIo<'static, Regs>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
io: IrqSafeSpinlock<Io>,
|
||||
}
|
||||
|
||||
/// ns16550a-style UART driver
|
||||
pub struct Ns16550a {
|
||||
inner: OneTimeInit<Arc<Terminal<Inner>>>,
|
||||
base: PhysicalAddress,
|
||||
irq: IrqHandle,
|
||||
}
|
||||
|
||||
impl Io {
|
||||
fn init(&mut self) {
|
||||
self.regs.LCR.write(
|
||||
LCR::BITS::Bits8 + LCR::BREAK::CLEAR + LCR::STOPBITS::CLEAR + LCR::PARITY::CLEAR,
|
||||
);
|
||||
self.regs.IER.set(0);
|
||||
}
|
||||
|
||||
fn send(&mut self, byte: u8) {
|
||||
while self.regs.LSR.matches_all(LSR::TFE::CLEAR) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
self.regs.DR.set(byte);
|
||||
}
|
||||
|
||||
fn handle_irq(&self) -> Option<u8> {
|
||||
let status = self.regs.FCR.get() & 0xF;
|
||||
if status == 0b1100 || status == 0b0100 {
|
||||
Some(self.regs.DR.get())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Ns16550a {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
log::info!("Init ns16550a @ {:#x}", self.base);
|
||||
let mut io = Io {
|
||||
regs: DeviceMemoryIo::map(self.base, Default::default())?,
|
||||
};
|
||||
io.init();
|
||||
|
||||
let input = TerminalInput::with_capacity(64)?;
|
||||
let output = Inner {
|
||||
io: IrqSafeSpinlock::new(io),
|
||||
};
|
||||
|
||||
let terminal = self.inner.init(Arc::new(Terminal::from_parts(
|
||||
TerminalOptions::const_default(),
|
||||
input,
|
||||
output,
|
||||
)));
|
||||
|
||||
DEVICE_REGISTRY
|
||||
.serial_terminal
|
||||
.register(terminal.clone(), Some(self.clone()))
|
||||
.ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
self.irq.register(self.clone())?;
|
||||
self.irq.enable()?;
|
||||
let io = self.inner.get().output().io.lock();
|
||||
io.regs.IER.modify(IER::RDR::SET);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"ns16550a UART"
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Ns16550a {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
let inner = self.inner.get();
|
||||
let output = inner.output();
|
||||
let byte = output.io.lock().handle_irq();
|
||||
if let Some(byte) = byte {
|
||||
inner.write_to_input(byte);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalOutput for Inner {
|
||||
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||
self.io.lock().send(byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
let mut lock = self.io.lock();
|
||||
for &byte in bytes {
|
||||
lock.send(byte);
|
||||
}
|
||||
Ok(bytes.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugSink for Ns16550a {
|
||||
fn putc(&self, c: u8) -> Result<(), Error> {
|
||||
self.inner.get().putc_to_output(c)
|
||||
}
|
||||
|
||||
fn supports_control_sequences(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver!(
|
||||
compatible: ["ns16550a"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
log::debug!("ns16550a base = {base:#x}");
|
||||
let irq = node.interrupt(0)?;
|
||||
|
||||
Some(Arc::new(Ns16550a {
|
||||
base,
|
||||
irq,
|
||||
inner: OneTimeInit::new(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1,304 +0,0 @@
|
||||
//! Synopsys DesignWare 8250 driver
|
||||
use abi::{error::Error, io::TerminalOptions};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockHandle, Hertz, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
|
||||
use libk::{
|
||||
debug::{self, DebugSink},
|
||||
device::manager::DEVICE_REGISTRY,
|
||||
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
LCR [
|
||||
DLAB OFFSET(7) NUMBITS(1) [],
|
||||
DLS OFFSET(0) NUMBITS(2) [
|
||||
DL8b = 0b11,
|
||||
],
|
||||
],
|
||||
IER [
|
||||
PTIME OFFSET(7) NUMBITS(1) [],
|
||||
EDSSI OFFSET(3) NUMBITS(1) [],
|
||||
ELSI OFFSET(2) NUMBITS(1) [],
|
||||
// Transmit buffer available
|
||||
ETBEI OFFSET(1) NUMBITS(1) [],
|
||||
// Receive data available
|
||||
ERBFI OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
LSR [
|
||||
// Data ready bit
|
||||
DR OFFSET(0) NUMBITS(1) [],
|
||||
// Transmitter holding register empty
|
||||
THRE OFFSET(5) NUMBITS(1) [],
|
||||
],
|
||||
USR [
|
||||
BUSY OFFSET(0) NUMBITS(1) [],
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
// DLAB=0, Write: transmitter holding register/Read: receiver buffer register
|
||||
// DLAB=1, Read/Write: divisor latch low
|
||||
(0x000 => DR: ReadWrite<u32>),
|
||||
// DLAB=0: Interrupt enable register
|
||||
// DLAB=1: Divisor latch high
|
||||
(0x004 => IER: ReadWrite<u32, IER::Register>),
|
||||
// Read: interrupt identification register/Write: frame control register
|
||||
(0x008 => IIR: ReadWrite<u32>),
|
||||
// Line control register
|
||||
(0x00C => LCR: ReadWrite<u32, LCR::Register>),
|
||||
// Modem control register
|
||||
(0x010 => MCR: ReadWrite<u32>),
|
||||
// Line status register
|
||||
(0x014 => LSR: ReadOnly<u32, LSR::Register>),
|
||||
// Modem status register
|
||||
(0x018 => MSR: ReadOnly<u32>),
|
||||
// Scratchpad
|
||||
(0x01C => SCR: ReadWrite<u32>),
|
||||
// Low-power divisor latch low
|
||||
(0x020 => LPDLL: ReadWrite<u32>),
|
||||
// Low-power divisor latch high
|
||||
(0x024 => LPDLH: ReadWrite<u32>),
|
||||
(0x028 => _0),
|
||||
// Shadow receive/transmit buffer
|
||||
(0x030 => SDR: [ReadWrite<u32>; 16]),
|
||||
(0x070 => FAR: ReadWrite<u32>),
|
||||
(0x074 => TFR: ReadOnly<u32>),
|
||||
(0x078 => RFW: WriteOnly<u32>),
|
||||
(0x07C => USR: ReadOnly<u32, USR::Register>),
|
||||
(0x080 => TFL: ReadOnly<u32>),
|
||||
(0x084 => RFL: ReadOnly<u32>),
|
||||
(0x088 => SRR: WriteOnly<u32>),
|
||||
(0x08C => SRTS: ReadWrite<u32>),
|
||||
(0x090 => SBCR: ReadWrite<u32>),
|
||||
(0x094 => SDMAM: ReadWrite<u32>),
|
||||
(0x098 => SFE: ReadWrite<u32>),
|
||||
(0x09C => SRT: ReadWrite<u32>),
|
||||
(0x0A0 => STET: ReadWrite<u32>),
|
||||
(0x0A4 => HTX: ReadWrite<u32>),
|
||||
(0x0A8 => DMASA: WriteOnly<u32>),
|
||||
(0x0AC => _1),
|
||||
(0x0F4 => CPR: ReadOnly<u32>),
|
||||
(0x0F8 => UCV: ReadOnly<u32>),
|
||||
(0x0FC => CTR: ReadOnly<u32>),
|
||||
(0x100 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct Io {
|
||||
regs: DeviceMemoryIo<'static, Regs>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
io: IrqSafeSpinlock<Io>,
|
||||
}
|
||||
|
||||
/// Synopsys DesignWare 8250 UART
|
||||
pub struct DwUart {
|
||||
base: PhysicalAddress,
|
||||
irq: IrqHandle,
|
||||
clk_baud: ClockHandle,
|
||||
#[allow(unused)]
|
||||
clk_apb: Option<ClockHandle>,
|
||||
rst: Option<ResetHandle>,
|
||||
|
||||
inner: OneTimeInit<Arc<Terminal<Inner>>>,
|
||||
}
|
||||
|
||||
impl Io {
|
||||
fn send(&mut self, byte: u8) {
|
||||
// TODO
|
||||
if byte == b'\n' {
|
||||
self.send(b'\r');
|
||||
}
|
||||
|
||||
while !self.regs.LSR.matches_all(LSR::THRE::SET) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
self.regs.DR.set(byte as u32);
|
||||
}
|
||||
|
||||
fn init(&mut self, baud_clock: Hertz, baud_rate: u64) {
|
||||
let divisor = (baud_clock.0 / (baud_rate * 16)) as u32;
|
||||
|
||||
self.wait_busy();
|
||||
self.regs.IER.set(0);
|
||||
for _ in 0..100 {
|
||||
let _ = self.regs.LSR.get();
|
||||
}
|
||||
for _ in 0..100 {
|
||||
let _ = self.regs.DR.get();
|
||||
}
|
||||
self.wait_busy();
|
||||
self.regs.LCR.write(LCR::DLAB::SET);
|
||||
self.wait_busy();
|
||||
self.regs.DR.set(divisor & 0xFF);
|
||||
self.regs.IER.set((divisor >> 8) & 0xFF);
|
||||
self.wait_busy();
|
||||
self.regs.LCR.write(LCR::DLS::DL8b);
|
||||
self.wait_busy();
|
||||
self.regs.IIR.set(0x01);
|
||||
self.wait_busy();
|
||||
self.regs.MCR.set(0x00);
|
||||
let _ = self.regs.LSR.get();
|
||||
for _ in 0..100 {
|
||||
let _ = self.regs.DR.get();
|
||||
}
|
||||
self.regs.SCR.set(0x00);
|
||||
}
|
||||
|
||||
fn handle_irq(&mut self) -> Option<u8> {
|
||||
let status = self.regs.IIR.get();
|
||||
|
||||
if status & 0xF == 4 {
|
||||
Some(self.regs.DR.get() as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_busy(&self) {
|
||||
for _ in 0..100000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
let mut timeout = 1000000;
|
||||
while timeout > 0 && self.regs.USR.matches_all(USR::BUSY::SET) {
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for DwUart {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
let inner = self.inner.get();
|
||||
let output = inner.output();
|
||||
let byte = output.io.lock().handle_irq();
|
||||
|
||||
if let Some(byte) = byte {
|
||||
inner.write_to_input(byte);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for DwUart {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
let baud_rate = 115200;
|
||||
let clk_baud_rate = self.clk_baud.rate()?;
|
||||
|
||||
// Prevent firmware (SBI in riscv64) from printing to UART while it's being
|
||||
// reset/initialized
|
||||
let guard = debug::MuteGuard::acquire();
|
||||
|
||||
let regs = DeviceMemoryIo::<Regs>::map(self.base, Default::default())?;
|
||||
let mut io = Io { regs };
|
||||
|
||||
if let Some(reset) = self.rst.as_ref() {
|
||||
reset.assert_for_cycles(100000)?;
|
||||
}
|
||||
|
||||
io.init(clk_baud_rate, baud_rate);
|
||||
|
||||
io.send(b'\r');
|
||||
io.send(b'\n');
|
||||
|
||||
drop(guard);
|
||||
|
||||
let input = TerminalInput::with_capacity(64)?;
|
||||
let output = Inner {
|
||||
io: IrqSafeSpinlock::new(io),
|
||||
};
|
||||
|
||||
let terminal = self.inner.init(Arc::new(Terminal::from_parts(
|
||||
TerminalOptions::const_default(),
|
||||
input,
|
||||
output,
|
||||
)));
|
||||
|
||||
DEVICE_REGISTRY
|
||||
.serial_terminal
|
||||
.register(terminal.clone(), Some(self.clone()))
|
||||
.ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
self.irq.register(self.clone())?;
|
||||
self.irq.enable()?;
|
||||
|
||||
let output = self.inner.get().output();
|
||||
let io = output.io.lock();
|
||||
io.regs.IER.modify(IER::ERBFI::SET);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"Synopsys DesignWare 8250 UART"
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugSink for DwUart {
|
||||
fn putc(&self, c: u8) -> Result<(), Error> {
|
||||
self.inner.get().putc_to_output(c)
|
||||
}
|
||||
|
||||
fn supports_control_sequences(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalOutput for Inner {
|
||||
fn write(&self, byte: u8) -> Result<(), Error> {
|
||||
self.io.lock().send(byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_multiple(&self, bytes: &[u8]) -> Result<usize, Error> {
|
||||
let mut lock = self.io.lock();
|
||||
for &byte in bytes {
|
||||
lock.send(byte);
|
||||
}
|
||||
Ok(bytes.len())
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["snps,dw-apb-uart"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
let irq = node.interrupt(0)?;
|
||||
let clk_baud = node.named_clock("baudclk")?;
|
||||
let clk_apb = node.named_clock("apb_pclk");
|
||||
let rst = node.reset(0);
|
||||
|
||||
Some(Arc::new(DwUart {
|
||||
base,
|
||||
irq,
|
||||
clk_baud,
|
||||
clk_apb,
|
||||
rst,
|
||||
|
||||
inner: OneTimeInit::new()
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,7 @@ extern crate compiler_builtins;
|
||||
extern crate ygg_driver_ahci;
|
||||
extern crate ygg_driver_net_rtl81xx;
|
||||
extern crate ygg_driver_nvme;
|
||||
extern crate ygg_driver_serial_8250;
|
||||
extern crate ygg_driver_usb_xhci;
|
||||
extern crate ygg_driver_virtio_blk;
|
||||
extern crate ygg_driver_virtio_gpu;
|
||||
|
||||
Reference in New Issue
Block a user