1use std::collections::HashMap;
36
37use crate::error::DataError;
38use crate::family::{matches, normalize};
39
40struct Embedded {
43 family: &'static str,
45 json: &'static str,
47}
48
49const FAMILIES: &[Embedded] = &[
50 Embedded {
51 family: "stm32mp25",
52 json: include_str!("../database/signal/stm32mp25.json"),
53 },
54 Embedded {
55 family: "stm32mp23",
56 json: include_str!("../database/signal/stm32mp23.json"),
57 },
58 Embedded {
59 family: "stm32mp21",
60 json: include_str!("../database/signal/stm32mp21.json"),
61 },
62 Embedded {
63 family: "mimx952",
64 json: include_str!("../database/signal/mimx952.json"),
65 },
66 Embedded {
67 family: "mimx95",
68 json: include_str!("../database/signal/mimx95.json"),
69 },
70 Embedded {
71 family: "mimx93",
72 json: include_str!("../database/signal/mimx93.json"),
73 },
74];
75
76#[derive(serde::Deserialize)]
77struct RawMap {
78 packages: Vec<RawPackage>,
79 pads: Vec<RawPad>,
80 signals: Vec<RawSignal>,
81 dma: Vec<DmaRequest>,
82}
83
84#[derive(serde::Deserialize)]
85struct RawPackage {
86 name: String,
87 parts: Vec<String>,
88}
89
90#[derive(serde::Deserialize)]
91struct RawPad {
92 name: String,
93 packages: Option<Vec<String>>,
94}
95
96#[derive(serde::Deserialize)]
97struct RawSignal {
98 pad: String,
99 function: String,
100 peripheral: Option<String>,
101 signal: Option<String>,
102 mux: RawMux,
103}
104
105#[derive(serde::Deserialize)]
106#[serde(tag = "kind", rename_all = "snake_case")]
107enum RawMux {
108 Af {
109 af: u8,
110 },
111 Additional,
112 Analog,
113 Iomuxc {
114 mux_register: String,
115 mux_mode: u8,
116 input_register: Option<String>,
117 input_daisy: Option<u8>,
118 config_register: String,
119 },
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub enum Mux {
125 Af(u8),
127 Additional,
130 Analog,
132 Iomuxc {
134 mux_register: u32,
136 mux_mode: u8,
138 input: Option<(u32, u8)>,
141 config_register: u32,
143 },
144}
145
146#[derive(Debug, Clone, PartialEq, Eq)]
148pub struct Package {
149 pub name: String,
151 pub parts: Vec<String>,
154}
155
156#[derive(Debug, Clone, PartialEq, Eq)]
158pub struct Pad {
159 pub name: String,
161 pub packages: Option<Vec<String>>,
163}
164
165#[derive(Debug, Clone, PartialEq, Eq)]
167pub struct Signal {
168 pub pad: String,
170 pub function: String,
172 pub peripheral: Option<String>,
175 pub signal: Option<String>,
177 pub mux: Mux,
179}
180
181#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
183pub struct DmaRequest {
184 pub controller: String,
187 pub request: u16,
189 pub function: String,
191 pub peripheral: Option<String>,
193 pub signal: Option<String>,
195}
196
197#[derive(Debug, Clone)]
199pub struct SignalMap {
200 packages: Vec<Package>,
201 pads: HashMap<String, Pad>,
203 signals: Vec<Signal>,
204 dma: Vec<DmaRequest>,
205}
206
207impl SignalMap {
208 pub fn packages(&self) -> &[Package] {
210 &self.packages
211 }
212
213 pub fn package_of(&self, chip: &str) -> Option<&Package> {
220 let chip = chip.to_ascii_uppercase();
221 self.packages.iter().find(|package| {
222 package.parts.iter().any(|part| {
223 let stem = part.strip_suffix(['x', 'X']).unwrap_or(part);
224 chip.starts_with(&stem.to_ascii_uppercase())
225 })
226 })
227 }
228
229 pub fn pad(&self, name: &str) -> Option<&Pad> {
231 self.pads.get(&name.to_ascii_uppercase())
232 }
233
234 pub fn signals(&self) -> &[Signal] {
236 &self.signals
237 }
238
239 pub fn signal_routes(&self, peripheral: &str, signal: &str) -> Vec<&Signal> {
245 let function = format!("{peripheral}_{signal}").to_ascii_uppercase();
246 self.signals
247 .iter()
248 .filter(|s| s.function == function)
249 .collect()
250 }
251
252 pub fn dma_requests(&self) -> &[DmaRequest] {
254 &self.dma
255 }
256
257 pub fn dma_requests_for(&self, peripheral: &str) -> Vec<&DmaRequest> {
259 let prefix = format!("{}_", peripheral.to_ascii_uppercase());
260 self.dma
261 .iter()
262 .filter(|d| {
263 d.peripheral
264 .as_deref()
265 .is_some_and(|p| p.eq_ignore_ascii_case(peripheral))
266 || d.function.starts_with(&prefix)
267 })
268 .collect()
269 }
270}
271
272pub fn load(chip: &str) -> Result<SignalMap, DataError> {
282 let needle = normalize(chip);
283 let entry = FAMILIES
284 .iter()
285 .find(|entry| matches(&needle, entry.family))
286 .ok_or_else(|| DataError::SignalMapUnavailable(chip.to_string()))?;
287 let parse_err = |detail: String| DataError::SignalMapParse {
288 chip: chip.to_string(),
289 detail,
290 };
291
292 let raw: RawMap = serde_json::from_str(entry.json).map_err(|err| parse_err(err.to_string()))?;
293
294 let mut signals = Vec::with_capacity(raw.signals.len());
295 for s in raw.signals {
296 let register = |value: &str| {
297 parse_hex(value).ok_or_else(|| {
298 parse_err(format!(
299 "{} on {}: bad register '{value}'",
300 s.function, s.pad
301 ))
302 })
303 };
304 let mux = match &s.mux {
305 RawMux::Af { af } => Mux::Af(*af),
306 RawMux::Additional => Mux::Additional,
307 RawMux::Analog => Mux::Analog,
308 RawMux::Iomuxc {
309 mux_register,
310 mux_mode,
311 input_register,
312 input_daisy,
313 config_register,
314 } => Mux::Iomuxc {
315 mux_register: register(mux_register)?,
316 mux_mode: *mux_mode,
317 input: match (input_register, input_daisy) {
318 (Some(reg), Some(daisy)) => Some((register(reg)?, *daisy)),
319 (None, None) => None,
320 _ => {
321 return Err(parse_err(format!(
322 "{} on {}: input register and daisy disagree",
323 s.function, s.pad
324 )));
325 }
326 },
327 config_register: register(config_register)?,
328 },
329 };
330 signals.push(Signal {
331 pad: s.pad,
332 function: s.function,
333 peripheral: s.peripheral,
334 signal: s.signal,
335 mux,
336 });
337 }
338
339 Ok(SignalMap {
340 packages: raw
341 .packages
342 .into_iter()
343 .map(|p| Package {
344 name: p.name,
345 parts: p.parts,
346 })
347 .collect(),
348 pads: raw
349 .pads
350 .into_iter()
351 .map(|p| {
352 (
353 p.name.to_ascii_uppercase(),
354 Pad {
355 name: p.name,
356 packages: p.packages,
357 },
358 )
359 })
360 .collect(),
361 signals,
362 dma: raw.dma,
363 })
364}
365
366fn parse_hex(value: &str) -> Option<u32> {
367 u32::from_str_radix(value.strip_prefix("0x")?, 16).ok()
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373
374 #[test]
377 fn every_family_parses() {
378 for chip in [
379 "stm32mp257fak3",
380 "stm32mp235fak3",
381 "stm32mp215fan3",
382 "mimx9596xvt",
383 "mimx9529avzxn",
384 "mimx9352",
385 ] {
386 let map = load(chip).unwrap_or_else(|err| panic!("{chip}: {err}"));
387 assert!(!map.signals().is_empty(), "{chip} has no signals");
388 assert!(!map.dma_requests().is_empty(), "{chip} has no DMA requests");
389 }
390 }
391
392 #[test]
393 fn rejects_other_dies_sharing_an_imx_prefix() {
394 for chip in ["mimx9375", "mimx9371xxvtx"] {
395 assert!(
396 matches!(load(chip), Err(DataError::SignalMapUnavailable(_))),
397 "{chip} should not resolve"
398 );
399 }
400 }
401
402 #[test]
403 fn stm32mp25_i2c1_sda_routes_and_package() {
404 let map = load("stm32mp257fak3").unwrap();
405 let mut routes: Vec<(String, Mux)> = map
406 .signal_routes("i2c1", "sda")
407 .into_iter()
408 .map(|s| (s.pad.clone(), s.mux))
409 .collect();
410 routes.sort_by(|a, b| a.0.cmp(&b.0));
411 assert_eq!(
412 routes,
413 [
414 ("PA2".into(), Mux::Af(10)),
415 ("PI1".into(), Mux::Af(9)),
416 ("PJ10".into(), Mux::Af(9)),
417 ]
418 );
419
420 let package = map
421 .package_of("stm32mp257fak3")
422 .expect("FAK is a known part");
423 assert_eq!(package.name, "VFBGA424");
424 assert!(map.package_of("stm32mp257").is_none());
425 let pa0 = map.pad("pa0").expect("PA0 exists");
426 assert!(pa0.packages.as_ref().unwrap().contains(&package.name));
427 }
428
429 #[test]
430 fn mimx93_lpuart1_tx_iomuxc_and_dma() {
431 let map = load("imx93").unwrap();
432 let routes = map.signal_routes("LPUART1", "TX");
433 assert_eq!(routes.len(), 1);
434 assert_eq!(routes[0].pad, "UART1_TXD");
435 assert_eq!(
436 routes[0].mux,
437 Mux::Iomuxc {
438 mux_register: 0x543c_0184,
439 mux_mode: 0,
440 input: None,
441 config_register: 0x543c_0334,
442 }
443 );
444 assert!(map.package_of("mimx9352").is_none());
445 assert_eq!(map.pad("UART1_TXD").unwrap().packages, None);
446
447 let dma: Vec<(&str, u16)> = map
448 .dma_requests_for("lpuart1")
449 .into_iter()
450 .map(|d| (d.function.as_str(), d.request))
451 .collect();
452 assert_eq!(dma, [("LPUART1_TX", 16), ("LPUART1_RX", 17)]);
453 }
454
455 #[test]
456 fn rejects_unknown_family() {
457 assert!(matches!(
458 load("stm32h747"),
459 Err(DataError::SignalMapUnavailable(_))
460 ));
461 }
462}