atsamd_hal/sercom/i2c/impl_ehal.rs
1//! [`embedded-hal`] trait implementations for [`I2c`]s
2
3use super::{I2c, config::AnyConfig, flags::Error};
4use crate::ehal::i2c::{self, ErrorKind, ErrorType, NoAcknowledgeSource, Operation};
5
6impl i2c::Error for Error {
7 #[allow(unreachable_patterns)]
8 fn kind(&self) -> ErrorKind {
9 match self {
10 Error::BusError => ErrorKind::Bus,
11 Error::ArbitrationLost => ErrorKind::ArbitrationLoss,
12 Error::LengthError => ErrorKind::Other,
13 Error::Nack => ErrorKind::NoAcknowledge(NoAcknowledgeSource::Unknown),
14 Error::Timeout => ErrorKind::Other,
15 // Pattern reachable when "dma" feature is enabled
16 _ => ErrorKind::Other,
17 }
18 }
19}
20
21impl<C: AnyConfig, D> ErrorType for I2c<C, D> {
22 type Error = Error;
23}
24
25impl<C: AnyConfig, D> I2c<C, D> {
26 pub(super) fn transaction_byte_by_byte(
27 &mut self,
28 address: u8,
29 operations: &mut [Operation<'_>],
30 ) -> Result<(), Error> {
31 let mut op_groups = chunk_operations(operations).peekable();
32
33 while let Some(group) = op_groups.next() {
34 let mut group = group.iter_mut();
35 // Unwrapping is OK here because chunk_operations will never give us a 0-length
36 // chunk.
37 let op = group.next().unwrap();
38
39 // First operation in the group - send a START with the address, and the first
40 // operation.
41 match op {
42 Operation::Read(buf) => self.do_read(address, buf)?,
43 Operation::Write(buf) => self.do_write(address, buf)?,
44 }
45
46 // For all subsequent operations, just send/read more bytes without any more
47 // ceremony.
48 for op in group {
49 match op {
50 Operation::Read(buf) => self.continue_read(buf)?,
51 Operation::Write(buf) => self.continue_write(buf)?,
52 }
53 }
54
55 let regs = &mut self.config.as_mut().registers;
56 if op_groups.peek().is_some() {
57 // If we still have more groups to go, send a repeated start
58 regs.cmd_repeated_start();
59 } else {
60 // Otherwise, send a stop
61 regs.cmd_stop();
62 }
63 }
64
65 Ok(())
66 }
67}
68
69impl<C: AnyConfig> i2c::I2c for I2c<C> {
70 fn transaction(
71 &mut self,
72 address: u8,
73 operations: &mut [Operation<'_>],
74 ) -> Result<(), Self::Error> {
75 self.transaction_byte_by_byte(address, operations)?;
76 Ok(())
77 }
78
79 fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
80 self.do_write(address, bytes)?;
81 self.cmd_stop();
82 Ok(())
83 }
84
85 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
86 self.do_read(address, buffer)?;
87 self.cmd_stop();
88 Ok(())
89 }
90
91 fn write_read(
92 &mut self,
93 address: u8,
94 bytes: &[u8],
95 buffer: &mut [u8],
96 ) -> Result<(), Self::Error> {
97 self.do_write_read(address, bytes, buffer)?;
98 self.cmd_stop();
99 Ok(())
100 }
101}
102
103#[cfg(feature = "dma")]
104mod dma {
105 use super::*;
106 use crate::dmac::ReadyChannel;
107 use crate::dmac::{AnyChannel, Ready, channel, sram::DmacDescriptor};
108 use crate::sercom::dma::{SercomPtr, SharedSliceBuffer, read_dma_linked, write_dma_linked};
109 use crate::sercom::{self, Sercom};
110
111 impl<C, D, S, R> I2c<C, D>
112 where
113 C: AnyConfig<Sercom = S>,
114 S: Sercom,
115 D: AnyChannel<Status = R>,
116 R: ReadyChannel,
117 {
118 pub(in super::super) fn sercom_ptr(&self) -> SercomPtr<sercom::i2c::Word> {
119 SercomPtr(self.data_ptr())
120 }
121
122 /// Walk up the transfer linked list, and calculate the number of beats
123 /// the entire block list contains.
124 ///
125 /// # Safety
126 ///
127 /// If `next` is [`Some`], the pointer in its `descaddr` field, along
128 /// with the descriptor it points to, etc, must point to a valid
129 /// [`DmacDescriptor`] memory location, or be null. They must not be
130 /// circular (ie, points to itself).
131 #[inline]
132 unsafe fn linked_transfer_length(next: &Option<&mut DmacDescriptor>) -> usize {
133 let mut cnt = 0;
134
135 if let Some(next) = next {
136 cnt += next.beat_count() as usize;
137 let mut next_ptr = next.next_descriptor();
138
139 while !next_ptr.is_null() {
140 let next = unsafe { *next_ptr };
141 cnt += next.beat_count() as usize;
142 next_ptr = next.next_descriptor();
143 }
144 }
145
146 cnt
147 }
148
149 /// Prepare an I2C read transaction, with the option to add in linked
150 /// transfers after this first transaction.
151 ///
152 /// # Safety
153 ///
154 /// If `next` is [`Some`], the pointer in its `descaddr` field, along
155 /// with the descriptor it points to, etc, must point to a valid
156 /// [`DmacDescriptor`] memory location, or be null. They must not be
157 /// circular (ie, points to itself). Any linked transfer must
158 /// strictly be a read transaction (destination pointer is a byte
159 /// buffer, source pointer is the SERCOM DATA register).
160 #[inline]
161 pub(in super::super) unsafe fn prepare_read_linked(
162 &mut self,
163 address: u8,
164 dest: &mut [u8],
165 next: &Option<&mut DmacDescriptor>,
166 ) -> Result<(), Error> {
167 if dest.is_empty() {
168 return Ok(());
169 }
170
171 self.check_bus_status()?;
172
173 // Calculate the total number of bytes for this transaction across all linked
174 // transfers, including the first transfer.
175 let transfer_len = unsafe { dest.len() + Self::linked_transfer_length(next) };
176
177 assert!(
178 transfer_len <= 255,
179 "I2C read transfers of more than 255 bytes are unsupported."
180 );
181
182 self.start_dma_read(address, transfer_len as u8);
183 Ok(())
184 }
185
186 /// Prepare an I2C write transaction, with the option to add in linked
187 /// transfers after this first transaction.
188 ///
189 /// # Safety
190 ///
191 /// If `next` is [`Some`], the pointer in its `descaddr` field, along
192 /// with the descriptor it points to, etc, must point to a valid
193 /// [`DmacDescriptor`] memory location, or be null. They must not be
194 /// circular (ie, points to itself). Any linked transfer must
195 /// strictly be a write transaction (source pointer is a byte buffer,
196 /// destination pointer is the SERCOM DATA register).
197 pub(in super::super) unsafe fn prepare_write_linked(
198 &mut self,
199 address: u8,
200 source: &[u8],
201 next: &Option<&mut DmacDescriptor>,
202 ) -> Result<(), Error> {
203 self.check_bus_status()?;
204
205 if source.is_empty() {
206 return Ok(());
207 }
208
209 // Calculate the total number of bytes for this transaction across all linked
210 // transfers, including the first transfer.
211 let transfer_len = unsafe { source.len() + Self::linked_transfer_length(next) };
212
213 assert!(
214 transfer_len <= 255,
215 "I2C write transfers of more than 255 bytes are unsupported."
216 );
217
218 self.start_dma_write(address, transfer_len as u8);
219 Ok(())
220 }
221 }
222
223 impl<C, D, S> I2c<C, D>
224 where
225 C: AnyConfig<Sercom = S>,
226 S: Sercom,
227 D: AnyChannel<Status = Ready>,
228 {
229 /// Make an I2C read transaction, with the option to add in linked
230 /// transfers after this first transaction.
231 ///
232 /// # Safety
233 ///
234 /// If `next` is [`Some`], the pointer in its `descaddr` field, along
235 /// with the descriptor it points to, etc, must point to a valid
236 /// [`DmacDescriptor`] memory location, or be null. They must not be
237 /// circular (ie, points to itself). Any linked transfer must
238 /// strictly be a read transaction (destination pointer is a byte
239 /// buffer, source pointer is the SERCOM DATA register).
240 #[inline]
241 unsafe fn read_linked(
242 &mut self,
243 address: u8,
244 mut dest: &mut [u8],
245 next: Option<&mut DmacDescriptor>,
246 ) -> Result<(), Error> {
247 unsafe {
248 self.prepare_read_linked(address, dest, &next)?;
249 let sercom_ptr = self.sercom_ptr();
250 let channel = self._dma_channel.as_mut();
251
252 // SAFETY: We must make sure that any DMA transfer is complete or stopped before
253 // returning.
254 read_dma_linked::<_, _, S>(channel, sercom_ptr, &mut dest, next);
255
256 while !channel.xfer_complete() {
257 core::hint::spin_loop();
258 }
259
260 // Defensively disable channel
261 channel.stop();
262
263 self.read_status().check_bus_error()?;
264 self._dma_channel.as_mut().xfer_success()?;
265 Ok(())
266 }
267 }
268
269 /// Make an I2C write transaction, with the option to add in linked
270 /// transfers after this first transaction.
271 ///
272 /// # Safety
273 ///
274 /// If `next` is [`Some`], the pointer in its `descaddr` field, along
275 /// with the descriptor it points to, etc, must point to a valid
276 /// [`DmacDescriptor`] memory location, or be null. They must not be
277 /// circular (ie, points to itself). Any linked transfer must
278 /// strictly be a write transaction (source pointer is a byte buffer,
279 /// destination pointer is the SERCOM DATA register).
280 #[inline]
281 unsafe fn write_linked(
282 &mut self,
283 address: u8,
284 source: &[u8],
285 next: Option<&mut DmacDescriptor>,
286 ) -> Result<(), Error> {
287 unsafe {
288 self.prepare_write_linked(address, source, &next)?;
289
290 let sercom_ptr = self.sercom_ptr();
291 let mut bytes = SharedSliceBuffer::from_slice(source);
292 let channel = self._dma_channel.as_mut();
293
294 // SAFETY: We must make sure that any DMA transfer is complete or stopped before
295 // returning.
296
297 write_dma_linked::<_, _, S>(channel, sercom_ptr, &mut bytes, next);
298
299 while !channel.xfer_complete() {
300 core::hint::spin_loop();
301 }
302
303 // Defensively disable channel
304 channel.stop();
305
306 while !self.read_status().is_idle() {
307 core::hint::spin_loop();
308 }
309
310 self.read_status().check_bus_error()?;
311 self._dma_channel.as_mut().xfer_success()?;
312 Ok(())
313 }
314 }
315 }
316
317 impl<C, D, S> i2c::I2c for I2c<C, D>
318 where
319 C: AnyConfig<Sercom = S>,
320 S: Sercom,
321 D: AnyChannel<Status = Ready>,
322 {
323 #[inline]
324 fn transaction(
325 &mut self,
326 address: u8,
327 operations: &mut [i2c::Operation<'_>],
328 ) -> Result<(), Self::Error> {
329 use i2c::Operation::{Read, Write};
330
331 const NUM_LINKED_TRANSFERS: usize = 16;
332
333 if operations.is_empty() {
334 return Ok(());
335 }
336
337 let mut sercom_ptr = self.sercom_ptr();
338
339 // Reserve some space for linked DMA transfer descriptors.
340 // Uses 256 bytes of memory.
341 //
342 // In practice this means that we can only support 17 continuously
343 // linked operations of the same type (R/W) before having to issue
344 // an I2C STOP. DMA-enabled I2C transfers automatically issue stop
345 // commands, and there is no way to turn off that behaviour.
346 //
347 // In the event that we have more than 17 contiguous operations of
348 // the same type, we must revert to the byte-by-byte I2C implementations.
349 let mut descriptors = heapless::Vec::<DmacDescriptor, NUM_LINKED_TRANSFERS>::new();
350
351 let op_groups = chunk_operations(operations);
352
353 for group in op_groups {
354 descriptors.clear();
355
356 // Default to byte-by-byte impl if we have more than 17 continuous operations,
357 // as we would overflow our DMA linked transfer reeserved space otherwise.
358 if group.len() > NUM_LINKED_TRANSFERS {
359 self.transaction_byte_by_byte(address, group)?;
360 } else {
361 // --- Setup all linked descriptors ---
362
363 // Skip the first operation; we will deal with it when creating the I2C transfer
364 // (read_dma_linked/write_dma_linked). Every other operation is a linked
365 // transfer, and we must treat them accordingly.
366 for op in group.iter_mut().skip(1) {
367 match op {
368 Read(buffer) => {
369 if buffer.is_empty() {
370 continue;
371 }
372 // Add a new linked descriptor to the stack
373 descriptors
374 .push(DmacDescriptor::default())
375 .unwrap_or_else(|_| panic!("BUG: DMAC descriptors overflow"));
376 let last_descriptor = descriptors.last_mut().unwrap();
377 let next_ptr =
378 (last_descriptor as *mut DmacDescriptor).wrapping_add(1);
379
380 unsafe {
381 channel::write_descriptor(
382 last_descriptor,
383 &mut sercom_ptr,
384 buffer,
385 // Always link the next descriptor. We then set the last
386 // transfer's link pointer to null lower down in the code.
387 next_ptr,
388 );
389 }
390 }
391
392 Write(bytes) => {
393 if bytes.is_empty() {
394 continue;
395 }
396 // Add a new linked descriptor to the stack
397 descriptors
398 .push(DmacDescriptor::default())
399 .unwrap_or_else(|_| panic!("BUG: DMAC descriptors overflow"));
400 let last_descriptor = descriptors.last_mut().unwrap();
401 let next_ptr =
402 (last_descriptor as *mut DmacDescriptor).wrapping_add(1);
403
404 let mut bytes = SharedSliceBuffer::from_slice(bytes);
405 unsafe {
406 channel::write_descriptor(
407 last_descriptor,
408 &mut bytes,
409 &mut sercom_ptr,
410 // Always link the next descriptor. We then set the last
411 // transfer's link pointer to null lower down in the code.
412 next_ptr,
413 );
414 }
415 }
416 }
417 }
418
419 // Set the last descriptor to a null pointer to stop the transfer, and avoid
420 // buffer overflow UB.
421 if let Some(d) = descriptors.last_mut() {
422 d.set_next_descriptor(core::ptr::null_mut());
423 }
424
425 // Now setup and perform the actual transfer
426 match group.first_mut().unwrap() {
427 Read(buffer) => unsafe {
428 self.read_linked(address, buffer, descriptors.first_mut())?;
429 },
430 Write(bytes) => unsafe {
431 self.write_linked(address, bytes, descriptors.first_mut())?;
432 },
433 }
434 }
435 }
436
437 Ok(())
438 }
439
440 #[inline]
441 fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
442 unsafe { self.write_linked(address, bytes, None) }
443 }
444
445 #[inline]
446 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
447 unsafe { self.read_linked(address, buffer, None) }
448 }
449
450 #[inline]
451 fn write_read(
452 &mut self,
453 address: u8,
454 bytes: &[u8],
455 buffer: &mut [u8],
456 ) -> Result<(), Self::Error> {
457 self.write(address, bytes)?;
458 self.read(address, buffer)?;
459 Ok(())
460 }
461 }
462}
463
464impl<C: AnyConfig> crate::ehal_02::blocking::i2c::Write for I2c<C> {
465 type Error = Error;
466
467 fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
468 self.do_write(addr, bytes)?;
469 self.cmd_stop();
470 Ok(())
471 }
472}
473
474impl<C: AnyConfig> crate::ehal_02::blocking::i2c::Read for I2c<C> {
475 type Error = Error;
476
477 fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
478 self.do_read(addr, buffer)?;
479 self.cmd_stop();
480 Ok(())
481 }
482}
483
484impl<C: AnyConfig> crate::ehal_02::blocking::i2c::WriteRead for I2c<C> {
485 type Error = Error;
486
487 fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {
488 self.do_write_read(addr, bytes, buffer)?;
489 self.cmd_stop();
490 Ok(())
491 }
492}
493
494/// Arrange all operations in contiguous chunks of the same R/W type
495pub(super) fn chunk_operations<'a, 'op>(
496 operations: &'a mut [Operation<'op>],
497) -> impl Iterator<Item = &'a mut [Operation<'op>]> {
498 use i2c::Operation::{Read, Write};
499
500 operations.chunk_by_mut(|this, next| {
501 matches!((this, next), (Write(_), Write(_)) | (Read(_), Read(_)))
502 })
503}