From b2c39b563a29c1e7057faa40957345c2b0faecd5 Mon Sep 17 00:00:00 2001 From: Kangheui Won Date: Mon, 9 Nov 2020 17:49:48 +1100 Subject: drivers/i2c/dw: Check for TX_ABORT in transfer When the host sends data in i2c bus, device might not send ACK. It means that data is not processed on the device side, but for now we don't check for that condition thus wait for the response which will not come. Designware i2c detect such situation and set TX_ABORT bit. Checking for the bit will enable other layers to immediately retry rather than wait-timeout-retry cycle. BUG=b:168838505 BRANCH=zork TEST=test on zork devices, now we see "Tx abort detected" instead of I2C timeout for tpm initializtion. Change-Id: Ib0163fbce55ccc99f677dbb096f67a58d2ef2bda Signed-off-by: Kangheui Won Reviewed-on: https://review.coreboot.org/c/coreboot/+/47360 Tested-by: build bot (Jenkins) Reviewed-by: Furquan Shaikh --- src/drivers/i2c/designware/dw_i2c.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/drivers/i2c/designware/dw_i2c.c b/src/drivers/i2c/designware/dw_i2c.c index 3181e7a0cf..c0212c3e27 100644 --- a/src/drivers/i2c/designware/dw_i2c.c +++ b/src/drivers/i2c/designware/dw_i2c.c @@ -419,6 +419,15 @@ static int _dw_i2c_transfer(unsigned int bus, const struct i2c_msg *segments, /* Read to clear INTR_STAT_STOP_DET */ read32(®s->clear_stop_det_intr); + /* Check TX abort */ + if (read32(®s->raw_intr_stat) & INTR_STAT_TX_ABORT) { + printk(BIOS_ERR, "I2C TX abort detected (%08x)\n", + read32(®s->tx_abort_source)); + /* clear INTR_STAT_TX_ABORT */ + read32(®s->clear_tx_abrt_intr); + goto out; + } + /* Wait for the bus to go idle */ if (dw_i2c_wait_for_bus_idle(regs)) { printk(BIOS_ERR, "I2C timeout waiting for bus %u idle\n", bus); @@ -747,8 +756,8 @@ int dw_i2c_init(unsigned int bus, const struct dw_i2c_bus_config *bcfg) write32(®s->rx_thresh, 0); write32(®s->tx_thresh, 0); - /* Enable stop detection interrupt */ - write32(®s->intr_mask, INTR_STAT_STOP_DET); + /* Enable stop detection and TX abort interrupt */ + write32(®s->intr_mask, INTR_STAT_STOP_DET | INTR_STAT_TX_ABORT); printk(BIOS_INFO, "DW I2C bus %u at %p (%u KHz)\n", bus, regs, speed / KHz); -- cgit v1.2.3