summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlie Mooney <charliemooney@chromium.org>2012-09-04 15:33:54 -0700
committerGerrit <chrome-bot@google.com>2012-09-05 10:29:58 -0700
commita8e006c86cfb93e236073d559c8900b8e83ad783 (patch)
treec3d20e27eabc6e9d505abd50bbc96ecbd9aaec34
parent46ecb0913b39a8d1f57438c4311c031a252ae062 (diff)
downloadchrome-ec-a8e006c86cfb93e236073d559c8900b8e83ad783.tar.gz
Snow: Add checking for more i2c error cases
There are a number of ways for the i2c to fail, and some are quite rare and have thus been overlooked. It's easy enough to handle these rationally, but we have to check for them. This checks that the i2c peripheral is actually in slave mode when it gets a slave event firing (stopping it from accidentally sending garbage on the tail end of another request) and makes sure a STOP bit is sent in the event that the BUSY signal isn't set at the moment we check it (if we check it at the moment that it is sending a 1, it may not be set). Finally, if the i2c can't send a STOP bit, the peripheral is reset to get it back to a sane state, specifically it needs to not be stuck in master mode forever. BUG=chrome-os-partner:13380 TEST=Boot machine normally, from AP run "while true; do ectool version; done" to start a loop of the long transaction that sends lots of spurious reads too. Then on the EC, run "pmu 10000" and then "battery 1000" to stress the bus from all sides. Once the EC is done, stop the AP's side of the stress test, and make sure the bus is still functioning. Tested the resetting, by making it reset the peripheral every 150 times, and confirmed that the following transfers work just fine. BRANCH=snow Change-Id: I265b3cddd25e1fd6ab4e8cf9c7290c875fad89f8 Signed-off-by: Charlie Mooney <charliemooney@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/32188 Reviewed-by: Doug Anderson <dianders@chromium.org>
-rw-r--r--chip/stm32/i2c.c40
1 files changed, 31 insertions, 9 deletions
diff --git a/chip/stm32/i2c.c b/chip/stm32/i2c.c
index 7068b9bce5..d2e1fceff4 100644
--- a/chip/stm32/i2c.c
+++ b/chip/stm32/i2c.c
@@ -282,11 +282,16 @@ static void i2c_process_command(void)
static void i2c_event_handler(int port)
{
-
/* save and clear status */
i2c_sr1[port] = STM32_I2C_SR1(port);
STM32_I2C_SR1(port) = 0;
+ /* Confirm that you are not in master mode */
+ if (STM32_I2C_SR2(port) & (1 << 0)) {
+ CPRINTF("I2C slave ISR triggered in master mode, ignoring.\n");
+ return;
+ }
+
/* transfer matched our slave address */
if (i2c_sr1[port] & (1 << 1)) {
/* If it's a receiver slave */
@@ -385,10 +390,6 @@ static int i2c_init2(void)
/* clear status */
STM32_I2C_SR1(I2C2) = 0;
- /* enable event and error interrupts */
- task_enable_irq(STM32_IRQ_I2C2_EV);
- task_enable_irq(STM32_IRQ_I2C2_ER);
-
board_i2c_post_init(I2C2);
CPUTS("done\n");
@@ -417,10 +418,6 @@ static int i2c_init1(void)
/* clear status */
STM32_I2C_SR1(I2C1) = 0;
- /* enable event and error interrupts */
- task_enable_irq(STM32_IRQ_I2C1_EV);
- task_enable_irq(STM32_IRQ_I2C1_ER);
-
board_i2c_post_init(I2C1);
return EC_SUCCESS;
@@ -435,6 +432,14 @@ static int i2c_init(void)
rc |= i2c_init2();
rc |= i2c_init1();
+ /* enable event and error interrupts */
+ if (!rc) {
+ task_enable_irq(STM32_IRQ_I2C1_EV);
+ task_enable_irq(STM32_IRQ_I2C1_ER);
+ task_enable_irq(STM32_IRQ_I2C2_EV);
+ task_enable_irq(STM32_IRQ_I2C2_ER);
+ }
+
return rc;
}
DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_DEFAULT);
@@ -650,10 +655,27 @@ static void handle_i2c_error(int port, int rv)
r = STM32_I2C_SR2(port);
/* Clear busy state */
t1 = get_time();
+
+ /**
+ * If the BUSY bit is faulty, send a stop bit just to be sure. It
+ * seems that this can be happen very briefly while sending a 1.
+ * We've not actually seen this happen, but we just want to be safe.
+ */
+ if (rv == EC_ERROR_TIMEOUT && !(r & 2)) {
+ CPRINTF("Bad BUSY bit detected.\n");
+ master_stop(port);
+ }
+
+ /* Try to send stop bits until the bus becomes idle */
while (r & 2) {
t2 = get_time();
if (t2.val - t1.val > I2C_TX_TIMEOUT_MASTER) {
dump_i2c_reg(port);
+ /* Reset the i2c periph to get it back to slave mode */
+ if (port == I2C1)
+ i2c_init1();
+ else
+ i2c_init2();
goto cr_cleanup;
}
/* Send stop */