jh7110: better clock driver
This commit is contained in:
@@ -36,7 +36,8 @@ impl ClockController for FixedClock {
|
||||
}
|
||||
|
||||
fn enable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
|
||||
Err(Error::InvalidOperation)
|
||||
// Assume always enabled
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
//! 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;
|
||||
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: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
}
|
||||
|
||||
impl Aoncrg {
|
||||
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))?;
|
||||
|
||||
match reg {
|
||||
ClockRegister::Fixed => Ok(()),
|
||||
ClockRegister::Gate(bit, parent) => {
|
||||
self.clk_enable_parent(parent)?;
|
||||
log::info!("jh7110-aoncrg: enable {name:?} @ {index}");
|
||||
let lock = self.mapping.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: &[(&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 regs = self.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 regs = self.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 mapping = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
|
||||
|
||||
let aoncrg = Arc::new(Aoncrg {
|
||||
base,
|
||||
clock_parents,
|
||||
mapping: IrqSafeSpinlock::new(mapping)
|
||||
});
|
||||
|
||||
node.make_reset_controller(aoncrg.clone());
|
||||
node.make_clock_controller(aoncrg.clone());
|
||||
Some(aoncrg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,514 @@
|
||||
//! StarFive JH7110 clock drivers
|
||||
|
||||
use core::{marker::PhantomData, ops::Index};
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockController, ClockHandle, ResetController, ResetHandle},
|
||||
device::Device,
|
||||
};
|
||||
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;
|
||||
|
||||
// SYSCRG clocks
|
||||
|
||||
const SYSCRG_CPU_ROOT: usize = 0x000 / 4;
|
||||
const SYSCRG_CPU_CORE: usize = 0x004 / 4;
|
||||
const SYSCRG_CPU_BUS: usize = 0x008 / 4;
|
||||
const SYSCRG_GPU_ROOT: usize = 0x00C / 4;
|
||||
const SYSCRG_PERH_ROOT: usize = 0x010 / 4;
|
||||
const SYSCRG_BUS_ROOT: usize = 0x014 / 4;
|
||||
const SYSCRG_NOCSTG_BUS: usize = 0x018 / 4;
|
||||
const SYSCRG_AXI_CFG0: usize = 0x01C / 4;
|
||||
const SYSCRG_STG_AXIAHB: usize = 0x020 / 4;
|
||||
const SYSCRG_AHB0: usize = 0x024 / 4;
|
||||
const SYSCRG_AHB1: usize = 0x028 / 4;
|
||||
const SYSCRG_APB_BUS: usize = 0x02C / 4;
|
||||
const SYSCRG_APB0: usize = 0x030 / 4;
|
||||
// gmac1
|
||||
const SYSCRG_GMAC1_AHB: usize = 0x184 / 4;
|
||||
const SYSCRG_GMAC1_AXI: usize = 0x188 / 4;
|
||||
const SYSCRG_GMAC_SRC: usize = 0x18C / 4;
|
||||
const SYSCRG_GMAC1_GTXCLK: usize = 0x190 / 4;
|
||||
const SYSCRG_GMAC1_RMII_RTX: usize = 0x194 / 4;
|
||||
const SYSCRG_GMAC1_PTP: usize = 0x198 / 4;
|
||||
const SYSCRG_GMAC1_RX: usize = 0x19C / 4;
|
||||
const SYSCRG_GMAC1_RX_INV: usize = 0x1A0 / 4;
|
||||
const SYSCRG_GMAC1_TX: usize = 0x1A4 / 4;
|
||||
const SYSCRG_GMAC1_TX_INV: usize = 0x1A8 / 4;
|
||||
const SYSCRG_GMAC1_GTXC: usize = 0x1AC / 4;
|
||||
// gmac0
|
||||
const SYSCRG_GMAC0_GTXCLK: usize = 0x1B0 / 4;
|
||||
const SYSCRG_GMAC0_PTP: usize = 0x1B4 / 4;
|
||||
const SYSCRG_GMAC_PHY: usize = 0x1B8 / 4;
|
||||
const SYSCRG_GMAC0_GTXC: usize = 0x1BC / 4;
|
||||
// TODO ...
|
||||
// uart
|
||||
const SYSCRG_UART0_APB: usize = 0x244 / 4;
|
||||
const SYSCRG_UART0_CORE: usize = 0x248 / 4;
|
||||
// jtag
|
||||
const SYSCRG_JTAG_CERTIFICATION_TRNG: usize = 0x2F4 / 4;
|
||||
const SYSCRG_CLOCK_COUNT: usize = SYSCRG_JTAG_CERTIFICATION_TRNG + 1;
|
||||
|
||||
const SYSCRG_OSC: usize = SYSCRG_CLOCK_COUNT;
|
||||
const SYSCRG_GMAC1_RMII_REFIN: usize = SYSCRG_CLOCK_COUNT + 1;
|
||||
const SYSCRG_GMAC1_RGMII_RXIN: usize = SYSCRG_CLOCK_COUNT + 2;
|
||||
#[allow(unused)]
|
||||
const SYSCRG_I2STX_BCLK_EXT: usize = SYSCRG_CLOCK_COUNT + 3;
|
||||
#[allow(unused)]
|
||||
const SYSCRG_I2STX_LRCK_EXT: usize = SYSCRG_CLOCK_COUNT + 4;
|
||||
#[allow(unused)]
|
||||
const SYSCRG_I2SRX_BCLK_EXT: usize = SYSCRG_CLOCK_COUNT + 5;
|
||||
#[allow(unused)]
|
||||
const SYSCRG_I2SRX_LRCK_EXT: usize = SYSCRG_CLOCK_COUNT + 6;
|
||||
#[allow(unused)]
|
||||
const SYSCRG_TDM_EXT: usize = SYSCRG_CLOCK_COUNT + 7;
|
||||
#[allow(unused)]
|
||||
const SYSCRG_MCLK_EXT: usize = SYSCRG_CLOCK_COUNT + 8;
|
||||
const SYSCRG_PLL0_OUT: usize = SYSCRG_CLOCK_COUNT + 9;
|
||||
const SYSCRG_PLL1_OUT: usize = SYSCRG_CLOCK_COUNT + 10;
|
||||
const SYSCRG_PLL2_OUT: usize = SYSCRG_CLOCK_COUNT + 11;
|
||||
|
||||
// AONCRG clocks
|
||||
|
||||
const AONCRG_OSC_DIV4: usize = 0x00 / 4;
|
||||
const AONCRG_APB_FUNC: usize = 0x04 / 4;
|
||||
// gmac0
|
||||
const AONCRG_GMAC0_AHB: usize = 0x08 / 4;
|
||||
const AONCRG_GMAC0_AXI: usize = 0x0C / 4;
|
||||
const AONCRG_GMAC0_RMII_RTX: usize = 0x10 / 4;
|
||||
const AONCRG_GMAC0_TX: usize = 0x14 / 4;
|
||||
const AONCRG_GMAC0_TX_INV: usize = 0x18 / 4;
|
||||
const AONCRG_GMAC0_RX: usize = 0x1C / 4;
|
||||
const AONCRG_GMAC0_RX_INV: usize = 0x20 / 4;
|
||||
// otpc
|
||||
const AONCRG_OTPC_APB: usize = 0x24 / 4;
|
||||
// rtc
|
||||
const AONCRG_RTC_APB: usize = 0x28 / 4;
|
||||
const AONCRG_RTC_INTERNAL: usize = 0x2C / 4;
|
||||
const AONCRG_RTC_32K: usize = 0x30 / 4;
|
||||
const AONCRG_RTC_CAL: usize = 0x34 / 4;
|
||||
|
||||
const AONCRG_CLOCK_COUNT: usize = AONCRG_RTC_CAL + 1;
|
||||
|
||||
const AONCRG_OSC: usize = AONCRG_CLOCK_COUNT;
|
||||
const AONCRG_GMAC0_RMII_REFIN: usize = AONCRG_CLOCK_COUNT + 1;
|
||||
const AONCRG_GMAC0_RGMII_RXIN: usize = AONCRG_CLOCK_COUNT + 2;
|
||||
const AONCRG_STG_AXIAHB: usize = AONCRG_CLOCK_COUNT + 3;
|
||||
const AONCRG_APB_BUS: usize = AONCRG_CLOCK_COUNT + 4;
|
||||
const AONCRG_GMAC0_GTXCLK: usize = AONCRG_CLOCK_COUNT + 5;
|
||||
const AONCRG_RTC_OSC: usize = AONCRG_CLOCK_COUNT + 6;
|
||||
|
||||
#[allow(unused)]
|
||||
enum ClockDef {
|
||||
Mux(&'static [usize]),
|
||||
Div(u32, usize),
|
||||
MuxDiv(u32, &'static [usize]),
|
||||
Gate(usize),
|
||||
GateDiv(u32, usize),
|
||||
GateMux(&'static [usize]),
|
||||
Inv(usize),
|
||||
}
|
||||
|
||||
trait ClockDefinition {
|
||||
const NAME: &'static str;
|
||||
const CLOCKS: &'static [Option<(&'static str, ClockDef)>];
|
||||
}
|
||||
|
||||
const SYSCRG_CLOCKS: &'static [Option<(&'static str, ClockDef)>] = &const {
|
||||
use ClockDef::*;
|
||||
|
||||
let mut t = [const { None }; SYSCRG_CLOCK_COUNT];
|
||||
|
||||
t[SYSCRG_CPU_ROOT] = Some(("clk_cpu_root", Mux(&[SYSCRG_OSC, SYSCRG_PLL0_OUT])));
|
||||
t[SYSCRG_CPU_CORE] = Some(("clk_cpu_core", Div(7, SYSCRG_CPU_ROOT)));
|
||||
t[SYSCRG_CPU_BUS] = Some(("clk_cpu_bus", Div(2, SYSCRG_CPU_CORE)));
|
||||
t[SYSCRG_GPU_ROOT] = Some(("clk_gpu_root", Mux(&[SYSCRG_PLL2_OUT, SYSCRG_PLL1_OUT])));
|
||||
t[SYSCRG_PERH_ROOT] = Some((
|
||||
"clk_perh_root",
|
||||
MuxDiv(2, &[SYSCRG_PLL0_OUT, SYSCRG_PLL2_OUT]),
|
||||
));
|
||||
t[SYSCRG_BUS_ROOT] = Some(("clk_bus_root", Mux(&[SYSCRG_OSC, SYSCRG_PLL2_OUT])));
|
||||
t[SYSCRG_NOCSTG_BUS] = Some(("clk_nocstg_bus", Div(3, SYSCRG_BUS_ROOT)));
|
||||
t[SYSCRG_AXI_CFG0] = Some(("clk_axi_cfg0", Div(3, SYSCRG_BUS_ROOT)));
|
||||
t[SYSCRG_STG_AXIAHB] = Some(("clk_stg_axiahb", Div(2, SYSCRG_AXI_CFG0)));
|
||||
t[SYSCRG_AHB0] = Some(("clk_ahb0", Gate(SYSCRG_STG_AXIAHB)));
|
||||
t[SYSCRG_AHB1] = Some(("clk_ahb1", Gate(SYSCRG_STG_AXIAHB)));
|
||||
t[SYSCRG_APB_BUS] = Some(("clk_apb_bus", Div(8, SYSCRG_STG_AXIAHB)));
|
||||
t[SYSCRG_APB0] = Some(("clk_apb0", Gate(SYSCRG_APB_BUS)));
|
||||
// gmac1
|
||||
t[SYSCRG_GMAC1_AHB] = Some(("clk_gmac1_ahb", Gate(SYSCRG_AHB0)));
|
||||
t[SYSCRG_GMAC1_AXI] = Some(("clk_gmac1_axi", Gate(SYSCRG_STG_AXIAHB)));
|
||||
t[SYSCRG_GMAC_SRC] = Some(("clk_gmac_src", Div(7, SYSCRG_PLL0_OUT)));
|
||||
t[SYSCRG_GMAC1_GTXCLK] = Some(("clk_gmac1_gtxclk", Div(15, SYSCRG_PLL0_OUT)));
|
||||
t[SYSCRG_GMAC1_RMII_RTX] = Some(("clk_gmac1_rmii_rtx", Div(30, SYSCRG_GMAC1_RMII_REFIN)));
|
||||
t[SYSCRG_GMAC1_PTP] = Some(("clk_gmac1_ptp", GateDiv(31, SYSCRG_GMAC_SRC)));
|
||||
t[SYSCRG_GMAC1_RX] = Some((
|
||||
"clk_gmac1_rx",
|
||||
Mux(&[SYSCRG_GMAC1_RGMII_RXIN, SYSCRG_GMAC1_RMII_RTX]),
|
||||
));
|
||||
t[SYSCRG_GMAC1_RX_INV] = Some(("clk_gmac1_rx_inv", Inv(SYSCRG_GMAC1_RX)));
|
||||
t[SYSCRG_GMAC1_TX] = Some((
|
||||
"clk_gmac1_tx",
|
||||
GateMux(&[SYSCRG_GMAC1_GTXCLK, SYSCRG_GMAC1_RMII_RTX]),
|
||||
));
|
||||
t[SYSCRG_GMAC1_TX_INV] = Some(("clk_gmac1_tx_inv", Inv(SYSCRG_GMAC1_TX)));
|
||||
t[SYSCRG_GMAC1_GTXC] = Some(("clk_gmac1_gtxc", Gate(SYSCRG_GMAC1_GTXCLK)));
|
||||
// gmac0
|
||||
t[SYSCRG_GMAC0_GTXCLK] = Some(("clk_gmac0_gtxclk", GateDiv(15, SYSCRG_PLL0_OUT)));
|
||||
t[SYSCRG_GMAC0_PTP] = Some(("clk_gmac0_ptp", GateDiv(31, SYSCRG_GMAC_SRC)));
|
||||
t[SYSCRG_GMAC_PHY] = Some(("clk_gmac_phy", GateDiv(31, SYSCRG_GMAC_SRC)));
|
||||
t[SYSCRG_GMAC0_GTXC] = Some(("clk_gmac0_gtxc", Gate(SYSCRG_GMAC0_GTXCLK)));
|
||||
|
||||
// TODO ...
|
||||
|
||||
// uart
|
||||
t[SYSCRG_UART0_APB] = Some(("clk_uart0_apb", Gate(SYSCRG_APB0)));
|
||||
t[SYSCRG_UART0_CORE] = Some(("clk_uart0_core", Gate(SYSCRG_OSC)));
|
||||
|
||||
t
|
||||
};
|
||||
|
||||
const AONCRG_CLOCKS: &'static [Option<(&'static str, ClockDef)>] = &const {
|
||||
use ClockDef::*;
|
||||
|
||||
let mut t = [const { None }; AONCRG_CLOCK_COUNT];
|
||||
|
||||
t[AONCRG_OSC_DIV4] = Some(("clk_osc_div4", Div(4, AONCRG_OSC)));
|
||||
t[AONCRG_APB_FUNC] = Some(("clk_apb_func", Mux(&[AONCRG_OSC_DIV4, AONCRG_OSC])));
|
||||
// gmac0
|
||||
t[AONCRG_GMAC0_AHB] = Some(("clk_gmac0_ahb", Gate(AONCRG_STG_AXIAHB)));
|
||||
t[AONCRG_GMAC0_AXI] = Some(("clk_gmac0_axi", Gate(AONCRG_STG_AXIAHB)));
|
||||
t[AONCRG_GMAC0_RMII_RTX] = Some(("clk_gmac0_rmii_rtx", Div(30, AONCRG_GMAC0_RMII_REFIN)));
|
||||
t[AONCRG_GMAC0_TX] = Some((
|
||||
"clk_gmac0_tx",
|
||||
GateMux(&[AONCRG_GMAC0_GTXCLK, AONCRG_GMAC0_RMII_RTX]),
|
||||
));
|
||||
t[AONCRG_GMAC0_TX_INV] = Some(("clk_gmac0_tx_inv", Inv(AONCRG_GMAC0_TX)));
|
||||
t[AONCRG_GMAC0_RX] = Some((
|
||||
"clk_gmac0_rx",
|
||||
Mux(&[AONCRG_GMAC0_RGMII_RXIN, AONCRG_GMAC0_RMII_RTX]),
|
||||
));
|
||||
t[AONCRG_GMAC0_RX_INV] = Some(("clk_gmac0_rx_inv", Inv(AONCRG_GMAC0_RX)));
|
||||
// otpc
|
||||
t[AONCRG_OTPC_APB] = Some(("clk_otpc_apb", Gate(AONCRG_APB_BUS)));
|
||||
// rtc
|
||||
t[AONCRG_RTC_APB] = Some(("clk_rtc_apb", Gate(AONCRG_APB_BUS)));
|
||||
t[AONCRG_RTC_INTERNAL] = Some(("clk_rtc_internal", Div(1022, AONCRG_OSC)));
|
||||
t[AONCRG_RTC_32K] = Some(("clk_rtc_32k", Mux(&[AONCRG_RTC_OSC, AONCRG_RTC_INTERNAL])));
|
||||
t[AONCRG_RTC_CAL] = Some(("clk_rtc_cal", Gate(AONCRG_OSC)));
|
||||
|
||||
t
|
||||
};
|
||||
|
||||
struct Crg<C, P> {
|
||||
clock_mapping: IrqSafeSpinlock<DeviceMemoryIoMut<'static, [u32]>>,
|
||||
reset_mapping: IrqSafeSpinlock<DeviceMemoryIoMut<'static, [u32]>>,
|
||||
parents: P,
|
||||
_pd: PhantomData<C>,
|
||||
}
|
||||
|
||||
// TODO this is just a dummy, which is assumed to be always running
|
||||
struct Pll;
|
||||
|
||||
unsafe impl<C, P> Send for Crg<C, P> {}
|
||||
unsafe impl<C, P> Sync for Crg<C, P> {}
|
||||
|
||||
impl<C: ClockDefinition, P: Index<usize, Output = ClockHandle>> Crg<C, P> {
|
||||
unsafe fn map(base: PhysicalAddress, parents: P) -> Result<Arc<Self>, Error> {
|
||||
let clock_mapping =
|
||||
DeviceMemoryIoMut::map_slice(base, C::CLOCKS.len(), Default::default())?;
|
||||
let reset_mapping =
|
||||
DeviceMemoryIoMut::map_slice(base.add(C::CLOCKS.len() * 4), 16, Default::default())?;
|
||||
Ok(Arc::new(Self {
|
||||
clock_mapping: IrqSafeSpinlock::new(clock_mapping),
|
||||
reset_mapping: IrqSafeSpinlock::new(reset_mapping),
|
||||
parents,
|
||||
_pd: PhantomData,
|
||||
}))
|
||||
}
|
||||
|
||||
fn clock(&self, index: usize) -> Result<&(&str, ClockDef), Error> {
|
||||
C::CLOCKS
|
||||
.get(index)
|
||||
.ok_or(Error::DoesNotExist)?
|
||||
.as_ref()
|
||||
.ok_or(Error::DoesNotExist)
|
||||
.inspect_err(|_| {
|
||||
log::error!(
|
||||
"{}: clock {:#x} does not exist/is not implemented",
|
||||
C::NAME,
|
||||
index
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn enable_clock_inner(&self, index: usize) -> Result<(), Error> {
|
||||
if index >= C::CLOCKS.len() {
|
||||
return self.parents[index - C::CLOCKS.len()].enable();
|
||||
}
|
||||
|
||||
let (clk_name, clk_def) = self.clock(index)?;
|
||||
|
||||
let (parent, write_gate) = match clk_def {
|
||||
ClockDef::GateDiv(_, parent) | ClockDef::Gate(parent) => (*parent, true),
|
||||
ClockDef::GateMux(parents) => (parents[0], true),
|
||||
|
||||
ClockDef::Inv(parent) | ClockDef::Div(_, parent) => (*parent, false),
|
||||
ClockDef::Mux(parents) | ClockDef::MuxDiv(_, parents) => (parents[0], false),
|
||||
};
|
||||
|
||||
// Enable parent
|
||||
self.enable_clock_inner(parent)?;
|
||||
|
||||
// Enable clock
|
||||
if write_gate {
|
||||
let mut lock = self.clock_mapping.lock();
|
||||
let ptr = &raw mut lock[index];
|
||||
unsafe {
|
||||
let val = ptr.read_volatile();
|
||||
if val & (1 << 31) == 0 {
|
||||
log::info!("{}: enable clock {:?}", C::NAME, clk_name);
|
||||
ptr.write_volatile(val | (1 << 31));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clock_rate_inner(&self, index: usize) -> Result<u64, Error> {
|
||||
if index >= C::CLOCKS.len() {
|
||||
return self.parents[index - C::CLOCKS.len()].rate();
|
||||
}
|
||||
|
||||
let (_, clk_def) = self.clock(index)?;
|
||||
|
||||
let (parent, div) = match clk_def {
|
||||
&ClockDef::Gate(parent) | &ClockDef::Inv(parent) => (parent, 1),
|
||||
&ClockDef::Div(div, parent) => (parent, div),
|
||||
// TODO read actual parent for muxes
|
||||
&ClockDef::GateDiv(div, parent) => (parent, div),
|
||||
ClockDef::GateMux(parents) | ClockDef::Mux(parents) => (parents[0], 1),
|
||||
ClockDef::MuxDiv(div, parents) => (parents[0], *div),
|
||||
};
|
||||
|
||||
let parent_rate = self.clock_rate_inner(parent)?;
|
||||
|
||||
Ok(parent_rate / div as u64)
|
||||
}
|
||||
|
||||
fn set_reset_asserted_inner(&self, index: usize, asserted: bool) -> Result<(), Error> {
|
||||
let reg_index = index / 32;
|
||||
let reg_shift = index % 32;
|
||||
|
||||
let mut lock = self.reset_mapping.lock();
|
||||
let ptr = &raw mut lock[reg_index];
|
||||
unsafe {
|
||||
let val = ptr.read_volatile();
|
||||
if asserted {
|
||||
ptr.write_volatile(val | (1 << reg_shift));
|
||||
} else {
|
||||
ptr.write_volatile(val & !(1 << reg_shift));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ClockDefinition, P> Device for Crg<C, P> {
|
||||
fn display_name(&self) -> &str {
|
||||
C::NAME
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ClockDefinition, P: Index<usize, Output = ClockHandle>> ClockController for Crg<C, P> {
|
||||
fn clock_rate(&self, clock: Option<u32>) -> Result<u64, Error> {
|
||||
let index = clock.ok_or(Error::InvalidArgument)? as usize;
|
||||
self.clock_rate_inner(index)
|
||||
}
|
||||
|
||||
fn set_clock_rate(&self, _clock: Option<u32>, _rate: u64) -> Result<u64, Error> {
|
||||
todo!("Clock rate update not supported for jh71x0 CRGs")
|
||||
}
|
||||
|
||||
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
|
||||
let index = clock.ok_or(Error::InvalidArgument)? as usize;
|
||||
self.enable_clock_inner(index)
|
||||
}
|
||||
|
||||
fn disable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
|
||||
todo!("Clock disable not supported for jh71x0 CRGs")
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ClockDefinition, P: Index<usize, Output = ClockHandle>> ResetController for Crg<C, P> {
|
||||
fn assert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
|
||||
let reset = reset.ok_or(Error::InvalidArgument)? as usize;
|
||||
self.set_reset_asserted_inner(reset, true)
|
||||
}
|
||||
|
||||
fn deassert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
|
||||
let reset = reset.ok_or(Error::InvalidArgument)? as usize;
|
||||
self.set_reset_asserted_inner(reset, false)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ClockDefinition + 'static, P: Index<usize, Output = ClockHandle> + 'static>
|
||||
DeviceTreeClockController for Crg<C, P>
|
||||
{
|
||||
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<C: ClockDefinition + 'static, P: Index<usize, Output = ClockHandle> + 'static>
|
||||
DeviceTreeResetController for Crg<C, P>
|
||||
{
|
||||
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,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Pll {
|
||||
fn display_name(&self) -> &str {
|
||||
"jh7110-pll"
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockController for Pll {
|
||||
fn set_clock_rate(&self, _clock: Option<u32>, _rate: u64) -> Result<u64, Error> {
|
||||
todo!("PLL rate configuration not yet implemented")
|
||||
}
|
||||
|
||||
fn clock_rate(&self, _clock: Option<u32>) -> Result<u64, Error> {
|
||||
todo!("PLL rate query not yet implemented")
|
||||
}
|
||||
|
||||
fn enable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
|
||||
// Assumed always enabled
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTreeClockController for Pll {
|
||||
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,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["starfive,jh7110-syscrg"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
struct Syscrg;
|
||||
|
||||
impl ClockDefinition for Syscrg {
|
||||
const NAME: &'static str = "jh7110-syscrg";
|
||||
const CLOCKS: &'static [Option<(&'static str, ClockDef)>] = SYSCRG_CLOCKS;
|
||||
}
|
||||
|
||||
let base = node.map_base(context, 0)?;
|
||||
let parents = [
|
||||
node.named_clock("osc")?,
|
||||
node.named_clock("gmac1_rmii_refin")?,
|
||||
node.named_clock("gmac1_rgmii_rxin")?,
|
||||
node.named_clock("i2stx_bclk_ext")?,
|
||||
node.named_clock("i2stx_lrck_ext")?,
|
||||
node.named_clock("i2srx_bclk_ext")?,
|
||||
node.named_clock("i2srx_lrck_ext")?,
|
||||
node.named_clock("tdm_ext")?,
|
||||
node.named_clock("mclk_ext")?,
|
||||
node.named_clock("pll0_out")?,
|
||||
node.named_clock("pll1_out")?,
|
||||
node.named_clock("pll2_out")?
|
||||
];
|
||||
|
||||
let syscrg = unsafe { Crg::<Syscrg, _>::map(base, parents) }.ok()?;
|
||||
|
||||
node.make_clock_controller(syscrg.clone());
|
||||
node.make_reset_controller(syscrg.clone());
|
||||
|
||||
Some(syscrg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["starfive,jh7110-aoncrg"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
struct Aoncrg;
|
||||
|
||||
impl ClockDefinition for Aoncrg {
|
||||
const NAME: &'static str = "jh7110-aoncrg";
|
||||
const CLOCKS: &'static [Option<(&'static str, ClockDef)>] = AONCRG_CLOCKS;
|
||||
}
|
||||
|
||||
let base = node.map_base(context, 0)?;
|
||||
let 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 = unsafe { Crg::<Aoncrg, _>::map(base, parents) }.ok()?;
|
||||
|
||||
node.make_clock_controller(aoncrg.clone());
|
||||
node.make_reset_controller(aoncrg.clone());
|
||||
|
||||
Some(aoncrg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["starfive,jh7110-pll"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let pll = Arc::new(Pll);
|
||||
|
||||
node.make_clock_controller(pll.clone());
|
||||
|
||||
Some(pll)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
//! 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;
|
||||
|
||||
const PARENT_CLK_OSC: usize = 0;
|
||||
|
||||
struct Syscrg {
|
||||
base: PhysicalAddress,
|
||||
parents: [ClockHandle; 1],
|
||||
mapping: IrqSafeSpinlock<DeviceMemoryIoMut<'static, [u32]>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum ClockParent {
|
||||
Int(u32),
|
||||
Ext(usize),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum ClockRegister {
|
||||
// Clock gate
|
||||
Gate(u32, ClockParent),
|
||||
// Clock inverter
|
||||
Inv(u32, ClockParent),
|
||||
// Clock divider (TODO)
|
||||
Div(ClockParent),
|
||||
Unimp,
|
||||
}
|
||||
|
||||
impl Syscrg {
|
||||
fn clk_enable_int(&self, index: u32, depth: u32) -> Result<(), Error> {
|
||||
let (name, reg) = Self::map_clock_index(index)
|
||||
.ok_or(Error::InvalidArgument)
|
||||
.inspect_err(|_| log::warn!("jh7110-syscrg: undefined clock {:?}", index))?;
|
||||
|
||||
match reg {
|
||||
ClockRegister::Gate(bit, parent) => {
|
||||
log::info!("jh7110-syscrg: enable {name:?} @ {index} /{depth}");
|
||||
self.clk_enable_parent(parent, depth)?;
|
||||
let mut lock = self.mapping.lock();
|
||||
lock[index as usize] |= 1 << bit;
|
||||
Ok(())
|
||||
}
|
||||
ClockRegister::Inv(bit, parent) => {
|
||||
log::info!("jh7110-syscrg: enable clk inv {name:?} @ {index}");
|
||||
self.clk_enable_parent(parent, depth)?;
|
||||
let mut lock = self.mapping.lock();
|
||||
lock[index as usize] |= 1 << bit;
|
||||
Ok(())
|
||||
}
|
||||
ClockRegister::Div(parent) => self.clk_enable_parent(parent, depth),
|
||||
ClockRegister::Unimp => {
|
||||
log::warn!("jh7110-syscrg: clock not implemented: {name:?}");
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clk_enable_parent(&self, parent: ClockParent, depth: u32) -> Result<(), Error> {
|
||||
match parent {
|
||||
ClockParent::Int(index) => self.clk_enable_int(index, depth + 1),
|
||||
ClockParent::Ext(clock) => self.parents.get(clock).ok_or(Error::DoesNotExist)?.enable(),
|
||||
ClockParent::None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn map_clock_index(index: u32) -> Option<(&'static str, ClockRegister)> {
|
||||
const CLOCKS: &[(&str, ClockRegister)] = &const {
|
||||
use ClockParent::*;
|
||||
use ClockRegister::*;
|
||||
|
||||
let mut t = [("", Unimp); 256];
|
||||
|
||||
t[0x01C / 4] = ("clk_axi_cfg0", Div(None));
|
||||
t[0x020 / 4] = ("clk_stg_axiahb", Div(Int(0x01C / 4)));
|
||||
t[0x024 / 4] = ("clk_ahb0", Gate(31, Int(0x020 / 4)));
|
||||
|
||||
t[0x184 / 4] = ("clk_gmac5_axi64_ahb", Gate(31, Int(0x024 / 4)));
|
||||
t[0x188 / 4] = ("clk_gmac5_axi64_axi", Gate(31, Int(0x020 / 4)));
|
||||
t[0x18C / 4] = ("clk_gmac_source", Div(None));
|
||||
t[0x190 / 4] = ("clk_gmac1_gtx", Gate(31, None));
|
||||
t[0x194 / 4] = ("clk_gmac1_rmii_rtx", Unimp);
|
||||
t[0x198 / 4] = ("clk_gmac5_axi64_ptp", Gate(31, Int(0x18C / 4)));
|
||||
t[0x19C / 4] = ("clk_gmac5_axi64_rx", Unimp);
|
||||
t[0x1A0 / 4] = ("clk_gmac5_axi64_rx_inv", Inv(30, Int(0x19C / 4)));
|
||||
t[0x1A4 / 4] = ("clk_gmac5_axi64_tx", Gate(31, None));
|
||||
t[0x1A8 / 4] = ("clk_gmac5_axi64_tx_inv", Inv(30, Int(0x1A4 / 4)));
|
||||
t[0x1AC / 4] = ("clk_gmac1_gtxc", Gate(31, Int(0x190 / 4)));
|
||||
t[0x1B0 / 4] = ("clk_gmac0_gtx", Gate(31, None));
|
||||
t[0x1B4 / 4] = ("clk_gmac0_ptp", Gate(31, Int(0x18C / 4)));
|
||||
t[0x1B8 / 4] = ("clk_gmac_phy", Gate(31, None));
|
||||
t[0x1BC / 4] = ("clk_gmac0_gtxc", Gate(31, Int(0x1B0 / 4)));
|
||||
|
||||
t[0x244 / 4] = ("clk_u0_uart_apb", Gate(31, None));
|
||||
t[0x248 / 4] = ("clk_u0_uart_core", Gate(31, Ext(PARENT_CLK_OSC)));
|
||||
|
||||
t
|
||||
};
|
||||
|
||||
let index = index as usize;
|
||||
CLOCKS.get(index).copied()
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
let index = clock.ok_or(Error::InvalidArgument)?;
|
||||
self.clk_enable_int(index, 0)
|
||||
}
|
||||
|
||||
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> {
|
||||
let (name, reg) = clock
|
||||
.and_then(Self::map_clock_index)
|
||||
.ok_or(Error::InvalidArgument)
|
||||
.inspect_err(|_| log::warn!("jh7110-syscrg: undefined clock {:?}", clock))?;
|
||||
|
||||
match reg {
|
||||
ClockRegister::Gate(_, parent) => match parent {
|
||||
ClockParent::Ext(n) => self.parents[n].rate(),
|
||||
ClockParent::Int(_) => {
|
||||
log::warn!("jh7110-syscrg: todo internal clock parents: {:?}", name);
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
ClockParent::None => {
|
||||
log::warn!("jh7110-syscrg: clock parent not specified {:?}", name);
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
},
|
||||
ClockRegister::Unimp | ClockRegister::Div(_) | ClockRegister::Inv(_, _) => {
|
||||
log::warn!("jh7110-syscrg: unimplemented clock {:?}", name);
|
||||
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 Syscrg {
|
||||
fn rst_trigger(&self, reset: u32, assert: bool) -> Result<(), Error> {
|
||||
let reg = (190 + reset / 32) as usize;
|
||||
let bit = reset % 32;
|
||||
let mut regs = self.mapping.lock();
|
||||
|
||||
let expect = if assert {
|
||||
regs[reg] |= 1 << bit;
|
||||
1 << bit
|
||||
} else {
|
||||
regs[reg] &= !(1 << bit);
|
||||
0
|
||||
};
|
||||
|
||||
let mut timeout = 1 << 20;
|
||||
while timeout > 0 && regs[reg] & (1 << bit) != expect {
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
}
|
||||
|
||||
if timeout > 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
log::warn!("jh7110-syscrg: reset timeout {reset}");
|
||||
Err(Error::TimedOut)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResetController for Syscrg {
|
||||
fn assert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
|
||||
let reset = reset.ok_or(Error::InvalidArgument)?;
|
||||
self.rst_trigger(reset, true)
|
||||
}
|
||||
|
||||
fn deassert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
|
||||
let reset = reset.ok_or(Error::InvalidArgument)?;
|
||||
self.rst_trigger(reset, false)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for Syscrg {}
|
||||
unsafe impl Send for Syscrg {}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["starfive,jh7110-syscrg"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
let osc = node.named_clock("osc")?;
|
||||
let mapping = unsafe { DeviceMemoryIoMut::map_slice(base, 256, Default::default()) }
|
||||
.inspect_err(|error| log::error!("jh7110-syscrg: {error:?}"))
|
||||
.ok()?;
|
||||
|
||||
let syscrg = Arc::new(Syscrg {
|
||||
base,
|
||||
parents: [osc],
|
||||
mapping: IrqSafeSpinlock::new(mapping),
|
||||
});
|
||||
node.make_reset_controller(syscrg.clone());
|
||||
node.make_clock_controller(syscrg.clone());
|
||||
Some(syscrg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,7 @@
|
||||
pub mod bcm2835_aux;
|
||||
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod jh7110_aoncrg;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod jh7110_syscrg;
|
||||
pub mod jh7110_clocks;
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod fixed;
|
||||
|
||||
Reference in New Issue
Block a user