In I2C_FUNC_SMBUS_BLOCK_DATA case, the I2C Designware driver does not handle correctly when it receives the length of SMBus block data response from SMBus slave device, which is outside the range 1-32 bytes. Consequently, the I2C Designware bus is stuck and cannot recover. Because if IC_EMPTYFIFO_HOLD_MASTER_EN is set, which cannot be detected from the registers, the controller can be disabled if the STOP bit is set. But it is only set after receiving block data response length.
Hence, to prevent the bus from stuck condition, after receiving the invalid block data response length, the driver will read another byte with STOP bit set.
Cc: stable@vger.kernel.org Signed-off-by: Tam Nguyen tamnguyenchi@os.amperecomputing.com --- drivers/i2c/busses/i2c-designware-master.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 55ea91a63382..94dadd785ed0 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -527,8 +527,19 @@ i2c_dw_read(struct dw_i2c_dev *dev)
regmap_read(dev->map, DW_IC_DATA_CMD, &tmp); /* Ensure length byte is a valid value */ - if (flags & I2C_M_RECV_LEN && - (tmp & DW_IC_DATA_CMD_DAT) <= I2C_SMBUS_BLOCK_MAX && tmp > 0) { + if (flags & I2C_M_RECV_LEN) { + /* + * if IC_EMPTYFIFO_HOLD_MASTER_EN is set, which cannot be + * detected from the registers, the controller can be + * disabled if the STOP bit is set. But it is only set + * after receiving block data response length in + * I2C_FUNC_SMBUS_BLOCK_DATA case. That needs to read + * another byte with STOP bit set when the block data + * response length is invalid to complete the transaction. + */ + if ((tmp & DW_IC_DATA_CMD_DAT) > I2C_SMBUS_BLOCK_MAX || tmp == 0) + tmp = 1; + len = i2c_dw_recv_len(dev, tmp); } *buf++ = tmp;