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;
}