summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 */