Merge branch 'fix/i2c_race_condition' into 'master'

fix(i2c_master): Fix i2c master race condition issue

Closes IDFGH-14704

See merge request espressif/esp-idf!37355
This commit is contained in:
Gao Xu 2025-02-28 17:38:10 +08:00
commit 9a917658a6

View File

@ -331,7 +331,7 @@ static bool s_i2c_read_command(i2c_master_bus_handle_t i2c_master, i2c_operation
i2c_ll_master_write_cmd_reg(hal->dev, hw_cmd, i2c_master->cmd_idx);
i2c_ll_master_write_cmd_reg(hal->dev, hw_end_cmd, i2c_master->cmd_idx + 1);
portEXIT_CRITICAL_SAFE(&handle->spinlock);
i2c_master->status = I2C_STATUS_READ;
atomic_store(&i2c_master->status, I2C_STATUS_READ);
portENTER_CRITICAL_SAFE(&handle->spinlock);
if (i2c_master->async_trans == false) {
i2c_hal_master_trans_start(hal);
@ -472,15 +472,15 @@ static void s_i2c_send_commands(i2c_master_bus_handle_t i2c_master, TickType_t t
while (i2c_master->i2c_trans.cmd_count) {
if (xSemaphoreTake(i2c_master->cmd_semphr, ticks_to_wait) != pdTRUE) {
// Software timeout, clear the command link and finish this transaction.
atomic_store(&i2c_master->status, I2C_STATUS_TIMEOUT);
i2c_master->cmd_idx = 0;
i2c_master->trans_idx = 0;
atomic_store(&i2c_master->status, I2C_STATUS_TIMEOUT);
ESP_LOGE(TAG, "I2C software timeout");
xSemaphoreGive(i2c_master->cmd_semphr);
return;
}
if (i2c_master->status == I2C_STATUS_TIMEOUT) {
if (atomic_load(&i2c_master->status) == I2C_STATUS_TIMEOUT) {
s_i2c_hw_fsm_reset(i2c_master);
i2c_master->cmd_idx = 0;
i2c_master->trans_idx = 0;
@ -489,7 +489,7 @@ static void s_i2c_send_commands(i2c_master_bus_handle_t i2c_master, TickType_t t
return;
}
if (i2c_master->status == I2C_STATUS_ACK_ERROR) {
if (atomic_load(&i2c_master->status) == I2C_STATUS_ACK_ERROR) {
ESP_LOGE(TAG, "I2C hardware NACK detected");
const i2c_ll_hw_cmd_t hw_stop_cmd = {
.op_code = I2C_LL_CMD_STOP,
@ -596,7 +596,7 @@ static esp_err_t s_i2c_transaction_start(i2c_master_dev_handle_t i2c_dev, int xf
TickType_t ticks_to_wait = (xfer_timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(xfer_timeout_ms);
// Sometimes when the FSM get stuck, the ACK_ERR interrupt will occur endlessly until we reset the FSM and clear bus.
esp_err_t ret = ESP_OK;
if (i2c_master->status == I2C_STATUS_TIMEOUT || i2c_ll_is_bus_busy(hal->dev)) {
if (atomic_load(&i2c_master->status) == I2C_STATUS_TIMEOUT || i2c_ll_is_bus_busy(hal->dev)) {
ESP_RETURN_ON_ERROR(s_i2c_hw_fsm_reset(i2c_master), TAG, "reset hardware failed");
}
@ -630,7 +630,7 @@ static esp_err_t s_i2c_transaction_start(i2c_master_dev_handle_t i2c_dev, int xf
} else {
s_i2c_send_commands(i2c_master, ticks_to_wait);
// Wait event bits
if (i2c_master->status != I2C_STATUS_DONE) {
if (atomic_load(&i2c_master->status) != I2C_STATUS_DONE) {
ret = ESP_ERR_INVALID_STATE;
}
// Interrupt can be disabled when on transaction finishes.
@ -650,7 +650,7 @@ I2C_MASTER_ISR_ATTR static void i2c_isr_receive_handler(i2c_master_bus_t *i2c_ma
{
i2c_hal_context_t *hal = &i2c_master->base->hal;
if (i2c_master->status == I2C_STATUS_READ) {
if (atomic_load(&i2c_master->status) == I2C_STATUS_READ) {
i2c_operation_t *i2c_operation = &i2c_master->i2c_trans.ops[i2c_master->trans_idx];
portENTER_CRITICAL_ISR(&i2c_master->base->spinlock);
i2c_ll_read_rxfifo(hal->dev, i2c_operation->data + i2c_operation->bytes_used, i2c_master->rx_cnt);