mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 03:27:30 -04:00
i2c: imx: fix locked bus on SMBus block-read of 0 (atomic)
SMBus 3.1 6.5.7 allows a Block Read byte count of 0, but the atomic
(polling) path rejects it as -EPROTO. Worse, it returns without a
NACK+STOP: the next receive cycle has already started, so the target
keeps holding SDA and the bus stays stuck until a power cycle for
this i2c controller.
Reading I2DR to obtain the count likewise arms the next byte on the
count > I2C_SMBUS_BLOCK_MAX path, which also returned -EPROTO directly
and left the bus held.
Handle both: NACK the in-flight dummy byte (TXAK) and extend msgs->len so
the existing last-byte handling emits STOP; the dummy byte is discarded.
A count of 0 is a valid empty block read; a count above
I2C_SMBUS_BLOCK_MAX is still reported as -EPROTO, but only after the bus
has been released.
The interrupt-driven path has the same flaw from a later commit and is
fixed separately, as it carries a different Fixes: tag and stable range.
Fixes: 8e8782c715 ("i2c: imx: add SMBus block read support")
Signed-off-by: Vincent Jardin <vjardin@free.fr>
Cc: <stable@vger.kernel.org> # v3.16+
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Acked-by: Carlos Song <carlos.song@nxp.com>
Reviewed-by: Stefan Eichenberger <eichest@gmail.com>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
Link: https://lore.kernel.org/r/20260713-for-upstream-i2c-lx2160-fix-v1-v3-1-073ac9e103a5@free.fr
This commit is contained in:
committed by
Andi Shyti
parent
627b6c94b8
commit
cb2fc37857
@@ -1415,6 +1415,7 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx,
|
||||
int i, result;
|
||||
unsigned int temp;
|
||||
int block_data = msgs->flags & I2C_M_RECV_LEN;
|
||||
int block_err = 0;
|
||||
|
||||
result = i2c_imx_prepare_read(i2c_imx, msgs, false);
|
||||
if (result)
|
||||
@@ -1436,8 +1437,20 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx,
|
||||
*/
|
||||
if ((!i) && block_data) {
|
||||
len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||
if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX))
|
||||
return -EPROTO;
|
||||
if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) {
|
||||
/*
|
||||
* SMBus 3.1 6.5.7: support count byte of 0.
|
||||
* I2C_SMBUS_BLOCK_MAX case should not hold the SDA either.
|
||||
*/
|
||||
if (len > I2C_SMBUS_BLOCK_MAX)
|
||||
block_err = -EPROTO;
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
temp |= I2CR_TXAK;
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
msgs->buf[0] = 0;
|
||||
msgs->len = 2;
|
||||
continue;
|
||||
}
|
||||
dev_dbg(&i2c_imx->adapter.dev,
|
||||
"<%s> read length: 0x%X\n",
|
||||
__func__, len);
|
||||
@@ -1485,7 +1498,7 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx,
|
||||
"<%s> read byte: B%d=0x%X\n",
|
||||
__func__, i, msgs->buf[i]);
|
||||
}
|
||||
return 0;
|
||||
return block_err;
|
||||
}
|
||||
|
||||
static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
|
||||
|
||||
Reference in New Issue
Block a user