]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
i2c-mlxbf: Add repeated start condition support
authorChris Babroski <cbabroski@nvidia.com>
Tue, 6 May 2025 19:30:58 +0000 (19:30 +0000)
committerAndi Shyti <andi@smida.it>
Fri, 23 May 2025 08:02:27 +0000 (10:02 +0200)
Add support for SMBus repeated start conditions to the Mellanox I2C
driver. This support is specifically enabled for the
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK implementation which is required for
communication with a specific I2C device on BlueField 3.

Signed-off-by: Chris Babroski <cbabroski@nvidia.com>
Reviewed-by: Asmaa Mnebhi <asmaa@nvidia.com>
Reviewed-by: Khalil Blaiech <kblaiech@nvidia.com>
Link: https://lore.kernel.org/r/20250506193059.321345-1-cbabroski@nvidia.com
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
drivers/i2c/busses/i2c-mlxbf.c

index 2e8291e9632309fad5ddc6b8093215b522f5eb8c..c035e5941fc85ab873458bd93e7f74a1f8950cb6 100644 (file)
 
 #define MLXBF_I2C_MASTER_ENABLE \
        (MLXBF_I2C_MASTER_LOCK_BIT | MLXBF_I2C_MASTER_BUSY_BIT | \
-        MLXBF_I2C_MASTER_START_BIT | MLXBF_I2C_MASTER_STOP_BIT)
+        MLXBF_I2C_MASTER_START_BIT)
 
 #define MLXBF_I2C_MASTER_ENABLE_WRITE \
        (MLXBF_I2C_MASTER_ENABLE | MLXBF_I2C_MASTER_CTL_WRITE_BIT)
@@ -338,6 +338,7 @@ enum {
        MLXBF_I2C_F_SMBUS_BLOCK = BIT(5),
        MLXBF_I2C_F_SMBUS_PEC = BIT(6),
        MLXBF_I2C_F_SMBUS_PROCESS_CALL = BIT(7),
+       MLXBF_I2C_F_WRITE_WITHOUT_STOP = BIT(8),
 };
 
 /* Mellanox BlueField chip type. */
@@ -638,16 +639,19 @@ static void mlxbf_i2c_smbus_read_data(struct mlxbf_i2c_priv *priv,
 }
 
 static int mlxbf_i2c_smbus_enable(struct mlxbf_i2c_priv *priv, u8 slave,
-                                 u8 len, u8 block_en, u8 pec_en, bool read)
+                                 u8 len, u8 block_en, u8 pec_en, bool read,
+                                 bool stop)
 {
-       u32 command;
+       u32 command = 0;
 
        /* Set Master GW control word. */
+       if (stop)
+               command |= MLXBF_I2C_MASTER_STOP_BIT;
        if (read) {
-               command = MLXBF_I2C_MASTER_ENABLE_READ;
+               command |= MLXBF_I2C_MASTER_ENABLE_READ;
                command |= rol32(len, MLXBF_I2C_MASTER_READ_SHIFT);
        } else {
-               command = MLXBF_I2C_MASTER_ENABLE_WRITE;
+               command |= MLXBF_I2C_MASTER_ENABLE_WRITE;
                command |= rol32(len, MLXBF_I2C_MASTER_WRITE_SHIFT);
        }
        command |= rol32(slave, MLXBF_I2C_MASTER_SLV_ADDR_SHIFT);
@@ -682,8 +686,10 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv,
        u8 op_idx, data_idx, data_len, write_len, read_len;
        struct mlxbf_i2c_smbus_operation *operation;
        u8 read_en, write_en, block_en, pec_en;
-       u8 slave, flags, addr;
+       bool stop_after_write = true;
+       u8 slave, addr;
        u8 *read_buf;
+       u32 flags;
        u32 bits;
        int ret;
 
@@ -755,7 +761,16 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv,
                        memcpy(data_desc + data_idx,
                               operation->buffer, operation->length);
                        data_idx += operation->length;
+
+                       /*
+                        * The stop condition can be skipped when writing on the bus
+                        * to implement a repeated start condition on the next read
+                        * as required for several SMBus and I2C operations.
+                        */
+                       if (flags & MLXBF_I2C_F_WRITE_WITHOUT_STOP)
+                               stop_after_write = false;
                }
+
                /*
                 * We assume that read operations are performed only once per
                 * SMBus transaction. *TBD* protect this statement so it won't
@@ -781,7 +796,7 @@ 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);
+                                        pec_en, 0, stop_after_write);
                if (ret)
                        goto out_unlock;
        }
@@ -791,7 +806,7 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv,
                mlxbf_i2c_smbus_write_data(priv, (const u8 *)&addr, 1,
                                           MLXBF_I2C_MASTER_DATA_DESC_ADDR, true);
                ret = mlxbf_i2c_smbus_enable(priv, slave, read_len, block_en,
-                                        pec_en, 1);
+                                        pec_en, 1, true);
                if (!ret) {
                        /* Get Master GW data descriptor. */
                        mlxbf_i2c_smbus_read_data(priv, data_desc, read_len + 1,
@@ -897,6 +912,9 @@ mlxbf_i2c_smbus_i2c_block_func(struct mlxbf_i2c_smbus_request *request,
        request->operation[0].flags |= pec_check ? MLXBF_I2C_F_SMBUS_PEC : 0;
        request->operation[0].buffer = command;
 
+       if (read)
+               request->operation[0].flags |= MLXBF_I2C_F_WRITE_WITHOUT_STOP;
+
        /*
         * As specified in the standard, the max number of bytes to read/write
         * per block operation is 32 bytes. In Golan code, the controller can