Skip to content

[stm32-v2] Multi-master deadlock: master_active set before START blocks target IRQs during BUSY wait, causing bus hang #99074

@AydinyanNarek

Description

@AydinyanNarek

Bug description

On STM32 I²C v2 in multi-master setups (device configured for both master + target), the Zephyr driver sets data->master_active = true in msg_init() before issuing a START and before the bus is confirmed idle. If the bus is still BUSY (e.g., peer master performs immediate repeated-STARTs), target IRQ handling is suppressed by the slave_attached && !master_active check; the local device fails to ACK as a target, the peer stalls with BUSY held, and our master side spins waiting for idle → deadlock / bus hang.

File: drivers/i2c/i2c_ll_stm32_v2.c
Function: msg_init()

Bug Fix:

#define STM32_I2C_ARLO_TIMEOUT_USEC  	 2000

static inline int wait_bus_idle(const struct device *dev, uint32_t timeout_us)
{
    const struct i2c_stm32_config *cfg = dev->config;
    I2C_TypeDef *i2c = cfg->i2c;
    uint32_t start = k_cycle_get_32();

    /* Wait for BUSY=0 (bus idle) */
    while (LL_I2C_IsActiveFlag_BUSY(i2c)) {
        if (k_cyc_to_us_near32(k_cycle_get_32() - start) > timeout_us) {
            return -EBUSY;
        }
    }
    return 0;
}

static inline void msg_init(const struct device *dev, struct i2c_msg *msg,
			    uint8_t *next_msg_flags, uint16_t slave,
			    uint32_t transfer)
{
	const struct i2c_stm32_config *cfg = dev->config;
	struct i2c_stm32_data *data = dev->data;
	I2C_TypeDef *i2c = cfg->i2c;

	if (LL_I2C_IsEnabledReloadMode(i2c)) {
		LL_I2C_SetTransferSize(i2c, msg->len);
	} else {
		if (I2C_ADDR_10_BITS & data->dev_config) {
			LL_I2C_SetMasterAddressingMode(i2c,
					LL_I2C_ADDRESSING_MODE_10BIT);
			LL_I2C_SetSlaveAddr(i2c, (uint32_t) slave);
		} else {
			LL_I2C_SetMasterAddressingMode(i2c,
				LL_I2C_ADDRESSING_MODE_7BIT);
			LL_I2C_SetSlaveAddr(i2c, (uint32_t) slave << 1);
		}

		if (!(msg->flags & I2C_MSG_STOP) && next_msg_flags &&
		    !(*next_msg_flags & I2C_MSG_RESTART)) {
			LL_I2C_EnableReloadMode(i2c);
		} else {
			LL_I2C_DisableReloadMode(i2c);
		}
		LL_I2C_DisableAutoEndMode(i2c);
		LL_I2C_SetTransferRequest(i2c, transfer);
		LL_I2C_SetTransferSize(i2c, msg->len);

		LL_I2C_Enable(i2c);

        /*wait for real bus idle BEFORE declaring master_active */
        if (wait_bus_idle(dev, STM32_I2C_ARLO_TIMEOUT_USEC) != 0) {
            /* Make sure we do NOT look like a master while we back out */
#if defined(CONFIG_I2C_TARGET)
            data->master_active = false;
#endif
            return;
        }

#if defined(CONFIG_I2C_TARGET)
        data->master_active = true;   /* <<< set only now */
#endif

		LL_I2C_GenerateStartCondition(i2c);
	}
}
Image Image Image

Regression

  • This is a regression.

Steps to reproduce

No response

Relevant log output

Impact

Showstopper – Prevents release or major functionality; system unusable.

Environment

Zephyr SDK, STM32

Additional Context

No response

Metadata

Metadata

Assignees

Labels

bugThe issue is a bug, or the PR is fixing a bugplatform: STM32ST Micro STM32

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions