Attention is currently required from: Tim Wawrzynczak. Hello Tim Wawrzynczak,
I'd like you to do a code review. Please visit
https://review.coreboot.org/c/coreboot/+/63210
to review the following change.
Change subject: drivers/i2c/dw_i2c: Add detect callback to i2c_bus_ops ......................................................................
drivers/i2c/dw_i2c: Add detect callback to i2c_bus_ops
This patch adds the I2C equivalent of an SMBus quick write to an I2C device, which is used by some I2C drivers as a way to probe the existence (or absence) of a certain device on the bus, based on whether or not a 0-byte write to an I2C address is ACKed or NACKed.
Change-Id: I9ed410669aabf9866329c6c6e74a39168a86b73e Signed-off-by: Matt DeVillier matt.devillier@gmail.com Signed-off-by: Tim Wawrzynczak twawrzynczak@chromium.org --- M src/drivers/i2c/designware/dw_i2c.c 1 file changed, 67 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/10/63210/1
diff --git a/src/drivers/i2c/designware/dw_i2c.c b/src/drivers/i2c/designware/dw_i2c.c index 56f3f27..f600f07 100644 --- a/src/drivers/i2c/designware/dw_i2c.c +++ b/src/drivers/i2c/designware/dw_i2c.c @@ -92,6 +92,9 @@ INTR_STAT_GEN_CALL = (1 << 11), };
+/* Bit 0 is 7-bit NAK, bits 1 and 2 are 10-bit NAKs */ +#define INTR_STAT_TX_ABORT_SOURCE_NAK_MASK 0x7 + /* I2C Controller MMIO register space */ struct dw_i2c_regs { uint32_t control; /* 0x0 */ @@ -480,6 +483,69 @@ return _dw_i2c_transfer(bus, &orig_msg[start], count - start); }
+static bool dw_i2c_detect(struct device *dev, unsigned int addr) +{ + struct dw_i2c_regs *regs; + struct stopwatch sw; + bool found = false; + + const int bus = dw_i2c_soc_dev_to_bus(dev); + regs = (struct dw_i2c_regs *)dw_i2c_base_address(bus); + if (!regs) { + printk(BIOS_ERR, "I2C bus %u base address not found\n", bus); + return CB_ERR; + } + + /* Setup transaction */ + write32(®s->target_addr, addr); + dw_i2c_enable(regs); + + stopwatch_init_usecs_expire(&sw, DW_I2C_TIMEOUT_US); + + while (!(read32(®s->status) & STATUS_TX_FIFO_NOT_FULL)) { + if (stopwatch_expired(&sw)) { + printk(BIOS_ERR, "I2C transmit timeout\n"); + return CB_ERR; + } + } + + /* stop immediately */ + write32(®s->cmd_data, CMD_DATA_STOP); + bool expired = false; + + do { + const uint32_t stat = read32(®s->raw_intr_stat); + const uint32_t src = read32(®s->tx_abort_source); + if (stat & INTR_STAT_TX_ABORT) { + if (src & INTR_STAT_TX_ABORT_SOURCE_NAK_MASK) + found = false; + + /* clear INTR_STAT_TX_ABORT */ + read32(®s->clear_tx_abrt_intr); + break; + } else if (stat & INTR_STAT_STOP_DET) { + found = true; + /* clear INTR_STST_STOP_DET */ + read32(®s->clear_stop_det_intr); + break; + } + + expired = stopwatch_expired(&sw); + } while (!expired); + + if (expired) + printk(BIOS_ERR, "Failed to detect stop bit or tx abort\n"); + + /* Wait for the bus to go idle */ + if (dw_i2c_wait_for_bus_idle(regs) != CB_SUCCESS) + printk(BIOS_ERR, "I2C timeout waiting for bus %u idle\n", bus); + + read32(®s->clear_intr); + dw_i2c_disable(regs); + + return found; +} + /* Global I2C bus handler, defined in include/device/i2c_simple.h */ int platform_i2c_transfer(unsigned int bus, struct i2c_msg *msg, int count) { @@ -850,4 +916,5 @@
const struct i2c_bus_operations dw_i2c_bus_ops = { .transfer = dw_i2c_dev_transfer, + .detect = dw_i2c_detect, };