@@ -296,6 +296,7 @@
* exact.
*/
#define MLXBF_I2C_SMBUS_TIMEOUT (300 * 1000) /* 300ms */
+#define MLXBF_I2C_SMBUS_LOCK_POLL_TIMEOUT (300 * 1000) /* 300ms */
/* Polling frequency in microseconds. */
#define MLXBF_I2C_POLL_FREQ_IN_USEC 200
@@ -523,6 +524,25 @@ static bool mlxbf_i2c_smbus_master_wait_for_idle(struct mlxbf_i2c_priv *priv)
return false;
}
+/*
+ * wait for the lock to be released before acquiring it.
+ */
+static bool mlxbf_i2c_smbus_master_lock(struct mlxbf_i2c_priv *priv)
+{
+ if (mlxbf_i2c_poll(priv->mst->io, MLXBF_I2C_SMBUS_MASTER_GW,
+ MLXBF_I2C_MASTER_LOCK_BIT, true,
+ MLXBF_I2C_SMBUS_LOCK_POLL_TIMEOUT))
+ return true;
+
+ return false;
+}
+
+static void mlxbf_i2c_smbus_master_unlock(struct mlxbf_i2c_priv *priv)
+{
+ /* Clear the gw to clear the lock */
+ writel(0, priv->mst->io + MLXBF_I2C_SMBUS_MASTER_GW);
+}
+
static bool mlxbf_i2c_smbus_transaction_success(u32 master_status,
u32 cause_status)
{
@@ -725,10 +745,18 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv,
slave = request->slave & GENMASK(6, 0);
addr = slave << 1;
- /* First of all, check whether the HW is idle. */
- if (WARN_ON(!mlxbf_i2c_smbus_master_wait_for_idle(priv)))
+ /* Try to acquire the smbus gw lock before any reads of the GW register since
+ * a read sets the lock.
+ */
+ if (WARN_ON(!mlxbf_i2c_smbus_master_lock(priv)))
return -EBUSY;
+ /* Check whether the HW is idle */
+ if (WARN_ON(!mlxbf_i2c_smbus_master_wait_for_idle(priv))) {
+ mlxbf_i2c_smbus_master_unlock(priv);
+ return -EBUSY;
+ }
+
/* Set first byte. */
data_desc[data_idx++] = addr;
@@ -752,8 +780,10 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv,
write_en = 1;
write_len += operation->length;
if (data_idx + operation->length >
- MLXBF_I2C_MASTER_DATA_DESC_SIZE)
+ MLXBF_I2C_MASTER_DATA_DESC_SIZE) {
+ mlxbf_i2c_smbus_master_unlock(priv);
return -ENOBUFS;
+ }
memcpy(data_desc + data_idx,
operation->buffer, operation->length);
data_idx += operation->length;
@@ -784,8 +814,10 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv,
if (write_en) {
ret = mlxbf_i2c_smbus_enable(priv, slave, write_len, block_en,
pec_en, 0);
- if (ret)
+ if (ret) {
+ mlxbf_i2c_smbus_master_unlock(priv);
return ret;
+ }
}
if (read_en) {
@@ -812,6 +844,8 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv,
priv->mst->io + priv->chip->smbus_master_fsm_off);
}
+ mlxbf_i2c_smbus_master_unlock(priv);
+
return ret;
}