mlxsw: spectrum: Use PMTM register to get max module width

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>
This commit is contained in:
Jiri Pirko 2019-10-31 11:42:08 +02:00 committed by David S. Miller
parent a513b1a591
commit 25911e1b97
4 changed files with 67 additions and 19 deletions

View 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)
{

View 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);

View 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,

View File

@ -4038,17 +4038,18 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp)
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 @@ static int mlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp, u8 base_port,
}
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;
}