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}