use crate::tree::{parse_reg, Compatible};
use crate::{Error, Result};
pub mod dw_apb_i2c;
pub mod dw_apb_uart;
pub mod pl022_ssp_spi;
use dw_apb_i2c::DwApbI2c;
use dw_apb_uart::DwApbUart;
use pl022_ssp_spi::Pl022SspSpi;
pub struct PeripheralCount {
pub i2c: usize,
pub spi: usize,
pub uart: usize,
}
impl PeripheralCount {
pub const fn new() -> Self {
Self {
i2c: 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::Weak)?;
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;
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, &mut count).ok())
.collect())
}
fn parse_peripheral(
node: &device_tree::Node,
addr_cells: usize,
size_cells: 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::DwApbI2c => {
let i2c_name = format!("i2c{}", count.i2c);
count.i2c += 1;
let interrupt = if node.has_prop("interrrupts") {
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("interrrupts") {
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::Pl022SspSpi => {
let spi_name = format!("spi{}", count.spi);
count.spi += 1;
let interrupt = if node.has_prop("interrrupts") {
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()
}
_ => create_peripheral(name, desc.as_str(), address, size, None, None, None)?,
})
}