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>
This commit is contained in:
Randall Spangler
2012-11-20 18:31:54 -08:00
committed by Gerrit
parent 3c575ccb02
commit e2f8466997
2 changed files with 43 additions and 5 deletions

View File

@@ -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;
}

View File

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