diff options
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlxsw/spectrum.c')
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 515 |
1 files changed, 365 insertions, 150 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index dcf9562bce8a..f7fd5e8fbf96 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -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> @@ -48,7 +49,7 @@ #define MLXSW_SP1_FWREV_MAJOR 13 #define MLXSW_SP1_FWREV_MINOR 2000 -#define MLXSW_SP1_FWREV_SUBMINOR 1886 +#define MLXSW_SP1_FWREV_SUBMINOR 2308 #define MLXSW_SP1_FWREV_CAN_RESET_MINOR 1702 static const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = { @@ -63,6 +64,21 @@ static const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = { "." __stringify(MLXSW_SP1_FWREV_MINOR) \ "." __stringify(MLXSW_SP1_FWREV_SUBMINOR) ".mfa2" +#define MLXSW_SP2_FWREV_MAJOR 29 +#define MLXSW_SP2_FWREV_MINOR 2000 +#define MLXSW_SP2_FWREV_SUBMINOR 2308 + +static const struct mlxsw_fw_rev mlxsw_sp2_fw_rev = { + .major = MLXSW_SP2_FWREV_MAJOR, + .minor = MLXSW_SP2_FWREV_MINOR, + .subminor = MLXSW_SP2_FWREV_SUBMINOR, +}; + +#define MLXSW_SP2_FW_FILENAME \ + "mellanox/mlxsw_spectrum2-" __stringify(MLXSW_SP2_FWREV_MAJOR) \ + "." __stringify(MLXSW_SP2_FWREV_MINOR) \ + "." __stringify(MLXSW_SP2_FWREV_SUBMINOR) ".mfa2" + static const char mlxsw_sp1_driver_name[] = "mlxsw_spectrum"; static const char mlxsw_sp2_driver_name[] = "mlxsw_spectrum2"; static const char mlxsw_sp3_driver_name[] = "mlxsw_spectrum3"; @@ -409,9 +425,7 @@ static int mlxsw_sp_fw_rev_validate(struct mlxsw_sp *mlxsw_sp) } if (MLXSW_SP_FWREV_MINOR_TO_BRANCH(rev->minor) == MLXSW_SP_FWREV_MINOR_TO_BRANCH(req_rev->minor) && - (rev->minor > req_rev->minor || - (rev->minor == req_rev->minor && - rev->subminor >= req_rev->subminor))) + mlxsw_core_fw_rev_minor_subminor_validate(rev, req_rev)) return 0; dev_info(mlxsw_sp->bus_info->dev, "The firmware version %d.%d.%d is incompatible with the driver\n", @@ -735,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); @@ -2914,9 +2962,22 @@ mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4[] = { #define MLXSW_SP2_MASK_ETHTOOL_200GAUI_4_200GBASE_CR4_KR4_LEN \ ARRAY_SIZE(mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4) +static const enum ethtool_link_mode_bit_indices +mlxsw_sp2_mask_ethtool_400gaui_8[] = { + ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT, + ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT, + ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT, + ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT, + ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT, +}; + +#define MLXSW_SP2_MASK_ETHTOOL_400GAUI_8_LEN \ + ARRAY_SIZE(mlxsw_sp2_mask_ethtool_400gaui_8) + #define MLXSW_SP_PORT_MASK_WIDTH_1X BIT(0) #define MLXSW_SP_PORT_MASK_WIDTH_2X BIT(1) #define MLXSW_SP_PORT_MASK_WIDTH_4X BIT(2) +#define MLXSW_SP_PORT_MASK_WIDTH_8X BIT(3) static u8 mlxsw_sp_port_mask_width_get(u8 width) { @@ -2927,6 +2988,8 @@ static u8 mlxsw_sp_port_mask_width_get(u8 width) return MLXSW_SP_PORT_MASK_WIDTH_2X; case 4: return MLXSW_SP_PORT_MASK_WIDTH_4X; + case 8: + return MLXSW_SP_PORT_MASK_WIDTH_8X; default: WARN_ON_ONCE(1); return 0; @@ -2948,7 +3011,8 @@ static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = { .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_SGMII_100M_LEN, .mask_width = MLXSW_SP_PORT_MASK_WIDTH_1X | MLXSW_SP_PORT_MASK_WIDTH_2X | - MLXSW_SP_PORT_MASK_WIDTH_4X, + MLXSW_SP_PORT_MASK_WIDTH_4X | + MLXSW_SP_PORT_MASK_WIDTH_8X, .speed = SPEED_100, }, { @@ -2957,7 +3021,8 @@ static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = { .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_1000BASE_X_SGMII_LEN, .mask_width = MLXSW_SP_PORT_MASK_WIDTH_1X | MLXSW_SP_PORT_MASK_WIDTH_2X | - MLXSW_SP_PORT_MASK_WIDTH_4X, + MLXSW_SP_PORT_MASK_WIDTH_4X | + MLXSW_SP_PORT_MASK_WIDTH_8X, .speed = SPEED_1000, }, { @@ -2966,7 +3031,8 @@ static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = { .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_2_5GBASE_X_2_5GMII_LEN, .mask_width = MLXSW_SP_PORT_MASK_WIDTH_1X | MLXSW_SP_PORT_MASK_WIDTH_2X | - MLXSW_SP_PORT_MASK_WIDTH_4X, + MLXSW_SP_PORT_MASK_WIDTH_4X | + MLXSW_SP_PORT_MASK_WIDTH_8X, .speed = SPEED_2500, }, { @@ -2975,7 +3041,8 @@ static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = { .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_5GBASE_R_LEN, .mask_width = MLXSW_SP_PORT_MASK_WIDTH_1X | MLXSW_SP_PORT_MASK_WIDTH_2X | - MLXSW_SP_PORT_MASK_WIDTH_4X, + MLXSW_SP_PORT_MASK_WIDTH_4X | + MLXSW_SP_PORT_MASK_WIDTH_8X, .speed = SPEED_5000, }, { @@ -2984,14 +3051,16 @@ static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = { .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_XFI_XAUI_1_10G_LEN, .mask_width = MLXSW_SP_PORT_MASK_WIDTH_1X | MLXSW_SP_PORT_MASK_WIDTH_2X | - MLXSW_SP_PORT_MASK_WIDTH_4X, + MLXSW_SP_PORT_MASK_WIDTH_4X | + MLXSW_SP_PORT_MASK_WIDTH_8X, .speed = SPEED_10000, }, { .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_XLAUI_4_XLPPI_4_40G, .mask_ethtool = mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g, .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_XLAUI_4_XLPPI_4_40G_LEN, - .mask_width = MLXSW_SP_PORT_MASK_WIDTH_4X, + .mask_width = MLXSW_SP_PORT_MASK_WIDTH_4X | + MLXSW_SP_PORT_MASK_WIDTH_8X, .speed = SPEED_40000, }, { @@ -3000,7 +3069,8 @@ static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = { .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_25GAUI_1_25GBASE_CR_KR_LEN, .mask_width = MLXSW_SP_PORT_MASK_WIDTH_1X | MLXSW_SP_PORT_MASK_WIDTH_2X | - MLXSW_SP_PORT_MASK_WIDTH_4X, + MLXSW_SP_PORT_MASK_WIDTH_4X | + MLXSW_SP_PORT_MASK_WIDTH_8X, .speed = SPEED_25000, }, { @@ -3008,7 +3078,8 @@ static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = { .mask_ethtool = mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2, .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_50GAUI_2_LAUI_2_50GBASE_CR2_KR2_LEN, .mask_width = MLXSW_SP_PORT_MASK_WIDTH_2X | - MLXSW_SP_PORT_MASK_WIDTH_4X, + MLXSW_SP_PORT_MASK_WIDTH_4X | + MLXSW_SP_PORT_MASK_WIDTH_8X, .speed = SPEED_50000, }, { @@ -3022,7 +3093,8 @@ static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = { .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_CAUI_4_100GBASE_CR4_KR4, .mask_ethtool = mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4, .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_CAUI_4_100GBASE_CR4_KR4_LEN, - .mask_width = MLXSW_SP_PORT_MASK_WIDTH_4X, + .mask_width = MLXSW_SP_PORT_MASK_WIDTH_4X | + MLXSW_SP_PORT_MASK_WIDTH_8X, .speed = SPEED_100000, }, { @@ -3036,9 +3108,17 @@ static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = { .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_200GAUI_4_200GBASE_CR4_KR4, .mask_ethtool = mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4, .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_200GAUI_4_200GBASE_CR4_KR4_LEN, - .mask_width = MLXSW_SP_PORT_MASK_WIDTH_4X, + .mask_width = MLXSW_SP_PORT_MASK_WIDTH_4X | + MLXSW_SP_PORT_MASK_WIDTH_8X, .speed = SPEED_200000, }, + { + .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_400GAUI_8, + .mask_ethtool = mlxsw_sp2_mask_ethtool_400gaui_8, + .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_400GAUI_8_LEN, + .mask_width = MLXSW_SP_PORT_MASK_WIDTH_8X, + .speed = SPEED_400000, + }, }; #define MLXSW_SP2_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sp2_port_link_mode) @@ -3435,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; @@ -3451,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, @@ -3612,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) { @@ -3635,15 +3718,15 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, goto err_alloc_etherdev; } SET_NETDEV_DEV(dev, mlxsw_sp->bus_info->dev); + dev_net_set(dev, mlxsw_sp_net(mlxsw_sp)); mlxsw_sp_port = netdev_priv(dev); mlxsw_sp_port->dev = dev; mlxsw_sp_port->mlxsw_sp = mlxsw_sp; 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); @@ -3668,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); @@ -3710,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); @@ -3933,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; @@ -3950,66 +4032,100 @@ 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) { - u8 offset = (local_port - 1) % MLXSW_SP_PORTS_PER_CLUSTER_MAX; + 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]) { + err = -ENOMEM; + 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) % 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; @@ -4022,45 +4138,55 @@ 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, 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", @@ -4069,47 +4195,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; + } - 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"); + /* 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 (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"); + 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; } - /* 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]) { + 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; + } + + /* 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; @@ -4118,7 +4267,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; } @@ -4126,19 +4275,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", @@ -4153,25 +4296,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; } @@ -4364,8 +4512,6 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { MLXSW_SP_RXL_NO_MARK(IPV6_MLDV2_LISTENER_REPORT, TRAP_TO_CPU, IPV6_MLD, false), /* L3 traps */ - MLXSW_SP_RXL_MARK(MTUERROR, TRAP_TO_CPU, ROUTER_EXP, false), - MLXSW_SP_RXL_MARK(TTLERROR, TRAP_TO_CPU, ROUTER_EXP, false), MLXSW_SP_RXL_L3_MARK(LBERROR, MIRROR_TO_CPU, LBERROR, false), MLXSW_SP_RXL_MARK(IP2ME, TRAP_TO_CPU, IP2ME, false), MLXSW_SP_RXL_MARK(IPV6_UNSPECIFIED_ADDRESS, TRAP_TO_CPU, ROUTER_EXP, @@ -4392,14 +4538,12 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { MLXSW_SP_RXL_MARK(L3_IPV6_REDIRECTION, TRAP_TO_CPU, IPV6_ND, false), MLXSW_SP_RXL_MARK(IPV6_MC_LINK_LOCAL_DEST, TRAP_TO_CPU, ROUTER_EXP, false), - MLXSW_SP_RXL_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, HOST_MISS, false), - MLXSW_SP_RXL_MARK(HOST_MISS_IPV6, TRAP_TO_CPU, HOST_MISS, false), MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV4, TRAP_TO_CPU, ROUTER_EXP, false), MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV6, TRAP_TO_CPU, ROUTER_EXP, false), MLXSW_SP_RXL_MARK(IPIP_DECAP_ERROR, TRAP_TO_CPU, ROUTER_EXP, false), MLXSW_SP_RXL_MARK(DECAP_ECN0, TRAP_TO_CPU, ROUTER_EXP, false), - MLXSW_SP_RXL_MARK(IPV4_VRRP, TRAP_TO_CPU, ROUTER_EXP, false), - MLXSW_SP_RXL_MARK(IPV6_VRRP, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(IPV4_VRRP, TRAP_TO_CPU, VRRP, false), + MLXSW_SP_RXL_MARK(IPV6_VRRP, TRAP_TO_CPU, VRRP, false), /* PKT Sample trap */ MLXSW_RXL(mlxsw_sp_rx_listener_sample_func, PKT_SAMPLE, MIRROR_TO_CPU, false, SP_IP2ME, DISCARD), @@ -4408,7 +4552,6 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { /* Multicast Router Traps */ MLXSW_SP_RXL_MARK(IPV4_PIM, TRAP_TO_CPU, PIM, false), MLXSW_SP_RXL_MARK(IPV6_PIM, TRAP_TO_CPU, PIM, false), - MLXSW_SP_RXL_MARK(RPF, TRAP_TO_CPU, RPF, false), MLXSW_SP_RXL_MARK(ACL1, TRAP_TO_CPU, MULTICAST, false), MLXSW_SP_RXL_L3_MARK(ACL2, TRAP_TO_CPU, MULTICAST, false), /* NVE traps */ @@ -4483,6 +4626,10 @@ static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core) rate = 19 * 1024; burst_size = 12; break; + case MLXSW_REG_HTGT_TRAP_GROUP_SP_VRRP: + rate = 360; + burst_size = 7; + break; default: continue; } @@ -4522,6 +4669,7 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core) case MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF: case MLXSW_REG_HTGT_TRAP_GROUP_SP_PIM: case MLXSW_REG_HTGT_TRAP_GROUP_SP_PTP0: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_VRRP: priority = 5; tc = 5; break; @@ -4738,7 +4886,8 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused, unsigned long event, void *ptr); static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, - const struct mlxsw_bus_info *mlxsw_bus_info) + const struct mlxsw_bus_info *mlxsw_bus_info, + struct netlink_ext_ack *extack) { struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); int err; @@ -4750,6 +4899,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, if (err) return err; + mlxsw_core_emad_string_tlv_enable(mlxsw_core); + err = mlxsw_sp_base_mac_get(mlxsw_sp); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Failed to get base mac\n"); @@ -4831,7 +4982,7 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, goto err_acl_init; } - err = mlxsw_sp_router_init(mlxsw_sp); + err = mlxsw_sp_router_init(mlxsw_sp, extack); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize router\n"); goto err_router_init; @@ -4864,7 +5015,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, * respin. */ mlxsw_sp->netdevice_nb.notifier_call = mlxsw_sp_netdevice_event; - err = register_netdevice_notifier(&mlxsw_sp->netdevice_nb); + err = register_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp), + &mlxsw_sp->netdevice_nb); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Failed to register netdev notifier\n"); goto err_netdev_notifier; @@ -4876,6 +5028,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"); @@ -4885,9 +5043,12 @@ 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(&mlxsw_sp->netdevice_nb); + unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp), + &mlxsw_sp->netdevice_nb); err_netdev_notifier: if (mlxsw_sp->clock) mlxsw_sp->ptp_ops->fini(mlxsw_sp->ptp_state); @@ -4924,7 +5085,8 @@ err_fids_init: } static int mlxsw_sp1_init(struct mlxsw_core *mlxsw_core, - const struct mlxsw_bus_info *mlxsw_bus_info) + const struct mlxsw_bus_info *mlxsw_bus_info, + struct netlink_ext_ack *extack) { struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); @@ -4944,14 +5106,17 @@ static int mlxsw_sp1_init(struct mlxsw_core *mlxsw_core, mlxsw_sp->listeners = mlxsw_sp1_listener; mlxsw_sp->listeners_count = ARRAY_SIZE(mlxsw_sp1_listener); - return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info); + return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack); } static int mlxsw_sp2_init(struct mlxsw_core *mlxsw_core, - const struct mlxsw_bus_info *mlxsw_bus_info) + const struct mlxsw_bus_info *mlxsw_bus_info, + struct netlink_ext_ack *extack) { struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); + mlxsw_sp->req_rev = &mlxsw_sp2_fw_rev; + mlxsw_sp->fw_filename = MLXSW_SP2_FW_FILENAME; mlxsw_sp->kvdl_ops = &mlxsw_sp2_kvdl_ops; mlxsw_sp->afa_ops = &mlxsw_sp2_act_afa_ops; mlxsw_sp->afk_ops = &mlxsw_sp2_afk_ops; @@ -4964,7 +5129,7 @@ static int mlxsw_sp2_init(struct mlxsw_core *mlxsw_core, mlxsw_sp->port_type_speed_ops = &mlxsw_sp2_port_type_speed_ops; mlxsw_sp->ptp_ops = &mlxsw_sp2_ptp_ops; - return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info); + return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack); } static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core) @@ -4972,8 +5137,10 @@ 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(&mlxsw_sp->netdevice_nb); + unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp), + &mlxsw_sp->netdevice_nb); if (mlxsw_sp->clock) { mlxsw_sp->ptp_ops->fini(mlxsw_sp->ptp_state); mlxsw_sp->ptp_ops->clock_fini(mlxsw_sp->clock); @@ -5165,14 +5332,61 @@ static int mlxsw_sp2_resources_kvd_register(struct mlxsw_core *mlxsw_core) &kvd_size_params); } +static int mlxsw_sp_resources_span_register(struct mlxsw_core *mlxsw_core) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_core); + struct devlink_resource_size_params span_size_params; + u32 max_span; + + if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_SPAN)) + return -EIO; + + max_span = MLXSW_CORE_RES_GET(mlxsw_core, MAX_SPAN); + devlink_resource_size_params_init(&span_size_params, max_span, max_span, + 1, DEVLINK_RESOURCE_UNIT_ENTRY); + + return devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_SPAN, + max_span, MLXSW_SP_RESOURCE_SPAN, + DEVLINK_RESOURCE_ID_PARENT_TOP, + &span_size_params); +} + static int mlxsw_sp1_resources_register(struct mlxsw_core *mlxsw_core) { - return mlxsw_sp1_resources_kvd_register(mlxsw_core); + int err; + + err = mlxsw_sp1_resources_kvd_register(mlxsw_core); + if (err) + return err; + + err = mlxsw_sp_resources_span_register(mlxsw_core); + if (err) + goto err_resources_span_register; + + return 0; + +err_resources_span_register: + devlink_resources_unregister(priv_to_devlink(mlxsw_core), NULL); + return err; } static int mlxsw_sp2_resources_register(struct mlxsw_core *mlxsw_core) { - return mlxsw_sp2_resources_kvd_register(mlxsw_core); + int err; + + err = mlxsw_sp2_resources_kvd_register(mlxsw_core); + if (err) + return err; + + err = mlxsw_sp_resources_span_register(mlxsw_core); + if (err) + goto err_resources_span_register; + + return 0; + +err_resources_span_register: + devlink_resources_unregister(priv_to_devlink(mlxsw_core), NULL); + return err; } static int mlxsw_sp_kvd_sizes_get(struct mlxsw_core *mlxsw_core, @@ -6565,3 +6779,4 @@ MODULE_DEVICE_TABLE(pci, mlxsw_sp1_pci_id_table); MODULE_DEVICE_TABLE(pci, mlxsw_sp2_pci_id_table); MODULE_DEVICE_TABLE(pci, mlxsw_sp3_pci_id_table); MODULE_FIRMWARE(MLXSW_SP1_FW_FILENAME); +MODULE_FIRMWARE(MLXSW_SP2_FW_FILENAME); |