summaryrefslogtreecommitdiff
path: root/chip/lm4/i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/lm4/i2c.c')
-rw-r--r--chip/lm4/i2c.c30
1 files changed, 21 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;