sifive: initial support for hifive unleashed a00

This commit is contained in:
2026-02-04 17:34:37 +02:00
parent 0e979a9e09
commit 4f648142c8
16 changed files with 934 additions and 15 deletions
+1
View File
@@ -67,6 +67,7 @@ ygg_driver_bsp_arm.path = "driver/bsp/arm" # PrimeCell components
ygg_driver_bsp_riscv.path = "driver/bsp/riscv"
ygg_driver_net_stmmac.path = "driver/net/stmmac"
ygg_driver_bsp_jh7110.path = "driver/bsp/jh7110"
ygg_driver_bsp_sifive.path = "driver/bsp/sifive"
[target.'cfg(target_arch = "x86_64")'.dependencies]
yboot-proto.workspace = true
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "ygg_driver_bsp_sifive"
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
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
futures-util.workspace = true
+121
View File
@@ -0,0 +1,121 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle, Hertz},
device::Device,
};
use device_tree::{
DeviceTreePropertyRead, TProp,
driver::{DeviceTreeClockController, Node, ProbeContext, device_tree_driver},
};
use libk::error::Error;
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{interfaces::Readable, register_structs, registers::ReadWrite};
const CLK_COREPLL: u32 = 0;
const CLK_DDRPLL: u32 = 1;
const CLK_GEMGXLPLL: u32 = 2;
const CLK_TLCLK: u32 = 3;
register_structs! {
Regs {
(0x000 => hfxosccfg: ReadWrite<u32>),
(0x004 => corepllcfg0: ReadWrite<u32>),
(0x008 => _0),
(0x00C => ddrpllcfg0: ReadWrite<u32>),
(0x010 => ddrpllcfg1: ReadWrite<u32>),
(0x014 => _1),
(0x01C => gemgxlpllcfg0: ReadWrite<u32>),
(0x020 => gemgxlpllcfg1: ReadWrite<u32>),
(0x024 => coreclksel: ReadWrite<u32>),
(0x028 => deviceresetreg: ReadWrite<u32>),
(0x02C => _2),
(0x100 => @END),
}
}
struct Prci {
clk_osc: ClockHandle,
clk_rtc: ClockHandle,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
impl Device for Prci {
fn display_name(&self) -> &str {
"sifive,fu540-c000-prci"
}
}
impl ClockController for Prci {
fn clock_rate(&self, clock: Option<u32>) -> Result<Hertz, Error> {
let _ = &self.clk_rtc;
let regs = self.regs.lock();
match clock {
Some(CLK_TLCLK) => {
// 0 = CORE_PLL
// 1 = HFCLK
let coreclksel = regs.coreclksel.get() & 1 != 0;
let glcm = if coreclksel {
self.clk_osc.rate()?
} else {
todo!()
};
Ok(glcm / 2)
}
Some(_) => todo!(),
None => Err(Error::InvalidArgument),
}
}
fn set_clock_rate(&self, _clock: Option<u32>, _rate: Hertz) -> Result<Hertz, Error> {
Err(Error::NotImplemented)
}
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
match clock {
Some(CLK_GEMGXLPLL) | Some(CLK_DDRPLL) | Some(CLK_COREPLL) => todo!(),
Some(CLK_TLCLK) => Ok(()),
_ => Err(Error::InvalidArgument),
}
}
fn disable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
Err(Error::NotImplemented)
}
}
impl DeviceTreeClockController for Prci {
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
let clock = property.read_cell(offset, 1)? as u32;
Some((
ClockHandle {
parent: self.clone(),
clock: Some(clock),
},
1,
))
}
}
device_tree_driver! {
compatible: ["sifive,fu540-c000-prci"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let base = node.map_base(context, 0)?;
let clk_osc = node.clock(0)?;
let clk_rtc = node.clock(1)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let prci = Arc::new(Prci {
regs: IrqSafeSpinlock::new(regs),
clk_osc,
clk_rtc
});
node.make_clock_controller(prci.clone());
Some(prci)
}
}
}
+6
View File
@@ -0,0 +1,6 @@
#![no_std]
extern crate alloc;
mod clock;
mod uart;
+228
View File
@@ -0,0 +1,228 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockHandle, Hertz},
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,
error::Error,
vfs::{Terminal, TerminalInput, TerminalOutput},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::io::{TerminalOptions, TerminalOutputOptions};
register_bitfields! {
u32,
txctrl [
txen OFFSET(0) NUMBITS(1) [],
nstop OFFSET(1) NUMBITS(1) [
One = 0,
Two = 1
],
txcnt OFFSET(16) NUMBITS(3) [],
],
rxctrl [
rxen OFFSET(0) NUMBITS(1) [],
rxcnt OFFSET(16) NUMBITS(3) [],
],
interrupt [
txwm OFFSET(0) NUMBITS(1) [],
rxwm OFFSET(1) NUMBITS(1) [],
],
}
register_structs! {
Regs {
(0x00 => txdata: WriteOnly<u32>),
(0x04 => rxdata: ReadOnly<u32>),
(0x08 => txctrl: ReadWrite<u32, txctrl::Register>),
(0x0C => rxctrl: ReadWrite<u32, rxctrl::Register>),
(0x10 => ie: ReadWrite<u32, interrupt::Register>),
(0x14 => ip: ReadOnly<u32, interrupt::Register>),
(0x18 => div: ReadWrite<u32>),
(0x1C => _0),
(0x20 => @END),
}
}
struct Inner {
clock: ClockHandle,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
struct Uart {
name: &'static str,
irq: IrqHandle,
clock: ClockHandle,
base: PhysicalAddress,
inner: OneTimeInit<Arc<Terminal<Inner>>>,
}
impl Regs {
fn set_baud_rate(&self, input_clk: Hertz, baud: u32) -> Result<(), Error> {
let div = Hertz::divider(input_clk, Hertz::from(baud)).ok_or(Error::InvalidArgument)?;
if div >= (1 << 20) || div == 0 {
return Err(Error::InvalidArgument);
}
let div = div - 1;
self.div.set(div);
Ok(())
}
fn baud_rate(&self, input_clk: Hertz) -> u32 {
let div = self.div.get() + 1;
(input_clk / div).0 as u32
}
fn write(&self, byte: u8) {
while !self.ip.matches_all(interrupt::txwm::SET) {
core::hint::spin_loop();
}
self.txdata.set(byte as u32);
}
}
impl Device for Uart {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let regs = unsafe { DeviceMemoryIo::<Regs>::map(self.base, Default::default()) }?;
self.clock.enable()?;
let input_clk = self.clock.rate()?;
{
let _guard = debug::MuteGuard::acquire();
regs.txctrl.write(txctrl::txen::CLEAR);
regs.rxctrl.write(rxctrl::rxen::CLEAR);
regs.set_baud_rate(input_clk, 115200)?;
regs.txctrl.write(txctrl::txen::SET + txctrl::txcnt.val(3));
regs.rxctrl.write(rxctrl::rxen::SET + rxctrl::rxcnt.val(0));
}
let input = TerminalInput::with_capacity(64)?;
let output = Inner {
regs: IrqSafeSpinlock::new(regs),
clock: self.clock.clone(),
};
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 regs = self.inner.get().output().regs.lock();
regs.ie.write(interrupt::rxwm::SET);
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl InterruptHandler for Uart {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let terminal = self.inner.get();
let byte = {
let regs = terminal.output().regs.lock();
if regs.ip.matches_all(interrupt::rxwm::SET) {
regs.rxdata.get() as u8
} else {
return false;
}
};
terminal.write_to_input(byte);
true
}
}
impl DebugSink for Uart {
fn putc(&self, c: u8) -> Result<(), Error> {
self.inner.get().putc_to_output(c)
}
fn puts(&self, s: &str) -> Result<(), Error> {
self.inner.get().write_to_output(s.as_bytes())?;
Ok(())
}
fn supports_control_sequences(&self) -> bool {
true
}
}
impl TerminalOutput for Inner {
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(b'\r');
}
regs.write(byte);
Ok(())
}
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(b'\r');
}
regs.write(byte);
}
Ok(bytes.len())
}
fn baud_rate(&self) -> u32 {
let input_clk = self.clock.rate().unwrap();
self.regs.lock().baud_rate(input_clk)
}
fn set_baud_rate(&self, baud: u32) -> Result<(), Error> {
let input_clk = self.clock.rate().unwrap();
self.regs.lock().set_baud_rate(input_clk, baud)
}
}
device_tree_driver! {
compatible: ["sifive,uart0"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let name = node.name().unwrap_or("uart");
let base = node.map_base(context, 0)?;
let clock = node.clock(0)?;
let irq = node.interrupt(0)?;
let uart = Arc::new(Uart {
name,
irq,
clock,
base,
inner: OneTimeInit::new()
});
Some(uart)
}
}
}
+2
View File
@@ -7,11 +7,13 @@ pub use freq::{Hertz, IntoHertz};
mod freq;
#[derive(Clone)]
pub struct ClockHandle {
pub parent: Arc<dyn ClockController>,
pub clock: Option<u32>,
}
#[derive(Clone)]
pub struct ResetHandle {
pub parent: Arc<dyn ResetController>,
pub reset: Option<u32>,
+10 -2
View File
@@ -132,7 +132,10 @@ impl PhysicalMemoryManager {
/// Allocates a contiguous range of physical pages, marking it as used with `usage`
pub fn alloc_contiguous_pages(&mut self, count: usize) -> Result<PhysicalAddress, Error> {
'l0: for i in self.last_free_bit..self.page_count {
if count >= self.page_count {
return Err(Error::OutOfMemory);
}
'l0: for i in self.last_free_bit..self.page_count - count {
for j in 0..count {
if self.is_alloc(i + j) {
continue 'l0;
@@ -164,8 +167,13 @@ impl PhysicalMemoryManager {
/// `addr` must be a page-aligned physical address previously allocated by this implementation.
pub unsafe fn free_page(&mut self, page: PhysicalAddress) {
let page = page.try_into_usize().unwrap();
assert!(page >= self.offset);
if page < self.offset {
panic!("Physical page below managed area: {page:#x}");
}
let index = (page - self.offset) / L3_PAGE_SIZE;
if index >= self.page_count {
panic!("Physical page above managed area: {page:#x}");
}
STATS.used_pages.fetch_sub(1, Ordering::Relaxed);
+4
View File
@@ -204,6 +204,10 @@ pub unsafe fn init_from_iter<I: Iterator<Item = PhysicalMemoryRegion> + Clone>(
ArchitectureImpl::halt();
}
/*
000000:?:libk_mm::phys:207: page_bitmap_phys_base=0x80060000
000000:?:libk_mm::phys:208: total_count=32768
*/
let mut manager = PhysicalMemoryManager::new(
page_bitmap_phys_base,
phys_start
+1
View File
@@ -80,6 +80,7 @@ cfg_if::cfg_if! {
} else if #[cfg(target_arch = "riscv64")] {
extern crate ygg_driver_bsp_riscv;
extern crate ygg_driver_bsp_jh7110;
extern crate ygg_driver_bsp_sifive;
extern crate ygg_driver_net_stmmac;
}