Commit 8c933eab authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-Make-port-split-code-more-generic'

Ido Schimmel says:

====================
mlxsw: Make port split code more generic

Jiri says:

Currently, we assume some limitations and constant values which are not
applicable for Spectrum-3 which has 8 lanes ports (instead of previous 4
lanes).

This patch does 2 things:

1) Generalizes the code to not use constants so it can work for 4, 8 and
   possibly 16 lanes.

2) Enforces some assumptions we had in the code but did not check.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d74361dc 973b7fdb
......@@ -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)
{
......
......@@ -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);
......
......@@ -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,
......
......@@ -3969,6 +3969,7 @@ MLXSW_ITEM32(reg, pmlp, local_port, 0x00, 16, 8);
* 1 - Lane 0 is used.
* 2 - Lanes 0 and 1 are used.
* 4 - Lanes 0, 1, 2 and 3 are used.
* 8 - Lanes 0-7 are used.
* Access: RW
*/
MLXSW_ITEM32(reg, pmlp, width, 0x00, 0, 8);
......@@ -3983,14 +3984,14 @@ MLXSW_ITEM32_INDEXED(reg, pmlp, module, 0x04, 0, 8, 0x04, 0x00, false);
* Tx Lane. When rxtx field is cleared, this field is used for Rx as well.
* Access: RW
*/
MLXSW_ITEM32_INDEXED(reg, pmlp, tx_lane, 0x04, 16, 2, 0x04, 0x00, false);
MLXSW_ITEM32_INDEXED(reg, pmlp, tx_lane, 0x04, 16, 4, 0x04, 0x00, false);
/* reg_pmlp_rx_lane
* Rx Lane. When rxtx field is cleared, this field is ignored and Rx lane is
* equal to Tx lane.
* Access: RW
*/
MLXSW_ITEM32_INDEXED(reg, pmlp, rx_lane, 0x04, 24, 2, 0x04, 0x00, false);
MLXSW_ITEM32_INDEXED(reg, pmlp, rx_lane, 0x04, 24, 4, 0x04, 0x00, false);
static inline void mlxsw_reg_pmlp_pack(char *payload, u8 local_port)
{
......@@ -5374,6 +5375,55 @@ static inline void mlxsw_reg_pplr_pack(char *payload, u8 local_port,
MLXSW_REG_PPLR_LB_TYPE_BIT_PHY_LOCAL : 0);
}
/* PMTM - Port Module Type Mapping Register
* ----------------------------------------
* The PMTM allows query or configuration of module types.
*/
#define MLXSW_REG_PMTM_ID 0x5067
#define MLXSW_REG_PMTM_LEN 0x10
MLXSW_REG_DEFINE(pmtm, MLXSW_REG_PMTM_ID, MLXSW_REG_PMTM_LEN);
/* reg_pmtm_module
* Module number.
* Access: Index
*/
MLXSW_ITEM32(reg, pmtm, module, 0x00, 16, 8);
enum mlxsw_reg_pmtm_module_type {
/* Backplane with 4 lanes */
MLXSW_REG_PMTM_MODULE_TYPE_BP_4X,
/* QSFP */
MLXSW_REG_PMTM_MODULE_TYPE_BP_QSFP,
/* SFP */
MLXSW_REG_PMTM_MODULE_TYPE_BP_SFP,
/* Backplane with single lane */
MLXSW_REG_PMTM_MODULE_TYPE_BP_1X = 4,
/* Backplane with two lane */
MLXSW_REG_PMTM_MODULE_TYPE_BP_2X = 8,
/* Chip2Chip */
MLXSW_REG_PMTM_MODULE_TYPE_C2C = 10,
};
/* reg_pmtm_module_type
* Module type.
* Access: RW
*/
MLXSW_ITEM32(reg, pmtm, module_type, 0x04, 0, 4);
static inline void mlxsw_reg_pmtm_pack(char *payload, u8 module)
{
MLXSW_REG_ZERO(pmtm, payload);
mlxsw_reg_pmtm_module_set(payload, module);
}
static inline void
mlxsw_reg_pmtm_unpack(char *payload,
enum mlxsw_reg_pmtm_module_type *module_type)
{
*module_type = mlxsw_reg_pmtm_module_type_get(payload);
}
/* HTGT - Host Trap Group Table
* ----------------------------
* Configures the properties for forwarding to CPU.
......@@ -10544,6 +10594,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = {
MLXSW_REG(pbmc),
MLXSW_REG(pspa),
MLXSW_REG(pplr),
MLXSW_REG(pmtm),
MLXSW_REG(htgt),
MLXSW_REG(hpkt),
MLXSW_REG(rgcr),
......
......@@ -26,6 +26,7 @@ enum mlxsw_res_id {
MLXSW_RES_ID_MAX_LAG_MEMBERS,
MLXSW_RES_ID_LOCAL_PORTS_IN_1X,
MLXSW_RES_ID_LOCAL_PORTS_IN_2X,
MLXSW_RES_ID_LOCAL_PORTS_IN_4X,
MLXSW_RES_ID_GUARANTEED_SHARED_BUFFER,
MLXSW_RES_ID_CELL_SIZE,
MLXSW_RES_ID_MAX_HEADROOM_SIZE,
......@@ -82,6 +83,7 @@ static u16 mlxsw_res_ids[] = {
[MLXSW_RES_ID_MAX_LAG_MEMBERS] = 0x2521,
[MLXSW_RES_ID_LOCAL_PORTS_IN_1X] = 0x2610,
[MLXSW_RES_ID_LOCAL_PORTS_IN_2X] = 0x2611,
[MLXSW_RES_ID_LOCAL_PORTS_IN_4X] = 0x2612,
[MLXSW_RES_ID_GUARANTEED_SHARED_BUFFER] = 0x2805, /* Bytes */
[MLXSW_RES_ID_CELL_SIZE] = 0x2803, /* Bytes */
[MLXSW_RES_ID_MAX_HEADROOM_SIZE] = 0x2811, /* Bytes */
......
......@@ -22,6 +22,7 @@
#include <linux/inetdevice.h>
#include <linux/netlink.h>
#include <linux/jhash.h>
#include <linux/log2.h>
#include <net/switchdev.h>
#include <net/pkt_cls.h>
#include <net/tc_act/tc_mirred.h>
......@@ -748,35 +749,69 @@ mlxsw_sp_port_system_port_mapping_set(struct mlxsw_sp_port *mlxsw_sp_port)
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sspr), sspr_pl);
}
static int mlxsw_sp_port_module_info_get(struct mlxsw_sp *mlxsw_sp,
u8 local_port, u8 *p_module,
u8 *p_width, u8 *p_lane)
static int
mlxsw_sp_port_module_info_get(struct mlxsw_sp *mlxsw_sp, u8 local_port,
struct mlxsw_sp_port_mapping *port_mapping)
{
char pmlp_pl[MLXSW_REG_PMLP_LEN];
bool separate_rxtx;
u8 module;
u8 width;
int err;
int i;
mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl);
if (err)
return err;
*p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);
*p_width = mlxsw_reg_pmlp_width_get(pmlp_pl);
*p_lane = mlxsw_reg_pmlp_tx_lane_get(pmlp_pl, 0);
module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);
width = mlxsw_reg_pmlp_width_get(pmlp_pl);
separate_rxtx = mlxsw_reg_pmlp_rxtx_get(pmlp_pl);
if (width && !is_power_of_2(width)) {
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unsupported module config: width value is not power of 2\n",
local_port);
return -EINVAL;
}
for (i = 0; i < width; i++) {
if (mlxsw_reg_pmlp_module_get(pmlp_pl, i) != module) {
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unsupported module config: contains multiple modules\n",
local_port);
return -EINVAL;
}
if (separate_rxtx &&
mlxsw_reg_pmlp_tx_lane_get(pmlp_pl, i) !=
mlxsw_reg_pmlp_rx_lane_get(pmlp_pl, i)) {
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unsupported module config: TX and RX lane numbers are different\n",
local_port);
return -EINVAL;
}
if (mlxsw_reg_pmlp_tx_lane_get(pmlp_pl, i) != i) {
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unsupported module config: TX and RX lane numbers are not sequential\n",
local_port);
return -EINVAL;
}
}
port_mapping->module = module;
port_mapping->width = width;
port_mapping->lane = mlxsw_reg_pmlp_tx_lane_get(pmlp_pl, 0);
return 0;
}
static int mlxsw_sp_port_module_map(struct mlxsw_sp_port *mlxsw_sp_port,
u8 module, u8 width, u8 lane)
static int mlxsw_sp_port_module_map(struct mlxsw_sp_port *mlxsw_sp_port)
{
struct mlxsw_sp_port_mapping *port_mapping = &mlxsw_sp_port->mapping;
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
char pmlp_pl[MLXSW_REG_PMLP_LEN];
int i;
mlxsw_reg_pmlp_pack(pmlp_pl, mlxsw_sp_port->local_port);
mlxsw_reg_pmlp_width_set(pmlp_pl, width);
for (i = 0; i < width; i++) {
mlxsw_reg_pmlp_module_set(pmlp_pl, i, module);
mlxsw_reg_pmlp_tx_lane_set(pmlp_pl, i, lane + i); /* Rx & Tx */
mlxsw_reg_pmlp_width_set(pmlp_pl, port_mapping->width);
for (i = 0; i < port_mapping->width; i++) {
mlxsw_reg_pmlp_module_set(pmlp_pl, i, port_mapping->module);
mlxsw_reg_pmlp_tx_lane_set(pmlp_pl, i, port_mapping->lane + i); /* Rx & Tx */
}
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl);
......@@ -3480,7 +3515,7 @@ static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
};
static int
mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 width)
mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
const struct mlxsw_sp_port_type_speed_ops *ops;
......@@ -3496,7 +3531,7 @@ mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 width)
&base_speed);
if (err)
return err;
upper_speed = base_speed * width;
upper_speed = base_speed * mlxsw_sp_port->mapping.width;
eth_proto_admin = ops->to_ptys_upper_speed(mlxsw_sp, upper_speed);
ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
......@@ -3657,15 +3692,18 @@ static int mlxsw_sp_port_tc_mc_mode_set(struct mlxsw_sp_port *mlxsw_sp_port,
}
static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
bool split, u8 module, u8 width, u8 lane)
u8 split_base_local_port,
struct mlxsw_sp_port_mapping *port_mapping)
{
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
bool split = !!split_base_local_port;
struct mlxsw_sp_port *mlxsw_sp_port;
struct net_device *dev;
int err;
err = mlxsw_core_port_init(mlxsw_sp->core, local_port,
module + 1, split, lane / width,
port_mapping->module + 1, split,
port_mapping->lane / port_mapping->width,
mlxsw_sp->base_mac,
sizeof(mlxsw_sp->base_mac));
if (err) {
......@@ -3687,9 +3725,8 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
mlxsw_sp_port->local_port = local_port;
mlxsw_sp_port->pvid = MLXSW_SP_DEFAULT_VID;
mlxsw_sp_port->split = split;
mlxsw_sp_port->mapping.module = module;
mlxsw_sp_port->mapping.width = width;
mlxsw_sp_port->mapping.lane = lane;
mlxsw_sp_port->split_base_local_port = split_base_local_port;
mlxsw_sp_port->mapping = *port_mapping;
mlxsw_sp_port->link.autoneg = 1;
INIT_LIST_HEAD(&mlxsw_sp_port->vlans_list);
INIT_LIST_HEAD(&mlxsw_sp_port->mall_tc_list);
......@@ -3714,7 +3751,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
dev->netdev_ops = &mlxsw_sp_port_netdev_ops;
dev->ethtool_ops = &mlxsw_sp_port_ethtool_ops;
err = mlxsw_sp_port_module_map(mlxsw_sp_port, module, width, lane);
err = mlxsw_sp_port_module_map(mlxsw_sp_port);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to map module\n",
mlxsw_sp_port->local_port);
......@@ -3756,7 +3793,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
goto err_port_system_port_mapping_set;
}
err = mlxsw_sp_port_speed_by_width_set(mlxsw_sp_port, width);
err = mlxsw_sp_port_speed_by_width_set(mlxsw_sp_port);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to enable speeds\n",
mlxsw_sp_port->local_port);
......@@ -3979,14 +4016,13 @@ static void mlxsw_sp_ports_remove(struct mlxsw_sp *mlxsw_sp)
if (mlxsw_sp_port_created(mlxsw_sp, i))
mlxsw_sp_port_remove(mlxsw_sp, i);
mlxsw_sp_cpu_port_remove(mlxsw_sp);
kfree(mlxsw_sp->port_to_module);
kfree(mlxsw_sp->ports);
}
static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp)
{
unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
u8 module, width, lane;
struct mlxsw_sp_port_mapping *port_mapping;
size_t alloc_size;
int i;
int err;
......@@ -3996,66 +4032,98 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp)
if (!mlxsw_sp->ports)
return -ENOMEM;
mlxsw_sp->port_to_module = kmalloc_array(max_ports, sizeof(int),
GFP_KERNEL);
if (!mlxsw_sp->port_to_module) {
err = -ENOMEM;
goto err_port_to_module_alloc;
}
err = mlxsw_sp_cpu_port_create(mlxsw_sp);
if (err)
goto err_cpu_port_create;
for (i = 1; i < max_ports; i++) {
/* Mark as invalid */
mlxsw_sp->port_to_module[i] = -1;
err = mlxsw_sp_port_module_info_get(mlxsw_sp, i, &module,
&width, &lane);
if (err)
goto err_port_module_info_get;
if (!width)
port_mapping = mlxsw_sp->port_mapping[i];
if (!port_mapping)
continue;
mlxsw_sp->port_to_module[i] = module;
err = mlxsw_sp_port_create(mlxsw_sp, i, false,
module, width, lane);
err = mlxsw_sp_port_create(mlxsw_sp, i, 0, port_mapping);
if (err)
goto err_port_create;
}
return 0;
err_port_create:
err_port_module_info_get:
for (i--; i >= 1; i--)
if (mlxsw_sp_port_created(mlxsw_sp, i))
mlxsw_sp_port_remove(mlxsw_sp, i);
mlxsw_sp_cpu_port_remove(mlxsw_sp);
err_cpu_port_create:
kfree(mlxsw_sp->port_to_module);
err_port_to_module_alloc:
kfree(mlxsw_sp->ports);
return err;
}
static u8 mlxsw_sp_cluster_base_port_get(u8 local_port)
static int mlxsw_sp_port_module_info_init(struct mlxsw_sp *mlxsw_sp)
{
unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
struct mlxsw_sp_port_mapping port_mapping;
int i;
int err;
mlxsw_sp->port_mapping = kcalloc(max_ports,
sizeof(struct mlxsw_sp_port_mapping *),
GFP_KERNEL);
if (!mlxsw_sp->port_mapping)
return -ENOMEM;
for (i = 1; i < max_ports; i++) {
err = mlxsw_sp_port_module_info_get(mlxsw_sp, i, &port_mapping);
if (err)
goto err_port_module_info_get;
if (!port_mapping.width)
continue;
mlxsw_sp->port_mapping[i] = kmemdup(&port_mapping,
sizeof(port_mapping),
GFP_KERNEL);
if (!mlxsw_sp->port_mapping[i])
goto err_port_module_info_dup;
}
return 0;
err_port_module_info_get:
err_port_module_info_dup:
for (i--; i >= 1; i--)
kfree(mlxsw_sp->port_mapping[i]);
kfree(mlxsw_sp->port_mapping);
return err;
}
static void mlxsw_sp_port_module_info_fini(struct mlxsw_sp *mlxsw_sp)
{
int i;
for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++)
kfree(mlxsw_sp->port_mapping[i]);
kfree(mlxsw_sp->port_mapping);
}
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)
static int
mlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp, u8 base_port,
struct mlxsw_sp_port_mapping *port_mapping,
unsigned int count, u8 offset)
{
u8 width = MLXSW_PORT_MODULE_MAX_WIDTH / count;
struct mlxsw_sp_port_mapping split_port_mapping;
int err, i;
split_port_mapping = *port_mapping;
split_port_mapping.width /= count;
for (i = 0; i < count; i++) {
err = mlxsw_sp_port_create(mlxsw_sp, base_port + i * offset,
true, module, width, i * width);
base_port, &split_port_mapping);
if (err)
goto err_port_create;
split_port_mapping.lane += split_port_mapping.width;
}
return 0;
......@@ -4068,45 +4136,55 @@ 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, u8 offset)
{
u8 local_port, module, width = MLXSW_PORT_MODULE_MAX_WIDTH;
struct mlxsw_sp_port_mapping *port_mapping;
int i;
/* Split by four means we need to re-create two ports, otherwise
* only one.
*/
count = count / 2;
for (i = 0; i < count; i++) {
local_port = base_port + i * 2;
if (mlxsw_sp->port_to_module[local_port] < 0)
/* Go over original unsplit ports in the gap and recreate them. */
for (i = 0; i < count * offset; i++) {
port_mapping = mlxsw_sp->port_mapping[base_port + i];
if (!port_mapping)
continue;
module = mlxsw_sp->port_to_module[local_port];
mlxsw_sp_port_create(mlxsw_sp, local_port, false, module,
width, 0);
mlxsw_sp_port_create(mlxsw_sp, base_port + i, 0, port_mapping);
}
}
static int mlxsw_sp_local_ports_offset(struct mlxsw_core *mlxsw_core,
unsigned int count,
unsigned int max_width)
{
enum mlxsw_res_id local_ports_in_x_res_id;
int split_width = max_width / count;
if (split_width == 1)
local_ports_in_x_res_id = MLXSW_RES_ID_LOCAL_PORTS_IN_1X;
else if (split_width == 2)
local_ports_in_x_res_id = MLXSW_RES_ID_LOCAL_PORTS_IN_2X;
else if (split_width == 4)
local_ports_in_x_res_id = MLXSW_RES_ID_LOCAL_PORTS_IN_4X;
else
return -EINVAL;
if (!mlxsw_core_res_valid(mlxsw_core, local_ports_in_x_res_id))
return -EINVAL;
return mlxsw_core_res_get(mlxsw_core, local_ports_in_x_res_id);
}
static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
unsigned int count,
struct netlink_ext_ack *extack)
{
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_mapping port_mapping;
struct mlxsw_sp_port *mlxsw_sp_port;
u8 module, cur_width, base_port;
int max_width;
u8 base_port;
int offset;
int i;
int err;
if (!MLXSW_CORE_RES_VALID(mlxsw_core, LOCAL_PORTS_IN_1X) ||
!MLXSW_CORE_RES_VALID(mlxsw_core, LOCAL_PORTS_IN_2X))
return -EIO;
local_ports_in_1x = MLXSW_CORE_RES_GET(mlxsw_core, LOCAL_PORTS_IN_1X);
local_ports_in_2x = MLXSW_CORE_RES_GET(mlxsw_core, LOCAL_PORTS_IN_2X);
mlxsw_sp_port = mlxsw_sp->ports[local_port];
if (!mlxsw_sp_port) {
dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
......@@ -4115,47 +4193,70 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
return -EINVAL;
}
module = mlxsw_sp_port->mapping.module;
cur_width = mlxsw_sp_port->mapping.width;
/* Split ports cannot be split. */
if (mlxsw_sp_port->split) {
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;
}
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;
}
/* Split port with non-max and 1 module width cannot be split. */
if (mlxsw_sp_port->mapping.width != max_width || max_width == 1) {
netdev_err(mlxsw_sp_port->dev, "Port cannot be split\n");
NL_SET_ERR_MSG_MOD(extack, "Port cannot be split");
return -EINVAL;
}
if (count != 2 && count != 4) {
netdev_err(mlxsw_sp_port->dev, "Port can only be split into 2 or 4 ports\n");
NL_SET_ERR_MSG_MOD(extack, "Port can only be split into 2 or 4 ports");
if (count == 1 || !is_power_of_2(count) || count > max_width) {
netdev_err(mlxsw_sp_port->dev, "Invalid split count\n");
NL_SET_ERR_MSG_MOD(extack, "Invalid split count");
return -EINVAL;
}
if (cur_width != MLXSW_PORT_MODULE_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");
offset = mlxsw_sp_local_ports_offset(mlxsw_core, count, max_width);
if (offset < 0) {
netdev_err(mlxsw_sp_port->dev, "Cannot obtain local port offset\n");
NL_SET_ERR_MSG_MOD(extack, "Cannot obtain local port offset");
return -EINVAL;
}
/* Make sure we have enough slave (even) ports for the split. */
if (count == 2) {
offset = local_ports_in_2x;
base_port = local_port;
if (mlxsw_sp->ports[base_port + local_ports_in_2x]) {
netdev_err(mlxsw_sp_port->dev, "Invalid split configuration\n");
NL_SET_ERR_MSG_MOD(extack, "Invalid split configuration");
return -EINVAL;
}
} else {
offset = local_ports_in_1x;
base_port = mlxsw_sp_cluster_base_port_get(local_port);
if (mlxsw_sp->ports[base_port + 1] ||
mlxsw_sp->ports[base_port + 3]) {
/* Only in case max split is being done, the local port and
* base port may differ.
*/
base_port = count == max_width ?
mlxsw_sp_cluster_base_port_get(local_port, max_width) :
local_port;
for (i = 0; i < count * offset; i++) {
/* Expect base port to exist and also the one in the middle in
* case of maximal split count.
*/
if (i == 0 || (count == max_width && i == count / 2))
continue;
if (mlxsw_sp_port_created(mlxsw_sp, base_port + i)) {
netdev_err(mlxsw_sp_port->dev, "Invalid split configuration\n");
NL_SET_ERR_MSG_MOD(extack, "Invalid split configuration");
return -EINVAL;
}
}
port_mapping = mlxsw_sp_port->mapping;
for (i = 0; i < count; i++)
if (mlxsw_sp_port_created(mlxsw_sp, base_port + i * offset))
mlxsw_sp_port_remove(mlxsw_sp, base_port + i * offset);
err = mlxsw_sp_port_split_create(mlxsw_sp, base_port, module, count,
offset);
err = mlxsw_sp_port_split_create(mlxsw_sp, base_port, &port_mapping,
count, offset);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to create split ports\n");
goto err_port_split_create;
......@@ -4164,7 +4265,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, offset);
return err;
}
......@@ -4172,19 +4273,13 @@ static int mlxsw_sp_port_unsplit(struct mlxsw_core *mlxsw_core, u8 local_port,
struct netlink_ext_ack *extack)
{
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 offset;
int i;
if (!MLXSW_CORE_RES_VALID(mlxsw_core, LOCAL_PORTS_IN_1X) ||
!MLXSW_CORE_RES_VALID(mlxsw_core, LOCAL_PORTS_IN_2X))
return -EIO;
local_ports_in_1x = MLXSW_CORE_RES_GET(mlxsw_core, LOCAL_PORTS_IN_1X);
local_ports_in_2x = MLXSW_CORE_RES_GET(mlxsw_core, LOCAL_PORTS_IN_2X);
mlxsw_sp_port = mlxsw_sp->ports[local_port];
if (!mlxsw_sp_port) {
dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
......@@ -4199,25 +4294,30 @@ 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;
}
if (count == 2)
offset = local_ports_in_2x;
else
offset = local_ports_in_1x;
count = max_width / mlxsw_sp_port->mapping.width;
base_port = mlxsw_sp_cluster_base_port_get(local_port);
offset = mlxsw_sp_local_ports_offset(mlxsw_core, count, max_width);
if (WARN_ON(offset < 0)) {
netdev_err(mlxsw_sp_port->dev, "Cannot obtain local port offset\n");
NL_SET_ERR_MSG_MOD(extack, "Cannot obtain local port offset");
return -EINVAL;
}
/* Determine which ports to remove. */
if (count == 2 && local_port >= base_port + 2)
base_port = base_port + 2;
base_port = mlxsw_sp_port->split_base_local_port;
for (i = 0; i < count; i++)
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, offset);
return 0;
}
......@@ -4924,6 +5024,12 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
goto err_dpipe_init;
}
err = mlxsw_sp_port_module_info_init(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to init port module info\n");
goto err_port_module_info_init;
}
err = mlxsw_sp_ports_create(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n");
......@@ -4933,6 +5039,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
return 0;
err_ports_create:
mlxsw_sp_port_module_info_fini(mlxsw_sp);
err_port_module_info_init:
mlxsw_sp_dpipe_fini(mlxsw_sp);
err_dpipe_init:
unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),
......@@ -5025,6 +5133,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
mlxsw_sp_ports_remove(mlxsw_sp);
mlxsw_sp_port_module_info_fini(mlxsw_sp);
mlxsw_sp_dpipe_fini(mlxsw_sp);
unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),
&mlxsw_sp->netdevice_nb);
......
......@@ -32,8 +32,6 @@
#define MLXSW_SP_MID_MAX 7000
#define MLXSW_SP_PORTS_PER_CLUSTER_MAX 4
#define MLXSW_SP_PORT_BASE_SPEED_25G 25000 /* Mb/s */
#define MLXSW_SP_PORT_BASE_SPEED_50G 50000 /* Mb/s */
......@@ -143,6 +141,12 @@ struct mlxsw_sp_port_type_speed_ops;
struct mlxsw_sp_ptp_state;
struct mlxsw_sp_ptp_ops;
struct mlxsw_sp_port_mapping {
u8 module;
u8 width;
u8 lane;
};
struct mlxsw_sp {
struct mlxsw_sp_port **ports;
struct mlxsw_core *core;
......@@ -150,7 +154,7 @@ struct mlxsw_sp {
unsigned char base_mac[ETH_ALEN];
const unsigned char *mac_mask;
struct mlxsw_sp_upper *lags;
int *port_to_module;
struct mlxsw_sp_port_mapping **port_mapping;
struct mlxsw_sp_sb *sb;
struct mlxsw_sp_bridge *bridge;
struct mlxsw_sp_router *router;
......@@ -259,11 +263,11 @@ struct mlxsw_sp_port {
struct ieee_pfc *pfc;
enum mlxsw_reg_qpts_trust_state trust_state;
} dcb;
struct {
u8 module;
u8 width;
u8 lane;
} mapping;
struct mlxsw_sp_port_mapping mapping; /* mapping is constant during the
* mlxsw_sp_port lifetime, however
* the same localport can have
* different mapping.
*/
/* TC handles */
struct list_head mall_tc_list;
struct {
......@@ -287,6 +291,7 @@ struct mlxsw_sp_port {
u16 egr_types;
struct mlxsw_sp_ptp_port_stats stats;
} ptp;
u8 split_base_local_port;
};
struct mlxsw_sp_port_type_speed_ops {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment