diff options
author | Randall Spangler <rspangler@chromium.org> | 2012-11-26 16:43:57 -0800 |
---|---|---|
committer | Gerrit <chrome-bot@google.com> | 2012-11-28 09:50:23 -0800 |
commit | d619cdd58f473efe7b8c67e817080fc16751b4a5 (patch) | |
tree | ebba31f7cd6903c56c7c1390a5d630cd0ea5035e | |
parent | e6bf4533c27d5d5a1ba041a11406248959073dd6 (diff) | |
download | chrome-ec-d619cdd58f473efe7b8c67e817080fc16751b4a5.tar.gz |
Handle arbitration lost on I2C ports
This seems to happen when the I2C signals come up so that the EC sees
a start condition from the remote end. In this case, the EC refuses
to talk on the I2C port until the EC's I2C state machine is reset.
Also, don't fail on bus-busy, since that's true during a multi-part
transaction such as an I2C string read.
BUG=chrome-os-partner:16262
BRANCH=link
TEST=boot system; 'battery' and 'temps' should give good info
Then run snanda's suspend_stress_test for a while and repeat.
Or a better test is to open 2 crosh shells, sudo bash in each, and
1) while true; do ectool temps all; sleep 0.5; done
2) suspend_stress_test
Then watch the EC console for "I2C5 bad status" errors. These happen
rarely, only on some systems. With this fix, they'll be reported when
they occur, but should not cause errors to be reported by 'ectool
temps all', since the I2C module will clear the arbitration-lost
status before retrying.
Change-Id: Idfaf9cd7e8ef2abcc0130332890329dd5d2ca052
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/38686
Reviewed-by: Yung-Chieh Lo <yjlou@chromium.org>
-rw-r--r-- | chip/lm4/i2c.c | 30 | ||||
-rw-r--r-- | chip/lm4/registers.h | 1 |
2 files changed, 22 insertions, 9 deletions
diff --git a/chip/lm4/i2c.c b/chip/lm4/i2c.c index 4a978e4a39..4a47011c15 100644 --- a/chip/lm4/i2c.c +++ b/chip/lm4/i2c.c @@ -92,7 +92,7 @@ static int wait_idle(int port) task_set_event(task_get_current(), event, 0); /* Check for errors */ - if (i & LM4_I2C_MCS_ERROR) + if (i & (LM4_I2C_MCS_CLKTO | LM4_I2C_MCS_ARBLST | LM4_I2C_MCS_ERROR)) return EC_ERROR_UNKNOWN; return EC_SUCCESS; @@ -121,15 +121,27 @@ static int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, if (out_size == 0 && in_size == 0) return EC_SUCCESS; - if (!started && (LM4_I2C_MCS(port) & - (LM4_I2C_MCS_CLKTO | LM4_I2C_MCS_BUSBSY))) { + reg_mcs = LM4_I2C_MCS(port); + if (!started && (reg_mcs & (LM4_I2C_MCS_CLKTO | LM4_I2C_MCS_ARBLST))) { + uint32_t tpr = LM4_I2C_MTPR(port); + + CPRINTF("[%T I2C%d bad status 0x%02x]\n", port, reg_mcs); + + /* Clock timeout or arbitration lost. Reset port to clear. */ + LM4_SYSTEM_SRI2C |= (1 << port); + clock_wait_cycles(3); + LM4_SYSTEM_SRI2C &= ~(1 << port); + clock_wait_cycles(3); + + /* Restore settings */ + LM4_I2C_MCR(port) = 0x10; + LM4_I2C_MTPR(port) = tpr; + /* - * Previous clock timeout or bus-busy. Bounce the master to - * clear these error states. + * We don't know what edges the slave saw, so sleep long enough + * that the slave will see the new start condition below. */ - LM4_I2C_MCR(port) = 0; - usleep(100000); - LM4_I2C_MCR(port) = 0x10; + usleep(1000); } if (out) { @@ -198,7 +210,7 @@ static int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, } /* Check for error conditions */ - if (LM4_I2C_MCS(port) & (LM4_I2C_MCS_CLKTO | LM4_I2C_MCS_BUSBSY | + if (LM4_I2C_MCS(port) & (LM4_I2C_MCS_CLKTO | LM4_I2C_MCS_ARBLST | LM4_I2C_MCS_ERROR)) return EC_ERROR_UNKNOWN; diff --git a/chip/lm4/registers.h b/chip/lm4/registers.h index f351bb2814..1818c73a1d 100644 --- a/chip/lm4/registers.h +++ b/chip/lm4/registers.h @@ -254,6 +254,7 @@ static inline int lm4_fan_addr(int ch, int offset) /* Note: USER_REG3 is used to hold pre-programming process data and should not * be modified by EC code. See crosbug.com/p/8889. */ #define LM4_SYSTEM_USER_REG3 LM4REG(0x400fe1ec) +#define LM4_SYSTEM_SRI2C LM4REG(0x400fe520) #define LM4_SYSTEM_SREEPROM LM4REG(0x400fe558) #define LM4_SYSTEM_RCGCWD LM4REG(0x400fe600) #define LM4_SYSTEM_RCGCTIMER LM4REG(0x400fe604) |