rv64/jh7110: implement syscrg stub + uart clock/reset

This commit is contained in:
Mark Poliakov 2025-01-23 14:08:44 +02:00
parent 975df985ac
commit 5d406feb07
11 changed files with 550 additions and 34 deletions

View File

@ -1,10 +1,73 @@
use alloc::sync::Arc;
use yggdrasil_abi::error::Error;
use crate::device::Device;
// TODO refine `clock` parameter types somehow
pub trait ClockController: Device {
fn enable_clock(&self, clock: u32) -> Result<(), Error>;
fn disable_clock(&self, clock: u32) -> Result<(), Error>;
pub struct ClockHandle {
pub parent: Arc<dyn ClockController>,
pub clock: Option<u32>,
}
pub struct ResetHandle {
pub parent: Arc<dyn ResetController>,
pub reset: Option<u32>,
}
pub trait ClockController: Device {
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error>;
fn disable_clock(&self, clock: Option<u32>) -> Result<(), Error>;
fn clock_rate(&self, clock: Option<u32>) -> Result<u64, Error> {
let _ = clock;
Err(Error::NotImplemented)
}
fn set_clock_rate(&self, clock: Option<u32>, rate: u64) -> Result<u64, Error> {
let _ = clock;
let _ = rate;
Err(Error::NotImplemented)
}
}
pub trait ResetController: Device {
fn assert_reset(&self, reset: Option<u32>) -> Result<(), Error>;
fn deassert_reset(&self, reset: Option<u32>) -> Result<(), Error>;
}
impl ClockHandle {
pub fn enable(&self) -> Result<(), Error> {
self.parent.enable_clock(self.clock)
}
pub fn disable(&self) -> Result<(), Error> {
self.parent.disable_clock(self.clock)
}
pub fn rate(&self) -> Result<u64, Error> {
self.parent.clock_rate(self.clock)
}
pub fn set_rate(&self, rate: u64) -> Result<u64, Error> {
self.parent.set_clock_rate(self.clock, rate)
}
}
impl ResetHandle {
pub fn assert(&self) -> Result<(), Error> {
self.parent.assert_reset(self.reset)
}
pub fn deassert(&self) -> Result<(), Error> {
self.parent.deassert_reset(self.reset)
}
pub fn assert_for_cycles(&self, cycles: u64) -> Result<(), Error> {
self.assert()?;
for _ in 0..cycles {
core::hint::spin_loop();
}
self.deassert()?;
Ok(())
}
}

View File

@ -1,12 +1,57 @@
//! Device-tree handling for controllers of various types: interrupt, clock etc.
use alloc::sync::Arc;
use device_api::interrupt::FullIrq;
use device_api::{
clock::{ClockHandle, ResetHandle},
interrupt::FullIrq,
};
use fdt_rs::spec::Phandle;
use crate::TProp;
use crate::{DeviceTreePropertyRead, TProp};
use super::{lookup_phandle, Node};
pub(crate) struct ClockIter<'dt> {
pub(crate) clocks: TProp<'dt>,
pub(crate) offset: usize,
}
pub(crate) struct ResetIter<'dt> {
pub(crate) resets: TProp<'dt>,
pub(crate) offset: usize,
}
impl Iterator for ClockIter<'_> {
type Item = ClockHandle;
fn next(&mut self) -> Option<Self::Item> {
if self.offset >= self.clocks.len() {
return None;
}
let phandle = self.clocks.read_cell(self.offset, 1)? as Phandle;
let clkc = lookup_phandle(phandle, true)?;
let clkc = clkc.clock_controler.try_get()?;
let (clock, len) = clkc.clone().map_clock(&self.clocks, self.offset + 1)?;
self.offset += len + 1;
Some(clock)
}
}
impl Iterator for ResetIter<'_> {
type Item = ResetHandle;
fn next(&mut self) -> Option<Self::Item> {
if self.offset >= self.resets.len() {
return None;
}
let phandle = self.resets.read_cell(self.offset, 1)? as Phandle;
let rstc = lookup_phandle(phandle, true)?;
let rstc = rstc.reset_controller.try_get()?;
let (reset, len) = rstc.clone().map_reset(&self.resets, self.offset + 1)?;
self.offset += len + 1;
Some(reset)
}
}
// Interrupt controller handling
/// Reads interrupt information, as interpreted by `interrupt_controller`, from `property` at a

View File

@ -14,7 +14,10 @@ pub mod util;
pub use controller::{map_interrupt, map_interrupt_at};
pub use macros::device_tree_driver;
pub use registry::{lookup_phandle, register_driver};
pub use traits::{DeviceTreeInterruptController, Driver, ProbeContext};
pub use traits::{
DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreeResetController, Driver,
ProbeContext,
};
pub use tree::{find_node, unflatten_device_tree, walk_device_tree, Node};
/// Performs a walk of the device tree and initializes nodes which haven't already been

View File

@ -4,6 +4,7 @@ use core::ops::Range;
use alloc::sync::{Arc, Weak};
use device_api::{
bus::Bus,
clock::{ClockHandle, ResetHandle},
device::Device,
interrupt::{ExternalInterruptController, FullIrq},
};
@ -28,6 +29,20 @@ pub trait DeviceTreeInterruptController {
fn as_interrupt_controller(self: Arc<Self>) -> Arc<dyn ExternalInterruptController>;
}
/// Device-tree based clock source interface
pub trait DeviceTreeClockController {
/// Reads clock information from `property` at given `offset` and maps it to a
/// [ClockHandle], returning the handle + the size of the clock entry.
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)>;
}
/// Device-tree based reset source interface
pub trait DeviceTreeResetController {
/// Reads reset information from `property` at given `offset` and maps it to a
/// [ResetHandle], returning the handle + the size of the reset entry.
fn map_reset(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ResetHandle, usize)>;
}
/// Context passed to the driver's `probe` function
pub struct ProbeContext {
pub(crate) bus: Option<Weak<dyn Bus>>,

View File

@ -7,7 +7,7 @@ use alloc::{
};
use device_api::{
bus::Bus,
clock::ClockController,
clock::{ClockController, ClockHandle, ResetHandle},
device::{Device, DeviceInitContext},
interrupt::{ExternalInterruptController, FullIrq, MessageInterruptController},
};
@ -20,9 +20,11 @@ use yggdrasil_abi::error::Error;
use crate::{DeviceTree, DeviceTreeNodeExt, DeviceTreePropertyRead, TNode, TProp};
use super::{
controller::{ClockIter, ResetIter},
lookup_phandle, map_interrupt,
registry::{register_phandle, DEVICE_TREE, DRIVERS, ROOT},
DeviceTreeInterruptController, Driver, ProbeContext,
DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreeResetController, Driver,
ProbeContext,
};
/// Represents a single node in the device tree, which may or may not:
@ -47,8 +49,11 @@ pub struct Node {
// Driver/device info
device: OneTimeInit<NodeDevice>,
init_token: OneTimeInit<()>,
pub(crate) interrupt_controller: OneTimeInit<Arc<dyn DeviceTreeInterruptController>>,
pub(crate) msi_controller: OneTimeInit<Arc<dyn MessageInterruptController>>,
pub(crate) clock_controler: OneTimeInit<Arc<dyn DeviceTreeClockController>>,
pub(crate) reset_controller: OneTimeInit<Arc<dyn DeviceTreeResetController>>,
}
enum NodeDevice {
@ -170,6 +175,18 @@ impl Node {
self.msi_controller.init(intc);
}
/// Informs the node of its capability as a reset controller, allowing `resets` to be mapped
/// through this device.
pub fn make_reset_controller(&self, rstc: Arc<dyn DeviceTreeResetController>) {
self.reset_controller.init(rstc);
}
/// Informs the node of its capability as a clock controller, allowing `clocks` to be mapped
/// through this device.
pub fn make_clock_controller(&self, clkc: Arc<dyn DeviceTreeClockController>) {
self.clock_controler.init(clkc);
}
/// Returns the device driver associated with this node, if any was probed.
pub fn driver(&self) -> Option<&'static dyn Driver> {
self.device.try_get()?.driver()
@ -287,6 +304,44 @@ impl Node {
.map(|range| PhysicalAddress::from_u64(range.start))
}
/// Returns an input `clock` handle for a given reset name
pub fn named_clock(&self, name: &str) -> Option<ClockHandle> {
self.property("clock-names")?
.as_str_list()
.position(|n| n == name)
.and_then(|i| self.clock(i))
}
/// Returns a `reset` handle for a given reset name
pub fn named_reset(&self, name: &str) -> Option<ResetHandle> {
self.property("reset-names")?
.as_str_list()
.position(|n| n == name)
.and_then(|i| self.reset(i))
}
/// Returns an iterator over the node's input clocks
pub fn clocks(&self) -> Option<impl Iterator<Item = ClockHandle>> {
let clocks = self.property("clocks")?;
Some(ClockIter { clocks, offset: 0 })
}
/// Returns an iterator over the node's resets
pub fn resets(&self) -> Option<impl Iterator<Item = ResetHandle>> {
let resets = self.property("resets")?;
Some(ResetIter { resets, offset: 0 })
}
/// Returns the `index`th input clock of the node
pub fn clock(&self, index: usize) -> Option<ClockHandle> {
self.clocks()?.nth(index)
}
/// Returns the `index`th reset of the node
pub fn reset(&self, index: usize) -> Option<ResetHandle> {
self.resets()?.nth(index)
}
/// Reads interrupt information from `interrupts[index]` property, mapped by the node's
/// `interrupt-parent`.
pub fn interrupt(&self, index: usize) -> Option<FullIrq> {
@ -349,6 +404,11 @@ impl Node {
pub fn prop_usize(&self, name: &str) -> Option<usize> {
self.dt_node.prop_cell_usize(name)
}
/// Interprets property `name` as a string value
pub fn prop_str(&self, name: &str) -> Option<&'static str> {
self.dt_node.prop_string(name)
}
}
impl fmt::Debug for Node {
@ -397,8 +457,11 @@ fn unflatten_node(
device: OneTimeInit::new(),
init_token: OneTimeInit::new(),
interrupt_controller: OneTimeInit::new(),
msi_controller: OneTimeInit::new(),
clock_controler: OneTimeInit::new(),
reset_controller: OneTimeInit::new(),
});
if let Some(phandle) = phandle {

View File

@ -1,4 +1,6 @@
//! Device tree nodes' property accessors
use core::marker::PhantomData;
use fdt_rs::{
base::iters::StringPropIter,
index::DevTreeIndexProp,
@ -9,7 +11,7 @@ use fdt_rs::{
pub trait CellTuple: Sized {
type Sizes: Copy;
fn read<P: DeviceTreePropertyRead + ?Sized>(
fn read<'a, P: DeviceTreePropertyRead<'a> + ?Sized>(
property: &P,
offset: usize,
sizes: Self::Sizes,
@ -18,16 +20,16 @@ pub trait CellTuple: Sized {
}
/// Accessor trait for a device tree property
pub trait DeviceTreePropertyRead {
pub trait DeviceTreePropertyRead<'a> {
/// Reads a cell value of `size` at a given `offset`.
///
/// **NOTE** supported cell sizes are 1 and 2. If the size is 3 and larger,
/// perform multiple split reads instead.
fn read_cell(&self, offset: usize, size: usize) -> Option<u64>;
/// Reads the property as a single string
fn as_str(&self) -> Option<&str>;
fn as_str(&self) -> Option<&'a str>;
/// Reads the property as an iterator over a `<stringlist>`
fn as_str_list(&self) -> impl Iterator<Item = &str>;
fn as_str_list(&self) -> impl Iterator<Item = &'a str>;
/// Returns the length of the property in bytes
fn len(&self) -> usize;
@ -44,11 +46,12 @@ pub trait DeviceTreePropertyRead {
/// Reads the property as an iterator over uniformly-sized tuples, specified by the
/// `sizes` parameter.
fn iter_cells<T: CellTuple>(&self, sizes: T::Sizes) -> CellTupleIter<Self, T> {
fn iter_cells<T: CellTuple>(&self, sizes: T::Sizes) -> CellTupleIter<'a, '_, Self, T> {
CellTupleIter {
property: self,
offset: 0,
sizes,
_pd: PhantomData,
}
}
@ -59,10 +62,11 @@ pub trait DeviceTreePropertyRead {
}
/// An iterator over the tuple cells of some device tree node's property
pub struct CellTupleIter<'a, P: DeviceTreePropertyRead + ?Sized, T: CellTuple> {
property: &'a P,
pub struct CellTupleIter<'a, 'p, P: DeviceTreePropertyRead<'a> + ?Sized, T: CellTuple> {
property: &'p P,
offset: usize,
sizes: T::Sizes,
_pd: PhantomData<&'a ()>,
}
/// An iterator over the string list propoerty
@ -70,7 +74,7 @@ pub struct StringListIter<'a> {
inner: StringPropIter<'a>,
}
impl DeviceTreePropertyRead for DevTreeIndexProp<'_, '_, '_> {
impl<'a> DeviceTreePropertyRead<'a> for DevTreeIndexProp<'a, '_, '_> {
fn read_cell(&self, offset: usize, size: usize) -> Option<u64> {
match size {
1 => self.u32(offset).ok().map(Into::into),
@ -84,11 +88,11 @@ impl DeviceTreePropertyRead for DevTreeIndexProp<'_, '_, '_> {
}
}
fn as_str(&self) -> Option<&str> {
fn as_str(&self) -> Option<&'a str> {
self.str().ok()
}
fn as_str_list(&self) -> impl Iterator<Item = &str> {
fn as_str_list(&self) -> impl Iterator<Item = &'a str> {
StringListIter {
inner: self.iter_str(),
}
@ -99,7 +103,9 @@ impl DeviceTreePropertyRead for DevTreeIndexProp<'_, '_, '_> {
}
}
impl<P: DeviceTreePropertyRead + ?Sized, T: CellTuple> Iterator for CellTupleIter<'_, P, T> {
impl<'a, P: DeviceTreePropertyRead<'a> + ?Sized, T: CellTuple> Iterator
for CellTupleIter<'a, '_, P, T>
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
@ -153,7 +159,7 @@ macro impl_cell_tuples(
impl CellTuple for impl_cell_tuple_types!($($index (u64))+) {
type Sizes = impl_cell_tuple_types!($($index (usize))+);
fn read<P: DeviceTreePropertyRead + ?Sized>(
fn read<'a, P: DeviceTreePropertyRead<'a> + ?Sized>(
property: &P,
offset: usize,
sizes: Self::Sizes,

View File

@ -4,7 +4,11 @@ use alloc::{
string::{String, ToString},
sync::Arc,
};
use core::{fmt, str::FromStr};
use core::{
fmt,
str::FromStr,
sync::atomic::{AtomicBool, Ordering},
};
use ring::RingLoggerSink;
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
@ -28,6 +32,22 @@ pub use ring::add_kernel_log_file;
pub use sink::{add_early_sink, add_serial_sink, add_sink, disable_early_sinks, DebugSink};
static DEBUG_LOCK: IrqSafeSpinlock<()> = IrqSafeSpinlock::new(());
static MUTE_DEBUG: AtomicBool = AtomicBool::new(false);
pub struct MuteGuard(bool);
impl MuteGuard {
pub fn acquire() -> Self {
let muted = MUTE_DEBUG.swap(true, Ordering::Acquire);
Self(muted)
}
}
impl Drop for MuteGuard {
fn drop(&mut self) {
MUTE_DEBUG.store(self.0, Ordering::Release);
}
}
struct KernelLoggerSink;
@ -90,6 +110,10 @@ impl log::Log for KernelLoggerSink {
}
fn log(&self, record: &log::Record) {
if MUTE_DEBUG.load(Ordering::Acquire) {
return;
}
if !self.enabled(record.metadata()) {
return;
}

View File

@ -0,0 +1,75 @@
//! Fixed clock driver
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle},
device::{Device, DeviceInitContext},
};
use device_tree::{
driver::{device_tree_driver, DeviceTreeClockController, Node, ProbeContext},
DeviceTreePropertyRead, TProp,
};
struct FixedClock {
name: &'static str,
frequency: u64,
}
impl Device for FixedClock {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl ClockController for FixedClock {
fn clock_rate(&self, clock: Option<u32>) -> Result<u64, Error> {
debug_assert!(clock.is_none());
Ok(self.frequency)
}
fn set_clock_rate(&self, _clock: Option<u32>, _rate: u64) -> Result<u64, Error> {
Err(Error::InvalidOperation)
}
fn enable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
Err(Error::InvalidOperation)
}
fn disable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
Err(Error::InvalidOperation)
}
}
impl DeviceTreeClockController for FixedClock {
fn map_clock(
self: Arc<Self>,
_property: &TProp,
_offset: usize,
) -> Option<(ClockHandle, usize)> {
Some((
ClockHandle {
clock: None,
parent: self,
},
0,
))
}
}
device_tree_driver! {
compatible: ["fixed-clock"],
driver: {
fn probe(&self, node: &Arc<Node>, _context: &ProbeContext) -> Option<Arc<dyn Device>> {
let name = node.name()?;
let frequency = node.property("clock-frequency")?.read_cell(0, 1)?;
log::info!("fixed-clock {name}: {frequency}");
let fixed = Arc::new(FixedClock { name, frequency });
node.make_clock_controller(fixed.clone());
Some(fixed)
}
}
}

View File

@ -0,0 +1,141 @@
//! Starfive JH7110 System Control Registers
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle, ResetController, ResetHandle},
device::{Device, DeviceInitContext},
};
use device_tree::{
driver::{
device_tree_driver, DeviceTreeClockController, DeviceTreeResetController, Node,
ProbeContext,
},
DeviceTreePropertyRead, TProp,
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
const CLK_OSC: usize = 0;
const CLK_UART0_CORE: u32 = 146;
struct Syscrg {
base: PhysicalAddress,
parents: [ClockHandle; 1],
mapping: OneTimeInit<IrqSafeSpinlock<DeviceMemoryIoMut<'static, [u32]>>>,
}
impl Syscrg {
fn ensure_init(&self) -> Result<&IrqSafeSpinlock<DeviceMemoryIoMut<'static, [u32]>>, Error> {
self.mapping.or_try_init_with(move || {
unsafe { DeviceMemoryIoMut::map_slice(self.base, 256, Default::default()) }
.map(IrqSafeSpinlock::new)
})
}
}
impl Device for Syscrg {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
log::warn!("TODO: init jh7110-syscrg @ {:#x}", self.base);
Ok(())
}
fn display_name(&self) -> &str {
"Starfive JH7110 SYSCRG"
}
}
impl ClockController for Syscrg {
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
log::warn!("TODO: jh7110-syscrg: enable clock {clock:?}");
Ok(())
}
fn disable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
log::warn!("TODO: jh7110-syscrg: disable clock {clock:?}");
Ok(())
}
fn clock_rate(&self, clock: Option<u32>) -> Result<u64, Error> {
match clock.ok_or(Error::InvalidArgument)? {
CLK_UART0_CORE => self.parents[CLK_OSC].rate(),
_ => {
log::warn!("TODO: jh7110-syscrg: read rate {:#x?}", clock);
Err(Error::NotImplemented)
}
}
}
fn set_clock_rate(&self, clock: Option<u32>, rate: u64) -> Result<u64, Error> {
log::warn!("TODO: jh7110-syscrg: set rate {clock:?} -> {rate}");
Ok(rate)
}
}
impl ResetController for Syscrg {
fn assert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
let reset = reset.ok_or(Error::InvalidArgument)?;
let reg = reset / 32;
let bit = reset % 32;
let regs = self.ensure_init()?;
regs.lock()[190 + reg as usize] |= 1 << bit;
Ok(())
}
fn deassert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
let reset = reset.ok_or(Error::InvalidArgument)?;
let reg = reset / 32;
let bit = reset % 32;
let regs = self.ensure_init()?;
regs.lock()[190 + reg as usize] &= !(1 << bit);
Ok(())
}
}
impl DeviceTreeClockController for Syscrg {
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
let clock = property.read_cell(offset, 1)? as u32;
Some((
ClockHandle {
clock: Some(clock),
parent: self.clone(),
},
1,
))
}
}
impl DeviceTreeResetController for Syscrg {
fn map_reset(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ResetHandle, usize)> {
let reset = property.read_cell(offset, 1)? as u32;
Some((
ResetHandle {
reset: Some(reset),
parent: self.clone(),
},
1,
))
}
}
device_tree_driver! {
compatible: ["starfive,jh7110-syscrg"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>> {
let base = node.map_base(context, 0)?;
let osc = node.named_clock("osc")?;
let syscrg = Arc::new(Syscrg {
base,
parents: [osc],
mapping: OneTimeInit::new(),
});
node.make_reset_controller(syscrg.clone());
node.make_clock_controller(syscrg.clone());
Some(syscrg)
}
}
}

View File

@ -2,3 +2,9 @@
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
pub mod bcm2835_aux;
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
pub mod jh7110_syscrg;
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
pub mod fixed;

View File

@ -2,12 +2,13 @@
use abi::{error::Error, io::TerminalOptions};
use alloc::sync::Arc;
use device_api::{
clock::{ClockHandle, ResetHandle},
device::{Device, DeviceInitContext},
interrupt::{FullIrq, InterruptHandler, IrqVector},
};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use libk::{
debug::DebugSink,
debug::{self, DebugSink},
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
vfs::{Terminal, TerminalInput, TerminalOutput},
};
@ -21,20 +22,29 @@ use tock_registers::{
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) [],
PTIME OFFSET(7) NUMBITS(1) [],
EDSSI OFFSET(3) NUMBITS(1) [],
ELSI OFFSET(2) NUMBITS(1) [],
// Transmit buffer available
ETBEI OFFSET(1) NUMBITS(1) [],
ETBEI OFFSET(1) NUMBITS(1) [],
// Receive data available
ERBFI OFFSET(0) NUMBITS(1) [],
ERBFI OFFSET(0) NUMBITS(1) [],
],
LSR [
// Data ready bit
DR OFFSET(0) NUMBITS(1) [],
DR OFFSET(0) NUMBITS(1) [],
// Transmitter holding register empty
THRE OFFSET(5) NUMBITS(1) [],
THRE OFFSET(5) NUMBITS(1) [],
],
USR [
BUSY OFFSET(0) NUMBITS(1) [],
]
}
@ -50,7 +60,7 @@ register_structs! {
// Read: interrupt identification register/Write: frame control register
(0x008 => IIR: ReadWrite<u32>),
// Line control register
(0x00C => LCR: ReadWrite<u32>),
(0x00C => LCR: ReadWrite<u32, LCR::Register>),
// Modem control register
(0x010 => MCR: ReadWrite<u32>),
// Line status register
@ -69,7 +79,7 @@ register_structs! {
(0x070 => FAR: ReadWrite<u32>),
(0x074 => TFR: ReadOnly<u32>),
(0x078 => RFW: WriteOnly<u32>),
(0x07C => USR: ReadOnly<u32>),
(0x07C => USR: ReadOnly<u32, USR::Register>),
(0x080 => TFL: ReadOnly<u32>),
(0x084 => RFL: ReadOnly<u32>),
(0x088 => SRR: WriteOnly<u32>),
@ -101,6 +111,11 @@ struct Inner {
pub struct DwUart {
base: PhysicalAddress,
irq: FullIrq,
clk_baud: ClockHandle,
#[allow(unused)]
clk_apb: Option<ClockHandle>,
rst: Option<ResetHandle>,
inner: OneTimeInit<Arc<Terminal<Inner>>>,
}
@ -117,8 +132,33 @@ impl Io {
self.regs.DR.set(byte as u32);
}
fn init(&mut self) {
fn init(&mut self, baud_clock: u64, baud_rate: u64) {
let divisor = (baud_clock / (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> {
@ -130,6 +170,17 @@ impl Io {
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 {
@ -149,9 +200,26 @@ impl InterruptHandler for DwUart {
impl Device for DwUart {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let regs = DeviceMemoryIo::map(self.base, Default::default())?;
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 };
io.init();
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 {
@ -219,10 +287,17 @@ device_tree_driver! {
fn probe(&self, node: &Arc<Node>, context: &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()
}))
}