drivers/i2c/dw_i2c: Adjust to handle 0-byte transfers

0-byte writes can be used as a way to probe/check presence of an i2c
device, so adjust _dw_i2c_transfer() to immediately set the STOP bit
and raise logger level for TX abort messages when the segment length
is zero. Adjust dw_i2c_transfer() to allow zero-segment-length
messages to be passed thru to _dw_i2c_transfer().

Tested as part of entire i2c-detect patch train.

Change-Id: I518e849f4c476c264a1464886b1853af66c0b29d
Signed-off-by: Matt DeVillier <matt.devillier@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/63561
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Martin L Roth <gaumless@tutanota.com>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
diff --git a/src/drivers/i2c/designware/dw_i2c.c b/src/drivers/i2c/designware/dw_i2c.c
index 761fa91..fdedf63 100644
--- a/src/drivers/i2c/designware/dw_i2c.c
+++ b/src/drivers/i2c/designware/dw_i2c.c
@@ -358,6 +358,7 @@
 	struct dw_i2c_regs *regs;
 	size_t byte;
 	enum cb_err ret = CB_ERR;
+	bool seg_zero_len = segments->len == 0;
 
 	regs = (struct dw_i2c_regs *)dw_i2c_base_address(bus);
 	if (!regs) {
@@ -374,6 +375,10 @@
 
 	dw_i2c_enable(regs);
 
+	if (seg_zero_len)
+		/* stop immediately */
+		write32(&regs->cmd_data, CMD_DATA_STOP);
+
 	/* Process each segment */
 	while (count--) {
 		if (CONFIG(DRIVERS_I2C_DESIGNWARE_DEBUG)) {
@@ -424,8 +429,8 @@
 
 	/* Check TX abort */
 	if (read32(&regs->raw_intr_stat) & INTR_STAT_TX_ABORT) {
-		printk(BIOS_ERR, "I2C TX abort detected (%08x)\n",
-		       read32(&regs->tx_abort_source));
+		printk(seg_zero_len ? BIOS_SPEW : BIOS_ERR, "I2C TX abort detected (%08x)\n",
+			read32(&regs->tx_abort_source));
 		/* clear INTR_STAT_TX_ABORT */
 		read32(&regs->clear_tx_abrt_intr);
 		goto out;
@@ -462,7 +467,7 @@
 	size_t start;
 	uint16_t addr;
 
-	if (count == 0 || !msg)
+	if (!msg)
 		return -1;
 
 	/* Break up the transfers at the differing slave address boundary. */