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}