Skip to content

Commit 69f035c

Browse files
tamng-amperewsakernel
authored andcommitted
i2c: designware: Handle invalid SMBus block data response length value
In the I2C_FUNC_SMBUS_BLOCK_DATA case, the invalid length byte value (outside of 1-32) of the SMBus block data response from the Slave device is not correctly handled by the I2C Designware driver. In case IC_EMPTYFIFO_HOLD_MASTER_EN==1, which cannot be detected from the registers, the Master can be disabled only if the STOP bit is set. Without STOP bit set, the Master remains active, holding the bus until receiving a block data response length. This hangs the bus and is unrecoverable. Avoid this by issuing another dump read to reach the stop condition when an invalid length byte is received. Cc: stable@vger.kernel.org Signed-off-by: Tam Nguyen <tamnguyenchi@os.amperecomputing.com> Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Link: https://lore.kernel.org/r/20230726080001.337353-3-tamnguyenchi@os.amperecomputing.com Reviewed-by: Andi Shyti <andi.shyti@kernel.org> Signed-off-by: Wolfram Sang <wsa@kernel.org>
1 parent 49d4db3 commit 69f035c

File tree

1 file changed

+13
-2
lines changed

1 file changed

+13
-2
lines changed

drivers/i2c/busses/i2c-designware-master.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -590,8 +590,19 @@ i2c_dw_read(struct dw_i2c_dev *dev)
590590
regmap_read(dev->map, DW_IC_DATA_CMD, &tmp);
591591
tmp &= DW_IC_DATA_CMD_DAT;
592592
/* Ensure length byte is a valid value */
593-
if (flags & I2C_M_RECV_LEN &&
594-
tmp <= I2C_SMBUS_BLOCK_MAX && tmp > 0) {
593+
if (flags & I2C_M_RECV_LEN) {
594+
/*
595+
* if IC_EMPTYFIFO_HOLD_MASTER_EN is set, which cannot be
596+
* detected from the registers, the controller can be
597+
* disabled if the STOP bit is set. But it is only set
598+
* after receiving block data response length in
599+
* I2C_FUNC_SMBUS_BLOCK_DATA case. That needs to read
600+
* another byte with STOP bit set when the block data
601+
* response length is invalid to complete the transaction.
602+
*/
603+
if (!tmp || tmp > I2C_SMBUS_BLOCK_MAX)
604+
tmp = 1;
605+
595606
len = i2c_dw_recv_len(dev, tmp);
596607
}
597608
*buf++ = tmp;

0 commit comments

Comments
 (0)