consortium_data/
chipmap.rs1use std::collections::HashMap;
27
28use crate::error::DataError;
29
30struct Embedded {
33 family: &'static str,
35 core: &'static str,
37 json: &'static str,
39}
40
41const CHIPS: &[Embedded] = &[
46 Embedded {
47 family: "stm32mp25",
48 core: "cm33",
49 json: include_str!("../database/chip/stm32mp25_cm33.json"),
50 },
51 Embedded {
52 family: "stm32mp25",
53 core: "cm0p",
54 json: include_str!("../database/chip/stm32mp25_cm0p.json"),
55 },
56 Embedded {
57 family: "stm32mp23",
58 core: "cm33",
59 json: include_str!("../database/chip/stm32mp23_cm33.json"),
60 },
61 Embedded {
62 family: "stm32mp21",
63 core: "cm33",
64 json: include_str!("../database/chip/stm32mp21_cm33.json"),
65 },
66 Embedded {
67 family: "mimx95",
68 core: "cm7",
69 json: include_str!("../database/chip/mimx95_cm7.json"),
70 },
71 Embedded {
72 family: "mimx93",
73 core: "cm33",
74 json: include_str!("../database/chip/mimx93_cm33.json"),
75 },
76];
77
78#[derive(serde::Deserialize)]
80struct RawChip {
81 irqs: Vec<RawIrq>,
82 peripherals: Vec<RawPeripheral>,
83}
84
85#[derive(serde::Deserialize)]
86struct RawIrq {
87 name: String,
88 value: u16,
89}
90
91#[derive(serde::Deserialize)]
92struct RawPeripheral {
93 name: String,
94 base_address: String,
95 block: String,
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct PeripheralEntry {
101 pub base: u32,
103 pub block: String,
105}
106
107impl PeripheralEntry {
108 pub fn kind(&self) -> &str {
110 self.block.split('/').next().unwrap_or(&self.block)
111 }
112}
113
114#[derive(Debug, Clone, PartialEq, Eq)]
116pub struct IrqEntry {
117 pub number: u16,
119 pub name: String,
122}
123
124#[derive(Debug, Clone)]
126pub struct ChipMap {
127 peripherals: HashMap<String, PeripheralEntry>,
129 irqs: HashMap<String, IrqEntry>,
131}
132
133impl ChipMap {
134 pub fn peripheral(&self, name: &str) -> Option<&PeripheralEntry> {
136 self.peripherals.get(&name.to_ascii_lowercase())
137 }
138
139 pub fn contains_peripheral(&self, name: &str) -> bool {
141 self.peripherals.contains_key(&name.to_ascii_lowercase())
142 }
143
144 pub fn irq(&self, name: &str) -> Option<&IrqEntry> {
146 self.irqs.get(&name.to_ascii_lowercase())
147 }
148}
149
150pub fn load(chip: &str, core: &str) -> Result<ChipMap, DataError> {
162 let needle = normalize(chip);
163 let core_key = core.to_ascii_lowercase();
164 let entry = CHIPS
165 .iter()
166 .find(|entry| needle.starts_with(entry.family) && entry.core == core_key)
167 .ok_or_else(|| DataError::ChipMapUnavailable {
168 chip: chip.to_string(),
169 core: core.to_string(),
170 })?;
171
172 let raw: RawChip = serde_json::from_str(entry.json).map_err(|err| DataError::ChipMapParse {
173 chip: chip.to_string(),
174 core: core.to_string(),
175 detail: err.to_string(),
176 })?;
177
178 let mut peripherals = HashMap::with_capacity(raw.peripherals.len());
179 for p in raw.peripherals {
180 let base = parse_hex(&p.base_address).ok_or_else(|| DataError::ChipMapParse {
181 chip: chip.to_string(),
182 core: core.to_string(),
183 detail: format!(
184 "peripheral '{}' has invalid base '{}'",
185 p.name, p.base_address
186 ),
187 })?;
188 peripherals.insert(
189 p.name.to_ascii_lowercase(),
190 PeripheralEntry {
191 base,
192 block: p.block,
193 },
194 );
195 }
196
197 let irqs = raw
198 .irqs
199 .into_iter()
200 .map(|irq| {
201 (
202 irq.name.to_ascii_lowercase(),
203 IrqEntry {
204 number: irq.value,
205 name: irq.name,
206 },
207 )
208 })
209 .collect();
210
211 Ok(ChipMap { peripherals, irqs })
212}
213
214fn parse_hex(value: &str) -> Option<u32> {
216 let trimmed = value.trim();
217 let digits = trimmed
218 .strip_prefix("0x")
219 .or_else(|| trimmed.strip_prefix("0X"))
220 .unwrap_or(trimmed);
221 u32::from_str_radix(digits, 16).ok()
222}
223
224fn normalize(chip: &str) -> String {
226 let lowered = chip.to_ascii_lowercase();
227 match lowered.strip_prefix("imx") {
228 Some(rest) => format!("mimx{rest}"),
229 None => lowered,
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236
237 #[test]
238 fn resolves_imx95_cm7_lpi2c_and_irq() {
239 let map = load("imx95", "cm7").expect("imx95 cm7 map should resolve");
240 let lpi2c1 = map.peripheral("LPI2C1").expect("LPI2C1 should exist");
241 assert_ne!(lpi2c1.base, 0);
242 assert_eq!(lpi2c1.kind(), "lpi2c");
243 assert!(map.irq("LPI2C1").is_some());
245 }
246
247 #[test]
248 fn resolves_stm32mp25_cm33_gpio() {
249 let map = load("stm32mp257fak3", "cm33").expect("stm32mp25 cm33 map should resolve");
250 let gpioa = map.peripheral("GPIOA").expect("GPIOA should exist");
251 assert_eq!(gpioa.kind(), "gpio");
252 assert!(map.contains_peripheral("i2c1"));
253 }
254
255 #[test]
256 fn rejects_unknown_core() {
257 assert!(matches!(
258 load("stm32mp257fak3", "cm4"),
259 Err(DataError::ChipMapUnavailable { .. })
260 ));
261 }
262}