diff options
Diffstat (limited to 'chip')
-rw-r--r-- | chip/stm32/i2c.c | 40 |
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 */ |