OSDN Git Service

mlxsw: spectrum: Use PMTM register to get max module width
authorJiri Pirko <jiri@mellanox.com>
Thu, 31 Oct 2019 09:42:08 +0000 (11:42 +0200)
committerDavid S. Miller <davem@davemloft.net>
Thu, 31 Oct 2019 17:54:46 +0000 (10:54 -0700)
Currently the max module width is hard-coded according to ASIC type.
That is not entirely correct, as the max module width might differ
per-board. Use PMTM register to query FW for maximal width of a module.

Signed-off-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/core.c
drivers/net/ethernet/mellanox/mlxsw/core.h
drivers/net/ethernet/mellanox/mlxsw/port.h
drivers/net/ethernet/mellanox/mlxsw/spectrum.c

index 2b59f84..235d199 100644 (file)
@@ -2017,6 +2017,35 @@ mlxsw_core_port_devlink_port_get(struct mlxsw_core *mlxsw_core,
 }
 EXPORT_SYMBOL(mlxsw_core_port_devlink_port_get);
 
+int mlxsw_core_module_max_width(struct mlxsw_core *mlxsw_core, u8 module)
+{
+       enum mlxsw_reg_pmtm_module_type module_type;
+       char pmtm_pl[MLXSW_REG_PMTM_LEN];
+       int err;
+
+       mlxsw_reg_pmtm_pack(pmtm_pl, module);
+       err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtm), pmtm_pl);
+       if (err)
+               return err;
+       mlxsw_reg_pmtm_unpack(pmtm_pl, &module_type);
+
+       /* Here we need to get the module width according to the module type. */
+
+       switch (module_type) {
+       case MLXSW_REG_PMTM_MODULE_TYPE_BP_4X: /* fall through */
+       case MLXSW_REG_PMTM_MODULE_TYPE_BP_QSFP:
+               return 4;
+       case MLXSW_REG_PMTM_MODULE_TYPE_BP_2X:
+               return 2;
+       case MLXSW_REG_PMTM_MODULE_TYPE_BP_SFP: /* fall through */
+       case MLXSW_REG_PMTM_MODULE_TYPE_BP_1X:
+               return 1;
+       default:
+               return -EINVAL;
+       }
+}
+EXPORT_SYMBOL(mlxsw_core_module_max_width);
+
 static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core,
                                    const char *buf, size_t size)
 {
index f250370..0d18bee 100644 (file)
@@ -200,6 +200,7 @@ enum devlink_port_type mlxsw_core_port_type_get(struct mlxsw_core *mlxsw_core,
 struct devlink_port *
 mlxsw_core_port_devlink_port_get(struct mlxsw_core *mlxsw_core,
                                 u8 local_port);
+int mlxsw_core_module_max_width(struct mlxsw_core *mlxsw_core, u8 module);
 
 int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay);
 bool mlxsw_core_schedule_work(struct work_struct *work);
index a33eeef..741fd29 100644 (file)
@@ -24,8 +24,6 @@
 
 #define MLXSW_PORT_DONT_CARE           0xFF
 
-#define MLXSW_PORT_MODULE_MAX_WIDTH    4
-
 enum mlxsw_port_admin_status {
        MLXSW_PORT_ADMIN_STATUS_UP = 1,
        MLXSW_PORT_ADMIN_STATUS_DOWN = 2,
index 97be4bc..149b2cc 100644 (file)
@@ -4038,17 +4038,18 @@ err_port_to_module_alloc:
        return err;
 }
 
-static u8 mlxsw_sp_cluster_base_port_get(u8 local_port)
+static u8 mlxsw_sp_cluster_base_port_get(u8 local_port, unsigned int max_width)
 {
-       u8 offset = (local_port - 1) % MLXSW_SP_PORTS_PER_CLUSTER_MAX;
+       u8 offset = (local_port - 1) % max_width;
 
        return local_port - offset;
 }
 
 static int mlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp, u8 base_port,
-                                     u8 module, unsigned int count, u8 offset)
+                                     u8 module, unsigned int count, u8 offset,
+                                     unsigned int max_width)
 {
-       u8 width = MLXSW_PORT_MODULE_MAX_WIDTH / count;
+       u8 width = max_width / count;
        int err, i;
 
        for (i = 0; i < count; i++) {
@@ -4068,9 +4069,10 @@ err_port_create:
 }
 
 static void mlxsw_sp_port_unsplit_create(struct mlxsw_sp *mlxsw_sp,
-                                        u8 base_port, unsigned int count)
+                                        u8 base_port, unsigned int count,
+                                        unsigned int max_width)
 {
-       u8 local_port, module, width = MLXSW_PORT_MODULE_MAX_WIDTH;
+       u8 local_port, module, width = max_width;
        int i;
 
        /* Split by four means we need to re-create two ports, otherwise
@@ -4096,7 +4098,8 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
        struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
        u8 local_ports_in_1x, local_ports_in_2x, offset;
        struct mlxsw_sp_port *mlxsw_sp_port;
-       u8 module, cur_width, base_port;
+       u8 module, base_port;
+       int max_width;
        int i;
        int err;
 
@@ -4116,7 +4119,14 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
        }
 
        module = mlxsw_sp_port->mapping.module;
-       cur_width = mlxsw_sp_port->mapping.width;
+
+       max_width = mlxsw_core_module_max_width(mlxsw_core,
+                                               mlxsw_sp_port->mapping.module);
+       if (max_width < 0) {
+               netdev_err(mlxsw_sp_port->dev, "Cannot get max width of port module\n");
+               NL_SET_ERR_MSG_MOD(extack, "Cannot get max width of port module");
+               return max_width;
+       }
 
        if (count != 2 && count != 4) {
                netdev_err(mlxsw_sp_port->dev, "Port can only be split into 2 or 4 ports\n");
@@ -4124,7 +4134,8 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
                return -EINVAL;
        }
 
-       if (cur_width != MLXSW_PORT_MODULE_MAX_WIDTH) {
+       /* Split port with non-max module width cannot be split. */
+       if (mlxsw_sp_port->mapping.width != max_width) {
                netdev_err(mlxsw_sp_port->dev, "Port cannot be split further\n");
                NL_SET_ERR_MSG_MOD(extack, "Port cannot be split further");
                return -EINVAL;
@@ -4141,7 +4152,8 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
                }
        } else {
                offset = local_ports_in_1x;
-               base_port = mlxsw_sp_cluster_base_port_get(local_port);
+               base_port = mlxsw_sp_cluster_base_port_get(local_port,
+                                                          max_width);
                if (mlxsw_sp->ports[base_port + 1] ||
                    mlxsw_sp->ports[base_port + 3]) {
                        netdev_err(mlxsw_sp_port->dev, "Invalid split configuration\n");
@@ -4155,7 +4167,7 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
                        mlxsw_sp_port_remove(mlxsw_sp, base_port + i * offset);
 
        err = mlxsw_sp_port_split_create(mlxsw_sp, base_port, module, count,
-                                        offset);
+                                        offset, max_width);
        if (err) {
                dev_err(mlxsw_sp->bus_info->dev, "Failed to create split ports\n");
                goto err_port_split_create;
@@ -4164,7 +4176,7 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
        return 0;
 
 err_port_split_create:
-       mlxsw_sp_port_unsplit_create(mlxsw_sp, base_port, count);
+       mlxsw_sp_port_unsplit_create(mlxsw_sp, base_port, count, max_width);
        return err;
 }
 
@@ -4174,8 +4186,9 @@ static int mlxsw_sp_port_unsplit(struct mlxsw_core *mlxsw_core, u8 local_port,
        struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
        u8 local_ports_in_1x, local_ports_in_2x, offset;
        struct mlxsw_sp_port *mlxsw_sp_port;
-       u8 cur_width, base_port;
        unsigned int count;
+       int max_width;
+       u8 base_port;
        int i;
 
        if (!MLXSW_CORE_RES_VALID(mlxsw_core, LOCAL_PORTS_IN_1X) ||
@@ -4199,15 +4212,22 @@ static int mlxsw_sp_port_unsplit(struct mlxsw_core *mlxsw_core, u8 local_port,
                return -EINVAL;
        }
 
-       cur_width = mlxsw_sp_port->mapping.width;
-       count = cur_width == 1 ? 4 : 2;
+       max_width = mlxsw_core_module_max_width(mlxsw_core,
+                                               mlxsw_sp_port->mapping.module);
+       if (max_width < 0) {
+               netdev_err(mlxsw_sp_port->dev, "Cannot get max width of port module\n");
+               NL_SET_ERR_MSG_MOD(extack, "Cannot get max width of port module");
+               return max_width;
+       }
+
+       count = max_width / mlxsw_sp_port->mapping.width;
 
        if (count == 2)
                offset = local_ports_in_2x;
        else
                offset = local_ports_in_1x;
 
-       base_port = mlxsw_sp_cluster_base_port_get(local_port);
+       base_port = mlxsw_sp_cluster_base_port_get(local_port, max_width);
 
        /* Determine which ports to remove. */
        if (count == 2 && local_port >= base_port + 2)
@@ -4217,7 +4237,7 @@ static int mlxsw_sp_port_unsplit(struct mlxsw_core *mlxsw_core, u8 local_port,
                if (mlxsw_sp_port_created(mlxsw_sp, base_port + i * offset))
                        mlxsw_sp_port_remove(mlxsw_sp, base_port + i * offset);
 
-       mlxsw_sp_port_unsplit_create(mlxsw_sp, base_port, count);
+       mlxsw_sp_port_unsplit_create(mlxsw_sp, base_port, count, max_width);
 
        return 0;
 }