From dbb676ab2b029af619929dd46987ab1244dc4787 Mon Sep 17 00:00:00 2001 From: Khaoula Bidani Date: Mon, 15 Sep 2025 09:17:54 +0200 Subject: [PATCH 1/2] drivers: i2c: Support target error callbacks Support I2C target error callback in I2C driver when registered by the bus target consumer. Signed-off-by: Khaoula Bidani Signed-off-by: Julien Racki --- include/zephyr/drivers/i2c.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/include/zephyr/drivers/i2c.h b/include/zephyr/drivers/i2c.h index 3e3c104a291da..28fb25e08cb1f 100644 --- a/include/zephyr/drivers/i2c.h +++ b/include/zephyr/drivers/i2c.h @@ -414,6 +414,34 @@ typedef int (*i2c_target_buf_read_requested_cb_t)( */ typedef int (*i2c_target_stop_cb_t)(struct i2c_target_config *config); +/** + * @brief I2C error reasons. + * + * Values that correspond to events or errors responsible for stopping + * an I2C transfer. + */ +enum i2c_error_reason { + I2C_ERROR_TIMEOUT = 0, /* Timeout error */ + I2C_ERROR_ARBITRATION, /* Bus arbitration size */ + I2C_ERROR_SIZE, /* Bad frame size */ + I2C_ERROR_DMA, /* DMA transfer error */ + I2C_ERROR_GENERIC, /* Any other bus error */ +}; + +/** @brief Function called when an error is detected on the I2C bus + * while acting as a target. + * + * This function is invoked by the controller when a bus error, + * arbitration lost, or other critical error is detected during + * a transaction addressed to this device. + * + * @param config the configuration structure associated with the + * device to which the operation is addressed. + * @param error_code an integer code identifying the error type. + */ +typedef void (*i2c_target_error_cb_t)(struct i2c_target_config *config, + enum i2c_error_reason error_code); + /** @brief Structure providing callbacks to be implemented for devices * that supports the I2C target API. * @@ -430,6 +458,7 @@ struct i2c_target_callbacks { i2c_target_buf_read_requested_cb_t buf_read_requested; #endif i2c_target_stop_cb_t stop; + i2c_target_error_cb_t error; }; /** @brief Structure describing a device that supports the I2C From d20a3b12bae50ebcfb6a28eb79d21e5310b4d509 Mon Sep 17 00:00:00 2001 From: Khaoula Bidani Date: Mon, 15 Sep 2025 09:16:32 +0200 Subject: [PATCH 2/2] drivers: i2c: stm32: add target error callback Allow applications to be notified of target I2C transfer error usingthe recently added target error callback mechanism. Signed-off-by: Khaoula Bidani Signed-off-by: Julien Racki --- drivers/i2c/i2c_ll_stm32_v1.c | 39 +++++++++++++++++++++++++++--- drivers/i2c/i2c_ll_stm32_v1_rtio.c | 28 ++++++++++++++++++--- drivers/i2c/i2c_ll_stm32_v2.c | 24 +++++++++++++++--- drivers/i2c/i2c_ll_stm32_v2_rtio.c | 19 ++++++++++++--- 4 files changed, 98 insertions(+), 12 deletions(-) diff --git a/drivers/i2c/i2c_ll_stm32_v1.c b/drivers/i2c/i2c_ll_stm32_v1.c index 5ae79b762592d..63e33e8fa9260 100644 --- a/drivers/i2c/i2c_ll_stm32_v1.c +++ b/drivers/i2c/i2c_ll_stm32_v1.c @@ -597,9 +597,11 @@ int i2c_stm32_error(const struct device *dev) I2C_TypeDef *i2c = cfg->i2c; #if defined(CONFIG_I2C_TARGET) - if (data->slave_attached && !data->master_active) { - /* No need for a slave error function right now. */ - return 0; + i2c_target_error_cb_t error_cb = NULL; + + if (data->slave_attached && !data->master_active && + data->slave_cfg != NULL && data->slave_cfg->callbacks != NULL) { + error_cb = data->slave_cfg->callbacks->error; } #endif @@ -607,17 +609,42 @@ int i2c_stm32_error(const struct device *dev) LL_I2C_ClearFlag_AF(i2c); LL_I2C_GenerateStopCondition(i2c); data->current.is_nack = 1U; +#if defined(CONFIG_I2C_TARGET) + if (error_cb != NULL) { + error_cb(data->slave_cfg, I2C_ERROR_GENERIC); + } +#endif goto end; } if (LL_I2C_IsActiveFlag_ARLO(i2c)) { LL_I2C_ClearFlag_ARLO(i2c); data->current.is_arlo = 1U; +#if defined(CONFIG_I2C_TARGET) + if (error_cb != NULL) { + error_cb(data->slave_cfg, I2C_ERROR_ARBITRATION); + } +#endif goto end; } if (LL_I2C_IsActiveFlag_BERR(i2c)) { LL_I2C_ClearFlag_BERR(i2c); data->current.is_err = 1U; +#if defined(CONFIG_I2C_TARGET) + if (error_cb != NULL) { + error_cb(data->slave_cfg, I2C_ERROR_GENERIC); + } +#endif + goto end; + } + + if (LL_I2C_IsActiveFlag_OVR(i2c)) { + LL_I2C_ClearFlag_OVR(i2c); +#if defined(CONFIG_I2C_TARGET) + if (error_cb != NULL) { + error_cb(data->slave_cfg, I2C_ERROR_GENERIC); + } +#endif goto end; } @@ -632,7 +659,13 @@ int i2c_stm32_error(const struct device *dev) #endif return 0; end: +#if defined(CONFIG_I2C_TARGET) + if (!data->slave_attached || data->master_active) { + i2c_stm32_master_mode_end(dev); + } +#else i2c_stm32_master_mode_end(dev); +#endif return -EIO; } diff --git a/drivers/i2c/i2c_ll_stm32_v1_rtio.c b/drivers/i2c/i2c_ll_stm32_v1_rtio.c index d07c40bbcd4b5..211c88245a888 100644 --- a/drivers/i2c/i2c_ll_stm32_v1_rtio.c +++ b/drivers/i2c/i2c_ll_stm32_v1_rtio.c @@ -452,31 +452,53 @@ int i2c_stm32_error(const struct device *dev) #if defined(CONFIG_I2C_TARGET) struct i2c_stm32_data *data = dev->data; + i2c_target_error_cb_t error_cb = NULL; - if (data->slave_attached && !data->master_active) { - /* No need for a target error function right now. */ - return 0; + if (data->slave_attached && !data->master_active && + data->slave_cfg != NULL && data->slave_cfg->callbacks != NULL) { + error_cb = data->slave_cfg->callbacks->error; } #endif if (LL_I2C_IsActiveFlag_AF(i2c)) { LL_I2C_ClearFlag_AF(i2c); LL_I2C_GenerateStopCondition(i2c); +#if defined(CONFIG_I2C_TARGET) + if (error_cb != NULL) { + error_cb(data->slave_cfg, I2C_ERROR_GENERIC); + } +#endif goto error; } if (LL_I2C_IsActiveFlag_ARLO(i2c)) { LL_I2C_ClearFlag_ARLO(i2c); +#if defined(CONFIG_I2C_TARGET) + if (error_cb != NULL) { + error_cb(data->slave_cfg, I2C_ERROR_ARBITRATION); + } +#endif goto error; } if (LL_I2C_IsActiveFlag_BERR(i2c)) { LL_I2C_ClearFlag_BERR(i2c); +#if defined(CONFIG_I2C_TARGET) + if (error_cb != NULL) { + error_cb(data->slave_cfg, I2C_ERROR_GENERIC); + } +#endif goto error; } return 0; error: +#if defined(CONFIG_I2C_TARGET) + if (!data->slave_attached || data->master_active) { + i2c_stm32_master_mode_end(dev, -EIO); + } +#else i2c_stm32_master_mode_end(dev, -EIO); +#endif return -EIO; } diff --git a/drivers/i2c/i2c_ll_stm32_v2.c b/drivers/i2c/i2c_ll_stm32_v2.c index 98b1fd256b023..bbcdcb2881827 100644 --- a/drivers/i2c/i2c_ll_stm32_v2.c +++ b/drivers/i2c/i2c_ll_stm32_v2.c @@ -593,15 +593,22 @@ int i2c_stm32_error(const struct device *dev) I2C_TypeDef *i2c = cfg->i2c; #if defined(CONFIG_I2C_TARGET) - if (data->slave_attached && !data->master_active) { - /* No need for a slave error function right now. */ - return 0; + i2c_target_error_cb_t error_cb = NULL; + + if (data->slave_attached && !data->master_active && + data->slave_cfg != NULL && data->slave_cfg->callbacks != NULL) { + error_cb = data->slave_cfg->callbacks->error; } #endif if (LL_I2C_IsActiveFlag_ARLO(i2c)) { LL_I2C_ClearFlag_ARLO(i2c); data->current.is_arlo = 1U; +#if defined(CONFIG_I2C_TARGET) + if (error_cb != NULL) { + error_cb(data->slave_cfg, I2C_ERROR_ARBITRATION); + } +#endif goto end; } @@ -613,6 +620,12 @@ int i2c_stm32_error(const struct device *dev) if (LL_I2C_IsActiveFlag_BERR(i2c)) { LL_I2C_ClearFlag_BERR(i2c); data->current.is_err = 1U; +#if defined(CONFIG_I2C_TARGET) + if (error_cb != NULL) { + error_cb(data->slave_cfg, I2C_ERROR_GENERIC); + } +#endif + goto end; } #if defined(CONFIG_SMBUS_STM32_SMBALERT) @@ -627,6 +640,11 @@ int i2c_stm32_error(const struct device *dev) return 0; end: +#if defined(CONFIG_I2C_TARGET) + if (data->slave_attached && !data->master_active) { + return -EIO; + } +#endif i2c_stm32_disable_transfer_interrupts(dev); /* Wakeup thread */ k_sem_give(&data->device_sync_sem); diff --git a/drivers/i2c/i2c_ll_stm32_v2_rtio.c b/drivers/i2c/i2c_ll_stm32_v2_rtio.c index 269288055ece8..d8c9a6c00dca5 100644 --- a/drivers/i2c/i2c_ll_stm32_v2_rtio.c +++ b/drivers/i2c/i2c_ll_stm32_v2_rtio.c @@ -428,17 +428,30 @@ int i2c_stm32_error(const struct device *dev) int ret = 0; #if defined(CONFIG_I2C_TARGET) - if (data->slave_attached && !data->master_active) { - /* No need for a target error function right now. */ - return 0; + i2c_target_error_cb_t error_cb = NULL; + + if (data->slave_attached && !data->master_active && + data->slave_cfg != NULL && data->slave_cfg->callbacks != NULL) { + error_cb = data->slave_cfg->callbacks->error; } #endif if (LL_I2C_IsActiveFlag_ARLO(i2c)) { LL_I2C_ClearFlag_ARLO(i2c); +#if defined(CONFIG_I2C_TARGET) + if (error_cb != NULL) { + error_cb(data->slave_cfg, I2C_ERROR_ARBITRATION); + } +#endif ret = -EIO; } +#if defined(CONFIG_I2C_TARGET) + if (data->slave_attached && !data->master_active) { + return ret; + } +#endif + if (ret) { i2c_stm32_master_mode_end(dev); if (i2c_rtio_complete(ctx, ret)) {