sifive: initial support for hifive unleashed a00
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod clock;
|
||||
mod uart;
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user