Skip to main content

osiris/drivers/
i2c.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::i2c::Bus>, 3>>> = LazyLock::new(|| {
9    let mut buses = Vec::<Result<hal::i2c::Bus>, 3>::new();
10    kprint!(
11        "Found {} I2C bus entries\n",
12        hal::device_tree::I2C_BUS_REGISTRY.len()
13    );
14    for cfg in hal::device_tree::I2C_BUS_REGISTRY {
15        let bus = hal::i2c::init(cfg).map_err(|e| e.into());
16
17        match &bus {
18            Ok(_) => kprint!("    Initialized I2C bus at 0x{:x}\n", cfg.instance),
19            Err(e) => kprint!(
20                "    Failed to initialize I2C bus at 0x{:x}: {e}\n",
21                cfg.instance
22            ),
23        }
24
25        buses.push(bus).expect("Bus must fit in inline storage.");
26    }
27    SpinLocked::new(buses)
28});
29
30pub struct Device {
31    desc: hal::i2c::Device,
32}
33
34impl Device {
35    fn new(desc: hal::i2c::Device) -> Self {
36        Self { desc }
37    }
38}
39
40impl Device {
41    pub fn open(compatible: &str, ordinal: usize) -> Result<Self> {
42        let dev_cfg = match hal::device_tree::i2c_device_by_compatible(compatible, ordinal) {
43            Some(cfg) => cfg,
44            None => {
45                return Err(kerr!(
46                    ENODEV,
47                    "i2c device not found: compatible={compatible}, ordinal={ordinal}"
48                ));
49            }
50        };
51
52        for (i, bus_cfg) in hal::device_tree::I2C_BUS_REGISTRY.iter().enumerate() {
53            if bus_cfg.node == dev_cfg.bus_node {
54                let buses = BUSES.lock();
55                let bus = &buses[i].as_ref().map_err(|e| e.clone())?;
56                let desc = hal::i2c::init_device(bus, dev_cfg)?;
57                return Ok(Self::new(desc));
58            }
59        }
60
61        Err(kerr!(EINVAL))
62    }
63
64    pub fn write(&self, tx: &[u8], timeout: u16) -> Result<()> {
65        match hal::i2c::write(&self.desc, tx, timeout) {
66            Ok(()) => Ok(()),
67            Err(e) => Err(e.into()),
68        }
69    }
70
71    pub fn read(&self, rx: &mut [u8], timeout: u16) -> Result<()> {
72        match hal::i2c::read(&self.desc, rx, timeout) {
73            Ok(()) => Ok(()),
74            Err(e) => Err(e.into()),
75        }
76    }
77
78    pub fn write_read(&self, tx: &[u8], rx: &mut [u8], timeout: u16) -> Result<()> {
79        match hal::i2c::write_read(&self.desc, tx, rx, timeout) {
80            Ok(()) => Ok(()),
81            Err(e) => Err(e.into()),
82        }
83    }
84
85    pub fn check_and_recover_bus(&self) -> Result<bool> {
86        match hal::i2c::bus_recovery_needed(&self.desc) {
87            Ok(true) => match hal::i2c::recover_bus(&self.desc) {
88                Ok(()) => return Ok(true),
89                Err(e) => return Err(e.into()),
90            },
91            Ok(false) => {}
92            Err(e) => return Err(e.into()),
93        }
94        Ok(false)
95    }
96}
97
98impl Drop for Device {
99    fn drop(&mut self) {
100        hal::i2c::deinit_device(&self.desc);
101    }
102}
103
104pub fn init() {
105    // Force initialization of the BUSES static.
106    let _ = LazyLock::force(&BUSES);
107}