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>
This commit is contained in:
Randall Spangler
2012-11-26 16:43:57 -08:00
committed by Gerrit
parent e6bf4533c2
commit d619cdd58f
2 changed files with 23 additions and 10 deletions

View File

@@ -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))) {
/*
* Previous clock timeout or bus-busy. Bounce the master to
* clear these error states.
*/
LM4_I2C_MCR(port) = 0;
usleep(100000);
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;
/*
* We don't know what edges the slave saw, so sleep long enough
* that the slave will see the new start condition below.
*/
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;

View File

@@ -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)