use crate::tree::{parse_reg, Compatible};
use crate::{Error, Result};
pub mod cdns_qspi_nor;
pub mod dw_apb_i2c;
pub mod dw_apb_uart;
pub mod jh7110_aon_pinctrl;
pub mod jh7110_aon_syscon;
pub mod jh7110_aoncrg;
pub mod jh7110_pmu;
pub mod jh7110_stg_syscon;
pub mod jh7110_stgcrg;
pub mod jh7110_sys_pinctrl;
pub mod jh7110_sys_syscon;
pub mod jh7110_syscrg;
pub mod jh7110_trng;
pub mod oc_pwm;
pub mod oe_omc;
pub mod oe_ophy;
pub mod pl022_ssp_spi;
pub mod riscv_clint;
pub mod riscv_plic;
use cdns_qspi_nor::CdnsQspiNor;
use dw_apb_i2c::DwApbI2c;
use dw_apb_uart::DwApbUart;
use jh7110_aon_pinctrl::Jh7110AonPinctrl;
use jh7110_aon_syscon::Jh7110AonSyscon;
use jh7110_aoncrg::Jh7110AonCrg;
use jh7110_pmu::Jh7110Pmu;
use jh7110_stg_syscon::Jh7110StgSyscon;
use jh7110_stgcrg::Jh7110StgCrg;
use jh7110_sys_pinctrl::Jh7110SysPinctrl;
use jh7110_sys_syscon::Jh7110SysSyscon;
use jh7110_syscrg::Jh7110SysCrg;
use jh7110_trng::Jh7110Trng;
use oc_pwm::OcPwm;
use oe_omc::OeOmc;
use oe_ophy::OeOphy;
use pl022_ssp_spi::Pl022SspSpi;
use riscv_clint::RiscvClint;
use riscv_plic::RiscvPlic;
pub struct PeripheralCount {
pub i2c: usize,
pub qspi: usize,
pub spi: usize,
pub uart: usize,
}
impl PeripheralCount {
pub const fn new() -> Self {
Self {
i2c: 0,
qspi: 0,
spi: 0,
uart: 0,
}
}
}
pub fn create_peripheral(
name: &str,
desc: &str,
base_address: u64,
size: u32,
interrupt: Option<Vec<svd::Interrupt>>,
registers: Option<Vec<svd::RegisterCluster>>,
dim_element: Option<svd::DimElement>,
) -> Result<svd::Peripheral> {
let info = svd::PeripheralInfo::builder()
.name(name.into())
.description(Some(desc.into()))
.base_address(base_address)
.address_block(Some(
[svd::AddressBlock::builder()
.offset(0)
.size(size)
.usage(svd::AddressBlockUsage::Registers)
.build(svd::ValidateLevel::Strict)?]
.into(),
))
.interrupt(interrupt)
.registers(registers)
.build(svd::ValidateLevel::Strict)?;
match dim_element {
Some(dim) => Ok(svd::Peripheral::Array(info, dim)),
None => Ok(svd::Peripheral::Single(info)),
}
}
pub fn parse_peripherals(root: &device_tree::Node) -> Result<Vec<svd::Peripheral>> {
let mut count = PeripheralCount::new();
let addr_cells = root.prop_u32("#address-cells").unwrap_or(1) as usize;
let size_cells = root.prop_u32("#size-cells").unwrap_or(1) as usize;
let harts = root
.children
.iter()
.find(|n| n.name == "cpus")
.ok_or(Error::Svd("no `cpus` node found in DeviceTree".into()))?
.children
.len();
Ok(root
.children
.iter()
.find(|n| n.name == "soc")
.ok_or(Error::Svd("no `soc` node found in DeviceTree".into()))?
.children
.iter()
.filter(|n| match n.prop_str("compatible") {
Ok(p) => {
log::debug!("Found peripheral `compatible` property: {p}");
Compatible::from(p).is_known()
}
Err(_) => false,
})
.filter_map(|n| parse_peripheral(n, addr_cells, size_cells, harts, &mut count).ok())
.collect())
}
fn parse_peripheral(
node: &device_tree::Node,
addr_cells: usize,
size_cells: usize,
harts: usize,
count: &mut PeripheralCount,
) -> Result<svd::Peripheral> {
let name = node.name.as_str();
let comp_str = node.prop_str("compatible")?;
let comp = Compatible::from(comp_str);
let reg_names = node.prop_str("reg-names").unwrap_or("");
let desc = format!("{name} {comp_str},{reg_names} peripheral generator");
let (address, size) = parse_reg(node, addr_cells, size_cells)?;
Ok(match comp {
Compatible::CdnsQspiNor => {
let qspi_name = format!("qspi{}", count.qspi);
count.qspi += 1;
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name(qspi_name.to_uppercase())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
CdnsQspiNor::create(qspi_name.as_str(), address, size, interrupt, 0)?.to_inner()
}
Compatible::DwApbI2c => {
let i2c_name = format!("i2c{}", count.i2c);
count.i2c += 1;
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name(i2c_name.to_uppercase())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
DwApbI2c::create(i2c_name.as_str(), address, size, interrupt, 0)?.to_inner()
}
Compatible::DwApbUart => {
let uart_name = format!("uart{}", count.uart);
count.uart += 1;
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name(uart_name.to_uppercase())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
DwApbUart::create(uart_name.as_str(), address, size, interrupt, 0)?.to_inner()
}
Compatible::Jh7110AonCrg => {
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name("AONCRG".into())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
Jh7110AonCrg::create("aoncrg", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110AonPinctrl => {
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name("AON_IOMUX".into())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
Jh7110AonPinctrl::create("aon_pinctrl", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110AonSyscon => {
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name("AON_SYSCON".into())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
Jh7110AonSyscon::create("aon_syscon", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110Pmu => {
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name("PMU".into())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
Jh7110Pmu::create("pmu", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110StgCrg => {
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name("STGCRG".into())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
Jh7110StgCrg::create("stgcrg", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110StgSyscon => {
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name("STG_SYSCON".into())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
Jh7110StgSyscon::create("stg_syscon", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110SysCrg => {
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name("SYSCRG".into())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
Jh7110SysCrg::create("syscrg", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110SysPinctrl => {
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name("SYS_IOMUX".into())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
Jh7110SysPinctrl::create("sys_pinctrl", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110SysSyscon => {
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name("SYS_SYSCON".into())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
Jh7110SysSyscon::create("sys_syscon", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110Trng => {
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name("TRNG".into())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
Jh7110Trng::create("trng", address, size, interrupt)?.to_inner()
}
Compatible::Pl022SspSpi => {
let spi_name = format!("spi{}", count.spi);
count.spi += 1;
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name(spi_name.to_uppercase())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
Pl022SspSpi::create(spi_name.as_str(), address, size, interrupt, 0)?.to_inner()
}
Compatible::RiscvClint => RiscvClint::create(address, size, harts)?.to_inner(),
Compatible::RiscvPlic => {
RiscvPlic::create(address, size, harts, node.prop_u32("riscv,ndev")? as usize)?
.to_inner()
}
Compatible::OcPwm => {
let interrupt = if node.has_prop("interrupts") {
Some(vec![svd::Interrupt::builder()
.name("PWM".into())
.value(node.prop_u32("interrupts")?)
.build(svd::ValidateLevel::Strict)?])
} else {
None
};
OcPwm::create("pwm", address, size, interrupt, 0)?.to_inner()
}
Compatible::OeOmc => OeOmc::create("dmc_ctrl", address, size, None, 0)?.to_inner(),
Compatible::OeOphy => OeOphy::create("dmc_phy", address, size, None, 0)?.to_inner(),
_ => create_peripheral(name, desc.as_str(), address, size, None, None, None)?,
})
}