net/mlx5e: Toggle link only after modifying port parameters
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx5 / core / port.c
index 53cc1e2c693b1ab3b015a52a0e34a7045f5ffd7c..1562e7310f5b7a20a19dffb261f4ab970c0b8bfc 100644 (file)
@@ -115,6 +115,19 @@ int mlx5_query_port_ptys(struct mlx5_core_dev *dev, u32 *ptys,
 }
 EXPORT_SYMBOL_GPL(mlx5_query_port_ptys);
 
+int mlx5_set_port_beacon(struct mlx5_core_dev *dev, u16 beacon_duration)
+{
+       u32 out[MLX5_ST_SZ_DW(mlcr_reg)];
+       u32 in[MLX5_ST_SZ_DW(mlcr_reg)];
+
+       memset(in, 0, sizeof(in));
+       MLX5_SET(mlcr_reg, in, local_port, 1);
+       MLX5_SET(mlcr_reg, in, beacon_duration, beacon_duration);
+
+       return mlx5_core_access_reg(dev, in, sizeof(in), out,
+                                   sizeof(out), MLX5_REG_MLCR, 0, 1);
+}
+
 int mlx5_query_port_proto_cap(struct mlx5_core_dev *dev,
                              u32 *proto_cap, int proto_mask)
 {
@@ -209,6 +222,18 @@ int mlx5_set_port_proto(struct mlx5_core_dev *dev, u32 proto_admin,
 }
 EXPORT_SYMBOL_GPL(mlx5_set_port_proto);
 
+/* This function should be used after setting a port register only */
+void mlx5_toggle_port_link(struct mlx5_core_dev *dev)
+{
+       enum mlx5_port_status ps;
+
+       mlx5_query_port_admin_status(dev, &ps);
+       mlx5_set_port_admin_status(dev, MLX5_PORT_DOWN);
+       if (ps == MLX5_PORT_UP)
+               mlx5_set_port_admin_status(dev, MLX5_PORT_UP);
+}
+EXPORT_SYMBOL_GPL(mlx5_toggle_port_link);
+
 int mlx5_set_port_admin_status(struct mlx5_core_dev *dev,
                               enum mlx5_port_status status)
 {
@@ -297,6 +322,82 @@ void mlx5_query_port_oper_mtu(struct mlx5_core_dev *dev, u16 *oper_mtu,
 }
 EXPORT_SYMBOL_GPL(mlx5_query_port_oper_mtu);
 
+static int mlx5_query_module_num(struct mlx5_core_dev *dev, int *module_num)
+{
+       u32 out[MLX5_ST_SZ_DW(pmlp_reg)];
+       u32 in[MLX5_ST_SZ_DW(pmlp_reg)];
+       int module_mapping;
+       int err;
+
+       memset(in, 0, sizeof(in));
+
+       MLX5_SET(pmlp_reg, in, local_port, 1);
+
+       err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out),
+                                  MLX5_REG_PMLP, 0, 0);
+       if (err)
+               return err;
+
+       module_mapping = MLX5_GET(pmlp_reg, out, lane0_module_mapping);
+       *module_num = module_mapping & MLX5_EEPROM_IDENTIFIER_BYTE_MASK;
+
+       return 0;
+}
+
+int mlx5_query_module_eeprom(struct mlx5_core_dev *dev,
+                            u16 offset, u16 size, u8 *data)
+{
+       u32 out[MLX5_ST_SZ_DW(mcia_reg)];
+       u32 in[MLX5_ST_SZ_DW(mcia_reg)];
+       int module_num;
+       u16 i2c_addr;
+       int status;
+       int err;
+       void *ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0);
+
+       err = mlx5_query_module_num(dev, &module_num);
+       if (err)
+               return err;
+
+       memset(in, 0, sizeof(in));
+       size = min_t(int, size, MLX5_EEPROM_MAX_BYTES);
+
+       if (offset < MLX5_EEPROM_PAGE_LENGTH &&
+           offset + size > MLX5_EEPROM_PAGE_LENGTH)
+               /* Cross pages read, read until offset 256 in low page */
+               size -= offset + size - MLX5_EEPROM_PAGE_LENGTH;
+
+       i2c_addr = MLX5_I2C_ADDR_LOW;
+       if (offset >= MLX5_EEPROM_PAGE_LENGTH) {
+               i2c_addr = MLX5_I2C_ADDR_HIGH;
+               offset -= MLX5_EEPROM_PAGE_LENGTH;
+       }
+
+       MLX5_SET(mcia_reg, in, l, 0);
+       MLX5_SET(mcia_reg, in, module, module_num);
+       MLX5_SET(mcia_reg, in, i2c_device_address, i2c_addr);
+       MLX5_SET(mcia_reg, in, page_number, 0);
+       MLX5_SET(mcia_reg, in, device_address, offset);
+       MLX5_SET(mcia_reg, in, size, size);
+
+       err = mlx5_core_access_reg(dev, in, sizeof(in), out,
+                                  sizeof(out), MLX5_REG_MCIA, 0, 0);
+       if (err)
+               return err;
+
+       status = MLX5_GET(mcia_reg, out, status);
+       if (status) {
+               mlx5_core_err(dev, "query_mcia_reg failed: status: 0x%x\n",
+                             status);
+               return -EIO;
+       }
+
+       memcpy(data, ptr, size);
+
+       return size;
+}
+EXPORT_SYMBOL_GPL(mlx5_query_module_eeprom);
+
 static int mlx5_query_port_pvlc(struct mlx5_core_dev *dev, u32 *pvlc,
                                int pvlc_size,  u8 local_port)
 {
@@ -607,3 +708,52 @@ int mlx5_query_port_wol(struct mlx5_core_dev *mdev, u8 *wol_mode)
        return err;
 }
 EXPORT_SYMBOL_GPL(mlx5_query_port_wol);
+
+static int mlx5_query_ports_check(struct mlx5_core_dev *mdev, u32 *out,
+                                 int outlen)
+{
+       u32 in[MLX5_ST_SZ_DW(pcmr_reg)];
+
+       memset(in, 0, sizeof(in));
+       MLX5_SET(pcmr_reg, in, local_port, 1);
+
+       return mlx5_core_access_reg(mdev, in, sizeof(in), out,
+                                   outlen, MLX5_REG_PCMR, 0, 0);
+}
+
+static int mlx5_set_ports_check(struct mlx5_core_dev *mdev, u32 *in, int inlen)
+{
+       u32 out[MLX5_ST_SZ_DW(pcmr_reg)];
+
+       return mlx5_core_access_reg(mdev, in, inlen, out,
+                                   sizeof(out), MLX5_REG_PCMR, 0, 1);
+}
+
+int mlx5_set_port_fcs(struct mlx5_core_dev *mdev, u8 enable)
+{
+       u32 in[MLX5_ST_SZ_DW(pcmr_reg)];
+
+       memset(in, 0, sizeof(in));
+       MLX5_SET(pcmr_reg, in, local_port, 1);
+       MLX5_SET(pcmr_reg, in, fcs_chk, enable);
+
+       return mlx5_set_ports_check(mdev, in, sizeof(in));
+}
+
+void mlx5_query_port_fcs(struct mlx5_core_dev *mdev, bool *supported,
+                        bool *enabled)
+{
+       u32 out[MLX5_ST_SZ_DW(pcmr_reg)];
+       /* Default values for FW which do not support MLX5_REG_PCMR */
+       *supported = false;
+       *enabled = true;
+
+       if (!MLX5_CAP_GEN(mdev, ports_check))
+               return;
+
+       if (mlx5_query_ports_check(mdev, out, sizeof(out)))
+               return;
+
+       *supported = !!(MLX5_GET(pcmr_reg, out, fcs_cap));
+       *enabled = !!(MLX5_GET(pcmr_reg, out, fcs_chk));
+}