You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2023/08/11 16:42:00 UTC

[nuttx] 05/08: s32k3xx:lpi2c fix status handeling & race

This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 2dc6365e24bc85e7a12eca1b2d9bf2f5bbcf44d9
Author: David Sidrane <Da...@NscDg.com>
AuthorDate: Wed May 17 07:57:36 2023 -0700

    s32k3xx:lpi2c fix status handeling & race
    
    s32k3:lpi2c:Simplify DMA and Non DMA usage
    
    s32k3xx: lpi2c dma invalidate cache after exchange
    
    s32k3xx:lpi2c fix timeout not stopping dma
---
 arch/arm/src/s32k3xx/s32k3xx_lpi2c.c | 689 +++++++++++++++++++----------------
 1 file changed, 384 insertions(+), 305 deletions(-)

diff --git a/arch/arm/src/s32k3xx/s32k3xx_lpi2c.c b/arch/arm/src/s32k3xx/s32k3xx_lpi2c.c
index 6881db08df..7dedd48a1a 100644
--- a/arch/arm/src/s32k3xx/s32k3xx_lpi2c.c
+++ b/arch/arm/src/s32k3xx/s32k3xx_lpi2c.c
@@ -18,7 +18,7 @@
  *
  ****************************************************************************/
 
-/* Copyright 2022 NXP */
+/* Copyright 2022, 2023 NXP */
 
 /****************************************************************************
  * Included Files
@@ -124,6 +124,8 @@
 #define MKI2C_INPUT(p) (((p) & (_PIN_PORT_MASK | _PIN_MASK)) | \
                          GPIO_INPUT)
 
+#define LPI2C_MSR_LIMITED_ERROR_MASK (LPI2C_MSR_ERROR_MASK & ~(LPI2C_MSR_FEF))
+
 /****************************************************************************
  * Private Types
  ****************************************************************************/
@@ -244,7 +246,7 @@ static uint32_t s32k3xx_lpi2c_toticks(int msgc, struct i2c_msg_s *msgs);
 #endif /* CONFIG_S32K3XX_I2C_DYNTIMEO */
 
 static inline int
-s32k3xx_lpi2c_sem_waitdone(struct s32k3xx_lpi2c_priv_s *priv);
+  s32k3xx_lpi2c_sem_waitdone(struct s32k3xx_lpi2c_priv_s *priv);
 
 #ifdef CONFIG_I2C_TRACE
 static void s32k3xx_lpi2c_tracereset(struct s32k3xx_lpi2c_priv_s *priv);
@@ -501,40 +503,39 @@ s32k3xx_lpi2c_sem_waitdone(struct s32k3xx_lpi2c_priv_s *priv)
   if (priv->rxdma == NULL && priv->txdma == NULL)
     {
 #endif
-  /* Enable Interrupts when master mode */
+      /* Clear the TX and RX FIFOs */
 
-  if (priv->config->mode == LPI2C_MASTER)
-    {
-      if ((priv->flags & I2C_M_READ) != 0)
-        {
-          regval = LPI2C_MIER_TDIE | LPI2C_MIER_RDIE | LPI2C_MIER_NDIE |
-                   LPI2C_MIER_ALIE | LPI2C_MIER_SDIE;
-          s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MIER_OFFSET, regval);
-        }
-      else
+      s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MCR_OFFSET, 0,
+                              LPI2C_MCR_RTF | LPI2C_MCR_RRF);
+
+      /* Enable Interrupts when master mode */
+
+      if (priv->config->mode == LPI2C_MASTER)
         {
-          regval = LPI2C_MIER_TDIE | LPI2C_MIER_NDIE | \
-                   LPI2C_MIER_ALIE | LPI2C_MIER_SDIE;
-          s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MIER_OFFSET, regval);
+          if ((priv->flags & I2C_M_READ) != 0)
+            {
+              regval = LPI2C_MIER_TDIE | LPI2C_MIER_RDIE |
+                       LPI2C_MIER_NDIE | LPI2C_MIER_ALIE | LPI2C_MIER_SDIE;
+              s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MIER_OFFSET, regval);
+            }
+          else
+            {
+              regval = LPI2C_MIER_TDIE | LPI2C_MIER_NDIE | \
+                       LPI2C_MIER_ALIE | LPI2C_MIER_SDIE;
+              s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MIER_OFFSET, regval);
+            }
         }
-    }
 
   /* Enable Interrupts when slave mode */
 
-  else
-    {
+      else
+        {
 #warning Missing logic for I2C Slave mode
-    }
-
-  /* Signal the interrupt handler that we are waiting.  NOTE:  Interrupts
-   * are currently disabled but will be temporarily re-enabled below when
-   * nxsem_tickwait_uninterruptible() sleeps.
-   */
+        }
 #ifdef CONFIG_S32K3XX_LPI2C_DMA
     }
-
 #endif
-  priv->intstate = INTSTATE_WAITING;
+
   do
     {
       /* Wait until either the transfer is complete or the timeout expires */
@@ -567,15 +568,10 @@ s32k3xx_lpi2c_sem_waitdone(struct s32k3xx_lpi2c_priv_s *priv)
 
   /* Disable I2C interrupts */
 
-  /* Enable Interrupts when master mode */
-
   if (priv->config->mode == LPI2C_MASTER)
     {
       s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MIER_OFFSET, 0);
     }
-
-  /* Enable Interrupts when slave mode */
-
   else
     {
       s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_SIER_OFFSET, 0);
@@ -600,13 +596,6 @@ s32k3xx_lpi2c_sem_waitdone(struct s32k3xx_lpi2c_priv_s *priv)
 #else
   timeout = CONFIG_S32K3XX_I2CTIMEOTICKS;
 #endif
-
-  /* Signal the interrupt handler that we are waiting.  NOTE:  Interrupts
-   * are currently disabled but will be temporarily re-enabled below when
-   * nxsem_tickwait_uninterruptible() sleeps.
-   */
-
-  priv->intstate = INTSTATE_WAITING;
   start = clock_systime_ticks();
 
   do
@@ -638,67 +627,103 @@ s32k3xx_lpi2c_sem_waitdone(struct s32k3xx_lpi2c_priv_s *priv)
 #endif
 
 /****************************************************************************
- * Name: s32k3xx_rxdma_callback
+ * Name: s32k3xx_lpi2c_sem_waitstop
  *
  * Description:
- *   This function performs the next I2C operation
+ *   Wait for a STOP to complete
  *
  ****************************************************************************/
-#ifdef CONFIG_S32K3XX_LPI2C_DMA
-static void s32k3xx_rxdma_callback(DMACH_HANDLE handle, void *arg, bool done,
-                              int result)
+
+static inline void
+s32k3xx_lpi2c_sem_waitstop(struct s32k3xx_lpi2c_priv_s *priv)
 {
-  struct s32k3xx_lpi2c_priv_s *priv = (struct s32k3xx_lpi2c_priv_s *)arg;
+  clock_t start;
+  clock_t elapsed;
+  clock_t timeout;
+  uint32_t regval;
 
-  s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET, 0,
-                              LPI2C_MIER_SDIE);
+  /* Select a timeout */
 
-  if (result != OK)
-    {
-      priv->status = s32k3xx_lpi2c_getstatus(priv);
+#ifdef CONFIG_S32K3XX_I2C_DYNTIMEO
+  timeout = USEC2TICK(CONFIG_S32K3XX_I2C_DYNTIMEO_STARTSTOP);
+#else
+  timeout = CONFIG_S32K3XX_I2CTIMEOTICKS;
+#endif
 
-      if ((priv->status & LPI2C_MSR_ERROR_MASK) != 0)
-        {
-          i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", priv->status);
+  /* Wait as stop might still be in progress; but stop might also
+   * be set because of a timeout error: "The [STOP] bit is set and
+   * cleared by software, cleared by hardware when a Stop condition is
+   * detected, set by hardware when a timeout error is detected."
+   */
 
-          s32k3xx_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0);
+  start = clock_systime_ticks();
+  do
+    {
+      /* Calculate the elapsed time */
 
-          /* Clear the TX and RX FIFOs */
+      elapsed = clock_systime_ticks() - start;
 
-          s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MCR_OFFSET, 0,
-                                LPI2C_MCR_RTF | LPI2C_MCR_RRF);
+      /* Check for STOP condition */
 
-          /* Clear the error */
+      if (priv->config->mode == LPI2C_MASTER)
+        {
+          regval = s32k3xx_lpi2c_getreg(priv, S32K3XX_LPI2C_MSR_OFFSET);
+          if ((regval & LPI2C_MSR_SDF) == LPI2C_MSR_SDF)
+            {
+              return;
+            }
+        }
 
-          s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET,
-                             (priv->status & (LPI2C_MSR_NDF |
-                                              LPI2C_MSR_ALF |
-                                              LPI2C_MSR_FEF |
-                                              LPI2C_MSR_PLTF)));
+      /* Enable Interrupts when slave mode */
 
-          if (priv->intstate == INTSTATE_WAITING)
+      else
+        {
+          regval = s32k3xx_lpi2c_getreg(priv, S32K3XX_LPI2C_SSR_OFFSET);
+          if ((regval & LPI2C_SSR_SDF) == LPI2C_SSR_SDF)
             {
-              /* inform the thread that transfer is complete
-               * and wake it up
-               */
+              return;
+            }
+        }
 
-              priv->intstate = INTSTATE_DONE;
-              nxsem_post(&priv->sem_isr);
+      /* Check for NACK error */
+
+      if (priv->config->mode == LPI2C_MASTER)
+        {
+          regval = s32k3xx_lpi2c_getreg(priv, S32K3XX_LPI2C_MSR_OFFSET);
+          if ((regval & LPI2C_MSR_NDF) == LPI2C_MSR_NDF)
+            {
+              return;
             }
         }
+
+      /* Enable Interrupts when slave mode */
+
+      else
+        {
+#warning Missing logic for I2C Slave
+        }
     }
+
+  /* Loop until the stop is complete or a timeout occurs. */
+
+  while (elapsed < timeout);
+
+  /* If we get here then a timeout occurred with the STOP condition
+   * still pending.
+   */
+
+  i2cinfo("Timeout with Status Register: %" PRIx32 "\n", regval);
 }
-#endif
 
 /****************************************************************************
- * Name: s32k3xx_txdma_callback
+ * Name: s32k3xx_rxdma_callback
  *
  * Description:
  *   This function performs the next I2C operation
  *
  ****************************************************************************/
 #ifdef CONFIG_S32K3XX_LPI2C_DMA
-static void s32k3xx_txdma_callback(DMACH_HANDLE handle, void *arg, bool done,
+static void s32k3xx_rxdma_callback(DMACH_HANDLE handle, void *arg, bool done,
                               int result)
 {
   struct s32k3xx_lpi2c_priv_s *priv = (struct s32k3xx_lpi2c_priv_s *)arg;
@@ -706,38 +731,61 @@ static void s32k3xx_txdma_callback(DMACH_HANDLE handle, void *arg, bool done,
   s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET, 0,
                               LPI2C_MIER_SDIE);
 
-  if (result != OK)
+  if (result == OK)
     {
-      priv->status = s32k3xx_lpi2c_getstatus(priv);
+      if ((priv->flags & I2C_M_NOSTOP) == 0)
+        {
+          s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STOP, 0);
+          s32k3xx_lpi2c_sendstop(priv);
+        }
+    }
+  else
+    {
+      uint32_t status = s32k3xx_lpi2c_getstatus(priv);
 
-      if ((priv->status & LPI2C_MSR_ERROR_MASK) != 0)
+      if ((status & LPI2C_MSR_ERROR_MASK) != 0)
         {
-          i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", priv->status);
+          i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", status);
 
           s32k3xx_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0);
+        }
+    }
+}
+#endif
 
-          /* Clear the TX and RX FIFOs */
-
-          s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MCR_OFFSET, 0,
-                                LPI2C_MCR_RTF | LPI2C_MCR_RRF);
+/****************************************************************************
+ * Name: s32k3xx_txdma_callback
+ *
+ * Description:
+ *   This function performs the next I2C operation
+ *
+ ****************************************************************************/
+#ifdef CONFIG_S32K3XX_LPI2C_DMA
+static void s32k3xx_txdma_callback(DMACH_HANDLE handle, void *arg, bool done,
+                              int result)
+{
+  struct s32k3xx_lpi2c_priv_s *priv = (struct s32k3xx_lpi2c_priv_s *)arg;
 
-          /* Clear the error */
+  s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET, 0,
+                          LPI2C_MIER_SDIE);
 
-          s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET,
-                             (priv->status & (LPI2C_MSR_NDF |
-                                              LPI2C_MSR_ALF |
-                                              LPI2C_MSR_FEF |
-                                              LPI2C_MSR_PLTF)));
+  if (result == OK)
+    {
+      if ((priv->flags & I2C_M_NOSTOP) == 0)
+        {
+          s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STOP, 0);
+          s32k3xx_lpi2c_sendstop(priv);
+        }
+    }
+  else
+    {
+      uint32_t status = s32k3xx_lpi2c_getstatus(priv);
 
-          if (priv->intstate == INTSTATE_WAITING)
-            {
-              /* inform the thread that transfer is complete
-               * and wake it up
-               */
+      if ((status & LPI2C_MSR_ERROR_MASK) != 0)
+        {
+          i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", status);
 
-              priv->intstate = INTSTATE_DONE;
-              nxsem_post(&priv->sem_isr);
-            }
+          s32k3xx_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0);
         }
     }
 }
@@ -1042,10 +1090,10 @@ static inline void
 
   /* Generate START condition and send the address */
 
-  /* Turn off auto_stop option */
+  /* Disable AUTOSTOP and NAK Ignore */
 
   s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MCFGR1_OFFSET,
-                        LPI2C_MCFGR1_IGNACK, 0);
+                        LPI2C_MCFGR1_IGNACK | LPI2C_MCFGR1_AUTOSTOP, 0);
 
   do
     {
@@ -1138,6 +1186,17 @@ static int s32k3xx_lpi2c_isr_process(struct s32k3xx_lpi2c_priv_s *priv)
 
   if (priv->rxdma != NULL || priv->txdma != NULL)
     {
+      /* Is there an Error condition */
+
+      if (current_status & LPI2C_MSR_LIMITED_ERROR_MASK)
+        {
+          s32k3xx_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0);
+
+          /* Return the full error status */
+
+          priv->status = current_status;
+        }
+
       /* End of packet or Stop */
 
       if ((status & (LPI2C_MSR_SDF | LPI2C_MSR_EPF)) != 0)
@@ -1149,270 +1208,246 @@ static int s32k3xx_lpi2c_isr_process(struct s32k3xx_lpi2c_priv_s *priv)
           s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET, status &
                                                            (LPI2C_MSR_SDF |
                                                            LPI2C_MSR_EPF));
-        }
 
-      /* Is there an Error condition */
-
-      if (current_status & LPI2C_MSR_ERROR_MASK)
-        {
-          s32k3xx_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0);
+          /* Mark that this transaction stopped */
 
-          /* Shutdown DMA */
+          priv->msgv = NULL;
+          priv->msgc = 0;
+          priv->dcnt = -1;
 
-          if (priv->rxdma != NULL)
+          if (priv->intstate == INTSTATE_WAITING)
             {
+              /* inform the thread that transfer is complete
+               * and wake it up
+               */
+
+              s32k3xx_dmach_stop(priv->txdma);
               s32k3xx_dmach_stop(priv->rxdma);
-            }
 
-          if (priv->txdma != NULL)
-            {
-               s32k3xx_dmach_stop(priv->txdma);
+              priv->intstate = INTSTATE_DONE;
+              nxsem_post(&priv->sem_isr);
             }
-
-          /* Clear the TX and RX FIFOs */
-
-          s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MCR_OFFSET, 0,
-                                LPI2C_MCR_RTF | LPI2C_MCR_RRF);
-
-          /* Clear the error */
-
-          s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET,
-                            (current_status & (LPI2C_MSR_NDF |
-                                               LPI2C_MSR_ALF |
-                                               LPI2C_MSR_FEF)));
-
-          /* Return the full error status */
-
-          status = current_status;
         }
 
-      /* Mark that this transaction stopped */
-
-      priv->msgv = NULL;
-      priv->msgc = 0;
-      priv->dcnt = -1;
-
-      if (priv->intstate == INTSTATE_WAITING)
-        {
-          /* Update Status once at the end */
-
-          priv->status = status;
-
-          /* inform the thread that transfer is complete
-           * and wake it up
-           */
-
-          priv->intstate = INTSTATE_DONE;
-          nxsem_post(&priv->sem_isr);
-        }
+      /* Clear the error */
 
+      s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET,
+                        (current_status & (LPI2C_MSR_NDF |
+                                           LPI2C_MSR_ALF |
+                                           LPI2C_MSR_FEF)));
       return OK;
     }
 
 #endif
-
   /* Check for new trace setup */
 
   s32k3xx_lpi2c_tracenew(priv, status);
 
-  /* After an error we can get a STOP Detect Flag  */
-
-  if (priv->intstate == INTSTATE_DONE && (status & LPI2C_MSR_SDF) != 0)
+  if ((status & LPI2C_MSR_LIMITED_ERROR_MASK) == 0)
     {
-      s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STOP, 0);
-      s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET, LPI2C_MSR_SDF);
-    }
+      /* Check if there is more bytes to send */
 
-  /* Check if there is more bytes to send */
-
-  else if (((priv->flags & I2C_M_READ) == 0) &&
-           (status & LPI2C_MSR_TDF) != 0)
-    {
-      if (priv->dcnt > 0)
+      if (((priv->flags & I2C_M_READ) == 0) &&
+               (status & LPI2C_MSR_TDF) != 0)
         {
-          s32k3xx_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt);
-          s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MTDR_OFFSET,
-                             LPI2C_MTDR_CMD_TXD |
-                             LPI2C_MTDR_DATA(*priv->ptr++));
-          priv->dcnt--;
-
-          if ((priv->msgc <= 0) && (priv->dcnt == 0))
+          if (priv->dcnt > 0)
             {
-              s32k3xx_lpi2c_sendstop(priv);
+              s32k3xx_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt);
+              s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MTDR_OFFSET,
+                                 LPI2C_MTDR_CMD_TXD |
+                                 LPI2C_MTDR_DATA(*priv->ptr++));
+              priv->dcnt--;
+
+              /* Last byte of last message? */
+
+              if ((priv->msgc <= 0) && (priv->dcnt == 0))
+                {
+                  if ((priv->flags & I2C_M_NOSTOP) == 0)
+                    {
+                      s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STOP, 0);
+
+                      /* Do this once */
+
+                      priv->flags |= I2C_M_NOSTOP;
+                      s32k3xx_lpi2c_sendstop(priv);
+                    }
+                }
             }
         }
-    }
 
-  /* Check if there is more bytes to read */
+      /* Check if there is more bytes to read */
 
-  else if (((priv->flags & I2C_M_READ) != 0) &&
-           (status & LPI2C_MSR_RDF) != 0)
-    {
-      /* Read a byte, if dcnt goes < 0, then read dummy bytes to ack ISRs */
-
-      if (priv->dcnt > 0)
+      else if (((priv->flags & I2C_M_READ) != 0) &&
+               (status & LPI2C_MSR_RDF) != 0)
         {
-          s32k3xx_lpi2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt);
+          /* Read a byte, if dcnt goes < 0, read dummy bytes to ack ISRs */
 
-          /* No interrupts or context switches should occur in the following
-           * sequence. Otherwise, additional bytes may be sent by the device.
-           */
+          if (priv->dcnt > 0)
+            {
+              s32k3xx_lpi2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt);
 
-#ifdef CONFIG_I2C_POLLED
-          irqstate_t flags = enter_critical_section();
-#endif
+              /* No interrupts or context switches should occur in the
+               * following sequence. Otherwise, additional bytes may be
+               * sent by the device.
+               */
 
-          /* Receive a byte */
+    #ifdef CONFIG_I2C_POLLED
+              irqstate_t flags = enter_critical_section();
+    #endif
 
-          *priv->ptr++ =
-            s32k3xx_lpi2c_getreg(priv, S32K3XX_LPI2C_MRDR_OFFSET) &
-            LPI2C_MRDR_DATA_MASK;
+              /* Receive a byte */
 
-          priv->dcnt--;
+              *priv->ptr++ = s32k3xx_lpi2c_getreg(priv,
+                                                S32K3XX_LPI2C_MRDR_OFFSET) &
+                                                LPI2C_MRDR_DATA_MASK;
+              priv->dcnt--;
 
-#ifdef CONFIG_I2C_POLLED
-          leave_critical_section(flags);
-#endif
-          if ((priv->msgc <= 0) && (priv->dcnt == 0))
-            {
-              s32k3xx_lpi2c_sendstop(priv);
-            }
-        }
-      else
-        {
-          s32k3xx_lpi2c_getreg(priv, S32K3XX_LPI2C_MRDR_OFFSET);
-        }
-    }
+    #ifdef CONFIG_I2C_POLLED
+              leave_critical_section(flags);
+    #endif
+              /* Last byte of last message? */
 
-  if (priv->dcnt <= 0)
-    {
-      if (priv->msgc > 0 && priv->msgv != NULL)
-        {
-          priv->ptr   = priv->msgv->buffer;
-          priv->dcnt  = priv->msgv->length;
-          priv->flags = priv->msgv->flags;
+              if ((priv->msgc <= 0) && (priv->dcnt == 0))
+                {
+                  if ((priv->flags & I2C_M_NOSTOP) == 0)
+                    {
+                      s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STOP, 0);
 
-          if ((priv->msgv->flags & I2C_M_NOSTART) == 0)
-            {
-              s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STARTRESTART,
-                                     priv->msgc);
-              s32k3xx_lpi2c_sendstart(priv, priv->msgv->addr);
+                      /* Do this once */
+
+                      priv->flags |= I2C_M_NOSTOP;
+                      s32k3xx_lpi2c_sendstop(priv);
+                    }
+                }
             }
           else
             {
-              s32k3xx_lpi2c_traceevent(priv, I2CEVENT_NOSTART, priv->msgc);
+              /* Read and discard data */
+
+              s32k3xx_lpi2c_getreg(priv, S32K3XX_LPI2C_MRDR_OFFSET);
             }
+        }
 
-          priv->msgv++;
-          priv->msgc--;
+      /* Start the first or next message */
 
-          if ((priv->flags & I2C_M_READ) != 0)
+      if (priv->dcnt <= 0 && (status & (LPI2C_MSR_EPF | LPI2C_MSR_SDF)) == 0)
+        {
+          if (priv->msgc > 0 && priv->msgv != NULL)
             {
-#ifndef CONFIG_I2C_POLLED
-              /* Stop TX interrupt */
+              priv->ptr   = priv->msgv->buffer;
+              priv->dcnt  = priv->msgv->length;
+              priv->flags = priv->msgv->flags;
 
-              s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET,
-                                    LPI2C_MIER_TDIE, LPI2C_MIER_RDIE);
-#endif
-              /* Set LPI2C in read mode */
+              if ((priv->flags & I2C_M_NOSTART) == 0)
+                {
+                  s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STARTRESTART,
+                                         priv->msgc);
 
-              s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MTDR_OFFSET,
-                                 LPI2C_MTDR_CMD_RXD |
-                                 LPI2C_MTDR_DATA((priv->dcnt - 1)));
-            }
-          else
-            {
-              /* Send the first byte from tx buffer */
+                  /* Do this once */
 
-              s32k3xx_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt);
-              s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MTDR_OFFSET,
-                                 LPI2C_MTDR_CMD_TXD |
-                                 LPI2C_MTDR_DATA(*priv->ptr++));
-              priv->dcnt--;
-              if ((priv->msgc <= 0) && (priv->dcnt == 0))
+                  priv->flags |= I2C_M_NOSTART;
+
+                  s32k3xx_lpi2c_sendstart(priv, priv->msgv->addr);
+                }
+              else
                 {
-                  s32k3xx_lpi2c_sendstop(priv);
+                  s32k3xx_lpi2c_traceevent(priv, I2CEVENT_NOSTART,
+                                           priv->msgc);
                 }
-            }
-        }
-      else if (priv->msgv && ((status & LPI2C_MSR_SDF) != 0))
-        {
-          s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STOP, 0);
-          s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET,
-                             LPI2C_MSR_SDF);
 
-          /* Check is there thread waiting for this event (there should be) */
+              priv->msgv++;
+              priv->msgc--;
 
+              if ((priv->flags & I2C_M_READ) != 0)
+                {
 #ifndef CONFIG_I2C_POLLED
-          if (priv->intstate == INTSTATE_WAITING)
-            {
-              /* Update Status once at the end */
+                  /* Stop TX interrupt */
 
-              priv->status = status;
+                  s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET,
+                                        LPI2C_MIER_TDIE, LPI2C_MIER_RDIE);
+#endif
+                  /* Set LPI2C in read mode */
 
-              /* inform the thread that transfer is complete
-               * and wake it up
-               */
+                  s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MTDR_OFFSET,
+                                     LPI2C_MTDR_CMD_RXD |
+                                     LPI2C_MTDR_DATA((priv->dcnt - 1)));
+                }
+              else
+                {
+                  /* Send the first byte from tx buffer */
 
-              priv->intstate = INTSTATE_DONE;
-              nxsem_post(&priv->sem_isr);
-            }
-#else
-          priv->status = status;
-          priv->intstate = INTSTATE_DONE;
-#endif
-          /* Mark that this transaction stopped */
+                  s32k3xx_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE,
+                                         priv->dcnt);
+                  s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MTDR_OFFSET,
+                                     LPI2C_MTDR_CMD_TXD |
+                                     LPI2C_MTDR_DATA(*priv->ptr++));
+                  priv->dcnt--;
 
-          priv->msgv = NULL;
-        }
-#ifndef CONFIG_I2C_POLLED
-      else
-        {
-          s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET,
-                                LPI2C_MIER_TDIE | LPI2C_MIER_RDIE, 0);
-        }
-#endif
-    }
+                  /* Last byte of last message? */
 
-  /* Check for errors */
+                  if ((priv->msgc <= 0) && (priv->dcnt == 0))
+                    {
+                      if ((priv->flags & I2C_M_NOSTOP) == 0)
+                        {
+                          s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STOP, 0);
 
-  if ((status & LPI2C_MSR_EPF) != 0)
-    {
-      s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET, LPI2C_MSR_EPF);
-    }
+                          /* Do this once */
 
-  if ((status & LPI2C_MSR_ERROR_MASK) != 0)
+                          priv->flags |= I2C_M_NOSTOP;
+                          s32k3xx_lpi2c_sendstop(priv);
+                        }
+                    }
+                }
+            }
+        }
+    }
+  else
     {
       s32k3xx_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0);
 
-      /* Clear the TX and RX FIFOs */
+      priv->status = status;
 
-      s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MCR_OFFSET, 0,
-                            LPI2C_MCR_RTF | LPI2C_MCR_RRF);
+      if ((priv->flags & I2C_M_NOSTOP) == 0)
+        {
+          s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STOP, 0);
+
+          /* Do this once */
+
+          priv->flags |= I2C_M_NOSTOP;
+          s32k3xx_lpi2c_sendstop(priv);
+        }
 
       /* Clear the error */
 
       s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET,
-                         (status & (LPI2C_MSR_NDF | LPI2C_MSR_ALF |
-                                    LPI2C_MSR_FEF)));
+                           (status & (LPI2C_MSR_NDF | LPI2C_MSR_ALF |
+                                      LPI2C_MSR_FEF | LPI2C_MSR_EPF)));
+    }
 
-#ifndef CONFIG_I2C_POLLED
-          if (priv->intstate == INTSTATE_WAITING)
-            {
-              /* Update Status once at the end */
+  /* Check for endof packet */
 
-              priv->status = status;
+  if ((status & (LPI2C_MSR_EPF | LPI2C_MSR_SDF)) != 0)
+    {
+      s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET, status &
+                           (LPI2C_MSR_EPF | LPI2C_MSR_SDF));
 
-              /* inform the thread that transfer is complete
-               * and wake it up
-               */
+#ifndef CONFIG_I2C_POLLED
+      if (priv->intstate == INTSTATE_WAITING)
+        {
+          /* inform the thread that transfer is complete
+           * and wake it up
+           */
 
-              priv->intstate = INTSTATE_DONE;
-              nxsem_post(&priv->sem_isr);
-            }
-#else
-          priv->status = status;
           priv->intstate = INTSTATE_DONE;
+
+          s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET,
+                               LPI2C_MIER_TDIE | LPI2C_MIER_RDIE |
+                               LPI2C_MIER_NDIE | LPI2C_MIER_ALIE |
+                               LPI2C_MIER_SDIE | LPI2C_MIER_EPIE, 0);
+          nxsem_post(&priv->sem_isr);
+        }
+#else
+      priv->intstate = INTSTATE_DONE;
 #endif
     }
 
@@ -1612,8 +1647,6 @@ static int s32k3xx_lpi2c_dma_data_configure(struct s32k3xx_lpi2c_priv_s
       config.daddr  = (uint32_t) msg->buffer;
       config.soff   = 0;
       config.doff   = sizeof(msg->buffer[0]);
-      up_invalidate_dcache((uintptr_t)msg->buffer,
-                           (uintptr_t)msg->buffer + msg->length);
     }
   else
     {
@@ -1631,10 +1664,10 @@ static int s32k3xx_lpi2c_dma_data_configure(struct s32k3xx_lpi2c_priv_s
 #endif
 
 /****************************************************************************
- * Name: s32k3xx_lpi2c_configure_dma_transfer
+ * Name: s32k3xx_lpi2c_form_command_list
  *
  * Description:
- *   DMA based I2C transfer function
+ *   Form the DMA command list
  *
  ****************************************************************************/
 
@@ -1709,20 +1742,19 @@ static int s32k3xx_lpi2c_dma_transfer(struct s32k3xx_lpi2c_priv_s *priv)
 
   /* Disable Interrupts */
 
-  s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET,
-                            LPI2C_MIER_RDIE | LPI2C_MIER_TDIE, 0);
+  s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MIER_OFFSET, 0);
 
   /* Disable DMA */
 
   s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MDER_OFFSET, LPI2C_MDER_TDDE |
                                                        LPI2C_MDER_RDDE, 0);
 
-  /* Turn off auto_stop option */
+  /* Enable AUTOSTOP and NAK Ignore */
 
   s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MCFGR1_OFFSET, 0,
                           LPI2C_MCFGR1_IGNACK | LPI2C_MCFGR1_AUTOSTOP);
 
-  /* Form chains of tcd to process the messages */
+  /* Form chains of TCDs to process the messages */
 
   for (m = 0; m < priv->msgc; m++)
     {
@@ -1752,13 +1784,30 @@ static int s32k3xx_lpi2c_dma_transfer(struct s32k3xx_lpi2c_priv_s *priv)
         }
     }
 
+  /* Clear the TX and RX FIFOs */
+
+  s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MCR_OFFSET, 0,
+                          LPI2C_MCR_RTF | LPI2C_MCR_RRF);
+
+  /* Reset the Error bits */
+
+  s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET, LPI2C_MSR_NDF |
+                                                       LPI2C_MSR_ALF |
+                                                       LPI2C_MSR_FEF);
+
+  /* Enable the Iterrupts */
+
   s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MIER_OFFSET,
                      LPI2C_MIER_NDIE | LPI2C_MIER_ALIE |
-                     LPI2C_MIER_PLTIE | LPI2C_MIER_FEIE);
+                     LPI2C_MIER_PLTIE);
+
+  /* Start The DMA */
 
   s32k3xx_dmach_start(priv->rxdma, s32k3xx_rxdma_callback, (void *)priv);
   s32k3xx_dmach_start(priv->txdma, s32k3xx_txdma_callback, (void *)priv);
 
+  /* Enable the DMA Request */
+
   s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MDER_OFFSET, 0,
                           LPI2C_MDER_TDDE | LPI2C_MDER_RDDE);
   return OK;
@@ -1778,6 +1827,9 @@ static int s32k3xx_lpi2c_transfer(struct i2c_master_s *dev,
 {
   struct s32k3xx_lpi2c_priv_s *priv = (struct s32k3xx_lpi2c_priv_s *)dev;
   int ret;
+#ifdef CONFIG_S32K3XX_LPI2C_DMA
+  int m;
+#endif
 
   DEBUGASSERT(count > 0);
 
@@ -1818,6 +1870,13 @@ static int s32k3xx_lpi2c_transfer(struct i2c_master_s *dev,
 
   priv->status = 0;
 
+  /* Signal the interrupt handler that we are waiting.  NOTE:  Interrupts
+   * are currently disabled but will be temporarily re-enabled below when
+   * nxsem_tickwait_uninterruptible() sleeps.
+   */
+
+  priv->intstate = INTSTATE_WAITING;
+
   /* Wait for an ISR, if there was a timeout, fetch latest status to get
    * the BUSY flag.
    */
@@ -1888,6 +1947,20 @@ static int s32k3xx_lpi2c_transfer(struct i2c_master_s *dev,
   priv->dcnt = 0;
   priv->ptr = NULL;
 
+#ifdef CONFIG_S32K3XX_LPI2C_DMA
+  if (priv->rxdma)
+    {
+      for (m = 0; m < count; m++)
+        {
+          if (msgs[m].flags & I2C_M_READ)
+            {
+            up_invalidate_dcache((uintptr_t)msgs[m].buffer,
+                                (uintptr_t)msgs[m].buffer + msgs[m].length);
+            }
+        }
+    }
+#endif
+
   nxmutex_unlock(&priv->lock);
   return ret;
 }
@@ -2047,7 +2120,8 @@ out:
 
 struct i2c_master_s *s32k3xx_i2cbus_initialize(int port)
 {
-  struct s32k3xx_lpi2c_priv_s *priv = NULL;
+  struct s32k3xx_lpi2c_priv_s * priv = NULL;
+  irqstate_t flags;
 
   /* Get I2C private structure */
 
@@ -2073,8 +2147,9 @@ struct i2c_master_s *s32k3xx_i2cbus_initialize(int port)
    * power-up hardware and configure GPIOs.
    */
 
-  nxmutex_lock(&priv->lock);
-  if (priv->refs++ == 0)
+  flags = enter_critical_section();
+
+  if ((volatile int)priv->refs++ == 0)
     {
       s32k3xx_lpi2c_init(priv);
 
@@ -2095,7 +2170,8 @@ struct i2c_master_s *s32k3xx_i2cbus_initialize(int port)
 #endif
     }
 
-  nxmutex_unlock(&priv->lock);
+  leave_critical_section(flags);
+
   return (struct i2c_master_s *)priv;
 }
 
@@ -2110,6 +2186,7 @@ struct i2c_master_s *s32k3xx_i2cbus_initialize(int port)
 int s32k3xx_i2cbus_uninitialize(struct i2c_master_s *dev)
 {
   struct s32k3xx_lpi2c_priv_s *priv = (struct s32k3xx_lpi2c_priv_s *)dev;
+  irqstate_t flags;
 
   DEBUGASSERT(dev);
 
@@ -2120,13 +2197,16 @@ int s32k3xx_i2cbus_uninitialize(struct i2c_master_s *dev)
       return ERROR;
     }
 
-  nxmutex_lock(&priv->lock);
+  flags = enter_critical_section();
+
   if (--priv->refs > 0)
     {
-      nxmutex_unlock(&priv->lock);
+      leave_critical_section(flags);
       return OK;
     }
 
+  leave_critical_section(flags);
+
   /* Disable power and other HW resource (GPIO's) */
 
 #ifdef CONFIG_S32K3XX_LPI2C_DMA
@@ -2146,7 +2226,6 @@ int s32k3xx_i2cbus_uninitialize(struct i2c_master_s *dev)
 #endif
 
   s32k3xx_lpi2c_deinit(priv);
-  nxmutex_unlock(&priv->lock);
 
   return OK;
 }