This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 6224690f92abf4bf46a9f9a15f7f9ca9149a8d1c
Author: Max Kriegleder <[email protected]>
AuthorDate: Sat Oct 24 12:53:47 2026 +0200

    arch/arm/nrf52: Use a repeated START for an I2C register read.
    
    A register read is a write of the register address with I2C_M_NOSTOP
    followed by a read from the same address, and many devices require a
    repeated START between the two. The driver ignored I2C_M_NOSTOP: every
    write ended in STOP (LASTTX -> STOP), so the read went out as a separate
    transaction.
    
    Found with an LSM6DSL, whose datasheet (DocID028475, tables 16 and 17)
    requires the repeated START for reads, while debugging out-of-order
    words in FIFO burst reads through /dev/i2c0.
    
    When a write with I2C_M_NOSTOP is followed by a read from the same
    address and both carry data, run them as one TWIM transfer: LASTTX ->
    STARTRX makes the repeated START, LASTRX -> STOP ends it, and the pair
    completes on a single STOPPED event. Every other sequence is handled as
    before.
    
    Assisted-by: Claude:claude-opus-5-5
    Signed-off-by: Max Kriegleder <[email protected]>
---
 arch/arm/src/nrf52/nrf52_i2c.c | 48 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 42 insertions(+), 6 deletions(-)

diff --git a/arch/arm/src/nrf52/nrf52_i2c.c b/arch/arm/src/nrf52/nrf52_i2c.c
index 7ea13584088..0960c08a506 100644
--- a/arch/arm/src/nrf52/nrf52_i2c.c
+++ b/arch/arm/src/nrf52/nrf52_i2c.c
@@ -28,6 +28,7 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <stdbool.h>
 
 #include <nuttx/debug.h>
 #include <nuttx/irq.h>
@@ -212,6 +213,7 @@ static int nrf52_i2c_transfer(struct i2c_master_s *dev,
   struct nrf52_i2c_priv_s *priv = (struct nrf52_i2c_priv_s *)dev;
   uint32_t regval = 0;
   int      ret = OK;
+  bool     rx_after = false;
 #ifndef CONFIG_NRF52_I2C_MASTER_DISABLE_NOSTART
   uint8_t *pack_buf = NULL;
 #endif
@@ -292,6 +294,8 @@ static int nrf52_i2c_transfer(struct i2c_master_s *dev,
       i2cinfo("ptr=%p dcnt=%d flags=%d addr=%d\n",
               priv->ptr, priv->dcnt, priv->flags, priv->addr);
 
+      rx_after = false;
+
       /* Write TWI address */
 
       regval = priv->addr;
@@ -371,10 +375,42 @@ 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 */
+          /* A write without STOP followed by a read from the same address
+           * (a register read): repeated START into the read, then STOP.
+           * Both run as one transfer ending in one STOPPED event.  Only
+           * for data in both directions, as the shortcuts need the LASTTX
+           * and LASTRX events.
+           */
 
-          nrf52_i2c_putreg(priv, NRF52_TWIM_SHORTS_OFFSET,
-                           TWIM_SHORTS_LASTTX_STOP);
+          rx_after = (priv->flags & I2C_M_NOSTOP) != 0 &&
+                     priv->dcnt > 0 &&
+                     priv->msgc > 1 &&
+                     priv->msgv[1].length > 0 &&
+                     (priv->msgv[1].flags & I2C_M_READ) != 0 &&
+                     (priv->msgv[1].flags & I2C_M_NOSTART) == 0 &&
+                     priv->msgv[1].addr == priv->addr;
+
+          if (rx_after)
+            {
+              regval = (uint32_t)priv->msgv[1].buffer;
+              DEBUGASSERT(nrf52_easydma_valid(regval));
+              nrf52_i2c_putreg(priv, NRF52_TWIM_RXDPTR_OFFSET, regval);
+              nrf52_i2c_putreg(priv, NRF52_TWIM_RXDMAXCNT_OFFSET,
+                               priv->msgv[1].length);
+
+              /* Shortcuts from LASTTX to STARTRX, LASTRX to STOP */
+
+              nrf52_i2c_putreg(priv, NRF52_TWIM_SHORTS_OFFSET,
+                               TWIM_SHORTS_LASTTX_STARTRX |
+                               TWIM_SHORTS_LASTRX_STOP);
+            }
+          else
+            {
+              /* Shortcut from LASTTX to STOP */
+
+              nrf52_i2c_putreg(priv, NRF52_TWIM_SHORTS_OFFSET,
+                               TWIM_SHORTS_LASTTX_STOP);
+            }
 
           /* Start TX sequence */
 
@@ -441,10 +477,10 @@ static int nrf52_i2c_transfer(struct i2c_master_s *dev,
         }
 #endif
 
-      /* Next message */
+      /* Next message (two after a write with its read) */
 
-      priv->msgc -= 1;
-      priv->msgv += 1;
+      priv->msgc -= rx_after ? 2 : 1;
+      priv->msgv += rx_after ? 2 : 1;
     }
   while (priv->msgc > 0);
 

Reply via email to