OSDN Git Service

mlxsw: i2c: Extend input parameters list of command API
authorVadim Pasternak <vadimp@mellanox.com>
Sun, 3 Mar 2019 09:12:15 +0000 (09:12 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 4 Mar 2019 06:23:00 +0000 (22:23 -0800)
Extend input parameters list of command API in mlxsw_i2c_cmd() in order
to support initialization commands. Up until now, only access commands
were supported by I2C driver.

Signed-off-by: Vadim Pasternak <vadimp@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/i2c.c

index eb46e0c..4708acc 100644 (file)
 #define MLXSW_I2C_CIR2_OFF_STATUS      (MLXSW_I2C_CIR2_BASE + \
                                         MLXSW_I2C_CIR_STATUS_OFF)
 #define MLXSW_I2C_OPMOD_SHIFT          12
+#define MLXSW_I2C_EVENT_BIT_SHIFT      22
 #define MLXSW_I2C_GO_BIT_SHIFT         23
 #define MLXSW_I2C_CIR_CTRL_STATUS_SHIFT        24
+#define MLXSW_I2C_EVENT_BIT            BIT(MLXSW_I2C_EVENT_BIT_SHIFT)
 #define MLXSW_I2C_GO_BIT               BIT(MLXSW_I2C_GO_BIT_SHIFT)
 #define MLXSW_I2C_GO_OPMODE            BIT(MLXSW_I2C_OPMOD_SHIFT)
 #define MLXSW_I2C_SET_IMM_CMD          (MLXSW_I2C_GO_OPMODE | \
@@ -33,6 +35,9 @@
 #define MLXSW_I2C_TLV_HDR_SIZE         0x10
 #define MLXSW_I2C_ADDR_WIDTH           4
 #define MLXSW_I2C_PUSH_CMD_SIZE                (MLXSW_I2C_ADDR_WIDTH + 4)
+#define MLXSW_I2C_SET_EVENT_CMD                (MLXSW_I2C_EVENT_BIT)
+#define MLXSW_I2C_PUSH_EVENT_CMD       (MLXSW_I2C_GO_BIT | \
+                                        MLXSW_I2C_SET_EVENT_CMD)
 #define MLXSW_I2C_READ_SEMA_SIZE       4
 #define MLXSW_I2C_PREP_SIZE            (MLXSW_I2C_ADDR_WIDTH + 28)
 #define MLXSW_I2C_MBOX_SIZE            20
@@ -44,6 +49,7 @@
 #define MLXSW_I2C_BLK_MAX              32
 #define MLXSW_I2C_RETRY                        5
 #define MLXSW_I2C_TIMEOUT_MSECS                5000
+#define MLXSW_I2C_MAX_DATA_SIZE                256
 
 /**
  * struct mlxsw_i2c - device private data:
@@ -213,6 +219,66 @@ static int mlxsw_i2c_write_cmd(struct i2c_client *client,
        return 0;
 }
 
+/* Routine posts initialization command to ASIC through mail box. */
+static int
+mlxsw_i2c_write_init_cmd(struct i2c_client *client,
+                        struct mlxsw_i2c *mlxsw_i2c, u16 opcode, u32 in_mod)
+{
+       __be32 push_cmd_buf[MLXSW_I2C_PUSH_CMD_SIZE / 4] = {
+               0, cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD)
+       };
+       __be32 prep_cmd_buf[MLXSW_I2C_PREP_SIZE / 4] = {
+               0, 0, 0, 0, 0, 0,
+               cpu_to_be32(client->adapter->nr & 0xffff),
+               cpu_to_be32(MLXSW_I2C_SET_EVENT_CMD)
+       };
+       struct i2c_msg push_cmd =
+               MLXSW_I2C_WRITE_MSG(client, push_cmd_buf,
+                                   MLXSW_I2C_PUSH_CMD_SIZE);
+       struct i2c_msg prep_cmd =
+               MLXSW_I2C_WRITE_MSG(client, prep_cmd_buf, MLXSW_I2C_PREP_SIZE);
+       u8 status;
+       int err;
+
+       push_cmd_buf[1] = cpu_to_be32(MLXSW_I2C_PUSH_EVENT_CMD | opcode);
+       prep_cmd_buf[3] = cpu_to_be32(in_mod);
+       prep_cmd_buf[7] = cpu_to_be32(MLXSW_I2C_GO_BIT | opcode);
+       mlxsw_i2c_set_slave_addr((u8 *)prep_cmd_buf,
+                                MLXSW_I2C_CIR2_BASE);
+       mlxsw_i2c_set_slave_addr((u8 *)push_cmd_buf,
+                                MLXSW_I2C_CIR2_OFF_STATUS);
+
+       /* Prepare Command Interface Register for transaction */
+       err = i2c_transfer(client->adapter, &prep_cmd, 1);
+       if (err < 0)
+               return err;
+       else if (err != 1)
+               return -EIO;
+
+       /* Write out Command Interface Register GO bit to push transaction */
+       err = i2c_transfer(client->adapter, &push_cmd, 1);
+       if (err < 0)
+               return err;
+       else if (err != 1)
+               return -EIO;
+
+       /* Wait until go bit is cleared. */
+       err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, &status);
+       if (err) {
+               dev_err(&client->dev, "HW semaphore is not released");
+               return err;
+       }
+
+       /* Validate transaction completion status. */
+       if (status) {
+               dev_err(&client->dev, "Bad transaction completion status %x\n",
+                       status);
+               return -EIO;
+       }
+
+       return 0;
+}
+
 /* Routine obtains mail box offsets from ASIC register space. */
 static int mlxsw_i2c_get_mbox(struct i2c_client *client,
                              struct mlxsw_i2c *mlxsw_i2c)
@@ -310,8 +376,8 @@ mlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num,
 
 /* Routine executes I2C command. */
 static int
-mlxsw_i2c_cmd(struct device *dev, size_t in_mbox_size, u8 *in_mbox,
-             size_t out_mbox_size, u8 *out_mbox, u8 *status)
+mlxsw_i2c_cmd(struct device *dev, u16 opcode, u32 in_mod, size_t in_mbox_size,
+             u8 *in_mbox, size_t out_mbox_size, u8 *out_mbox, u8 *status)
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client);
@@ -326,24 +392,40 @@ mlxsw_i2c_cmd(struct device *dev, size_t in_mbox_size, u8 *in_mbox,
 
        WARN_ON(in_mbox_size % sizeof(u32) || out_mbox_size % sizeof(u32));
 
-       reg_size = mlxsw_i2c_get_reg_size(in_mbox);
-       num = reg_size / MLXSW_I2C_BLK_MAX;
-       if (reg_size % MLXSW_I2C_BLK_MAX)
-               num++;
+       if (in_mbox) {
+               reg_size = mlxsw_i2c_get_reg_size(in_mbox);
+               num = reg_size / MLXSW_I2C_BLK_MAX;
+               if (reg_size % MLXSW_I2C_BLK_MAX)
+                       num++;
 
-       if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) {
-               dev_err(&client->dev, "Could not acquire lock");
-               return -EINVAL;
-       }
+               if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) {
+                       dev_err(&client->dev, "Could not acquire lock");
+                       return -EINVAL;
+               }
 
-       err = mlxsw_i2c_write(dev, reg_size, in_mbox, num, status);
-       if (err)
-               goto cmd_fail;
+               err = mlxsw_i2c_write(dev, reg_size, in_mbox, num, status);
+               if (err)
+                       goto cmd_fail;
+
+               /* No out mailbox is case of write transaction. */
+               if (!out_mbox) {
+                       mutex_unlock(&mlxsw_i2c->cmd.lock);
+                       return 0;
+               }
+       } else {
+               /* No input mailbox is case of initialization query command. */
+               reg_size = MLXSW_I2C_MAX_DATA_SIZE;
+               num = reg_size / MLXSW_I2C_BLK_MAX;
 
-       /* No out mailbox is case of write transaction. */
-       if (!out_mbox) {
-               mutex_unlock(&mlxsw_i2c->cmd.lock);
-               return 0;
+               if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) {
+                       dev_err(&client->dev, "Could not acquire lock");
+                       return -EINVAL;
+               }
+
+               err = mlxsw_i2c_write_init_cmd(client, mlxsw_i2c, opcode,
+                                              in_mod);
+               if (err)
+                       goto cmd_fail;
        }
 
        /* Send read transaction to get output mailbox content. */
@@ -395,8 +477,8 @@ static int mlxsw_i2c_cmd_exec(void *bus_priv, u16 opcode, u8 opcode_mod,
 {
        struct mlxsw_i2c *mlxsw_i2c = bus_priv;
 
-       return mlxsw_i2c_cmd(mlxsw_i2c->dev, in_mbox_size, in_mbox,
-                            out_mbox_size, out_mbox, status);
+       return mlxsw_i2c_cmd(mlxsw_i2c->dev, opcode, in_mod, in_mbox_size,
+                            in_mbox, out_mbox_size, out_mbox, status);
 }
 
 static bool mlxsw_i2c_skb_transmit_busy(void *bus_priv,