atsamd_hal/sercom/i2c/impl_ehal.rs
1//! [`embedded-hal`] trait implementations for [`I2c`]s
2
3use super::{config::AnyConfig, flags::Error, I2c};
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::{channel, sram::DmacDescriptor, AnyChannel, Ready};
108 use crate::sercom::dma::{read_dma_linked, write_dma_linked, SercomPtr, SharedSliceBuffer};
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 = *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 = 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 = 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 self.prepare_read_linked(address, dest, &next)?;
248 let sercom_ptr = self.sercom_ptr();
249 let channel = self._dma_channel.as_mut();
250
251 // SAFETY: We must make sure that any DMA transfer is complete or stopped before
252 // returning.
253 read_dma_linked::<_, _, S>(channel, sercom_ptr, &mut dest, next);
254
255 while !channel.xfer_complete() {
256 core::hint::spin_loop();
257 }
258
259 // Defensively disable channel
260 channel.stop();
261
262 self.read_status().check_bus_error()?;
263 self._dma_channel.as_mut().xfer_success()?;
264 Ok(())
265 }
266
267 /// Make an I2C write transaction, with the option to add in linked
268 /// transfers after this first transaction.
269 ///
270 /// # Safety
271 ///
272 /// If `next` is [`Some`], the pointer in its `descaddr` field, along
273 /// with the descriptor it points to, etc, must point to a valid
274 /// [`DmacDescriptor`] memory location, or be null. They must not be
275 /// circular (ie, points to itself). Any linked transfer must
276 /// strictly be a write transaction (source pointer is a byte buffer,
277 /// destination pointer is the SERCOM DATA register).
278 #[inline]
279 unsafe fn write_linked(
280 &mut self,
281 address: u8,
282 source: &[u8],
283 next: Option<&mut DmacDescriptor>,
284 ) -> Result<(), Error> {
285 self.prepare_write_linked(address, source, &next)?;
286
287 let sercom_ptr = self.sercom_ptr();
288 let mut bytes = SharedSliceBuffer::from_slice(source);
289 let channel = self._dma_channel.as_mut();
290
291 // SAFETY: We must make sure that any DMA transfer is complete or stopped before
292 // returning.
293 write_dma_linked::<_, _, S>(channel, sercom_ptr, &mut bytes, next);
294
295 while !channel.xfer_complete() {
296 core::hint::spin_loop();
297 }
298
299 // Defensively disable channel
300 channel.stop();
301
302 while !self.read_status().is_idle() {
303 core::hint::spin_loop();
304 }
305
306 self.read_status().check_bus_error()?;
307 self._dma_channel.as_mut().xfer_success()?;
308 Ok(())
309 }
310 }
311
312 impl<C, D, S> i2c::I2c for I2c<C, D>
313 where
314 C: AnyConfig<Sercom = S>,
315 S: Sercom,
316 D: AnyChannel<Status = Ready>,
317 {
318 #[inline]
319 fn transaction(
320 &mut self,
321 address: u8,
322 operations: &mut [i2c::Operation<'_>],
323 ) -> Result<(), Self::Error> {
324 use i2c::Operation::{Read, Write};
325
326 const NUM_LINKED_TRANSFERS: usize = 16;
327
328 if operations.is_empty() {
329 return Ok(());
330 }
331
332 let mut sercom_ptr = self.sercom_ptr();
333
334 // Reserve some space for linked DMA transfer descriptors.
335 // Uses 256 bytes of memory.
336 //
337 // In practice this means that we can only support 17 continuously
338 // linked operations of the same type (R/W) before having to issue
339 // an I2C STOP. DMA-enabled I2C transfers automatically issue stop
340 // commands, and there is no way to turn off that behaviour.
341 //
342 // In the event that we have more than 17 contiguous operations of
343 // the same type, we must revert to the byte-by-byte I2C implementations.
344 let mut descriptors = heapless::Vec::<DmacDescriptor, NUM_LINKED_TRANSFERS>::new();
345
346 let op_groups = chunk_operations(operations);
347
348 for group in op_groups {
349 descriptors.clear();
350
351 // Default to byte-by-byte impl if we have more than 17 continuous operations,
352 // as we would overflow our DMA linked transfer reeserved space otherwise.
353 if group.len() > NUM_LINKED_TRANSFERS {
354 self.transaction_byte_by_byte(address, group)?;
355 } else {
356 // --- Setup all linked descriptors ---
357
358 // Skip the first operation; we will deal with it when creating the I2C transfer
359 // (read_dma_linked/write_dma_linked). Every other operation is a linked
360 // transfer, and we must treat them accordingly.
361 for op in group.iter_mut().skip(1) {
362 match op {
363 Read(ref mut buffer) => {
364 if buffer.is_empty() {
365 continue;
366 }
367 // Add a new linked descriptor to the stack
368 descriptors
369 .push(DmacDescriptor::default())
370 .unwrap_or_else(|_| panic!("BUG: DMAC descriptors overflow"));
371 let last_descriptor = descriptors.last_mut().unwrap();
372 let next_ptr =
373 (last_descriptor as *mut DmacDescriptor).wrapping_add(1);
374
375 unsafe {
376 channel::write_descriptor(
377 last_descriptor,
378 &mut sercom_ptr,
379 buffer,
380 // Always link the next descriptor. We then set the last
381 // transfer's link pointer to null lower down in the code.
382 next_ptr,
383 );
384 }
385 }
386
387 Write(bytes) => {
388 if bytes.is_empty() {
389 continue;
390 }
391 // Add a new linked descriptor to the stack
392 descriptors
393 .push(DmacDescriptor::default())
394 .unwrap_or_else(|_| panic!("BUG: DMAC descriptors overflow"));
395 let last_descriptor = descriptors.last_mut().unwrap();
396 let next_ptr =
397 (last_descriptor as *mut DmacDescriptor).wrapping_add(1);
398
399 let mut bytes = SharedSliceBuffer::from_slice(bytes);
400 unsafe {
401 channel::write_descriptor(
402 last_descriptor,
403 &mut bytes,
404 &mut sercom_ptr,
405 // Always link the next descriptor. We then set the last
406 // transfer's link pointer to null lower down in the code.
407 next_ptr,
408 );
409 }
410 }
411 }
412 }
413
414 // Set the last descriptor to a null pointer to stop the transfer, and avoid
415 // buffer overflow UB.
416 if let Some(d) = descriptors.last_mut() {
417 d.set_next_descriptor(core::ptr::null_mut());
418 }
419
420 // Now setup and perform the actual transfer
421 match group.first_mut().unwrap() {
422 Read(ref mut buffer) => unsafe {
423 self.read_linked(address, buffer, descriptors.first_mut())?;
424 },
425 Write(bytes) => unsafe {
426 self.write_linked(address, bytes, descriptors.first_mut())?;
427 },
428 }
429 }
430 }
431
432 Ok(())
433 }
434
435 #[inline]
436 fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
437 unsafe { self.write_linked(address, bytes, None) }
438 }
439
440 #[inline]
441 fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
442 unsafe { self.read_linked(address, buffer, None) }
443 }
444
445 #[inline]
446 fn write_read(
447 &mut self,
448 address: u8,
449 bytes: &[u8],
450 buffer: &mut [u8],
451 ) -> Result<(), Self::Error> {
452 self.write(address, bytes)?;
453 self.read(address, buffer)?;
454 Ok(())
455 }
456 }
457}
458
459impl<C: AnyConfig> crate::ehal_02::blocking::i2c::Write for I2c<C> {
460 type Error = Error;
461
462 fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
463 self.do_write(addr, bytes)?;
464 self.cmd_stop();
465 Ok(())
466 }
467}
468
469impl<C: AnyConfig> crate::ehal_02::blocking::i2c::Read for I2c<C> {
470 type Error = Error;
471
472 fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
473 self.do_read(addr, buffer)?;
474 self.cmd_stop();
475 Ok(())
476 }
477}
478
479impl<C: AnyConfig> crate::ehal_02::blocking::i2c::WriteRead for I2c<C> {
480 type Error = Error;
481
482 fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {
483 self.do_write_read(addr, bytes, buffer)?;
484 self.cmd_stop();
485 Ok(())
486 }
487}
488
489/// Arrange all operations in contiguous chunks of the same R/W type
490pub(super) fn chunk_operations<'a, 'op>(
491 operations: &'a mut [Operation<'op>],
492) -> impl Iterator<Item = &'a mut [Operation<'op>]> {
493 use i2c::Operation::{Read, Write};
494
495 operations.chunk_by_mut(|this, next| {
496 matches!((this, next), (Write(_), Write(_)) | (Read(_), Read(_)))
497 })
498}