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;
}