Commit 9f855779 authored by Akhil R's avatar Akhil R Committed by Wolfram Sang
Browse files

i2c: tegra: Fix PEC support for SMBUS block read



Update the msg->len value correctly for SMBUS block read. The discrepancy
went unnoticed as msg->len is used in SMBUS transfers only when a PEC
byte is added.

Fixes: d7583c8a ("i2c: tegra: Add SMBus block read function")
Signed-off-by: default avatarAkhil R <akhilrajeev@nvidia.com>
Acked-by: default avatarThierry Reding <treding@nvidia.com>
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
parent 825a0714
Loading
Loading
Loading
Loading
+27 −13
Original line number Diff line number Diff line
@@ -242,9 +242,10 @@ struct tegra_i2c_hw_feature {
 * @is_dvc: identifies the DVC I2C controller, has a different register layout
 * @is_vi: identifies the VI I2C controller, has a different register layout
 * @msg_complete: transfer completion notifier
 * @msg_buf_remaining: size of unsent data in the message buffer
 * @msg_len: length of message in current transfer
 * @msg_err: error code for completed message
 * @msg_buf: pointer to current message data
 * @msg_buf_remaining: size of unsent data in the message buffer
 * @msg_read: indicates that the transfer is a read access
 * @timings: i2c timings information like bus frequency
 * @multimaster_mode: indicates that I2C controller is in multi-master mode
@@ -277,6 +278,7 @@ struct tegra_i2c_dev {

	struct completion msg_complete;
	size_t msg_buf_remaining;
	unsigned int msg_len;
	int msg_err;
	u8 *msg_buf;

@@ -1169,7 +1171,7 @@ static void tegra_i2c_push_packet_header(struct tegra_i2c_dev *i2c_dev,
	else
		i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);

	packet_header = msg->len - 1;
	packet_header = i2c_dev->msg_len - 1;

	if (i2c_dev->dma_mode && !i2c_dev->msg_read)
		*dma_buf++ = packet_header;
@@ -1242,20 +1244,32 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
		return err;

	i2c_dev->msg_buf = msg->buf;
	i2c_dev->msg_len = msg->len;

	/* The condition true implies smbus block read and len is already read */
	if (msg->flags & I2C_M_RECV_LEN && end_state != MSG_END_CONTINUE)
		i2c_dev->msg_buf = msg->buf + 1;

	i2c_dev->msg_buf_remaining = msg->len;
	i2c_dev->msg_err = I2C_ERR_NONE;
	i2c_dev->msg_read = !!(msg->flags & I2C_M_RD);
	reinit_completion(&i2c_dev->msg_complete);

	/*
	 * For SMBUS block read command, read only 1 byte in the first transfer.
	 * Adjust that 1 byte for the next transfer in the msg buffer and msg
	 * length.
	 */
	if (msg->flags & I2C_M_RECV_LEN) {
		if (end_state == MSG_END_CONTINUE) {
			i2c_dev->msg_len = 1;
		} else {
			i2c_dev->msg_buf += 1;
			i2c_dev->msg_len -= 1;
		}
	}

	i2c_dev->msg_buf_remaining = i2c_dev->msg_len;

	if (i2c_dev->msg_read)
		xfer_size = msg->len;
		xfer_size = i2c_dev->msg_len;
	else
		xfer_size = msg->len + I2C_PACKET_HEADER_SIZE;
		xfer_size = i2c_dev->msg_len + I2C_PACKET_HEADER_SIZE;

	xfer_size = ALIGN(xfer_size, BYTES_PER_FIFO_WORD);

@@ -1295,7 +1309,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
	if (!i2c_dev->msg_read) {
		if (i2c_dev->dma_mode) {
			memcpy(i2c_dev->dma_buf + I2C_PACKET_HEADER_SIZE,
			       msg->buf, msg->len);
			       msg->buf, i2c_dev->msg_len);

			dma_sync_single_for_device(i2c_dev->dma_dev,
						   i2c_dev->dma_phys,
@@ -1352,7 +1366,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
						i2c_dev->dma_phys,
						xfer_size, DMA_FROM_DEVICE);

			memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf, msg->len);
			memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf, i2c_dev->msg_len);
		}
	}

@@ -1408,8 +1422,8 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
			ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], MSG_END_CONTINUE);
			if (ret)
				break;
			/* Set the read byte as msg len */
			msgs[i].len = msgs[i].buf[0];
			/* Set the msg length from first byte */
			msgs[i].len += msgs[i].buf[0];
			dev_dbg(i2c_dev->dev, "reading %d bytes\n", msgs[i].len);
		}
		ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type);