From: Aniket Randive <aniket.randive@oss.qualcomm.com>
To: Wolfram Sang <wsa+renesas@sang-engineering.com>,
Andi Shyti <andi.shyti@kernel.org>,
Mukesh Savaliya <mukesh.savaliya@oss.qualcomm.com>,
Dmitry Guzman <Dmitry.Guzman@mobileye.com>
Cc: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>,
linux-i2c@vger.kernel.org, linux-arm-msm@vger.kernel.org,
linux-kernel@vger.kernel.org,
Aniket Randive <aniket.randive@oss.qualcomm.com>
Subject: [PATCH v7 2/2] i2c: qcom-geni: Add dynamic transfer timeout based on transfer length and frequency
Date: Thu, 13 Aug 2026 11:15:03 +0530 [thread overview]
Message-ID: <20260813-master-v7-2-3f9010cf9229@oss.qualcomm.com> (raw)
In-Reply-To: <20260813-master-v7-0-3f9010cf9229@oss.qualcomm.com>
The driver uses a static XFER_TIMEOUT of HZ (1 second) for all transfers
regardless of message length or bus frequency, causing unnecessary
delays on error paths.
Use i2c_update_timeout() from i2c-core to compute the timeout dynamically
from the message length and the bus frequency, then read adap->timeout at
each wait site. For FIFO, SE-DMA, and GPI single-descriptor paths the
timeout is computed per message using that message's length. For the GPI
multi-descriptor path, the maximum message length across the batch is used
since one completion covers the entire BEI batch and the timeout must
cover the longest individual message.
A 10x safety margin over the theoretical wire time is applied, with a
300ms floor to account for I2C clock stretching and other situations where
a slave may keep SCL asserted for an extended period, including faulty
devices holding the bus. Both constants remain private to this driver.
Signed-off-by: Aniket Randive <aniket.randive@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-qcom-geni.c | 44 ++++++++++++++++++++++++++++++--------
1 file changed, 35 insertions(+), 9 deletions(-)
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 658636c1ee0e..c98d9e7b250c 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -79,9 +79,14 @@ enum geni_i2c_err_code {
#define ABORT_TIMEOUT HZ
#define CANCEL_TIMEOUT HZ
-#define XFER_TIMEOUT HZ
#define RST_TIMEOUT HZ
+/* 9 bits per byte (8 data + 1 ACK), 10x safety margin */
+#define I2C_TIMEOUT_SAFETY_COEFFICIENT 10
+
+/* 300ms floor: budget for clock stretching; slave may hold SCL low indefinitely */
+#define I2C_TIMEOUT_MIN_USEC 300000
+
struct geni_i2c_desc {
bool no_dma_support;
unsigned int tx_fifo_depth;
@@ -513,7 +518,9 @@ 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);
+ i2c_update_timeout(&gi2c->adap, gi2c->clk_freq_out, len,
+ I2C_TIMEOUT_SAFETY_COEFFICIENT, I2C_TIMEOUT_MIN_USEC);
+ time_left = wait_for_completion_timeout(&gi2c->done, gi2c->adap.timeout);
if (!time_left || (gi2c->err && gi2c->err != gi2c_log[ADDR_NACK].err))
geni_i2c_cancel_xfer(gi2c);
@@ -555,7 +562,9 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
writel_relaxed(1, se->base + SE_GENI_TX_WATERMARK_REG);
cur = gi2c->cur;
- time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
+ i2c_update_timeout(&gi2c->adap, gi2c->clk_freq_out, len,
+ I2C_TIMEOUT_SAFETY_COEFFICIENT, I2C_TIMEOUT_MIN_USEC);
+ time_left = wait_for_completion_timeout(&gi2c->done, gi2c->adap.timeout);
if (!time_left || (gi2c->err && gi2c->err != gi2c_log[ADDR_NACK].err))
geni_i2c_cancel_xfer(gi2c);
@@ -633,7 +642,7 @@ static void geni_i2c_gpi_multi_desc_unmap(struct geni_i2c_dev *gi2c, struct i2c_
* geni_i2c_gpi_multi_xfer_timeout_handler() - Handles multi message transfer timeout
* @dev: Pointer to the corresponding dev node
* @multi_xfer: Pointer to the geni_i2c_gpi_multi_desc_xfer
- * @transfer_timeout_msecs: Timeout value in milliseconds
+ * @timeout_jiffies: Per-message completion timeout in jiffies
* @transfer_comp: Completion object of the transfer
*
* This function waits for the completion of each processed transfer messages
@@ -643,18 +652,18 @@ static void geni_i2c_gpi_multi_desc_unmap(struct geni_i2c_dev *gi2c, struct i2c_
*/
static int geni_i2c_gpi_multi_xfer_timeout_handler(struct device *dev,
struct geni_i2c_gpi_multi_desc_xfer *multi_xfer,
- u32 transfer_timeout_msecs,
+ unsigned long timeout_jiffies,
struct completion *transfer_comp)
{
int i;
- u32 time_left;
+ unsigned long time_left;
for (i = 0; i < multi_xfer->msg_idx_cnt - 1; i++) {
reinit_completion(transfer_comp);
if (multi_xfer->msg_idx_cnt != multi_xfer->irq_cnt) {
time_left = wait_for_completion_timeout(transfer_comp,
- transfer_timeout_msecs);
+ timeout_jiffies);
if (!time_left) {
dev_err(dev, "%s: Transfer timeout\n", __func__);
return -ETIMEDOUT;
@@ -778,8 +787,22 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[],
dma_async_issue_pending(gi2c->tx_c);
if ((msg_idx == (gi2c->num_msgs - 1)) || flags & DMA_PREP_INTERRUPT) {
+ size_t max_len = 0;
+ int j;
+
+ /*
+ * Use the longest message as the timeout base: one completion
+ * covers the whole BEI batch, so the budget must fit the worst
+ * case single-message wire time.
+ */
+ for (j = 0; j < gi2c->num_msgs; j++)
+ max_len = max_t(size_t, max_len, msgs[j].len);
+ i2c_update_timeout(&gi2c->adap, gi2c->clk_freq_out, max_len,
+ I2C_TIMEOUT_SAFETY_COEFFICIENT,
+ I2C_TIMEOUT_MIN_USEC);
ret = geni_i2c_gpi_multi_xfer_timeout_handler(gi2c->se.dev, gi2c_gpi_xfer,
- XFER_TIMEOUT, &gi2c->done);
+ gi2c->adap.timeout,
+ &gi2c->done);
if (ret) {
dev_err(gi2c->se.dev,
"I2C multi write msg transfer timeout: %d\n",
@@ -899,7 +922,10 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
if (!gi2c->is_tx_multi_desc_xfer) {
dma_async_issue_pending(gi2c->tx_c);
- time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
+ i2c_update_timeout(&gi2c->adap, gi2c->clk_freq_out, msgs[i].len,
+ I2C_TIMEOUT_SAFETY_COEFFICIENT,
+ I2C_TIMEOUT_MIN_USEC);
+ time_left = wait_for_completion_timeout(&gi2c->done, gi2c->adap.timeout);
if (!time_left) {
dev_err(gi2c->se.dev, "%s:I2C timeout\n", __func__);
gi2c->err = -ETIMEDOUT;
--
2.34.1
next prev parent reply other threads:[~2026-08-13 5:45 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-13 5:45 [PATCH v7 0/2] i2c: Add dynamic transfer timeout based on message length and frequency Aniket Randive
2026-08-13 5:45 ` [PATCH v7 1/2] i2c: core: Add i2c_update_timeout() helper for dynamic transfer timeouts Aniket Randive
2026-08-24 6:19 ` Mukesh Savaliya
2026-08-24 10:06 ` Aniket RANDIVE
2026-08-24 13:44 ` Mukesh Savaliya
2026-08-26 10:51 ` Aniket RANDIVE
2026-08-13 5:45 ` Aniket Randive [this message]
2026-08-24 7:11 ` [PATCH v7 2/2] i2c: qcom-geni: Add dynamic transfer timeout based on transfer length and frequency Mukesh Savaliya
2026-08-26 10:49 ` Aniket RANDIVE
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260813-master-v7-2-3f9010cf9229@oss.qualcomm.com \
--to=aniket.randive@oss.qualcomm.com \
--cc=Dmitry.Guzman@mobileye.com \
--cc=andi.shyti@kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-i2c@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mukesh.savaliya@oss.qualcomm.com \
--cc=viken.dadhaniya@oss.qualcomm.com \
--cc=wsa+renesas@sang-engineering.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox