summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-11-26 16:43:57 -0800
committerGerrit <chrome-bot@google.com>2012-11-28 09:50:23 -0800
commitd619cdd58f473efe7b8c67e817080fc16751b4a5 (patch)
treeebba31f7cd6903c56c7c1390a5d630cd0ea5035e
parente6bf4533c27d5d5a1ba041a11406248959073dd6 (diff)
downloadchrome-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.c30
-rw-r--r--chip/lm4/registers.h1
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)