i2c-qcom-geni: Calculate transfer timeout based on payload size
Large I2C transfers gets timed out, which can take more than
1 second for completion. To support such large transfers,
calculate transfer completion timeout based on the payload size.
Change-Id: Iccb8466ae334ba2940588528be18235f63f2fddd
Signed-off-by: Shrey Vijay <shreyv@codeaurora.org>
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index c7f6c6b..bc4af98 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -77,6 +77,10 @@
#define I2C_AUTO_SUSPEND_DELAY 250
+#define I2C_TIMEOUT_SAFETY_COEFFICIENT 10
+
+#define I2C_TIMEOUT_MIN_USEC 500000
+
enum i2c_se_mode {
UNINITIALIZED,
FIFO_SE_DMA,
@@ -89,6 +93,7 @@
unsigned int tx_wm;
int irq;
int err;
+ u32 xfer_timeout;
struct i2c_adapter adap;
struct completion xfer;
struct i2c_msg *cur;
@@ -192,12 +197,26 @@
mb();
}
+static inline void qcom_geni_i2c_calc_timeout(struct geni_i2c_dev *gi2c)
+{
+
+ struct geni_i2c_clk_fld *clk_itr = geni_i2c_clk_map + gi2c->clk_fld_idx;
+ size_t bit_cnt = gi2c->cur->len*9;
+ size_t bit_usec = (bit_cnt*USEC_PER_SEC)/clk_itr->clk_freq_out;
+ size_t xfer_max_usec = (bit_usec*I2C_TIMEOUT_SAFETY_COEFFICIENT) +
+ I2C_TIMEOUT_MIN_USEC;
+
+ gi2c->xfer_timeout = usecs_to_jiffies(xfer_max_usec);
+
+}
+
static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
{
if (gi2c->cur)
GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev,
- "len:%d, slv-addr:0x%x, RD/WR:%d\n", gi2c->cur->len,
- gi2c->cur->addr, gi2c->cur->flags);
+ "len:%d, slv-addr:0x%x, RD/WR:%d timeout:%u\n",
+ gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags,
+ gi2c->xfer_timeout);
if (err == I2C_NACK || err == GENI_ABORT_DONE) {
GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "%s\n",
@@ -476,6 +495,7 @@
struct device *tx_dev = gi2c->wrapper_dev;
gi2c->cur = &msgs[i];
+ qcom_geni_i2c_calc_timeout(gi2c);
if (!gi2c->cfg_sent) {
segs++;
sg_init_table(gi2c->tx_sg, segs);
@@ -567,7 +587,8 @@
tx_cookie = dmaengine_submit(gi2c->tx_desc);
dma_async_issue_pending(gi2c->tx_c);
- timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
+ timeout = wait_for_completion_timeout(&gi2c->xfer,
+ gi2c->xfer_timeout);
if (msgs[i].flags & I2C_M_RD)
geni_se_iommu_unmap_buf(rx_dev, &gi2c->rx_ph,
msgs[i].len, DMA_FROM_DEVICE);
@@ -577,7 +598,8 @@
if (!timeout) {
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
- "GSI Txn timed out\n");
+ "GSI Txn timed out: %u len: %d\n",
+ gi2c->xfer_timeout, gi2c->cur->len);
gi2c->err = -ETIMEDOUT;
}
if (gi2c->err) {
@@ -633,6 +655,7 @@
m_param |= ((msgs[i].addr & 0x7F) << SLV_ADDR_SHFT);
gi2c->cur = &msgs[i];
+ qcom_geni_i2c_calc_timeout(gi2c);
mode = msgs[i].len > 32 ? SE_DMA : FIFO_MODE;
ret = geni_se_select_mode(gi2c->base, mode);
if (ret) {
@@ -682,7 +705,8 @@
}
/* Ensure FIFO write go through before waiting for Done evet */
mb();
- timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
+ timeout = wait_for_completion_timeout(&gi2c->xfer,
+ gi2c->xfer_timeout);
if (!timeout) {
geni_i2c_err(gi2c, GENI_TIMEOUT);
gi2c->cur = NULL;