diff options
-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) |