Merge branch 'i2c/for-4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa...
[linux-2.6-block.git] / drivers / i2c / busses / i2c-designware-core.c
index b403fa5ecf4994c538b9b1d005f9658a93a45072..6d81c56184d33d4757414fd748ace9013c8a3b14 100644 (file)
@@ -536,6 +536,8 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
        intr_mask = DW_IC_INTR_DEFAULT_MASK;
 
        for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) {
+               u32 flags = msgs[dev->msg_write_idx].flags;
+
                /*
                 * if target address has changed, we need to
                 * reprogram the target address in the i2c
@@ -581,8 +583,15 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
                         * detected from the registers so we set it always
                         * when writing/reading the last byte.
                         */
+
+                       /*
+                        * i2c-core.c always sets the buffer length of
+                        * I2C_FUNC_SMBUS_BLOCK_DATA to 1. The length will
+                        * be adjusted when receiving the first byte.
+                        * Thus we can't stop the transaction here.
+                        */
                        if (dev->msg_write_idx == dev->msgs_num - 1 &&
-                           buf_len == 1)
+                           buf_len == 1 && !(flags & I2C_M_RECV_LEN))
                                cmd |= BIT(9);
 
                        if (need_restart) {
@@ -607,7 +616,12 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
                dev->tx_buf = buf;
                dev->tx_buf_len = buf_len;
 
-               if (buf_len > 0) {
+               /*
+                * Because we don't know the buffer length in the
+                * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop
+                * the transaction here.
+                */
+               if (buf_len > 0 || flags & I2C_M_RECV_LEN) {
                        /* more bytes to be written */
                        dev->status |= STATUS_WRITE_IN_PROGRESS;
                        break;
@@ -628,6 +642,24 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
        dw_writel(dev, intr_mask,  DW_IC_INTR_MASK);
 }
 
+static u8
+i2c_dw_recv_len(struct dw_i2c_dev *dev, u8 len)
+{
+       struct i2c_msg *msgs = dev->msgs;
+       u32 flags = msgs[dev->msg_read_idx].flags;
+
+       /*
+        * Adjust the buffer length and mask the flag
+        * after receiving the first byte.
+        */
+       len += (flags & I2C_CLIENT_PEC) ? 2 : 1;
+       dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding);
+       msgs[dev->msg_read_idx].len = len;
+       msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN;
+
+       return len;
+}
+
 static void
 i2c_dw_read(struct dw_i2c_dev *dev)
 {
@@ -652,7 +684,15 @@ i2c_dw_read(struct dw_i2c_dev *dev)
                rx_valid = dw_readl(dev, DW_IC_RXFLR);
 
                for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
-                       *buf++ = dw_readl(dev, DW_IC_DATA_CMD);
+                       u32 flags = msgs[dev->msg_read_idx].flags;
+
+                       *buf = dw_readl(dev, DW_IC_DATA_CMD);
+                       /* Ensure length byte is a valid value */
+                       if (flags & I2C_M_RECV_LEN &&
+                               *buf <= I2C_SMBUS_BLOCK_MAX && *buf > 0) {
+                               len = i2c_dw_recv_len(dev, *buf);
+                       }
+                       buf++;
                        dev->rx_outstanding--;
                }