summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-tegra.c
diff options
context:
space:
mode:
authorAkhil R <akhilrajeev@nvidia.com>2023-04-27 15:39:14 +0300
committerWolfram Sang <wsa@kernel.org>2023-04-30 08:49:06 +0300
commit9f855779a3874eee70e9f6be57b5f7774f14e510 (patch)
treee7add429b7369a3ec28768ab5b3177d95634803d /drivers/i2c/busses/i2c-tegra.c
parent825a0714d2b3883d4f8ff64f6933fb73ee3f1834 (diff)
downloadlinux-9f855779a3874eee70e9f6be57b5f7774f14e510.tar.xz
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: d7583c8a5748 ("i2c: tegra: Add SMBus block read function") Signed-off-by: Akhil R <akhilrajeev@nvidia.com> Acked-by: Thierry Reding <treding@nvidia.com> Signed-off-by: Wolfram Sang <wsa@kernel.org>
Diffstat (limited to 'drivers/i2c/busses/i2c-tegra.c')
-rw-r--r--drivers/i2c/busses/i2c-tegra.c40
1 files changed, 27 insertions, 13 deletions
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 6aab84c8d22b..157066f06a32 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -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);