From a9ecff5f1c34684eb7e2f2578c4fa43738fe469a Mon Sep 17 00:00:00 2001 From: Max Kriegleder Date: Tue, 11 Nov 2025 16:45:33 +0100 Subject: [PATCH] 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 --- arch/arm/src/nrf52/nrf52_i2c.c | 159 +++++++++------------------------ arch/arm/src/nrf53/nrf53_i2c.c | 159 +++++++++------------------------ arch/arm/src/nrf91/nrf91_i2c.c | 159 +++++++++------------------------ 3 files changed, 123 insertions(+), 354 deletions(-) diff --git a/arch/arm/src/nrf52/nrf52_i2c.c b/arch/arm/src/nrf52/nrf52_i2c.c index 03ee1ec471f..091ffa1191c 100644 --- a/arch/arm/src/nrf52/nrf52_i2c.c +++ b/arch/arm/src/nrf52/nrf52_i2c.c @@ -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 */ diff --git a/arch/arm/src/nrf53/nrf53_i2c.c b/arch/arm/src/nrf53/nrf53_i2c.c index 56e4d2c57d6..94eb1ddb8af 100644 --- a/arch/arm/src/nrf53/nrf53_i2c.c +++ b/arch/arm/src/nrf53/nrf53_i2c.c @@ -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 */ diff --git a/arch/arm/src/nrf91/nrf91_i2c.c b/arch/arm/src/nrf91/nrf91_i2c.c index 9c1810781c4..7d6deb48b96 100644 --- a/arch/arm/src/nrf91/nrf91_i2c.c +++ b/arch/arm/src/nrf91/nrf91_i2c.c @@ -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 */