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
This commit is contained in:
parent
afdce6e8c2
commit
2dc6365e24
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user