From 2dc6365e24bc85e7a12eca1b2d9bf2f5bbcf44d9 Mon Sep 17 00:00:00 2001 From: David Sidrane Date: Wed, 17 May 2023 07:57:36 -0700 Subject: [PATCH] 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 | 723 +++++++++++++++------------ 1 file changed, 401 insertions(+), 322 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) + 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_RDIE | 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); + } } - 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 @@ -637,6 +626,95 @@ s32k3xx_lpi2c_sem_waitdone(struct s32k3xx_lpi2c_priv_s *priv) } #endif +/**************************************************************************** + * Name: s32k3xx_lpi2c_sem_waitstop + * + * Description: + * Wait for a STOP to complete + * + ****************************************************************************/ + +static inline void +s32k3xx_lpi2c_sem_waitstop(struct s32k3xx_lpi2c_priv_s *priv) +{ + clock_t start; + clock_t elapsed; + clock_t timeout; + uint32_t regval; + + /* Select a timeout */ + +#ifdef CONFIG_S32K3XX_I2C_DYNTIMEO + timeout = USEC2TICK(CONFIG_S32K3XX_I2C_DYNTIMEO_STARTSTOP); +#else + timeout = CONFIG_S32K3XX_I2CTIMEOTICKS; +#endif + + /* 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." + */ + + start = clock_systime_ticks(); + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + + /* Check for STOP condition */ + + if (priv->config->mode == LPI2C_MASTER) + { + regval = s32k3xx_lpi2c_getreg(priv, S32K3XX_LPI2C_MSR_OFFSET); + if ((regval & LPI2C_MSR_SDF) == LPI2C_MSR_SDF) + { + return; + } + } + + /* Enable Interrupts when slave mode */ + + else + { + regval = s32k3xx_lpi2c_getreg(priv, S32K3XX_LPI2C_SSR_OFFSET); + if ((regval & LPI2C_SSR_SDF) == LPI2C_SSR_SDF) + { + return; + } + } + + /* 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); +} + /**************************************************************************** * Name: s32k3xx_rxdma_callback * @@ -653,38 +731,23 @@ static void s32k3xx_rxdma_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->status & LPI2C_MSR_ERROR_MASK) != 0) + if ((priv->flags & I2C_M_NOSTOP) == 0) { - i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", priv->status); + s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + s32k3xx_lpi2c_sendstop(priv); + } + } + else + { + uint32_t status = s32k3xx_lpi2c_getstatus(priv); + + if ((status & LPI2C_MSR_ERROR_MASK) != 0) + { + i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", status); s32k3xx_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); - - /* 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, - (priv->status & (LPI2C_MSR_NDF | - LPI2C_MSR_ALF | - LPI2C_MSR_FEF | - LPI2C_MSR_PLTF))); - - 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); - } } } } @@ -704,40 +767,25 @@ static void s32k3xx_txdma_callback(DMACH_HANDLE handle, void *arg, bool done, struct s32k3xx_lpi2c_priv_s *priv = (struct s32k3xx_lpi2c_priv_s *)arg; s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET, 0, - LPI2C_MIER_SDIE); + LPI2C_MIER_SDIE); - if (result != OK) + if (result == OK) { - priv->status = s32k3xx_lpi2c_getstatus(priv); - - if ((priv->status & LPI2C_MSR_ERROR_MASK) != 0) + if ((priv->flags & I2C_M_NOSTOP) == 0) { - i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", priv->status); + s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + s32k3xx_lpi2c_sendstop(priv); + } + } + else + { + uint32_t status = s32k3xx_lpi2c_getstatus(priv); + + if ((status & LPI2C_MSR_ERROR_MASK) != 0) + { + i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", status); s32k3xx_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); - - /* 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, - (priv->status & (LPI2C_MSR_NDF | - LPI2C_MSR_ALF | - LPI2C_MSR_FEF | - LPI2C_MSR_PLTF))); - - 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); - } } } } @@ -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 */ + /* Mark that this transaction stopped */ - if (current_status & LPI2C_MSR_ERROR_MASK) - { - s32k3xx_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); + priv->msgv = NULL; + priv->msgc = 0; + priv->dcnt = -1; - /* Shutdown DMA */ - - 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); + + priv->intstate = INTSTATE_DONE; + nxsem_post(&priv->sem_isr); } - - if (priv->txdma != NULL) - { - s32k3xx_dmach_stop(priv->txdma); - } - - /* 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); - } - } - } - - /* 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) - { - s32k3xx_lpi2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt); - - /* No interrupts or context switches should occur in the following - * sequence. Otherwise, additional bytes may be sent by the device. - */ - -#ifdef CONFIG_I2C_POLLED - irqstate_t flags = enter_critical_section(); -#endif - - /* Receive a byte */ - - *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); - } - } - - 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->msgv->flags & I2C_M_NOSTART) == 0) - { - s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STARTRESTART, - priv->msgc); - s32k3xx_lpi2c_sendstart(priv, priv->msgv->addr); - } - else - { - s32k3xx_lpi2c_traceevent(priv, I2CEVENT_NOSTART, priv->msgc); - } - - priv->msgv++; - priv->msgc--; - - if ((priv->flags & I2C_M_READ) != 0) - { -#ifndef CONFIG_I2C_POLLED - /* Stop TX interrupt */ - - s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET, - LPI2C_MIER_TDIE, LPI2C_MIER_RDIE); -#endif - /* Set LPI2C in read mode */ - - 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 */ - 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)) { - s32k3xx_lpi2c_sendstop(priv); + 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); + } } } } - else if (priv->msgv && ((status & LPI2C_MSR_SDF) != 0)) + + /* Check if there is more bytes to read */ + + else if (((priv->flags & I2C_M_READ) != 0) && + (status & LPI2C_MSR_RDF) != 0) { - s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); - s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET, - LPI2C_MSR_SDF); + /* Read a byte, if dcnt goes < 0, read dummy bytes to ack ISRs */ - /* Check is there thread waiting for this event (there should be) */ - -#ifndef CONFIG_I2C_POLLED - if (priv->intstate == INTSTATE_WAITING) + if (priv->dcnt > 0) { - /* Update Status once at the end */ + s32k3xx_lpi2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt); - priv->status = status; - - /* inform the thread that transfer is complete - * and wake it up + /* No interrupts or context switches should occur in the + * following sequence. Otherwise, additional bytes may be + * sent by the device. */ - priv->intstate = INTSTATE_DONE; - nxsem_post(&priv->sem_isr); + #ifdef CONFIG_I2C_POLLED + irqstate_t flags = enter_critical_section(); + #endif + + /* Receive a byte */ + + *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 + /* 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); + } + } } -#else - priv->status = status; - priv->intstate = INTSTATE_DONE; -#endif - /* Mark that this transaction stopped */ + else + { + /* Read and discard data */ - priv->msgv = NULL; + s32k3xx_lpi2c_getreg(priv, S32K3XX_LPI2C_MRDR_OFFSET); + } } -#ifndef CONFIG_I2C_POLLED - else + + /* Start the first or next message */ + + if (priv->dcnt <= 0 && (status & (LPI2C_MSR_EPF | LPI2C_MSR_SDF)) == 0) { - s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET, - LPI2C_MIER_TDIE | LPI2C_MIER_RDIE, 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->flags & I2C_M_NOSTART) == 0) + { + s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STARTRESTART, + priv->msgc); + + /* Do this once */ + + priv->flags |= I2C_M_NOSTART; + + s32k3xx_lpi2c_sendstart(priv, priv->msgv->addr); + } + else + { + s32k3xx_lpi2c_traceevent(priv, I2CEVENT_NOSTART, + priv->msgc); + } + + priv->msgv++; + priv->msgc--; + + if ((priv->flags & I2C_M_READ) != 0) + { +#ifndef CONFIG_I2C_POLLED + /* Stop TX interrupt */ + + s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET, + LPI2C_MIER_TDIE, LPI2C_MIER_RDIE); #endif + /* Set LPI2C in read mode */ + + 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 */ + + 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 for errors */ - - if ((status & LPI2C_MSR_EPF) != 0) - { - s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET, LPI2C_MSR_EPF); - } - - if ((status & LPI2C_MSR_ERROR_MASK) != 0) + 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))); + } + + /* Check for endof packet */ + + if ((status & (LPI2C_MSR_EPF | LPI2C_MSR_SDF)) != 0) + { + s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET, status & + (LPI2C_MSR_EPF | LPI2C_MSR_SDF)); #ifndef CONFIG_I2C_POLLED - if (priv->intstate == INTSTATE_WAITING) - { - /* Update Status once at the end */ + if (priv->intstate == INTSTATE_WAITING) + { + /* inform the thread that transfer is complete + * and wake it up + */ - priv->status = status; - - /* 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; }