From 76988a321a880f20d59adacc08588d3689118184 Mon Sep 17 00:00:00 2001 From: matteotullo Date: Thu, 27 Feb 2025 15:51:14 -0800 Subject: [PATCH] Add NoDma I2C master async bus implementation --- src/i2c/master.rs | 357 ++++++++++++++++++++++++++++++---------------- 1 file changed, 236 insertions(+), 121 deletions(-) diff --git a/src/i2c/master.rs b/src/i2c/master.rs index 3c811647..c8950706 100644 --- a/src/i2c/master.rs +++ b/src/i2c/master.rs @@ -128,7 +128,7 @@ impl<'a> I2cMaster<'a, Blocking> { T::enable(clock); T::into_i2c(); - let this = Self::new_inner::(fc, scl, sda, speed, None)?; + let this = Self::new_inner::(fc, scl, sda, speed, None, None)?; Ok(this) } @@ -331,7 +331,34 @@ impl<'a> I2cMaster<'a, Async> { T::into_i2c(); let ch = dma::Dma::reserve_channel(dma_ch); - let this = Self::new_inner::(fc, scl, sda, speed, Some(ch))?; + let this = Self::new_inner::(fc, scl, sda, speed, Some(ch), None)?; + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Ok(this) + } + + /// IMPORTANT: DO NOT USE unless you are aware of the performance implications of not using DMA. + /// This NoDma I2C bus should only be used when a Flexcomm doesn't support DMA, such as Flexcomm 15. + /// This bus performs byte by byte transfers, awaiting between each byte, + /// which can lead to bus timeouts if a byte is not handled fast enough. + /// + /// use flexcomm fc with Pins scl, sda as an I2C Master bus, configuring to speed and pull + pub fn new_async_nodma( + fc: impl Peripheral

+ 'a, + scl: impl Peripheral

> + 'a, + sda: impl Peripheral

> + 'a, + _irq: impl interrupt::typelevel::Binding> + 'a, + // TODO - integrate clock APIs to allow dynamic freq selection | clock: crate::flexcomm::Clock, + speed: Speed, + ) -> Result { + // TODO - clock integration + let clock = crate::flexcomm::Clock::Sfro; + T::enable(clock); + T::into_i2c(); + + let this = Self::new_inner::(fc, scl, sda, speed, None)?; T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; @@ -490,14 +517,155 @@ impl<'a> I2cMaster<'a, Async> { self.start(address, true).await?; - // Read one byte less using DMA and then read the last byte manually - let (dma_read, last_byte) = read.split_at_mut(read.len() - 1); + if self.dma_ch.is_some() { + // Do a DMA recv + // Read one byte less using DMA and then read the last byte manually + let (dma_read, last_byte) = read.split_at_mut(read.len() - 1); + + if !dma_read.is_empty() { + let transfer = dma::transfer::Transfer::new_read( + self.dma_ch.as_mut().unwrap(), + i2cregs.mstdat().as_ptr() as *mut u8, + dma_read, + Default::default(), + ); + + // According to sections 24.7.7.1 and 24.7.7.2, we should + // first program the DMA channel for carrying out a transfer + // and only then set MSTDMA bit. + // + // Additionally, at this point we know the slave has + // acknowledged the address. + i2cregs.mstctl().write(|w| w.mstdma().enabled()); + + let res = select( + transfer, + poll_fn(|cx| { + I2C_WAKERS[self.info.index].register(cx.waker()); + + i2cregs.intenset().write(|w| { + w.mstpendingen() + .set_bit() + .mstarblossen() + .set_bit() + .mstststperren() + .set_bit() + }); + + let stat = i2cregs.stat().read(); + + if stat.mstarbloss().is_arbitration_loss() { + Poll::Ready(Err::<(), Error>(TransferError::ArbitrationLoss.into())) + } else if stat.mstststperr().is_error() { + Poll::Ready(Err::<(), Error>(TransferError::StartStopError.into())) + } else { + Poll::Pending + } + }), + ) + .await; + + i2cregs.mstctl().write(|w| w.mstdma().disabled()); + + if let Either::Second(e) = res { + e?; + } + } + + self.wait_on( + |me| { + let stat = me.info.regs.stat().read(); + + if stat.mstpending().is_pending() { + Poll::Ready(Ok::<(), Error>(())) + } else if stat.mstarbloss().is_arbitration_loss() { + Poll::Ready(Err(TransferError::ArbitrationLoss.into())) + } else if stat.mstststperr().is_error() { + Poll::Ready(Err(TransferError::StartStopError.into())) + } else { + Poll::Pending + } + }, + |me| { + me.info.regs.intenset().write(|w| { + w.mstpendingen() + .set_bit() + .mstarblossen() + .set_bit() + .mstststperren() + .set_bit() + }); + }, + ) + .await?; + + // Read the last byte + last_byte[0] = i2cregs.mstdat().read().data().bits(); + } else { + // No DMA recv, we must manually recv one byte at a time. + let read_len = read.len(); + + for (i, r) in read.iter_mut().enumerate() { + self.wait_on( + |me| { + let stat = me.info.regs.stat().read(); + + if stat.mstpending().is_pending() { + Poll::Ready(Ok::<(), Error>(())) + } else if stat.mstarbloss().is_arbitration_loss() { + Poll::Ready(Err(TransferError::ArbitrationLoss.into())) + } else if stat.mstststperr().is_error() { + Poll::Ready(Err(TransferError::StartStopError.into())) + } else { + Poll::Pending + } + }, + |me| { + me.info.regs.intenset().write(|w| { + w.mstpendingen() + .set_bit() + .mstarblossen() + .set_bit() + .mstststperren() + .set_bit() + }); + }, + ) + .await?; + + // check transmission continuity + if !i2cregs.stat().read().mststate().is_receive_ready() { + return Err(TransferError::ReadFail.into()); + } + + self.check_for_bus_errors()?; + + *r = i2cregs.mstdat().read().data().bits(); - if !dma_read.is_empty() { - let transfer = dma::transfer::Transfer::new_read( + // continue after ACK until last byte + if i < read_len - 1 { + i2cregs.mstctl().write(|w| w.mstcontinue().set_bit()); + } + } + } + Ok(()) + } + + async fn write_no_stop(&mut self, address: u16, write: &[u8]) -> Result<()> { + // Procedure from 24.3.1.1 pg 545 + let i2cregs = self.info.regs; + + self.start(address, false).await?; + + if write.is_empty() { + return Ok(()); + } + + if self.dma_ch.is_some() { + let transfer = dma::transfer::Transfer::new_write( self.dma_ch.as_mut().unwrap(), + write, i2cregs.mstdat().as_ptr() as *mut u8, - dma_read, Default::default(), ); @@ -541,125 +709,72 @@ impl<'a> I2cMaster<'a, Async> { if let Either::Second(e) = res { e?; } - } - - self.wait_on( - |me| { - let stat = me.info.regs.stat().read(); - - if stat.mstpending().is_pending() { - Poll::Ready(Ok::<(), Error>(())) - } else if stat.mstarbloss().is_arbitration_loss() { - Poll::Ready(Err(TransferError::ArbitrationLoss.into())) - } else if stat.mstststperr().is_error() { - Poll::Ready(Err(TransferError::StartStopError.into())) - } else { - Poll::Pending - } - }, - |me| { - me.info.regs.intenset().write(|w| { - w.mstpendingen() - .set_bit() - .mstarblossen() - .set_bit() - .mstststperren() - .set_bit() - }); - }, - ) - .await?; - // Read the last byte - last_byte[0] = i2cregs.mstdat().read().data().bits(); + self.wait_on( + |me| { + let stat = me.info.regs.stat().read(); - Ok(()) - } - - async fn write_no_stop(&mut self, address: u16, write: &[u8]) -> Result<()> { - // Procedure from 24.3.1.1 pg 545 - let i2cregs = self.info.regs; - - self.start(address, false).await?; - - if write.is_empty() { - return Ok(()); - } - - let transfer = dma::transfer::Transfer::new_write( - self.dma_ch.as_mut().unwrap(), - write, - i2cregs.mstdat().as_ptr() as *mut u8, - Default::default(), - ); - - // According to sections 24.7.7.1 and 24.7.7.2, we should - // first program the DMA channel for carrying out a transfer - // and only then set MSTDMA bit. - // - // Additionally, at this point we know the slave has - // acknowledged the address. - i2cregs.mstctl().write(|w| w.mstdma().enabled()); - - let res = select( - transfer, - poll_fn(|cx| { - I2C_WAKERS[self.info.index].register(cx.waker()); - - i2cregs.intenset().write(|w| { - w.mstpendingen() - .set_bit() - .mstarblossen() - .set_bit() - .mstststperren() - .set_bit() - }); - - let stat = i2cregs.stat().read(); - - if stat.mstarbloss().is_arbitration_loss() { - Poll::Ready(Err::<(), Error>(TransferError::ArbitrationLoss.into())) - } else if stat.mstststperr().is_error() { - Poll::Ready(Err::<(), Error>(TransferError::StartStopError.into())) - } else { - Poll::Pending - } - }), - ) - .await; + if stat.mstpending().is_pending() { + Poll::Ready(Ok::<(), Error>(())) + } else if stat.mstarbloss().is_arbitration_loss() { + Poll::Ready(Err(TransferError::ArbitrationLoss.into())) + } else if stat.mstststperr().is_error() { + Poll::Ready(Err(TransferError::StartStopError.into())) + } else { + Poll::Pending + } + }, + |me| { + me.info.regs.intenset().write(|w| { + w.mstpendingen() + .set_bit() + .mstarblossen() + .set_bit() + .mstststperren() + .set_bit() + }); + }, + ) + .await + } else { + for byte in write.iter() { + i2cregs.mstdat().write(|w| + // SAFETY: unsafe only due to .bits usage + unsafe { w.data().bits(*byte) }); - i2cregs.mstctl().write(|w| w.mstdma().disabled()); + i2cregs.mstctl().write(|w| w.mstcontinue().set_bit()); - if let Either::Second(e) = res { - e?; + self.wait_on( + |me| { + let stat = me.info.regs.stat().read(); + + if stat.mstpending().is_pending() { + Poll::Ready(Ok::<(), Error>(())) + } else if stat.mstarbloss().is_arbitration_loss() { + Poll::Ready(Err(TransferError::ArbitrationLoss.into())) + } else if stat.mstststperr().is_error() { + Poll::Ready(Err(TransferError::StartStopError.into())) + } else { + Poll::Pending + } + }, + |me| { + me.info.regs.intenset().write(|w| { + w.mstpendingen() + .set_bit() + .mstarblossen() + .set_bit() + .mstststperren() + .set_bit() + }); + }, + ) + .await?; + + self.check_for_bus_errors()?; + } + Ok(()) } - - self.wait_on( - |me| { - let stat = me.info.regs.stat().read(); - - if stat.mstpending().is_pending() { - Poll::Ready(Ok::<(), Error>(())) - } else if stat.mstarbloss().is_arbitration_loss() { - Poll::Ready(Err(TransferError::ArbitrationLoss.into())) - } else if stat.mstststperr().is_error() { - Poll::Ready(Err(TransferError::StartStopError.into())) - } else { - Poll::Pending - } - }, - |me| { - me.info.regs.intenset().write(|w| { - w.mstpendingen() - .set_bit() - .mstarblossen() - .set_bit() - .mstststperren() - .set_bit() - }); - }, - ) - .await } async fn stop(&mut self) -> Result<()> {