summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-11-20 18:31:54 -0800
committerGerrit <chrome-bot@google.com>2012-11-26 14:49:49 -0800
commite2f84669979f63df4a11d78caee9abf3e9b1724a (patch)
tree421b42c02cd9b30eb69db6feb9070dfae21e6e29
parent3c575ccb02ff58eaae3e7c3dd8e2e86ad25ac106 (diff)
downloadchrome-ec-e2f84669979f63df4a11d78caee9abf3e9b1724a.tar.gz
Handle bus errors on thermal I2C bus
1) Properly report I2C errors on TMP006 as error, not device-not-powered. 2) Treat clock timeout and bus-busy I2C status as error (previously ignored). 3) If clock timeout or bus-busy, reset I2C master for that bus to clear the error. These should help with systems where the thermal I2C bus gets into a weird state on suspend/resume. 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. Change-Id: I534be8236a4d6de82575fe6d33a68502ce0a3a95 Original-Change-Id: Iec5d6bbd357d2e5eb3dc3d361c829f353e996ab6 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/38444 Reviewed-on: https://gerrit.chromium.org/gerrit/38659 Reviewed-by: Yung-Chieh Lo <yjlou@chromium.org>
-rw-r--r--chip/lm4/i2c.c31
-rw-r--r--common/tmp006.c17
2 files changed, 43 insertions, 5 deletions
diff --git a/chip/lm4/i2c.c b/chip/lm4/i2c.c
index 7f0fa19c95..4a978e4a39 100644
--- a/chip/lm4/i2c.c
+++ b/chip/lm4/i2c.c
@@ -21,6 +21,7 @@
#define NUM_PORTS 6 /* Number of physical ports */
+/* Flags for writes to MCS */
#define LM4_I2C_MCS_RUN (1 << 0)
#define LM4_I2C_MCS_START (1 << 1)
#define LM4_I2C_MCS_STOP (1 << 2)
@@ -28,6 +29,16 @@
#define LM4_I2C_MCS_HS (1 << 4)
#define LM4_I2C_MCS_QCMD (1 << 5)
+/* Flags for reads from MCS */
+#define LM4_I2C_MCS_BUSY (1 << 0)
+#define LM4_I2C_MCS_ERROR (1 << 1)
+#define LM4_I2C_MCS_ADRACK (1 << 2)
+#define LM4_I2C_MCS_DATACK (1 << 3)
+#define LM4_I2C_MCS_ARBLST (1 << 4)
+#define LM4_I2C_MCS_IDLE (1 << 5)
+#define LM4_I2C_MCS_BUSBSY (1 << 6)
+#define LM4_I2C_MCS_CLKTO (1 << 7)
+
#define START 1
#define STOP 1
#define NO_START 0
@@ -49,7 +60,7 @@ static int wait_idle(int port)
int event = 0;
i = LM4_I2C_MCS(port);
- while (i & 0x01) {
+ while (i & LM4_I2C_MCS_BUSY) {
/* Port is busy, so wait for the interrupt */
task_waiting_on_port[port] = task_get_current();
LM4_I2C_MIMR(port) = 0x03;
@@ -81,7 +92,7 @@ static int wait_idle(int port)
task_set_event(task_get_current(), event, 0);
/* Check for errors */
- if (i & 0x02)
+ if (i & LM4_I2C_MCS_ERROR)
return EC_ERROR_UNKNOWN;
return EC_SUCCESS;
@@ -110,6 +121,17 @@ 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);
+ LM4_I2C_MCR(port) = 0x10;
+ }
+
if (out) {
LM4_I2C_MSA(port) = slave_addr & 0xff;
for (i = 0; i < out_size; i++) {
@@ -175,6 +197,11 @@ 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 |
+ LM4_I2C_MCS_ERROR))
+ return EC_ERROR_UNKNOWN;
+
return EC_SUCCESS;
}
diff --git a/common/tmp006.c b/common/tmp006.c
index b4a1a8b967..3cda55ac1a 100644
--- a/common/tmp006.c
+++ b/common/tmp006.c
@@ -169,7 +169,10 @@ static int tmp006_poll_sensor(int sensor_id)
*/
if (tdata->fail && (FAIL_POWER | FAIL_INIT)) {
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x02, &v);
- if (!(v & 0x80)) {
+ if (rv) {
+ tdata->fail |= FAIL_I2C;
+ return EC_ERROR_UNKNOWN;
+ } else if (!(v & 0x80)) {
tdata->fail |= FAIL_NOT_READY;
return EC_ERROR_UNKNOWN;
}
@@ -222,8 +225,16 @@ int tmp006_get_val(int idx, int *temp_ptr)
int tidx = idx >> 1;
const struct tmp006_data_t *tdata = tmp006_data + tidx;
- if (tdata->fail & FAIL_POWER)
- return EC_ERROR_NOT_POWERED;
+ if (tdata->fail & FAIL_POWER) {
+ /*
+ * Sensor isn't powered, or hasn't successfully provided data
+ * since being powered. Keep reporting not-powered until
+ * we get good data (which will clear FAIL_POWER) or there is
+ * an I2C error.
+ */
+ return (tdata->fail & FAIL_I2C) ? EC_ERROR_UNKNOWN :
+ EC_ERROR_NOT_POWERED;
+ }
/* Check the low bit to determine which temperature to read. */
if ((idx & 0x1) == 0)