You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by vi...@apache.org on 2018/11/19 17:07:54 UTC

[mynewt-core] 07/07: Add new API for double buffered I2C

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

vipulrahane pushed a commit to branch dma_i2c_new_api
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit b9fd22e9c34cca5eed58ebf148415e5e2a341d62
Author: Vipul Rahane <vi...@runtime.io>
AuthorDate: Mon Nov 19 09:07:02 2018 -0800

    Add new API for double buffered I2C
---
 hw/drivers/led/lp5523/src/lp5523.c         |  27 ++++++-
 hw/drivers/led/lp5523/syscfg.yml           |   4 +-
 hw/drivers/sensors/lis2dh12/src/lis2dh12.c |  22 ++++-
 hw/drivers/sensors/lis2dh12/syscfg.yml     |   2 +-
 hw/drivers/sensors/lps33hw/src/lps33hw.c   |  21 ++++-
 hw/drivers/sensors/lps33hw/syscfg.yml      |   4 +-
 hw/drivers/sensors/lps33thw/src/lps33thw.c |  19 ++++-
 hw/drivers/sensors/lps33thw/syscfg.yml     |   2 +-
 hw/drivers/sensors/ms5840/src/ms5840.c     |  21 ++++-
 hw/drivers/sensors/ms5840/syscfg.yml       |   2 +-
 hw/hal/include/hal/hal_i2c.h               |  20 ++++-
 hw/hal/syscfg.yml                          |   4 +
 hw/mcu/nordic/nrf52xxx/src/hal_i2c.c       | 124 ++++++++++++++++++++++++++---
 hw/mcu/nordic/nrf52xxx/syscfg.yml          |   1 +
 hw/util/i2cn/include/i2cn/i2cn.h           |   4 +
 hw/util/i2cn/src/i2cn.c                    |  26 ++++++
 16 files changed, 267 insertions(+), 36 deletions(-)

diff --git a/hw/drivers/led/lp5523/src/lp5523.c b/hw/drivers/led/lp5523/src/lp5523.c
index 894a5fc..6e0bdb4 100644
--- a/hw/drivers/led/lp5523/src/lp5523.c
+++ b/hw/drivers/led/lp5523/src/lp5523.c
@@ -31,12 +31,14 @@
 STATS_SECT_START(lp5523_stat_section)
     STATS_SECT_ENTRY(read_errors)
     STATS_SECT_ENTRY(write_errors)
+    STATS_SECT_ENTRY(write_read_errors)
 STATS_SECT_END
 
 /* Define stat names for querying */
 STATS_NAME_START(lp5523_stat_section)
     STATS_NAME(lp5523_stat_section, read_errors)
     STATS_NAME(lp5523_stat_section, write_errors)
+    STATS_NAME(lp5523_stat_section, write_read_errors)
 STATS_NAME_END(lp5523_stat_section)
 
 /* Global variable used to hold stats data */
@@ -87,8 +89,10 @@ lp5523_get_reg(struct led_itf *itf, enum lp5523_registers addr,
 
     struct hal_i2c_master_data data_struct = {
         .address = itf->li_addr,
-        .len = 1,
-        .buffer = &addr
+        .len1 = 1,
+        .buffer1 = &addr,
+        .len2 = 1,
+        .buffer2 = value
     };
 
     rc = led_itf_lock(itf, MYNEWT_VAL(LP5523_ITF_LOCK_TMO));
@@ -96,6 +100,7 @@ lp5523_get_reg(struct led_itf *itf, enum lp5523_registers addr,
         return rc;
     }
 
+#if 0
     /* Register write */
     rc = i2cn_master_write(itf->li_num, &data_struct, MYNEWT_VAL(LP5523_I2C_TIMEOUT_TICKS), 0,
                            MYNEWT_VAL(LP5523_I2C_RETRIES));
@@ -117,6 +122,16 @@ lp5523_get_reg(struct led_itf *itf, enum lp5523_registers addr,
                     itf->li_addr, addr);
          STATS_INC(g_lp5523stats, read_errors);
     }
+#endif
+    rc = i2cn_master_write_read(itf->li_num, &data_struct, MYNEWT_VAL(LP5523_I2C_TIMEOUT_TICKS), 0,
+                                MYNEWT_VAL(LP5523_I2C_RETRIES));
+
+    if (rc) {
+        LP5523_LOG(ERROR, "I2C wr failed at address 0x%02X\n",
+                   itf->li_addr);
+        STATS_INC(g_lp5523stats, write_read_errors);
+        goto err;
+    }
 
 err:
     led_itf_unlock(itf);
@@ -554,7 +569,13 @@ lp5523_get_engine_int(struct led_itf *itf, uint8_t engine, uint8_t *flag)
 int
 lp5523_reset(struct led_itf *itf)
 {
-    return lp5523_set_reg(itf, LP5523_RESET, 0xff);
+    int rc;
+
+    rc = lp5523_set_reg(itf, LP5523_RESET, 0xff);
+
+    os_time_delay(1);
+
+    return rc;
 }
 
 int
diff --git a/hw/drivers/led/lp5523/syscfg.yml b/hw/drivers/led/lp5523/syscfg.yml
index 49a64c3..bea4c2c 100644
--- a/hw/drivers/led/lp5523/syscfg.yml
+++ b/hw/drivers/led/lp5523/syscfg.yml
@@ -43,7 +43,7 @@ syscfg.defs:
         description: >
             Number of retries to use for failed I2C communication.  A retry is
             used when the LP5523 sends an unexpected NACK.
-        value: 2
+        value: 0
     LP5523_STARTUP_SEQ_DELAY:
         description: >
             Startup sequence delay for chip to finish start sequence
@@ -52,4 +52,4 @@ syscfg.defs:
     LP5523_I2C_TIMEOUT_TICKS:
         description: >
             Number of OS ticks to wait for each I2C transaction to complete.
-        value: 3
+        value: 10
diff --git a/hw/drivers/sensors/lis2dh12/src/lis2dh12.c b/hw/drivers/sensors/lis2dh12/src/lis2dh12.c
index fcc272a..3558f35 100644
--- a/hw/drivers/sensors/lis2dh12/src/lis2dh12.c
+++ b/hw/drivers/sensors/lis2dh12/src/lis2dh12.c
@@ -152,6 +152,7 @@ STATS_SECT_START(lis2dh12_stat_section)
     STATS_SECT_ENTRY(orient_chg_y_notify)
     STATS_SECT_ENTRY(orient_chg_z_notify)
 #endif
+    STATS_SECT_ENTRY(write_read_errors)
 STATS_SECT_END
 
 /* Define stat names for querying */
@@ -170,6 +171,7 @@ STATS_NAME_START(lis2dh12_stat_section)
     STATS_NAME(lis2dh12_stat_section, orient_chg_y_notify)
     STATS_NAME(lis2dh12_stat_section, orient_chg_z_notify)
 #endif
+    STATS_NAME(lis2dh12_stat_section, write_read_errors)
 STATS_NAME_END(lis2dh12_stat_section)
 
 /* Global variable used to hold stats data */
@@ -227,24 +229,27 @@ lis2dh12_i2c_readlen(struct sensor_itf *itf, uint8_t addr, uint8_t *buffer,
                      uint8_t len)
 {
     int rc;
+
     if (len > 1)
     {
         addr |= 0x80;
     }
 
-    uint8_t payload[20] = { addr, 0, 0, 0, 0, 0, 0, 0,
+    uint8_t payload[20] = {   0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0};
 
     struct hal_i2c_master_data data_struct = {
         .address = itf->si_addr,
-        .len = 1,
-        .buffer = payload
+        .len1 = 1,
+        .buffer1 = &addr,
+        .len2 = len,
+        .buffer2 = payload
     };
 
     /* Clear the supplied buffer */
     memset(buffer, 0, len);
-
+#if 0
     /* Register write */
     rc = i2cn_master_write(itf->si_num, &data_struct, MYNEWT_VAL(LIS2DH12_I2C_TIMEOUT_TICKS), 1,
                            MYNEWT_VAL(LIS2DH12_I2C_RETRIES));
@@ -266,6 +271,15 @@ lis2dh12_i2c_readlen(struct sensor_itf *itf, uint8_t addr, uint8_t *buffer,
         STATS_INC(g_lis2dh12stats, read_errors);
         goto err;
     }
+#endif
+    rc = i2cn_master_write_read(itf->si_num, &data_struct, MYNEWT_VAL(LIS2DH12_I2C_TIMEOUT_TICKS) * (len + 1), 1,
+                                MYNEWT_VAL(LIS2DH12_I2C_RETRIES));
+    if (rc) {
+        LIS2DH12_LOG(ERROR, "Failed to read from 0x%02X:0x%02X\n",
+                     data_struct.address, addr);
+        STATS_INC(g_lis2dh12stats, write_read_errors);
+        goto err;
+    }
 
     /* Copy the I2C results into the supplied buffer */
     memcpy(buffer, payload, len);
diff --git a/hw/drivers/sensors/lis2dh12/syscfg.yml b/hw/drivers/sensors/lis2dh12/syscfg.yml
index 72cd520..d2251f0 100644
--- a/hw/drivers/sensors/lis2dh12/syscfg.yml
+++ b/hw/drivers/sensors/lis2dh12/syscfg.yml
@@ -28,7 +28,7 @@ syscfg.defs:
         description: >
             Number of retries to use for failed I2C communication.  A retry is
             used when the LIS2DH12 sends an unexpected NACK.
-        value: 2
+        value: 0
     LIS2DH12_I2C_TIMEOUT_TICKS:
         description: >
             Number of OS ticks to wait for each I2C transaction to complete.
diff --git a/hw/drivers/sensors/lps33hw/src/lps33hw.c b/hw/drivers/sensors/lps33hw/src/lps33hw.c
index 62706b5..dd096c7 100644
--- a/hw/drivers/sensors/lps33hw/src/lps33hw.c
+++ b/hw/drivers/sensors/lps33hw/src/lps33hw.c
@@ -47,12 +47,14 @@ static struct hal_spi_settings spi_lps33hw_settings = {
 STATS_SECT_START(lps33hw_stat_section)
     STATS_SECT_ENTRY(read_errors)
     STATS_SECT_ENTRY(write_errors)
+    STATS_SECT_ENTRY(write_read_errors)
 STATS_SECT_END
 
 /* Define stat names for querying */
 STATS_NAME_START(lps33hw_stat_section)
     STATS_NAME(lps33hw_stat_section, read_errors)
     STATS_NAME(lps33hw_stat_section, write_errors)
+    STATS_NAME(lps33hw_stat_section, write_read_errors)
 STATS_NAME_END(lps33hw_stat_section)
 
 /* Global variable used to hold stats data */
@@ -183,7 +185,7 @@ lps33hw_i2c_set_reg(struct sensor_itf *itf, uint8_t reg, uint8_t value)
         LPS33HW_LOG(ERROR,
                     "Failed to write to 0x%02X:0x%02X with value 0x%02X\n",
                     itf->si_addr, reg, value);
-        STATS_INC(g_lps33hwstats, read_errors);
+        STATS_INC(g_lps33hwstats, write_errors);
     }
 
     return rc;
@@ -342,10 +344,13 @@ lps33hw_i2c_get_regs(struct sensor_itf *itf, uint8_t reg, uint8_t size,
 
     struct hal_i2c_master_data data_struct = {
         .address = itf->si_addr,
-        .len = 1,
-        .buffer = &reg
+        .len1 = 1,
+        .buffer1 = &reg,
+        .len2 = size,
+        .buffer2 = buffer
     };
 
+#if 0
     /* Register write */
     rc = i2cn_master_write(itf->si_num, &data_struct, MYNEWT_VAL(LPS33HW_I2C_TIMEOUT_TICKS), 0,
                            MYNEWT_VAL(LPS33HW_I2C_RETRIES));
@@ -368,6 +373,16 @@ lps33hw_i2c_get_regs(struct sensor_itf *itf, uint8_t reg, uint8_t size,
                     itf->si_addr, reg);
         STATS_INC(g_lps33hwstats, read_errors);
     }
+#endif
+    rc = i2cn_master_write_read(itf->si_num, &data_struct,
+                                (MYNEWT_VAL(LPS33HW_I2C_TIMEOUT_TICKS)) * (size + 1), 1,
+                                MYNEWT_VAL(LPS33HW_I2C_RETRIES));
+
+    if (rc) {
+        LPS33HW_LOG(ERROR, "Failed to write-read from 0x%02X:0x%02X\n",
+                    itf->si_addr, reg);
+        STATS_INC(g_lps33hwstats, write_read_errors);
+    }
     return rc;
 }
 
diff --git a/hw/drivers/sensors/lps33hw/syscfg.yml b/hw/drivers/sensors/lps33hw/syscfg.yml
index 12f5a63..89eaa8e 100644
--- a/hw/drivers/sensors/lps33hw/syscfg.yml
+++ b/hw/drivers/sensors/lps33hw/syscfg.yml
@@ -42,9 +42,9 @@ syscfg.defs:
         description: >
             Number of retries to use for failed I2C communication.  A retry is
             used when the LPS33HW sends an unexpected NACK.
-        value: 2
+        value: 0
     LPS33HW_I2C_TIMEOUT_TICKS:
         description: >
             Number of OS ticks to wait for each I2C transaction to complete.
         value: 3
- 
\ No newline at end of file
+ 
diff --git a/hw/drivers/sensors/lps33thw/src/lps33thw.c b/hw/drivers/sensors/lps33thw/src/lps33thw.c
index 0d38500..1b368d3 100644
--- a/hw/drivers/sensors/lps33thw/src/lps33thw.c
+++ b/hw/drivers/sensors/lps33thw/src/lps33thw.c
@@ -342,10 +342,12 @@ lps33thw_i2c_get_regs(struct sensor_itf *itf, uint8_t reg, uint8_t size,
 
     struct hal_i2c_master_data data_struct = {
         .address = itf->si_addr,
-        .len = 1,
-        .buffer = &reg
+        .len1 = 1,
+        .buffer1 = &reg,
+        .len2 = size,
+        .buffer2 = buffer
     };
-
+#if 0
     /* Register write */
     rc = i2cn_master_write(itf->si_num, &data_struct, MYNEWT_VAL(LPS33THW_I2C_TIMEOUT_TICKS), 0,
                            MYNEWT_VAL(LPS33THW_I2C_RETRIES));
@@ -368,6 +370,17 @@ lps33thw_i2c_get_regs(struct sensor_itf *itf, uint8_t reg, uint8_t size,
                     itf->si_addr, reg);
         STATS_INC(g_lps33thwstats, read_errors);
     }
+#endif
+    rc = i2cn_master_write_read(itf->si_num, &data_struct,
+                                (MYNEWT_VAL(LPS33THW_I2C_TIMEOUT_TICKS)) * (size + 1), 1,
+                                MYNEWT_VAL(LPS33THW_I2C_RETRIES));
+
+    if (rc) {
+        LPS33THW_LOG(ERROR, "Failed to write-read from 0x%02X:0x%02X\n",
+                    itf->si_addr, reg);
+        STATS_INC(g_lps33thwstats, read_errors);
+    }
+
     return rc;
 }
 
diff --git a/hw/drivers/sensors/lps33thw/syscfg.yml b/hw/drivers/sensors/lps33thw/syscfg.yml
index ba1aa29..46773d5 100644
--- a/hw/drivers/sensors/lps33thw/syscfg.yml
+++ b/hw/drivers/sensors/lps33thw/syscfg.yml
@@ -42,7 +42,7 @@ syscfg.defs:
         description: >
             Number of retries to use for failed I2C communication.  A retry is
             used when the LPS33THW sends an unexpected NACK.
-        value: 2
+        value: 0
     LPS33THW_I2C_TIMEOUT_TICKS:
         description: >
             Number of OS ticks to wait for each I2C transaction to complete.
diff --git a/hw/drivers/sensors/ms5840/src/ms5840.c b/hw/drivers/sensors/ms5840/src/ms5840.c
index ab421de..dac0321 100644
--- a/hw/drivers/sensors/ms5840/src/ms5840.c
+++ b/hw/drivers/sensors/ms5840/src/ms5840.c
@@ -51,6 +51,7 @@ static uint16_t cnv_time[6] = {
 STATS_SECT_START(ms5840_stat_section)
     STATS_SECT_ENTRY(read_errors)
     STATS_SECT_ENTRY(write_errors)
+    STATS_SECT_ENTRY(write_read_errors)
     STATS_SECT_ENTRY(eeprom_crc_errors)
 STATS_SECT_END
 
@@ -58,6 +59,7 @@ STATS_SECT_END
 STATS_NAME_START(ms5840_stat_section)
     STATS_NAME(ms5840_stat_section, read_errors)
     STATS_NAME(ms5840_stat_section, write_errors)
+    STATS_NAME(ms5840_stat_section, write_read_errors)
     STATS_NAME(ms5840_stat_section, eeprom_crc_errors)
 STATS_NAME_END(ms5840_stat_section)
 
@@ -365,12 +367,14 @@ ms5840_readlen(struct sensor_itf *itf, uint8_t addr, uint8_t *buffer,
                uint8_t len)
 {
     int rc;
-    uint8_t payload[3] = {addr, 0, 0};
+    uint8_t payload[3] = {0};
 
     struct hal_i2c_master_data data_struct = {
         .address = itf->si_addr,
-        .len = 1,
-        .buffer = payload
+        .len1 = 1,
+        .buffer1 = &addr,
+        .len2 = len,
+        .buffer2 = payload,
     };
 
     /* Clear the supplied buffer */
@@ -380,7 +384,7 @@ ms5840_readlen(struct sensor_itf *itf, uint8_t addr, uint8_t *buffer,
     if (rc) {
         return rc;
     }
-
+#if 0
     /* Command write */
     rc = i2cn_master_write(itf->si_num, &data_struct, MYNEWT_VAL(MS5840_I2C_TIMEOUT_TICKS), 1,
                            MYNEWT_VAL(MS5840_I2C_RETRIES));
@@ -402,6 +406,15 @@ ms5840_readlen(struct sensor_itf *itf, uint8_t addr, uint8_t *buffer,
         STATS_INC(g_ms5840stats, read_errors);
         goto err;
     }
+#endif
+    rc = i2cn_master_write_read(itf->si_num, &data_struct, MYNEWT_VAL(MS5840_I2C_TIMEOUT_TICKS) * (len + 1), 1,
+                                MYNEWT_VAL(MS5840_I2C_RETRIES));
+    if (rc) {
+        MS5840_LOG(ERROR, "Failed to write-read from 0x%02X:0x%02X\n",
+                   data_struct.address, addr);
+        STATS_INC(g_ms5840stats, write_read_errors);
+        goto err;
+    }
 
     /* Copy the I2C results into the supplied buffer */
     memcpy(buffer, payload, len);
diff --git a/hw/drivers/sensors/ms5840/syscfg.yml b/hw/drivers/sensors/ms5840/syscfg.yml
index 7a405ea..8c3f76d 100644
--- a/hw/drivers/sensors/ms5840/syscfg.yml
+++ b/hw/drivers/sensors/ms5840/syscfg.yml
@@ -27,7 +27,7 @@ syscfg.defs:
         description: >
             Number of retries to use for failed I2C communication.  A retry is
             used when the MS5840 sends an unexpected NACK.
-        value: 2
+        value: 0
     MS5840_I2C_TIMEOUT_TICKS:
         description: >
             Number of OS ticks to wait for each I2C transaction to complete.
diff --git a/hw/hal/include/hal/hal_i2c.h b/hw/hal/include/hal/hal_i2c.h
index 0d1173e..a794621 100644
--- a/hw/hal/include/hal/hal_i2c.h
+++ b/hw/hal/include/hal/hal_i2c.h
@@ -114,10 +114,25 @@ struct hal_i2c_master_data {
      * only the top 7-bits to this function as 0x40
      */
     uint8_t  address;
-    /** Number of buffer bytes to transmit or receive */
+//#if MYNEWT_VAL(HAL_I2C_DOUBLE_BUFFERED)
+    /** Number of buffer bytes to transmit or receive using buffer 1*/
     uint16_t len;
+    /** Number of buffer bytes to transmit or receive using buffer 2*/
+    uint16_t len1;
+//#else
+    /** Number of buffer bytes to transmit or receive */
+    uint16_t len2;
+//#endif
+
+//#if MYNEWT_VAL(HAL_I2C_DOUBLE_BUFFERED)
+    /** Buffer space to hold the transmit or receive */
+    uint8_t *buffer1;
+    /** Buffer space to hold the transmit or receive */
+    uint8_t *buffer2;
+//#else
     /** Buffer space to hold the transmit or receive */
     uint8_t *buffer;
+//#endif
 };
 
 /**
@@ -180,6 +195,9 @@ int hal_i2c_master_read(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
  */
 int hal_i2c_master_probe(uint8_t i2c_num, uint8_t address,
                          uint32_t timeout);
+int
+hal_i2c_master_write_read(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
+                          uint32_t timo, uint8_t last_op);
 
 #ifdef __cplusplus
 }
diff --git a/hw/hal/syscfg.yml b/hw/hal/syscfg.yml
index f49dff1..4333534 100644
--- a/hw/hal/syscfg.yml
+++ b/hw/hal/syscfg.yml
@@ -33,6 +33,10 @@ syscfg.defs:
             buffer of this size is allocated on the stack during verify
             operations.
         value: 16
+    HAL_I2C_DOUBLE_BUFFERED:
+        description: >
+            Specify whether the HAL uses double buffered implementation
+        value: 0
 
 syscfg.vals.OS_DEBUG_MODE:
     HAL_FLASH_VERIFY_WRITES: 1
diff --git a/hw/mcu/nordic/nrf52xxx/src/hal_i2c.c b/hw/mcu/nordic/nrf52xxx/src/hal_i2c.c
index c2c5e71..b792318 100644
--- a/hw/mcu/nordic/nrf52xxx/src/hal_i2c.c
+++ b/hw/mcu/nordic/nrf52xxx/src/hal_i2c.c
@@ -399,19 +399,34 @@ hal_i2c_handle_transact_end(NRF_TWIM_Type *regs, uint8_t op, uint32_t start,
 {
     int rc;
     volatile uint32_t *evt;
-    os_time_t now = 0;
+    volatile os_time_t now = 0;
 
-    while(1) {
-        /*
-         * Use last_op as the determining factor for the type of event to be
-         * monitored
-         */
-        if (last_op) {
-            evt = &regs->EVENTS_STOPPED;
-        } else {
-            evt = &regs->EVENTS_SUSPENDED;
+    /*
+     * Use last_op as the determining factor for the type of event to be
+     * monitored
+     */
+    if (last_op) {
+        evt = &regs->EVENTS_STOPPED;
+    } else {
+        evt = &regs->EVENTS_SUSPENDED;
+    }
+
+    /* since there is no SUSPEND short for RX, we have to specifically
+     * deal with it
+     */
+    if (op == I2C_READ && !last_op) {
+        while (!regs->EVENTS_LASTRX) {
+            now = os_time_get();
+            if (OS_TIME_TICK_GT(now, abs_timo)) {
+                rc = HAL_I2C_ERR_TIMEOUT;
+                goto err;
+            }
         }
 
+        regs->TASKS_SUSPEND = 1;
+    }
+
+    while(1) {
         if (*evt) {
             if (evt == &regs->EVENTS_STOPPED) {
 #if MYNEWT_VAL(NRF52_HANDLE_ANOMALY_109)
@@ -433,10 +448,11 @@ hal_i2c_handle_transact_end(NRF_TWIM_Type *regs, uint8_t op, uint32_t start,
             goto err;
         }
     }
+    regs->TASKS_RESUME  = 1;
 
     g_start = os_cputime_get32() - g_start;
 
-    console_printf("t:%lu r:%u\n", g_start, op);
+    //console_printf("t:%lu r:%u\n", g_start, op);
 
     return 0;
 err:
@@ -638,6 +654,8 @@ hal_i2c_master_read(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
      */
     if (last_op) {
         regs->SHORTS = TWIM_SHORTS_LASTRX_STOP_Msk;
+    } else {
+        regs->SHORTS = 0;
     }
 
     /* Starts an I2C transaction using TWIM/EasyDMA */
@@ -661,6 +679,90 @@ err:
 }
 
 int
+hal_i2c_master_write_read(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
+                          uint32_t timo, uint8_t last_op)
+{
+    int rc;
+    os_time_t now;
+    uint32_t start;
+    NRF_TWIM_Type *regs;
+    struct nrf52_hal_i2c *i2c;
+
+    start = os_time_get();
+    g_start = os_cputime_get32();
+
+    /* Resolve the I2C bus */
+    rc = hal_i2c_resolve(i2c_num, &i2c);
+    if (rc != 0) {
+        return rc;
+    }
+
+    regs = i2c->nhi_regs;
+
+    /* Detect errors on the bus based on the previous and current
+     * condition of the bus
+     */
+    rc = hal_i2c_bus_error_detect(i2c);
+    if (rc) {
+        goto err;
+    }
+
+    /* Configure the RXD registers for EasyDMA access to work with buffers of
+     * specific length and address of the slave
+     */
+    regs->ADDRESS    = pdata->address;
+    regs->TXD.MAXCNT = pdata->len1;
+    regs->TXD.PTR    = (uint32_t)pdata->buffer1;
+    regs->TXD.LIST   = 0;
+    /* Disable and clear interrupts */
+    regs->INTENCLR   = NRF_TWIM_ALL_INTS_MASK;
+    regs->INTEN      = 0;
+    regs->SHORTS = TWIM_SHORTS_LASTTX_STARTRX_Msk;
+
+    /* Starts an I2C transaction using TWIM/EasyDMA */
+    rc = hal_i2c_handle_transact_start(i2c, I2C_WRITE, start + timo, pdata->address);
+    if (rc) {
+        goto err;
+    }
+
+    while (!regs->EVENTS_LASTTX) {
+        now = os_time_get();
+        if (OS_TIME_TICK_GT(now, start + timo)) {
+            rc = HAL_I2C_ERR_TIMEOUT;
+            return rc;
+        }
+    }
+
+    regs->RXD.MAXCNT = pdata->len2;
+    regs->RXD.PTR    = (uint32_t)pdata->buffer2;
+    regs->RXD.LIST   = 0;
+    /* Only set short for RX->STOP for last_op:1 since there is no suspend short
+     * available in nrf52832
+     */
+    if (last_op) {
+        regs->SHORTS = TWIM_SHORTS_LASTRX_STOP_Msk;
+    } else {
+        regs->SHORTS = 0;
+    }
+
+    regs->TASKS_RESUME  = 1;
+
+    /* Ends an I2C transaction using TWIM/EasyDMA */
+    rc = hal_i2c_handle_transact_end(regs, I2C_READ, start, start + timo, last_op);
+    if (rc) {
+        goto err;
+    }
+
+    /* Save the current last op to detect bus errors later */
+    i2c->nhi_prev_last_op = last_op;
+
+    return 0;
+err:
+    return hal_i2c_handle_errors(i2c, rc, start + timo);
+}
+
+
+int
 hal_i2c_master_probe(uint8_t i2c_num, uint8_t address, uint32_t timo)
 {
     struct hal_i2c_master_data rx;
diff --git a/hw/mcu/nordic/nrf52xxx/syscfg.yml b/hw/mcu/nordic/nrf52xxx/syscfg.yml
index acd907f..98dde96 100644
--- a/hw/mcu/nordic/nrf52xxx/syscfg.yml
+++ b/hw/mcu/nordic/nrf52xxx/syscfg.yml
@@ -410,3 +410,4 @@ syscfg.restrictions:
     - "!SPI_3_SLAVE || (SPI_3_SLAVE_PIN_SCK && SPI_3_SLAVE_PIN_MOSI && SPI_3_SLAVE_PIN_MISO && SPI_3_SLAVE_PIN_SS)"
     - "!UART_0 || (UART_0_PIN_TX && UART_0_PIN_RX)"
     - "!UART_1 || (UART_1_PIN_TX && UART_1_PIN_RX)"
+    - "HAL_I2C_DOUBLE_BUFFERED"
diff --git a/hw/util/i2cn/include/i2cn/i2cn.h b/hw/util/i2cn/include/i2cn/i2cn.h
index d6caaae..f2b4851 100644
--- a/hw/util/i2cn/include/i2cn/i2cn.h
+++ b/hw/util/i2cn/include/i2cn/i2cn.h
@@ -64,6 +64,10 @@ int i2cn_master_read(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
 int i2cn_master_write(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
                       uint32_t timeout, uint8_t last_op, int retries);
 
+int
+i2cn_master_write_read(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
+                       uint32_t timeout, uint8_t last_op, int retries);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/hw/util/i2cn/src/i2cn.c b/hw/util/i2cn/src/i2cn.c
index 2322e1f..536a8bd 100644
--- a/hw/util/i2cn/src/i2cn.c
+++ b/hw/util/i2cn/src/i2cn.c
@@ -19,6 +19,7 @@
 
 #include "hal/hal_i2c.h"
 #include "i2cn/i2cn.h"
+#include <console/console.h>
 
 int
 i2cn_master_read(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
@@ -37,6 +38,7 @@ i2cn_master_read(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
         if (rc == 0) {
             break;
         }
+        console_printf("r addr: %02x n: %u rc: %d\n", pdata->address, retries, rc);
     }
 
     return rc;
@@ -59,6 +61,30 @@ i2cn_master_write(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
         if (rc == 0) {
             break;
         }
+        console_printf("w addr: %02x n: %u rc: %d\n", pdata->address, retries, rc);
+    }
+
+    return rc;
+}
+
+int
+i2cn_master_write_read(uint8_t i2c_num, struct hal_i2c_master_data *pdata,
+                       uint32_t timeout, uint8_t last_op, int retries)
+{
+    int rc = 0;
+    int i;
+
+    /* Ensure at least one try. */
+    if (retries < 0) {
+        retries = 0;
+    }
+
+    for (i = 0; i <= retries; i++) {
+        rc = hal_i2c_master_write_read(i2c_num, pdata, timeout, last_op);
+        if (rc == 0) {
+            break;
+        }
+        console_printf("wr addr: %02x n: %u rc: %d\n", pdata->address, retries, rc);
     }
 
     return rc;