[PATCH 3/3] i2c: qcom-geni: Avoid unnecessary transfer cancel on address NACK
From: Praveen Talari
Date: Wed Jul 08 2026 - 02:17:17 EST
When a target does not acknowledge its address phase, the GENI hardware
raises a NACK interrupt. In this case, SE_GENI_M_GP_LENGTH remains zero,
indicating that no data phase has started and the transfer was aborted at
the address stage.
The driver currently treats all transfer errors similarly and always
issues geni_i2c_cancel_xfer() followed by TX/RX FSM resets for DMA
transfers. For address NACKs, the transfer has already terminated in
hardware and issuing an additional cancel/reset sequence is unnecessary.
Track address NACKs separately by checking the transfer progress length
when a NACK interrupt is reported. Skip the transfer cancel operation and
DMA FSM reset paths when the NACK occurred during the address phase.
This avoids redundant cancel/reset operations for a normal address NACK
condition while preserving the existing error recovery flow for all other
transfer failures.
Co-developed-by: Naresh Maramaina <naresh.maramaina@xxxxxxxxxxxxxxxx>
Signed-off-by: Naresh Maramaina <naresh.maramaina@xxxxxxxxxxxxxxxx>
Signed-off-by: Praveen Talari <praveen.talari@xxxxxxxxxxxxxxxx>
---
drivers/i2c/busses/i2c-qcom-geni.c | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 9490aee4928c..0448654f2678 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -130,6 +130,7 @@ struct geni_i2c_dev {
struct dma_chan *rx_c;
bool no_dma;
bool gpi_mode;
+ bool addr_nack;
bool is_tx_multi_desc_xfer;
u32 num_msgs;
struct geni_i2c_gpi_multi_desc_xfer i2c_multi_desc_config;
@@ -293,8 +294,12 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev)
if (!cur ||
m_stat & (M_CMD_FAILURE_EN | M_CMD_ABORT_EN) ||
dm_rx_st & (DM_I2C_CB_ERR)) {
- if (m_stat & M_GP_IRQ_1_EN)
+ if (m_stat & M_GP_IRQ_1_EN) {
geni_i2c_err(gi2c, NACK);
+ val = readl_relaxed(base + SE_GENI_M_GP_LENGTH);
+ if (!val)
+ gi2c->addr_nack = true;
+ }
if (m_stat & M_GP_IRQ_3_EN)
geni_i2c_err(gi2c, BUS_PROTO);
if (m_stat & M_GP_IRQ_4_EN)
@@ -443,7 +448,7 @@ static void geni_i2c_rx_msg_cleanup(struct geni_i2c_dev *gi2c,
{
gi2c->cur_rd = 0;
if (gi2c->dma_buf) {
- if (gi2c->err)
+ if (gi2c->err && !gi2c->addr_nack)
geni_i2c_rx_fsm_rst(gi2c);
geni_se_rx_dma_unprep(&gi2c->se, gi2c->dma_addr, gi2c->xfer_len);
i2c_put_dma_safe_msg_buf(gi2c->dma_buf, cur, !gi2c->err);
@@ -455,7 +460,7 @@ static void geni_i2c_tx_msg_cleanup(struct geni_i2c_dev *gi2c,
{
gi2c->cur_wr = 0;
if (gi2c->dma_buf) {
- if (gi2c->err)
+ if (gi2c->err && !gi2c->addr_nack)
geni_i2c_tx_fsm_rst(gi2c);
geni_se_tx_dma_unprep(&gi2c->se, gi2c->dma_addr, gi2c->xfer_len);
i2c_put_dma_safe_msg_buf(gi2c->dma_buf, cur, !gi2c->err);
@@ -493,7 +498,7 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
cur = gi2c->cur;
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
- if (!time_left)
+ if (!time_left || (gi2c->err && !gi2c->addr_nack))
geni_i2c_cancel_xfer(gi2c);
geni_i2c_rx_msg_cleanup(gi2c, cur);
@@ -535,7 +540,7 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
cur = gi2c->cur;
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
- if (!time_left)
+ if (!time_left || (gi2c->err && !gi2c->addr_nack))
geni_i2c_cancel_xfer(gi2c);
geni_i2c_tx_msg_cleanup(gi2c, cur);
@@ -917,6 +922,7 @@ 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->addr_nack = false;
gi2c->err = 0;
reinit_completion(&gi2c->done);
if (msgs[i].flags & I2C_M_RD)
--
2.34.1