nanohub: i2c: detect and clock through stuck low sda am: 44b122a273
am: aad7f8db68

Change-Id: If81bc9be8fb01dc47e193878e60d49e119beef95
diff --git a/firmware/os/core/timer.c b/firmware/os/core/timer.c
index ace8072..f9c3352 100644
--- a/firmware/os/core/timer.c
+++ b/firmware/os/core/timer.c
@@ -62,6 +62,14 @@
     return platGetTicks();
 }
 
+void timDelay(uint32_t length)
+{
+    uint64_t curTime = timGetTime();
+
+    while (curTime + length > timGetTime())
+        ;
+}
+
 static struct Timer *timFindTimerById(uint32_t timId) /* no locks taken. be careful what you do with this */
 {
     uint32_t i;
diff --git a/firmware/os/inc/timer.h b/firmware/os/inc/timer.h
index abc5e19..30e359a 100644
--- a/firmware/os/inc/timer.h
+++ b/firmware/os/inc/timer.h
@@ -38,6 +38,7 @@
 
 
 uint64_t timGetTime(void);   /* Time since some stable reference point in nanoseconds */
+void timDelay(uint32_t length);
 
 uint32_t timTimerSet(uint64_t length, uint32_t jitterPpm, uint32_t driftPpm, TimTimerCbkF cbk, void* data, bool oneShot); /* return timer id or 0 if failed */
 uint32_t timTimerSetAsApp(uint64_t length, uint32_t jitterPpm, uint32_t driftPpm, uint32_t tid, void* data, bool oneShot); /* return timer id or 0 if failed */
diff --git a/firmware/os/platform/stm32/i2c.c b/firmware/os/platform/stm32/i2c.c
index 189e8f9..7ba7b7c 100644
--- a/firmware/os/platform/stm32/i2c.c
+++ b/firmware/os/platform/stm32/i2c.c
@@ -26,6 +26,7 @@
 #include <atomicBitset.h>
 #include <atomic.h>
 #include <platform.h>
+#include <timer.h>
 
 #include <plat/cmsis.h>
 #include <plat/dma.h>
@@ -775,6 +776,49 @@
     return gpio;
 }
 
+static int i2cMasterReset(uint32_t busId, uint32_t speed)
+{
+    struct Gpio *sda, *scl;
+    int cnt = 0;
+    uint32_t delay;
+
+    if (busId >= ARRAY_SIZE(mStmI2cDevs))
+        return -EINVAL;
+
+    const struct StmI2cBoardCfg *board = boardStmI2cCfg(busId);
+    if (!board)
+        return -EINVAL;
+
+    sda = gpioRequest(board->gpioSda.gpioNum);
+    gpioConfigOutput(sda, board->gpioSpeed, GPIO_PULL_NONE, GPIO_OUT_OPEN_DRAIN, 1);
+    if (gpioGet(sda) == 0) {
+        // 50% duty cycle for the clock
+        delay = 500000000UL/speed;
+
+        scl = gpioRequest(board->gpioScl.gpioNum);
+        gpioConfigOutput(scl, board->gpioSpeed, GPIO_PULL_NONE, GPIO_OUT_OPEN_DRAIN, 1);
+        do {
+            // generate clock pulse
+            gpioSet(scl, 1);
+            timDelay(delay);
+            gpioSet(scl, 0);
+            timDelay(delay);
+            cnt ++;
+        } while (gpioGet(sda) == 0 && cnt < 9);
+
+        // generate STOP condition
+        gpioSet(sda, 0);
+        gpioSet(scl, 1);
+        timDelay(delay);
+        gpioSet(sda, 1);
+        timDelay(delay);
+        gpioRelease(scl);
+    }
+    gpioRelease(sda);
+
+    return cnt;
+}
+
 int i2cMasterRequest(uint32_t busId, uint32_t speed)
 {
     if (busId >= ARRAY_SIZE(mStmI2cDevs))
@@ -797,6 +841,8 @@
         pdev->last = 1;
         atomicBitsetInit(mXfersValid, I2C_MAX_QUEUE_DEPTH);
 
+        i2cMasterReset(busId, speed);
+
         pdev->scl = stmI2cGpioInit(board, &board->gpioScl);
         pdev->sda = stmI2cGpioInit(board, &board->gpioSda);