Browse Source

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 2066b8c7b9)
backport-88631-to-v4.1-branch
Jean Nanchen 1 month ago committed by github-actions[bot]
parent
commit
60ff81522f
  1. 41
      drivers/i2c/i2c_ll_stm32_v2.c

41
drivers/i2c/i2c_ll_stm32_v2.c

@ -745,17 +745,26 @@ static inline int msg_done(const struct device *dev, @@ -745,17 +745,26 @@ static inline int msg_done(const struct device *dev,
{
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 @@ static int stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg, @@ -772,6 +781,7 @@ static int stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg,
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 @@ static int stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg, @@ -785,6 +795,11 @@ static int stm32_i2c_msg_write(const struct device *dev, struct i2c_msg *msg,
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 @@ static int stm32_i2c_msg_read(const struct device *dev, struct i2c_msg *msg, @@ -802,6 +817,7 @@ static int stm32_i2c_msg_read(const struct device *dev, struct i2c_msg *msg,
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 @@ static int stm32_i2c_msg_read(const struct device *dev, struct i2c_msg *msg, @@ -811,6 +827,10 @@ static int stm32_i2c_msg_read(const struct device *dev, struct i2c_msg *msg,
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 @@ int stm32_i2c_transaction(const struct device *dev, @@ -1181,5 +1201,26 @@ int stm32_i2c_transaction(const struct device *dev,
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;
}

Loading…
Cancel
Save