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}