diff options
author | Ben Fennema <fennema@google.com> | 2017-11-29 10:46:08 -0800 |
---|---|---|
committer | Ben Fennema <fennema@google.com> | 2017-12-20 14:17:29 -0800 |
commit | 44b122a273da6aff4ec56c292898f8204811dd4d (patch) | |
tree | 435b840fcf7750eb189af390cbc7812ea56eee17 | |
parent | 35642255034ed82fe39c2324e7851a19af87681d (diff) | |
download | contexthub-o-mr1-iot-preview-7.tar.gz |
nanohub: i2c: detect and clock through stuck low sdaandroid-wear-8.0.0_r1android-p-preview-1android-o-mr1-iot-preview-8android-o-mr1-iot-preview-7android-n-iot-release-lg-thinq-wk7o-mr1-iot-preview-8o-mr1-iot-preview-7
If the chip is reset in the middle of an i2c read, the
slave will still be waiting for additional pulses of sdc.
This prevents all chips on the stuck i2c bus from being
detected until a power cycle.
Detect that sda is stuck low and attempt to clock through
the issue.
Bug: 65966547
Test: run accel at a high rate over i2c and reset the stm32.
Confirm all chips on i2c are detected after reset.
Change-Id: I3a7ec24556a693b5506ba65f4eefa6e9f36e5818
Signed-off-by: Ben Fennema <fennema@google.com>
-rw-r--r-- | firmware/os/core/timer.c | 8 | ||||
-rw-r--r-- | firmware/os/inc/timer.h | 1 | ||||
-rw-r--r-- | firmware/os/platform/stm32/i2c.c | 46 |
3 files changed, 55 insertions, 0 deletions
diff --git a/firmware/os/core/timer.c b/firmware/os/core/timer.c index ace80723..f9c33523 100644 --- a/firmware/os/core/timer.c +++ b/firmware/os/core/timer.c @@ -62,6 +62,14 @@ uint64_t timGetTime(void) 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 abc5e19f..30e359a2 100644 --- a/firmware/os/inc/timer.h +++ b/firmware/os/inc/timer.h @@ -38,6 +38,7 @@ typedef void (*TimTimerCbkF)(uint32_t timerId, void* data); 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 189e8f9d..7ba7b7ce 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 @@ static inline struct Gpio* stmI2cGpioInit(const struct StmI2cBoardCfg *board, co 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 @@ int i2cMasterRequest(uint32_t busId, uint32_t speed) pdev->last = 1; atomicBitsetInit(mXfersValid, I2C_MAX_QUEUE_DEPTH); + i2cMasterReset(busId, speed); + pdev->scl = stmI2cGpioInit(board, &board->gpioScl); pdev->sda = stmI2cGpioInit(board, &board->gpioSda); |