218 lines
6.6 KiB
Rust
218 lines
6.6 KiB
Rust
//! Starfive JH7110 Always-on CRG
|
|
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::DeviceMemoryIo};
|
|
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
|
use tock_registers::{
|
|
interfaces::{Readable, Writeable},
|
|
register_structs,
|
|
registers::ReadWrite,
|
|
};
|
|
|
|
const PARENT_OSC: usize = 0;
|
|
// const PARENT_GMAC0_RMII_REFIN: usize = 1;
|
|
// const PARENT_GMAC0_RGMII_RXIN: usize = 2;
|
|
const PARENT_STG_AXIAHB: usize = 3;
|
|
// const PARENT_APB_BUS: usize = 4;
|
|
// const PARENT_GMAC0_GTXCLK: usize = 5;
|
|
// const PARENT_RTC_OSC: usize = 6;
|
|
|
|
register_structs! {
|
|
#[allow(non_snake_case)]
|
|
Regs {
|
|
(0x000 => CLOCKS: [ReadWrite<u32>; 14]),
|
|
(0x038 => RESETS: ReadWrite<u32>),
|
|
(0x03C => AONCRG_RESET_STATUS: ReadWrite<u32>),
|
|
(0x040 => @END),
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
enum ClockRegister {
|
|
Gate(u32, ClockParent),
|
|
#[allow(unused)]
|
|
FixedDiv(u32, ClockParent),
|
|
// TODO
|
|
Fixed,
|
|
Unimp,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
enum ClockParent {
|
|
Int(u32),
|
|
Ext(usize),
|
|
}
|
|
|
|
/// JH7110 AONCRG driver
|
|
pub struct Aoncrg {
|
|
base: PhysicalAddress,
|
|
clock_parents: [ClockHandle; 7],
|
|
mapping: OneTimeInit<IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>>,
|
|
}
|
|
|
|
impl Aoncrg {
|
|
fn ensure_init(&self) -> Result<&IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>, Error> {
|
|
self.mapping.or_try_init_with(|| {
|
|
unsafe { DeviceMemoryIo::map(self.base, Default::default()) }.map(IrqSafeSpinlock::new)
|
|
})
|
|
}
|
|
|
|
fn clk_enable_int(&self, index: u32) -> Result<(), Error> {
|
|
let (name, reg) = Self::map_clock_index(index)
|
|
.ok_or(Error::InvalidArgument)
|
|
.inspect_err(|_| log::warn!("jh7110-syscrg: undefined clock {:?}", index))?;
|
|
let regs = self.ensure_init()?;
|
|
|
|
match reg {
|
|
ClockRegister::Fixed => Ok(()),
|
|
ClockRegister::Gate(bit, parent) => {
|
|
self.clk_enable_parent(parent)?;
|
|
log::info!("jh7110-aoncrg: enable {name:?} @ {index}");
|
|
let lock = regs.lock();
|
|
lock.CLOCKS[index as usize].set(lock.CLOCKS[index as usize].get() | (1 << bit));
|
|
Ok(())
|
|
}
|
|
ClockRegister::Unimp => {
|
|
log::warn!("jh7110-aoncrg: unimplemented clock {name:?}");
|
|
Err(Error::NotImplemented)
|
|
}
|
|
ClockRegister::FixedDiv(_, parent) => {
|
|
self.clk_enable_parent(parent)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
fn clk_enable_parent(&self, parent: ClockParent) -> Result<(), Error> {
|
|
match parent {
|
|
ClockParent::Int(index) => self.clk_enable_int(index),
|
|
ClockParent::Ext(clock) => self.clock_parents[clock].enable(),
|
|
}
|
|
}
|
|
|
|
fn map_clock_index(index: u32) -> Option<(&'static str, ClockRegister)> {
|
|
use ClockParent::*;
|
|
use ClockRegister::*;
|
|
|
|
const CLOCK_MAP: &[(&'static str, ClockRegister)] = &[
|
|
/* 0 */ ("clk_osc", FixedDiv(4, Ext(PARENT_OSC))),
|
|
/* 1 */ ("clk_aon_apb_func", Unimp),
|
|
/* 2 */ ("clk_gmac5_ahb", Gate(31, Ext(PARENT_STG_AXIAHB))),
|
|
/* 3 */ ("clk_gmac5_axi", Gate(31, Ext(PARENT_STG_AXIAHB))),
|
|
/* 4 */ ("clk_gmac5_rmii_rtx", Unimp),
|
|
/* 5 */ ("clk_gmac5_axi64_tx", Fixed),
|
|
/* 6 */ ("clk_gmac5_axi64_tx_inv", Gate(30, Int(5))),
|
|
];
|
|
|
|
CLOCK_MAP.get(index as usize).copied()
|
|
}
|
|
}
|
|
|
|
impl Device for Aoncrg {
|
|
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
|
log::warn!("TODO: init jh7110-aoncrg @ {:#x}", self.base);
|
|
Ok(())
|
|
}
|
|
|
|
fn display_name(&self) -> &str {
|
|
"Starfive JH7110 AONCRG"
|
|
}
|
|
}
|
|
|
|
impl ClockController for Aoncrg {
|
|
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
|
|
let index = clock.ok_or(Error::InvalidArgument)?;
|
|
self.clk_enable_int(index)
|
|
}
|
|
|
|
fn disable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
|
|
log::warn!("jh7110: disable clock {clock:?}");
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl ResetController for Aoncrg {
|
|
fn assert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
|
|
let reset = reset.ok_or(Error::InvalidArgument)?;
|
|
let mapping = self.ensure_init()?;
|
|
let regs = mapping.lock();
|
|
let val = regs.RESETS.get();
|
|
regs.RESETS.set(val | (1 << reset));
|
|
Ok(())
|
|
}
|
|
|
|
fn deassert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
|
|
let reset = reset.ok_or(Error::InvalidArgument)?;
|
|
let mapping = self.ensure_init()?;
|
|
let regs = mapping.lock();
|
|
let val = regs.RESETS.get();
|
|
regs.RESETS.set(val & !(1 << reset));
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl DeviceTreeClockController for Aoncrg {
|
|
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
|
|
let cell = property.read_cell(offset, 1)? as u32;
|
|
Some((
|
|
ClockHandle {
|
|
parent: self,
|
|
clock: Some(cell),
|
|
},
|
|
1,
|
|
))
|
|
}
|
|
}
|
|
|
|
impl DeviceTreeResetController for Aoncrg {
|
|
fn map_reset(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ResetHandle, usize)> {
|
|
let cell = property.read_cell(offset, 1)? as u32;
|
|
Some((
|
|
ResetHandle {
|
|
parent: self,
|
|
reset: Some(cell),
|
|
},
|
|
1,
|
|
))
|
|
}
|
|
}
|
|
|
|
device_tree_driver! {
|
|
compatible: ["starfive,jh7110-aoncrg"],
|
|
driver: {
|
|
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
|
let base = node.map_base(context, 0)?;
|
|
|
|
let clock_parents = [
|
|
node.named_clock("osc")?,
|
|
node.named_clock("gmac0_rmii_refin")?,
|
|
node.named_clock("gmac0_rgmii_rxin")?,
|
|
node.named_clock("stg_axiahb")?,
|
|
node.named_clock("apb_bus")?,
|
|
node.named_clock("gmac0_gtxclk")?,
|
|
node.named_clock("rtc_osc")?,
|
|
];
|
|
|
|
let aoncrg = Arc::new(Aoncrg {
|
|
base,
|
|
clock_parents,
|
|
mapping: OneTimeInit::new()
|
|
});
|
|
node.make_reset_controller(aoncrg.clone());
|
|
node.make_clock_controller(aoncrg.clone());
|
|
Some(aoncrg)
|
|
}
|
|
}
|
|
}
|