Send the next transfer in the i2c irq handler, and only signal
complete() when the entire transaction has been completed.

Signed-off-by: Liguo Zhang <liguo.zh...@mediatek.com>
---
 drivers/i2c/busses/i2c-mt65xx.c | 220 ++++++++++++++++++++++------------------
 1 file changed, 123 insertions(+), 97 deletions(-)

diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
index 453358b..15ce3b8 100644
--- a/drivers/i2c/busses/i2c-mt65xx.c
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -139,6 +139,15 @@ struct mtk_i2c_compatible {
        unsigned char support_33bits: 1;
 };
 
+struct mtk_i2c_transaction {
+       u32 num;
+       u32 index;
+       dma_addr_t wpaddr;
+       dma_addr_t rpaddr;
+       struct i2c_msg *msgs;
+       int result;
+};
+
 struct mtk_i2c {
        struct i2c_adapter adap;        /* i2c host adapter */
        struct device *dev;
@@ -161,6 +170,7 @@ struct mtk_i2c {
        unsigned char auto_restart;
        bool ignore_restart_irq;
        const struct mtk_i2c_compatible *dev_comp;
+       struct mtk_i2c_transaction transac;
 };
 
 static const struct i2c_adapter_quirks mt6577_i2c_quirks = {
@@ -378,8 +388,7 @@ static inline u32 mtk_i2c_set_4g_mode(dma_addr_t addr)
        return (addr & BIT_ULL(32)) ? I2C_DMA_4G_MODE : I2C_DMA_CLR_FLAG;
 }
 
-static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
-                              int num, int left_num)
+static int mtk_i2c_start_transfer(struct mtk_i2c *i2c)
 {
        u16 addr_reg;
        u16 start_reg;
@@ -388,15 +397,31 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, 
struct i2c_msg *msgs,
        u32 reg_4g_mode;
        dma_addr_t rpaddr = 0;
        dma_addr_t wpaddr = 0;
-       int ret;
+       struct i2c_msg *msg;
+       struct mtk_i2c_transaction *i2c_transac;
+       int num;
+       int left_num;
 
        i2c->irq_stat = 0;
 
+       i2c_transac = &i2c->transac;
+       num = i2c_transac->num;
+       left_num = i2c_transac->num - i2c_transac->index - 1;
+       msg = &i2c_transac->msgs[i2c_transac->index];
+
+       if (!msg->buf)
+               return -EINVAL;
+
+       if (i2c->op == I2C_MASTER_WRRD)
+               left_num--;
+       else if (msg->flags & I2C_M_RD)
+               i2c->op = I2C_MASTER_RD;
+       else
+               i2c->op = I2C_MASTER_WR;
+
        if (i2c->auto_restart)
                restart_flag = I2C_RS_TRANSFER;
 
-       reinit_completion(&i2c->msg_complete);
-
        control_reg = readw(i2c->base + OFFSET_CONTROL) &
                        ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS);
        if ((i2c->speed_hz > 400000) || (left_num >= 1))
@@ -413,7 +438,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct 
i2c_msg *msgs,
        else
                writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF);
 
-       addr_reg = msgs->addr << 1;
+       addr_reg = msg->addr << 1;
        if (i2c->op == I2C_MASTER_RD)
                addr_reg |= 0x1;
 
@@ -431,16 +456,16 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, 
struct i2c_msg *msgs,
        /* Set transfer and transaction len */
        if (i2c->op == I2C_MASTER_WRRD) {
                if (i2c->dev_comp->aux_len_reg) {
-                       writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
-                       writew((msgs + 1)->len, i2c->base +
+                       writew(msg->len, i2c->base + OFFSET_TRANSFER_LEN);
+                       writew((msg + 1)->len, i2c->base +
                               OFFSET_TRANSFER_LEN_AUX);
                } else {
-                       writew(msgs->len | ((msgs + 1)->len) << 8,
+                       writew(msg->len | ((msg + 1)->len) << 8,
                               i2c->base + OFFSET_TRANSFER_LEN);
                }
                writew(I2C_WRRD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN);
        } else {
-               writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
+               writew(msg->len, i2c->base + OFFSET_TRANSFER_LEN);
                writew(num, i2c->base + OFFSET_TRANSAC_LEN);
        }
 
@@ -448,8 +473,8 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct 
i2c_msg *msgs,
        if (i2c->op == I2C_MASTER_RD) {
                writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG);
                writel(I2C_DMA_CON_RX, i2c->pdmabase + OFFSET_CON);
-               rpaddr = dma_map_single(i2c->dev, msgs->buf,
-                                       msgs->len, DMA_FROM_DEVICE);
+               rpaddr = dma_map_single(i2c->dev, msg->buf, msg->len,
+                                       DMA_FROM_DEVICE);
                if (dma_mapping_error(i2c->dev, rpaddr))
                        return -ENOMEM;
 
@@ -459,12 +484,13 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, 
struct i2c_msg *msgs,
                }
 
                writel((u32)rpaddr, i2c->pdmabase + OFFSET_RX_MEM_ADDR);
-               writel(msgs->len, i2c->pdmabase + OFFSET_RX_LEN);
+               writel(msg->len, i2c->pdmabase + OFFSET_RX_LEN);
+               i2c_transac->rpaddr = rpaddr;
        } else if (i2c->op == I2C_MASTER_WR) {
                writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG);
                writel(I2C_DMA_CON_TX, i2c->pdmabase + OFFSET_CON);
-               wpaddr = dma_map_single(i2c->dev, msgs->buf,
-                                       msgs->len, DMA_TO_DEVICE);
+               wpaddr = dma_map_single(i2c->dev, msg->buf, msg->len,
+                                       DMA_TO_DEVICE);
                if (dma_mapping_error(i2c->dev, wpaddr))
                        return -ENOMEM;
 
@@ -474,20 +500,21 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, 
struct i2c_msg *msgs,
                }
 
                writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR);
-               writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN);
+               writel(msg->len, i2c->pdmabase + OFFSET_TX_LEN);
+
+               i2c_transac->wpaddr = wpaddr;
        } else {
                writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_INT_FLAG);
                writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_CON);
-               wpaddr = dma_map_single(i2c->dev, msgs->buf,
-                                       msgs->len, DMA_TO_DEVICE);
+               wpaddr = dma_map_single(i2c->dev, msg->buf, msg->len,
+                                       DMA_TO_DEVICE);
                if (dma_mapping_error(i2c->dev, wpaddr))
                        return -ENOMEM;
-               rpaddr = dma_map_single(i2c->dev, (msgs + 1)->buf,
-                                       (msgs + 1)->len,
-                                       DMA_FROM_DEVICE);
+               rpaddr = dma_map_single(i2c->dev, (msg + 1)->buf,
+                                       (msg + 1)->len, DMA_FROM_DEVICE);
                if (dma_mapping_error(i2c->dev, rpaddr)) {
-                       dma_unmap_single(i2c->dev, wpaddr,
-                                        msgs->len, DMA_TO_DEVICE);
+                       dma_unmap_single(i2c->dev, wpaddr, msg->len,
+                                        DMA_TO_DEVICE);
                        return -ENOMEM;
                }
 
@@ -501,8 +528,11 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct 
i2c_msg *msgs,
 
                writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR);
                writel((u32)rpaddr, i2c->pdmabase + OFFSET_RX_MEM_ADDR);
-               writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN);
-               writel((msgs + 1)->len, i2c->pdmabase + OFFSET_RX_LEN);
+               writel(msg->len, i2c->pdmabase + OFFSET_TX_LEN);
+               writel((msg + 1)->len, i2c->pdmabase + OFFSET_RX_LEN);
+
+               i2c_transac->wpaddr = wpaddr;
+               i2c_transac->rpaddr = rpaddr;
        }
 
        writel(I2C_DMA_START_EN, i2c->pdmabase + OFFSET_EN);
@@ -516,40 +546,6 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct 
i2c_msg *msgs,
        }
        writew(start_reg, i2c->base + OFFSET_START);
 
-       ret = wait_for_completion_timeout(&i2c->msg_complete,
-                                         i2c->adap.timeout);
-
-       /* Clear interrupt mask */
-       writew(~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
-              I2C_TRANSAC_COMP), i2c->base + OFFSET_INTR_MASK);
-
-       if (i2c->op == I2C_MASTER_WR) {
-               dma_unmap_single(i2c->dev, wpaddr,
-                                msgs->len, DMA_TO_DEVICE);
-       } else if (i2c->op == I2C_MASTER_RD) {
-               dma_unmap_single(i2c->dev, rpaddr,
-                                msgs->len, DMA_FROM_DEVICE);
-       } else {
-               dma_unmap_single(i2c->dev, wpaddr, msgs->len,
-                                DMA_TO_DEVICE);
-               dma_unmap_single(i2c->dev, rpaddr, (msgs + 1)->len,
-                                DMA_FROM_DEVICE);
-       }
-
-       if (ret == 0) {
-               dev_dbg(i2c->dev, "addr: %x, transfer timeout\n", msgs->addr);
-               mtk_i2c_init_hw(i2c);
-               return -ETIMEDOUT;
-       }
-
-       completion_done(&i2c->msg_complete);
-
-       if (i2c->irq_stat & (I2C_HS_NACKERR | I2C_ACKERR)) {
-               dev_dbg(i2c->dev, "addr: %x, transfer ACK error\n", msgs->addr);
-               mtk_i2c_init_hw(i2c);
-               return -ENXIO;
-       }
-
        return 0;
 }
 
@@ -559,19 +555,27 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
        int ret;
        int left_num = num;
        struct mtk_i2c *i2c = i2c_get_adapdata(adap);
+       struct mtk_i2c_transaction *i2c_transac = &i2c->transac;
+
+       i2c_transac->num = num;
+       i2c_transac->index = 0;
+       i2c_transac->msgs = msgs;
+       i2c_transac->result = 0;
+
+       reinit_completion(&i2c->msg_complete);
 
        ret = mtk_i2c_clock_enable(i2c);
        if (ret)
                return ret;
 
+       i2c->op = 0;
        i2c->auto_restart = i2c->dev_comp->auto_restart;
 
        /* checking if we can skip restart and optimize using WRRD mode */
-       if (i2c->auto_restart && num == 2) {
-               if (!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD) &&
-                   msgs[0].addr == msgs[1].addr) {
-                       i2c->auto_restart = 0;
-               }
+       if (num == 2 && !(msgs[0].flags & I2C_M_RD) &&
+           (msgs[1].flags & I2C_M_RD) && msgs[0].addr == msgs[1].addr) {
+               i2c->op = I2C_MASTER_WRRD;
+               i2c->auto_restart = 0;
        }
 
        if (i2c->auto_restart && num >= 2 && i2c->speed_hz > MAX_FS_MODE_SPEED)
@@ -582,33 +586,33 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
        else
                i2c->ignore_restart_irq = false;
 
-       while (left_num--) {
-               if (!msgs->buf) {
-                       dev_dbg(i2c->dev, "data buffer is NULL.\n");
-                       ret = -EINVAL;
-                       goto err_exit;
-               }
+       /* always use DMA mode. */
+       ret = mtk_i2c_start_transfer(i2c);
+       if (ret < 0)
+               goto err_exit;
 
-               if (msgs->flags & I2C_M_RD)
-                       i2c->op = I2C_MASTER_RD;
-               else
-                       i2c->op = I2C_MASTER_WR;
-
-               if (!i2c->auto_restart) {
-                       if (num > 1) {
-                               /* combined two messages into one transaction */
-                               i2c->op = I2C_MASTER_WRRD;
-                               left_num--;
-                       }
-               }
+       ret = wait_for_completion_timeout(&i2c->msg_complete,
+                                         i2c->adap.timeout);
 
-               /* always use DMA mode. */
-               ret = mtk_i2c_do_transfer(i2c, msgs, num, left_num);
-               if (ret < 0)
-                       goto err_exit;
+       if (ret == 0) {
+               dev_dbg(i2c->dev, "addr: %x, transfer timeout\n", msgs->addr);
+               mtk_i2c_init_hw(i2c);
+               ret = -ETIMEDOUT;
+               goto err_exit;
+       }
 
-               msgs++;
+       if (i2c->irq_stat & (I2C_HS_NACKERR | I2C_ACKERR)) {
+               dev_dbg(i2c->dev, "addr: %x, transfer ACK error\n", msgs->addr);
+               mtk_i2c_init_hw(i2c);
+               ret = -ENXIO;
+               goto err_exit;
+       }
+
+       if (i2c_transac->result) {
+               ret = i2c_transac->result;
+               goto err_exit;
        }
+
        /* the return value is number of executed messages */
        ret = num;
 
@@ -621,29 +625,51 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
 {
        struct mtk_i2c *i2c = dev_id;
        u16 restart_flag = 0;
-       u16 intr_stat;
+       struct i2c_msg *msg;
+       struct mtk_i2c_transaction *i2c_transac = &i2c->transac;
 
        if (i2c->auto_restart)
                restart_flag = I2C_RS_TRANSFER;
 
-       intr_stat = readw(i2c->base + OFFSET_INTR_STAT);
-       writew(intr_stat, i2c->base + OFFSET_INTR_STAT);
+       /* Clear interrupt mask */
+       writew(~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
+              I2C_TRANSAC_COMP), i2c->base + OFFSET_INTR_MASK);
 
-       /*
-        * when occurs ack error, i2c controller generate two interrupts
-        * first is the ack error interrupt, then the complete interrupt
-        * i2c->irq_stat need keep the two interrupt value.
-        */
-       i2c->irq_stat |= intr_stat;
+       i2c->irq_stat = readw(i2c->base + OFFSET_INTR_STAT);
+       writew(i2c->irq_stat, i2c->base + OFFSET_INTR_STAT);
 
        if (i2c->ignore_restart_irq && (i2c->irq_stat & restart_flag)) {
                i2c->ignore_restart_irq = false;
                i2c->irq_stat = 0;
+               /* Enable interrupt */
+               writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
+                      I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_MASK);
                writew(I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG | I2C_TRANSAC_START,
                       i2c->base + OFFSET_START);
        } else {
-               if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag))
+               msg = &i2c_transac->msgs[i2c_transac->index];
+
+               if (i2c->op == I2C_MASTER_WR || i2c->op == I2C_MASTER_WRRD) {
+                       dma_unmap_single(i2c->dev, i2c_transac->wpaddr,
+                                        msg->len, DMA_TO_DEVICE);
+               }
+
+               if (i2c->op == I2C_MASTER_WRRD)
+                       dma_unmap_single(i2c->dev, i2c_transac->rpaddr,
+                                        (msg + 1)->len, DMA_FROM_DEVICE);
+
+               if (i2c->op == I2C_MASTER_RD)
+                       dma_unmap_single(i2c->dev, i2c_transac->rpaddr,
+                                        msg->len, DMA_FROM_DEVICE);
+
+               if (i2c->irq_stat & ~restart_flag) {
                        complete(&i2c->msg_complete);
+               } else {
+                       i2c_transac->index++;
+                       i2c_transac->result = mtk_i2c_start_transfer(i2c);
+                       if (i2c_transac->result)
+                               complete(&i2c->msg_complete);
+               }
        }
 
        return IRQ_HANDLED;
-- 
1.8.1.1.dirty

Reply via email to