drivers: i2c: stm32: add timeout to avoid infinite loop Fix issue where STM32 I2C LL driver could block forever when SDA and SCL are shorted and interrupts are disabled (CONFIG_I2C_STM32_INTERRUPT=n). Added timeouts to all blocking wait loops in the STM32 LL I2C driver to avoid indefinite blocking. Fixes #88506 Signed-off-by: Jean Nanchen <jean.nanchen@hevs.ch> (cherry picked from commit 2066b8c7b9b5b149d6395a57282202c534f4eb8b)
diff --git a/drivers/i2c/i2c_ll_stm32_v2.c b/drivers/i2c/i2c_ll_stm32_v2.c index 8e97fff..e4ec27d 100644 --- a/drivers/i2c/i2c_ll_stm32_v2.c +++ b/drivers/i2c/i2c_ll_stm32_v2.c
@@ -745,17 +745,26 @@ { const struct i2c_stm32_config *cfg = dev->config; I2C_TypeDef *i2c = cfg->i2c; + int64_t start_time = k_uptime_get(); /* Wait for transfer to complete */ while (!LL_I2C_IsActiveFlag_TC(i2c) && !LL_I2C_IsActiveFlag_TCR(i2c)) { if (check_errors(dev, __func__)) { return -EIO; } + if ((k_uptime_get() - start_time) > + STM32_I2C_TRANSFER_TIMEOUT_MSEC) { + return -ETIMEDOUT; + } } /* Issue stop condition if necessary */ if (current_msg_flags & I2C_MSG_STOP) { LL_I2C_GenerateStopCondition(i2c); while (!LL_I2C_IsActiveFlag_STOP(i2c)) { + if ((k_uptime_get() - start_time) > + STM32_I2C_TRANSFER_TIMEOUT_MSEC) { + return -ETIMEDOUT; + } } LL_I2C_ClearFlag_STOP(i2c); @@ -772,6 +781,7 @@ I2C_TypeDef *i2c = cfg->i2c; unsigned int len = 0U; uint8_t *buf = msg->buf; + int64_t start_time = k_uptime_get(); msg_init(dev, msg, next_msg_flags, slave, LL_I2C_REQUEST_WRITE); @@ -785,6 +795,11 @@ if (check_errors(dev, __func__)) { return -EIO; } + + if ((k_uptime_get() - start_time) > + STM32_I2C_TRANSFER_TIMEOUT_MSEC) { + return -ETIMEDOUT; + } } LL_I2C_TransmitData8(i2c, *buf); @@ -802,6 +817,7 @@ I2C_TypeDef *i2c = cfg->i2c; unsigned int len = 0U; uint8_t *buf = msg->buf; + int64_t start_time = k_uptime_get(); msg_init(dev, msg, next_msg_flags, slave, LL_I2C_REQUEST_READ); @@ -811,6 +827,10 @@ if (check_errors(dev, __func__)) { return -EIO; } + if ((k_uptime_get() - start_time) > + STM32_I2C_TRANSFER_TIMEOUT_MSEC) { + return -ETIMEDOUT; + } } *buf = LL_I2C_ReceiveData8(i2c); @@ -1181,5 +1201,26 @@ msg.len = rest; } while (rest > 0U); +#ifndef CONFIG_I2C_STM32_INTERRUPT + struct i2c_stm32_data *data = dev->data; + + if (ret == -ETIMEDOUT) { + if (LL_I2C_IsEnabledReloadMode(i2c)) { + LL_I2C_DisableReloadMode(i2c); + } +#if defined(CONFIG_I2C_TARGET) + data->master_active = false; + if (!data->slave_attached && !data->smbalert_active) { + LL_I2C_Disable(i2c); + } +#else + if (!data->smbalert_active) { + LL_I2C_Disable(i2c); + } +#endif + return -EIO; + } +#endif /* !CONFIG_I2C_STM32_INTERRUPT */ + return ret; }