From d1140a5bc5f7497322c89dfb5e1e020418cff36a Mon Sep 17 00:00:00 2001 From: Tero Salminen Date: Mon, 25 Mar 2024 15:59:41 +0200 Subject: [PATCH] mpfs_i2c: fix semaphore race condition MPFS_I2C_ST_STOP_SENT interrupt might be received right after semaphore timeout, which would cause the semaphore to be in the incorrect signaled state when the next transfer starts. Signed-off-by: Tero Salminen --- arch/risc-v/src/mpfs/mpfs_i2c.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/arch/risc-v/src/mpfs/mpfs_i2c.c b/arch/risc-v/src/mpfs/mpfs_i2c.c index 31af4ca7efc6f..4b77552d3ba26 100644 --- a/arch/risc-v/src/mpfs/mpfs_i2c.c +++ b/arch/risc-v/src/mpfs/mpfs_i2c.c @@ -425,6 +425,10 @@ static int mpfs_i2c_init(struct mpfs_i2c_priv_s *priv) putreg32(MPFS_I2C_CTRL_ENS1_MASK, MPFS_I2C_CTRL); +#ifdef CONFIG_MPFS_COREI2C + nxsem_init(&priv->sem_isr, 0, 0); +#endif + priv->initialized = true; } @@ -469,8 +473,24 @@ static void mpfs_i2c_deinit(struct mpfs_i2c_priv_s *priv) static int mpfs_i2c_sem_waitdone(struct mpfs_i2c_priv_s *priv) { + int res = 0; uint32_t timeout = mpfs_i2c_timeout(priv->msgc, priv->msgv); - return nxsem_tickwait_uninterruptible(&priv->sem_isr, USEC2TICK(timeout)); + + res = nxsem_tickwait_uninterruptible(&priv->sem_isr, USEC2TICK(timeout)); + + /* -> race condition <- */ + + priv->inflight = false; + + /* Handle a race condition above in which interrupt MPFS_I2C_ST_STOP_SENT was + * received right after semaphore timeout. In that case semaphore is signalled + * and has the incorrect state when the next transfer starts. + */ + if (res < 0) { + res = nxsem_tickwait_uninterruptible(&priv->sem_isr, USEC2TICK(timeout)); + } + + return res; } /**************************************************************************** @@ -872,7 +892,6 @@ static int mpfs_i2c_transfer(struct i2c_master_s *dev, if (mpfs_i2c_sem_waitdone(priv) < 0) { i2cinfo("Message %" PRIu8 " timed out.\n", priv->msgid); - priv->inflight = false; ret = -ETIMEDOUT; break; } @@ -933,6 +952,8 @@ static int mpfs_i2c_reset(struct i2c_master_s *dev) nxmutex_lock(&priv->lock); + i2cerr("i2c bus %d reset\n", priv->id); + mpfs_i2c_deinit(priv); ret = mpfs_i2c_init(priv); @@ -1138,7 +1159,6 @@ struct i2c_master_s *mpfs_i2cbus_initialize(int port) priv->hw_base = CONFIG_MPFS_COREI2C_BASE + port * CONFIG_MPFS_COREI2C_INST_OFFSET; priv->plic_irq = MPFS_IRQ_FABRIC_F2H_0 + CONFIG_MPFS_COREI2C_IRQNUM + port; - nxsem_init(&priv->sem_isr, 0, 0); priv->status = MPFS_I2C_SUCCESS; priv->fpga = true; #endif