Skip to main content

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 separately 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<'a> {
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    /// Set the client for the I2CDevice.
234    fn set_client(&'a self, client: &'a dyn I2CClient);
235}
236
237pub trait SMBusDevice<'a>: I2CDevice<'a> {
238    /// Write data then read data to a slave device in an SMBus
239    /// compatible way.
240    ///
241    /// This function will use the I2C master to write data to a device and
242    /// then read data from the device in a SMBus compatible way. This will be
243    /// a best effort attempt to match the SMBus specification based on what
244    /// the hardware can support.
245    /// This function is expected to make any hardware changes required to
246    /// support SMBus and then revert those changes to support future I2C.
247    ///
248    /// data: The buffer to write the data from and read back to
249    /// write_len: The length of the write operation
250    /// read_len: The length of the read operation
251    fn smbus_write_read(
252        &self,
253        data: &'static mut [u8],
254        write_len: usize,
255        read_len: usize,
256    ) -> Result<(), (Error, &'static mut [u8])>;
257
258    /// Write data to a slave device in an SMBus compatible way.
259    ///
260    /// This function will use the I2C master to write data to a device in a
261    /// SMBus compatible way. This will be a best effort attempt to match the
262    /// SMBus specification based on what the hardware can support.
263    /// This function is expected to make any hardware changes required to
264    /// support SMBus and then revert those changes to support future I2C.
265    ///
266    /// data: The buffer to write the data from
267    /// len: The length of the operation
268    fn smbus_write(
269        &self,
270        data: &'static mut [u8],
271        len: usize,
272    ) -> Result<(), (Error, &'static mut [u8])>;
273
274    /// Read data from a slave device in an SMBus compatible way.
275    ///
276    /// This function will use the I2C master to read data from a device in a
277    /// SMBus compatible way. This will be a best effort attempt to match the
278    /// SMBus specification based on what the hardware can support.
279    /// This function is expected to make any hardware changes required to
280    /// support SMBus and then revert those changes to support future I2C.
281    ///
282    /// buffer: The buffer to store the data to
283    /// len: The length of the operation
284    fn smbus_read(
285        &self,
286        buffer: &'static mut [u8],
287        len: usize,
288    ) -> Result<(), (Error, &'static mut [u8])>;
289}
290
291/// Client interface for I2CDevice implementations.
292pub trait I2CClient {
293    /// Called when an I2C command completed. The `error` denotes whether the command completed
294    /// successfully or if an error occured.
295    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), Error>);
296}
297
298pub struct NoSMBus;
299
300impl<'a> I2CMaster<'a> for NoSMBus {
301    fn set_master_client(&self, _master_client: &'a dyn I2CHwMasterClient) {}
302    fn enable(&self) {}
303    fn disable(&self) {}
304    fn write_read(
305        &self,
306        _addr: u8,
307        data: &'static mut [u8],
308        _write_len: usize,
309        _read_len: usize,
310    ) -> Result<(), (Error, &'static mut [u8])> {
311        Err((Error::NotSupported, data))
312    }
313    fn write(
314        &self,
315        _addr: u8,
316        data: &'static mut [u8],
317        _len: usize,
318    ) -> Result<(), (Error, &'static mut [u8])> {
319        Err((Error::NotSupported, data))
320    }
321    fn read(
322        &self,
323        _addr: u8,
324        buffer: &'static mut [u8],
325        _len: usize,
326    ) -> Result<(), (Error, &'static mut [u8])> {
327        Err((Error::NotSupported, buffer))
328    }
329}
330
331impl SMBusMaster<'_> for NoSMBus {
332    fn smbus_write_read(
333        &self,
334        _addr: u8,
335        data: &'static mut [u8],
336        _write_len: usize,
337        _read_len: usize,
338    ) -> Result<(), (Error, &'static mut [u8])> {
339        Err((Error::NotSupported, data))
340    }
341
342    fn smbus_write(
343        &self,
344        _addr: u8,
345        data: &'static mut [u8],
346        _len: usize,
347    ) -> Result<(), (Error, &'static mut [u8])> {
348        Err((Error::NotSupported, data))
349    }
350
351    fn smbus_read(
352        &self,
353        _addr: u8,
354        buffer: &'static mut [u8],
355        _len: usize,
356    ) -> Result<(), (Error, &'static mut [u8])> {
357        Err((Error::NotSupported, buffer))
358    }
359}