svd_generator/svd/
peripheral.rs

1use crate::tree::{parse_interrupts, Compatible};
2use crate::{Error, Result};
3
4pub mod cdns_qspi_nor;
5pub mod cdns_usb3;
6pub mod cdns_xspi_nor;
7pub mod dw_apb_i2c;
8pub mod dw_apb_uart;
9pub mod dw_axi_dmac;
10pub mod dw_mmc;
11pub mod dwgmac;
12pub mod dwmac;
13pub mod jh7110_aon_pinctrl;
14pub mod jh7110_aon_syscon;
15pub mod jh7110_aoncrg;
16pub mod jh7110_crypto;
17pub mod jh7110_isp_syscon;
18pub mod jh7110_ispcrg;
19pub mod jh7110_mipitx_dphy;
20pub mod jh7110_pmu;
21pub mod jh7110_stg_syscon;
22pub mod jh7110_stgcrg;
23pub mod jh7110_sys_pinctrl;
24pub mod jh7110_sys_syscon;
25pub mod jh7110_syscrg;
26pub mod jh7110_tdm;
27pub mod jh7110_trng;
28pub mod jh7110_vout_syscon;
29pub mod jh7110_voutcrg;
30pub mod jh7110_wdt;
31pub mod oc_pwm;
32pub mod oe_omc;
33pub mod oe_ophy;
34pub mod pl022_ssp_spi;
35pub mod pl080_dmac;
36pub mod riscv_clint;
37pub mod riscv_plic;
38pub mod sifive_u74_l2pm;
39pub mod sifive_u74_sram;
40
41use cdns_qspi_nor::CdnsQspiNor;
42use cdns_usb3::{CdnsUsb3, CdnsUsb3Offsets};
43use cdns_xspi_nor::CdnsXspiNor;
44use dw_apb_i2c::DwApbI2c;
45use dw_apb_uart::DwApbUart;
46use dw_axi_dmac::DwAxiDmac;
47use dw_mmc::{DwMmc, DwMmcModel};
48use dwgmac::{DwGmac, DwGmacVersion};
49use dwmac::DwMac;
50use jh7110_aon_pinctrl::Jh7110AonPinctrl;
51use jh7110_aon_syscon::Jh7110AonSyscon;
52use jh7110_aoncrg::Jh7110AonCrg;
53use jh7110_crypto::Jh7110Crypto;
54use jh7110_isp_syscon::Jh7110IspSyscon;
55use jh7110_ispcrg::Jh7110IspCrg;
56use jh7110_mipitx_dphy::Jh7110MipiTxDphy;
57use jh7110_pmu::Jh7110Pmu;
58use jh7110_stg_syscon::Jh7110StgSyscon;
59use jh7110_stgcrg::Jh7110StgCrg;
60use jh7110_sys_pinctrl::Jh7110SysPinctrl;
61use jh7110_sys_syscon::Jh7110SysSyscon;
62use jh7110_syscrg::Jh7110SysCrg;
63use jh7110_tdm::Jh7110Tdm;
64use jh7110_trng::Jh7110Trng;
65use jh7110_vout_syscon::Jh7110VoutSyscon;
66use jh7110_voutcrg::Jh7110VoutCrg;
67use jh7110_wdt::Jh7110Wdt;
68use oc_pwm::OcPwm;
69use oe_omc::OeOmc;
70use oe_ophy::OeOphy;
71use pl022_ssp_spi::Pl022SspSpi;
72use pl080_dmac::Pl080Dmac;
73use riscv_clint::RiscvClint;
74use riscv_plic::RiscvPlic;
75use sifive_u74_l2pm::SiFiveU74L2pm;
76use sifive_u74_sram::SiFiveU74Sram;
77
78/// Represents the number of peripherals in the SVD device description.
79pub struct PeripheralCount {
80    /// Number of I2C peripherals
81    pub i2c: usize,
82    /// Number of QSPI peripherals
83    pub qspi: usize,
84    /// Number of SPI peripherals
85    pub spi: usize,
86    /// Number of UART peripherals
87    pub uart: usize,
88    /// Number of Ethernet MAC peripherals
89    pub mac: usize,
90    /// Number of MMC peripherals
91    pub mmc: usize,
92    /// Number of USB peripherals
93    pub usb: usize,
94}
95
96impl PeripheralCount {
97    /// Creates a new [PeripheralCount].
98    pub const fn new() -> Self {
99        Self {
100            i2c: 0,
101            qspi: 0,
102            spi: 0,
103            uart: 0,
104            mac: 0,
105            mmc: 0,
106            usb: 0,
107        }
108    }
109}
110
111impl Default for PeripheralCount {
112    fn default() -> Self {
113        Self::new()
114    }
115}
116
117/// Convenience function to create an SVD [`Peripheral`](svd::Peripheral).
118pub fn create_peripheral(
119    name: &str,
120    desc: &str,
121    base_address: u64,
122    size: u32,
123    interrupt: Option<Vec<svd::Interrupt>>,
124    registers: Option<Vec<svd::RegisterCluster>>,
125    dim_element: Option<svd::DimElement>,
126) -> Result<svd::Peripheral> {
127    let info = svd::PeripheralInfo::builder()
128        .name(name.into())
129        .description(Some(desc.into()))
130        .base_address(base_address)
131        .address_block(Some(
132            [svd::AddressBlock::builder()
133                .offset(0)
134                .size(size)
135                .usage(svd::AddressBlockUsage::Registers)
136                .build(svd::ValidateLevel::Strict)?]
137            .into(),
138        ))
139        .interrupt(interrupt)
140        .registers(registers)
141        .build(svd::ValidateLevel::Strict)?;
142
143    match dim_element {
144        Some(dim) => Ok(svd::Peripheral::Array(info, dim)),
145        None => Ok(svd::Peripheral::Single(info)),
146    }
147}
148
149/// Parse SVD peripheral information from the DeviceTree root [`Node`](device_tree::Node).
150///
151/// # Examples
152///
153/// ```no_run
154/// use fdt::Fdt;
155/// use svd_generator::svd::parse_peripherals;
156///
157/// // just an example
158/// // this would be bytes loaded from a flattened DeviceTree file
159/// let dt_buf = &[0u8];
160/// let dt = Fdt::new(dt_buf).unwrap();
161/// let _peripherals = parse_peripherals(&dt).unwrap();
162/// ```
163pub fn parse_peripherals(dt: &fdt::Fdt) -> Result<Vec<svd::Peripheral>> {
164    let mut count = PeripheralCount::new();
165
166    let harts = dt.cpus().count();
167
168    Ok(dt
169        .find_node("/soc")
170        .ok_or(Error::Svd("no `soc` node found in DeviceTree".into()))?
171        .children()
172        .filter(|n| Compatible::from(n).is_known())
173        .filter_map(|n| parse_peripheral(&n, harts, &mut count).ok())
174        .collect())
175}
176
177fn parse_peripheral(
178    node: &fdt::node::FdtNode,
179    harts: usize,
180    count: &mut PeripheralCount,
181) -> Result<svd::Peripheral> {
182    let name = node.name;
183    let comp = Compatible::from(node);
184    let comp_str = node
185        .property("compatible")
186        .ok_or(Error::DeviceTree(format!(
187            "{name} missing `compatible` property"
188        )))?
189        .as_str()
190        .unwrap_or(<&str>::from(&comp));
191
192    let reg_names = node
193        .property("reg-names")
194        .map(|p| p.as_str().unwrap_or(""))
195        .unwrap_or("");
196    let desc = format!("{name} {comp_str},{reg_names} peripheral generator");
197
198    let (address, size) = if node.property("reg").is_some() {
199        let r = node
200            .reg()
201            .next()
202            .ok_or(Error::DeviceTree("empty `reg` property".into()))?;
203
204        let size = r.size.ok_or(Error::DeviceTree(
205            "`reg` property missing `size` field".into(),
206        ))?;
207
208        (r.starting_address as u64, size as u32)
209    } else if node.property("ranges").is_some() {
210        node.ranges()
211            .next()
212            .map(|m| (m.parent_bus_address as u64, m.size as u32))
213            .ok_or(Error::DeviceTree("invalid `ranges` property".into()))?
214    } else {
215        return Err(Error::DeviceTree(
216            "no valid address, size property found".into(),
217        ));
218    };
219
220    Ok(match comp {
221        Compatible::CdnsQspiNor => {
222            let interrupt = parse_interrupts(node, "QSPI", Some(count.qspi)).ok();
223
224            CdnsQspiNor::create("qspi", address, size, interrupt, 0)?.to_inner()
225        }
226        Compatible::CdnsXspiNor => {
227            let interrupt = parse_interrupts(node, "XSPI", Some(count.qspi)).ok();
228
229            CdnsXspiNor::create("xspi", address, size, interrupt, 0)?.to_inner()
230        }
231        Compatible::DwApbI2c => {
232            let i2c_name = format!("i2c{}", count.i2c);
233
234            let interrupt = parse_interrupts(node, "I2C", Some(count.i2c)).ok();
235
236            count.i2c += 1;
237
238            DwApbI2c::create(i2c_name.as_str(), address, size, interrupt, 0)?.to_inner()
239        }
240        Compatible::DwApbUart => {
241            let uart_name = format!("uart{}", count.uart);
242
243            let interrupt = parse_interrupts(node, "UART", Some(count.uart)).ok();
244
245            count.uart += 1;
246
247            DwApbUart::create(uart_name.as_str(), address, size, interrupt, 0)?.to_inner()
248        }
249        Compatible::DwAxiDmac => {
250            let interrupt = parse_interrupts(node, "DMA", None).ok();
251            let dma_channels = node
252                .property("dma-channels")
253                .and_then(|p| p.as_usize())
254                .unwrap_or_default() as u64;
255
256            DwAxiDmac::create("dma", address, size, interrupt, dma_channels, 0)?.to_inner()
257        }
258        Compatible::DwMac => {
259            let mac_name = format!("gmac{}", count.mac);
260
261            let interrupt = parse_interrupts(node, "GMAC", Some(count.mac)).ok();
262
263            count.mac += 1;
264
265            let version = DwGmacVersion::from(comp_str);
266
267            match version {
268                DwGmacVersion::Version340 | DwGmacVersion::Version350 => {
269                    if node
270                        .property("phy-mode")
271                        .map(|p| p.as_str().unwrap_or("").contains("rgmii"))
272                        .unwrap_or(false)
273                    {
274                        DwGmac::create(mac_name.as_str(), address, size, interrupt, version, 0)?
275                            .to_inner()
276                    } else {
277                        DwMac::create(mac_name.as_str(), address, size, interrupt, 0)?.to_inner()
278                    }
279                }
280                _ => DwGmac::create(mac_name.as_str(), address, size, interrupt, version, 0)?
281                    .to_inner(),
282            }
283        }
284        Compatible::DwMmc => {
285            let mmc_name = format!("mmc{}", count.mmc);
286
287            let interrupt = parse_interrupts(node, "MMC", Some(count.mmc)).ok();
288
289            count.mmc += 1;
290
291            let model = DwMmcModel::try_from(comp_str)?;
292
293            DwMmc::create(&mmc_name, address, size, interrupt, model)?.to_inner()
294        }
295        Compatible::Jh7110AonCrg => {
296            let interrupt = parse_interrupts(node, "AONCRG", None).ok();
297
298            Jh7110AonCrg::create("aoncrg", address, size, interrupt)?.to_inner()
299        }
300        Compatible::Jh7110AonPinctrl => {
301            let interrupt = parse_interrupts(node, "AON_IOMUX", None).ok();
302
303            Jh7110AonPinctrl::create("aon_pinctrl", address, size, interrupt)?.to_inner()
304        }
305        Compatible::Jh7110AonSyscon => {
306            let interrupt = parse_interrupts(node, "AON_SYSCON", None).ok();
307
308            Jh7110AonSyscon::create("aon_syscon", address, size, interrupt)?.to_inner()
309        }
310        Compatible::Jh7110Crypto => {
311            let interrupt = parse_interrupts(node, "CRYPTO", None).ok();
312
313            Jh7110Crypto::create("crypto", address, size, interrupt)?.to_inner()
314        }
315        Compatible::Jh7110IspCrg => {
316            let interrupt = parse_interrupts(node, "ISPCRG", None).ok();
317
318            Jh7110IspCrg::create("ispcrg", address, size, interrupt)?.to_inner()
319        }
320        Compatible::Jh7110IspSyscon => {
321            let interrupt = parse_interrupts(node, "ISP_SYSCON", None).ok();
322
323            Jh7110IspSyscon::create("isp_syscon", address, size, interrupt)?.to_inner()
324        }
325        Compatible::Jh7110MipiTxDphy => {
326            let interrupt = parse_interrupts(node, "MIPITX_DPHY", None).ok();
327
328            Jh7110MipiTxDphy::create("mipitx_dphy", address, size, interrupt)?.to_inner()
329        }
330        Compatible::Jh7110Pmu => {
331            let interrupt = parse_interrupts(node, "PMU", None).ok();
332
333            Jh7110Pmu::create("pmu", address, size, interrupt)?.to_inner()
334        }
335        Compatible::Jh7110StgCrg => {
336            let interrupt = parse_interrupts(node, "STGCRG", None).ok();
337
338            Jh7110StgCrg::create("stgcrg", address, size, interrupt)?.to_inner()
339        }
340        Compatible::Jh7110StgSyscon => {
341            let interrupt = parse_interrupts(node, "STG_SYSCON", None).ok();
342
343            Jh7110StgSyscon::create("stg_syscon", address, size, interrupt)?.to_inner()
344        }
345        Compatible::Jh7110SysCrg => {
346            let interrupt = parse_interrupts(node, "SYSCRG", None).ok();
347
348            Jh7110SysCrg::create("syscrg", address, size, interrupt)?.to_inner()
349        }
350        Compatible::Jh7110SysPinctrl => {
351            let interrupt = parse_interrupts(node, "SYS_IOMUX", None).ok();
352
353            Jh7110SysPinctrl::create("sys_pinctrl", address, size, interrupt)?.to_inner()
354        }
355        Compatible::Jh7110SysSyscon => {
356            let interrupt = parse_interrupts(node, "SYS_SYCON", None).ok();
357
358            Jh7110SysSyscon::create("sys_syscon", address, size, interrupt)?.to_inner()
359        }
360        Compatible::Jh7110Tdm => {
361            let interrupt = parse_interrupts(node, "TDM", None).ok();
362
363            Jh7110Tdm::create("tdm", address, size, interrupt)?.to_inner()
364        }
365        Compatible::Jh7110Trng => {
366            let interrupt = parse_interrupts(node, "TRNG", None).ok();
367
368            Jh7110Trng::create("trng", address, size, interrupt)?.to_inner()
369        }
370        Compatible::Jh7110Usb => {
371            let usb_name = format!("usb{}", count.usb);
372
373            let usb_node = node
374                .children()
375                .find(|n| Compatible::from(n) == Compatible::CdnsUsb3)
376                .ok_or(Error::DeviceTree(
377                    "StarFive JH7110 USB does not have `cdns,usb3` child node".into(),
378                ))?;
379
380            let reg_offsets = CdnsUsb3Offsets::try_from(usb_node)?;
381
382            let interrupt = parse_interrupts(&usb_node, &usb_name, Some(count.usb)).ok();
383
384            count.usb += 1;
385
386            CdnsUsb3::create(usb_name.as_str(), address, size, interrupt, reg_offsets, 0)?
387                .to_inner()
388        }
389        Compatible::Jh7110VoutCrg => {
390            let interrupt = parse_interrupts(node, "VOUTCRG", None).ok();
391
392            Jh7110VoutCrg::create("voutcrg", address, size, interrupt)?.to_inner()
393        }
394        Compatible::Jh7110VoutSyscon => {
395            let interrupt = parse_interrupts(node, "VOUT_SYSCON", None).ok();
396
397            Jh7110VoutSyscon::create("vout_syscon", address, size, interrupt)?.to_inner()
398        }
399        Compatible::Jh7110Wdt => {
400            let interrupt = parse_interrupts(node, "WDT", None).ok();
401
402            Jh7110Wdt::create("wdt", address, size, interrupt, 0)?.to_inner()
403        }
404        Compatible::Pl022SspSpi => {
405            let spi_name = format!("spi{}", count.spi);
406
407            let interrupt = parse_interrupts(node, "SPI", Some(count.spi)).ok();
408
409            count.spi += 1;
410
411            Pl022SspSpi::create(spi_name.as_str(), address, size, interrupt, 0)?.to_inner()
412        }
413        Compatible::Pl080Dmac => {
414            let interrupt = parse_interrupts(node, "SDMA", None).ok();
415
416            Pl080Dmac::create("sdma", address, size, interrupt, 0)?.to_inner()
417        }
418        Compatible::RiscvClint => RiscvClint::create(address, size, harts)?.to_inner(),
419        Compatible::RiscvPlic => {
420            let ndev = node
421                .property("riscv,ndev")
422                .map(|p| p.as_usize().unwrap_or(1))
423                .ok_or(Error::DeviceTree(
424                    "riscv,plic missing `riscv,ndev` property".into(),
425                ))?;
426
427            RiscvPlic::create(address, size, harts, ndev)?.to_inner()
428        }
429        Compatible::OcPwm => {
430            let interrupt = parse_interrupts(node, "PWM", None).ok();
431
432            OcPwm::create("pwm", address, size, interrupt, 0)?.to_inner()
433        }
434        Compatible::OeOmc => {
435            let interrupt = parse_interrupts(node, "DMC_CTRL", None).ok();
436
437            OeOmc::create("dmc_ctrl", address, size, interrupt, 0)?.to_inner()
438        }
439        Compatible::OeOphy => {
440            let interrupt = parse_interrupts(node, "DMC_PHY", None).ok();
441
442            OeOphy::create("dmc_phy", address, size, interrupt, 0)?.to_inner()
443        }
444        Compatible::SiFiveU74L2pm => {
445            let interrupt = parse_interrupts(node, "L2PM", None).ok();
446
447            SiFiveU74L2pm::create("l2pm", address, size, interrupt, 0)?.to_inner()
448        }
449        Compatible::SiFiveU74Sram => {
450            let interrupt = parse_interrupts(node, "SRAM", None).ok();
451
452            SiFiveU74Sram::create("sram", address, size, interrupt, 0)?.to_inner()
453        }
454        _ => create_peripheral(name, desc.as_str(), address, size, None, None, None)?,
455    })
456}