Skip to content

Commit 0ce3c86

Browse files
tamng-ampereFishwaldo
authored andcommitted
i2c: designware: Handle invalid SMBus block data response length value
commit 69f035c upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 1627e3b commit 0ce3c86

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
@@ -527,8 +527,19 @@ i2c_dw_read(struct dw_i2c_dev *dev)
527527
regmap_read(dev->map, DW_IC_DATA_CMD, &tmp);
528528
tmp &= DW_IC_DATA_CMD_DAT;
529529
/* Ensure length byte is a valid value */
530-
if (flags & I2C_M_RECV_LEN &&
531-
tmp <= I2C_SMBUS_BLOCK_MAX && tmp > 0) {
530+
if (flags & I2C_M_RECV_LEN) {
531+
/*
532+
* if IC_EMPTYFIFO_HOLD_MASTER_EN is set, which cannot be
533+
* detected from the registers, the controller can be
534+
* disabled if the STOP bit is set. But it is only set
535+
* after receiving block data response length in
536+
* I2C_FUNC_SMBUS_BLOCK_DATA case. That needs to read
537+
* another byte with STOP bit set when the block data
538+
* response length is invalid to complete the transaction.
539+
*/
540+
if (!tmp || tmp > I2C_SMBUS_BLOCK_MAX)
541+
tmp = 1;
542+
532543
len = i2c_dw_recv_len(dev, tmp);
533544
}
534545
*buf++ = tmp;

0 commit comments

Comments
 (0)