i2c: designware: queue only one read command for block reads

i2c_dw_xfer_msg() pre-queues up to rx_fifo_depth read commands based
on the initial msg.len. For I2C_M_RECV_LEN transfers the real length
is unknown until the first byte arrives and i2c_dw_recv_len() runs.
When a large initial length is passed, the surplus pre-queued reads
leave rx_outstanding non-zero after i2c_dw_recv_len() adjusts
msg.len, so the completion condition is never met and the transfer
times out. In a loop, this repeatedly times out the controller and
disrupts unrelated driver-bound devices on the same adapter.

Queue only one read command while I2C_M_RECV_LEN is pending.
i2c_dw_recv_len() re-enables TX_EMPTY, and the rest are queued once
the device-reported length is known.

Fixes: c3ae106050 ("i2c: designware: Implement support for SMBus block read and write")
Signed-off-by: Navon John Lukose <navonjohnlukose@gmail.com>
Cc: <stable@vger.kernel.org> # v4.10+
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
This commit is contained in:
Navon John Lukose 2026-09-21 04:30:17 +05:30 committed by Andi Shyti
parent a65917d0a2
commit 73dae605ca
No known key found for this signature in database
GPG Key ID: DA78056626D32D6E

View File

@ -441,6 +441,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
if (dev->rx_outstanding >= dev->rx_fifo_depth)
break;
/*
* For block reads, queue only the length
* byte until i2c_dw_recv_len() runs.
*/
if ((flags & I2C_M_RECV_LEN) &&
buf_len < msgs[dev->msg_write_idx].len)
break;
regmap_write(dev->map, DW_IC_DATA_CMD,
cmd | 0x100);
rx_limit--;