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}