ite: drivers/i2c: it8xxx2: fix i2c_reset issue
After i2c_reset, there is still no external pull-up I2C bus,
and finishing off the rest of loop causes the code to hang
indefinitely.
This patch fixes that if I2C bus is not available(No external
pull-up), dropping the transaction.
Signed-off-by: Tim Lin <tim2.lin@ite.corp-partner.google.com>
diff --git a/drivers/i2c/i2c_ite_it8xxx2.c b/drivers/i2c/i2c_ite_it8xxx2.c
index 3ded466..cc7d5fc 100644
--- a/drivers/i2c/i2c_ite_it8xxx2.c
+++ b/drivers/i2c/i2c_ite_it8xxx2.c
@@ -172,6 +172,16 @@
return (IT83XX_I2C_STR(base) & E_HOSTA_BB);
}
+static int i2c_bus_not_available(const struct device *dev)
+{
+ if (i2c_is_busy(dev) ||
+ (i2c_get_line_levels(dev) != I2C_LINE_IDLE)) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
static void i2c_reset(const struct device *dev, int cause)
{
const struct i2c_it8xxx2_config *config = DEV_CFG(dev);
@@ -711,6 +721,7 @@
{
struct i2c_it8xxx2_data *data = DEV_DATA(dev);
const struct i2c_it8xxx2_config *config = DEV_CFG(dev);
+ int res;
/* Check for NULL pointers */
if (dev == NULL) {
@@ -721,6 +732,18 @@
LOG_ERR("Device message is NULL");
return -EINVAL;
}
+ /* Make sure we're in a good state to start */
+ if (i2c_bus_not_available(dev)) {
+ /* reset i2c port */
+ i2c_reset(dev, I2C_RC_NO_IDLE_FOR_START);
+ /*
+ * After resetting I2C bus, if I2C bus is not available
+ * (No external pull-up), drop the transaction.
+ */
+ if (i2c_bus_not_available(dev)) {
+ return -EIO;
+ }
+ }
msgs->flags |= I2C_MSG_START;
@@ -732,12 +755,6 @@
data->msgs = &(msgs[i]);
data->addr_16bit = addr;
- /* Make sure we're in a good state to start */
- if ((msgs->flags & I2C_MSG_START) && (i2c_is_busy(dev)
- || (i2c_get_line_levels(dev) != I2C_LINE_IDLE))) {
- /* reset i2c port */
- i2c_reset(dev, I2C_RC_NO_IDLE_FOR_START);
- }
if (msgs->flags & I2C_MSG_START) {
data->i2ccs = I2C_CH_NORMAL;
/* enable i2c interrupt */
@@ -746,7 +763,15 @@
/* Start transaction */
i2c_transaction(dev);
/* Wait for the transfer to complete */
- k_sem_take(&data->device_sync_sem, K_FOREVER);
+ /* TODO: the timeout should be adjustable */
+ res = k_sem_take(&data->device_sync_sem, K_MSEC(100));
+ if (res != 0) {
+ data->err = ETIMEDOUT;
+ /* reset i2c port */
+ i2c_reset(dev, I2C_RC_TIMEOUT);
+ /* If this message is sent fail, drop the transaction. */
+ break;
+ }
}
/* reset i2c channel status */