]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
i2c: xlp9xx: Fix issue seen when updating receive length
authorGeorge Cherian <george.cherian@cavium.com>
Fri, 13 Jul 2018 15:05:00 +0000 (17:05 +0200)
committerStefan Bader <stefan.bader@canonical.com>
Tue, 14 Aug 2018 10:30:36 +0000 (12:30 +0200)
The hardware does not handle updates to the length register gracefully
if the new value is less than the number of bytes received so far. If
this happens, the i2c controller will not stop the receive transaction
properly.

Fix this by ensuring that the updated length is ok. This is done by
making sure that the new length written to hardware is at least few
bytes more than the bytes received so far.

While at that refactor the length updation to a new function.

BugLink: https://launchpad.net/bugs/1781476
Signed-off-by: Jayachandran C <jnair@caviumnetworks.com>
Signed-off-by: George Cherian <george.cherian@cavium.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
(cherry picked from commit 8d504d804ab657779254bdd37079d2442d75cbe8)
Signed-off-by: Manoj Iyer <manoj.iyer@canonical.com>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
Acked-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
drivers/i2c/busses/i2c-xlp9xx.c

index eb8913eba0c5c144c7d1b0986f51862bac1887f7..0d7cbe6721c3075b7512b8bea87704a82655e6c6 100644 (file)
@@ -155,9 +155,28 @@ static void xlp9xx_i2c_fill_tx_fifo(struct xlp9xx_i2c_dev *priv)
        priv->msg_buf += len;
 }
 
+static void xlp9xx_i2c_update_rlen(struct xlp9xx_i2c_dev *priv)
+{
+       u32 val, len;
+
+       /*
+        * Update receive length. Re-read len to get the latest value,
+        * and then add 4 to have a minimum value that can be safely
+        * written. This is to account for the byte read above, the
+        * transfer in progress and any delays in the register I/O
+        */
+       val = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_CTRL);
+       len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) &
+                                 XLP9XX_I2C_FIFO_WCNT_MASK;
+       len = max_t(u32, priv->msg_len, len + 4);
+       val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) |
+                       (len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT);
+       xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val);
+}
+
 static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv)
 {
-       u32 len, i, val;
+       u32 len, i;
        u8 rlen, *buf = priv->msg_buf;
 
        len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) &
@@ -168,20 +187,13 @@ static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv)
                /* read length byte */
                rlen = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
                *buf++ = rlen;
-               len--;
-
                if (priv->client_pec)
                        ++rlen;
                /* update remaining bytes and message length */
                priv->msg_buf_remaining = rlen;
                priv->msg_len = rlen + 1;
                priv->len_recv = false;
-
-               /* Update transfer length to read only actual data */
-               val = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_CTRL);
-               val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) |
-                       ((rlen + 1) << XLP9XX_I2C_CTRL_MCTLEN_SHIFT);
-               xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val);
+               xlp9xx_i2c_update_rlen(priv);
        } else {
                len = min(priv->msg_buf_remaining, len);
                for (i = 0; i < len; i++, buf++)