diff options
author | Vincent Palatin <vpalatin@chromium.org> | 2012-05-22 23:13:29 +0000 |
---|---|---|
committer | Vincent Palatin <vpalatin@chromium.org> | 2012-05-23 00:52:36 +0000 |
commit | d9954b304a5146378e78003fb095c2842d6b2ad3 (patch) | |
tree | 8c3a957043ea8b416906af1798d6cddf6172f553 | |
parent | 8b468a8a269a79b6af075c4872649524ff6841a0 (diff) | |
download | chrome-ec-d9954b304a5146378e78003fb095c2842d6b2ad3.tar.gz |
stm32: try to be more robust against malformed commands
Timeout properly when the AP doesn't want our bytes.
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
BUG=chrome-os-partner:8865
TEST=On Snow, boot with an old kernel.
Change-Id: Iac4fa5c3606f2e8731927326fad291dae26a615c
-rw-r--r-- | chip/stm32/i2c.c | 27 |
1 files changed, 21 insertions, 6 deletions
diff --git a/chip/stm32/i2c.c b/chip/stm32/i2c.c index e1018ed31b..d4df0648ee 100644 --- a/chip/stm32/i2c.c +++ b/chip/stm32/i2c.c @@ -13,6 +13,7 @@ #include "i2c.h" #include "registers.h" #include "task.h" +#include "timer.h" #include "util.h" /* Console output macros */ @@ -28,6 +29,9 @@ /* Clock divider for I2C controller */ #define I2C_CCR (CPU_CLOCK/(2 * I2C_FREQ)) +/* Transmit timeout in microseconds */ +#define I2C_TX_TIMEOUT 10000 /* us */ + #define NUM_PORTS 2 #define I2C1 STM32_I2C1_PORT #define I2C2 STM32_I2C2_PORT @@ -41,12 +45,19 @@ static uint8_t host_buffer[EC_PARAM_SIZE + 2]; /* current position in host buffer for reception */ static int rx_index; +/* indicates if a wait loop should abort */ +static volatile int abort_transaction; -static void wait_tx(int port) +static int wait_tx(int port) { - /* TODO: Add timeouts and error checking for safety */ - while (!(STM32_I2C_SR1(port) & (1 << 7))) + static timestamp_t deadline; + + deadline.val = get_time().val + I2C_TX_TIMEOUT; + /* wait for TxE or errors (Timeout, STOP, BERR, AF) */ + while (!(STM32_I2C_SR1(port) & (1<<7)) && !abort_transaction && + (get_time().val < deadline.val)) ; + return !(STM32_I2C_SR1(port) & (1 << 7)); } static int i2c_write_raw(int port, void *buf, int len) @@ -54,9 +65,13 @@ static int i2c_write_raw(int port, void *buf, int len) int i; uint8_t *data = buf; + abort_transaction = 0; for (i = 0; i < len; i++) { STM32_I2C_DR(port) = data[i]; - wait_tx(port); + if (wait_tx(port)) { + CPRINTF("TX failed\n"); + break; + } } return len; @@ -117,6 +132,7 @@ static void i2c_event_handler(int port) STM32_I2C_SR1(port); STM32_I2C_SR2(port); } else if (i2c_sr1[port] & (1 << 4)) { + abort_transaction = 1; /* clear STOPF bit by reading SR1 and then writing CR1 */ STM32_I2C_SR1(port); STM32_I2C_CR1(port) = STM32_I2C_CR1(port); @@ -154,17 +170,16 @@ static void i2c_error_handler(int port) { i2c_sr1[port] = STM32_I2C_SR1(port); -#ifdef CONFIG_DEBUG if (i2c_sr1[port] & 1 << 10) { /* ACK failed (NACK); expected when AP reads final byte. * Software must clear AF bit. */ } else { + abort_transaction = 1; CPRINTF("%s: I2C_SR1(%s): 0x%04x\n", __func__, port, i2c_sr1[port]); CPRINTF("%s: I2C_SR2(%s): 0x%04x\n", __func__, port, STM32_I2C_SR2(port)); } -#endif STM32_I2C_SR1(port) &= ~0xdf00; } |