[PATCH v3 1/3] i2c: qcom-geni: use cancel command before abort on transfer timeout
Praveen Talari <[email protected]>
| Newsgroups | org.kernel.vger.linux-i2c,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <20260716-fix_cancel_sequence_on_failure_for_i2c-v3-1-9091315a33a0@oss.qualcomm.com> |
The GENI I2C driver currently invokes geni_se_abort_m_cmd() directly when a transfer times out. However, the GENI hardware command cancellation flow requires a cancel command to be issued first. An abort should only be used as a fallback when the cancel operation itself fails to complete. Introduce a dedicated cancel_done completion that is signaled when M_CMD_CANCEL_EN is received. The timeout recovery path waits for cancel completion and escalates to geni_i2c_abort_xfer() only if the cancel command does not complete within the expected time. Co-developed-by: Naresh Maramaina <[email protected]> Signed-off-by: Naresh Maramaina <[email protected]> Reviewed-by: Mukesh Kumar Savaliya <[email protected]> Signed-off-by: Praveen Talari <[email protected]> --- drivers/i2c/busses/i2c-qcom-geni.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index 96dbf04138be..15403edb355a 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -74,6 +74,7 @@ enum geni_i2c_err_code { #define PACKING_BYTES_PW 4 #define ABORT_TIMEOUT HZ +#define CANCEL_TIMEOUT HZ #define XFER_TIMEOUT HZ #define RST_TIMEOUT HZ @@ -112,6 +113,7 @@ struct geni_i2c_dev { int err; struct i2c_adapter adap; struct completion done; + struct completion cancel_done; struct i2c_msg *cur; int cur_wr; int cur_rd; @@ -361,6 +363,8 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev) dm_tx_st & TX_DMA_DONE || dm_tx_st & TX_RESET_DONE || dm_rx_st & RX_DMA_DONE || dm_rx_st & RX_RESET_DONE) complete(&gi2c->done); + if (m_stat & M_CMD_CANCEL_EN) + complete(&gi2c->cancel_done); spin_unlock(&gi2c->lock); @@ -387,6 +391,27 @@ static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c) dev_err(gi2c->se.dev, "Timeout abort_m_cmd\n"); } +static void geni_i2c_cancel_xfer(struct geni_i2c_dev *gi2c) +{ + unsigned long time_left = msecs_to_jiffies(CANCEL_TIMEOUT); + unsigned long flags; + + reinit_completion(&gi2c->cancel_done); + + spin_lock_irqsave(&gi2c->lock, flags); + if (!gi2c->err) + geni_i2c_err(gi2c, GENI_TIMEOUT); + gi2c->cur = NULL; + geni_se_cancel_m_cmd(&gi2c->se); + spin_unlock_irqrestore(&gi2c->lock, flags); + + time_left = wait_for_completion_timeout(&gi2c->cancel_done, time_left); + if (!time_left) { + dev_err(gi2c->se.dev, "Timeout cancel_m_cmd\n"); + geni_i2c_abort_xfer(gi2c); + } +} + static void geni_i2c_rx_fsm_rst(struct geni_i2c_dev *gi2c) { u32 val; @@ -473,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) - geni_i2c_abort_xfer(gi2c); + geni_i2c_cancel_xfer(gi2c); geni_i2c_rx_msg_cleanup(gi2c, cur); @@ -515,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) - geni_i2c_abort_xfer(gi2c); + geni_i2c_cancel_xfer(gi2c); geni_i2c_tx_msg_cleanup(gi2c, cur); @@ -1107,6 +1132,7 @@ static int geni_i2c_probe(struct platform_device *pdev) gi2c->adap.algo = &geni_i2c_algo; init_completion(&gi2c->done); + init_completion(&gi2c->cancel_done); spin_lock_init(&gi2c->lock); platform_set_drvdata(pdev, gi2c); -- 2.34.1