kernel/hil/
i2c.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Interface for I2C master and slave peripherals.
6
7use crate::ErrorCode;
8
9use core::fmt;
10use core::fmt::{Display, Formatter};
11
12/// The type of error encountered during I2C communication.
13#[derive(Copy, Clone, Debug, Eq, PartialEq)]
14pub enum Error {
15    /// The slave did not acknowledge the chip address. Most likely the address
16    /// is incorrect or the slave is not properly connected.
17    AddressNak,
18
19    /// The data was not acknowledged by the slave.
20    DataNak,
21
22    /// Arbitration lost, meaning the state of the data line does not correspond
23    /// to the data driven onto it. This can happen, for example, when a
24    /// higher-priority transmission is in progress by a different master.
25    ArbitrationLost,
26
27    /// A start condition was received before received data has been read
28    /// from the receive register.
29    Overrun,
30
31    /// The requested operation wasn't supported.
32    NotSupported,
33
34    /// The underlying device has another request in progress
35    Busy,
36}
37
38impl From<Error> for ErrorCode {
39    fn from(val: Error) -> Self {
40        match val {
41            Error::AddressNak | Error::DataNak => ErrorCode::NOACK,
42            Error::ArbitrationLost => ErrorCode::RESERVE,
43            Error::Overrun => ErrorCode::SIZE,
44            Error::NotSupported => ErrorCode::NOSUPPORT,
45            Error::Busy => ErrorCode::BUSY,
46        }
47    }
48}
49
50impl Display for Error {
51    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
52        let display_str = match *self {
53            Error::AddressNak => "I2C Address Not Acknowledged",
54            Error::DataNak => "I2C Data Not Acknowledged",
55            Error::ArbitrationLost => "I2C Bus Arbitration Lost",
56            Error::Overrun => "I2C receive overrun",
57            Error::NotSupported => "I2C/SMBus command not supported",
58            Error::Busy => "I2C/SMBus is busy",
59        };
60        write!(fmt, "{}", display_str)
61    }
62}
63
64/// This specifies what type of transmission just finished from a Master device.
65#[derive(Copy, Clone, Debug)]
66pub enum SlaveTransmissionType {
67    Write,
68    Read,
69}
70
71/// Interface for an I2C Master hardware driver.
72pub trait I2CMaster<'a> {
73    fn set_master_client(&self, master_client: &'a dyn I2CHwMasterClient);
74    fn enable(&self);
75    fn disable(&self);
76    fn write_read(
77        &self,
78        addr: u8,
79        data: &'static mut [u8],
80        write_len: usize,
81        read_len: usize,
82    ) -> Result<(), (Error, &'static mut [u8])>;
83    fn write(
84        &self,
85        addr: u8,
86        data: &'static mut [u8],
87        len: usize,
88    ) -> Result<(), (Error, &'static mut [u8])>;
89    fn read(
90        &self,
91        addr: u8,
92        buffer: &'static mut [u8],
93        len: usize,
94    ) -> Result<(), (Error, &'static mut [u8])>;
95}
96
97/// Interface for an SMBus Master hardware driver.
98/// The device implementing this will also seperately implement
99/// I2CMaster.
100pub trait SMBusMaster<'a>: I2CMaster<'a> {
101    /// Write data then read data via the I2C Master device in an SMBus
102    /// compatible way.
103    ///
104    /// This function will use the I2C master to write data to a device and
105    /// then read data from the device in a SMBus compatible way. This will be
106    /// a best effort attempt to match the SMBus specification based on what
107    /// the hardware can support.
108    /// This function is expected to make any hardware changes required to
109    /// support SMBus and then revert those changes to support future I2C.
110    ///
111    /// addr: The address of the device to write to
112    /// data: The buffer to write the data from and read back to
113    /// write_len: The length of the write operation
114    /// read_len: The length of the read operation
115    fn smbus_write_read(
116        &self,
117        addr: u8,
118        data: &'static mut [u8],
119        write_len: usize,
120        read_len: usize,
121    ) -> Result<(), (Error, &'static mut [u8])>;
122
123    /// Write data via the I2C Master device in an SMBus compatible way.
124    ///
125    /// This function will use the I2C master to write data to a device in a
126    /// SMBus compatible way. This will be a best effort attempt to match the
127    /// SMBus specification based on what the hardware can support.
128    /// This function is expected to make any hardware changes required to
129    /// support SMBus and then revert those changes to support future I2C.
130    ///
131    /// addr: The address of the device to write to
132    /// data: The buffer to write the data from
133    /// len: The length of the operation
134    fn smbus_write(
135        &self,
136        addr: u8,
137        data: &'static mut [u8],
138        len: usize,
139    ) -> Result<(), (Error, &'static mut [u8])>;
140
141    /// Read data via the I2C Master device in an SMBus compatible way.
142    ///
143    /// This function will use the I2C master to read data from a device in a
144    /// SMBus compatible way. This will be a best effort attempt to match the
145    /// SMBus specification based on what the hardware can support.
146    /// This function is expected to make any hardware changes required to
147    /// support SMBus and then revert those changes to support future I2C.
148    ///
149    /// addr: The address of the device to read from
150    /// buffer: The buffer to store the data to
151    /// len: The length of the operation
152    fn smbus_read(
153        &self,
154        addr: u8,
155        buffer: &'static mut [u8],
156        len: usize,
157    ) -> Result<(), (Error, &'static mut [u8])>;
158}
159
160/// Interface for an I2C Slave hardware driver.
161pub trait I2CSlave<'a> {
162    fn set_slave_client(&self, slave_client: &'a dyn I2CHwSlaveClient);
163    fn enable(&self);
164    fn disable(&self);
165    fn set_address(&self, addr: u8) -> Result<(), Error>;
166    fn write_receive(
167        &self,
168        data: &'static mut [u8],
169        max_len: usize,
170    ) -> Result<(), (Error, &'static mut [u8])>;
171    fn read_send(
172        &self,
173        data: &'static mut [u8],
174        max_len: usize,
175    ) -> Result<(), (Error, &'static mut [u8])>;
176    fn listen(&self);
177}
178
179/// Convenience type for capsules that need hardware that supports both
180/// Master and Slave modes.
181pub trait I2CMasterSlave<'a>: I2CMaster<'a> + I2CSlave<'a> {}
182// Provide blanket implementations for trait group
183// impl<T: I2CMaster + I2CSlave> I2CMasterSlave for T {}
184
185/// Client interface for capsules that use I2CMaster devices.
186pub trait I2CHwMasterClient {
187    /// Called when an I2C command completed. The `error` denotes whether the command completed
188    /// successfully or if an error occurred.
189    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), Error>);
190}
191
192/// Client interface for capsules that use I2CSlave devices.
193pub trait I2CHwSlaveClient {
194    /// Called when an I2C command completed.
195    fn command_complete(
196        &self,
197        buffer: &'static mut [u8],
198        length: usize,
199        transmission_type: SlaveTransmissionType,
200    );
201
202    /// Called from the I2C slave hardware to say that a Master has sent us
203    /// a read message, but the driver did not have a buffer containing data
204    /// setup, and therefore cannot respond. The I2C slave hardware will stretch
205    /// the clock while waiting for the upper layer capsule to provide data
206    /// to send to the remote master. Call `I2CSlave::read_send()` to provide
207    /// data.
208    fn read_expected(&self);
209
210    /// Called from the I2C slave hardware to say that a Master has sent us
211    /// a write message, but there was no buffer setup to read the bytes into.
212    /// The HW will stretch the clock while waiting for the user to call
213    /// `I2CSlave::write_receive()` with a buffer.
214    fn write_expected(&self);
215}
216
217/// Higher-level interface for I2C Master commands that wraps in the I2C
218/// address. It gives an interface for communicating with a specific I2C
219/// device.
220pub trait I2CDevice {
221    fn enable(&self);
222    fn disable(&self);
223    fn write_read(
224        &self,
225        data: &'static mut [u8],
226        write_len: usize,
227        read_len: usize,
228    ) -> Result<(), (Error, &'static mut [u8])>;
229    fn write(&self, data: &'static mut [u8], len: usize) -> Result<(), (Error, &'static mut [u8])>;
230    fn read(&self, buffer: &'static mut [u8], len: usize)
231        -> Result<(), (Error, &'static mut [u8])>;
232}
233
234pub trait SMBusDevice: I2CDevice {
235    /// Write data then read data to a slave device in an SMBus
236    /// compatible way.
237    ///
238    /// This function will use the I2C master to write data to a device and
239    /// then read data from the device in a SMBus compatible way. This will be
240    /// a best effort attempt to match the SMBus specification based on what
241    /// the hardware can support.
242    /// This function is expected to make any hardware changes required to
243    /// support SMBus and then revert those changes to support future I2C.
244    ///
245    /// data: The buffer to write the data from and read back to
246    /// write_len: The length of the write operation
247    /// read_len: The length of the read operation
248    fn smbus_write_read(
249        &self,
250        data: &'static mut [u8],
251        write_len: usize,
252        read_len: usize,
253    ) -> Result<(), (Error, &'static mut [u8])>;
254
255    /// Write data to a slave device in an SMBus compatible way.
256    ///
257    /// This function will use the I2C master to write data to a device in a
258    /// SMBus compatible way. This will be a best effort attempt to match the
259    /// SMBus specification based on what the hardware can support.
260    /// This function is expected to make any hardware changes required to
261    /// support SMBus and then revert those changes to support future I2C.
262    ///
263    /// data: The buffer to write the data from
264    /// len: The length of the operation
265    fn smbus_write(
266        &self,
267        data: &'static mut [u8],
268        len: usize,
269    ) -> Result<(), (Error, &'static mut [u8])>;
270
271    /// Read data from a slave device in an SMBus compatible way.
272    ///
273    /// This function will use the I2C master to read data from a device in a
274    /// SMBus compatible way. This will be a best effort attempt to match the
275    /// SMBus specification based on what the hardware can support.
276    /// This function is expected to make any hardware changes required to
277    /// support SMBus and then revert those changes to support future I2C.
278    ///
279    /// buffer: The buffer to store the data to
280    /// len: The length of the operation
281    fn smbus_read(
282        &self,
283        buffer: &'static mut [u8],
284        len: usize,
285    ) -> Result<(), (Error, &'static mut [u8])>;
286}
287
288/// Client interface for I2CDevice implementations.
289pub trait I2CClient {
290    /// Called when an I2C command completed. The `error` denotes whether the command completed
291    /// successfully or if an error occured.
292    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), Error>);
293}
294
295pub struct NoSMBus;
296
297impl<'a> I2CMaster<'a> for NoSMBus {
298    fn set_master_client(&self, _master_client: &'a dyn I2CHwMasterClient) {}
299    fn enable(&self) {}
300    fn disable(&self) {}
301    fn write_read(
302        &self,
303        _addr: u8,
304        data: &'static mut [u8],
305        _write_len: usize,
306        _read_len: usize,
307    ) -> Result<(), (Error, &'static mut [u8])> {
308        Err((Error::NotSupported, data))
309    }
310    fn write(
311        &self,
312        _addr: u8,
313        data: &'static mut [u8],
314        _len: usize,
315    ) -> Result<(), (Error, &'static mut [u8])> {
316        Err((Error::NotSupported, data))
317    }
318    fn read(
319        &self,
320        _addr: u8,
321        buffer: &'static mut [u8],
322        _len: usize,
323    ) -> Result<(), (Error, &'static mut [u8])> {
324        Err((Error::NotSupported, buffer))
325    }
326}
327
328impl SMBusMaster<'_> for NoSMBus {
329    fn smbus_write_read(
330        &self,
331        _addr: u8,
332        data: &'static mut [u8],
333        _write_len: usize,
334        _read_len: usize,
335    ) -> Result<(), (Error, &'static mut [u8])> {
336        Err((Error::NotSupported, data))
337    }
338
339    fn smbus_write(
340        &self,
341        _addr: u8,
342        data: &'static mut [u8],
343        _len: usize,
344    ) -> Result<(), (Error, &'static mut [u8])> {
345        Err((Error::NotSupported, data))
346    }
347
348    fn smbus_read(
349        &self,
350        _addr: u8,
351        buffer: &'static mut [u8],
352        _len: usize,
353    ) -> Result<(), (Error, &'static mut [u8])> {
354        Err((Error::NotSupported, buffer))
355    }
356}