drivers: i2c: i2c_max32.c: Fix handling of 0-length I2C scan transactions

The I2C shell allows a user to input "i2c scan i2c0" for instance, to
scan addresses on the i2c0 bus enabled in DT. This currently causes
an infinite loop when CONFIG_I2C_MAX32_INTERRUPT is enabled.
The infinite loops happens because 0-length transactions
(tx_len == rx_len == 0) not being handled both by the Async
i2c_max32_transfer and by the controller ISR.

This commit makes two changes:
1) [ISR] When an address ACK is received, if there is simply no data to
send or receive, then just give up the semaphore, preventing the
i2c_max32_transfer function from waiting infinitely.
2) [i2c_max32_transfer] After getting the semaphore back, if there is no
data to send or receive, then avoid waiting for the BUSY flag to clear
since clock stretching should not occur by definition for transactions
which merely contain an address ACK.

Signed-off-by: Brandon Hurst <brandon.hurst@analog.com>
This commit is contained in:
Brandon Hurst 2025-04-16 11:20:40 -07:00 committed by Benjamin Cabé
commit 7ae44ec850

View file

@ -532,11 +532,19 @@ static int i2c_max32_transfer(const struct device *dev, struct i2c_msg *msgs, ui
ret = data->err;
} else {
if (data->flags & I2C_MSG_STOP) {
/* Wait for busy flag to be cleared */
while (i2c->status & ADI_MAX32_I2C_STATUS_MASTER_BUSY) {
/* 0 length transactions are needed for I2C SCANs */
if ((req->tx_len == req->rx_len) && (req->tx_len == 0)) {
MXC_I2C_ClearFlags(i2c, ADI_MAX32_I2C_INT_FL0_MASK,
ADI_MAX32_I2C_INT_FL1_MASK);
} else {
/* Wait for busy flag to be cleared for clock stetching
* use-cases
*/
while (i2c->status & ADI_MAX32_I2C_STATUS_MASTER_BUSY) {
}
MXC_I2C_ClearFlags(i2c, ADI_MAX32_I2C_INT_FL0_MASK,
ADI_MAX32_I2C_INT_FL1_MASK);
}
MXC_I2C_ClearFlags(i2c, ADI_MAX32_I2C_INT_FL0_MASK,
ADI_MAX32_I2C_INT_FL1_MASK);
}
}
if (ret) {
@ -755,6 +763,12 @@ static void i2c_max32_isr_controller(const struct device *dev, mxc_i2c_regs_t *i
MXC_I2C_EnableInt(
i2c, ADI_MAX32_I2C_INT_EN0_RX_THD | ADI_MAX32_I2C_INT_EN0_DONE, 0);
}
/* 0-length transactions are needed for I2C scans.
* In these cases, just give up the semaphore.
*/
else if ((req->tx_len == req->rx_len) && (req->tx_len == 0)) {
k_sem_give(&data->xfer);
}
}
if (req->tx_len &&