arch/arm/{nrf52|nrf53|nrf91}/i2c: fix I2C bus getting stuck during read

During I2C read, one-too-many byte is read, which can lead to the I2C bus
getting stuck. This is likely due to the STOP condition being set at the
wrong time or being missed completely. The chip offers a shortcut, such
that the STOP condition is set automatically after the last byte is being
written/read.

Signed-off-by: Max Kriegleder <max.kriegleder@gmail.com>
This commit is contained in:
Max Kriegleder
2025-11-11 16:45:33 +01:00
committed by Alin Jerpelea
parent 6689a408f3
commit a9ecff5f1c
3 changed files with 123 additions and 354 deletions

View File

@@ -369,47 +369,14 @@ static int nrf52_i2c_transfer(struct i2c_master_s *dev,
regval = priv->dcnt;
nrf52_i2c_putreg(priv, NRF52_TWIM_TXDMAXCNT_OFFSET, regval);
/* Shortcut from LASTTX to STOP */
nrf52_i2c_putreg(priv, NRF52_TWIM_SHORTS_OFFSET,
TWIM_SHORTS_LASTTX_STOP);
/* Start TX sequence */
nrf52_i2c_putreg(priv, NRF52_TWIM_TASKS_STARTTX_OFFSET, 1);
/* Wait for last TX event */
#ifdef CONFIG_I2C_POLLED
while (nrf52_i2c_getreg(priv,
NRF52_TWIM_EVENTS_LASTTX_OFFSET) != 1);
while (1)
{
regval = nrf52_i2c_getreg(priv,
NRF52_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf52_i2c_putreg(priv,
NRF52_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
if (nrf52_i2c_getreg(priv,
NRF52_TWIM_EVENTS_LASTTX_OFFSET) == 1)
{
break;
}
}
/* Clear event */
nrf52_i2c_putreg(priv, NRF52_TWIM_EVENTS_LASTTX_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
{
ret = priv->status;
goto errout;
}
#endif
}
else
{
@@ -424,47 +391,52 @@ static int nrf52_i2c_transfer(struct i2c_master_s *dev,
regval = priv->dcnt;
nrf52_i2c_putreg(priv, NRF52_TWIM_RXDMAXCNT_OFFSET, regval);
/* Shortcuts from LASTRX to STOP */
nrf52_i2c_putreg(priv, NRF52_TWIM_SHORTS_OFFSET,
TWIM_SHORTS_LASTRX_STOP);
/* Start RX sequence */
nrf52_i2c_putreg(priv, NRF52_TWIM_TASKS_STARTRX_OFFSET, 1);
}
/* Wait for last RX done */
/* Wait for stop event */
#ifdef CONFIG_I2C_POLLED
while (1)
{
regval = nrf52_i2c_getreg(priv,
NRF52_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf52_i2c_putreg(priv,
NRF52_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
if (nrf52_i2c_getreg(priv,
NRF52_TWIM_EVENTS_LASTRX_OFFSET) == 1)
{
break;
}
}
/* Clear event */
nrf52_i2c_putreg(priv, NRF52_TWIM_EVENTS_LASTRX_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
while (1)
{
regval = nrf52_i2c_getreg(priv,
NRF52_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
ret = priv->status;
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf52_i2c_putreg(priv,
NRF52_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
#endif
if (nrf52_i2c_getreg(priv,
NRF52_TWIM_EVENTS_STOPPED_OFFSET) == 1)
{
break;
}
}
/* Clear event */
nrf52_i2c_putreg(priv, NRF52_TWIM_EVENTS_STOPPED_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
{
ret = priv->status;
goto errout;
}
#endif
/* Next message */
priv->msgc -= 1;
@@ -472,46 +444,6 @@ static int nrf52_i2c_transfer(struct i2c_master_s *dev,
}
while (priv->msgc > 0);
/* TWIM stop */
nrf52_i2c_putreg(priv, NRF52_TWIM_TASKS_STOP_OFFSET, 1);
/* Wait for stop event */
#ifdef CONFIG_I2C_POLLED
while (1)
{
regval = nrf52_i2c_getreg(priv,
NRF52_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf52_i2c_putreg(priv,
NRF52_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
if (nrf52_i2c_getreg(priv,
NRF52_TWIM_EVENTS_STOPPED_OFFSET) == 1)
{
break;
}
}
/* Clear event */
nrf52_i2c_putreg(priv, NRF52_TWIM_EVENTS_STOPPED_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
{
ret = priv->status;
goto errout;
}
#endif
errout:
#ifndef CONFIG_NRF52_I2C_MASTER_DISABLE_NOSTART
if (pack_buf != NULL && pack_buf != priv->copy_buf)
@@ -569,10 +501,6 @@ static int nrf52_i2c_isr(int irq, void *context, void *arg)
{
i2cinfo("I2C LASTTX\n");
/* TX done */
nxsem_post(&priv->sem_isr);
/* Clear event */
nrf52_i2c_putreg(priv, NRF52_TWIM_EVENTS_LASTTX_OFFSET, 0);
@@ -586,10 +514,6 @@ static int nrf52_i2c_isr(int irq, void *context, void *arg)
{
i2cinfo("I2C LASTRX\n");
/* RX done */
nxsem_post(&priv->sem_isr);
/* Clear event */
nrf52_i2c_putreg(priv, NRF52_TWIM_EVENTS_LASTRX_OFFSET, 0);
@@ -694,8 +618,7 @@ static int nrf52_i2c_init(struct nrf52_i2c_priv_s *priv)
#ifndef CONFIG_I2C_POLLED
/* Enable I2C interrupts */
regval = (TWIM_INT_LASTRX | TWIM_INT_LASTTX | TWIM_INT_STOPPED |
TWIM_INT_ERROR);
regval = (TWIM_INT_STOPPED | TWIM_INT_ERROR);
nrf52_i2c_putreg(priv, NRF52_TWIM_INTEN_OFFSET, regval);
/* Attach error and event interrupts to the ISRs */

View File

@@ -419,47 +419,14 @@ static int nrf53_i2c_transfer(struct i2c_master_s *dev,
regval = priv->dcnt;
nrf53_i2c_putreg(priv, NRF53_TWIM_TXDMAXCNT_OFFSET, regval);
/* Shortcut from LASTTX to STOP */
nrf53_i2c_putreg(priv, NRF53_TWIM_SHORTS_OFFSET,
TWIM_SHORTS_LASTTX_STOP);
/* Start TX sequence */
nrf53_i2c_putreg(priv, NRF53_TWIM_TASKS_STARTTX_OFFSET, 1);
/* Wait for last TX event */
#ifdef CONFIG_I2C_POLLED
while (nrf53_i2c_getreg(priv,
NRF53_TWIM_EVENTS_LASTTX_OFFSET) != 1);
while (1)
{
regval = nrf53_i2c_getreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf53_i2c_putreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
if (nrf53_i2c_getreg(priv,
NRF53_TWIM_EVENTS_LASTTX_OFFSET) == 1)
{
break;
}
}
/* Clear event */
nrf53_i2c_putreg(priv, NRF53_TWIM_EVENTS_LASTTX_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
{
ret = priv->status;
goto errout;
}
#endif
}
else
{
@@ -474,47 +441,52 @@ static int nrf53_i2c_transfer(struct i2c_master_s *dev,
regval = priv->dcnt;
nrf53_i2c_putreg(priv, NRF53_TWIM_RXDMAXCNT_OFFSET, regval);
/* Shortcuts from LASTRX to STOP */
nrf53_i2c_putreg(priv, NRF53_TWIM_SHORTS_OFFSET,
TWIM_SHORTS_LASTRX_STOP);
/* Start RX sequence */
nrf53_i2c_putreg(priv, NRF53_TWIM_TASKS_STARTRX_OFFSET, 1);
}
/* Wait for last RX done */
/* Wait for stop event */
#ifdef CONFIG_I2C_POLLED
while (1)
{
regval = nrf53_i2c_getreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf53_i2c_putreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
if (nrf53_i2c_getreg(priv,
NRF53_TWIM_EVENTS_LASTRX_OFFSET) == 1)
{
break;
}
}
/* Clear event */
nrf53_i2c_putreg(priv, NRF53_TWIM_EVENTS_LASTRX_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
while (1)
{
regval = nrf53_i2c_getreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
ret = priv->status;
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf53_i2c_putreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
#endif
if (nrf53_i2c_getreg(priv,
NRF53_TWIM_EVENTS_STOPPED_OFFSET) == 1)
{
break;
}
}
/* Clear event */
nrf53_i2c_putreg(priv, NRF53_TWIM_EVENTS_STOPPED_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
{
ret = priv->status;
goto errout;
}
#endif
/* Next message */
priv->msgc -= 1;
@@ -522,46 +494,6 @@ static int nrf53_i2c_transfer(struct i2c_master_s *dev,
}
while (priv->msgc > 0);
/* TWIM stop */
nrf53_i2c_putreg(priv, NRF53_TWIM_TASKS_STOP_OFFSET, 1);
/* Wait for stop event */
#ifdef CONFIG_I2C_POLLED
while (1)
{
regval = nrf53_i2c_getreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf53_i2c_putreg(priv,
NRF53_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
if (nrf53_i2c_getreg(priv,
NRF53_TWIM_EVENTS_STOPPED_OFFSET) == 1)
{
break;
}
}
/* Clear event */
nrf53_i2c_putreg(priv, NRF53_TWIM_EVENTS_STOPPED_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
{
ret = priv->status;
goto errout;
}
#endif
errout:
#ifndef CONFIG_NRF53_I2C_MASTER_DISABLE_NOSTART
if (pack_buf != NULL && pack_buf != priv->copy_buf)
@@ -619,10 +551,6 @@ static int nrf53_i2c_isr(int irq, void *context, void *arg)
{
i2cinfo("I2C LASTTX\n");
/* TX done */
nxsem_post(&priv->sem_isr);
/* Clear event */
nrf53_i2c_putreg(priv, NRF53_TWIM_EVENTS_LASTTX_OFFSET, 0);
@@ -636,10 +564,6 @@ static int nrf53_i2c_isr(int irq, void *context, void *arg)
{
i2cinfo("I2C LASTRX\n");
/* RX done */
nxsem_post(&priv->sem_isr);
/* Clear event */
nrf53_i2c_putreg(priv, NRF53_TWIM_EVENTS_LASTRX_OFFSET, 0);
@@ -744,8 +668,7 @@ static int nrf53_i2c_init(struct nrf53_i2c_priv_s *priv)
#ifndef CONFIG_I2C_POLLED
/* Enable I2C interrupts */
regval = (TWIM_INT_LASTRX | TWIM_INT_LASTTX | TWIM_INT_STOPPED |
TWIM_INT_ERROR);
regval = (TWIM_INT_STOPPED | TWIM_INT_ERROR);
nrf53_i2c_putreg(priv, NRF53_TWIM_INTEN_OFFSET, regval);
/* Attach error and event interrupts to the ISRs */

View File

@@ -419,47 +419,14 @@ static int nrf91_i2c_transfer(struct i2c_master_s *dev,
regval = priv->dcnt;
nrf91_i2c_putreg(priv, NRF91_TWIM_TXDMAXCNT_OFFSET, regval);
/* Shortcut from LASTTX to STOP */
nrf91_i2c_putreg(priv, NRF91_TWIM_SHORTS_OFFSET,
TWIM_SHORTS_LASTTX_STOP);
/* Start TX sequence */
nrf91_i2c_putreg(priv, NRF91_TWIM_TASKS_STARTTX_OFFSET, 1);
/* Wait for last TX event */
#ifdef CONFIG_I2C_POLLED
while (nrf91_i2c_getreg(priv,
NRF91_TWIM_EVENTS_LASTTX_OFFSET) != 1);
while (1)
{
regval = nrf91_i2c_getreg(priv,
NRF91_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf91_i2c_putreg(priv,
NRF91_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
if (nrf91_i2c_getreg(priv,
NRF91_TWIM_EVENTS_LASTTX_OFFSET) == 1)
{
break;
}
}
/* Clear event */
nrf91_i2c_putreg(priv, NRF91_TWIM_EVENTS_LASTTX_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
{
ret = priv->status;
goto errout;
}
#endif
}
else
{
@@ -474,47 +441,52 @@ static int nrf91_i2c_transfer(struct i2c_master_s *dev,
regval = priv->dcnt;
nrf91_i2c_putreg(priv, NRF91_TWIM_RXDMAXCNT_OFFSET, regval);
/* Shortcuts from LASTRX to STOP */
nrf91_i2c_putreg(priv, NRF91_TWIM_SHORTS_OFFSET,
TWIM_SHORTS_LASTRX_STOP);
/* Start RX sequence */
nrf91_i2c_putreg(priv, NRF91_TWIM_TASKS_STARTRX_OFFSET, 1);
}
/* Wait for last RX done */
/* Wait for stop event */
#ifdef CONFIG_I2C_POLLED
while (1)
{
regval = nrf91_i2c_getreg(priv,
NRF91_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf91_i2c_putreg(priv,
NRF91_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
if (nrf91_i2c_getreg(priv,
NRF91_TWIM_EVENTS_LASTRX_OFFSET) == 1)
{
break;
}
}
/* Clear event */
nrf91_i2c_putreg(priv, NRF91_TWIM_EVENTS_LASTRX_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
while (1)
{
regval = nrf91_i2c_getreg(priv,
NRF91_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
ret = priv->status;
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf91_i2c_putreg(priv,
NRF91_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
#endif
if (nrf91_i2c_getreg(priv,
NRF91_TWIM_EVENTS_STOPPED_OFFSET) == 1)
{
break;
}
}
/* Clear event */
nrf91_i2c_putreg(priv, NRF91_TWIM_EVENTS_STOPPED_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
{
ret = priv->status;
goto errout;
}
#endif
/* Next message */
priv->msgc -= 1;
@@ -522,46 +494,6 @@ static int nrf91_i2c_transfer(struct i2c_master_s *dev,
}
while (priv->msgc > 0);
/* TWIM stop */
nrf91_i2c_putreg(priv, NRF91_TWIM_TASKS_STOP_OFFSET, 1);
/* Wait for stop event */
#ifdef CONFIG_I2C_POLLED
while (1)
{
regval = nrf91_i2c_getreg(priv,
NRF91_TWIM_ERRORSRC_OFFSET) & 0x7;
if (regval != 0)
{
i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval);
ret = -1;
nrf91_i2c_putreg(priv,
NRF91_TWIM_ERRORSRC_OFFSET, 0x7);
goto errout;
}
if (nrf91_i2c_getreg(priv,
NRF91_TWIM_EVENTS_STOPPED_OFFSET) == 1)
{
break;
}
}
/* Clear event */
nrf91_i2c_putreg(priv, NRF91_TWIM_EVENTS_STOPPED_OFFSET, 0);
#else
nxsem_wait(&priv->sem_isr);
if (priv->status < 0)
{
ret = priv->status;
goto errout;
}
#endif
errout:
#ifndef CONFIG_NRF91_I2C_MASTER_DISABLE_NOSTART
if (pack_buf != NULL && pack_buf != priv->copy_buf)
@@ -619,10 +551,6 @@ static int nrf91_i2c_isr(int irq, void *context, void *arg)
{
i2cinfo("I2C LASTTX\n");
/* TX done */
nxsem_post(&priv->sem_isr);
/* Clear event */
nrf91_i2c_putreg(priv, NRF91_TWIM_EVENTS_LASTTX_OFFSET, 0);
@@ -636,10 +564,6 @@ static int nrf91_i2c_isr(int irq, void *context, void *arg)
{
i2cinfo("I2C LASTRX\n");
/* RX done */
nxsem_post(&priv->sem_isr);
/* Clear event */
nrf91_i2c_putreg(priv, NRF91_TWIM_EVENTS_LASTRX_OFFSET, 0);
@@ -744,8 +668,7 @@ static int nrf91_i2c_init(struct nrf91_i2c_priv_s *priv)
#ifndef CONFIG_I2C_POLLED
/* Enable I2C interrupts */
regval = (TWIM_INT_LASTRX | TWIM_INT_LASTTX | TWIM_INT_STOPPED |
TWIM_INT_ERROR);
regval = (TWIM_INT_STOPPED | TWIM_INT_ERROR);
nrf91_i2c_putreg(priv, NRF91_TWIM_INTEN_OFFSET, regval);
/* Attach error and event interrupts to the ISRs */