[PATCH v3 2/3] i2c: qcom-geni: use dedicated completions for abort and reset events

From: Praveen Talari

Date: Thu Jul 16 2026 - 02:40:01 EST


The driver uses a common completion event for transfer, abort and DMA
reset operations. This allows unrelated completion events to
prematurely wake abort and reset waiters, leading to incorrect
synchronization.

Introduce dedicated completions for abort, TX reset, and RX reset
operations, and signal them only from their respective interrupt
events. This removes the dependency on shared completion state and
eliminates the abort_done flag-based synchronization.

Co-developed-by: Naresh Maramaina <naresh.maramaina@xxxxxxxxxxxxxxxx>
Signed-off-by: Naresh Maramaina <naresh.maramaina@xxxxxxxxxxxxxxxx>
Reviewed-by: Mukesh Kumar Savaliya <mukesh.savaliya@xxxxxxxxxxxxxxxx>
Signed-off-by: Praveen Talari <praveen.talari@xxxxxxxxxxxxxxxx>
---
drivers/i2c/busses/i2c-qcom-geni.c | 54 +++++++++++++++++++-------------------
1 file changed, 27 insertions(+), 27 deletions(-)

diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 15403edb355a..9490aee4928c 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -113,7 +113,10 @@ struct geni_i2c_dev {
int err;
struct i2c_adapter adap;
struct completion done;
+ struct completion abort_done;
struct completion cancel_done;
+ struct completion tx_reset_done;
+ struct completion rx_reset_done;
struct i2c_msg *cur;
int cur_wr;
int cur_rd;
@@ -127,7 +130,6 @@ struct geni_i2c_dev {
struct dma_chan *rx_c;
bool no_dma;
bool gpi_mode;
- bool abort_done;
bool is_tx_multi_desc_xfer;
u32 num_msgs;
struct geni_i2c_gpi_multi_desc_xfer i2c_multi_desc_config;
@@ -256,8 +258,6 @@ static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)

switch (err) {
case GENI_ABORT_DONE:
- gi2c->abort_done = true;
- break;
case NACK:
case GENI_TIMEOUT:
dev_dbg(gi2c->se.dev, "%s\n", gi2c_log[err].msg);
@@ -359,12 +359,18 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev)
writel_relaxed(dm_rx_st, base + SE_DMA_RX_IRQ_CLR);

/* if this is err with done-bit not set, handle that through timeout. */
- if (m_stat & M_CMD_DONE_EN || m_stat & M_CMD_ABORT_EN ||
- dm_tx_st & TX_DMA_DONE || dm_tx_st & TX_RESET_DONE ||
- dm_rx_st & RX_DMA_DONE || dm_rx_st & RX_RESET_DONE)
+ if (m_stat & M_CMD_DONE_EN ||
+ dm_tx_st & TX_DMA_DONE ||
+ dm_rx_st & RX_DMA_DONE)
complete(&gi2c->done);
if (m_stat & M_CMD_CANCEL_EN)
complete(&gi2c->cancel_done);
+ if (m_stat & M_CMD_ABORT_EN)
+ complete(&gi2c->abort_done);
+ if (dm_tx_st & TX_RESET_DONE)
+ complete(&gi2c->tx_reset_done);
+ if (dm_rx_st & RX_RESET_DONE)
+ complete(&gi2c->rx_reset_done);

spin_unlock(&gi2c->lock);

@@ -376,17 +382,13 @@ static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c)
unsigned long time_left = ABORT_TIMEOUT;
unsigned long flags;

+ reinit_completion(&gi2c->abort_done);
+
spin_lock_irqsave(&gi2c->lock, flags);
- geni_i2c_err(gi2c, GENI_TIMEOUT);
- gi2c->cur = NULL;
- gi2c->abort_done = false;
geni_se_abort_m_cmd(&gi2c->se);
spin_unlock_irqrestore(&gi2c->lock, flags);

- do {
- time_left = wait_for_completion_timeout(&gi2c->done, time_left);
- } while (!gi2c->abort_done && time_left);
-
+ time_left = wait_for_completion_timeout(&gi2c->abort_done, time_left);
if (!time_left)
dev_err(gi2c->se.dev, "Timeout abort_m_cmd\n");
}
@@ -414,31 +416,25 @@ static void geni_i2c_cancel_xfer(struct geni_i2c_dev *gi2c)

static void geni_i2c_rx_fsm_rst(struct geni_i2c_dev *gi2c)
{
- u32 val;
unsigned long time_left = RST_TIMEOUT;

+ reinit_completion(&gi2c->rx_reset_done);
writel_relaxed(1, gi2c->se.base + SE_DMA_RX_FSM_RST);
- do {
- time_left = wait_for_completion_timeout(&gi2c->done, time_left);
- val = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
- } while (!(val & RX_RESET_DONE) && time_left);

- if (!(val & RX_RESET_DONE))
+ time_left = wait_for_completion_timeout(&gi2c->rx_reset_done, time_left);
+ if (!time_left)
dev_err(gi2c->se.dev, "Timeout resetting RX_FSM\n");
}

static void geni_i2c_tx_fsm_rst(struct geni_i2c_dev *gi2c)
{
- u32 val;
unsigned long time_left = RST_TIMEOUT;

+ reinit_completion(&gi2c->tx_reset_done);
writel_relaxed(1, gi2c->se.base + SE_DMA_TX_FSM_RST);
- do {
- time_left = wait_for_completion_timeout(&gi2c->done, time_left);
- val = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
- } while (!(val & TX_RESET_DONE) && time_left);

- if (!(val & TX_RESET_DONE))
+ time_left = wait_for_completion_timeout(&gi2c->tx_reset_done, time_left);
+ if (!time_left)
dev_err(gi2c->se.dev, "Timeout resetting TX_FSM\n");
}

@@ -851,6 +847,7 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
for (i = 0; i < num; i++) {
gi2c->cur = &msgs[i];
gi2c->err = 0;
+ reinit_completion(&gi2c->done);
dev_dbg(gi2c->se.dev, "msg[%d].len:%d\n", i, gi2c->cur->len);

peripheral.stretch = 0;
@@ -920,6 +917,8 @@ static int geni_i2c_fifo_xfer(struct geni_i2c_dev *gi2c,
m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);

gi2c->cur = &msgs[i];
+ gi2c->err = 0;
+ reinit_completion(&gi2c->done);
if (msgs[i].flags & I2C_M_RD)
ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
else
@@ -939,8 +938,6 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
int ret;

- gi2c->err = 0;
- reinit_completion(&gi2c->done);
ret = pm_runtime_get_sync(gi2c->se.dev);
if (ret < 0) {
dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
@@ -1132,7 +1129,10 @@ static int geni_i2c_probe(struct platform_device *pdev)

gi2c->adap.algo = &geni_i2c_algo;
init_completion(&gi2c->done);
+ init_completion(&gi2c->abort_done);
init_completion(&gi2c->cancel_done);
+ init_completion(&gi2c->tx_reset_done);
+ init_completion(&gi2c->rx_reset_done);
spin_lock_init(&gi2c->lock);
platform_set_drvdata(pdev, gi2c);


--
2.34.1