Skip to main content

osiris/drivers/
spi.rs

1use crate::error::Result;
2use crate::hal;
3use crate::sync::once::LazyLock;
4use crate::sync::spinlock::SpinLocked;
5use crate::types::array::Vec;
6
7// TODO: The 3 is must be a device-tree driven constant.
8static BUSES: LazyLock<SpinLocked<Vec<Result<hal::spi::Bus>, 3>>> = LazyLock::new(|| {
9    let mut buses = Vec::<Result<hal::spi::Bus>, 3>::new();
10    kprint!(
11        "Found {} SPI bus entries\n",
12        hal::device_tree::SPI_BUS_REGISTRY.len()
13    );
14    for cfg in hal::device_tree::SPI_BUS_REGISTRY {
15        let bus = hal::spi::init(cfg).map_err(|e| e.into());
16        match &bus {
17            Ok(_) => kprint!("    Initialized SPI bus at 0x{:x}\n", cfg.instance),
18            Err(e) => kprint!(
19                "    Failed to initialize SPI bus at 0x{:x}: {e}\n",
20                cfg.instance
21            ),
22        }
23        buses.push(bus).expect("Bus must fit in inline storage.");
24    }
25    SpinLocked::new(buses)
26});
27
28#[derive(Clone, Copy)]
29pub struct Config {
30    pub hz: Option<u32>,
31}
32
33impl Default for Config {
34    fn default() -> Self {
35        Self { hz: None }
36    }
37}
38
39pub struct Device {
40    desc: hal::spi::Device,
41}
42
43impl Device {
44    fn new(desc: hal::spi::Device) -> Self {
45        Self { desc }
46    }
47}
48
49impl Device {
50    pub fn open(compatible: &str, ordinal: usize, config: Config) -> Result<Self> {
51        let dev_cfg = match hal::device_tree::spi_device_by_compatible(compatible, ordinal) {
52            Some(cfg) => cfg,
53            None => {
54                return Err(kerr!(
55                    ENODEV,
56                    "spi device not found: compatible={compatible}, ordinal={ordinal}"
57                ));
58            }
59        };
60
61        for (i, bus_cfg) in hal::device_tree::SPI_BUS_REGISTRY.iter().enumerate() {
62            if bus_cfg.node == dev_cfg.bus_node {
63                let buses = BUSES.lock();
64                let bus = &buses[i].as_ref().map_err(|e| e.clone())?;
65                let desc = hal::spi::init_device(bus, dev_cfg, config.hz)?;
66                return Ok(Self::new(desc));
67            }
68        }
69
70        Err(kerr!(EINVAL))
71    }
72
73    pub fn transfer_u8(&self, tx: &[u8], rx: &mut [u8]) -> Result<()> {
74        match hal::spi::transfer_words(&self.desc, tx, rx) {
75            Ok(()) => Ok(()),
76            Err(e) => Err(e.into()),
77        }
78    }
79
80    pub fn transfer_u16(&self, tx: &[u16], rx: &mut [u16]) -> Result<()> {
81        match hal::spi::transfer_words(&self.desc, tx, rx) {
82            Ok(()) => Ok(()),
83            Err(e) => Err(e.into()),
84        }
85    }
86}
87
88impl Drop for Device {
89    fn drop(&mut self) {
90        hal::spi::deinit_device(&self.desc);
91    }
92}
93
94pub fn init() {
95    // Force initialization of the BUSES static.
96    let _ = LazyLock::force(&BUSES);
97}