use crate::tree::{parse_interrupts, Compatible};
use crate::{Error, Result};
pub mod cdns_qspi_nor;
pub mod cdns_usb3;
pub mod cdns_xspi_nor;
pub mod dw_apb_i2c;
pub mod dw_apb_uart;
pub mod dw_axi_dmac;
pub mod dw_mmc;
pub mod dwgmac;
pub mod dwmac;
pub mod jh7110_aon_pinctrl;
pub mod jh7110_aon_syscon;
pub mod jh7110_aoncrg;
pub mod jh7110_crypto;
pub mod jh7110_isp_syscon;
pub mod jh7110_ispcrg;
pub mod jh7110_mipitx_dphy;
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_tdm;
pub mod jh7110_trng;
pub mod jh7110_vout_syscon;
pub mod jh7110_voutcrg;
pub mod jh7110_wdt;
pub mod oc_pwm;
pub mod oe_omc;
pub mod oe_ophy;
pub mod pl022_ssp_spi;
pub mod pl080_dmac;
pub mod riscv_clint;
pub mod riscv_plic;
pub mod sifive_u74_sram;
use cdns_qspi_nor::CdnsQspiNor;
use cdns_usb3::{CdnsUsb3, CdnsUsb3Offsets};
use cdns_xspi_nor::CdnsXspiNor;
use dw_apb_i2c::DwApbI2c;
use dw_apb_uart::DwApbUart;
use dw_axi_dmac::DwAxiDmac;
use dw_mmc::{DwMmc, DwMmcModel};
use dwgmac::{DwGmac, DwGmacVersion};
use dwmac::DwMac;
use jh7110_aon_pinctrl::Jh7110AonPinctrl;
use jh7110_aon_syscon::Jh7110AonSyscon;
use jh7110_aoncrg::Jh7110AonCrg;
use jh7110_crypto::Jh7110Crypto;
use jh7110_isp_syscon::Jh7110IspSyscon;
use jh7110_ispcrg::Jh7110IspCrg;
use jh7110_mipitx_dphy::Jh7110MipiTxDphy;
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_tdm::Jh7110Tdm;
use jh7110_trng::Jh7110Trng;
use jh7110_vout_syscon::Jh7110VoutSyscon;
use jh7110_voutcrg::Jh7110VoutCrg;
use jh7110_wdt::Jh7110Wdt;
use oc_pwm::OcPwm;
use oe_omc::OeOmc;
use oe_ophy::OeOphy;
use pl022_ssp_spi::Pl022SspSpi;
use pl080_dmac::Pl080Dmac;
use riscv_clint::RiscvClint;
use riscv_plic::RiscvPlic;
use sifive_u74_sram::SiFiveU74Sram;
pub struct PeripheralCount {
pub i2c: usize,
pub qspi: usize,
pub spi: usize,
pub uart: usize,
pub mac: usize,
pub mmc: usize,
pub usb: usize,
}
impl PeripheralCount {
pub const fn new() -> Self {
Self {
i2c: 0,
qspi: 0,
spi: 0,
uart: 0,
mac: 0,
mmc: 0,
usb: 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(dt: &fdt::Fdt) -> Result<Vec<svd::Peripheral>> {
let mut count = PeripheralCount::new();
let harts = dt.cpus().count();
Ok(dt
.find_node("/soc")
.ok_or(Error::Svd("no `soc` node found in DeviceTree".into()))?
.children()
.filter(|n| Compatible::from(n).is_known())
.filter_map(|n| parse_peripheral(&n, harts, &mut count).ok())
.collect())
}
fn parse_peripheral(
node: &fdt::node::FdtNode,
harts: usize,
count: &mut PeripheralCount,
) -> Result<svd::Peripheral> {
let name = node.name;
let comp = Compatible::from(node);
let comp_str = node
.property("compatible")
.ok_or(Error::DeviceTree(format!(
"{name} missing `compatible` property"
)))?
.as_str()
.unwrap_or(<&str>::from(&comp));
let reg_names = node
.property("reg-names")
.map(|p| p.as_str().unwrap_or(""))
.unwrap_or("");
let desc = format!("{name} {comp_str},{reg_names} peripheral generator");
let (address, size) = if node.property("reg").is_some() {
let r = node.reg().next().ok_or(Error::DeviceTree("empty `reg` property".into()))?;
let size = r.size.ok_or(Error::DeviceTree(
"`reg` property missing `size` field".into(),
))?;
(r.starting_address as u64, size as u32)
} else if node.property("ranges").is_some() {
node
.ranges()
.next()
.map(|m| (m.parent_bus_address as u64, m.size as u32))
.ok_or(Error::DeviceTree("invalid `ranges` property".into()))?
} else {
return Err(Error::DeviceTree(
"no valid address, size property found".into(),
));
};
Ok(match comp {
Compatible::CdnsQspiNor => {
let interrupt = parse_interrupts(node, "QSPI", Some(count.qspi)).ok();
CdnsQspiNor::create("qspi", address, size, interrupt, 0)?.to_inner()
}
Compatible::CdnsXspiNor => {
let interrupt = parse_interrupts(node, "XSPI", Some(count.qspi)).ok();
CdnsXspiNor::create("xspi", address, size, interrupt, 0)?.to_inner()
}
Compatible::DwApbI2c => {
let i2c_name = format!("i2c{}", count.i2c);
let interrupt = parse_interrupts(node, "I2C", Some(count.i2c)).ok();
count.i2c += 1;
DwApbI2c::create(i2c_name.as_str(), address, size, interrupt, 0)?.to_inner()
}
Compatible::DwApbUart => {
let uart_name = format!("uart{}", count.uart);
let interrupt = parse_interrupts(node, "UART", Some(count.uart)).ok();
count.uart += 1;
DwApbUart::create(uart_name.as_str(), address, size, interrupt, 0)?.to_inner()
}
Compatible::DwAxiDmac => {
let interrupt = parse_interrupts(node, "DMA", None).ok();
DwAxiDmac::create("dma", address, size, interrupt, 0)?.to_inner()
}
Compatible::DwMac => {
let mac_name = format!("gmac{}", count.mac);
let interrupt = parse_interrupts(node, "GMAC", Some(count.mac)).ok();
count.mac += 1;
let version = DwGmacVersion::from(comp_str);
match version {
DwGmacVersion::Version340 | DwGmacVersion::Version350 => {
if node
.property("phy-mode")
.map(|p| p.as_str().unwrap_or("").contains("rgmii"))
.unwrap_or(false)
{
DwGmac::create(mac_name.as_str(), address, size, interrupt, version, 0)?
.to_inner()
} else {
DwMac::create(mac_name.as_str(), address, size, interrupt, 0)?.to_inner()
}
}
_ => DwGmac::create(mac_name.as_str(), address, size, interrupt, version, 0)?
.to_inner(),
}
}
Compatible::DwMmc => {
let mmc_name = format!("mmc{}", count.mmc);
let interrupt = parse_interrupts(node, "MMC", Some(count.mmc)).ok();
count.mmc += 1;
let model = DwMmcModel::try_from(comp_str)?;
DwMmc::create(&mmc_name, address, size, interrupt, model)?.to_inner()
}
Compatible::Jh7110AonCrg => {
let interrupt = parse_interrupts(node, "AONCRG", None).ok();
Jh7110AonCrg::create("aoncrg", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110AonPinctrl => {
let interrupt = parse_interrupts(node, "AON_IOMUX", None).ok();
Jh7110AonPinctrl::create("aon_pinctrl", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110AonSyscon => {
let interrupt = parse_interrupts(node, "AON_SYSCON", None).ok();
Jh7110AonSyscon::create("aon_syscon", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110Crypto => {
let interrupt = parse_interrupts(node, "CRYPTO", None).ok();
Jh7110Crypto::create("crypto", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110IspCrg => {
let interrupt = parse_interrupts(node, "ISPCRG", None).ok();
Jh7110IspCrg::create("ispcrg", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110IspSyscon => {
let interrupt = parse_interrupts(node, "ISP_SYSCON", None).ok();
Jh7110IspSyscon::create("isp_syscon", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110MipiTxDphy => {
let interrupt = parse_interrupts(node, "MIPITX_DPHY", None).ok();
Jh7110MipiTxDphy::create("mipitx_dphy", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110Pmu => {
let interrupt = parse_interrupts(node, "PMU", None).ok();
Jh7110Pmu::create("pmu", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110StgCrg => {
let interrupt = parse_interrupts(node, "STGCRG", None).ok();
Jh7110StgCrg::create("stgcrg", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110StgSyscon => {
let interrupt = parse_interrupts(node, "STG_SYSCON", None).ok();
Jh7110StgSyscon::create("stg_syscon", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110SysCrg => {
let interrupt = parse_interrupts(node, "SYSCRG", None).ok();
Jh7110SysCrg::create("syscrg", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110SysPinctrl => {
let interrupt = parse_interrupts(node, "SYS_IOMUX", None).ok();
Jh7110SysPinctrl::create("sys_pinctrl", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110SysSyscon => {
let interrupt = parse_interrupts(node, "SYS_SYCON", None).ok();
Jh7110SysSyscon::create("sys_syscon", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110Tdm => {
let interrupt = parse_interrupts(node, "TDM", None).ok();
Jh7110Tdm::create("tdm", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110Trng => {
let interrupt = parse_interrupts(node, "TRNG", None).ok();
Jh7110Trng::create("trng", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110Usb => {
let usb_name = format!("usb{}", count.usb);
let usb_node = node
.children()
.find(|n| Compatible::from(n) == Compatible::CdnsUsb3)
.ok_or(Error::DeviceTree(
"StarFive JH7110 USB does not have `cdns,usb3` child node".into(),
))?;
let reg_offsets = CdnsUsb3Offsets::try_from(usb_node)?;
let interrupt = parse_interrupts(&usb_node, &usb_name, Some(count.usb)).ok();
count.usb += 1;
CdnsUsb3::create(usb_name.as_str(), address, size, interrupt, reg_offsets, 0)?
.to_inner()
}
Compatible::Jh7110VoutCrg => {
let interrupt = parse_interrupts(node, "VOUTCRG", None).ok();
Jh7110VoutCrg::create("voutcrg", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110VoutSyscon => {
let interrupt = parse_interrupts(node, "VOUT_SYSCON", None).ok();
Jh7110VoutSyscon::create("vout_syscon", address, size, interrupt)?.to_inner()
}
Compatible::Jh7110Wdt => {
let interrupt = parse_interrupts(node, "WDT", None).ok();
Jh7110Wdt::create("wdt", address, size, interrupt, 0)?.to_inner()
}
Compatible::Pl022SspSpi => {
let spi_name = format!("spi{}", count.spi);
let interrupt = parse_interrupts(node, "SPI", Some(count.spi)).ok();
count.spi += 1;
Pl022SspSpi::create(spi_name.as_str(), address, size, interrupt, 0)?.to_inner()
}
Compatible::Pl080Dmac => {
let interrupt = parse_interrupts(node, "SDMA", None).ok();
Pl080Dmac::create("sdma", address, size, interrupt, 0)?.to_inner()
}
Compatible::RiscvClint => RiscvClint::create(address, size, harts)?.to_inner(),
Compatible::RiscvPlic => {
let ndev = node
.property("riscv,ndev")
.map(|p| p.as_usize().unwrap_or(1))
.ok_or(Error::DeviceTree(
"riscv,plic missing `riscv,ndev` property".into(),
))?;
RiscvPlic::create(address, size, harts, ndev)?.to_inner()
}
Compatible::OcPwm => {
let interrupt = parse_interrupts(node, "PWM", None).ok();
OcPwm::create("pwm", address, size, interrupt, 0)?.to_inner()
}
Compatible::OeOmc => {
let interrupt = parse_interrupts(node, "DMC_CTRL", None).ok();
OeOmc::create("dmc_ctrl", address, size, interrupt, 0)?.to_inner()
}
Compatible::OeOphy => {
let interrupt = parse_interrupts(node, "DMC_PHY", None).ok();
OeOphy::create("dmc_phy", address, size, interrupt, 0)?.to_inner()
}
Compatible::SiFiveU74Sram => {
let interrupt = parse_interrupts(node, "SRAM", None).ok();
SiFiveU74Sram::create("sram", address, size, interrupt, 0)?.to_inner()
}
_ => create_peripheral(name, desc.as_str(), address, size, None, None, None)?,
})
}