aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/net/ethernet/mellanox/mlxsw
diff options
context:
space:
mode:
authorMaarten Lankhorst <maarten.lankhorst@linux.intel.com>2020-08-25 11:00:02 +0200
committerMaarten Lankhorst <maarten.lankhorst@linux.intel.com>2020-08-25 11:00:02 +0200
commit2d9ad4cfaf4d32a64a4ed556e5bcab9121215026 (patch)
tree3572e6cd05effa4e2943cee817defb2b9a72afd1 /drivers/net/ethernet/mellanox/mlxsw
parentdrm/modeset-lock: Take the modeset BKL for legacy drivers (diff)
parentLinux 5.9-rc2 (diff)
downloadwireguard-linux-2d9ad4cfaf4d32a64a4ed556e5bcab9121215026.tar.xz
wireguard-linux-2d9ad4cfaf4d32a64a4ed556e5bcab9121215026.zip
Merge tag 'v5.9-rc2' into drm-misc-fixes
Backmerge requested by Tomi for a fix to omap inconsistent locking state issue, and because we need at least v5.9-rc2 now. Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlxsw')
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/Makefile3
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.c42
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.h25
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c355
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h10
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_env.c53
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_thermal.c91
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/minimal.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/pci.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/pci_hw.h7
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/reg.h196
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/resources.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.c1680
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.h151
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c108
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.c31
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c8
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c1644
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_flow.c18
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c30
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_matchall.c29
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_policer.c468
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c524
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c59
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c655
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h33
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c269
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.h18
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/switchib.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/switchx2.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/trap.h10
32 files changed, 4637 insertions, 1901 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile
index 4aeabb35c943..892724380ea2 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/Makefile
+++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile
@@ -30,7 +30,8 @@ mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \
spectrum_mr_tcam.o spectrum_mr.o \
spectrum_qdisc.o spectrum_span.o \
spectrum_nve.o spectrum_nve_vxlan.o \
- spectrum_dpipe.o spectrum_trap.o
+ spectrum_dpipe.o spectrum_trap.o \
+ spectrum_ethtool.o spectrum_policer.o
mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB) += spectrum_dcb.o
mlxsw_spectrum-$(CONFIG_PTP_1588_CLOCK) += spectrum_ptp.o
obj-$(CONFIG_MLXSW_MINIMAL) += mlxsw_minimal.o
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c
index d6d6fe64887b..08d101138fbe 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.c
@@ -1177,14 +1177,15 @@ static void mlxsw_devlink_trap_fini(struct devlink *devlink,
static int mlxsw_devlink_trap_action_set(struct devlink *devlink,
const struct devlink_trap *trap,
- enum devlink_trap_action action)
+ enum devlink_trap_action action,
+ struct netlink_ext_ack *extack)
{
struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
if (!mlxsw_driver->trap_action_set)
return -EOPNOTSUPP;
- return mlxsw_driver->trap_action_set(mlxsw_core, trap, action);
+ return mlxsw_driver->trap_action_set(mlxsw_core, trap, action, extack);
}
static int
@@ -1202,14 +1203,15 @@ mlxsw_devlink_trap_group_init(struct devlink *devlink,
static int
mlxsw_devlink_trap_group_set(struct devlink *devlink,
const struct devlink_trap_group *group,
- const struct devlink_trap_policer *policer)
+ const struct devlink_trap_policer *policer,
+ struct netlink_ext_ack *extack)
{
struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
if (!mlxsw_driver->trap_group_set)
return -EOPNOTSUPP;
- return mlxsw_driver->trap_group_set(mlxsw_core, group, policer);
+ return mlxsw_driver->trap_group_set(mlxsw_core, group, policer, extack);
}
static int
@@ -1525,7 +1527,8 @@ static bool __is_rx_listener_equal(const struct mlxsw_rx_listener *rxl_a,
{
return (rxl_a->func == rxl_b->func &&
rxl_a->local_port == rxl_b->local_port &&
- rxl_a->trap_id == rxl_b->trap_id);
+ rxl_a->trap_id == rxl_b->trap_id &&
+ rxl_a->mirror_reason == rxl_b->mirror_reason);
}
static struct mlxsw_rx_listener_item *
@@ -1814,7 +1817,7 @@ static int mlxsw_core_reg_access_emad(struct mlxsw_core *mlxsw_core,
err = mlxsw_emad_reg_access(mlxsw_core, reg, payload, type, trans,
bulk_list, cb, cb_priv, tid);
if (err) {
- kfree(trans);
+ kfree_rcu(trans, rcu);
return err;
}
return 0;
@@ -2045,17 +2048,20 @@ void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb,
rxl = &rxl_item->rxl;
if ((rxl->local_port == MLXSW_PORT_DONT_CARE ||
rxl->local_port == local_port) &&
- rxl->trap_id == rx_info->trap_id) {
+ rxl->trap_id == rx_info->trap_id &&
+ rxl->mirror_reason == rx_info->mirror_reason) {
if (rxl_item->enabled)
found = true;
break;
}
}
- rcu_read_unlock();
- if (!found)
+ if (!found) {
+ rcu_read_unlock();
goto drop;
+ }
rxl->func(skb, local_port, rxl_item->priv);
+ rcu_read_unlock();
return;
drop:
@@ -2123,6 +2129,7 @@ static int __mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port,
enum devlink_port_flavour flavour,
u32 port_number, bool split,
u32 split_port_subnumber,
+ bool splittable, u32 lanes,
const unsigned char *switch_id,
unsigned char switch_id_len)
{
@@ -2130,12 +2137,19 @@ static int __mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port,
struct mlxsw_core_port *mlxsw_core_port =
&mlxsw_core->ports[local_port];
struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port;
+ struct devlink_port_attrs attrs = {};
int err;
+ attrs.split = split;
+ attrs.lanes = lanes;
+ attrs.splittable = splittable;
+ attrs.flavour = flavour;
+ attrs.phys.port_number = port_number;
+ attrs.phys.split_subport_number = split_port_subnumber;
+ memcpy(attrs.switch_id.id, switch_id, switch_id_len);
+ attrs.switch_id.id_len = switch_id_len;
mlxsw_core_port->local_port = local_port;
- devlink_port_attrs_set(devlink_port, flavour, port_number,
- split, split_port_subnumber,
- switch_id, switch_id_len);
+ devlink_port_attrs_set(devlink_port, &attrs);
err = devlink_port_register(devlink, devlink_port, local_port);
if (err)
memset(mlxsw_core_port, 0, sizeof(*mlxsw_core_port));
@@ -2155,12 +2169,14 @@ static void __mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port)
int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port,
u32 port_number, bool split,
u32 split_port_subnumber,
+ bool splittable, u32 lanes,
const unsigned char *switch_id,
unsigned char switch_id_len)
{
return __mlxsw_core_port_init(mlxsw_core, local_port,
DEVLINK_PORT_FLAVOUR_PHYSICAL,
port_number, split, split_port_subnumber,
+ splittable, lanes,
switch_id, switch_id_len);
}
EXPORT_SYMBOL(mlxsw_core_port_init);
@@ -2182,7 +2198,7 @@ int mlxsw_core_cpu_port_init(struct mlxsw_core *mlxsw_core,
err = __mlxsw_core_port_init(mlxsw_core, MLXSW_PORT_CPU_PORT,
DEVLINK_PORT_FLAVOUR_CPU,
- 0, false, 0,
+ 0, false, 0, false, 0,
switch_id, switch_id_len);
if (err)
return err;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h
index 22b0dfa7cfae..11af3308f8cc 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.h
@@ -61,6 +61,7 @@ void mlxsw_core_ptp_transmitted(struct mlxsw_core *mlxsw_core,
struct mlxsw_rx_listener {
void (*func)(struct sk_buff *skb, u8 local_port, void *priv);
u8 local_port;
+ u8 mirror_reason;
u16 trap_id;
};
@@ -88,13 +89,15 @@ struct mlxsw_listener {
};
#define __MLXSW_RXL(_func, _trap_id, _en_action, _is_ctrl, _en_trap_group, \
- _dis_action, _enabled_on_register, _dis_trap_group) \
+ _dis_action, _enabled_on_register, _dis_trap_group, \
+ _mirror_reason) \
{ \
.trap_id = MLXSW_TRAP_ID_##_trap_id, \
.rx_listener = \
{ \
.func = _func, \
.local_port = MLXSW_PORT_DONT_CARE, \
+ .mirror_reason = _mirror_reason, \
.trap_id = MLXSW_TRAP_ID_##_trap_id, \
}, \
.en_action = MLXSW_REG_HPKT_ACTION_##_en_action, \
@@ -108,12 +111,17 @@ struct mlxsw_listener {
#define MLXSW_RXL(_func, _trap_id, _en_action, _is_ctrl, _trap_group, \
_dis_action) \
__MLXSW_RXL(_func, _trap_id, _en_action, _is_ctrl, _trap_group, \
- _dis_action, true, _trap_group)
+ _dis_action, true, _trap_group, 0)
#define MLXSW_RXL_DIS(_func, _trap_id, _en_action, _is_ctrl, _en_trap_group, \
_dis_action, _dis_trap_group) \
__MLXSW_RXL(_func, _trap_id, _en_action, _is_ctrl, _en_trap_group, \
- _dis_action, false, _dis_trap_group)
+ _dis_action, false, _dis_trap_group, 0)
+
+#define MLXSW_RXL_MIRROR(_func, _session_id, _trap_group, _mirror_reason) \
+ __MLXSW_RXL(_func, MIRROR_SESSION##_session_id, TRAP_TO_CPU, false, \
+ _trap_group, TRAP_TO_CPU, true, _trap_group, \
+ _mirror_reason)
#define MLXSW_EVENTL(_func, _trap_id, _trap_group) \
{ \
@@ -176,6 +184,7 @@ struct mlxsw_rx_info {
u16 lag_id;
} u;
u8 lag_port_index;
+ u8 mirror_reason;
int trap_id;
};
@@ -191,8 +200,8 @@ void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core,
void *mlxsw_core_port_driver_priv(struct mlxsw_core_port *mlxsw_core_port);
int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port,
- u32 port_number, bool split,
- u32 split_port_subnumber,
+ u32 port_number, bool split, u32 split_port_subnumber,
+ bool splittable, u32 lanes,
const unsigned char *switch_id,
unsigned char switch_id_len);
void mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port);
@@ -324,12 +333,14 @@ struct mlxsw_driver {
const struct devlink_trap *trap, void *trap_ctx);
int (*trap_action_set)(struct mlxsw_core *mlxsw_core,
const struct devlink_trap *trap,
- enum devlink_trap_action action);
+ enum devlink_trap_action action,
+ struct netlink_ext_ack *extack);
int (*trap_group_init)(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group);
int (*trap_group_set)(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group,
- const struct devlink_trap_policer *policer);
+ const struct devlink_trap_policer *policer,
+ struct netlink_ext_ack *extack);
int (*trap_policer_init)(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer);
void (*trap_policer_fini)(struct mlxsw_core *mlxsw_core,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c
index c3d04319ff44..4d699fe98cb6 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c
@@ -67,7 +67,9 @@ struct mlxsw_afa {
struct rhashtable set_ht;
struct rhashtable fwd_entry_ht;
struct rhashtable cookie_ht;
+ struct rhashtable policer_ht;
struct idr cookie_idr;
+ struct list_head policer_list;
};
#define MLXSW_AFA_SET_LEN 0xA8
@@ -88,9 +90,11 @@ struct mlxsw_afa_set {
struct rhash_head ht_node;
struct mlxsw_afa_set_ht_key ht_key;
u32 kvdl_index;
- bool shared; /* Inserted in hashtable (doesn't mean that
+ u8 shared:1, /* Inserted in hashtable (doesn't mean that
* kvdl_index is valid).
*/
+ has_trap:1,
+ has_police:1;
unsigned int ref_count;
struct mlxsw_afa_set *next; /* Pointer to the next set. */
struct mlxsw_afa_set *prev; /* Pointer to the previous set,
@@ -175,6 +179,21 @@ static const struct rhashtable_params mlxsw_afa_cookie_ht_params = {
.automatic_shrinking = true,
};
+struct mlxsw_afa_policer {
+ struct rhash_head ht_node;
+ struct list_head list; /* Member of policer_list */
+ refcount_t ref_count;
+ u32 fa_index;
+ u16 policer_index;
+};
+
+static const struct rhashtable_params mlxsw_afa_policer_ht_params = {
+ .key_len = sizeof(u32),
+ .key_offset = offsetof(struct mlxsw_afa_policer, fa_index),
+ .head_offset = offsetof(struct mlxsw_afa_policer, ht_node),
+ .automatic_shrinking = true,
+};
+
struct mlxsw_afa *mlxsw_afa_create(unsigned int max_acts_per_set,
const struct mlxsw_afa_ops *ops,
void *ops_priv)
@@ -196,12 +215,19 @@ struct mlxsw_afa *mlxsw_afa_create(unsigned int max_acts_per_set,
&mlxsw_afa_cookie_ht_params);
if (err)
goto err_cookie_rhashtable_init;
+ err = rhashtable_init(&mlxsw_afa->policer_ht,
+ &mlxsw_afa_policer_ht_params);
+ if (err)
+ goto err_policer_rhashtable_init;
idr_init(&mlxsw_afa->cookie_idr);
+ INIT_LIST_HEAD(&mlxsw_afa->policer_list);
mlxsw_afa->max_acts_per_set = max_acts_per_set;
mlxsw_afa->ops = ops;
mlxsw_afa->ops_priv = ops_priv;
return mlxsw_afa;
+err_policer_rhashtable_init:
+ rhashtable_destroy(&mlxsw_afa->cookie_ht);
err_cookie_rhashtable_init:
rhashtable_destroy(&mlxsw_afa->fwd_entry_ht);
err_fwd_entry_rhashtable_init:
@@ -214,8 +240,10 @@ EXPORT_SYMBOL(mlxsw_afa_create);
void mlxsw_afa_destroy(struct mlxsw_afa *mlxsw_afa)
{
+ WARN_ON(!list_empty(&mlxsw_afa->policer_list));
WARN_ON(!idr_is_empty(&mlxsw_afa->cookie_idr));
idr_destroy(&mlxsw_afa->cookie_idr);
+ rhashtable_destroy(&mlxsw_afa->policer_ht);
rhashtable_destroy(&mlxsw_afa->cookie_ht);
rhashtable_destroy(&mlxsw_afa->fwd_entry_ht);
rhashtable_destroy(&mlxsw_afa->set_ht);
@@ -836,19 +864,172 @@ err_cookie_get:
return ERR_PTR(err);
}
+static struct mlxsw_afa_policer *
+mlxsw_afa_policer_create(struct mlxsw_afa *mlxsw_afa, u32 fa_index,
+ u64 rate_bytes_ps, u32 burst,
+ struct netlink_ext_ack *extack)
+{
+ struct mlxsw_afa_policer *policer;
+ int err;
+
+ policer = kzalloc(sizeof(*policer), GFP_KERNEL);
+ if (!policer)
+ return ERR_PTR(-ENOMEM);
+
+ err = mlxsw_afa->ops->policer_add(mlxsw_afa->ops_priv, rate_bytes_ps,
+ burst, &policer->policer_index,
+ extack);
+ if (err)
+ goto err_policer_add;
+
+ refcount_set(&policer->ref_count, 1);
+ policer->fa_index = fa_index;
+
+ err = rhashtable_insert_fast(&mlxsw_afa->policer_ht, &policer->ht_node,
+ mlxsw_afa_policer_ht_params);
+ if (err)
+ goto err_rhashtable_insert;
+
+ list_add_tail(&policer->list, &mlxsw_afa->policer_list);
+
+ return policer;
+
+err_rhashtable_insert:
+ mlxsw_afa->ops->policer_del(mlxsw_afa->ops_priv,
+ policer->policer_index);
+err_policer_add:
+ kfree(policer);
+ return ERR_PTR(err);
+}
+
+static void mlxsw_afa_policer_destroy(struct mlxsw_afa *mlxsw_afa,
+ struct mlxsw_afa_policer *policer)
+{
+ list_del(&policer->list);
+ rhashtable_remove_fast(&mlxsw_afa->policer_ht, &policer->ht_node,
+ mlxsw_afa_policer_ht_params);
+ mlxsw_afa->ops->policer_del(mlxsw_afa->ops_priv,
+ policer->policer_index);
+ kfree(policer);
+}
+
+static struct mlxsw_afa_policer *
+mlxsw_afa_policer_get(struct mlxsw_afa *mlxsw_afa, u32 fa_index,
+ u64 rate_bytes_ps, u32 burst,
+ struct netlink_ext_ack *extack)
+{
+ struct mlxsw_afa_policer *policer;
+
+ policer = rhashtable_lookup_fast(&mlxsw_afa->policer_ht, &fa_index,
+ mlxsw_afa_policer_ht_params);
+ if (policer) {
+ refcount_inc(&policer->ref_count);
+ return policer;
+ }
+
+ return mlxsw_afa_policer_create(mlxsw_afa, fa_index, rate_bytes_ps,
+ burst, extack);
+}
+
+static void mlxsw_afa_policer_put(struct mlxsw_afa *mlxsw_afa,
+ struct mlxsw_afa_policer *policer)
+{
+ if (!refcount_dec_and_test(&policer->ref_count))
+ return;
+ mlxsw_afa_policer_destroy(mlxsw_afa, policer);
+}
+
+struct mlxsw_afa_policer_ref {
+ struct mlxsw_afa_resource resource;
+ struct mlxsw_afa_policer *policer;
+};
+
+static void
+mlxsw_afa_policer_ref_destroy(struct mlxsw_afa_block *block,
+ struct mlxsw_afa_policer_ref *policer_ref)
+{
+ mlxsw_afa_resource_del(&policer_ref->resource);
+ mlxsw_afa_policer_put(block->afa, policer_ref->policer);
+ kfree(policer_ref);
+}
+
+static void
+mlxsw_afa_policer_ref_destructor(struct mlxsw_afa_block *block,
+ struct mlxsw_afa_resource *resource)
+{
+ struct mlxsw_afa_policer_ref *policer_ref;
+
+ policer_ref = container_of(resource, struct mlxsw_afa_policer_ref,
+ resource);
+ mlxsw_afa_policer_ref_destroy(block, policer_ref);
+}
+
+static struct mlxsw_afa_policer_ref *
+mlxsw_afa_policer_ref_create(struct mlxsw_afa_block *block, u32 fa_index,
+ u64 rate_bytes_ps, u32 burst,
+ struct netlink_ext_ack *extack)
+{
+ struct mlxsw_afa_policer_ref *policer_ref;
+ struct mlxsw_afa_policer *policer;
+ int err;
+
+ policer_ref = kzalloc(sizeof(*policer_ref), GFP_KERNEL);
+ if (!policer_ref)
+ return ERR_PTR(-ENOMEM);
+
+ policer = mlxsw_afa_policer_get(block->afa, fa_index, rate_bytes_ps,
+ burst, extack);
+ if (IS_ERR(policer)) {
+ err = PTR_ERR(policer);
+ goto err_policer_get;
+ }
+
+ policer_ref->policer = policer;
+ policer_ref->resource.destructor = mlxsw_afa_policer_ref_destructor;
+ mlxsw_afa_resource_add(block, &policer_ref->resource);
+
+ return policer_ref;
+
+err_policer_get:
+ kfree(policer_ref);
+ return ERR_PTR(err);
+}
+
#define MLXSW_AFA_ONE_ACTION_LEN 32
#define MLXSW_AFA_PAYLOAD_OFFSET 4
-static char *mlxsw_afa_block_append_action(struct mlxsw_afa_block *block,
- u8 action_code, u8 action_size)
+enum mlxsw_afa_action_type {
+ MLXSW_AFA_ACTION_TYPE_TRAP,
+ MLXSW_AFA_ACTION_TYPE_POLICE,
+ MLXSW_AFA_ACTION_TYPE_OTHER,
+};
+
+static bool
+mlxsw_afa_block_need_split(const struct mlxsw_afa_block *block,
+ enum mlxsw_afa_action_type type)
+{
+ struct mlxsw_afa_set *cur_set = block->cur_set;
+
+ /* Due to a hardware limitation, police action cannot be in the same
+ * action set with MLXSW_AFA_TRAP_CODE or MLXSW_AFA_TRAPWU_CODE
+ * actions. Work around this limitation by creating a new action set
+ * and place the new action there.
+ */
+ return (cur_set->has_trap && type == MLXSW_AFA_ACTION_TYPE_POLICE) ||
+ (cur_set->has_police && type == MLXSW_AFA_ACTION_TYPE_TRAP);
+}
+
+static char *mlxsw_afa_block_append_action_ext(struct mlxsw_afa_block *block,
+ u8 action_code, u8 action_size,
+ enum mlxsw_afa_action_type type)
{
char *oneact;
char *actions;
if (block->finished)
return ERR_PTR(-EINVAL);
- if (block->cur_act_index + action_size >
- block->afa->max_acts_per_set) {
+ if (block->cur_act_index + action_size > block->afa->max_acts_per_set ||
+ mlxsw_afa_block_need_split(block, type)) {
struct mlxsw_afa_set *set;
/* The appended action won't fit into the current action set,
@@ -863,6 +1044,17 @@ static char *mlxsw_afa_block_append_action(struct mlxsw_afa_block *block,
block->cur_set = set;
}
+ switch (type) {
+ case MLXSW_AFA_ACTION_TYPE_TRAP:
+ block->cur_set->has_trap = true;
+ break;
+ case MLXSW_AFA_ACTION_TYPE_POLICE:
+ block->cur_set->has_police = true;
+ break;
+ default:
+ break;
+ }
+
actions = block->cur_set->ht_key.enc_actions;
oneact = actions + block->cur_act_index * MLXSW_AFA_ONE_ACTION_LEN;
block->cur_act_index += action_size;
@@ -870,6 +1062,14 @@ static char *mlxsw_afa_block_append_action(struct mlxsw_afa_block *block,
return oneact + MLXSW_AFA_PAYLOAD_OFFSET;
}
+static char *mlxsw_afa_block_append_action(struct mlxsw_afa_block *block,
+ u8 action_code, u8 action_size)
+{
+ return mlxsw_afa_block_append_action_ext(block, action_code,
+ action_size,
+ MLXSW_AFA_ACTION_TYPE_OTHER);
+}
+
/* VLAN Action
* -----------
* VLAN action is used for manipulating VLANs. It can be used to implement QinQ,
@@ -1048,11 +1248,20 @@ mlxsw_afa_trap_mirror_pack(char *payload, bool mirror_enable,
mlxsw_afa_trap_mirror_agent_set(payload, mirror_agent);
}
+static char *mlxsw_afa_block_append_action_trap(struct mlxsw_afa_block *block,
+ u8 action_code, u8 action_size)
+{
+ return mlxsw_afa_block_append_action_ext(block, action_code,
+ action_size,
+ MLXSW_AFA_ACTION_TYPE_TRAP);
+}
+
static int mlxsw_afa_block_append_drop_plain(struct mlxsw_afa_block *block,
bool ingress)
{
- char *act = mlxsw_afa_block_append_action(block, MLXSW_AFA_TRAP_CODE,
- MLXSW_AFA_TRAP_SIZE);
+ char *act = mlxsw_afa_block_append_action_trap(block,
+ MLXSW_AFA_TRAP_CODE,
+ MLXSW_AFA_TRAP_SIZE);
if (IS_ERR(act))
return PTR_ERR(act);
@@ -1081,8 +1290,8 @@ mlxsw_afa_block_append_drop_with_cookie(struct mlxsw_afa_block *block,
}
cookie_index = cookie_ref->cookie->cookie_index;
- act = mlxsw_afa_block_append_action(block, MLXSW_AFA_TRAPWU_CODE,
- MLXSW_AFA_TRAPWU_SIZE);
+ act = mlxsw_afa_block_append_action_trap(block, MLXSW_AFA_TRAPWU_CODE,
+ MLXSW_AFA_TRAPWU_SIZE);
if (IS_ERR(act)) {
NL_SET_ERR_MSG_MOD(extack, "Cannot append drop with cookie action");
err = PTR_ERR(act);
@@ -1113,8 +1322,9 @@ EXPORT_SYMBOL(mlxsw_afa_block_append_drop);
int mlxsw_afa_block_append_trap(struct mlxsw_afa_block *block, u16 trap_id)
{
- char *act = mlxsw_afa_block_append_action(block, MLXSW_AFA_TRAP_CODE,
- MLXSW_AFA_TRAP_SIZE);
+ char *act = mlxsw_afa_block_append_action_trap(block,
+ MLXSW_AFA_TRAP_CODE,
+ MLXSW_AFA_TRAP_SIZE);
if (IS_ERR(act))
return PTR_ERR(act);
@@ -1127,8 +1337,9 @@ EXPORT_SYMBOL(mlxsw_afa_block_append_trap);
int mlxsw_afa_block_append_trap_and_forward(struct mlxsw_afa_block *block,
u16 trap_id)
{
- char *act = mlxsw_afa_block_append_action(block, MLXSW_AFA_TRAP_CODE,
- MLXSW_AFA_TRAP_SIZE);
+ char *act = mlxsw_afa_block_append_action_trap(block,
+ MLXSW_AFA_TRAP_CODE,
+ MLXSW_AFA_TRAP_SIZE);
if (IS_ERR(act))
return PTR_ERR(act);
@@ -1199,9 +1410,10 @@ static int
mlxsw_afa_block_append_allocated_mirror(struct mlxsw_afa_block *block,
u8 mirror_agent)
{
- char *act = mlxsw_afa_block_append_action(block,
- MLXSW_AFA_TRAP_CODE,
- MLXSW_AFA_TRAP_SIZE);
+ char *act = mlxsw_afa_block_append_action_trap(block,
+ MLXSW_AFA_TRAP_CODE,
+ MLXSW_AFA_TRAP_SIZE);
+
if (IS_ERR(act))
return PTR_ERR(act);
mlxsw_afa_trap_pack(act, MLXSW_AFA_TRAP_TRAP_ACTION_NOP,
@@ -1496,6 +1708,19 @@ EXPORT_SYMBOL(mlxsw_afa_block_append_fwd);
#define MLXSW_AFA_POLCNT_CODE 0x08
#define MLXSW_AFA_POLCNT_SIZE 1
+enum {
+ MLXSW_AFA_POLCNT_COUNTER,
+ MLXSW_AFA_POLCNT_POLICER,
+};
+
+/* afa_polcnt_c_p
+ * Counter or policer.
+ * Indicates whether the action binds a policer or a counter to the flow.
+ * 0: Counter
+ * 1: Policer
+ */
+MLXSW_ITEM32(afa, polcnt, c_p, 0x00, 31, 1);
+
enum mlxsw_afa_polcnt_counter_set_type {
/* No count */
MLXSW_AFA_POLCNT_COUNTER_SET_TYPE_NO_COUNT = 0x00,
@@ -1515,15 +1740,28 @@ MLXSW_ITEM32(afa, polcnt, counter_set_type, 0x04, 24, 8);
*/
MLXSW_ITEM32(afa, polcnt, counter_index, 0x04, 0, 24);
+/* afa_polcnt_pid
+ * Policer ID.
+ * Reserved when c_p = 0
+ */
+MLXSW_ITEM32(afa, polcnt, pid, 0x08, 0, 14);
+
static inline void
mlxsw_afa_polcnt_pack(char *payload,
enum mlxsw_afa_polcnt_counter_set_type set_type,
u32 counter_index)
{
+ mlxsw_afa_polcnt_c_p_set(payload, MLXSW_AFA_POLCNT_COUNTER);
mlxsw_afa_polcnt_counter_set_type_set(payload, set_type);
mlxsw_afa_polcnt_counter_index_set(payload, counter_index);
}
+static void mlxsw_afa_polcnt_policer_pack(char *payload, u16 policer_index)
+{
+ mlxsw_afa_polcnt_c_p_set(payload, MLXSW_AFA_POLCNT_POLICER);
+ mlxsw_afa_polcnt_pid_set(payload, policer_index);
+}
+
int mlxsw_afa_block_append_allocated_counter(struct mlxsw_afa_block *block,
u32 counter_index)
{
@@ -1567,6 +1805,40 @@ err_append_allocated_counter:
}
EXPORT_SYMBOL(mlxsw_afa_block_append_counter);
+int mlxsw_afa_block_append_police(struct mlxsw_afa_block *block,
+ u32 fa_index, u64 rate_bytes_ps, u32 burst,
+ u16 *p_policer_index,
+ struct netlink_ext_ack *extack)
+{
+ struct mlxsw_afa_policer_ref *policer_ref;
+ char *act;
+ int err;
+
+ policer_ref = mlxsw_afa_policer_ref_create(block, fa_index,
+ rate_bytes_ps,
+ burst, extack);
+ if (IS_ERR(policer_ref))
+ return PTR_ERR(policer_ref);
+ *p_policer_index = policer_ref->policer->policer_index;
+
+ act = mlxsw_afa_block_append_action_ext(block, MLXSW_AFA_POLCNT_CODE,
+ MLXSW_AFA_POLCNT_SIZE,
+ MLXSW_AFA_ACTION_TYPE_POLICE);
+ if (IS_ERR(act)) {
+ NL_SET_ERR_MSG_MOD(extack, "Cannot append police action");
+ err = PTR_ERR(act);
+ goto err_append_action;
+ }
+ mlxsw_afa_polcnt_policer_pack(act, *p_policer_index);
+
+ return 0;
+
+err_append_action:
+ mlxsw_afa_policer_ref_destroy(block, policer_ref);
+ return err;
+}
+EXPORT_SYMBOL(mlxsw_afa_block_append_police);
+
/* Virtual Router and Forwarding Domain Action
* -------------------------------------------
* Virtual Switch action is used for manipulate the Virtual Router (VR),
@@ -1684,3 +1956,54 @@ int mlxsw_afa_block_append_mcrouter(struct mlxsw_afa_block *block,
return 0;
}
EXPORT_SYMBOL(mlxsw_afa_block_append_mcrouter);
+
+/* L4 Port Action
+ * --------------
+ * The L4_PORT_ACTION is used for modifying the sport and dport fields of the packet, e.g. for NAT.
+ * If (the L4 is TCP) or if (the L4 is UDP and checksum field!=0) then the L4 checksum is updated.
+ */
+
+#define MLXSW_AFA_L4PORT_CODE 0x12
+#define MLXSW_AFA_L4PORT_SIZE 1
+
+enum mlxsw_afa_l4port_s_d {
+ /* configure src_l4_port */
+ MLXSW_AFA_L4PORT_S_D_SRC,
+ /* configure dst_l4_port */
+ MLXSW_AFA_L4PORT_S_D_DST,
+};
+
+/* afa_l4port_s_d
+ * Source or destination.
+ */
+MLXSW_ITEM32(afa, l4port, s_d, 0x00, 31, 1);
+
+/* afa_l4port_l4_port
+ * Number of port to change to.
+ */
+MLXSW_ITEM32(afa, l4port, l4_port, 0x08, 0, 16);
+
+static void mlxsw_afa_l4port_pack(char *payload, enum mlxsw_afa_l4port_s_d s_d, u16 l4_port)
+{
+ mlxsw_afa_l4port_s_d_set(payload, s_d);
+ mlxsw_afa_l4port_l4_port_set(payload, l4_port);
+}
+
+int mlxsw_afa_block_append_l4port(struct mlxsw_afa_block *block, bool is_dport, u16 l4_port,
+ struct netlink_ext_ack *extack)
+{
+ enum mlxsw_afa_l4port_s_d s_d = is_dport ? MLXSW_AFA_L4PORT_S_D_DST :
+ MLXSW_AFA_L4PORT_S_D_SRC;
+ char *act = mlxsw_afa_block_append_action(block,
+ MLXSW_AFA_L4PORT_CODE,
+ MLXSW_AFA_L4PORT_SIZE);
+
+ if (IS_ERR(act)) {
+ NL_SET_ERR_MSG_MOD(extack, "Cannot append L4_PORT action");
+ return PTR_ERR(act);
+ }
+
+ mlxsw_afa_l4port_pack(act, s_d, l4_port);
+ return 0;
+}
+EXPORT_SYMBOL(mlxsw_afa_block_append_l4port);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h
index 8c2705e16ef7..b652497b1002 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h
@@ -26,6 +26,10 @@ struct mlxsw_afa_ops {
bool ingress, int *p_span_id);
void (*mirror_del)(void *priv, u8 local_in_port, int span_id,
bool ingress);
+ int (*policer_add)(void *priv, u64 rate_bytes_ps, u32 burst,
+ u16 *p_policer_index,
+ struct netlink_ext_ack *extack);
+ void (*policer_del)(void *priv, u16 policer_index);
bool dummy_first_set;
};
@@ -82,5 +86,11 @@ int mlxsw_afa_block_append_fid_set(struct mlxsw_afa_block *block, u16 fid,
int mlxsw_afa_block_append_mcrouter(struct mlxsw_afa_block *block,
u16 expected_irif, u16 min_mtu,
bool rmid_valid, u32 kvdl_index);
+int mlxsw_afa_block_append_l4port(struct mlxsw_afa_block *block, bool is_dport, u16 l4_port,
+ struct netlink_ext_ack *extack);
+int mlxsw_afa_block_append_police(struct mlxsw_afa_block *block,
+ u32 fa_index, u64 rate_bytes_ps, u32 burst,
+ u16 *p_policer_index,
+ struct netlink_ext_ack *extack);
#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c
index a7d86df7123f..44fa02cbb683 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_env.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.c
@@ -11,7 +11,7 @@
#include "reg.h"
static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id,
- bool *qsfp)
+ bool *qsfp, bool *cmis)
{
char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
char mcia_pl[MLXSW_REG_MCIA_LEN];
@@ -25,15 +25,19 @@ static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id,
return err;
mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
ident = eeprom_tmp[0];
+ *cmis = false;
switch (ident) {
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
*qsfp = false;
break;
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP: /* fall-through */
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
- case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28: /* fall-through */
+ case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
+ *qsfp = true;
+ break;
case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
*qsfp = true;
+ *cmis = true;
break;
default:
return -EINVAL;
@@ -70,8 +74,9 @@ mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, int module,
if (qsfp) {
/* When reading upper pages 1, 2 and 3 the offset
* starts at 128. Please refer to "QSFP+ Memory Map"
- * figure in SFF-8436 specification for graphical
- * depiction.
+ * figure in SFF-8436 specification and to "CMIS Module
+ * Memory Map" figure in CMIS specification for
+ * graphical depiction.
*/
page = MLXSW_REG_MCIA_PAGE_GET(offset);
offset -= MLXSW_REG_MCIA_EEPROM_UP_PAGE_LENGTH * page;
@@ -116,7 +121,8 @@ int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
char mtmp_pl[MLXSW_REG_MTMP_LEN];
unsigned int module_temp;
- bool qsfp;
+ bool qsfp, cmis;
+ int page;
int err;
mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module,
@@ -140,21 +146,28 @@ int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
*/
/* Validate module identifier value. */
- err = mlxsw_env_validate_cable_ident(core, module, &qsfp);
+ err = mlxsw_env_validate_cable_ident(core, module, &qsfp, &cmis);
if (err)
return err;
- if (qsfp)
- mlxsw_reg_mcia_pack(mcia_pl, module, 0,
- MLXSW_REG_MCIA_TH_PAGE_NUM,
+ if (qsfp) {
+ /* For QSFP/CMIS module-defined thresholds are located in page
+ * 02h, otherwise in page 03h.
+ */
+ if (cmis)
+ page = MLXSW_REG_MCIA_TH_PAGE_CMIS_NUM;
+ else
+ page = MLXSW_REG_MCIA_TH_PAGE_NUM;
+ mlxsw_reg_mcia_pack(mcia_pl, module, 0, page,
MLXSW_REG_MCIA_TH_PAGE_OFF + off,
MLXSW_REG_MCIA_TH_ITEM_SIZE,
MLXSW_REG_MCIA_I2C_ADDR_LOW);
- else
+ } else {
mlxsw_reg_mcia_pack(mcia_pl, module, 0,
MLXSW_REG_MCIA_PAGE0_LO,
off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
MLXSW_REG_MCIA_I2C_ADDR_HIGH);
+ }
err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
if (err)
@@ -221,6 +234,22 @@ int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module,
else
modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;
break;
+ case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
+ /* Use SFF_8636 as base type. ethtool should recognize specific
+ * type through the identifier value.
+ */
+ modinfo->type = ETH_MODULE_SFF_8636;
+ /* Verify if module EEPROM is a flat memory. In case of flat
+ * memory only page 00h (0-255 bytes) can be read. Otherwise
+ * upper pages 01h and 02h can also be read. Upper pages 10h
+ * and 11h are currently not supported by the driver.
+ */
+ if (module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_TYPE_ID] &
+ MLXSW_REG_MCIA_EEPROM_CMIS_FLAT_MEMORY)
+ modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
+ else
+ modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
+ break;
default:
return -EINVAL;
}
@@ -235,8 +264,8 @@ int mlxsw_env_get_module_eeprom(struct net_device *netdev,
{
int offset = ee->offset;
unsigned int read_size;
+ bool qsfp, cmis;
int i = 0;
- bool qsfp;
int err;
if (!ee->len)
@@ -244,7 +273,7 @@ int mlxsw_env_get_module_eeprom(struct net_device *netdev,
memset(data, 0, ee->len);
/* Validate module identifier value. */
- err = mlxsw_env_validate_cable_ident(mlxsw_core, module, &qsfp);
+ err = mlxsw_env_validate_cable_ident(mlxsw_core, module, &qsfp, &cmis);
if (err)
return err;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
index 05f8d5a92862..8fa286ccdd6b 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
@@ -98,7 +98,6 @@ struct mlxsw_thermal_module {
struct mlxsw_thermal *parent;
struct thermal_zone_device *tzdev;
struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
- enum thermal_device_mode mode;
int module; /* Module or gearbox number */
};
@@ -110,7 +109,6 @@ struct mlxsw_thermal {
struct thermal_cooling_device *cdevs[MLXSW_MFCR_PWMS_MAX];
u8 cooling_levels[MLXSW_THERMAL_MAX_STATE + 1];
struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
- enum thermal_device_mode mode;
struct mlxsw_thermal_module *tz_module_arr;
u8 tz_module_num;
struct mlxsw_thermal_module *tz_gearbox_arr;
@@ -277,36 +275,6 @@ static int mlxsw_thermal_unbind(struct thermal_zone_device *tzdev,
return 0;
}
-static int mlxsw_thermal_get_mode(struct thermal_zone_device *tzdev,
- enum thermal_device_mode *mode)
-{
- struct mlxsw_thermal *thermal = tzdev->devdata;
-
- *mode = thermal->mode;
-
- return 0;
-}
-
-static int mlxsw_thermal_set_mode(struct thermal_zone_device *tzdev,
- enum thermal_device_mode mode)
-{
- struct mlxsw_thermal *thermal = tzdev->devdata;
-
- mutex_lock(&tzdev->lock);
-
- if (mode == THERMAL_DEVICE_ENABLED)
- tzdev->polling_delay = thermal->polling_delay;
- else
- tzdev->polling_delay = 0;
-
- mutex_unlock(&tzdev->lock);
-
- thermal->mode = mode;
- thermal_zone_device_update(tzdev, THERMAL_EVENT_UNSPECIFIED);
-
- return 0;
-}
-
static int mlxsw_thermal_get_temp(struct thermal_zone_device *tzdev,
int *p_temp)
{
@@ -406,8 +374,6 @@ static int mlxsw_thermal_trend_get(struct thermal_zone_device *tzdev,
static struct thermal_zone_device_ops mlxsw_thermal_ops = {
.bind = mlxsw_thermal_bind,
.unbind = mlxsw_thermal_unbind,
- .get_mode = mlxsw_thermal_get_mode,
- .set_mode = mlxsw_thermal_set_mode,
.get_temp = mlxsw_thermal_get_temp,
.get_trip_type = mlxsw_thermal_get_trip_type,
.get_trip_temp = mlxsw_thermal_get_trip_temp,
@@ -465,37 +431,6 @@ static int mlxsw_thermal_module_unbind(struct thermal_zone_device *tzdev,
return err;
}
-static int mlxsw_thermal_module_mode_get(struct thermal_zone_device *tzdev,
- enum thermal_device_mode *mode)
-{
- struct mlxsw_thermal_module *tz = tzdev->devdata;
-
- *mode = tz->mode;
-
- return 0;
-}
-
-static int mlxsw_thermal_module_mode_set(struct thermal_zone_device *tzdev,
- enum thermal_device_mode mode)
-{
- struct mlxsw_thermal_module *tz = tzdev->devdata;
- struct mlxsw_thermal *thermal = tz->parent;
-
- mutex_lock(&tzdev->lock);
-
- if (mode == THERMAL_DEVICE_ENABLED)
- tzdev->polling_delay = thermal->polling_delay;
- else
- tzdev->polling_delay = 0;
-
- mutex_unlock(&tzdev->lock);
-
- tz->mode = mode;
- thermal_zone_device_update(tzdev, THERMAL_EVENT_UNSPECIFIED);
-
- return 0;
-}
-
static int mlxsw_thermal_module_temp_get(struct thermal_zone_device *tzdev,
int *p_temp)
{
@@ -611,8 +546,6 @@ static int mlxsw_thermal_module_trend_get(struct thermal_zone_device *tzdev,
static struct thermal_zone_device_ops mlxsw_thermal_module_ops = {
.bind = mlxsw_thermal_module_bind,
.unbind = mlxsw_thermal_module_unbind,
- .get_mode = mlxsw_thermal_module_mode_get,
- .set_mode = mlxsw_thermal_module_mode_set,
.get_temp = mlxsw_thermal_module_temp_get,
.get_trip_type = mlxsw_thermal_module_trip_type_get,
.get_trip_temp = mlxsw_thermal_module_trip_temp_get,
@@ -650,8 +583,6 @@ static int mlxsw_thermal_gearbox_temp_get(struct thermal_zone_device *tzdev,
static struct thermal_zone_device_ops mlxsw_thermal_gearbox_ops = {
.bind = mlxsw_thermal_module_bind,
.unbind = mlxsw_thermal_module_unbind,
- .get_mode = mlxsw_thermal_module_mode_get,
- .set_mode = mlxsw_thermal_module_mode_set,
.get_temp = mlxsw_thermal_gearbox_temp_get,
.get_trip_type = mlxsw_thermal_module_trip_type_get,
.get_trip_temp = mlxsw_thermal_module_trip_temp_get,
@@ -780,8 +711,11 @@ mlxsw_thermal_module_tz_init(struct mlxsw_thermal_module *module_tz)
return err;
}
- module_tz->mode = THERMAL_DEVICE_ENABLED;
- return 0;
+ err = thermal_zone_device_enable(module_tz->tzdev);
+ if (err)
+ thermal_zone_device_unregister(module_tz->tzdev);
+
+ return err;
}
static void mlxsw_thermal_module_tz_fini(struct thermal_zone_device *tzdev)
@@ -884,6 +818,7 @@ static int
mlxsw_thermal_gearbox_tz_init(struct mlxsw_thermal_module *gearbox_tz)
{
char tz_name[MLXSW_THERMAL_ZONE_MAX_NAME];
+ int ret;
snprintf(tz_name, sizeof(tz_name), "mlxsw-gearbox%d",
gearbox_tz->module + 1);
@@ -896,8 +831,11 @@ mlxsw_thermal_gearbox_tz_init(struct mlxsw_thermal_module *gearbox_tz)
if (IS_ERR(gearbox_tz->tzdev))
return PTR_ERR(gearbox_tz->tzdev);
- gearbox_tz->mode = THERMAL_DEVICE_ENABLED;
- return 0;
+ ret = thermal_zone_device_enable(gearbox_tz->tzdev);
+ if (ret)
+ thermal_zone_device_unregister(gearbox_tz->tzdev);
+
+ return ret;
}
static void
@@ -1065,10 +1003,15 @@ int mlxsw_thermal_init(struct mlxsw_core *core,
if (err)
goto err_unreg_modules_tzdev;
- thermal->mode = THERMAL_DEVICE_ENABLED;
+ err = thermal_zone_device_enable(thermal->tzdev);
+ if (err)
+ goto err_unreg_gearboxes;
+
*p_thermal = thermal;
return 0;
+err_unreg_gearboxes:
+ mlxsw_thermal_gearboxes_fini(thermal);
err_unreg_modules_tzdev:
mlxsw_thermal_modules_fini(thermal);
err_unreg_tzdev:
diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c
index c4caeeadcba9..c010db2c9dba 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c
@@ -164,8 +164,8 @@ mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u8 local_port, u8 module)
int err;
err = mlxsw_core_port_init(mlxsw_m->core, local_port,
- module + 1, false, 0,
- mlxsw_m->base_mac,
+ module + 1, false, 0, false,
+ 0, mlxsw_m->base_mac,
sizeof(mlxsw_m->base_mac));
if (err) {
dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to init core port\n",
diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c
index c04ec1a92826..1c64b03ff48e 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/pci.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c
@@ -547,9 +547,9 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci,
{
struct pci_dev *pdev = mlxsw_pci->pdev;
struct mlxsw_pci_queue_elem_info *elem_info;
+ struct mlxsw_rx_info rx_info = {};
char *wqe;
struct sk_buff *skb;
- struct mlxsw_rx_info rx_info;
u16 byte_count;
int err;
@@ -582,6 +582,10 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci,
if (mlxsw_pci->max_cqe_ver >= MLXSW_PCI_CQE_V2)
cookie_index = mlxsw_pci_cqe2_user_def_val_orig_pkt_len_get(cqe);
mlxsw_skb_cb(skb)->cookie_index = cookie_index;
+ } else if (rx_info.trap_id >= MLXSW_TRAP_ID_MIRROR_SESSION0 &&
+ rx_info.trap_id <= MLXSW_TRAP_ID_MIRROR_SESSION7 &&
+ mlxsw_pci->max_cqe_ver >= MLXSW_PCI_CQE_V2) {
+ rx_info.mirror_reason = mlxsw_pci_cqe2_mirror_reason_get(cqe);
}
byte_count = mlxsw_pci_cqe_byte_count_get(cqe);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h b/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h
index 32c7cabfb261..a2c1fbd3e0d1 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/pci_hw.h
@@ -176,7 +176,7 @@ MLXSW_ITEM32(pci, cqe, byte_count, 0x04, 0, 14);
/* pci_cqe_trap_id
* Trap ID that captured the packet.
*/
-MLXSW_ITEM32(pci, cqe, trap_id, 0x08, 0, 9);
+MLXSW_ITEM32(pci, cqe, trap_id, 0x08, 0, 10);
/* pci_cqe_crc
* Length include CRC. Indicates the length field includes
@@ -213,6 +213,11 @@ mlxsw_pci_cqe_item_helpers(dqn, 0, 12, 12);
*/
MLXSW_ITEM32(pci, cqe2, user_def_val_orig_pkt_len, 0x14, 0, 20);
+/* pci_cqe_mirror_reason
+ * Mirror reason.
+ */
+MLXSW_ITEM32(pci, cqe2, mirror_reason, 0x18, 24, 8);
+
/* pci_cqe_owner
* Ownership bit.
*/
diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h
index fcb88d4271bf..079b080de7f7 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/reg.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h
@@ -3405,11 +3405,20 @@ MLXSW_ITEM32(reg, qpcr, violate_action, 0x18, 0, 4);
*/
MLXSW_ITEM64(reg, qpcr, violate_count, 0x20, 0, 64);
+/* Packets */
#define MLXSW_REG_QPCR_LOWEST_CIR 1
#define MLXSW_REG_QPCR_HIGHEST_CIR (2 * 1000 * 1000 * 1000) /* 2Gpps */
#define MLXSW_REG_QPCR_LOWEST_CBS 4
#define MLXSW_REG_QPCR_HIGHEST_CBS 24
+/* Bandwidth */
+#define MLXSW_REG_QPCR_LOWEST_CIR_BITS 1024 /* bps */
+#define MLXSW_REG_QPCR_HIGHEST_CIR_BITS 2000000000000ULL /* 2Tbps */
+#define MLXSW_REG_QPCR_LOWEST_CBS_BITS_SP1 4
+#define MLXSW_REG_QPCR_LOWEST_CBS_BITS_SP2 4
+#define MLXSW_REG_QPCR_HIGHEST_CBS_BITS_SP1 25
+#define MLXSW_REG_QPCR_HIGHEST_CBS_BITS_SP2 31
+
static inline void mlxsw_reg_qpcr_pack(char *payload, u16 pid,
enum mlxsw_reg_qpcr_ir_units ir_units,
bool bytes, u32 cir, u16 cbs)
@@ -5438,6 +5447,56 @@ static inline void mlxsw_reg_pplr_pack(char *payload, u8 local_port,
MLXSW_REG_PPLR_LB_TYPE_BIT_PHY_LOCAL : 0);
}
+/* PDDR - Port Diagnostics Database Register
+ * -----------------------------------------
+ * The PDDR enables to read the Phy debug database
+ */
+#define MLXSW_REG_PDDR_ID 0x5031
+#define MLXSW_REG_PDDR_LEN 0x100
+
+MLXSW_REG_DEFINE(pddr, MLXSW_REG_PDDR_ID, MLXSW_REG_PDDR_LEN);
+
+/* reg_pddr_local_port
+ * Local port number.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, pddr, local_port, 0x00, 16, 8);
+
+enum mlxsw_reg_pddr_page_select {
+ MLXSW_REG_PDDR_PAGE_SELECT_TROUBLESHOOTING_INFO = 1,
+};
+
+/* reg_pddr_page_select
+ * Page select index.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, pddr, page_select, 0x04, 0, 8);
+
+enum mlxsw_reg_pddr_trblsh_group_opcode {
+ /* Monitor opcodes */
+ MLXSW_REG_PDDR_TRBLSH_GROUP_OPCODE_MONITOR,
+};
+
+/* reg_pddr_group_opcode
+ * Group selector.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, pddr, trblsh_group_opcode, 0x08, 0, 16);
+
+/* reg_pddr_status_opcode
+ * Group selector.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, pddr, trblsh_status_opcode, 0x0C, 0, 16);
+
+static inline void mlxsw_reg_pddr_pack(char *payload, u8 local_port,
+ u8 page_select)
+{
+ MLXSW_REG_ZERO(pddr, payload);
+ mlxsw_reg_pddr_local_port_set(payload, local_port);
+ mlxsw_reg_pddr_page_select_set(payload, page_select);
+}
+
/* PMTM - Port Module Type Mapping Register
* ----------------------------------------
* The PMTM allows query or configuration of module types.
@@ -5536,6 +5595,7 @@ enum mlxsw_reg_htgt_trap_group {
MLXSW_REG_HTGT_TRAP_GROUP_SP_MULTICAST,
MLXSW_REG_HTGT_TRAP_GROUP_SP_NEIGH_DISCOVERY,
MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP,
+ MLXSW_REG_HTGT_TRAP_GROUP_SP_EXTERNAL_ROUTE,
MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME,
MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP,
MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT,
@@ -5554,6 +5614,7 @@ enum mlxsw_reg_htgt_trap_group {
MLXSW_REG_HTGT_TRAP_GROUP_SP_L3_EXCEPTIONS,
MLXSW_REG_HTGT_TRAP_GROUP_SP_TUNNEL_DISCARDS,
MLXSW_REG_HTGT_TRAP_GROUP_SP_ACL_DISCARDS,
+ MLXSW_REG_HTGT_TRAP_GROUP_SP_BUFFER_DISCARDS,
__MLXSW_REG_HTGT_TRAP_GROUP_MAX,
MLXSW_REG_HTGT_TRAP_GROUP_MAX = __MLXSW_REG_HTGT_TRAP_GROUP_MAX - 1
@@ -5728,7 +5789,7 @@ MLXSW_ITEM32(reg, hpkt, trap_group, 0x00, 12, 6);
* Note: A trap ID can only be associated with a single trap group. The device
* will associate the trap ID with the last trap group configured.
*/
-MLXSW_ITEM32(reg, hpkt, trap_id, 0x00, 0, 9);
+MLXSW_ITEM32(reg, hpkt, trap_id, 0x00, 0, 10);
enum {
MLXSW_REG_HPKT_CTRL_PACKET_DEFAULT,
@@ -8546,8 +8607,10 @@ MLXSW_ITEM32(reg, mcia, size, 0x08, 0, 16);
#define MLXSW_REG_MCIA_PAGE0_LO_OFF 0xa0
#define MLXSW_REG_MCIA_TH_ITEM_SIZE 2
#define MLXSW_REG_MCIA_TH_PAGE_NUM 3
+#define MLXSW_REG_MCIA_TH_PAGE_CMIS_NUM 2
#define MLXSW_REG_MCIA_PAGE0_LO 0
#define MLXSW_REG_MCIA_TH_PAGE_OFF 0x80
+#define MLXSW_REG_MCIA_EEPROM_CMIS_FLAT_MEMORY BIT(7)
enum mlxsw_reg_mcia_eeprom_module_info_rev_id {
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_UNSPC = 0x00,
@@ -8566,6 +8629,7 @@ enum mlxsw_reg_mcia_eeprom_module_info_id {
enum mlxsw_reg_mcia_eeprom_module_info {
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID,
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID,
+ MLXSW_REG_MCIA_EEPROM_MODULE_INFO_TYPE_ID,
MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE,
};
@@ -8612,6 +8676,13 @@ MLXSW_REG_DEFINE(mpat, MLXSW_REG_MPAT_ID, MLXSW_REG_MPAT_LEN);
*/
MLXSW_ITEM32(reg, mpat, pa_id, 0x00, 28, 4);
+/* reg_mpat_session_id
+ * Mirror Session ID.
+ * Used for MIRROR_SESSION<i> trap.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mpat, session_id, 0x00, 24, 4);
+
/* reg_mpat_system_port
* A unique port identifier for the final destination of the packet.
* Access: RW
@@ -8669,6 +8740,18 @@ enum mlxsw_reg_mpat_span_type {
*/
MLXSW_ITEM32(reg, mpat, span_type, 0x04, 0, 4);
+/* reg_mpat_pide
+ * Policer enable.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mpat, pide, 0x0C, 15, 1);
+
+/* reg_mpat_pid
+ * Policer ID.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mpat, pid, 0x0C, 0, 14);
+
/* Remote SPAN - Ethernet VLAN
* - - - - - - - - - - - - - -
*/
@@ -9452,6 +9535,114 @@ MLXSW_ITEM32(reg, mogcr, ptp_iftc, 0x00, 1, 1);
*/
MLXSW_ITEM32(reg, mogcr, ptp_eftc, 0x00, 0, 1);
+/* reg_mogcr_mirroring_pid_base
+ * Base policer id for mirroring policers.
+ * Must have an even value (e.g. 1000, not 1001).
+ * Reserved when SwitchX/-2, Switch-IB/2, Spectrum-1 and Quantum.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mogcr, mirroring_pid_base, 0x0C, 0, 14);
+
+/* MPAGR - Monitoring Port Analyzer Global Register
+ * ------------------------------------------------
+ * This register is used for global port analyzer configurations.
+ * Note: This register is not supported by current FW versions for Spectrum-1.
+ */
+#define MLXSW_REG_MPAGR_ID 0x9089
+#define MLXSW_REG_MPAGR_LEN 0x0C
+
+MLXSW_REG_DEFINE(mpagr, MLXSW_REG_MPAGR_ID, MLXSW_REG_MPAGR_LEN);
+
+enum mlxsw_reg_mpagr_trigger {
+ MLXSW_REG_MPAGR_TRIGGER_EGRESS,
+ MLXSW_REG_MPAGR_TRIGGER_INGRESS,
+ MLXSW_REG_MPAGR_TRIGGER_INGRESS_WRED,
+ MLXSW_REG_MPAGR_TRIGGER_INGRESS_SHARED_BUFFER,
+ MLXSW_REG_MPAGR_TRIGGER_INGRESS_ING_CONG,
+ MLXSW_REG_MPAGR_TRIGGER_INGRESS_EGR_CONG,
+ MLXSW_REG_MPAGR_TRIGGER_EGRESS_ECN,
+ MLXSW_REG_MPAGR_TRIGGER_EGRESS_HIGH_LATENCY,
+};
+
+/* reg_mpagr_trigger
+ * Mirror trigger.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, mpagr, trigger, 0x00, 0, 4);
+
+/* reg_mpagr_pa_id
+ * Port analyzer ID.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mpagr, pa_id, 0x04, 0, 4);
+
+/* reg_mpagr_probability_rate
+ * Sampling rate.
+ * Valid values are: 1 to 3.5*10^9
+ * Value of 1 means "sample all". Default is 1.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mpagr, probability_rate, 0x08, 0, 32);
+
+static inline void mlxsw_reg_mpagr_pack(char *payload,
+ enum mlxsw_reg_mpagr_trigger trigger,
+ u8 pa_id, u32 probability_rate)
+{
+ MLXSW_REG_ZERO(mpagr, payload);
+ mlxsw_reg_mpagr_trigger_set(payload, trigger);
+ mlxsw_reg_mpagr_pa_id_set(payload, pa_id);
+ mlxsw_reg_mpagr_probability_rate_set(payload, probability_rate);
+}
+
+/* MOMTE - Monitoring Mirror Trigger Enable Register
+ * -------------------------------------------------
+ * This register is used to configure the mirror enable for different mirror
+ * reasons.
+ */
+#define MLXSW_REG_MOMTE_ID 0x908D
+#define MLXSW_REG_MOMTE_LEN 0x10
+
+MLXSW_REG_DEFINE(momte, MLXSW_REG_MOMTE_ID, MLXSW_REG_MOMTE_LEN);
+
+/* reg_momte_local_port
+ * Local port number.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, momte, local_port, 0x00, 16, 8);
+
+enum mlxsw_reg_momte_type {
+ MLXSW_REG_MOMTE_TYPE_WRED = 0x20,
+ MLXSW_REG_MOMTE_TYPE_SHARED_BUFFER_TCLASS = 0x31,
+ MLXSW_REG_MOMTE_TYPE_SHARED_BUFFER_TCLASS_DESCRIPTORS = 0x32,
+ MLXSW_REG_MOMTE_TYPE_SHARED_BUFFER_EGRESS_PORT = 0x33,
+ MLXSW_REG_MOMTE_TYPE_ING_CONG = 0x40,
+ MLXSW_REG_MOMTE_TYPE_EGR_CONG = 0x50,
+ MLXSW_REG_MOMTE_TYPE_ECN = 0x60,
+ MLXSW_REG_MOMTE_TYPE_HIGH_LATENCY = 0x70,
+};
+
+/* reg_momte_type
+ * Type of mirroring.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, momte, type, 0x04, 0, 8);
+
+/* reg_momte_tclass_en
+ * TClass/PG mirror enable. Each bit represents corresponding tclass.
+ * 0: disable (default)
+ * 1: enable
+ * Access: RW
+ */
+MLXSW_ITEM_BIT_ARRAY(reg, momte, tclass_en, 0x08, 0x08, 1);
+
+static inline void mlxsw_reg_momte_pack(char *payload, u8 local_port,
+ enum mlxsw_reg_momte_type type)
+{
+ MLXSW_REG_ZERO(momte, payload);
+ mlxsw_reg_momte_local_port_set(payload, local_port);
+ mlxsw_reg_momte_type_set(payload, type);
+}
+
/* MTPPPC - Time Precision Packet Port Configuration
* -------------------------------------------------
* This register serves for configuration of which PTP messages should be
@@ -10758,6 +10949,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = {
MLXSW_REG(pbmc),
MLXSW_REG(pspa),
MLXSW_REG(pplr),
+ MLXSW_REG(pddr),
MLXSW_REG(pmtm),
MLXSW_REG(htgt),
MLXSW_REG(hpkt),
@@ -10802,6 +10994,8 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = {
MLXSW_REG(mgpc),
MLXSW_REG(mprs),
MLXSW_REG(mogcr),
+ MLXSW_REG(mpagr),
+ MLXSW_REG(momte),
MLXSW_REG(mtpppc),
MLXSW_REG(mtpptr),
MLXSW_REG(mtptpt),
diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h
index d62496ef299c..a56c9e19a390 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/resources.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h
@@ -47,6 +47,7 @@ enum mlxsw_res_id {
MLXSW_RES_ID_ACL_ERPT_ENTRIES_8KB,
MLXSW_RES_ID_ACL_ERPT_ENTRIES_12KB,
MLXSW_RES_ID_ACL_MAX_BF_LOG,
+ MLXSW_RES_ID_MAX_GLOBAL_POLICERS,
MLXSW_RES_ID_MAX_CPU_POLICERS,
MLXSW_RES_ID_MAX_VRS,
MLXSW_RES_ID_MAX_RIFS,
@@ -105,6 +106,7 @@ static u16 mlxsw_res_ids[] = {
[MLXSW_RES_ID_ACL_ERPT_ENTRIES_8KB] = 0x2952,
[MLXSW_RES_ID_ACL_ERPT_ENTRIES_12KB] = 0x2953,
[MLXSW_RES_ID_ACL_MAX_BF_LOG] = 0x2960,
+ [MLXSW_RES_ID_MAX_GLOBAL_POLICERS] = 0x2A10,
[MLXSW_RES_ID_MAX_CPU_POLICERS] = 0x2A13,
[MLXSW_RES_ID_MAX_VRS] = 0x2C01,
[MLXSW_RES_ID_MAX_RIFS] = 0x2C02,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 029ea344ad65..fdf9aa8314b2 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -45,8 +45,8 @@
#include "../mlxfw/mlxfw.h"
#define MLXSW_SP1_FWREV_MAJOR 13
-#define MLXSW_SP1_FWREV_MINOR 2000
-#define MLXSW_SP1_FWREV_SUBMINOR 2714
+#define MLXSW_SP1_FWREV_MINOR 2007
+#define MLXSW_SP1_FWREV_SUBMINOR 1168
#define MLXSW_SP1_FWREV_CAN_RESET_MINOR 1702
static const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = {
@@ -62,8 +62,8 @@ static const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = {
"." __stringify(MLXSW_SP1_FWREV_SUBMINOR) ".mfa2"
#define MLXSW_SP2_FWREV_MAJOR 29
-#define MLXSW_SP2_FWREV_MINOR 2000
-#define MLXSW_SP2_FWREV_SUBMINOR 2714
+#define MLXSW_SP2_FWREV_MINOR 2007
+#define MLXSW_SP2_FWREV_SUBMINOR 1168
static const struct mlxsw_fw_rev mlxsw_sp2_fw_rev = {
.major = MLXSW_SP2_FWREV_MAJOR,
@@ -76,10 +76,24 @@ static const struct mlxsw_fw_rev mlxsw_sp2_fw_rev = {
"." __stringify(MLXSW_SP2_FWREV_MINOR) \
"." __stringify(MLXSW_SP2_FWREV_SUBMINOR) ".mfa2"
+#define MLXSW_SP3_FWREV_MAJOR 30
+#define MLXSW_SP3_FWREV_MINOR 2007
+#define MLXSW_SP3_FWREV_SUBMINOR 1168
+
+static const struct mlxsw_fw_rev mlxsw_sp3_fw_rev = {
+ .major = MLXSW_SP3_FWREV_MAJOR,
+ .minor = MLXSW_SP3_FWREV_MINOR,
+ .subminor = MLXSW_SP3_FWREV_SUBMINOR,
+};
+
+#define MLXSW_SP3_FW_FILENAME \
+ "mellanox/mlxsw_spectrum3-" __stringify(MLXSW_SP3_FWREV_MAJOR) \
+ "." __stringify(MLXSW_SP3_FWREV_MINOR) \
+ "." __stringify(MLXSW_SP3_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";
-static const char mlxsw_sp_driver_version[] = "1.0";
static const unsigned char mlxsw_sp1_mac_mask[ETH_ALEN] = {
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00
@@ -161,43 +175,6 @@ struct mlxsw_sp_mlxfw_dev {
struct mlxsw_sp *mlxsw_sp;
};
-struct mlxsw_sp_ptp_ops {
- struct mlxsw_sp_ptp_clock *
- (*clock_init)(struct mlxsw_sp *mlxsw_sp, struct device *dev);
- void (*clock_fini)(struct mlxsw_sp_ptp_clock *clock);
-
- struct mlxsw_sp_ptp_state *(*init)(struct mlxsw_sp *mlxsw_sp);
- void (*fini)(struct mlxsw_sp_ptp_state *ptp_state);
-
- /* Notify a driver that a packet that might be PTP was received. Driver
- * is responsible for freeing the passed-in SKB.
- */
- void (*receive)(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
- u8 local_port);
-
- /* Notify a driver that a timestamped packet was transmitted. Driver
- * is responsible for freeing the passed-in SKB.
- */
- void (*transmitted)(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
- u8 local_port);
-
- int (*hwtstamp_get)(struct mlxsw_sp_port *mlxsw_sp_port,
- struct hwtstamp_config *config);
- int (*hwtstamp_set)(struct mlxsw_sp_port *mlxsw_sp_port,
- struct hwtstamp_config *config);
- void (*shaper_work)(struct work_struct *work);
- int (*get_ts_info)(struct mlxsw_sp *mlxsw_sp,
- struct ethtool_ts_info *info);
- int (*get_stats_count)(void);
- void (*get_stats_strings)(u8 **p);
- void (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
- u64 *data, int data_index);
-};
-
-struct mlxsw_sp_span_ops {
- u32 (*buffsize_get)(int mtu, u32 speed);
-};
-
static int mlxsw_sp_component_query(struct mlxfw_dev *mlxfw_dev,
u16 component_index, u32 *p_max_size,
u8 *p_align_bits, u16 *p_max_write_size)
@@ -580,8 +557,8 @@ static int mlxsw_sp_base_mac_get(struct mlxsw_sp *mlxsw_sp)
return 0;
}
-static int mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port,
- bool is_up)
+int mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port,
+ bool is_up)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
char paos_pl[MLXSW_REG_PAOS_LEN];
@@ -995,8 +972,8 @@ int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu,
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl);
}
-static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
- int mtu, bool pause_en)
+int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
+ int mtu, bool pause_en)
{
u8 def_prio_tc[IEEE_8021QAZ_MAX_TCS] = {0};
bool dcb_en = !!mlxsw_sp_port->dcb.ets;
@@ -1088,8 +1065,8 @@ static int mlxsw_sp_port_get_offload_stats(int attr_id, const struct net_device
return -EINVAL;
}
-static int mlxsw_sp_port_get_stats_raw(struct net_device *dev, int grp,
- int prio, char *ppcnt_pl)
+int mlxsw_sp_port_get_stats_raw(struct net_device *dev, int grp,
+ int prio, char *ppcnt_pl)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
@@ -1352,6 +1329,21 @@ static int mlxsw_sp_port_kill_vid(struct net_device *dev,
return 0;
}
+static int mlxsw_sp_setup_tc_block(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct flow_block_offload *f)
+{
+ switch (f->binder_type) {
+ case FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS:
+ return mlxsw_sp_setup_tc_block_clsact(mlxsw_sp_port, f, true);
+ case FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS:
+ return mlxsw_sp_setup_tc_block_clsact(mlxsw_sp_port, f, false);
+ case FLOW_BLOCK_BINDER_TYPE_RED_EARLY_DROP:
+ return mlxsw_sp_setup_tc_block_qevent_early_drop(mlxsw_sp_port, f);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
@@ -1547,1500 +1539,6 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = {
.ndo_do_ioctl = mlxsw_sp_port_ioctl,
};
-static void mlxsw_sp_port_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *drvinfo)
-{
- struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
- struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
-
- strlcpy(drvinfo->driver, mlxsw_sp->bus_info->device_kind,
- sizeof(drvinfo->driver));
- strlcpy(drvinfo->version, mlxsw_sp_driver_version,
- sizeof(drvinfo->version));
- snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
- "%d.%d.%d",
- mlxsw_sp->bus_info->fw_rev.major,
- mlxsw_sp->bus_info->fw_rev.minor,
- mlxsw_sp->bus_info->fw_rev.subminor);
- strlcpy(drvinfo->bus_info, mlxsw_sp->bus_info->device_name,
- sizeof(drvinfo->bus_info));
-}
-
-static void mlxsw_sp_port_get_pauseparam(struct net_device *dev,
- struct ethtool_pauseparam *pause)
-{
- struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
-
- pause->rx_pause = mlxsw_sp_port->link.rx_pause;
- pause->tx_pause = mlxsw_sp_port->link.tx_pause;
-}
-
-static int mlxsw_sp_port_pause_set(struct mlxsw_sp_port *mlxsw_sp_port,
- struct ethtool_pauseparam *pause)
-{
- char pfcc_pl[MLXSW_REG_PFCC_LEN];
-
- mlxsw_reg_pfcc_pack(pfcc_pl, mlxsw_sp_port->local_port);
- mlxsw_reg_pfcc_pprx_set(pfcc_pl, pause->rx_pause);
- mlxsw_reg_pfcc_pptx_set(pfcc_pl, pause->tx_pause);
-
- return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pfcc),
- pfcc_pl);
-}
-
-static int mlxsw_sp_port_set_pauseparam(struct net_device *dev,
- struct ethtool_pauseparam *pause)
-{
- struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
- bool pause_en = pause->tx_pause || pause->rx_pause;
- int err;
-
- if (mlxsw_sp_port->dcb.pfc && mlxsw_sp_port->dcb.pfc->pfc_en) {
- netdev_err(dev, "PFC already enabled on port\n");
- return -EINVAL;
- }
-
- if (pause->autoneg) {
- netdev_err(dev, "PAUSE frames autonegotiation isn't supported\n");
- return -EINVAL;
- }
-
- err = mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, pause_en);
- if (err) {
- netdev_err(dev, "Failed to configure port's headroom\n");
- return err;
- }
-
- err = mlxsw_sp_port_pause_set(mlxsw_sp_port, pause);
- if (err) {
- netdev_err(dev, "Failed to set PAUSE parameters\n");
- goto err_port_pause_configure;
- }
-
- mlxsw_sp_port->link.rx_pause = pause->rx_pause;
- mlxsw_sp_port->link.tx_pause = pause->tx_pause;
-
- return 0;
-
-err_port_pause_configure:
- pause_en = mlxsw_sp_port_is_pause_en(mlxsw_sp_port);
- mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, pause_en);
- return err;
-}
-
-struct mlxsw_sp_port_hw_stats {
- char str[ETH_GSTRING_LEN];
- u64 (*getter)(const char *payload);
- bool cells_bytes;
-};
-
-static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = {
- {
- .str = "a_frames_transmitted_ok",
- .getter = mlxsw_reg_ppcnt_a_frames_transmitted_ok_get,
- },
- {
- .str = "a_frames_received_ok",
- .getter = mlxsw_reg_ppcnt_a_frames_received_ok_get,
- },
- {
- .str = "a_frame_check_sequence_errors",
- .getter = mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get,
- },
- {
- .str = "a_alignment_errors",
- .getter = mlxsw_reg_ppcnt_a_alignment_errors_get,
- },
- {
- .str = "a_octets_transmitted_ok",
- .getter = mlxsw_reg_ppcnt_a_octets_transmitted_ok_get,
- },
- {
- .str = "a_octets_received_ok",
- .getter = mlxsw_reg_ppcnt_a_octets_received_ok_get,
- },
- {
- .str = "a_multicast_frames_xmitted_ok",
- .getter = mlxsw_reg_ppcnt_a_multicast_frames_xmitted_ok_get,
- },
- {
- .str = "a_broadcast_frames_xmitted_ok",
- .getter = mlxsw_reg_ppcnt_a_broadcast_frames_xmitted_ok_get,
- },
- {
- .str = "a_multicast_frames_received_ok",
- .getter = mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get,
- },
- {
- .str = "a_broadcast_frames_received_ok",
- .getter = mlxsw_reg_ppcnt_a_broadcast_frames_received_ok_get,
- },
- {
- .str = "a_in_range_length_errors",
- .getter = mlxsw_reg_ppcnt_a_in_range_length_errors_get,
- },
- {
- .str = "a_out_of_range_length_field",
- .getter = mlxsw_reg_ppcnt_a_out_of_range_length_field_get,
- },
- {
- .str = "a_frame_too_long_errors",
- .getter = mlxsw_reg_ppcnt_a_frame_too_long_errors_get,
- },
- {
- .str = "a_symbol_error_during_carrier",
- .getter = mlxsw_reg_ppcnt_a_symbol_error_during_carrier_get,
- },
- {
- .str = "a_mac_control_frames_transmitted",
- .getter = mlxsw_reg_ppcnt_a_mac_control_frames_transmitted_get,
- },
- {
- .str = "a_mac_control_frames_received",
- .getter = mlxsw_reg_ppcnt_a_mac_control_frames_received_get,
- },
- {
- .str = "a_unsupported_opcodes_received",
- .getter = mlxsw_reg_ppcnt_a_unsupported_opcodes_received_get,
- },
- {
- .str = "a_pause_mac_ctrl_frames_received",
- .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_received_get,
- },
- {
- .str = "a_pause_mac_ctrl_frames_xmitted",
- .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_transmitted_get,
- },
-};
-
-#define MLXSW_SP_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_stats)
-
-static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2863_stats[] = {
- {
- .str = "if_in_discards",
- .getter = mlxsw_reg_ppcnt_if_in_discards_get,
- },
- {
- .str = "if_out_discards",
- .getter = mlxsw_reg_ppcnt_if_out_discards_get,
- },
- {
- .str = "if_out_errors",
- .getter = mlxsw_reg_ppcnt_if_out_errors_get,
- },
-};
-
-#define MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN \
- ARRAY_SIZE(mlxsw_sp_port_hw_rfc_2863_stats)
-
-static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2819_stats[] = {
- {
- .str = "ether_stats_undersize_pkts",
- .getter = mlxsw_reg_ppcnt_ether_stats_undersize_pkts_get,
- },
- {
- .str = "ether_stats_oversize_pkts",
- .getter = mlxsw_reg_ppcnt_ether_stats_oversize_pkts_get,
- },
- {
- .str = "ether_stats_fragments",
- .getter = mlxsw_reg_ppcnt_ether_stats_fragments_get,
- },
- {
- .str = "ether_pkts64octets",
- .getter = mlxsw_reg_ppcnt_ether_stats_pkts64octets_get,
- },
- {
- .str = "ether_pkts65to127octets",
- .getter = mlxsw_reg_ppcnt_ether_stats_pkts65to127octets_get,
- },
- {
- .str = "ether_pkts128to255octets",
- .getter = mlxsw_reg_ppcnt_ether_stats_pkts128to255octets_get,
- },
- {
- .str = "ether_pkts256to511octets",
- .getter = mlxsw_reg_ppcnt_ether_stats_pkts256to511octets_get,
- },
- {
- .str = "ether_pkts512to1023octets",
- .getter = mlxsw_reg_ppcnt_ether_stats_pkts512to1023octets_get,
- },
- {
- .str = "ether_pkts1024to1518octets",
- .getter = mlxsw_reg_ppcnt_ether_stats_pkts1024to1518octets_get,
- },
- {
- .str = "ether_pkts1519to2047octets",
- .getter = mlxsw_reg_ppcnt_ether_stats_pkts1519to2047octets_get,
- },
- {
- .str = "ether_pkts2048to4095octets",
- .getter = mlxsw_reg_ppcnt_ether_stats_pkts2048to4095octets_get,
- },
- {
- .str = "ether_pkts4096to8191octets",
- .getter = mlxsw_reg_ppcnt_ether_stats_pkts4096to8191octets_get,
- },
- {
- .str = "ether_pkts8192to10239octets",
- .getter = mlxsw_reg_ppcnt_ether_stats_pkts8192to10239octets_get,
- },
-};
-
-#define MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN \
- ARRAY_SIZE(mlxsw_sp_port_hw_rfc_2819_stats)
-
-static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_3635_stats[] = {
- {
- .str = "dot3stats_fcs_errors",
- .getter = mlxsw_reg_ppcnt_dot3stats_fcs_errors_get,
- },
- {
- .str = "dot3stats_symbol_errors",
- .getter = mlxsw_reg_ppcnt_dot3stats_symbol_errors_get,
- },
- {
- .str = "dot3control_in_unknown_opcodes",
- .getter = mlxsw_reg_ppcnt_dot3control_in_unknown_opcodes_get,
- },
- {
- .str = "dot3in_pause_frames",
- .getter = mlxsw_reg_ppcnt_dot3in_pause_frames_get,
- },
-};
-
-#define MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN \
- ARRAY_SIZE(mlxsw_sp_port_hw_rfc_3635_stats)
-
-static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_ext_stats[] = {
- {
- .str = "ecn_marked",
- .getter = mlxsw_reg_ppcnt_ecn_marked_get,
- },
-};
-
-#define MLXSW_SP_PORT_HW_EXT_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_ext_stats)
-
-static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_discard_stats[] = {
- {
- .str = "discard_ingress_general",
- .getter = mlxsw_reg_ppcnt_ingress_general_get,
- },
- {
- .str = "discard_ingress_policy_engine",
- .getter = mlxsw_reg_ppcnt_ingress_policy_engine_get,
- },
- {
- .str = "discard_ingress_vlan_membership",
- .getter = mlxsw_reg_ppcnt_ingress_vlan_membership_get,
- },
- {
- .str = "discard_ingress_tag_frame_type",
- .getter = mlxsw_reg_ppcnt_ingress_tag_frame_type_get,
- },
- {
- .str = "discard_egress_vlan_membership",
- .getter = mlxsw_reg_ppcnt_egress_vlan_membership_get,
- },
- {
- .str = "discard_loopback_filter",
- .getter = mlxsw_reg_ppcnt_loopback_filter_get,
- },
- {
- .str = "discard_egress_general",
- .getter = mlxsw_reg_ppcnt_egress_general_get,
- },
- {
- .str = "discard_egress_hoq",
- .getter = mlxsw_reg_ppcnt_egress_hoq_get,
- },
- {
- .str = "discard_egress_policy_engine",
- .getter = mlxsw_reg_ppcnt_egress_policy_engine_get,
- },
- {
- .str = "discard_ingress_tx_link_down",
- .getter = mlxsw_reg_ppcnt_ingress_tx_link_down_get,
- },
- {
- .str = "discard_egress_stp_filter",
- .getter = mlxsw_reg_ppcnt_egress_stp_filter_get,
- },
- {
- .str = "discard_egress_sll",
- .getter = mlxsw_reg_ppcnt_egress_sll_get,
- },
-};
-
-#define MLXSW_SP_PORT_HW_DISCARD_STATS_LEN \
- ARRAY_SIZE(mlxsw_sp_port_hw_discard_stats)
-
-static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_prio_stats[] = {
- {
- .str = "rx_octets_prio",
- .getter = mlxsw_reg_ppcnt_rx_octets_get,
- },
- {
- .str = "rx_frames_prio",
- .getter = mlxsw_reg_ppcnt_rx_frames_get,
- },
- {
- .str = "tx_octets_prio",
- .getter = mlxsw_reg_ppcnt_tx_octets_get,
- },
- {
- .str = "tx_frames_prio",
- .getter = mlxsw_reg_ppcnt_tx_frames_get,
- },
- {
- .str = "rx_pause_prio",
- .getter = mlxsw_reg_ppcnt_rx_pause_get,
- },
- {
- .str = "rx_pause_duration_prio",
- .getter = mlxsw_reg_ppcnt_rx_pause_duration_get,
- },
- {
- .str = "tx_pause_prio",
- .getter = mlxsw_reg_ppcnt_tx_pause_get,
- },
- {
- .str = "tx_pause_duration_prio",
- .getter = mlxsw_reg_ppcnt_tx_pause_duration_get,
- },
-};
-
-#define MLXSW_SP_PORT_HW_PRIO_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_prio_stats)
-
-static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_tc_stats[] = {
- {
- .str = "tc_transmit_queue_tc",
- .getter = mlxsw_reg_ppcnt_tc_transmit_queue_get,
- .cells_bytes = true,
- },
- {
- .str = "tc_no_buffer_discard_uc_tc",
- .getter = mlxsw_reg_ppcnt_tc_no_buffer_discard_uc_get,
- },
-};
-
-#define MLXSW_SP_PORT_HW_TC_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_tc_stats)
-
-#define MLXSW_SP_PORT_ETHTOOL_STATS_LEN (MLXSW_SP_PORT_HW_STATS_LEN + \
- MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN + \
- MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN + \
- MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN + \
- MLXSW_SP_PORT_HW_EXT_STATS_LEN + \
- MLXSW_SP_PORT_HW_DISCARD_STATS_LEN + \
- (MLXSW_SP_PORT_HW_PRIO_STATS_LEN * \
- IEEE_8021QAZ_MAX_TCS) + \
- (MLXSW_SP_PORT_HW_TC_STATS_LEN * \
- TC_MAX_QUEUE))
-
-static void mlxsw_sp_port_get_prio_strings(u8 **p, int prio)
-{
- int i;
-
- for (i = 0; i < MLXSW_SP_PORT_HW_PRIO_STATS_LEN; i++) {
- snprintf(*p, ETH_GSTRING_LEN, "%.29s_%.1d",
- mlxsw_sp_port_hw_prio_stats[i].str, prio);
- *p += ETH_GSTRING_LEN;
- }
-}
-
-static void mlxsw_sp_port_get_tc_strings(u8 **p, int tc)
-{
- int i;
-
- for (i = 0; i < MLXSW_SP_PORT_HW_TC_STATS_LEN; i++) {
- snprintf(*p, ETH_GSTRING_LEN, "%.29s_%.1d",
- mlxsw_sp_port_hw_tc_stats[i].str, tc);
- *p += ETH_GSTRING_LEN;
- }
-}
-
-static void mlxsw_sp_port_get_strings(struct net_device *dev,
- u32 stringset, u8 *data)
-{
- struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
- u8 *p = data;
- int i;
-
- switch (stringset) {
- case ETH_SS_STATS:
- for (i = 0; i < MLXSW_SP_PORT_HW_STATS_LEN; i++) {
- memcpy(p, mlxsw_sp_port_hw_stats[i].str,
- ETH_GSTRING_LEN);
- p += ETH_GSTRING_LEN;
- }
-
- for (i = 0; i < MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN; i++) {
- memcpy(p, mlxsw_sp_port_hw_rfc_2863_stats[i].str,
- ETH_GSTRING_LEN);
- p += ETH_GSTRING_LEN;
- }
-
- for (i = 0; i < MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN; i++) {
- memcpy(p, mlxsw_sp_port_hw_rfc_2819_stats[i].str,
- ETH_GSTRING_LEN);
- p += ETH_GSTRING_LEN;
- }
-
- for (i = 0; i < MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN; i++) {
- memcpy(p, mlxsw_sp_port_hw_rfc_3635_stats[i].str,
- ETH_GSTRING_LEN);
- p += ETH_GSTRING_LEN;
- }
-
- for (i = 0; i < MLXSW_SP_PORT_HW_EXT_STATS_LEN; i++) {
- memcpy(p, mlxsw_sp_port_hw_ext_stats[i].str,
- ETH_GSTRING_LEN);
- p += ETH_GSTRING_LEN;
- }
-
- for (i = 0; i < MLXSW_SP_PORT_HW_DISCARD_STATS_LEN; i++) {
- memcpy(p, mlxsw_sp_port_hw_discard_stats[i].str,
- ETH_GSTRING_LEN);
- p += ETH_GSTRING_LEN;
- }
-
- for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
- mlxsw_sp_port_get_prio_strings(&p, i);
-
- for (i = 0; i < TC_MAX_QUEUE; i++)
- mlxsw_sp_port_get_tc_strings(&p, i);
-
- mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats_strings(&p);
- break;
- }
-}
-
-static int mlxsw_sp_port_set_phys_id(struct net_device *dev,
- enum ethtool_phys_id_state state)
-{
- struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
- struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
- char mlcr_pl[MLXSW_REG_MLCR_LEN];
- bool active;
-
- switch (state) {
- case ETHTOOL_ID_ACTIVE:
- active = true;
- break;
- case ETHTOOL_ID_INACTIVE:
- active = false;
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- mlxsw_reg_mlcr_pack(mlcr_pl, mlxsw_sp_port->local_port, active);
- return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mlcr), mlcr_pl);
-}
-
-static int
-mlxsw_sp_get_hw_stats_by_group(struct mlxsw_sp_port_hw_stats **p_hw_stats,
- int *p_len, enum mlxsw_reg_ppcnt_grp grp)
-{
- switch (grp) {
- case MLXSW_REG_PPCNT_IEEE_8023_CNT:
- *p_hw_stats = mlxsw_sp_port_hw_stats;
- *p_len = MLXSW_SP_PORT_HW_STATS_LEN;
- break;
- case MLXSW_REG_PPCNT_RFC_2863_CNT:
- *p_hw_stats = mlxsw_sp_port_hw_rfc_2863_stats;
- *p_len = MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN;
- break;
- case MLXSW_REG_PPCNT_RFC_2819_CNT:
- *p_hw_stats = mlxsw_sp_port_hw_rfc_2819_stats;
- *p_len = MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN;
- break;
- case MLXSW_REG_PPCNT_RFC_3635_CNT:
- *p_hw_stats = mlxsw_sp_port_hw_rfc_3635_stats;
- *p_len = MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN;
- break;
- case MLXSW_REG_PPCNT_EXT_CNT:
- *p_hw_stats = mlxsw_sp_port_hw_ext_stats;
- *p_len = MLXSW_SP_PORT_HW_EXT_STATS_LEN;
- break;
- case MLXSW_REG_PPCNT_DISCARD_CNT:
- *p_hw_stats = mlxsw_sp_port_hw_discard_stats;
- *p_len = MLXSW_SP_PORT_HW_DISCARD_STATS_LEN;
- break;
- case MLXSW_REG_PPCNT_PRIO_CNT:
- *p_hw_stats = mlxsw_sp_port_hw_prio_stats;
- *p_len = MLXSW_SP_PORT_HW_PRIO_STATS_LEN;
- break;
- case MLXSW_REG_PPCNT_TC_CNT:
- *p_hw_stats = mlxsw_sp_port_hw_tc_stats;
- *p_len = MLXSW_SP_PORT_HW_TC_STATS_LEN;
- break;
- default:
- WARN_ON(1);
- return -EOPNOTSUPP;
- }
- return 0;
-}
-
-static void __mlxsw_sp_port_get_stats(struct net_device *dev,
- enum mlxsw_reg_ppcnt_grp grp, int prio,
- u64 *data, int data_index)
-{
- struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
- struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
- struct mlxsw_sp_port_hw_stats *hw_stats;
- char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
- int i, len;
- int err;
-
- err = mlxsw_sp_get_hw_stats_by_group(&hw_stats, &len, grp);
- if (err)
- return;
- mlxsw_sp_port_get_stats_raw(dev, grp, prio, ppcnt_pl);
- for (i = 0; i < len; i++) {
- data[data_index + i] = hw_stats[i].getter(ppcnt_pl);
- if (!hw_stats[i].cells_bytes)
- continue;
- data[data_index + i] = mlxsw_sp_cells_bytes(mlxsw_sp,
- data[data_index + i]);
- }
-}
-
-static void mlxsw_sp_port_get_stats(struct net_device *dev,
- struct ethtool_stats *stats, u64 *data)
-{
- struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
- int i, data_index = 0;
-
- /* IEEE 802.3 Counters */
- __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT, 0,
- data, data_index);
- data_index = MLXSW_SP_PORT_HW_STATS_LEN;
-
- /* RFC 2863 Counters */
- __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_2863_CNT, 0,
- data, data_index);
- data_index += MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN;
-
- /* RFC 2819 Counters */
- __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_2819_CNT, 0,
- data, data_index);
- data_index += MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN;
-
- /* RFC 3635 Counters */
- __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_3635_CNT, 0,
- data, data_index);
- data_index += MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN;
-
- /* Extended Counters */
- __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_EXT_CNT, 0,
- data, data_index);
- data_index += MLXSW_SP_PORT_HW_EXT_STATS_LEN;
-
- /* Discard Counters */
- __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_DISCARD_CNT, 0,
- data, data_index);
- data_index += MLXSW_SP_PORT_HW_DISCARD_STATS_LEN;
-
- /* Per-Priority Counters */
- for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
- __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_PRIO_CNT, i,
- data, data_index);
- data_index += MLXSW_SP_PORT_HW_PRIO_STATS_LEN;
- }
-
- /* Per-TC Counters */
- for (i = 0; i < TC_MAX_QUEUE; i++) {
- __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_TC_CNT, i,
- data, data_index);
- data_index += MLXSW_SP_PORT_HW_TC_STATS_LEN;
- }
-
- /* PTP counters */
- mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats(mlxsw_sp_port,
- data, data_index);
- data_index += mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats_count();
-}
-
-static int mlxsw_sp_port_get_sset_count(struct net_device *dev, int sset)
-{
- struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
-
- switch (sset) {
- case ETH_SS_STATS:
- return MLXSW_SP_PORT_ETHTOOL_STATS_LEN +
- mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats_count();
- default:
- return -EOPNOTSUPP;
- }
-}
-
-struct mlxsw_sp1_port_link_mode {
- enum ethtool_link_mode_bit_indices mask_ethtool;
- u32 mask;
- u32 speed;
-};
-
-static const struct mlxsw_sp1_port_link_mode mlxsw_sp1_port_link_mode[] = {
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_100BASE_T,
- .mask_ethtool = ETHTOOL_LINK_MODE_100baseT_Full_BIT,
- .speed = SPEED_100,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_SGMII |
- MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX,
- .mask_ethtool = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
- .speed = SPEED_1000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_T,
- .mask_ethtool = ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
- .speed = SPEED_10000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CX4 |
- MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4,
- .mask_ethtool = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
- .speed = SPEED_10000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
- MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
- MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
- MLXSW_REG_PTYS_ETH_SPEED_10GBASE_ER_LR,
- .mask_ethtool = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
- .speed = SPEED_10000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_20GBASE_KR2,
- .mask_ethtool = ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
- .speed = SPEED_20000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4,
- .mask_ethtool = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
- .speed = SPEED_40000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4,
- .mask_ethtool = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
- .speed = SPEED_40000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4,
- .mask_ethtool = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
- .speed = SPEED_40000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_LR4_ER4,
- .mask_ethtool = ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
- .speed = SPEED_40000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_CR,
- .mask_ethtool = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
- .speed = SPEED_25000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_KR,
- .mask_ethtool = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
- .speed = SPEED_25000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_SR,
- .mask_ethtool = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
- .speed = SPEED_25000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_CR2,
- .mask_ethtool = ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
- .speed = SPEED_50000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR2,
- .mask_ethtool = ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
- .speed = SPEED_50000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_SR2,
- .mask_ethtool = ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
- .speed = SPEED_50000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4,
- .mask_ethtool = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
- .speed = SPEED_100000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4,
- .mask_ethtool = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
- .speed = SPEED_100000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4,
- .mask_ethtool = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
- .speed = SPEED_100000,
- },
- {
- .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_LR4_ER4,
- .mask_ethtool = ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
- .speed = SPEED_100000,
- },
-};
-
-#define MLXSW_SP1_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sp1_port_link_mode)
-
-static void
-mlxsw_sp1_from_ptys_supported_port(struct mlxsw_sp *mlxsw_sp,
- u32 ptys_eth_proto,
- struct ethtool_link_ksettings *cmd)
-{
- if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
- MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
- MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 |
- MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 |
- MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
- MLXSW_REG_PTYS_ETH_SPEED_SGMII))
- ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
-
- if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
- MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 |
- MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 |
- MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 |
- MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX))
- ethtool_link_ksettings_add_link_mode(cmd, supported, Backplane);
-}
-
-static void
-mlxsw_sp1_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
- u8 width, unsigned long *mode)
-{
- int i;
-
- for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
- if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask)
- __set_bit(mlxsw_sp1_port_link_mode[i].mask_ethtool,
- mode);
- }
-}
-
-static u32
-mlxsw_sp1_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto)
-{
- int i;
-
- for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
- if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask)
- return mlxsw_sp1_port_link_mode[i].speed;
- }
-
- return SPEED_UNKNOWN;
-}
-
-static void
-mlxsw_sp1_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
- u32 ptys_eth_proto,
- struct ethtool_link_ksettings *cmd)
-{
- cmd->base.speed = SPEED_UNKNOWN;
- cmd->base.duplex = DUPLEX_UNKNOWN;
-
- if (!carrier_ok)
- return;
-
- cmd->base.speed = mlxsw_sp1_from_ptys_speed(mlxsw_sp, ptys_eth_proto);
- if (cmd->base.speed != SPEED_UNKNOWN)
- cmd->base.duplex = DUPLEX_FULL;
-}
-
-static u32
-mlxsw_sp1_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp, u8 width,
- const struct ethtool_link_ksettings *cmd)
-{
- u32 ptys_proto = 0;
- int i;
-
- for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
- if (test_bit(mlxsw_sp1_port_link_mode[i].mask_ethtool,
- cmd->link_modes.advertising))
- ptys_proto |= mlxsw_sp1_port_link_mode[i].mask;
- }
- return ptys_proto;
-}
-
-static u32 mlxsw_sp1_to_ptys_speed(struct mlxsw_sp *mlxsw_sp, u8 width,
- u32 speed)
-{
- u32 ptys_proto = 0;
- int i;
-
- for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
- if (speed == mlxsw_sp1_port_link_mode[i].speed)
- ptys_proto |= mlxsw_sp1_port_link_mode[i].mask;
- }
- return ptys_proto;
-}
-
-static void
-mlxsw_sp1_reg_ptys_eth_pack(struct mlxsw_sp *mlxsw_sp, char *payload,
- u8 local_port, u32 proto_admin, bool autoneg)
-{
- mlxsw_reg_ptys_eth_pack(payload, local_port, proto_admin, autoneg);
-}
-
-static void
-mlxsw_sp1_reg_ptys_eth_unpack(struct mlxsw_sp *mlxsw_sp, char *payload,
- u32 *p_eth_proto_cap, u32 *p_eth_proto_admin,
- u32 *p_eth_proto_oper)
-{
- mlxsw_reg_ptys_eth_unpack(payload, p_eth_proto_cap, p_eth_proto_admin,
- p_eth_proto_oper);
-}
-
-static const struct mlxsw_sp_port_type_speed_ops
-mlxsw_sp1_port_type_speed_ops = {
- .from_ptys_supported_port = mlxsw_sp1_from_ptys_supported_port,
- .from_ptys_link = mlxsw_sp1_from_ptys_link,
- .from_ptys_speed = mlxsw_sp1_from_ptys_speed,
- .from_ptys_speed_duplex = mlxsw_sp1_from_ptys_speed_duplex,
- .to_ptys_advert_link = mlxsw_sp1_to_ptys_advert_link,
- .to_ptys_speed = mlxsw_sp1_to_ptys_speed,
- .reg_ptys_eth_pack = mlxsw_sp1_reg_ptys_eth_pack,
- .reg_ptys_eth_unpack = mlxsw_sp1_reg_ptys_eth_unpack,
-};
-
-static const enum ethtool_link_mode_bit_indices
-mlxsw_sp2_mask_ethtool_sgmii_100m[] = {
- ETHTOOL_LINK_MODE_100baseT_Full_BIT,
-};
-
-#define MLXSW_SP2_MASK_ETHTOOL_SGMII_100M_LEN \
- ARRAY_SIZE(mlxsw_sp2_mask_ethtool_sgmii_100m)
-
-static const enum ethtool_link_mode_bit_indices
-mlxsw_sp2_mask_ethtool_1000base_x_sgmii[] = {
- ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
- ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
-};
-
-#define MLXSW_SP2_MASK_ETHTOOL_1000BASE_X_SGMII_LEN \
- ARRAY_SIZE(mlxsw_sp2_mask_ethtool_1000base_x_sgmii)
-
-static const enum ethtool_link_mode_bit_indices
-mlxsw_sp2_mask_ethtool_2_5gbase_x_2_5gmii[] = {
- ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
-};
-
-#define MLXSW_SP2_MASK_ETHTOOL_2_5GBASE_X_2_5GMII_LEN \
- ARRAY_SIZE(mlxsw_sp2_mask_ethtool_2_5gbase_x_2_5gmii)
-
-static const enum ethtool_link_mode_bit_indices
-mlxsw_sp2_mask_ethtool_5gbase_r[] = {
- ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
-};
-
-#define MLXSW_SP2_MASK_ETHTOOL_5GBASE_R_LEN \
- ARRAY_SIZE(mlxsw_sp2_mask_ethtool_5gbase_r)
-
-static const enum ethtool_link_mode_bit_indices
-mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g[] = {
- ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
- ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
- ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
- ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
- ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
- ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
- ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
-};
-
-#define MLXSW_SP2_MASK_ETHTOOL_XFI_XAUI_1_10G_LEN \
- ARRAY_SIZE(mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g)
-
-static const enum ethtool_link_mode_bit_indices
-mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g[] = {
- ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
- ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
- ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
- ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
-};
-
-#define MLXSW_SP2_MASK_ETHTOOL_XLAUI_4_XLPPI_4_40G_LEN \
- ARRAY_SIZE(mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g)
-
-static const enum ethtool_link_mode_bit_indices
-mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr[] = {
- ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
- ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
- ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
-};
-
-#define MLXSW_SP2_MASK_ETHTOOL_25GAUI_1_25GBASE_CR_KR_LEN \
- ARRAY_SIZE(mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr)
-
-static const enum ethtool_link_mode_bit_indices
-mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2[] = {
- ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
- ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
- ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
-};
-
-#define MLXSW_SP2_MASK_ETHTOOL_50GAUI_2_LAUI_2_50GBASE_CR2_KR2_LEN \
- ARRAY_SIZE(mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2)
-
-static const enum ethtool_link_mode_bit_indices
-mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr[] = {
- ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
- ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
- ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
- ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
- ETHTOOL_LINK_MODE_50000baseDR_Full_BIT,
-};
-
-#define MLXSW_SP2_MASK_ETHTOOL_50GAUI_1_LAUI_1_50GBASE_CR_KR_LEN \
- ARRAY_SIZE(mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr)
-
-static const enum ethtool_link_mode_bit_indices
-mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4[] = {
- ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
- ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
- ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
- ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
-};
-
-#define MLXSW_SP2_MASK_ETHTOOL_CAUI_4_100GBASE_CR4_KR4_LEN \
- ARRAY_SIZE(mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4)
-
-static const enum ethtool_link_mode_bit_indices
-mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2[] = {
- ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
- ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
- ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
- ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
- ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT,
-};
-
-#define MLXSW_SP2_MASK_ETHTOOL_100GAUI_2_100GBASE_CR2_KR2_LEN \
- ARRAY_SIZE(mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2)
-
-static const enum ethtool_link_mode_bit_indices
-mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4[] = {
- ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
- ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
- ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
- ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT,
- ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
-};
-
-#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)
-{
- switch (width) {
- case 1:
- return MLXSW_SP_PORT_MASK_WIDTH_1X;
- case 2:
- 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;
- }
-}
-
-struct mlxsw_sp2_port_link_mode {
- const enum ethtool_link_mode_bit_indices *mask_ethtool;
- int m_ethtool_len;
- u32 mask;
- u32 speed;
- u8 mask_width;
-};
-
-static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = {
- {
- .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_SGMII_100M,
- .mask_ethtool = mlxsw_sp2_mask_ethtool_sgmii_100m,
- .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_8X,
- .speed = SPEED_100,
- },
- {
- .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_1000BASE_X_SGMII,
- .mask_ethtool = mlxsw_sp2_mask_ethtool_1000base_x_sgmii,
- .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_8X,
- .speed = SPEED_1000,
- },
- {
- .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_2_5GBASE_X_2_5GMII,
- .mask_ethtool = mlxsw_sp2_mask_ethtool_2_5gbase_x_2_5gmii,
- .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_8X,
- .speed = SPEED_2500,
- },
- {
- .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_5GBASE_R,
- .mask_ethtool = mlxsw_sp2_mask_ethtool_5gbase_r,
- .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_8X,
- .speed = SPEED_5000,
- },
- {
- .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_XFI_XAUI_1_10G,
- .mask_ethtool = mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g,
- .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_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 |
- MLXSW_SP_PORT_MASK_WIDTH_8X,
- .speed = SPEED_40000,
- },
- {
- .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_25GAUI_1_25GBASE_CR_KR,
- .mask_ethtool = mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr,
- .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_8X,
- .speed = SPEED_25000,
- },
- {
- .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_50GAUI_2_LAUI_2_50GBASE_CR2_KR2,
- .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_8X,
- .speed = SPEED_50000,
- },
- {
- .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_50GAUI_1_LAUI_1_50GBASE_CR_KR,
- .mask_ethtool = mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr,
- .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_50GAUI_1_LAUI_1_50GBASE_CR_KR_LEN,
- .mask_width = MLXSW_SP_PORT_MASK_WIDTH_1X,
- .speed = SPEED_50000,
- },
- {
- .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 |
- MLXSW_SP_PORT_MASK_WIDTH_8X,
- .speed = SPEED_100000,
- },
- {
- .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_100GAUI_2_100GBASE_CR2_KR2,
- .mask_ethtool = mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2,
- .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_100GAUI_2_100GBASE_CR2_KR2_LEN,
- .mask_width = MLXSW_SP_PORT_MASK_WIDTH_2X,
- .speed = SPEED_100000,
- },
- {
- .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 |
- 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)
-
-static void
-mlxsw_sp2_from_ptys_supported_port(struct mlxsw_sp *mlxsw_sp,
- u32 ptys_eth_proto,
- struct ethtool_link_ksettings *cmd)
-{
- ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
- ethtool_link_ksettings_add_link_mode(cmd, supported, Backplane);
-}
-
-static void
-mlxsw_sp2_set_bit_ethtool(const struct mlxsw_sp2_port_link_mode *link_mode,
- unsigned long *mode)
-{
- int i;
-
- for (i = 0; i < link_mode->m_ethtool_len; i++)
- __set_bit(link_mode->mask_ethtool[i], mode);
-}
-
-static void
-mlxsw_sp2_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
- u8 width, unsigned long *mode)
-{
- u8 mask_width = mlxsw_sp_port_mask_width_get(width);
- int i;
-
- for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
- if ((ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) &&
- (mask_width & mlxsw_sp2_port_link_mode[i].mask_width))
- mlxsw_sp2_set_bit_ethtool(&mlxsw_sp2_port_link_mode[i],
- mode);
- }
-}
-
-static u32
-mlxsw_sp2_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto)
-{
- int i;
-
- for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
- if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask)
- return mlxsw_sp2_port_link_mode[i].speed;
- }
-
- return SPEED_UNKNOWN;
-}
-
-static void
-mlxsw_sp2_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
- u32 ptys_eth_proto,
- struct ethtool_link_ksettings *cmd)
-{
- cmd->base.speed = SPEED_UNKNOWN;
- cmd->base.duplex = DUPLEX_UNKNOWN;
-
- if (!carrier_ok)
- return;
-
- cmd->base.speed = mlxsw_sp2_from_ptys_speed(mlxsw_sp, ptys_eth_proto);
- if (cmd->base.speed != SPEED_UNKNOWN)
- cmd->base.duplex = DUPLEX_FULL;
-}
-
-static bool
-mlxsw_sp2_test_bit_ethtool(const struct mlxsw_sp2_port_link_mode *link_mode,
- const unsigned long *mode)
-{
- int cnt = 0;
- int i;
-
- for (i = 0; i < link_mode->m_ethtool_len; i++) {
- if (test_bit(link_mode->mask_ethtool[i], mode))
- cnt++;
- }
-
- return cnt == link_mode->m_ethtool_len;
-}
-
-static u32
-mlxsw_sp2_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp, u8 width,
- const struct ethtool_link_ksettings *cmd)
-{
- u8 mask_width = mlxsw_sp_port_mask_width_get(width);
- u32 ptys_proto = 0;
- int i;
-
- for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
- if ((mask_width & mlxsw_sp2_port_link_mode[i].mask_width) &&
- mlxsw_sp2_test_bit_ethtool(&mlxsw_sp2_port_link_mode[i],
- cmd->link_modes.advertising))
- ptys_proto |= mlxsw_sp2_port_link_mode[i].mask;
- }
- return ptys_proto;
-}
-
-static u32 mlxsw_sp2_to_ptys_speed(struct mlxsw_sp *mlxsw_sp,
- u8 width, u32 speed)
-{
- u8 mask_width = mlxsw_sp_port_mask_width_get(width);
- u32 ptys_proto = 0;
- int i;
-
- for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
- if ((speed == mlxsw_sp2_port_link_mode[i].speed) &&
- (mask_width & mlxsw_sp2_port_link_mode[i].mask_width))
- ptys_proto |= mlxsw_sp2_port_link_mode[i].mask;
- }
- return ptys_proto;
-}
-
-static void
-mlxsw_sp2_reg_ptys_eth_pack(struct mlxsw_sp *mlxsw_sp, char *payload,
- u8 local_port, u32 proto_admin,
- bool autoneg)
-{
- mlxsw_reg_ptys_ext_eth_pack(payload, local_port, proto_admin, autoneg);
-}
-
-static void
-mlxsw_sp2_reg_ptys_eth_unpack(struct mlxsw_sp *mlxsw_sp, char *payload,
- u32 *p_eth_proto_cap, u32 *p_eth_proto_admin,
- u32 *p_eth_proto_oper)
-{
- mlxsw_reg_ptys_ext_eth_unpack(payload, p_eth_proto_cap,
- p_eth_proto_admin, p_eth_proto_oper);
-}
-
-static const struct mlxsw_sp_port_type_speed_ops
-mlxsw_sp2_port_type_speed_ops = {
- .from_ptys_supported_port = mlxsw_sp2_from_ptys_supported_port,
- .from_ptys_link = mlxsw_sp2_from_ptys_link,
- .from_ptys_speed = mlxsw_sp2_from_ptys_speed,
- .from_ptys_speed_duplex = mlxsw_sp2_from_ptys_speed_duplex,
- .to_ptys_advert_link = mlxsw_sp2_to_ptys_advert_link,
- .to_ptys_speed = mlxsw_sp2_to_ptys_speed,
- .reg_ptys_eth_pack = mlxsw_sp2_reg_ptys_eth_pack,
- .reg_ptys_eth_unpack = mlxsw_sp2_reg_ptys_eth_unpack,
-};
-
-static void
-mlxsw_sp_port_get_link_supported(struct mlxsw_sp *mlxsw_sp, u32 eth_proto_cap,
- u8 width, struct ethtool_link_ksettings *cmd)
-{
- const struct mlxsw_sp_port_type_speed_ops *ops;
-
- ops = mlxsw_sp->port_type_speed_ops;
-
- ethtool_link_ksettings_add_link_mode(cmd, supported, Asym_Pause);
- ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
- ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);
-
- ops->from_ptys_supported_port(mlxsw_sp, eth_proto_cap, cmd);
- ops->from_ptys_link(mlxsw_sp, eth_proto_cap, width,
- cmd->link_modes.supported);
-}
-
-static void
-mlxsw_sp_port_get_link_advertise(struct mlxsw_sp *mlxsw_sp,
- u32 eth_proto_admin, bool autoneg, u8 width,
- struct ethtool_link_ksettings *cmd)
-{
- const struct mlxsw_sp_port_type_speed_ops *ops;
-
- ops = mlxsw_sp->port_type_speed_ops;
-
- if (!autoneg)
- return;
-
- ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
- ops->from_ptys_link(mlxsw_sp, eth_proto_admin, width,
- cmd->link_modes.advertising);
-}
-
-static u8
-mlxsw_sp_port_connector_port(enum mlxsw_reg_ptys_connector_type connector_type)
-{
- switch (connector_type) {
- case MLXSW_REG_PTYS_CONNECTOR_TYPE_UNKNOWN_OR_NO_CONNECTOR:
- return PORT_OTHER;
- case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_NONE:
- return PORT_NONE;
- case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_TP:
- return PORT_TP;
- case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_AUI:
- return PORT_AUI;
- case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_BNC:
- return PORT_BNC;
- case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_MII:
- return PORT_MII;
- case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_FIBRE:
- return PORT_FIBRE;
- case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_DA:
- return PORT_DA;
- case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_OTHER:
- return PORT_OTHER;
- default:
- WARN_ON_ONCE(1);
- return PORT_OTHER;
- }
-}
-
-static int mlxsw_sp_port_get_link_ksettings(struct net_device *dev,
- struct ethtool_link_ksettings *cmd)
-{
- u32 eth_proto_cap, eth_proto_admin, eth_proto_oper;
- struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
- struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
- const struct mlxsw_sp_port_type_speed_ops *ops;
- char ptys_pl[MLXSW_REG_PTYS_LEN];
- u8 connector_type;
- bool autoneg;
- int err;
-
- ops = mlxsw_sp->port_type_speed_ops;
-
- autoneg = mlxsw_sp_port->link.autoneg;
- ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
- 0, false);
- err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
- if (err)
- return err;
- ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, &eth_proto_cap,
- &eth_proto_admin, &eth_proto_oper);
-
- mlxsw_sp_port_get_link_supported(mlxsw_sp, eth_proto_cap,
- mlxsw_sp_port->mapping.width, cmd);
-
- mlxsw_sp_port_get_link_advertise(mlxsw_sp, eth_proto_admin, autoneg,
- mlxsw_sp_port->mapping.width, cmd);
-
- cmd->base.autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
- connector_type = mlxsw_reg_ptys_connector_type_get(ptys_pl);
- cmd->base.port = mlxsw_sp_port_connector_port(connector_type);
- ops->from_ptys_speed_duplex(mlxsw_sp, netif_carrier_ok(dev),
- eth_proto_oper, cmd);
-
- return 0;
-}
-
-static int
-mlxsw_sp_port_set_link_ksettings(struct net_device *dev,
- const struct ethtool_link_ksettings *cmd)
-{
- struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
- struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
- const struct mlxsw_sp_port_type_speed_ops *ops;
- char ptys_pl[MLXSW_REG_PTYS_LEN];
- u32 eth_proto_cap, eth_proto_new;
- bool autoneg;
- int err;
-
- ops = mlxsw_sp->port_type_speed_ops;
-
- ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
- 0, false);
- err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
- if (err)
- return err;
- ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, &eth_proto_cap, NULL, NULL);
-
- autoneg = cmd->base.autoneg == AUTONEG_ENABLE;
- eth_proto_new = autoneg ?
- ops->to_ptys_advert_link(mlxsw_sp, mlxsw_sp_port->mapping.width,
- cmd) :
- ops->to_ptys_speed(mlxsw_sp, mlxsw_sp_port->mapping.width,
- cmd->base.speed);
-
- eth_proto_new = eth_proto_new & eth_proto_cap;
- if (!eth_proto_new) {
- netdev_err(dev, "No supported speed requested\n");
- return -EINVAL;
- }
-
- ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
- eth_proto_new, autoneg);
- err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
- if (err)
- return err;
-
- mlxsw_sp_port->link.autoneg = autoneg;
-
- if (!netif_running(dev))
- return 0;
-
- mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
- mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true);
-
- return 0;
-}
-
-static int mlxsw_sp_get_module_info(struct net_device *netdev,
- struct ethtool_modinfo *modinfo)
-{
- struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
- struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
- int err;
-
- err = mlxsw_env_get_module_info(mlxsw_sp->core,
- mlxsw_sp_port->mapping.module,
- modinfo);
-
- return err;
-}
-
-static int mlxsw_sp_get_module_eeprom(struct net_device *netdev,
- struct ethtool_eeprom *ee,
- u8 *data)
-{
- struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
- struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
- int err;
-
- err = mlxsw_env_get_module_eeprom(netdev, mlxsw_sp->core,
- mlxsw_sp_port->mapping.module, ee,
- data);
-
- return err;
-}
-
-static int
-mlxsw_sp_get_ts_info(struct net_device *netdev, struct ethtool_ts_info *info)
-{
- struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
- struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
-
- return mlxsw_sp->ptp_ops->get_ts_info(mlxsw_sp, info);
-}
-
-static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
- .get_drvinfo = mlxsw_sp_port_get_drvinfo,
- .get_link = ethtool_op_get_link,
- .get_pauseparam = mlxsw_sp_port_get_pauseparam,
- .set_pauseparam = mlxsw_sp_port_set_pauseparam,
- .get_strings = mlxsw_sp_port_get_strings,
- .set_phys_id = mlxsw_sp_port_set_phys_id,
- .get_ethtool_stats = mlxsw_sp_port_get_stats,
- .get_sset_count = mlxsw_sp_port_get_sset_count,
- .get_link_ksettings = mlxsw_sp_port_get_link_ksettings,
- .set_link_ksettings = mlxsw_sp_port_set_link_ksettings,
- .get_module_info = mlxsw_sp_get_module_info,
- .get_module_eeprom = mlxsw_sp_get_module_eeprom,
- .get_ts_info = mlxsw_sp_get_ts_info,
-};
-
static int
mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port)
{
@@ -3246,12 +1744,16 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
bool split = !!split_base_local_port;
struct mlxsw_sp_port *mlxsw_sp_port;
+ u32 lanes = port_mapping->width;
struct net_device *dev;
+ bool splittable;
int err;
+ splittable = lanes > 1 && !split;
err = mlxsw_core_port_init(mlxsw_sp->core, local_port,
port_mapping->module + 1, split,
- port_mapping->lane / port_mapping->width,
+ port_mapping->lane / lanes,
+ splittable, lanes,
mlxsw_sp->base_mac,
sizeof(mlxsw_sp->base_mac));
if (err) {
@@ -3745,13 +2247,6 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
return -EINVAL;
}
- /* 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) {
@@ -3760,19 +2255,13 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
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) {
+ /* Split port with non-max cannot be split. */
+ if (mlxsw_sp_port->mapping.width != max_width) {
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 == 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;
- }
-
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");
@@ -4334,52 +2823,6 @@ static const struct mlxsw_sp_ptp_ops mlxsw_sp2_ptp_ops = {
.get_stats = mlxsw_sp2_get_stats,
};
-static u32 mlxsw_sp1_span_buffsize_get(int mtu, u32 speed)
-{
- return mtu * 5 / 2;
-}
-
-static const struct mlxsw_sp_span_ops mlxsw_sp1_span_ops = {
- .buffsize_get = mlxsw_sp1_span_buffsize_get,
-};
-
-#define MLXSW_SP2_SPAN_EG_MIRROR_BUFFER_FACTOR 38
-#define MLXSW_SP3_SPAN_EG_MIRROR_BUFFER_FACTOR 50
-
-static u32 __mlxsw_sp_span_buffsize_get(int mtu, u32 speed, u32 buffer_factor)
-{
- return 3 * mtu + buffer_factor * speed / 1000;
-}
-
-static u32 mlxsw_sp2_span_buffsize_get(int mtu, u32 speed)
-{
- int factor = MLXSW_SP2_SPAN_EG_MIRROR_BUFFER_FACTOR;
-
- return __mlxsw_sp_span_buffsize_get(mtu, speed, factor);
-}
-
-static const struct mlxsw_sp_span_ops mlxsw_sp2_span_ops = {
- .buffsize_get = mlxsw_sp2_span_buffsize_get,
-};
-
-static u32 mlxsw_sp3_span_buffsize_get(int mtu, u32 speed)
-{
- int factor = MLXSW_SP3_SPAN_EG_MIRROR_BUFFER_FACTOR;
-
- return __mlxsw_sp_span_buffsize_get(mtu, speed, factor);
-}
-
-static const struct mlxsw_sp_span_ops mlxsw_sp3_span_ops = {
- .buffsize_get = mlxsw_sp3_span_buffsize_get,
-};
-
-u32 mlxsw_sp_span_buffsize_get(struct mlxsw_sp *mlxsw_sp, int mtu, u32 speed)
-{
- u32 buffsize = mlxsw_sp->span_ops->buffsize_get(speed, mtu);
-
- return mlxsw_sp_bytes_cells(mlxsw_sp, buffsize) + 1;
-}
-
static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
unsigned long event, void *ptr);
@@ -4417,6 +2860,12 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
goto err_fids_init;
}
+ err = mlxsw_sp_policers_init(mlxsw_sp);
+ if (err) {
+ dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize policers\n");
+ goto err_policers_init;
+ }
+
err = mlxsw_sp_traps_init(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to set traps\n");
@@ -4576,6 +3025,8 @@ err_buffers_init:
err_devlink_traps_init:
mlxsw_sp_traps_fini(mlxsw_sp);
err_traps_init:
+ mlxsw_sp_policers_fini(mlxsw_sp);
+err_policers_init:
mlxsw_sp_fids_fini(mlxsw_sp);
err_fids_init:
mlxsw_sp_kvdl_fini(mlxsw_sp);
@@ -4594,6 +3045,7 @@ static int mlxsw_sp1_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->afa_ops = &mlxsw_sp1_act_afa_ops;
mlxsw_sp->afk_ops = &mlxsw_sp1_afk_ops;
mlxsw_sp->mr_tcam_ops = &mlxsw_sp1_mr_tcam_ops;
+ mlxsw_sp->acl_rulei_ops = &mlxsw_sp1_acl_rulei_ops;
mlxsw_sp->acl_tcam_ops = &mlxsw_sp1_acl_tcam_ops;
mlxsw_sp->nve_ops_arr = mlxsw_sp1_nve_ops_arr;
mlxsw_sp->mac_mask = mlxsw_sp1_mac_mask;
@@ -4602,6 +3054,8 @@ static int mlxsw_sp1_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->port_type_speed_ops = &mlxsw_sp1_port_type_speed_ops;
mlxsw_sp->ptp_ops = &mlxsw_sp1_ptp_ops;
mlxsw_sp->span_ops = &mlxsw_sp1_span_ops;
+ mlxsw_sp->policer_core_ops = &mlxsw_sp1_policer_core_ops;
+ mlxsw_sp->trap_ops = &mlxsw_sp1_trap_ops;
mlxsw_sp->listeners = mlxsw_sp1_listener;
mlxsw_sp->listeners_count = ARRAY_SIZE(mlxsw_sp1_listener);
mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP1;
@@ -4621,6 +3075,7 @@ static int mlxsw_sp2_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->afa_ops = &mlxsw_sp2_act_afa_ops;
mlxsw_sp->afk_ops = &mlxsw_sp2_afk_ops;
mlxsw_sp->mr_tcam_ops = &mlxsw_sp2_mr_tcam_ops;
+ mlxsw_sp->acl_rulei_ops = &mlxsw_sp2_acl_rulei_ops;
mlxsw_sp->acl_tcam_ops = &mlxsw_sp2_acl_tcam_ops;
mlxsw_sp->nve_ops_arr = mlxsw_sp2_nve_ops_arr;
mlxsw_sp->mac_mask = mlxsw_sp2_mac_mask;
@@ -4629,6 +3084,8 @@ 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;
mlxsw_sp->span_ops = &mlxsw_sp2_span_ops;
+ mlxsw_sp->policer_core_ops = &mlxsw_sp2_policer_core_ops;
+ mlxsw_sp->trap_ops = &mlxsw_sp2_trap_ops;
mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP2;
return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack);
@@ -4640,10 +3097,13 @@ static int mlxsw_sp3_init(struct mlxsw_core *mlxsw_core,
{
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
+ mlxsw_sp->req_rev = &mlxsw_sp3_fw_rev;
+ mlxsw_sp->fw_filename = MLXSW_SP3_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;
mlxsw_sp->mr_tcam_ops = &mlxsw_sp2_mr_tcam_ops;
+ mlxsw_sp->acl_rulei_ops = &mlxsw_sp2_acl_rulei_ops;
mlxsw_sp->acl_tcam_ops = &mlxsw_sp2_acl_tcam_ops;
mlxsw_sp->nve_ops_arr = mlxsw_sp2_nve_ops_arr;
mlxsw_sp->mac_mask = mlxsw_sp2_mac_mask;
@@ -4652,6 +3112,8 @@ static int mlxsw_sp3_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;
mlxsw_sp->span_ops = &mlxsw_sp3_span_ops;
+ mlxsw_sp->policer_core_ops = &mlxsw_sp2_policer_core_ops;
+ mlxsw_sp->trap_ops = &mlxsw_sp2_trap_ops;
mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP3;
return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack);
@@ -4681,6 +3143,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
mlxsw_sp_buffers_fini(mlxsw_sp);
mlxsw_sp_devlink_traps_fini(mlxsw_sp);
mlxsw_sp_traps_fini(mlxsw_sp);
+ mlxsw_sp_policers_fini(mlxsw_sp);
mlxsw_sp_fids_fini(mlxsw_sp);
mlxsw_sp_kvdl_fini(mlxsw_sp);
}
@@ -4892,6 +3355,10 @@ static int mlxsw_sp1_resources_register(struct mlxsw_core *mlxsw_core)
if (err)
goto err_resources_counter_register;
+ err = mlxsw_sp_policer_resources_register(mlxsw_core);
+ if (err)
+ goto err_resources_counter_register;
+
return 0;
err_resources_counter_register:
@@ -4916,6 +3383,10 @@ static int mlxsw_sp2_resources_register(struct mlxsw_core *mlxsw_core)
if (err)
goto err_resources_counter_register;
+ err = mlxsw_sp_policer_resources_register(mlxsw_core);
+ if (err)
+ goto err_resources_counter_register;
+
return 0;
err_resources_counter_register:
@@ -6330,3 +4801,4 @@ 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);
+MODULE_FIRMWARE(MLXSW_SP3_FW_FILENAME);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index 3abe3e7d89bc..f9ba59641b4d 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -62,6 +62,8 @@ enum mlxsw_sp_resource_id {
MLXSW_SP_RESOURCE_COUNTERS,
MLXSW_SP_RESOURCE_COUNTERS_FLOW,
MLXSW_SP_RESOURCE_COUNTERS_RIF,
+ MLXSW_SP_RESOURCE_GLOBAL_POLICERS,
+ MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS,
};
struct mlxsw_sp_port;
@@ -120,6 +122,7 @@ struct mlxsw_sp_kvdl;
struct mlxsw_sp_nve;
struct mlxsw_sp_kvdl_ops;
struct mlxsw_sp_mr_tcam_ops;
+struct mlxsw_sp_acl_rulei_ops;
struct mlxsw_sp_acl_tcam_ops;
struct mlxsw_sp_nve_ops;
struct mlxsw_sp_sb_vals;
@@ -150,6 +153,7 @@ struct mlxsw_sp {
struct mlxsw_afa *afa;
struct mlxsw_sp_acl *acl;
struct mlxsw_sp_fid_core *fid_core;
+ struct mlxsw_sp_policer_core *policer_core;
struct mlxsw_sp_kvdl *kvdl;
struct mlxsw_sp_nve *nve;
struct notifier_block netdevice_nb;
@@ -164,6 +168,7 @@ struct mlxsw_sp {
const struct mlxsw_afa_ops *afa_ops;
const struct mlxsw_afk_ops *afk_ops;
const struct mlxsw_sp_mr_tcam_ops *mr_tcam_ops;
+ const struct mlxsw_sp_acl_rulei_ops *acl_rulei_ops;
const struct mlxsw_sp_acl_tcam_ops *acl_tcam_ops;
const struct mlxsw_sp_nve_ops **nve_ops_arr;
const struct mlxsw_sp_rif_ops **rif_ops_arr;
@@ -171,11 +176,46 @@ struct mlxsw_sp {
const struct mlxsw_sp_port_type_speed_ops *port_type_speed_ops;
const struct mlxsw_sp_ptp_ops *ptp_ops;
const struct mlxsw_sp_span_ops *span_ops;
+ const struct mlxsw_sp_policer_core_ops *policer_core_ops;
+ const struct mlxsw_sp_trap_ops *trap_ops;
const struct mlxsw_listener *listeners;
size_t listeners_count;
u32 lowest_shaper_bs;
};
+struct mlxsw_sp_ptp_ops {
+ struct mlxsw_sp_ptp_clock *
+ (*clock_init)(struct mlxsw_sp *mlxsw_sp, struct device *dev);
+ void (*clock_fini)(struct mlxsw_sp_ptp_clock *clock);
+
+ struct mlxsw_sp_ptp_state *(*init)(struct mlxsw_sp *mlxsw_sp);
+ void (*fini)(struct mlxsw_sp_ptp_state *ptp_state);
+
+ /* Notify a driver that a packet that might be PTP was received. Driver
+ * is responsible for freeing the passed-in SKB.
+ */
+ void (*receive)(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
+ u8 local_port);
+
+ /* Notify a driver that a timestamped packet was transmitted. Driver
+ * is responsible for freeing the passed-in SKB.
+ */
+ void (*transmitted)(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
+ u8 local_port);
+
+ int (*hwtstamp_get)(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct hwtstamp_config *config);
+ int (*hwtstamp_set)(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct hwtstamp_config *config);
+ void (*shaper_work)(struct work_struct *work);
+ int (*get_ts_info)(struct mlxsw_sp *mlxsw_sp,
+ struct ethtool_ts_info *info);
+ int (*get_stats_count)(void);
+ void (*get_stats_strings)(u8 **p);
+ void (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
+ u64 *data, int data_index);
+};
+
static inline struct mlxsw_sp_upper *
mlxsw_sp_lag_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
{
@@ -391,6 +431,13 @@ enum mlxsw_sp_flood_type {
MLXSW_SP_FLOOD_TYPE_MC,
};
+int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
+ int mtu, bool pause_en);
+int mlxsw_sp_port_get_stats_raw(struct net_device *dev, int grp,
+ int prio, char *ppcnt_pl);
+int mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port,
+ bool is_up);
+
/* spectrum_buffers.c */
int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp);
void mlxsw_sp_buffers_fini(struct mlxsw_sp *mlxsw_sp);
@@ -497,7 +544,6 @@ int mlxsw_sp_flow_counter_alloc(struct mlxsw_sp *mlxsw_sp,
unsigned int *p_counter_index);
void mlxsw_sp_flow_counter_free(struct mlxsw_sp *mlxsw_sp,
unsigned int counter_index);
-u32 mlxsw_sp_span_buffsize_get(struct mlxsw_sp *mlxsw_sp, int mtu, u32 speed);
bool mlxsw_sp_port_dev_check(const struct net_device *dev);
struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev);
struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find(struct net_device *dev);
@@ -644,8 +690,10 @@ struct mlxsw_sp_acl_rule_info {
u8 action_created:1,
ingress_bind_blocker:1,
egress_bind_blocker:1,
- counter_valid:1;
+ counter_valid:1,
+ policer_index_valid:1;
unsigned int counter_index;
+ u16 policer_index;
};
/* spectrum_flow.c */
@@ -669,7 +717,6 @@ struct mlxsw_sp_flow_block {
struct mlxsw_sp_flow_block_binding {
struct list_head list;
- struct net_device *dev;
struct mlxsw_sp_port *mlxsw_sp_port;
bool ingress;
};
@@ -727,8 +774,9 @@ mlxsw_sp_flow_block_is_mixed_bound(const struct mlxsw_sp_flow_block *block)
struct mlxsw_sp_flow_block *mlxsw_sp_flow_block_create(struct mlxsw_sp *mlxsw_sp,
struct net *net);
void mlxsw_sp_flow_block_destroy(struct mlxsw_sp_flow_block *block);
-int mlxsw_sp_setup_tc_block(struct mlxsw_sp_port *mlxsw_sp_port,
- struct flow_block_offload *f);
+int mlxsw_sp_setup_tc_block_clsact(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct flow_block_offload *f,
+ bool ingress);
/* spectrum_acl.c */
struct mlxsw_sp_acl_ruleset;
@@ -806,6 +854,10 @@ int mlxsw_sp_acl_rulei_act_mangle(struct mlxsw_sp *mlxsw_sp,
enum flow_action_mangle_base htype,
u32 offset, u32 mask, u32 val,
struct netlink_ext_ack *extack);
+int mlxsw_sp_acl_rulei_act_police(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_acl_rule_info *rulei,
+ u32 index, u64 rate_bytes_ps,
+ u32 burst, struct netlink_ext_ack *extack);
int mlxsw_sp_acl_rulei_act_count(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_rule_info *rulei,
struct netlink_ext_ack *extack);
@@ -838,7 +890,8 @@ struct mlxsw_sp_acl_rule_info *
mlxsw_sp_acl_rule_rulei(struct mlxsw_sp_acl_rule *rule);
int mlxsw_sp_acl_rule_get_stats(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_rule *rule,
- u64 *packets, u64 *bytes, u64 *last_use,
+ u64 *packets, u64 *bytes, u64 *drops,
+ u64 *last_use,
enum flow_action_hw_stats *used_hw_stats);
struct mlxsw_sp_fid *mlxsw_sp_acl_dummy_fid(struct mlxsw_sp *mlxsw_sp);
@@ -854,6 +907,17 @@ void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp);
u32 mlxsw_sp_acl_region_rehash_intrvl_get(struct mlxsw_sp *mlxsw_sp);
int mlxsw_sp_acl_region_rehash_intrvl_set(struct mlxsw_sp *mlxsw_sp, u32 val);
+struct mlxsw_sp_acl_mangle_action;
+
+struct mlxsw_sp_acl_rulei_ops {
+ int (*act_mangle_field)(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_rule_info *rulei,
+ struct mlxsw_sp_acl_mangle_action *mact, u32 val,
+ struct netlink_ext_ack *extack);
+};
+
+extern struct mlxsw_sp_acl_rulei_ops mlxsw_sp1_acl_rulei_ops;
+extern struct mlxsw_sp_acl_rulei_ops mlxsw_sp2_acl_rulei_ops;
+
/* spectrum_acl_tcam.c */
struct mlxsw_sp_acl_tcam;
struct mlxsw_sp_acl_tcam_region;
@@ -909,6 +973,35 @@ extern const struct mlxsw_afk_ops mlxsw_sp1_afk_ops;
extern const struct mlxsw_afk_ops mlxsw_sp2_afk_ops;
/* spectrum_matchall.c */
+enum mlxsw_sp_mall_action_type {
+ MLXSW_SP_MALL_ACTION_TYPE_MIRROR,
+ MLXSW_SP_MALL_ACTION_TYPE_SAMPLE,
+ MLXSW_SP_MALL_ACTION_TYPE_TRAP,
+};
+
+struct mlxsw_sp_mall_mirror_entry {
+ const struct net_device *to_dev;
+ int span_id;
+};
+
+struct mlxsw_sp_mall_trap_entry {
+ int span_id;
+};
+
+struct mlxsw_sp_mall_entry {
+ struct list_head list;
+ unsigned long cookie;
+ unsigned int priority;
+ enum mlxsw_sp_mall_action_type type;
+ bool ingress;
+ union {
+ struct mlxsw_sp_mall_mirror_entry mirror;
+ struct mlxsw_sp_mall_trap_entry trap;
+ struct mlxsw_sp_port_sample sample;
+ };
+ struct rcu_head rcu;
+};
+
int mlxsw_sp_mall_replace(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_flow_block *block,
struct tc_cls_matchall_offload *f);
@@ -955,6 +1048,8 @@ int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_tbf_qopt_offload *p);
int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
struct tc_fifo_qopt_offload *p);
+int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct flow_block_offload *f);
/* spectrum_fid.c */
bool mlxsw_sp_fid_is_dummy(struct mlxsw_sp *mlxsw_sp, u16 fid_index);
@@ -1088,12 +1183,14 @@ void mlxsw_sp_trap_fini(struct mlxsw_core *mlxsw_core,
const struct devlink_trap *trap, void *trap_ctx);
int mlxsw_sp_trap_action_set(struct mlxsw_core *mlxsw_core,
const struct devlink_trap *trap,
- enum devlink_trap_action action);
+ enum devlink_trap_action action,
+ struct netlink_ext_ack *extack);
int mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group);
int mlxsw_sp_trap_group_set(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group,
- const struct devlink_trap_policer *policer);
+ const struct devlink_trap_policer *policer,
+ struct netlink_ext_ack *extack);
int
mlxsw_sp_trap_policer_init(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer);
@@ -1107,10 +1204,48 @@ int
mlxsw_sp_trap_policer_counter_get(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer,
u64 *p_drops);
+int mlxsw_sp_trap_group_policer_hw_id_get(struct mlxsw_sp *mlxsw_sp, u16 id,
+ bool *p_enabled, u16 *p_hw_id);
static inline struct net *mlxsw_sp_net(struct mlxsw_sp *mlxsw_sp)
{
return mlxsw_core_net(mlxsw_sp->core);
}
+/* spectrum_ethtool.c */
+extern const struct ethtool_ops mlxsw_sp_port_ethtool_ops;
+extern const struct mlxsw_sp_port_type_speed_ops mlxsw_sp1_port_type_speed_ops;
+extern const struct mlxsw_sp_port_type_speed_ops mlxsw_sp2_port_type_speed_ops;
+
+/* spectrum_policer.c */
+extern const struct mlxsw_sp_policer_core_ops mlxsw_sp1_policer_core_ops;
+extern const struct mlxsw_sp_policer_core_ops mlxsw_sp2_policer_core_ops;
+
+enum mlxsw_sp_policer_type {
+ MLXSW_SP_POLICER_TYPE_SINGLE_RATE,
+
+ __MLXSW_SP_POLICER_TYPE_MAX,
+ MLXSW_SP_POLICER_TYPE_MAX = __MLXSW_SP_POLICER_TYPE_MAX - 1,
+};
+
+struct mlxsw_sp_policer_params {
+ u64 rate;
+ u64 burst;
+ bool bytes;
+};
+
+int mlxsw_sp_policer_add(struct mlxsw_sp *mlxsw_sp,
+ enum mlxsw_sp_policer_type type,
+ const struct mlxsw_sp_policer_params *params,
+ struct netlink_ext_ack *extack, u16 *p_policer_index);
+void mlxsw_sp_policer_del(struct mlxsw_sp *mlxsw_sp,
+ enum mlxsw_sp_policer_type type,
+ u16 policer_index);
+int mlxsw_sp_policer_drops_counter_get(struct mlxsw_sp *mlxsw_sp,
+ enum mlxsw_sp_policer_type type,
+ u16 policer_index, u64 *p_drops);
+int mlxsw_sp_policers_init(struct mlxsw_sp *mlxsw_sp);
+void mlxsw_sp_policers_fini(struct mlxsw_sp *mlxsw_sp);
+int mlxsw_sp_policer_resources_register(struct mlxsw_core *mlxsw_core);
+
#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c
index 47da9ee0045d..8cfa03a75374 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c
@@ -66,6 +66,7 @@ struct mlxsw_sp_acl_rule {
u64 last_used;
u64 last_packets;
u64 last_bytes;
+ u64 last_drops;
unsigned long priv[];
/* priv has to be always the last item */
};
@@ -508,6 +509,8 @@ enum mlxsw_sp_acl_mangle_field {
MLXSW_SP_ACL_MANGLE_FIELD_IP_DSFIELD,
MLXSW_SP_ACL_MANGLE_FIELD_IP_DSCP,
MLXSW_SP_ACL_MANGLE_FIELD_IP_ECN,
+ MLXSW_SP_ACL_MANGLE_FIELD_IP_SPORT,
+ MLXSW_SP_ACL_MANGLE_FIELD_IP_DPORT,
};
struct mlxsw_sp_acl_mangle_action {
@@ -538,13 +541,26 @@ struct mlxsw_sp_acl_mangle_action {
MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_IP6, \
_offset, _mask, _shift, _field)
+#define MLXSW_SP_ACL_MANGLE_ACTION_TCP(_offset, _mask, _shift, _field) \
+ MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_TCP, _offset, _mask, _shift, _field)
+
+#define MLXSW_SP_ACL_MANGLE_ACTION_UDP(_offset, _mask, _shift, _field) \
+ MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_UDP, _offset, _mask, _shift, _field)
+
static struct mlxsw_sp_acl_mangle_action mlxsw_sp_acl_mangle_actions[] = {
MLXSW_SP_ACL_MANGLE_ACTION_IP4(0, 0xff00ffff, 16, IP_DSFIELD),
MLXSW_SP_ACL_MANGLE_ACTION_IP4(0, 0xff03ffff, 18, IP_DSCP),
MLXSW_SP_ACL_MANGLE_ACTION_IP4(0, 0xfffcffff, 16, IP_ECN),
+
MLXSW_SP_ACL_MANGLE_ACTION_IP6(0, 0xf00fffff, 20, IP_DSFIELD),
MLXSW_SP_ACL_MANGLE_ACTION_IP6(0, 0xf03fffff, 22, IP_DSCP),
MLXSW_SP_ACL_MANGLE_ACTION_IP6(0, 0xffcfffff, 20, IP_ECN),
+
+ MLXSW_SP_ACL_MANGLE_ACTION_TCP(0, 0x0000ffff, 16, IP_SPORT),
+ MLXSW_SP_ACL_MANGLE_ACTION_TCP(0, 0xffff0000, 0, IP_DPORT),
+
+ MLXSW_SP_ACL_MANGLE_ACTION_UDP(0, 0x0000ffff, 16, IP_SPORT),
+ MLXSW_SP_ACL_MANGLE_ACTION_UDP(0, 0xffff0000, 0, IP_DPORT),
};
static int
@@ -563,11 +579,48 @@ mlxsw_sp_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp,
case MLXSW_SP_ACL_MANGLE_FIELD_IP_ECN:
return mlxsw_afa_block_append_qos_ecn(rulei->act_block,
val, extack);
+ default:
+ return -EOPNOTSUPP;
}
+}
- /* We shouldn't have gotten a match in the first place! */
- WARN_ONCE(1, "Unhandled mangle field");
- return -EINVAL;
+static int mlxsw_sp1_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_acl_rule_info *rulei,
+ struct mlxsw_sp_acl_mangle_action *mact,
+ u32 val, struct netlink_ext_ack *extack)
+{
+ int err;
+
+ err = mlxsw_sp_acl_rulei_act_mangle_field(mlxsw_sp, rulei, mact, val, extack);
+ if (err != -EOPNOTSUPP)
+ return err;
+
+ NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field");
+ return err;
+}
+
+static int mlxsw_sp2_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_acl_rule_info *rulei,
+ struct mlxsw_sp_acl_mangle_action *mact,
+ u32 val, struct netlink_ext_ack *extack)
+{
+ int err;
+
+ err = mlxsw_sp_acl_rulei_act_mangle_field(mlxsw_sp, rulei, mact, val, extack);
+ if (err != -EOPNOTSUPP)
+ return err;
+
+ switch (mact->field) {
+ case MLXSW_SP_ACL_MANGLE_FIELD_IP_SPORT:
+ return mlxsw_afa_block_append_l4port(rulei->act_block, false, val, extack);
+ case MLXSW_SP_ACL_MANGLE_FIELD_IP_DPORT:
+ return mlxsw_afa_block_append_l4port(rulei->act_block, true, val, extack);
+ default:
+ break;
+ }
+
+ NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field");
+ return err;
}
int mlxsw_sp_acl_rulei_act_mangle(struct mlxsw_sp *mlxsw_sp,
@@ -576,6 +629,7 @@ int mlxsw_sp_acl_rulei_act_mangle(struct mlxsw_sp *mlxsw_sp,
u32 offset, u32 mask, u32 val,
struct netlink_ext_ack *extack)
{
+ const struct mlxsw_sp_acl_rulei_ops *acl_rulei_ops = mlxsw_sp->acl_rulei_ops;
struct mlxsw_sp_acl_mangle_action *mact;
size_t i;
@@ -585,16 +639,34 @@ int mlxsw_sp_acl_rulei_act_mangle(struct mlxsw_sp *mlxsw_sp,
mact->offset == offset &&
mact->mask == mask) {
val >>= mact->shift;
- return mlxsw_sp_acl_rulei_act_mangle_field(mlxsw_sp,
- rulei, mact,
- val, extack);
+ return acl_rulei_ops->act_mangle_field(mlxsw_sp,
+ rulei, mact,
+ val, extack);
}
}
- NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field");
+ NL_SET_ERR_MSG_MOD(extack, "Unknown mangle field");
return -EINVAL;
}
+int mlxsw_sp_acl_rulei_act_police(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_acl_rule_info *rulei,
+ u32 index, u64 rate_bytes_ps,
+ u32 burst, struct netlink_ext_ack *extack)
+{
+ int err;
+
+ err = mlxsw_afa_block_append_police(rulei->act_block, index,
+ rate_bytes_ps, burst,
+ &rulei->policer_index, extack);
+ if (err)
+ return err;
+
+ rulei->policer_index_valid = true;
+
+ return 0;
+}
+
int mlxsw_sp_acl_rulei_act_count(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_rule_info *rulei,
struct netlink_ext_ack *extack)
@@ -815,13 +887,16 @@ static void mlxsw_sp_acl_rule_activity_update_work(struct work_struct *work)
int mlxsw_sp_acl_rule_get_stats(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_rule *rule,
- u64 *packets, u64 *bytes, u64 *last_use,
+ u64 *packets, u64 *bytes, u64 *drops,
+ u64 *last_use,
enum flow_action_hw_stats *used_hw_stats)
{
+ enum mlxsw_sp_policer_type type = MLXSW_SP_POLICER_TYPE_SINGLE_RATE;
struct mlxsw_sp_acl_rule_info *rulei;
u64 current_packets = 0;
u64 current_bytes = 0;
+ u64 current_drops = 0;
int err;
rulei = mlxsw_sp_acl_rule_rulei(rule);
@@ -833,12 +908,21 @@ int mlxsw_sp_acl_rule_get_stats(struct mlxsw_sp *mlxsw_sp,
return err;
*used_hw_stats = FLOW_ACTION_HW_STATS_IMMEDIATE;
}
+ if (rulei->policer_index_valid) {
+ err = mlxsw_sp_policer_drops_counter_get(mlxsw_sp, type,
+ rulei->policer_index,
+ &current_drops);
+ if (err)
+ return err;
+ }
*packets = current_packets - rule->last_packets;
*bytes = current_bytes - rule->last_bytes;
+ *drops = current_drops - rule->last_drops;
*last_use = rule->last_used;
rule->last_bytes = current_bytes;
rule->last_packets = current_packets;
+ rule->last_drops = current_drops;
return 0;
}
@@ -930,3 +1014,11 @@ int mlxsw_sp_acl_region_rehash_intrvl_set(struct mlxsw_sp *mlxsw_sp, u32 val)
return mlxsw_sp_acl_tcam_vregion_rehash_intrvl_set(mlxsw_sp,
&acl->tcam, val);
}
+
+struct mlxsw_sp_acl_rulei_ops mlxsw_sp1_acl_rulei_ops = {
+ .act_mangle_field = mlxsw_sp1_acl_rulei_act_mangle_field,
+};
+
+struct mlxsw_sp_acl_rulei_ops mlxsw_sp2_acl_rulei_ops = {
+ .act_mangle_field = mlxsw_sp2_acl_rulei_act_mangle_field,
+};
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.c
index 73d56012654b..90372d1c28d4 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.c
@@ -136,11 +136,13 @@ mlxsw_sp_act_mirror_add(void *priv, u8 local_in_port,
const struct net_device *out_dev,
bool ingress, int *p_span_id)
{
+ struct mlxsw_sp_span_agent_parms agent_parms = {};
struct mlxsw_sp_port *mlxsw_sp_port;
struct mlxsw_sp *mlxsw_sp = priv;
int err;
- err = mlxsw_sp_span_agent_get(mlxsw_sp, out_dev, p_span_id);
+ agent_parms.to_dev = out_dev;
+ err = mlxsw_sp_span_agent_get(mlxsw_sp, p_span_id, &agent_parms);
if (err)
return err;
@@ -167,6 +169,29 @@ mlxsw_sp_act_mirror_del(void *priv, u8 local_in_port, int span_id, bool ingress)
mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
}
+static int mlxsw_sp_act_policer_add(void *priv, u64 rate_bytes_ps, u32 burst,
+ u16 *p_policer_index,
+ struct netlink_ext_ack *extack)
+{
+ struct mlxsw_sp_policer_params params;
+ struct mlxsw_sp *mlxsw_sp = priv;
+
+ params.rate = rate_bytes_ps;
+ params.burst = burst;
+ params.bytes = true;
+ return mlxsw_sp_policer_add(mlxsw_sp,
+ MLXSW_SP_POLICER_TYPE_SINGLE_RATE,
+ &params, extack, p_policer_index);
+}
+
+static void mlxsw_sp_act_policer_del(void *priv, u16 policer_index)
+{
+ struct mlxsw_sp *mlxsw_sp = priv;
+
+ mlxsw_sp_policer_del(mlxsw_sp, MLXSW_SP_POLICER_TYPE_SINGLE_RATE,
+ policer_index);
+}
+
const struct mlxsw_afa_ops mlxsw_sp1_act_afa_ops = {
.kvdl_set_add = mlxsw_sp1_act_kvdl_set_add,
.kvdl_set_del = mlxsw_sp_act_kvdl_set_del,
@@ -177,6 +202,8 @@ const struct mlxsw_afa_ops mlxsw_sp1_act_afa_ops = {
.counter_index_put = mlxsw_sp_act_counter_index_put,
.mirror_add = mlxsw_sp_act_mirror_add,
.mirror_del = mlxsw_sp_act_mirror_del,
+ .policer_add = mlxsw_sp_act_policer_add,
+ .policer_del = mlxsw_sp_act_policer_del,
};
const struct mlxsw_afa_ops mlxsw_sp2_act_afa_ops = {
@@ -189,6 +216,8 @@ const struct mlxsw_afa_ops mlxsw_sp2_act_afa_ops = {
.counter_index_put = mlxsw_sp_act_counter_index_put,
.mirror_add = mlxsw_sp_act_mirror_add,
.mirror_del = mlxsw_sp_act_mirror_del,
+ .policer_add = mlxsw_sp_act_policer_add,
+ .policer_del = mlxsw_sp_act_policer_del,
.dummy_first_set = true,
};
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c
index 7974982533b5..b65b93a2b9bc 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c
@@ -121,7 +121,6 @@ int mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp)
{
unsigned int sub_pools_count = ARRAY_SIZE(mlxsw_sp_counter_sub_pools);
struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
- struct mlxsw_sp_counter_sub_pool *sub_pool;
struct mlxsw_sp_counter_pool *pool;
unsigned int map_size;
int err;
@@ -131,9 +130,9 @@ int mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp)
if (!pool)
return -ENOMEM;
mlxsw_sp->counter_pool = pool;
- memcpy(pool->sub_pools, mlxsw_sp_counter_sub_pools,
- sub_pools_count * sizeof(*sub_pool));
pool->sub_pools_count = sub_pools_count;
+ memcpy(pool->sub_pools, mlxsw_sp_counter_sub_pools,
+ flex_array_size(pool, sub_pools, pool->sub_pools_count));
spin_lock_init(&pool->counter_pool_lock);
atomic_set(&pool->active_entries_count, 0);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c
index 49a72a8f1f57..0d3fb2e51ea5 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c
@@ -110,8 +110,8 @@ static int mlxsw_sp_port_pg_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl);
}
-static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
- struct ieee_ets *ets)
+static int mlxsw_sp_port_headroom_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct ieee_ets *ets)
{
bool pause_en = mlxsw_sp_port_is_pause_en(mlxsw_sp_port);
struct ieee_ets *my_ets = mlxsw_sp_port->dcb.ets;
@@ -138,7 +138,7 @@ static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
err = mlxsw_sp_port_pg_destroy(mlxsw_sp_port, my_ets->prio_tc,
ets->prio_tc);
if (err)
- netdev_warn(dev, "Failed to remove ununsed PGs\n");
+ netdev_warn(dev, "Failed to remove unused PGs\n");
return 0;
@@ -180,7 +180,7 @@ static int __mlxsw_sp_dcbnl_ieee_setets(struct mlxsw_sp_port *mlxsw_sp_port,
}
/* Ingress configuration. */
- err = mlxsw_sp_port_headroom_set(mlxsw_sp_port, ets);
+ err = mlxsw_sp_port_headroom_ets_set(mlxsw_sp_port, ets);
if (err)
goto err_port_headroom_set;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c
new file mode 100644
index 000000000000..14c78f73bb65
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c
@@ -0,0 +1,1644 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/* Copyright (c) 2020 Mellanox Technologies. All rights reserved */
+
+#include "reg.h"
+#include "spectrum.h"
+#include "core_env.h"
+
+static const char mlxsw_sp_driver_version[] = "1.0";
+
+static void mlxsw_sp_port_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+
+ strlcpy(drvinfo->driver, mlxsw_sp->bus_info->device_kind,
+ sizeof(drvinfo->driver));
+ strlcpy(drvinfo->version, mlxsw_sp_driver_version,
+ sizeof(drvinfo->version));
+ snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
+ "%d.%d.%d",
+ mlxsw_sp->bus_info->fw_rev.major,
+ mlxsw_sp->bus_info->fw_rev.minor,
+ mlxsw_sp->bus_info->fw_rev.subminor);
+ strlcpy(drvinfo->bus_info, mlxsw_sp->bus_info->device_name,
+ sizeof(drvinfo->bus_info));
+}
+
+struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping {
+ u32 status_opcode;
+ enum ethtool_link_ext_state link_ext_state;
+ u8 link_ext_substate;
+};
+
+static const struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping
+mlxsw_sp_link_ext_state_opcode_map[] = {
+ {2, ETHTOOL_LINK_EXT_STATE_AUTONEG,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED},
+ {3, ETHTOOL_LINK_EXT_STATE_AUTONEG,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED},
+ {4, ETHTOOL_LINK_EXT_STATE_AUTONEG,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED},
+ {36, ETHTOOL_LINK_EXT_STATE_AUTONEG,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE},
+ {38, ETHTOOL_LINK_EXT_STATE_AUTONEG,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE},
+ {39, ETHTOOL_LINK_EXT_STATE_AUTONEG,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD},
+
+ {5, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE,
+ ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED},
+ {6, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE,
+ ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT},
+ {7, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE,
+ ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY},
+ {8, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE, 0},
+ {14, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE,
+ ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT},
+
+ {9, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK},
+ {10, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK},
+ {11, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS},
+ {12, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED},
+ {13, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED},
+
+ {15, ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY, 0},
+ {17, ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY,
+ ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS},
+ {42, ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY,
+ ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE},
+
+ {1024, ETHTOOL_LINK_EXT_STATE_NO_CABLE, 0},
+
+ {16, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
+ ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE},
+ {20, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
+ ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE},
+ {29, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
+ ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE},
+ {1025, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
+ ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE},
+ {1029, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
+ ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE},
+ {1031, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE, 0},
+
+ {1027, ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE, 0},
+
+ {23, ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE, 0},
+
+ {1032, ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED, 0},
+
+ {1030, ETHTOOL_LINK_EXT_STATE_OVERHEAT, 0},
+};
+
+static void
+mlxsw_sp_port_set_link_ext_state(struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping
+ link_ext_state_mapping,
+ struct ethtool_link_ext_state_info *link_ext_state_info)
+{
+ switch (link_ext_state_mapping.link_ext_state) {
+ case ETHTOOL_LINK_EXT_STATE_AUTONEG:
+ link_ext_state_info->autoneg =
+ link_ext_state_mapping.link_ext_substate;
+ break;
+ case ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE:
+ link_ext_state_info->link_training =
+ link_ext_state_mapping.link_ext_substate;
+ break;
+ case ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH:
+ link_ext_state_info->link_logical_mismatch =
+ link_ext_state_mapping.link_ext_substate;
+ break;
+ case ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY:
+ link_ext_state_info->bad_signal_integrity =
+ link_ext_state_mapping.link_ext_substate;
+ break;
+ case ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE:
+ link_ext_state_info->cable_issue =
+ link_ext_state_mapping.link_ext_substate;
+ break;
+ default:
+ break;
+ }
+
+ link_ext_state_info->link_ext_state = link_ext_state_mapping.link_ext_state;
+}
+
+static int
+mlxsw_sp_port_get_link_ext_state(struct net_device *dev,
+ struct ethtool_link_ext_state_info *link_ext_state_info)
+{
+ struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping link_ext_state_mapping;
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ char pddr_pl[MLXSW_REG_PDDR_LEN];
+ int opcode, err, i;
+ u32 status_opcode;
+
+ if (netif_carrier_ok(dev))
+ return -ENODATA;
+
+ mlxsw_reg_pddr_pack(pddr_pl, mlxsw_sp_port->local_port,
+ MLXSW_REG_PDDR_PAGE_SELECT_TROUBLESHOOTING_INFO);
+
+ opcode = MLXSW_REG_PDDR_TRBLSH_GROUP_OPCODE_MONITOR;
+ mlxsw_reg_pddr_trblsh_group_opcode_set(pddr_pl, opcode);
+
+ err = mlxsw_reg_query(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pddr),
+ pddr_pl);
+ if (err)
+ return err;
+
+ status_opcode = mlxsw_reg_pddr_trblsh_status_opcode_get(pddr_pl);
+ if (!status_opcode)
+ return -ENODATA;
+
+ for (i = 0; i < ARRAY_SIZE(mlxsw_sp_link_ext_state_opcode_map); i++) {
+ link_ext_state_mapping = mlxsw_sp_link_ext_state_opcode_map[i];
+ if (link_ext_state_mapping.status_opcode == status_opcode) {
+ mlxsw_sp_port_set_link_ext_state(link_ext_state_mapping,
+ link_ext_state_info);
+ return 0;
+ }
+ }
+
+ return -ENODATA;
+}
+
+static void mlxsw_sp_port_get_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pause)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+
+ pause->rx_pause = mlxsw_sp_port->link.rx_pause;
+ pause->tx_pause = mlxsw_sp_port->link.tx_pause;
+}
+
+static int mlxsw_sp_port_pause_set(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct ethtool_pauseparam *pause)
+{
+ char pfcc_pl[MLXSW_REG_PFCC_LEN];
+
+ mlxsw_reg_pfcc_pack(pfcc_pl, mlxsw_sp_port->local_port);
+ mlxsw_reg_pfcc_pprx_set(pfcc_pl, pause->rx_pause);
+ mlxsw_reg_pfcc_pptx_set(pfcc_pl, pause->tx_pause);
+
+ return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pfcc),
+ pfcc_pl);
+}
+
+static int mlxsw_sp_port_set_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pause)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ bool pause_en = pause->tx_pause || pause->rx_pause;
+ int err;
+
+ if (mlxsw_sp_port->dcb.pfc && mlxsw_sp_port->dcb.pfc->pfc_en) {
+ netdev_err(dev, "PFC already enabled on port\n");
+ return -EINVAL;
+ }
+
+ if (pause->autoneg) {
+ netdev_err(dev, "PAUSE frames autonegotiation isn't supported\n");
+ return -EINVAL;
+ }
+
+ err = mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, pause_en);
+ if (err) {
+ netdev_err(dev, "Failed to configure port's headroom\n");
+ return err;
+ }
+
+ err = mlxsw_sp_port_pause_set(mlxsw_sp_port, pause);
+ if (err) {
+ netdev_err(dev, "Failed to set PAUSE parameters\n");
+ goto err_port_pause_configure;
+ }
+
+ mlxsw_sp_port->link.rx_pause = pause->rx_pause;
+ mlxsw_sp_port->link.tx_pause = pause->tx_pause;
+
+ return 0;
+
+err_port_pause_configure:
+ pause_en = mlxsw_sp_port_is_pause_en(mlxsw_sp_port);
+ mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, pause_en);
+ return err;
+}
+
+struct mlxsw_sp_port_hw_stats {
+ char str[ETH_GSTRING_LEN];
+ u64 (*getter)(const char *payload);
+ bool cells_bytes;
+};
+
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = {
+ {
+ .str = "a_frames_transmitted_ok",
+ .getter = mlxsw_reg_ppcnt_a_frames_transmitted_ok_get,
+ },
+ {
+ .str = "a_frames_received_ok",
+ .getter = mlxsw_reg_ppcnt_a_frames_received_ok_get,
+ },
+ {
+ .str = "a_frame_check_sequence_errors",
+ .getter = mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get,
+ },
+ {
+ .str = "a_alignment_errors",
+ .getter = mlxsw_reg_ppcnt_a_alignment_errors_get,
+ },
+ {
+ .str = "a_octets_transmitted_ok",
+ .getter = mlxsw_reg_ppcnt_a_octets_transmitted_ok_get,
+ },
+ {
+ .str = "a_octets_received_ok",
+ .getter = mlxsw_reg_ppcnt_a_octets_received_ok_get,
+ },
+ {
+ .str = "a_multicast_frames_xmitted_ok",
+ .getter = mlxsw_reg_ppcnt_a_multicast_frames_xmitted_ok_get,
+ },
+ {
+ .str = "a_broadcast_frames_xmitted_ok",
+ .getter = mlxsw_reg_ppcnt_a_broadcast_frames_xmitted_ok_get,
+ },
+ {
+ .str = "a_multicast_frames_received_ok",
+ .getter = mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get,
+ },
+ {
+ .str = "a_broadcast_frames_received_ok",
+ .getter = mlxsw_reg_ppcnt_a_broadcast_frames_received_ok_get,
+ },
+ {
+ .str = "a_in_range_length_errors",
+ .getter = mlxsw_reg_ppcnt_a_in_range_length_errors_get,
+ },
+ {
+ .str = "a_out_of_range_length_field",
+ .getter = mlxsw_reg_ppcnt_a_out_of_range_length_field_get,
+ },
+ {
+ .str = "a_frame_too_long_errors",
+ .getter = mlxsw_reg_ppcnt_a_frame_too_long_errors_get,
+ },
+ {
+ .str = "a_symbol_error_during_carrier",
+ .getter = mlxsw_reg_ppcnt_a_symbol_error_during_carrier_get,
+ },
+ {
+ .str = "a_mac_control_frames_transmitted",
+ .getter = mlxsw_reg_ppcnt_a_mac_control_frames_transmitted_get,
+ },
+ {
+ .str = "a_mac_control_frames_received",
+ .getter = mlxsw_reg_ppcnt_a_mac_control_frames_received_get,
+ },
+ {
+ .str = "a_unsupported_opcodes_received",
+ .getter = mlxsw_reg_ppcnt_a_unsupported_opcodes_received_get,
+ },
+ {
+ .str = "a_pause_mac_ctrl_frames_received",
+ .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_received_get,
+ },
+ {
+ .str = "a_pause_mac_ctrl_frames_xmitted",
+ .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_transmitted_get,
+ },
+};
+
+#define MLXSW_SP_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_stats)
+
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2863_stats[] = {
+ {
+ .str = "if_in_discards",
+ .getter = mlxsw_reg_ppcnt_if_in_discards_get,
+ },
+ {
+ .str = "if_out_discards",
+ .getter = mlxsw_reg_ppcnt_if_out_discards_get,
+ },
+ {
+ .str = "if_out_errors",
+ .getter = mlxsw_reg_ppcnt_if_out_errors_get,
+ },
+};
+
+#define MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN \
+ ARRAY_SIZE(mlxsw_sp_port_hw_rfc_2863_stats)
+
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2819_stats[] = {
+ {
+ .str = "ether_stats_undersize_pkts",
+ .getter = mlxsw_reg_ppcnt_ether_stats_undersize_pkts_get,
+ },
+ {
+ .str = "ether_stats_oversize_pkts",
+ .getter = mlxsw_reg_ppcnt_ether_stats_oversize_pkts_get,
+ },
+ {
+ .str = "ether_stats_fragments",
+ .getter = mlxsw_reg_ppcnt_ether_stats_fragments_get,
+ },
+ {
+ .str = "ether_pkts64octets",
+ .getter = mlxsw_reg_ppcnt_ether_stats_pkts64octets_get,
+ },
+ {
+ .str = "ether_pkts65to127octets",
+ .getter = mlxsw_reg_ppcnt_ether_stats_pkts65to127octets_get,
+ },
+ {
+ .str = "ether_pkts128to255octets",
+ .getter = mlxsw_reg_ppcnt_ether_stats_pkts128to255octets_get,
+ },
+ {
+ .str = "ether_pkts256to511octets",
+ .getter = mlxsw_reg_ppcnt_ether_stats_pkts256to511octets_get,
+ },
+ {
+ .str = "ether_pkts512to1023octets",
+ .getter = mlxsw_reg_ppcnt_ether_stats_pkts512to1023octets_get,
+ },
+ {
+ .str = "ether_pkts1024to1518octets",
+ .getter = mlxsw_reg_ppcnt_ether_stats_pkts1024to1518octets_get,
+ },
+ {
+ .str = "ether_pkts1519to2047octets",
+ .getter = mlxsw_reg_ppcnt_ether_stats_pkts1519to2047octets_get,
+ },
+ {
+ .str = "ether_pkts2048to4095octets",
+ .getter = mlxsw_reg_ppcnt_ether_stats_pkts2048to4095octets_get,
+ },
+ {
+ .str = "ether_pkts4096to8191octets",
+ .getter = mlxsw_reg_ppcnt_ether_stats_pkts4096to8191octets_get,
+ },
+ {
+ .str = "ether_pkts8192to10239octets",
+ .getter = mlxsw_reg_ppcnt_ether_stats_pkts8192to10239octets_get,
+ },
+};
+
+#define MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN \
+ ARRAY_SIZE(mlxsw_sp_port_hw_rfc_2819_stats)
+
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_3635_stats[] = {
+ {
+ .str = "dot3stats_fcs_errors",
+ .getter = mlxsw_reg_ppcnt_dot3stats_fcs_errors_get,
+ },
+ {
+ .str = "dot3stats_symbol_errors",
+ .getter = mlxsw_reg_ppcnt_dot3stats_symbol_errors_get,
+ },
+ {
+ .str = "dot3control_in_unknown_opcodes",
+ .getter = mlxsw_reg_ppcnt_dot3control_in_unknown_opcodes_get,
+ },
+ {
+ .str = "dot3in_pause_frames",
+ .getter = mlxsw_reg_ppcnt_dot3in_pause_frames_get,
+ },
+};
+
+#define MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN \
+ ARRAY_SIZE(mlxsw_sp_port_hw_rfc_3635_stats)
+
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_ext_stats[] = {
+ {
+ .str = "ecn_marked",
+ .getter = mlxsw_reg_ppcnt_ecn_marked_get,
+ },
+};
+
+#define MLXSW_SP_PORT_HW_EXT_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_ext_stats)
+
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_discard_stats[] = {
+ {
+ .str = "discard_ingress_general",
+ .getter = mlxsw_reg_ppcnt_ingress_general_get,
+ },
+ {
+ .str = "discard_ingress_policy_engine",
+ .getter = mlxsw_reg_ppcnt_ingress_policy_engine_get,
+ },
+ {
+ .str = "discard_ingress_vlan_membership",
+ .getter = mlxsw_reg_ppcnt_ingress_vlan_membership_get,
+ },
+ {
+ .str = "discard_ingress_tag_frame_type",
+ .getter = mlxsw_reg_ppcnt_ingress_tag_frame_type_get,
+ },
+ {
+ .str = "discard_egress_vlan_membership",
+ .getter = mlxsw_reg_ppcnt_egress_vlan_membership_get,
+ },
+ {
+ .str = "discard_loopback_filter",
+ .getter = mlxsw_reg_ppcnt_loopback_filter_get,
+ },
+ {
+ .str = "discard_egress_general",
+ .getter = mlxsw_reg_ppcnt_egress_general_get,
+ },
+ {
+ .str = "discard_egress_hoq",
+ .getter = mlxsw_reg_ppcnt_egress_hoq_get,
+ },
+ {
+ .str = "discard_egress_policy_engine",
+ .getter = mlxsw_reg_ppcnt_egress_policy_engine_get,
+ },
+ {
+ .str = "discard_ingress_tx_link_down",
+ .getter = mlxsw_reg_ppcnt_ingress_tx_link_down_get,
+ },
+ {
+ .str = "discard_egress_stp_filter",
+ .getter = mlxsw_reg_ppcnt_egress_stp_filter_get,
+ },
+ {
+ .str = "discard_egress_sll",
+ .getter = mlxsw_reg_ppcnt_egress_sll_get,
+ },
+};
+
+#define MLXSW_SP_PORT_HW_DISCARD_STATS_LEN \
+ ARRAY_SIZE(mlxsw_sp_port_hw_discard_stats)
+
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_prio_stats[] = {
+ {
+ .str = "rx_octets_prio",
+ .getter = mlxsw_reg_ppcnt_rx_octets_get,
+ },
+ {
+ .str = "rx_frames_prio",
+ .getter = mlxsw_reg_ppcnt_rx_frames_get,
+ },
+ {
+ .str = "tx_octets_prio",
+ .getter = mlxsw_reg_ppcnt_tx_octets_get,
+ },
+ {
+ .str = "tx_frames_prio",
+ .getter = mlxsw_reg_ppcnt_tx_frames_get,
+ },
+ {
+ .str = "rx_pause_prio",
+ .getter = mlxsw_reg_ppcnt_rx_pause_get,
+ },
+ {
+ .str = "rx_pause_duration_prio",
+ .getter = mlxsw_reg_ppcnt_rx_pause_duration_get,
+ },
+ {
+ .str = "tx_pause_prio",
+ .getter = mlxsw_reg_ppcnt_tx_pause_get,
+ },
+ {
+ .str = "tx_pause_duration_prio",
+ .getter = mlxsw_reg_ppcnt_tx_pause_duration_get,
+ },
+};
+
+#define MLXSW_SP_PORT_HW_PRIO_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_prio_stats)
+
+static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_tc_stats[] = {
+ {
+ .str = "tc_transmit_queue_tc",
+ .getter = mlxsw_reg_ppcnt_tc_transmit_queue_get,
+ .cells_bytes = true,
+ },
+ {
+ .str = "tc_no_buffer_discard_uc_tc",
+ .getter = mlxsw_reg_ppcnt_tc_no_buffer_discard_uc_get,
+ },
+};
+
+#define MLXSW_SP_PORT_HW_TC_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_tc_stats)
+
+#define MLXSW_SP_PORT_ETHTOOL_STATS_LEN (MLXSW_SP_PORT_HW_STATS_LEN + \
+ MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN + \
+ MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN + \
+ MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN + \
+ MLXSW_SP_PORT_HW_EXT_STATS_LEN + \
+ MLXSW_SP_PORT_HW_DISCARD_STATS_LEN + \
+ (MLXSW_SP_PORT_HW_PRIO_STATS_LEN * \
+ IEEE_8021QAZ_MAX_TCS) + \
+ (MLXSW_SP_PORT_HW_TC_STATS_LEN * \
+ TC_MAX_QUEUE))
+
+static void mlxsw_sp_port_get_prio_strings(u8 **p, int prio)
+{
+ int i;
+
+ for (i = 0; i < MLXSW_SP_PORT_HW_PRIO_STATS_LEN; i++) {
+ snprintf(*p, ETH_GSTRING_LEN, "%.29s_%.1d",
+ mlxsw_sp_port_hw_prio_stats[i].str, prio);
+ *p += ETH_GSTRING_LEN;
+ }
+}
+
+static void mlxsw_sp_port_get_tc_strings(u8 **p, int tc)
+{
+ int i;
+
+ for (i = 0; i < MLXSW_SP_PORT_HW_TC_STATS_LEN; i++) {
+ snprintf(*p, ETH_GSTRING_LEN, "%.29s_%.1d",
+ mlxsw_sp_port_hw_tc_stats[i].str, tc);
+ *p += ETH_GSTRING_LEN;
+ }
+}
+
+static void mlxsw_sp_port_get_strings(struct net_device *dev,
+ u32 stringset, u8 *data)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ u8 *p = data;
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < MLXSW_SP_PORT_HW_STATS_LEN; i++) {
+ memcpy(p, mlxsw_sp_port_hw_stats[i].str,
+ ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+
+ for (i = 0; i < MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN; i++) {
+ memcpy(p, mlxsw_sp_port_hw_rfc_2863_stats[i].str,
+ ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+
+ for (i = 0; i < MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN; i++) {
+ memcpy(p, mlxsw_sp_port_hw_rfc_2819_stats[i].str,
+ ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+
+ for (i = 0; i < MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN; i++) {
+ memcpy(p, mlxsw_sp_port_hw_rfc_3635_stats[i].str,
+ ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+
+ for (i = 0; i < MLXSW_SP_PORT_HW_EXT_STATS_LEN; i++) {
+ memcpy(p, mlxsw_sp_port_hw_ext_stats[i].str,
+ ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+
+ for (i = 0; i < MLXSW_SP_PORT_HW_DISCARD_STATS_LEN; i++) {
+ memcpy(p, mlxsw_sp_port_hw_discard_stats[i].str,
+ ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
+ mlxsw_sp_port_get_prio_strings(&p, i);
+
+ for (i = 0; i < TC_MAX_QUEUE; i++)
+ mlxsw_sp_port_get_tc_strings(&p, i);
+
+ mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats_strings(&p);
+ break;
+ }
+}
+
+static int mlxsw_sp_port_set_phys_id(struct net_device *dev,
+ enum ethtool_phys_id_state state)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char mlcr_pl[MLXSW_REG_MLCR_LEN];
+ bool active;
+
+ switch (state) {
+ case ETHTOOL_ID_ACTIVE:
+ active = true;
+ break;
+ case ETHTOOL_ID_INACTIVE:
+ active = false;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ mlxsw_reg_mlcr_pack(mlcr_pl, mlxsw_sp_port->local_port, active);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mlcr), mlcr_pl);
+}
+
+static int
+mlxsw_sp_get_hw_stats_by_group(struct mlxsw_sp_port_hw_stats **p_hw_stats,
+ int *p_len, enum mlxsw_reg_ppcnt_grp grp)
+{
+ switch (grp) {
+ case MLXSW_REG_PPCNT_IEEE_8023_CNT:
+ *p_hw_stats = mlxsw_sp_port_hw_stats;
+ *p_len = MLXSW_SP_PORT_HW_STATS_LEN;
+ break;
+ case MLXSW_REG_PPCNT_RFC_2863_CNT:
+ *p_hw_stats = mlxsw_sp_port_hw_rfc_2863_stats;
+ *p_len = MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN;
+ break;
+ case MLXSW_REG_PPCNT_RFC_2819_CNT:
+ *p_hw_stats = mlxsw_sp_port_hw_rfc_2819_stats;
+ *p_len = MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN;
+ break;
+ case MLXSW_REG_PPCNT_RFC_3635_CNT:
+ *p_hw_stats = mlxsw_sp_port_hw_rfc_3635_stats;
+ *p_len = MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN;
+ break;
+ case MLXSW_REG_PPCNT_EXT_CNT:
+ *p_hw_stats = mlxsw_sp_port_hw_ext_stats;
+ *p_len = MLXSW_SP_PORT_HW_EXT_STATS_LEN;
+ break;
+ case MLXSW_REG_PPCNT_DISCARD_CNT:
+ *p_hw_stats = mlxsw_sp_port_hw_discard_stats;
+ *p_len = MLXSW_SP_PORT_HW_DISCARD_STATS_LEN;
+ break;
+ case MLXSW_REG_PPCNT_PRIO_CNT:
+ *p_hw_stats = mlxsw_sp_port_hw_prio_stats;
+ *p_len = MLXSW_SP_PORT_HW_PRIO_STATS_LEN;
+ break;
+ case MLXSW_REG_PPCNT_TC_CNT:
+ *p_hw_stats = mlxsw_sp_port_hw_tc_stats;
+ *p_len = MLXSW_SP_PORT_HW_TC_STATS_LEN;
+ break;
+ default:
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static void __mlxsw_sp_port_get_stats(struct net_device *dev,
+ enum mlxsw_reg_ppcnt_grp grp, int prio,
+ u64 *data, int data_index)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct mlxsw_sp_port_hw_stats *hw_stats;
+ char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
+ int i, len;
+ int err;
+
+ err = mlxsw_sp_get_hw_stats_by_group(&hw_stats, &len, grp);
+ if (err)
+ return;
+ mlxsw_sp_port_get_stats_raw(dev, grp, prio, ppcnt_pl);
+ for (i = 0; i < len; i++) {
+ data[data_index + i] = hw_stats[i].getter(ppcnt_pl);
+ if (!hw_stats[i].cells_bytes)
+ continue;
+ data[data_index + i] = mlxsw_sp_cells_bytes(mlxsw_sp,
+ data[data_index + i]);
+ }
+}
+
+static void mlxsw_sp_port_get_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ int i, data_index = 0;
+
+ /* IEEE 802.3 Counters */
+ __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT, 0,
+ data, data_index);
+ data_index = MLXSW_SP_PORT_HW_STATS_LEN;
+
+ /* RFC 2863 Counters */
+ __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_2863_CNT, 0,
+ data, data_index);
+ data_index += MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN;
+
+ /* RFC 2819 Counters */
+ __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_2819_CNT, 0,
+ data, data_index);
+ data_index += MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN;
+
+ /* RFC 3635 Counters */
+ __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_3635_CNT, 0,
+ data, data_index);
+ data_index += MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN;
+
+ /* Extended Counters */
+ __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_EXT_CNT, 0,
+ data, data_index);
+ data_index += MLXSW_SP_PORT_HW_EXT_STATS_LEN;
+
+ /* Discard Counters */
+ __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_DISCARD_CNT, 0,
+ data, data_index);
+ data_index += MLXSW_SP_PORT_HW_DISCARD_STATS_LEN;
+
+ /* Per-Priority Counters */
+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
+ __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_PRIO_CNT, i,
+ data, data_index);
+ data_index += MLXSW_SP_PORT_HW_PRIO_STATS_LEN;
+ }
+
+ /* Per-TC Counters */
+ for (i = 0; i < TC_MAX_QUEUE; i++) {
+ __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_TC_CNT, i,
+ data, data_index);
+ data_index += MLXSW_SP_PORT_HW_TC_STATS_LEN;
+ }
+
+ /* PTP counters */
+ mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats(mlxsw_sp_port,
+ data, data_index);
+ data_index += mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats_count();
+}
+
+static int mlxsw_sp_port_get_sset_count(struct net_device *dev, int sset)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+
+ switch (sset) {
+ case ETH_SS_STATS:
+ return MLXSW_SP_PORT_ETHTOOL_STATS_LEN +
+ mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats_count();
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void
+mlxsw_sp_port_get_link_supported(struct mlxsw_sp *mlxsw_sp, u32 eth_proto_cap,
+ u8 width, struct ethtool_link_ksettings *cmd)
+{
+ const struct mlxsw_sp_port_type_speed_ops *ops;
+
+ ops = mlxsw_sp->port_type_speed_ops;
+
+ ethtool_link_ksettings_add_link_mode(cmd, supported, Asym_Pause);
+ ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
+ ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);
+
+ ops->from_ptys_supported_port(mlxsw_sp, eth_proto_cap, cmd);
+ ops->from_ptys_link(mlxsw_sp, eth_proto_cap, width,
+ cmd->link_modes.supported);
+}
+
+static void
+mlxsw_sp_port_get_link_advertise(struct mlxsw_sp *mlxsw_sp,
+ u32 eth_proto_admin, bool autoneg, u8 width,
+ struct ethtool_link_ksettings *cmd)
+{
+ const struct mlxsw_sp_port_type_speed_ops *ops;
+
+ ops = mlxsw_sp->port_type_speed_ops;
+
+ if (!autoneg)
+ return;
+
+ ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
+ ops->from_ptys_link(mlxsw_sp, eth_proto_admin, width,
+ cmd->link_modes.advertising);
+}
+
+static u8
+mlxsw_sp_port_connector_port(enum mlxsw_reg_ptys_connector_type connector_type)
+{
+ switch (connector_type) {
+ case MLXSW_REG_PTYS_CONNECTOR_TYPE_UNKNOWN_OR_NO_CONNECTOR:
+ return PORT_OTHER;
+ case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_NONE:
+ return PORT_NONE;
+ case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_TP:
+ return PORT_TP;
+ case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_AUI:
+ return PORT_AUI;
+ case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_BNC:
+ return PORT_BNC;
+ case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_MII:
+ return PORT_MII;
+ case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_FIBRE:
+ return PORT_FIBRE;
+ case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_DA:
+ return PORT_DA;
+ case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_OTHER:
+ return PORT_OTHER;
+ default:
+ WARN_ON_ONCE(1);
+ return PORT_OTHER;
+ }
+}
+
+static int mlxsw_sp_port_get_link_ksettings(struct net_device *dev,
+ struct ethtool_link_ksettings *cmd)
+{
+ u32 eth_proto_cap, eth_proto_admin, eth_proto_oper;
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ const struct mlxsw_sp_port_type_speed_ops *ops;
+ char ptys_pl[MLXSW_REG_PTYS_LEN];
+ u8 connector_type;
+ bool autoneg;
+ int err;
+
+ ops = mlxsw_sp->port_type_speed_ops;
+
+ autoneg = mlxsw_sp_port->link.autoneg;
+ ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
+ 0, false);
+ err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
+ if (err)
+ return err;
+ ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, &eth_proto_cap,
+ &eth_proto_admin, &eth_proto_oper);
+
+ mlxsw_sp_port_get_link_supported(mlxsw_sp, eth_proto_cap,
+ mlxsw_sp_port->mapping.width, cmd);
+
+ mlxsw_sp_port_get_link_advertise(mlxsw_sp, eth_proto_admin, autoneg,
+ mlxsw_sp_port->mapping.width, cmd);
+
+ cmd->base.autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
+ connector_type = mlxsw_reg_ptys_connector_type_get(ptys_pl);
+ cmd->base.port = mlxsw_sp_port_connector_port(connector_type);
+ ops->from_ptys_speed_duplex(mlxsw_sp, netif_carrier_ok(dev),
+ eth_proto_oper, cmd);
+
+ return 0;
+}
+
+static int
+mlxsw_sp_port_set_link_ksettings(struct net_device *dev,
+ const struct ethtool_link_ksettings *cmd)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ const struct mlxsw_sp_port_type_speed_ops *ops;
+ char ptys_pl[MLXSW_REG_PTYS_LEN];
+ u32 eth_proto_cap, eth_proto_new;
+ bool autoneg;
+ int err;
+
+ ops = mlxsw_sp->port_type_speed_ops;
+
+ ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
+ 0, false);
+ err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
+ if (err)
+ return err;
+ ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, &eth_proto_cap, NULL, NULL);
+
+ autoneg = cmd->base.autoneg == AUTONEG_ENABLE;
+ eth_proto_new = autoneg ?
+ ops->to_ptys_advert_link(mlxsw_sp, mlxsw_sp_port->mapping.width,
+ cmd) :
+ ops->to_ptys_speed(mlxsw_sp, mlxsw_sp_port->mapping.width,
+ cmd->base.speed);
+
+ eth_proto_new = eth_proto_new & eth_proto_cap;
+ if (!eth_proto_new) {
+ netdev_err(dev, "No supported speed requested\n");
+ return -EINVAL;
+ }
+
+ ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
+ eth_proto_new, autoneg);
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
+ if (err)
+ return err;
+
+ mlxsw_sp_port->link.autoneg = autoneg;
+
+ if (!netif_running(dev))
+ return 0;
+
+ mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
+ mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true);
+
+ return 0;
+}
+
+static int mlxsw_sp_get_module_info(struct net_device *netdev,
+ struct ethtool_modinfo *modinfo)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ int err;
+
+ err = mlxsw_env_get_module_info(mlxsw_sp->core,
+ mlxsw_sp_port->mapping.module,
+ modinfo);
+
+ return err;
+}
+
+static int mlxsw_sp_get_module_eeprom(struct net_device *netdev,
+ struct ethtool_eeprom *ee, u8 *data)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ int err;
+
+ err = mlxsw_env_get_module_eeprom(netdev, mlxsw_sp->core,
+ mlxsw_sp_port->mapping.module, ee,
+ data);
+
+ return err;
+}
+
+static int
+mlxsw_sp_get_ts_info(struct net_device *netdev, struct ethtool_ts_info *info)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+
+ return mlxsw_sp->ptp_ops->get_ts_info(mlxsw_sp, info);
+}
+
+const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
+ .get_drvinfo = mlxsw_sp_port_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_link_ext_state = mlxsw_sp_port_get_link_ext_state,
+ .get_pauseparam = mlxsw_sp_port_get_pauseparam,
+ .set_pauseparam = mlxsw_sp_port_set_pauseparam,
+ .get_strings = mlxsw_sp_port_get_strings,
+ .set_phys_id = mlxsw_sp_port_set_phys_id,
+ .get_ethtool_stats = mlxsw_sp_port_get_stats,
+ .get_sset_count = mlxsw_sp_port_get_sset_count,
+ .get_link_ksettings = mlxsw_sp_port_get_link_ksettings,
+ .set_link_ksettings = mlxsw_sp_port_set_link_ksettings,
+ .get_module_info = mlxsw_sp_get_module_info,
+ .get_module_eeprom = mlxsw_sp_get_module_eeprom,
+ .get_ts_info = mlxsw_sp_get_ts_info,
+};
+
+struct mlxsw_sp1_port_link_mode {
+ enum ethtool_link_mode_bit_indices mask_ethtool;
+ u32 mask;
+ u32 speed;
+};
+
+static const struct mlxsw_sp1_port_link_mode mlxsw_sp1_port_link_mode[] = {
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_100BASE_T,
+ .mask_ethtool = ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+ .speed = SPEED_100,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_SGMII |
+ MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX,
+ .mask_ethtool = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+ .speed = SPEED_1000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_T,
+ .mask_ethtool = ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+ .speed = SPEED_10000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CX4 |
+ MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4,
+ .mask_ethtool = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+ .speed = SPEED_10000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
+ MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
+ MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
+ MLXSW_REG_PTYS_ETH_SPEED_10GBASE_ER_LR,
+ .mask_ethtool = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+ .speed = SPEED_10000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_20GBASE_KR2,
+ .mask_ethtool = ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
+ .speed = SPEED_20000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4,
+ .mask_ethtool = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
+ .speed = SPEED_40000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4,
+ .mask_ethtool = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
+ .speed = SPEED_40000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4,
+ .mask_ethtool = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
+ .speed = SPEED_40000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_LR4_ER4,
+ .mask_ethtool = ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
+ .speed = SPEED_40000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_CR,
+ .mask_ethtool = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
+ .speed = SPEED_25000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_KR,
+ .mask_ethtool = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
+ .speed = SPEED_25000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_SR,
+ .mask_ethtool = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
+ .speed = SPEED_25000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_CR2,
+ .mask_ethtool = ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
+ .speed = SPEED_50000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR2,
+ .mask_ethtool = ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
+ .speed = SPEED_50000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_SR2,
+ .mask_ethtool = ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
+ .speed = SPEED_50000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4,
+ .mask_ethtool = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
+ .speed = SPEED_100000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4,
+ .mask_ethtool = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
+ .speed = SPEED_100000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4,
+ .mask_ethtool = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
+ .speed = SPEED_100000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_LR4_ER4,
+ .mask_ethtool = ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
+ .speed = SPEED_100000,
+ },
+};
+
+#define MLXSW_SP1_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sp1_port_link_mode)
+
+static void
+mlxsw_sp1_from_ptys_supported_port(struct mlxsw_sp *mlxsw_sp,
+ u32 ptys_eth_proto,
+ struct ethtool_link_ksettings *cmd)
+{
+ if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
+ MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
+ MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 |
+ MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 |
+ MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
+ MLXSW_REG_PTYS_ETH_SPEED_SGMII))
+ ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
+
+ if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
+ MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 |
+ MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 |
+ MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 |
+ MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX))
+ ethtool_link_ksettings_add_link_mode(cmd, supported, Backplane);
+}
+
+static void
+mlxsw_sp1_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
+ u8 width, unsigned long *mode)
+{
+ int i;
+
+ for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
+ if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask)
+ __set_bit(mlxsw_sp1_port_link_mode[i].mask_ethtool,
+ mode);
+ }
+}
+
+static u32
+mlxsw_sp1_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto)
+{
+ int i;
+
+ for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
+ if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask)
+ return mlxsw_sp1_port_link_mode[i].speed;
+ }
+
+ return SPEED_UNKNOWN;
+}
+
+static void
+mlxsw_sp1_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
+ u32 ptys_eth_proto,
+ struct ethtool_link_ksettings *cmd)
+{
+ cmd->base.speed = SPEED_UNKNOWN;
+ cmd->base.duplex = DUPLEX_UNKNOWN;
+
+ if (!carrier_ok)
+ return;
+
+ cmd->base.speed = mlxsw_sp1_from_ptys_speed(mlxsw_sp, ptys_eth_proto);
+ if (cmd->base.speed != SPEED_UNKNOWN)
+ cmd->base.duplex = DUPLEX_FULL;
+}
+
+static u32
+mlxsw_sp1_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp, u8 width,
+ const struct ethtool_link_ksettings *cmd)
+{
+ u32 ptys_proto = 0;
+ int i;
+
+ for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
+ if (test_bit(mlxsw_sp1_port_link_mode[i].mask_ethtool,
+ cmd->link_modes.advertising))
+ ptys_proto |= mlxsw_sp1_port_link_mode[i].mask;
+ }
+ return ptys_proto;
+}
+
+static u32 mlxsw_sp1_to_ptys_speed(struct mlxsw_sp *mlxsw_sp, u8 width,
+ u32 speed)
+{
+ u32 ptys_proto = 0;
+ int i;
+
+ for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
+ if (speed == mlxsw_sp1_port_link_mode[i].speed)
+ ptys_proto |= mlxsw_sp1_port_link_mode[i].mask;
+ }
+ return ptys_proto;
+}
+
+static void
+mlxsw_sp1_reg_ptys_eth_pack(struct mlxsw_sp *mlxsw_sp, char *payload,
+ u8 local_port, u32 proto_admin, bool autoneg)
+{
+ mlxsw_reg_ptys_eth_pack(payload, local_port, proto_admin, autoneg);
+}
+
+static void
+mlxsw_sp1_reg_ptys_eth_unpack(struct mlxsw_sp *mlxsw_sp, char *payload,
+ u32 *p_eth_proto_cap, u32 *p_eth_proto_admin,
+ u32 *p_eth_proto_oper)
+{
+ mlxsw_reg_ptys_eth_unpack(payload, p_eth_proto_cap, p_eth_proto_admin,
+ p_eth_proto_oper);
+}
+
+const struct mlxsw_sp_port_type_speed_ops mlxsw_sp1_port_type_speed_ops = {
+ .from_ptys_supported_port = mlxsw_sp1_from_ptys_supported_port,
+ .from_ptys_link = mlxsw_sp1_from_ptys_link,
+ .from_ptys_speed = mlxsw_sp1_from_ptys_speed,
+ .from_ptys_speed_duplex = mlxsw_sp1_from_ptys_speed_duplex,
+ .to_ptys_advert_link = mlxsw_sp1_to_ptys_advert_link,
+ .to_ptys_speed = mlxsw_sp1_to_ptys_speed,
+ .reg_ptys_eth_pack = mlxsw_sp1_reg_ptys_eth_pack,
+ .reg_ptys_eth_unpack = mlxsw_sp1_reg_ptys_eth_unpack,
+};
+
+static const enum ethtool_link_mode_bit_indices
+mlxsw_sp2_mask_ethtool_sgmii_100m[] = {
+ ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+};
+
+#define MLXSW_SP2_MASK_ETHTOOL_SGMII_100M_LEN \
+ ARRAY_SIZE(mlxsw_sp2_mask_ethtool_sgmii_100m)
+
+static const enum ethtool_link_mode_bit_indices
+mlxsw_sp2_mask_ethtool_1000base_x_sgmii[] = {
+ ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+};
+
+#define MLXSW_SP2_MASK_ETHTOOL_1000BASE_X_SGMII_LEN \
+ ARRAY_SIZE(mlxsw_sp2_mask_ethtool_1000base_x_sgmii)
+
+static const enum ethtool_link_mode_bit_indices
+mlxsw_sp2_mask_ethtool_2_5gbase_x_2_5gmii[] = {
+ ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
+};
+
+#define MLXSW_SP2_MASK_ETHTOOL_2_5GBASE_X_2_5GMII_LEN \
+ ARRAY_SIZE(mlxsw_sp2_mask_ethtool_2_5gbase_x_2_5gmii)
+
+static const enum ethtool_link_mode_bit_indices
+mlxsw_sp2_mask_ethtool_5gbase_r[] = {
+ ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+};
+
+#define MLXSW_SP2_MASK_ETHTOOL_5GBASE_R_LEN \
+ ARRAY_SIZE(mlxsw_sp2_mask_ethtool_5gbase_r)
+
+static const enum ethtool_link_mode_bit_indices
+mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g[] = {
+ ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
+ ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
+};
+
+#define MLXSW_SP2_MASK_ETHTOOL_XFI_XAUI_1_10G_LEN \
+ ARRAY_SIZE(mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g)
+
+static const enum ethtool_link_mode_bit_indices
+mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g[] = {
+ ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
+ ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
+ ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
+ ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
+};
+
+#define MLXSW_SP2_MASK_ETHTOOL_XLAUI_4_XLPPI_4_40G_LEN \
+ ARRAY_SIZE(mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g)
+
+static const enum ethtool_link_mode_bit_indices
+mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr[] = {
+ ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
+};
+
+#define MLXSW_SP2_MASK_ETHTOOL_25GAUI_1_25GBASE_CR_KR_LEN \
+ ARRAY_SIZE(mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr)
+
+static const enum ethtool_link_mode_bit_indices
+mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2[] = {
+ ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
+};
+
+#define MLXSW_SP2_MASK_ETHTOOL_50GAUI_2_LAUI_2_50GBASE_CR2_KR2_LEN \
+ ARRAY_SIZE(mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2)
+
+static const enum ethtool_link_mode_bit_indices
+mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr[] = {
+ ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseDR_Full_BIT,
+};
+
+#define MLXSW_SP2_MASK_ETHTOOL_50GAUI_1_LAUI_1_50GBASE_CR_KR_LEN \
+ ARRAY_SIZE(mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr)
+
+static const enum ethtool_link_mode_bit_indices
+mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4[] = {
+ ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
+};
+
+#define MLXSW_SP2_MASK_ETHTOOL_CAUI_4_100GBASE_CR4_KR4_LEN \
+ ARRAY_SIZE(mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4)
+
+static const enum ethtool_link_mode_bit_indices
+mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2[] = {
+ ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT,
+};
+
+#define MLXSW_SP2_MASK_ETHTOOL_100GAUI_2_100GBASE_CR2_KR2_LEN \
+ ARRAY_SIZE(mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2)
+
+static const enum ethtool_link_mode_bit_indices
+mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4[] = {
+ ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
+};
+
+#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)
+{
+ switch (width) {
+ case 1:
+ return MLXSW_SP_PORT_MASK_WIDTH_1X;
+ case 2:
+ 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;
+ }
+}
+
+struct mlxsw_sp2_port_link_mode {
+ const enum ethtool_link_mode_bit_indices *mask_ethtool;
+ int m_ethtool_len;
+ u32 mask;
+ u32 speed;
+ u8 mask_width;
+};
+
+static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = {
+ {
+ .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_SGMII_100M,
+ .mask_ethtool = mlxsw_sp2_mask_ethtool_sgmii_100m,
+ .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_8X,
+ .speed = SPEED_100,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_1000BASE_X_SGMII,
+ .mask_ethtool = mlxsw_sp2_mask_ethtool_1000base_x_sgmii,
+ .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_8X,
+ .speed = SPEED_1000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_2_5GBASE_X_2_5GMII,
+ .mask_ethtool = mlxsw_sp2_mask_ethtool_2_5gbase_x_2_5gmii,
+ .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_8X,
+ .speed = SPEED_2500,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_5GBASE_R,
+ .mask_ethtool = mlxsw_sp2_mask_ethtool_5gbase_r,
+ .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_8X,
+ .speed = SPEED_5000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_XFI_XAUI_1_10G,
+ .mask_ethtool = mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g,
+ .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_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 |
+ MLXSW_SP_PORT_MASK_WIDTH_8X,
+ .speed = SPEED_40000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_25GAUI_1_25GBASE_CR_KR,
+ .mask_ethtool = mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr,
+ .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_8X,
+ .speed = SPEED_25000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_50GAUI_2_LAUI_2_50GBASE_CR2_KR2,
+ .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_8X,
+ .speed = SPEED_50000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_50GAUI_1_LAUI_1_50GBASE_CR_KR,
+ .mask_ethtool = mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr,
+ .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_50GAUI_1_LAUI_1_50GBASE_CR_KR_LEN,
+ .mask_width = MLXSW_SP_PORT_MASK_WIDTH_1X,
+ .speed = SPEED_50000,
+ },
+ {
+ .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 |
+ MLXSW_SP_PORT_MASK_WIDTH_8X,
+ .speed = SPEED_100000,
+ },
+ {
+ .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_100GAUI_2_100GBASE_CR2_KR2,
+ .mask_ethtool = mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2,
+ .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_100GAUI_2_100GBASE_CR2_KR2_LEN,
+ .mask_width = MLXSW_SP_PORT_MASK_WIDTH_2X,
+ .speed = SPEED_100000,
+ },
+ {
+ .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 |
+ 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)
+
+static void
+mlxsw_sp2_from_ptys_supported_port(struct mlxsw_sp *mlxsw_sp,
+ u32 ptys_eth_proto,
+ struct ethtool_link_ksettings *cmd)
+{
+ ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
+ ethtool_link_ksettings_add_link_mode(cmd, supported, Backplane);
+}
+
+static void
+mlxsw_sp2_set_bit_ethtool(const struct mlxsw_sp2_port_link_mode *link_mode,
+ unsigned long *mode)
+{
+ int i;
+
+ for (i = 0; i < link_mode->m_ethtool_len; i++)
+ __set_bit(link_mode->mask_ethtool[i], mode);
+}
+
+static void
+mlxsw_sp2_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
+ u8 width, unsigned long *mode)
+{
+ u8 mask_width = mlxsw_sp_port_mask_width_get(width);
+ int i;
+
+ for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
+ if ((ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) &&
+ (mask_width & mlxsw_sp2_port_link_mode[i].mask_width))
+ mlxsw_sp2_set_bit_ethtool(&mlxsw_sp2_port_link_mode[i],
+ mode);
+ }
+}
+
+static u32
+mlxsw_sp2_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto)
+{
+ int i;
+
+ for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
+ if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask)
+ return mlxsw_sp2_port_link_mode[i].speed;
+ }
+
+ return SPEED_UNKNOWN;
+}
+
+static void
+mlxsw_sp2_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
+ u32 ptys_eth_proto,
+ struct ethtool_link_ksettings *cmd)
+{
+ cmd->base.speed = SPEED_UNKNOWN;
+ cmd->base.duplex = DUPLEX_UNKNOWN;
+
+ if (!carrier_ok)
+ return;
+
+ cmd->base.speed = mlxsw_sp2_from_ptys_speed(mlxsw_sp, ptys_eth_proto);
+ if (cmd->base.speed != SPEED_UNKNOWN)
+ cmd->base.duplex = DUPLEX_FULL;
+}
+
+static bool
+mlxsw_sp2_test_bit_ethtool(const struct mlxsw_sp2_port_link_mode *link_mode,
+ const unsigned long *mode)
+{
+ int cnt = 0;
+ int i;
+
+ for (i = 0; i < link_mode->m_ethtool_len; i++) {
+ if (test_bit(link_mode->mask_ethtool[i], mode))
+ cnt++;
+ }
+
+ return cnt == link_mode->m_ethtool_len;
+}
+
+static u32
+mlxsw_sp2_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp, u8 width,
+ const struct ethtool_link_ksettings *cmd)
+{
+ u8 mask_width = mlxsw_sp_port_mask_width_get(width);
+ u32 ptys_proto = 0;
+ int i;
+
+ for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
+ if ((mask_width & mlxsw_sp2_port_link_mode[i].mask_width) &&
+ mlxsw_sp2_test_bit_ethtool(&mlxsw_sp2_port_link_mode[i],
+ cmd->link_modes.advertising))
+ ptys_proto |= mlxsw_sp2_port_link_mode[i].mask;
+ }
+ return ptys_proto;
+}
+
+static u32 mlxsw_sp2_to_ptys_speed(struct mlxsw_sp *mlxsw_sp,
+ u8 width, u32 speed)
+{
+ u8 mask_width = mlxsw_sp_port_mask_width_get(width);
+ u32 ptys_proto = 0;
+ int i;
+
+ for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
+ if ((speed == mlxsw_sp2_port_link_mode[i].speed) &&
+ (mask_width & mlxsw_sp2_port_link_mode[i].mask_width))
+ ptys_proto |= mlxsw_sp2_port_link_mode[i].mask;
+ }
+ return ptys_proto;
+}
+
+static void
+mlxsw_sp2_reg_ptys_eth_pack(struct mlxsw_sp *mlxsw_sp, char *payload,
+ u8 local_port, u32 proto_admin,
+ bool autoneg)
+{
+ mlxsw_reg_ptys_ext_eth_pack(payload, local_port, proto_admin, autoneg);
+}
+
+static void
+mlxsw_sp2_reg_ptys_eth_unpack(struct mlxsw_sp *mlxsw_sp, char *payload,
+ u32 *p_eth_proto_cap, u32 *p_eth_proto_admin,
+ u32 *p_eth_proto_oper)
+{
+ mlxsw_reg_ptys_ext_eth_unpack(payload, p_eth_proto_cap,
+ p_eth_proto_admin, p_eth_proto_oper);
+}
+
+const struct mlxsw_sp_port_type_speed_ops mlxsw_sp2_port_type_speed_ops = {
+ .from_ptys_supported_port = mlxsw_sp2_from_ptys_supported_port,
+ .from_ptys_link = mlxsw_sp2_from_ptys_link,
+ .from_ptys_speed = mlxsw_sp2_from_ptys_speed,
+ .from_ptys_speed_duplex = mlxsw_sp2_from_ptys_speed_duplex,
+ .to_ptys_advert_link = mlxsw_sp2_to_ptys_advert_link,
+ .to_ptys_speed = mlxsw_sp2_to_ptys_speed,
+ .reg_ptys_eth_pack = mlxsw_sp2_reg_ptys_eth_pack,
+ .reg_ptys_eth_unpack = mlxsw_sp2_reg_ptys_eth_unpack,
+};
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flow.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flow.c
index 47b66f347ff1..0456cda33808 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flow.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flow.c
@@ -219,8 +219,7 @@ static int mlxsw_sp_setup_tc_block_bind(struct mlxsw_sp_port *mlxsw_sp_port,
mlxsw_sp_tc_block_release);
if (IS_ERR(block_cb)) {
mlxsw_sp_flow_block_destroy(flow_block);
- err = PTR_ERR(block_cb);
- goto err_cb_register;
+ return PTR_ERR(block_cb);
}
register_block = true;
} else {
@@ -247,7 +246,6 @@ static int mlxsw_sp_setup_tc_block_bind(struct mlxsw_sp_port *mlxsw_sp_port,
err_block_bind:
if (!flow_block_cb_decref(block_cb))
flow_block_cb_free(block_cb);
-err_cb_register:
return err;
}
@@ -279,18 +277,10 @@ static void mlxsw_sp_setup_tc_block_unbind(struct mlxsw_sp_port *mlxsw_sp_port,
}
}
-int mlxsw_sp_setup_tc_block(struct mlxsw_sp_port *mlxsw_sp_port,
- struct flow_block_offload *f)
+int mlxsw_sp_setup_tc_block_clsact(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct flow_block_offload *f,
+ bool ingress)
{
- bool ingress;
-
- if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
- ingress = true;
- else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
- ingress = false;
- else
- return -EOPNOTSUPP;
-
f->driver_block_list = &mlxsw_sp_block_cb_list;
switch (f->command) {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c
index 51e1b3930c56..41855e58564b 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c
@@ -4,6 +4,7 @@
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
+#include <linux/log2.h>
#include <net/net_namespace.h>
#include <net/flow_dissector.h>
#include <net/pkt_cls.h>
@@ -22,6 +23,7 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
{
const struct flow_action_entry *act;
int mirror_act_count = 0;
+ int police_act_count = 0;
int err, i;
if (!flow_action_has_entries(flow_action))
@@ -180,6 +182,28 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
return err;
break;
}
+ case FLOW_ACTION_POLICE: {
+ u32 burst;
+
+ if (police_act_count++) {
+ NL_SET_ERR_MSG_MOD(extack, "Multiple police actions per rule are not supported");
+ return -EOPNOTSUPP;
+ }
+
+ /* The kernel might adjust the requested burst size so
+ * that it is not exactly a power of two. Re-adjust it
+ * here since the hardware only supports burst sizes
+ * that are a power of two.
+ */
+ burst = roundup_pow_of_two(act->police.burst);
+ err = mlxsw_sp_acl_rulei_act_police(mlxsw_sp, rulei,
+ act->police.index,
+ act->police.rate_bytes_ps,
+ burst, extack);
+ if (err)
+ return err;
+ break;
+ }
default:
NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n");
@@ -616,6 +640,7 @@ int mlxsw_sp_flower_stats(struct mlxsw_sp *mlxsw_sp,
u64 packets;
u64 lastuse;
u64 bytes;
+ u64 drops;
int err;
ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
@@ -629,11 +654,12 @@ int mlxsw_sp_flower_stats(struct mlxsw_sp *mlxsw_sp,
return -EINVAL;
err = mlxsw_sp_acl_rule_get_stats(mlxsw_sp, rule, &packets, &bytes,
- &lastuse, &used_hw_stats);
+ &drops, &lastuse, &used_hw_stats);
if (err)
goto err_rule_get_stats;
- flow_stats_update(&f->stats, bytes, packets, lastuse, used_hw_stats);
+ flow_stats_update(&f->stats, bytes, packets, drops, lastuse,
+ used_hw_stats);
mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
return 0;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_matchall.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_matchall.c
index f1a44a8eda55..f30599ad6019 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_matchall.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_matchall.c
@@ -10,29 +10,6 @@
#include "spectrum_span.h"
#include "reg.h"
-enum mlxsw_sp_mall_action_type {
- MLXSW_SP_MALL_ACTION_TYPE_MIRROR,
- MLXSW_SP_MALL_ACTION_TYPE_SAMPLE,
-};
-
-struct mlxsw_sp_mall_mirror_entry {
- const struct net_device *to_dev;
- int span_id;
-};
-
-struct mlxsw_sp_mall_entry {
- struct list_head list;
- unsigned long cookie;
- unsigned int priority;
- enum mlxsw_sp_mall_action_type type;
- bool ingress;
- union {
- struct mlxsw_sp_mall_mirror_entry mirror;
- struct mlxsw_sp_port_sample sample;
- };
- struct rcu_head rcu;
-};
-
static struct mlxsw_sp_mall_entry *
mlxsw_sp_mall_entry_find(struct mlxsw_sp_flow_block *block, unsigned long cookie)
{
@@ -50,6 +27,7 @@ mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_mall_entry *mall_entry)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct mlxsw_sp_span_agent_parms agent_parms = {};
struct mlxsw_sp_span_trigger_parms parms;
enum mlxsw_sp_span_trigger trigger;
int err;
@@ -59,8 +37,9 @@ mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port,
return -EINVAL;
}
- err = mlxsw_sp_span_agent_get(mlxsw_sp, mall_entry->mirror.to_dev,
- &mall_entry->mirror.span_id);
+ agent_parms.to_dev = mall_entry->mirror.to_dev;
+ err = mlxsw_sp_span_agent_get(mlxsw_sp, &mall_entry->mirror.span_id,
+ &agent_parms);
if (err)
return err;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_policer.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_policer.c
new file mode 100644
index 000000000000..39052e5c12fd
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_policer.c
@@ -0,0 +1,468 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/* Copyright (c) 2020 Mellanox Technologies. All rights reserved */
+
+#include <linux/idr.h>
+#include <linux/log2.h>
+#include <linux/mutex.h>
+#include <linux/netlink.h>
+#include <net/devlink.h>
+
+#include "spectrum.h"
+
+struct mlxsw_sp_policer_family {
+ enum mlxsw_sp_policer_type type;
+ enum mlxsw_reg_qpcr_g qpcr_type;
+ struct mlxsw_sp *mlxsw_sp;
+ u16 start_index; /* Inclusive */
+ u16 end_index; /* Exclusive */
+ struct idr policer_idr;
+ struct mutex lock; /* Protects policer_idr */
+ atomic_t policers_count;
+ const struct mlxsw_sp_policer_family_ops *ops;
+};
+
+struct mlxsw_sp_policer {
+ struct mlxsw_sp_policer_params params;
+ u16 index;
+};
+
+struct mlxsw_sp_policer_family_ops {
+ int (*init)(struct mlxsw_sp_policer_family *family);
+ void (*fini)(struct mlxsw_sp_policer_family *family);
+ int (*policer_index_alloc)(struct mlxsw_sp_policer_family *family,
+ struct mlxsw_sp_policer *policer);
+ struct mlxsw_sp_policer * (*policer_index_free)(struct mlxsw_sp_policer_family *family,
+ u16 policer_index);
+ int (*policer_init)(struct mlxsw_sp_policer_family *family,
+ const struct mlxsw_sp_policer *policer);
+ int (*policer_params_check)(const struct mlxsw_sp_policer_family *family,
+ const struct mlxsw_sp_policer_params *params,
+ struct netlink_ext_ack *extack);
+};
+
+struct mlxsw_sp_policer_core {
+ struct mlxsw_sp_policer_family *family_arr[MLXSW_SP_POLICER_TYPE_MAX + 1];
+ const struct mlxsw_sp_policer_core_ops *ops;
+ u8 lowest_bs_bits;
+ u8 highest_bs_bits;
+};
+
+struct mlxsw_sp_policer_core_ops {
+ int (*init)(struct mlxsw_sp_policer_core *policer_core);
+};
+
+static u64 mlxsw_sp_policer_rate_bytes_ps_kbps(u64 rate_bytes_ps)
+{
+ return div_u64(rate_bytes_ps, 1000) * BITS_PER_BYTE;
+}
+
+static u8 mlxsw_sp_policer_burst_bytes_hw_units(u64 burst_bytes)
+{
+ /* Provided burst size is in bytes. The ASIC burst size value is
+ * (2 ^ bs) * 512 bits. Convert the provided size to 512-bit units.
+ */
+ u64 bs512 = div_u64(burst_bytes, 64);
+
+ if (!bs512)
+ return 0;
+
+ return fls64(bs512) - 1;
+}
+
+static u64 mlxsw_sp_policer_single_rate_occ_get(void *priv)
+{
+ struct mlxsw_sp_policer_family *family = priv;
+
+ return atomic_read(&family->policers_count);
+}
+
+static int
+mlxsw_sp_policer_single_rate_family_init(struct mlxsw_sp_policer_family *family)
+{
+ struct mlxsw_core *core = family->mlxsw_sp->core;
+ struct devlink *devlink;
+
+ /* CPU policers are allocated from the first N policers in the global
+ * range, so skip them.
+ */
+ if (!MLXSW_CORE_RES_VALID(core, MAX_GLOBAL_POLICERS) ||
+ !MLXSW_CORE_RES_VALID(core, MAX_CPU_POLICERS))
+ return -EIO;
+
+ family->start_index = MLXSW_CORE_RES_GET(core, MAX_CPU_POLICERS);
+ family->end_index = MLXSW_CORE_RES_GET(core, MAX_GLOBAL_POLICERS);
+
+ atomic_set(&family->policers_count, 0);
+ devlink = priv_to_devlink(core);
+ devlink_resource_occ_get_register(devlink,
+ MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS,
+ mlxsw_sp_policer_single_rate_occ_get,
+ family);
+
+ return 0;
+}
+
+static void
+mlxsw_sp_policer_single_rate_family_fini(struct mlxsw_sp_policer_family *family)
+{
+ struct devlink *devlink = priv_to_devlink(family->mlxsw_sp->core);
+
+ devlink_resource_occ_get_unregister(devlink,
+ MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS);
+ WARN_ON(atomic_read(&family->policers_count) != 0);
+}
+
+static int
+mlxsw_sp_policer_single_rate_index_alloc(struct mlxsw_sp_policer_family *family,
+ struct mlxsw_sp_policer *policer)
+{
+ int id;
+
+ mutex_lock(&family->lock);
+ id = idr_alloc(&family->policer_idr, policer, family->start_index,
+ family->end_index, GFP_KERNEL);
+ mutex_unlock(&family->lock);
+
+ if (id < 0)
+ return id;
+
+ atomic_inc(&family->policers_count);
+ policer->index = id;
+
+ return 0;
+}
+
+static struct mlxsw_sp_policer *
+mlxsw_sp_policer_single_rate_index_free(struct mlxsw_sp_policer_family *family,
+ u16 policer_index)
+{
+ struct mlxsw_sp_policer *policer;
+
+ atomic_dec(&family->policers_count);
+
+ mutex_lock(&family->lock);
+ policer = idr_remove(&family->policer_idr, policer_index);
+ mutex_unlock(&family->lock);
+
+ WARN_ON(!policer);
+
+ return policer;
+}
+
+static int
+mlxsw_sp_policer_single_rate_init(struct mlxsw_sp_policer_family *family,
+ const struct mlxsw_sp_policer *policer)
+{
+ u64 rate_kbps = mlxsw_sp_policer_rate_bytes_ps_kbps(policer->params.rate);
+ u8 bs = mlxsw_sp_policer_burst_bytes_hw_units(policer->params.burst);
+ struct mlxsw_sp *mlxsw_sp = family->mlxsw_sp;
+ char qpcr_pl[MLXSW_REG_QPCR_LEN];
+
+ mlxsw_reg_qpcr_pack(qpcr_pl, policer->index, MLXSW_REG_QPCR_IR_UNITS_K,
+ true, rate_kbps, bs);
+ mlxsw_reg_qpcr_clear_counter_set(qpcr_pl, true);
+
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
+}
+
+static int
+mlxsw_sp_policer_single_rate_params_check(const struct mlxsw_sp_policer_family *family,
+ const struct mlxsw_sp_policer_params *params,
+ struct netlink_ext_ack *extack)
+{
+ struct mlxsw_sp_policer_core *policer_core = family->mlxsw_sp->policer_core;
+ u64 rate_bps = params->rate * BITS_PER_BYTE;
+ u8 bs;
+
+ if (!params->bytes) {
+ NL_SET_ERR_MSG_MOD(extack, "Only bandwidth policing is currently supported by single rate policers");
+ return -EINVAL;
+ }
+
+ if (!is_power_of_2(params->burst)) {
+ NL_SET_ERR_MSG_MOD(extack, "Policer burst size is not power of two");
+ return -EINVAL;
+ }
+
+ bs = mlxsw_sp_policer_burst_bytes_hw_units(params->burst);
+
+ if (bs < policer_core->lowest_bs_bits) {
+ NL_SET_ERR_MSG_MOD(extack, "Policer burst size lower than limit");
+ return -EINVAL;
+ }
+
+ if (bs > policer_core->highest_bs_bits) {
+ NL_SET_ERR_MSG_MOD(extack, "Policer burst size higher than limit");
+ return -EINVAL;
+ }
+
+ if (rate_bps < MLXSW_REG_QPCR_LOWEST_CIR_BITS) {
+ NL_SET_ERR_MSG_MOD(extack, "Policer rate lower than limit");
+ return -EINVAL;
+ }
+
+ if (rate_bps > MLXSW_REG_QPCR_HIGHEST_CIR_BITS) {
+ NL_SET_ERR_MSG_MOD(extack, "Policer rate higher than limit");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct mlxsw_sp_policer_family_ops mlxsw_sp_policer_single_rate_ops = {
+ .init = mlxsw_sp_policer_single_rate_family_init,
+ .fini = mlxsw_sp_policer_single_rate_family_fini,
+ .policer_index_alloc = mlxsw_sp_policer_single_rate_index_alloc,
+ .policer_index_free = mlxsw_sp_policer_single_rate_index_free,
+ .policer_init = mlxsw_sp_policer_single_rate_init,
+ .policer_params_check = mlxsw_sp_policer_single_rate_params_check,
+};
+
+static const struct mlxsw_sp_policer_family mlxsw_sp_policer_single_rate_family = {
+ .type = MLXSW_SP_POLICER_TYPE_SINGLE_RATE,
+ .qpcr_type = MLXSW_REG_QPCR_G_GLOBAL,
+ .ops = &mlxsw_sp_policer_single_rate_ops,
+};
+
+static const struct mlxsw_sp_policer_family *mlxsw_sp_policer_family_arr[] = {
+ [MLXSW_SP_POLICER_TYPE_SINGLE_RATE] = &mlxsw_sp_policer_single_rate_family,
+};
+
+int mlxsw_sp_policer_add(struct mlxsw_sp *mlxsw_sp,
+ enum mlxsw_sp_policer_type type,
+ const struct mlxsw_sp_policer_params *params,
+ struct netlink_ext_ack *extack, u16 *p_policer_index)
+{
+ struct mlxsw_sp_policer_family *family;
+ struct mlxsw_sp_policer *policer;
+ int err;
+
+ family = mlxsw_sp->policer_core->family_arr[type];
+
+ err = family->ops->policer_params_check(family, params, extack);
+ if (err)
+ return err;
+
+ policer = kmalloc(sizeof(*policer), GFP_KERNEL);
+ if (!policer)
+ return -ENOMEM;
+ policer->params = *params;
+
+ err = family->ops->policer_index_alloc(family, policer);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack, "Failed to allocate policer index");
+ goto err_policer_index_alloc;
+ }
+
+ err = family->ops->policer_init(family, policer);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack, "Failed to initialize policer");
+ goto err_policer_init;
+ }
+
+ *p_policer_index = policer->index;
+
+ return 0;
+
+err_policer_init:
+ family->ops->policer_index_free(family, policer->index);
+err_policer_index_alloc:
+ kfree(policer);
+ return err;
+}
+
+void mlxsw_sp_policer_del(struct mlxsw_sp *mlxsw_sp,
+ enum mlxsw_sp_policer_type type, u16 policer_index)
+{
+ struct mlxsw_sp_policer_family *family;
+ struct mlxsw_sp_policer *policer;
+
+ family = mlxsw_sp->policer_core->family_arr[type];
+ policer = family->ops->policer_index_free(family, policer_index);
+ kfree(policer);
+}
+
+int mlxsw_sp_policer_drops_counter_get(struct mlxsw_sp *mlxsw_sp,
+ enum mlxsw_sp_policer_type type,
+ u16 policer_index, u64 *p_drops)
+{
+ struct mlxsw_sp_policer_family *family;
+ char qpcr_pl[MLXSW_REG_QPCR_LEN];
+ int err;
+
+ family = mlxsw_sp->policer_core->family_arr[type];
+
+ MLXSW_REG_ZERO(qpcr, qpcr_pl);
+ mlxsw_reg_qpcr_pid_set(qpcr_pl, policer_index);
+ mlxsw_reg_qpcr_g_set(qpcr_pl, family->qpcr_type);
+ err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
+ if (err)
+ return err;
+
+ *p_drops = mlxsw_reg_qpcr_violate_count_get(qpcr_pl);
+
+ return 0;
+}
+
+static int
+mlxsw_sp_policer_family_register(struct mlxsw_sp *mlxsw_sp,
+ const struct mlxsw_sp_policer_family *tmpl)
+{
+ struct mlxsw_sp_policer_family *family;
+ int err;
+
+ family = kmemdup(tmpl, sizeof(*family), GFP_KERNEL);
+ if (!family)
+ return -ENOMEM;
+
+ family->mlxsw_sp = mlxsw_sp;
+ idr_init(&family->policer_idr);
+ mutex_init(&family->lock);
+
+ err = family->ops->init(family);
+ if (err)
+ goto err_family_init;
+
+ if (WARN_ON(family->start_index >= family->end_index)) {
+ err = -EINVAL;
+ goto err_index_check;
+ }
+
+ mlxsw_sp->policer_core->family_arr[tmpl->type] = family;
+
+ return 0;
+
+err_index_check:
+ family->ops->fini(family);
+err_family_init:
+ mutex_destroy(&family->lock);
+ idr_destroy(&family->policer_idr);
+ kfree(family);
+ return err;
+}
+
+static void
+mlxsw_sp_policer_family_unregister(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_policer_family *family)
+{
+ family->ops->fini(family);
+ mutex_destroy(&family->lock);
+ WARN_ON(!idr_is_empty(&family->policer_idr));
+ idr_destroy(&family->policer_idr);
+ kfree(family);
+}
+
+int mlxsw_sp_policers_init(struct mlxsw_sp *mlxsw_sp)
+{
+ struct mlxsw_sp_policer_core *policer_core;
+ int i, err;
+
+ policer_core = kzalloc(sizeof(*policer_core), GFP_KERNEL);
+ if (!policer_core)
+ return -ENOMEM;
+ mlxsw_sp->policer_core = policer_core;
+ policer_core->ops = mlxsw_sp->policer_core_ops;
+
+ err = policer_core->ops->init(policer_core);
+ if (err)
+ goto err_init;
+
+ for (i = 0; i < MLXSW_SP_POLICER_TYPE_MAX + 1; i++) {
+ err = mlxsw_sp_policer_family_register(mlxsw_sp, mlxsw_sp_policer_family_arr[i]);
+ if (err)
+ goto err_family_register;
+ }
+
+ return 0;
+
+err_family_register:
+ for (i--; i >= 0; i--) {
+ struct mlxsw_sp_policer_family *family;
+
+ family = mlxsw_sp->policer_core->family_arr[i];
+ mlxsw_sp_policer_family_unregister(mlxsw_sp, family);
+ }
+err_init:
+ kfree(mlxsw_sp->policer_core);
+ return err;
+}
+
+void mlxsw_sp_policers_fini(struct mlxsw_sp *mlxsw_sp)
+{
+ int i;
+
+ for (i = MLXSW_SP_POLICER_TYPE_MAX; i >= 0; i--) {
+ struct mlxsw_sp_policer_family *family;
+
+ family = mlxsw_sp->policer_core->family_arr[i];
+ mlxsw_sp_policer_family_unregister(mlxsw_sp, family);
+ }
+
+ kfree(mlxsw_sp->policer_core);
+}
+
+int mlxsw_sp_policer_resources_register(struct mlxsw_core *mlxsw_core)
+{
+ u64 global_policers, cpu_policers, single_rate_policers;
+ struct devlink *devlink = priv_to_devlink(mlxsw_core);
+ struct devlink_resource_size_params size_params;
+ int err;
+
+ if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_GLOBAL_POLICERS) ||
+ !MLXSW_CORE_RES_VALID(mlxsw_core, MAX_CPU_POLICERS))
+ return -EIO;
+
+ global_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_GLOBAL_POLICERS);
+ cpu_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_CPU_POLICERS);
+ single_rate_policers = global_policers - cpu_policers;
+
+ devlink_resource_size_params_init(&size_params, global_policers,
+ global_policers, 1,
+ DEVLINK_RESOURCE_UNIT_ENTRY);
+ err = devlink_resource_register(devlink, "global_policers",
+ global_policers,
+ MLXSW_SP_RESOURCE_GLOBAL_POLICERS,
+ DEVLINK_RESOURCE_ID_PARENT_TOP,
+ &size_params);
+ if (err)
+ return err;
+
+ devlink_resource_size_params_init(&size_params, single_rate_policers,
+ single_rate_policers, 1,
+ DEVLINK_RESOURCE_UNIT_ENTRY);
+ err = devlink_resource_register(devlink, "single_rate_policers",
+ single_rate_policers,
+ MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS,
+ MLXSW_SP_RESOURCE_GLOBAL_POLICERS,
+ &size_params);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int
+mlxsw_sp1_policer_core_init(struct mlxsw_sp_policer_core *policer_core)
+{
+ policer_core->lowest_bs_bits = MLXSW_REG_QPCR_LOWEST_CBS_BITS_SP1;
+ policer_core->highest_bs_bits = MLXSW_REG_QPCR_HIGHEST_CBS_BITS_SP1;
+
+ return 0;
+}
+
+const struct mlxsw_sp_policer_core_ops mlxsw_sp1_policer_core_ops = {
+ .init = mlxsw_sp1_policer_core_init,
+};
+
+static int
+mlxsw_sp2_policer_core_init(struct mlxsw_sp_policer_core *policer_core)
+{
+ policer_core->lowest_bs_bits = MLXSW_REG_QPCR_LOWEST_CBS_BITS_SP2;
+ policer_core->highest_bs_bits = MLXSW_REG_QPCR_HIGHEST_CBS_BITS_SP2;
+
+ return 0;
+}
+
+const struct mlxsw_sp_policer_core_ops mlxsw_sp2_policer_core_ops = {
+ .init = mlxsw_sp2_policer_core_init,
+};
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
index 670a43fe2a00..964fd444bb10 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
@@ -8,6 +8,7 @@
#include <net/red.h>
#include "spectrum.h"
+#include "spectrum_span.h"
#include "reg.h"
#define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1)
@@ -1272,6 +1273,529 @@ int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
}
}
+struct mlxsw_sp_qevent_block {
+ struct list_head binding_list;
+ struct list_head mall_entry_list;
+ struct mlxsw_sp *mlxsw_sp;
+};
+
+struct mlxsw_sp_qevent_binding {
+ struct list_head list;
+ struct mlxsw_sp_port *mlxsw_sp_port;
+ u32 handle;
+ int tclass_num;
+ enum mlxsw_sp_span_trigger span_trigger;
+};
+
+static LIST_HEAD(mlxsw_sp_qevent_block_cb_list);
+
+static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_mall_entry *mall_entry,
+ struct mlxsw_sp_qevent_binding *qevent_binding,
+ const struct mlxsw_sp_span_agent_parms *agent_parms,
+ int *p_span_id)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
+ struct mlxsw_sp_span_trigger_parms trigger_parms = {};
+ int span_id;
+ int err;
+
+ err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms);
+ if (err)
+ return err;
+
+ err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, true);
+ if (err)
+ goto err_analyzed_port_get;
+
+ trigger_parms.span_id = span_id;
+ err = mlxsw_sp_span_agent_bind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port,
+ &trigger_parms);
+ if (err)
+ goto err_agent_bind;
+
+ err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, qevent_binding->span_trigger,
+ qevent_binding->tclass_num);
+ if (err)
+ goto err_trigger_enable;
+
+ *p_span_id = span_id;
+ return 0;
+
+err_trigger_enable:
+ mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port,
+ &trigger_parms);
+err_agent_bind:
+ mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true);
+err_analyzed_port_get:
+ mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
+ return err;
+}
+
+static void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_qevent_binding *qevent_binding,
+ int span_id)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
+ struct mlxsw_sp_span_trigger_parms trigger_parms = {
+ .span_id = span_id,
+ };
+
+ mlxsw_sp_span_trigger_disable(mlxsw_sp_port, qevent_binding->span_trigger,
+ qevent_binding->tclass_num);
+ mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port,
+ &trigger_parms);
+ mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true);
+ mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
+}
+
+static int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_mall_entry *mall_entry,
+ struct mlxsw_sp_qevent_binding *qevent_binding)
+{
+ struct mlxsw_sp_span_agent_parms agent_parms = {
+ .to_dev = mall_entry->mirror.to_dev,
+ };
+
+ return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
+ &agent_parms, &mall_entry->mirror.span_id);
+}
+
+static void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_mall_entry *mall_entry,
+ struct mlxsw_sp_qevent_binding *qevent_binding)
+{
+ mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id);
+}
+
+static int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_mall_entry *mall_entry,
+ struct mlxsw_sp_qevent_binding *qevent_binding)
+{
+ struct mlxsw_sp_span_agent_parms agent_parms = {};
+ int err;
+
+ err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp,
+ DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS,
+ &agent_parms.policer_enable,
+ &agent_parms.policer_id);
+ if (err)
+ return err;
+
+ return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
+ &agent_parms, &mall_entry->trap.span_id);
+}
+
+static void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_mall_entry *mall_entry,
+ struct mlxsw_sp_qevent_binding *qevent_binding)
+{
+ mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id);
+}
+
+static int mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_mall_entry *mall_entry,
+ struct mlxsw_sp_qevent_binding *qevent_binding)
+{
+ switch (mall_entry->type) {
+ case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
+ return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding);
+ case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
+ return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding);
+ default:
+ /* This should have been validated away. */
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+}
+
+static void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_mall_entry *mall_entry,
+ struct mlxsw_sp_qevent_binding *qevent_binding)
+{
+ switch (mall_entry->type) {
+ case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
+ return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
+ case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
+ return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
+ default:
+ WARN_ON(1);
+ return;
+ }
+}
+
+static int mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block,
+ struct mlxsw_sp_qevent_binding *qevent_binding)
+{
+ struct mlxsw_sp_mall_entry *mall_entry;
+ int err;
+
+ list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) {
+ err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry,
+ qevent_binding);
+ if (err)
+ goto err_entry_configure;
+ }
+
+ return 0;
+
+err_entry_configure:
+ list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list)
+ mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
+ qevent_binding);
+ return err;
+}
+
+static void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block,
+ struct mlxsw_sp_qevent_binding *qevent_binding)
+{
+ struct mlxsw_sp_mall_entry *mall_entry;
+
+ list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list)
+ mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
+ qevent_binding);
+}
+
+static int mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block)
+{
+ struct mlxsw_sp_qevent_binding *qevent_binding;
+ int err;
+
+ list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) {
+ err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding);
+ if (err)
+ goto err_binding_configure;
+ }
+
+ return 0;
+
+err_binding_configure:
+ list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list)
+ mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
+ return err;
+}
+
+static void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block)
+{
+ struct mlxsw_sp_qevent_binding *qevent_binding;
+
+ list_for_each_entry(qevent_binding, &qevent_block->binding_list, list)
+ mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
+}
+
+static struct mlxsw_sp_mall_entry *
+mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie)
+{
+ struct mlxsw_sp_mall_entry *mall_entry;
+
+ list_for_each_entry(mall_entry, &block->mall_entry_list, list)
+ if (mall_entry->cookie == cookie)
+ return mall_entry;
+
+ return NULL;
+}
+
+static int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_qevent_block *qevent_block,
+ struct tc_cls_matchall_offload *f)
+{
+ struct mlxsw_sp_mall_entry *mall_entry;
+ struct flow_action_entry *act;
+ int err;
+
+ /* It should not currently be possible to replace a matchall rule. So
+ * this must be a new rule.
+ */
+ if (!list_empty(&qevent_block->mall_entry_list)) {
+ NL_SET_ERR_MSG(f->common.extack, "At most one filter supported");
+ return -EOPNOTSUPP;
+ }
+ if (f->rule->action.num_entries != 1) {
+ NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported");
+ return -EOPNOTSUPP;
+ }
+ if (f->common.chain_index) {
+ NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported");
+ return -EOPNOTSUPP;
+ }
+ if (f->common.protocol != htons(ETH_P_ALL)) {
+ NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported");
+ return -EOPNOTSUPP;
+ }
+
+ act = &f->rule->action.entries[0];
+ if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) {
+ NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents");
+ return -EOPNOTSUPP;
+ }
+
+ mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);
+ if (!mall_entry)
+ return -ENOMEM;
+ mall_entry->cookie = f->cookie;
+
+ if (act->id == FLOW_ACTION_MIRRED) {
+ mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR;
+ mall_entry->mirror.to_dev = act->dev;
+ } else if (act->id == FLOW_ACTION_TRAP) {
+ mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP;
+ } else {
+ NL_SET_ERR_MSG(f->common.extack, "Unsupported action");
+ err = -EOPNOTSUPP;
+ goto err_unsupported_action;
+ }
+
+ list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list);
+
+ err = mlxsw_sp_qevent_block_configure(qevent_block);
+ if (err)
+ goto err_block_configure;
+
+ return 0;
+
+err_block_configure:
+ list_del(&mall_entry->list);
+err_unsupported_action:
+ kfree(mall_entry);
+ return err;
+}
+
+static void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block,
+ struct tc_cls_matchall_offload *f)
+{
+ struct mlxsw_sp_mall_entry *mall_entry;
+
+ mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie);
+ if (!mall_entry)
+ return;
+
+ mlxsw_sp_qevent_block_deconfigure(qevent_block);
+
+ list_del(&mall_entry->list);
+ kfree(mall_entry);
+}
+
+static int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block,
+ struct tc_cls_matchall_offload *f)
+{
+ struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp;
+
+ switch (f->command) {
+ case TC_CLSMATCHALL_REPLACE:
+ return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f);
+ case TC_CLSMATCHALL_DESTROY:
+ mlxsw_sp_qevent_mall_destroy(qevent_block, f);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
+{
+ struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
+
+ switch (type) {
+ case TC_SETUP_CLSMATCHALL:
+ return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp,
+ struct net *net)
+{
+ struct mlxsw_sp_qevent_block *qevent_block;
+
+ qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL);
+ if (!qevent_block)
+ return NULL;
+
+ INIT_LIST_HEAD(&qevent_block->binding_list);
+ INIT_LIST_HEAD(&qevent_block->mall_entry_list);
+ qevent_block->mlxsw_sp = mlxsw_sp;
+ return qevent_block;
+}
+
+static void
+mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block)
+{
+ WARN_ON(!list_empty(&qevent_block->binding_list));
+ WARN_ON(!list_empty(&qevent_block->mall_entry_list));
+ kfree(qevent_block);
+}
+
+static void mlxsw_sp_qevent_block_release(void *cb_priv)
+{
+ struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
+
+ mlxsw_sp_qevent_block_destroy(qevent_block);
+}
+
+static struct mlxsw_sp_qevent_binding *
+mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num,
+ enum mlxsw_sp_span_trigger span_trigger)
+{
+ struct mlxsw_sp_qevent_binding *binding;
+
+ binding = kzalloc(sizeof(*binding), GFP_KERNEL);
+ if (!binding)
+ return ERR_PTR(-ENOMEM);
+
+ binding->mlxsw_sp_port = mlxsw_sp_port;
+ binding->handle = handle;
+ binding->tclass_num = tclass_num;
+ binding->span_trigger = span_trigger;
+ return binding;
+}
+
+static void
+mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding)
+{
+ kfree(binding);
+}
+
+static struct mlxsw_sp_qevent_binding *
+mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block,
+ struct mlxsw_sp_port *mlxsw_sp_port,
+ u32 handle,
+ enum mlxsw_sp_span_trigger span_trigger)
+{
+ struct mlxsw_sp_qevent_binding *qevent_binding;
+
+ list_for_each_entry(qevent_binding, &block->binding_list, list)
+ if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port &&
+ qevent_binding->handle == handle &&
+ qevent_binding->span_trigger == span_trigger)
+ return qevent_binding;
+ return NULL;
+}
+
+static int mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct flow_block_offload *f,
+ enum mlxsw_sp_span_trigger span_trigger)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct mlxsw_sp_qevent_binding *qevent_binding;
+ struct mlxsw_sp_qevent_block *qevent_block;
+ struct flow_block_cb *block_cb;
+ struct mlxsw_sp_qdisc *qdisc;
+ bool register_block = false;
+ int err;
+
+ block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
+ if (!block_cb) {
+ qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net);
+ if (!qevent_block)
+ return -ENOMEM;
+ block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block,
+ mlxsw_sp_qevent_block_release);
+ if (IS_ERR(block_cb)) {
+ mlxsw_sp_qevent_block_destroy(qevent_block);
+ return PTR_ERR(block_cb);
+ }
+ register_block = true;
+ } else {
+ qevent_block = flow_block_cb_priv(block_cb);
+ }
+ flow_block_cb_incref(block_cb);
+
+ qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle);
+ if (!qdisc) {
+ NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded");
+ err = -ENOENT;
+ goto err_find_qdisc;
+ }
+
+ if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
+ span_trigger))) {
+ err = -EEXIST;
+ goto err_binding_exists;
+ }
+
+ qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port, f->sch->handle,
+ qdisc->tclass_num, span_trigger);
+ if (IS_ERR(qevent_binding)) {
+ err = PTR_ERR(qevent_binding);
+ goto err_binding_create;
+ }
+
+ err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding);
+ if (err)
+ goto err_binding_configure;
+
+ list_add(&qevent_binding->list, &qevent_block->binding_list);
+
+ if (register_block) {
+ flow_block_cb_add(block_cb, f);
+ list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list);
+ }
+
+ return 0;
+
+err_binding_configure:
+ mlxsw_sp_qevent_binding_destroy(qevent_binding);
+err_binding_create:
+err_binding_exists:
+err_find_qdisc:
+ if (!flow_block_cb_decref(block_cb))
+ flow_block_cb_free(block_cb);
+ return err;
+}
+
+static void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct flow_block_offload *f,
+ enum mlxsw_sp_span_trigger span_trigger)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct mlxsw_sp_qevent_binding *qevent_binding;
+ struct mlxsw_sp_qevent_block *qevent_block;
+ struct flow_block_cb *block_cb;
+
+ block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
+ if (!block_cb)
+ return;
+ qevent_block = flow_block_cb_priv(block_cb);
+
+ qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
+ span_trigger);
+ if (!qevent_binding)
+ return;
+
+ list_del(&qevent_binding->list);
+ mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
+ mlxsw_sp_qevent_binding_destroy(qevent_binding);
+
+ if (!flow_block_cb_decref(block_cb)) {
+ flow_block_cb_remove(block_cb, f);
+ list_del(&block_cb->driver_list);
+ }
+}
+
+static int mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct flow_block_offload *f,
+ enum mlxsw_sp_span_trigger span_trigger)
+{
+ f->driver_block_list = &mlxsw_sp_qevent_block_cb_list;
+
+ switch (f->command) {
+ case FLOW_BLOCK_BIND:
+ return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f, span_trigger);
+ case FLOW_BLOCK_UNBIND:
+ mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct flow_block_offload *f)
+{
+ return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, MLXSW_SP_SPAN_TRIGGER_EARLY_DROP);
+}
+
int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
{
struct mlxsw_sp_qdisc_state *qdisc_state;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index 019ed503aadf..0521e9d48c45 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -5001,15 +5001,6 @@ static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
static bool mlxsw_sp_fib6_rt_should_ignore(const struct fib6_info *rt)
{
- /* Packets with link-local destination IP arriving to the router
- * are trapped to the CPU, so no need to program specific routes
- * for them. Only allow prefix routes (usually one fe80::/64) so
- * that packets are trapped for the right reason.
- */
- if ((ipv6_addr_type(&rt->fib6_dst.addr) & IPV6_ADDR_LINKLOCAL) &&
- (rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)))
- return true;
-
/* Multicast routes aren't supported, so ignore them. Neighbour
* Discovery packets are specifically trapped.
*/
@@ -8078,16 +8069,6 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp->router = router;
router->mlxsw_sp = mlxsw_sp;
- router->inetaddr_nb.notifier_call = mlxsw_sp_inetaddr_event;
- err = register_inetaddr_notifier(&router->inetaddr_nb);
- if (err)
- goto err_register_inetaddr_notifier;
-
- router->inet6addr_nb.notifier_call = mlxsw_sp_inet6addr_event;
- err = register_inet6addr_notifier(&router->inet6addr_nb);
- if (err)
- goto err_register_inet6addr_notifier;
-
INIT_LIST_HEAD(&mlxsw_sp->router->nexthop_neighs_list);
err = __mlxsw_sp_router_init(mlxsw_sp);
if (err)
@@ -8128,12 +8109,6 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
if (err)
goto err_neigh_init;
- mlxsw_sp->router->netevent_nb.notifier_call =
- mlxsw_sp_router_netevent_event;
- err = register_netevent_notifier(&mlxsw_sp->router->netevent_nb);
- if (err)
- goto err_register_netevent_notifier;
-
err = mlxsw_sp_mp_hash_init(mlxsw_sp);
if (err)
goto err_mp_hash_init;
@@ -8142,6 +8117,22 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
if (err)
goto err_dscp_init;
+ router->inetaddr_nb.notifier_call = mlxsw_sp_inetaddr_event;
+ err = register_inetaddr_notifier(&router->inetaddr_nb);
+ if (err)
+ goto err_register_inetaddr_notifier;
+
+ router->inet6addr_nb.notifier_call = mlxsw_sp_inet6addr_event;
+ err = register_inet6addr_notifier(&router->inet6addr_nb);
+ if (err)
+ goto err_register_inet6addr_notifier;
+
+ mlxsw_sp->router->netevent_nb.notifier_call =
+ mlxsw_sp_router_netevent_event;
+ err = register_netevent_notifier(&mlxsw_sp->router->netevent_nb);
+ if (err)
+ goto err_register_netevent_notifier;
+
mlxsw_sp->router->fib_nb.notifier_call = mlxsw_sp_router_fib_event;
err = register_fib_notifier(mlxsw_sp_net(mlxsw_sp),
&mlxsw_sp->router->fib_nb,
@@ -8152,10 +8143,15 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
return 0;
err_register_fib_notifier:
-err_dscp_init:
-err_mp_hash_init:
unregister_netevent_notifier(&mlxsw_sp->router->netevent_nb);
err_register_netevent_notifier:
+ unregister_inet6addr_notifier(&router->inet6addr_nb);
+err_register_inet6addr_notifier:
+ unregister_inetaddr_notifier(&router->inetaddr_nb);
+err_register_inetaddr_notifier:
+ mlxsw_core_flush_owq();
+err_dscp_init:
+err_mp_hash_init:
mlxsw_sp_neigh_fini(mlxsw_sp);
err_neigh_init:
mlxsw_sp_vrs_fini(mlxsw_sp);
@@ -8174,10 +8170,6 @@ err_ipips_init:
err_rifs_init:
__mlxsw_sp_router_fini(mlxsw_sp);
err_router_init:
- unregister_inet6addr_notifier(&router->inet6addr_nb);
-err_register_inet6addr_notifier:
- unregister_inetaddr_notifier(&router->inetaddr_nb);
-err_register_inetaddr_notifier:
mutex_destroy(&mlxsw_sp->router->lock);
kfree(mlxsw_sp->router);
return err;
@@ -8188,6 +8180,9 @@ void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
unregister_fib_notifier(mlxsw_sp_net(mlxsw_sp),
&mlxsw_sp->router->fib_nb);
unregister_netevent_notifier(&mlxsw_sp->router->netevent_nb);
+ unregister_inet6addr_notifier(&mlxsw_sp->router->inet6addr_nb);
+ unregister_inetaddr_notifier(&mlxsw_sp->router->inetaddr_nb);
+ mlxsw_core_flush_owq();
mlxsw_sp_neigh_fini(mlxsw_sp);
mlxsw_sp_vrs_fini(mlxsw_sp);
mlxsw_sp_mr_fini(mlxsw_sp);
@@ -8197,8 +8192,6 @@ void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
mlxsw_sp_ipips_fini(mlxsw_sp);
mlxsw_sp_rifs_fini(mlxsw_sp);
__mlxsw_sp_router_fini(mlxsw_sp);
- unregister_inet6addr_notifier(&mlxsw_sp->router->inet6addr_nb);
- unregister_inetaddr_notifier(&mlxsw_sp->router->inetaddr_nb);
mutex_destroy(&mlxsw_sp->router->lock);
kfree(mlxsw_sp->router);
}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
index 92351a79addc..5c959a995199 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
@@ -21,9 +21,14 @@
struct mlxsw_sp_span {
struct work_struct work;
struct mlxsw_sp *mlxsw_sp;
+ const struct mlxsw_sp_span_trigger_ops **span_trigger_ops_arr;
+ const struct mlxsw_sp_span_entry_ops **span_entry_ops_arr;
+ size_t span_entry_ops_arr_size;
struct list_head analyzed_ports_list;
struct mutex analyzed_ports_lock; /* Protects analyzed_ports_list */
struct list_head trigger_entries_list;
+ u16 policer_id_base;
+ refcount_t policer_id_base_ref_count;
atomic_t active_entries_count;
int entries_count;
struct mlxsw_sp_span_entry entries[];
@@ -38,12 +43,31 @@ struct mlxsw_sp_span_analyzed_port {
struct mlxsw_sp_span_trigger_entry {
struct list_head list; /* Member of trigger_entries_list */
+ struct mlxsw_sp_span *span;
+ const struct mlxsw_sp_span_trigger_ops *ops;
refcount_t ref_count;
u8 local_port;
enum mlxsw_sp_span_trigger trigger;
struct mlxsw_sp_span_trigger_parms parms;
};
+enum mlxsw_sp_span_trigger_type {
+ MLXSW_SP_SPAN_TRIGGER_TYPE_PORT,
+ MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL,
+};
+
+struct mlxsw_sp_span_trigger_ops {
+ int (*bind)(struct mlxsw_sp_span_trigger_entry *trigger_entry);
+ void (*unbind)(struct mlxsw_sp_span_trigger_entry *trigger_entry);
+ bool (*matches)(struct mlxsw_sp_span_trigger_entry *trigger_entry,
+ enum mlxsw_sp_span_trigger trigger,
+ struct mlxsw_sp_port *mlxsw_sp_port);
+ int (*enable)(struct mlxsw_sp_span_trigger_entry *trigger_entry,
+ struct mlxsw_sp_port *mlxsw_sp_port, u8 tc);
+ void (*disable)(struct mlxsw_sp_span_trigger_entry *trigger_entry,
+ struct mlxsw_sp_port *mlxsw_sp_port, u8 tc);
+};
+
static void mlxsw_sp_span_respin_work(struct work_struct *work);
static u64 mlxsw_sp_span_occ_get(void *priv)
@@ -57,7 +81,7 @@ int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
{
struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
struct mlxsw_sp_span *span;
- int i, entries_count;
+ int i, entries_count, err;
if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_SPAN))
return -EIO;
@@ -66,6 +90,7 @@ int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
span = kzalloc(struct_size(span, entries, entries_count), GFP_KERNEL);
if (!span)
return -ENOMEM;
+ refcount_set(&span->policer_id_base_ref_count, 0);
span->entries_count = entries_count;
atomic_set(&span->active_entries_count, 0);
mutex_init(&span->analyzed_ports_lock);
@@ -77,11 +102,20 @@ int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
for (i = 0; i < mlxsw_sp->span->entries_count; i++)
mlxsw_sp->span->entries[i].id = i;
+ err = mlxsw_sp->span_ops->init(mlxsw_sp);
+ if (err)
+ goto err_init;
+
devlink_resource_occ_get_register(devlink, MLXSW_SP_RESOURCE_SPAN,
mlxsw_sp_span_occ_get, mlxsw_sp);
INIT_WORK(&span->work, mlxsw_sp_span_respin_work);
return 0;
+
+err_init:
+ mutex_destroy(&mlxsw_sp->span->analyzed_ports_lock);
+ kfree(mlxsw_sp->span);
+ return err;
}
void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp)
@@ -97,8 +131,41 @@ void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp)
kfree(mlxsw_sp->span);
}
+static bool mlxsw_sp1_span_cpu_can_handle(const struct net_device *dev)
+{
+ return !dev;
+}
+
+static int mlxsw_sp1_span_entry_cpu_parms(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev,
+ struct mlxsw_sp_span_parms *sparmsp)
+{
+ return -EOPNOTSUPP;
+}
+
+static int
+mlxsw_sp1_span_entry_cpu_configure(struct mlxsw_sp_span_entry *span_entry,
+ struct mlxsw_sp_span_parms sparms)
+{
+ return -EOPNOTSUPP;
+}
+
+static void
+mlxsw_sp1_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry)
+{
+}
+
+static const
+struct mlxsw_sp_span_entry_ops mlxsw_sp1_span_entry_ops_cpu = {
+ .can_handle = mlxsw_sp1_span_cpu_can_handle,
+ .parms_set = mlxsw_sp1_span_entry_cpu_parms,
+ .configure = mlxsw_sp1_span_entry_cpu_configure,
+ .deconfigure = mlxsw_sp1_span_entry_cpu_deconfigure,
+};
+
static int
-mlxsw_sp_span_entry_phys_parms(const struct net_device *to_dev,
+mlxsw_sp_span_entry_phys_parms(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev,
struct mlxsw_sp_span_parms *sparmsp)
{
sparmsp->dest_port = netdev_priv(to_dev);
@@ -118,6 +185,8 @@ mlxsw_sp_span_entry_phys_configure(struct mlxsw_sp_span_entry *span_entry,
/* Create a new port analayzer entry for local_port. */
mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
+ mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
+ mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
}
@@ -374,7 +443,8 @@ out:
}
static int
-mlxsw_sp_span_entry_gretap4_parms(const struct net_device *to_dev,
+mlxsw_sp_span_entry_gretap4_parms(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev,
struct mlxsw_sp_span_parms *sparmsp)
{
struct ip_tunnel_parm tparm = mlxsw_sp_ipip_netdev_parms4(to_dev);
@@ -413,6 +483,8 @@ mlxsw_sp_span_entry_gretap4_configure(struct mlxsw_sp_span_entry *span_entry,
/* Create a new port analayzer entry for local_port. */
mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
+ mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
+ mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
@@ -475,7 +547,8 @@ out:
}
static int
-mlxsw_sp_span_entry_gretap6_parms(const struct net_device *to_dev,
+mlxsw_sp_span_entry_gretap6_parms(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev,
struct mlxsw_sp_span_parms *sparmsp)
{
struct __ip6_tnl_parm tparm = mlxsw_sp_ipip_netdev_parms6(to_dev);
@@ -514,6 +587,8 @@ mlxsw_sp_span_entry_gretap6_configure(struct mlxsw_sp_span_entry *span_entry,
/* Create a new port analayzer entry for local_port. */
mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
+ mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
+ mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
@@ -549,7 +624,8 @@ mlxsw_sp_span_vlan_can_handle(const struct net_device *dev)
}
static int
-mlxsw_sp_span_entry_vlan_parms(const struct net_device *to_dev,
+mlxsw_sp_span_entry_vlan_parms(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev,
struct mlxsw_sp_span_parms *sparmsp)
{
struct net_device *real_dev;
@@ -576,6 +652,8 @@ mlxsw_sp_span_entry_vlan_configure(struct mlxsw_sp_span_entry *span_entry,
mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH);
+ mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
+ mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
@@ -597,7 +675,61 @@ struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_vlan = {
};
static const
-struct mlxsw_sp_span_entry_ops *const mlxsw_sp_span_entry_types[] = {
+struct mlxsw_sp_span_entry_ops *mlxsw_sp1_span_entry_ops_arr[] = {
+ &mlxsw_sp1_span_entry_ops_cpu,
+ &mlxsw_sp_span_entry_ops_phys,
+#if IS_ENABLED(CONFIG_NET_IPGRE)
+ &mlxsw_sp_span_entry_ops_gretap4,
+#endif
+#if IS_ENABLED(CONFIG_IPV6_GRE)
+ &mlxsw_sp_span_entry_ops_gretap6,
+#endif
+ &mlxsw_sp_span_entry_ops_vlan,
+};
+
+static bool mlxsw_sp2_span_cpu_can_handle(const struct net_device *dev)
+{
+ return !dev;
+}
+
+static int mlxsw_sp2_span_entry_cpu_parms(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev,
+ struct mlxsw_sp_span_parms *sparmsp)
+{
+ sparmsp->dest_port = mlxsw_sp->ports[MLXSW_PORT_CPU_PORT];
+ return 0;
+}
+
+static int
+mlxsw_sp2_span_entry_cpu_configure(struct mlxsw_sp_span_entry *span_entry,
+ struct mlxsw_sp_span_parms sparms)
+{
+ /* Mirroring to the CPU port is like mirroring to any other physical
+ * port. Its local port is used instead of that of the physical port.
+ */
+ return mlxsw_sp_span_entry_phys_configure(span_entry, sparms);
+}
+
+static void
+mlxsw_sp2_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry)
+{
+ enum mlxsw_reg_mpat_span_type span_type;
+
+ span_type = MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH;
+ mlxsw_sp_span_entry_deconfigure_common(span_entry, span_type);
+}
+
+static const
+struct mlxsw_sp_span_entry_ops mlxsw_sp2_span_entry_ops_cpu = {
+ .can_handle = mlxsw_sp2_span_cpu_can_handle,
+ .parms_set = mlxsw_sp2_span_entry_cpu_parms,
+ .configure = mlxsw_sp2_span_entry_cpu_configure,
+ .deconfigure = mlxsw_sp2_span_entry_cpu_deconfigure,
+};
+
+static const
+struct mlxsw_sp_span_entry_ops *mlxsw_sp2_span_entry_ops_arr[] = {
+ &mlxsw_sp2_span_entry_ops_cpu,
&mlxsw_sp_span_entry_ops_phys,
#if IS_ENABLED(CONFIG_NET_IPGRE)
&mlxsw_sp_span_entry_ops_gretap4,
@@ -609,7 +741,8 @@ struct mlxsw_sp_span_entry_ops *const mlxsw_sp_span_entry_types[] = {
};
static int
-mlxsw_sp_span_entry_nop_parms(const struct net_device *to_dev,
+mlxsw_sp_span_entry_nop_parms(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev,
struct mlxsw_sp_span_parms *sparmsp)
{
return mlxsw_sp_span_entry_unoffloadable(sparmsp);
@@ -644,16 +777,15 @@ mlxsw_sp_span_entry_configure(struct mlxsw_sp *mlxsw_sp,
goto set_parms;
if (sparms.dest_port->mlxsw_sp != mlxsw_sp) {
- netdev_err(span_entry->to_dev, "Cannot mirror to %s, which belongs to a different mlxsw instance",
- sparms.dest_port->dev->name);
+ dev_err(mlxsw_sp->bus_info->dev,
+ "Cannot mirror to a port which belongs to a different mlxsw instance\n");
sparms.dest_port = NULL;
goto set_parms;
}
err = span_entry->ops->configure(span_entry, sparms);
if (err) {
- netdev_err(span_entry->to_dev, "Failed to offload mirror to %s",
- sparms.dest_port->dev->name);
+ dev_err(mlxsw_sp->bus_info->dev, "Failed to offload mirror\n");
sparms.dest_port = NULL;
goto set_parms;
}
@@ -669,6 +801,46 @@ mlxsw_sp_span_entry_deconfigure(struct mlxsw_sp_span_entry *span_entry)
span_entry->ops->deconfigure(span_entry);
}
+static int mlxsw_sp_span_policer_id_base_set(struct mlxsw_sp_span *span,
+ u16 policer_id)
+{
+ struct mlxsw_sp *mlxsw_sp = span->mlxsw_sp;
+ u16 policer_id_base;
+ int err;
+
+ /* Policers set on SPAN agents must be in the range of
+ * `policer_id_base .. policer_id_base + max_span_agents - 1`. If the
+ * base is set and the new policer is not within the range, then we
+ * must error out.
+ */
+ if (refcount_read(&span->policer_id_base_ref_count)) {
+ if (policer_id < span->policer_id_base ||
+ policer_id >= span->policer_id_base + span->entries_count)
+ return -EINVAL;
+
+ refcount_inc(&span->policer_id_base_ref_count);
+ return 0;
+ }
+
+ /* Base must be even. */
+ policer_id_base = policer_id % 2 == 0 ? policer_id : policer_id - 1;
+ err = mlxsw_sp->span_ops->policer_id_base_set(mlxsw_sp,
+ policer_id_base);
+ if (err)
+ return err;
+
+ span->policer_id_base = policer_id_base;
+ refcount_set(&span->policer_id_base_ref_count, 1);
+
+ return 0;
+}
+
+static void mlxsw_sp_span_policer_id_base_unset(struct mlxsw_sp_span *span)
+{
+ if (refcount_dec_and_test(&span->policer_id_base_ref_count))
+ span->policer_id_base = 0;
+}
+
static struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_create(struct mlxsw_sp *mlxsw_sp,
const struct net_device *to_dev,
@@ -688,6 +860,15 @@ mlxsw_sp_span_entry_create(struct mlxsw_sp *mlxsw_sp,
if (!span_entry)
return NULL;
+ if (sparms.policer_enable) {
+ int err;
+
+ err = mlxsw_sp_span_policer_id_base_set(mlxsw_sp->span,
+ sparms.policer_id);
+ if (err)
+ return NULL;
+ }
+
atomic_inc(&mlxsw_sp->span->active_entries_count);
span_entry->ops = ops;
refcount_set(&span_entry->ref_count, 1);
@@ -702,6 +883,8 @@ static void mlxsw_sp_span_entry_destroy(struct mlxsw_sp *mlxsw_sp,
{
mlxsw_sp_span_entry_deconfigure(span_entry);
atomic_dec(&mlxsw_sp->span->active_entries_count);
+ if (span_entry->parms.policer_enable)
+ mlxsw_sp_span_policer_id_base_unset(mlxsw_sp->span);
}
struct mlxsw_sp_span_entry *
@@ -741,6 +924,24 @@ mlxsw_sp_span_entry_find_by_id(struct mlxsw_sp *mlxsw_sp, int span_id)
}
static struct mlxsw_sp_span_entry *
+mlxsw_sp_span_entry_find_by_parms(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev,
+ const struct mlxsw_sp_span_parms *sparms)
+{
+ int i;
+
+ for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
+ struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
+
+ if (refcount_read(&curr->ref_count) && curr->to_dev == to_dev &&
+ curr->parms.policer_enable == sparms->policer_enable &&
+ curr->parms.policer_id == sparms->policer_id)
+ return curr;
+ }
+ return NULL;
+}
+
+static struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_get(struct mlxsw_sp *mlxsw_sp,
const struct net_device *to_dev,
const struct mlxsw_sp_span_entry_ops *ops,
@@ -748,7 +949,8 @@ mlxsw_sp_span_entry_get(struct mlxsw_sp *mlxsw_sp,
{
struct mlxsw_sp_span_entry *span_entry;
- span_entry = mlxsw_sp_span_entry_find_by_port(mlxsw_sp, to_dev);
+ span_entry = mlxsw_sp_span_entry_find_by_parms(mlxsw_sp, to_dev,
+ &sparms);
if (span_entry) {
/* Already exists, just take a reference */
refcount_inc(&span_entry->ref_count);
@@ -766,6 +968,14 @@ static int mlxsw_sp_span_entry_put(struct mlxsw_sp *mlxsw_sp,
return 0;
}
+static u32 mlxsw_sp_span_buffsize_get(struct mlxsw_sp *mlxsw_sp, int mtu,
+ u32 speed)
+{
+ u32 buffsize = mlxsw_sp->span_ops->buffsize_get(speed, mtu);
+
+ return mlxsw_sp_bytes_cells(mlxsw_sp, buffsize) + 1;
+}
+
static int
mlxsw_sp_span_port_buffer_update(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu)
{
@@ -857,11 +1067,12 @@ static const struct mlxsw_sp_span_entry_ops *
mlxsw_sp_span_entry_ops(struct mlxsw_sp *mlxsw_sp,
const struct net_device *to_dev)
{
+ struct mlxsw_sp_span *span = mlxsw_sp->span;
size_t i;
- for (i = 0; i < ARRAY_SIZE(mlxsw_sp_span_entry_types); ++i)
- if (mlxsw_sp_span_entry_types[i]->can_handle(to_dev))
- return mlxsw_sp_span_entry_types[i];
+ for (i = 0; i < span->span_entry_ops_arr_size; ++i)
+ if (span->span_entry_ops_arr[i]->can_handle(to_dev))
+ return span->span_entry_ops_arr[i];
return NULL;
}
@@ -883,7 +1094,7 @@ static void mlxsw_sp_span_respin_work(struct work_struct *work)
if (!refcount_read(&curr->ref_count))
continue;
- err = curr->ops->parms_set(curr->to_dev, &sparms);
+ err = curr->ops->parms_set(mlxsw_sp, curr->to_dev, &sparms);
if (err)
continue;
@@ -902,9 +1113,10 @@ void mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp)
mlxsw_core_schedule_work(&mlxsw_sp->span->work);
}
-int mlxsw_sp_span_agent_get(struct mlxsw_sp *mlxsw_sp,
- const struct net_device *to_dev, int *p_span_id)
+int mlxsw_sp_span_agent_get(struct mlxsw_sp *mlxsw_sp, int *p_span_id,
+ const struct mlxsw_sp_span_agent_parms *parms)
{
+ const struct net_device *to_dev = parms->to_dev;
const struct mlxsw_sp_span_entry_ops *ops;
struct mlxsw_sp_span_entry *span_entry;
struct mlxsw_sp_span_parms sparms;
@@ -919,10 +1131,12 @@ int mlxsw_sp_span_agent_get(struct mlxsw_sp *mlxsw_sp,
}
memset(&sparms, 0, sizeof(sparms));
- err = ops->parms_set(to_dev, &sparms);
+ err = ops->parms_set(mlxsw_sp, to_dev, &sparms);
if (err)
return err;
+ sparms.policer_id = parms->policer_id;
+ sparms.policer_enable = parms->policer_enable;
span_entry = mlxsw_sp_span_entry_get(mlxsw_sp, to_dev, ops, sparms);
if (!span_entry)
return -ENOBUFS;
@@ -1051,9 +1265,9 @@ out_unlock:
}
static int
-__mlxsw_sp_span_trigger_entry_bind(struct mlxsw_sp_span *span,
- struct mlxsw_sp_span_trigger_entry *
- trigger_entry, bool enable)
+__mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span *span,
+ struct mlxsw_sp_span_trigger_entry *
+ trigger_entry, bool enable)
{
char mpar_pl[MLXSW_REG_MPAR_LEN];
enum mlxsw_reg_mpar_i_e i_e;
@@ -1076,19 +1290,254 @@ __mlxsw_sp_span_trigger_entry_bind(struct mlxsw_sp_span *span,
}
static int
-mlxsw_sp_span_trigger_entry_bind(struct mlxsw_sp_span *span,
- struct mlxsw_sp_span_trigger_entry *
- trigger_entry)
+mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry)
+{
+ return __mlxsw_sp_span_trigger_port_bind(trigger_entry->span,
+ trigger_entry, true);
+}
+
+static void
+mlxsw_sp_span_trigger_port_unbind(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry)
+{
+ __mlxsw_sp_span_trigger_port_bind(trigger_entry->span, trigger_entry,
+ false);
+}
+
+static bool
+mlxsw_sp_span_trigger_port_matches(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry,
+ enum mlxsw_sp_span_trigger trigger,
+ struct mlxsw_sp_port *mlxsw_sp_port)
+{
+ return trigger_entry->trigger == trigger &&
+ trigger_entry->local_port == mlxsw_sp_port->local_port;
+}
+
+static int
+mlxsw_sp_span_trigger_port_enable(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry,
+ struct mlxsw_sp_port *mlxsw_sp_port, u8 tc)
{
- return __mlxsw_sp_span_trigger_entry_bind(span, trigger_entry, true);
+ /* Port trigger are enabled during binding. */
+ return 0;
}
static void
-mlxsw_sp_span_trigger_entry_unbind(struct mlxsw_sp_span *span,
- struct mlxsw_sp_span_trigger_entry *
+mlxsw_sp_span_trigger_port_disable(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry,
+ struct mlxsw_sp_port *mlxsw_sp_port, u8 tc)
+{
+}
+
+static const struct mlxsw_sp_span_trigger_ops
+mlxsw_sp_span_trigger_port_ops = {
+ .bind = mlxsw_sp_span_trigger_port_bind,
+ .unbind = mlxsw_sp_span_trigger_port_unbind,
+ .matches = mlxsw_sp_span_trigger_port_matches,
+ .enable = mlxsw_sp_span_trigger_port_enable,
+ .disable = mlxsw_sp_span_trigger_port_disable,
+};
+
+static int
+mlxsw_sp1_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry *
trigger_entry)
{
- __mlxsw_sp_span_trigger_entry_bind(span, trigger_entry, false);
+ return -EOPNOTSUPP;
+}
+
+static void
+mlxsw_sp1_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry)
+{
+}
+
+static bool
+mlxsw_sp1_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry,
+ enum mlxsw_sp_span_trigger trigger,
+ struct mlxsw_sp_port *mlxsw_sp_port)
+{
+ WARN_ON_ONCE(1);
+ return false;
+}
+
+static int
+mlxsw_sp1_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry,
+ struct mlxsw_sp_port *mlxsw_sp_port,
+ u8 tc)
+{
+ return -EOPNOTSUPP;
+}
+
+static void
+mlxsw_sp1_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry,
+ struct mlxsw_sp_port *mlxsw_sp_port,
+ u8 tc)
+{
+}
+
+static const struct mlxsw_sp_span_trigger_ops
+mlxsw_sp1_span_trigger_global_ops = {
+ .bind = mlxsw_sp1_span_trigger_global_bind,
+ .unbind = mlxsw_sp1_span_trigger_global_unbind,
+ .matches = mlxsw_sp1_span_trigger_global_matches,
+ .enable = mlxsw_sp1_span_trigger_global_enable,
+ .disable = mlxsw_sp1_span_trigger_global_disable,
+};
+
+static const struct mlxsw_sp_span_trigger_ops *
+mlxsw_sp1_span_trigger_ops_arr[] = {
+ [MLXSW_SP_SPAN_TRIGGER_TYPE_PORT] = &mlxsw_sp_span_trigger_port_ops,
+ [MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL] =
+ &mlxsw_sp1_span_trigger_global_ops,
+};
+
+static int
+mlxsw_sp2_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry)
+{
+ struct mlxsw_sp *mlxsw_sp = trigger_entry->span->mlxsw_sp;
+ enum mlxsw_reg_mpagr_trigger trigger;
+ char mpagr_pl[MLXSW_REG_MPAGR_LEN];
+
+ switch (trigger_entry->trigger) {
+ case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP:
+ trigger = MLXSW_REG_MPAGR_TRIGGER_INGRESS_SHARED_BUFFER;
+ break;
+ case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP:
+ trigger = MLXSW_REG_MPAGR_TRIGGER_INGRESS_WRED;
+ break;
+ case MLXSW_SP_SPAN_TRIGGER_ECN:
+ trigger = MLXSW_REG_MPAGR_TRIGGER_EGRESS_ECN;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ mlxsw_reg_mpagr_pack(mpagr_pl, trigger, trigger_entry->parms.span_id,
+ 1);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpagr), mpagr_pl);
+}
+
+static void
+mlxsw_sp2_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry)
+{
+ /* There is no unbinding for global triggers. The trigger should be
+ * disabled on all ports by now.
+ */
+}
+
+static bool
+mlxsw_sp2_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry,
+ enum mlxsw_sp_span_trigger trigger,
+ struct mlxsw_sp_port *mlxsw_sp_port)
+{
+ return trigger_entry->trigger == trigger;
+}
+
+static int
+__mlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry,
+ struct mlxsw_sp_port *mlxsw_sp_port,
+ u8 tc, bool enable)
+{
+ struct mlxsw_sp *mlxsw_sp = trigger_entry->span->mlxsw_sp;
+ char momte_pl[MLXSW_REG_MOMTE_LEN];
+ enum mlxsw_reg_momte_type type;
+ int err;
+
+ switch (trigger_entry->trigger) {
+ case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP:
+ type = MLXSW_REG_MOMTE_TYPE_SHARED_BUFFER_TCLASS;
+ break;
+ case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP:
+ type = MLXSW_REG_MOMTE_TYPE_WRED;
+ break;
+ case MLXSW_SP_SPAN_TRIGGER_ECN:
+ type = MLXSW_REG_MOMTE_TYPE_ECN;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ /* Query existing configuration in order to only change the state of
+ * the specified traffic class.
+ */
+ mlxsw_reg_momte_pack(momte_pl, mlxsw_sp_port->local_port, type);
+ err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(momte), momte_pl);
+ if (err)
+ return err;
+
+ mlxsw_reg_momte_tclass_en_set(momte_pl, tc, enable);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(momte), momte_pl);
+}
+
+static int
+mlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry,
+ struct mlxsw_sp_port *mlxsw_sp_port,
+ u8 tc)
+{
+ return __mlxsw_sp2_span_trigger_global_enable(trigger_entry,
+ mlxsw_sp_port, tc, true);
+}
+
+static void
+mlxsw_sp2_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry *
+ trigger_entry,
+ struct mlxsw_sp_port *mlxsw_sp_port,
+ u8 tc)
+{
+ __mlxsw_sp2_span_trigger_global_enable(trigger_entry, mlxsw_sp_port, tc,
+ false);
+}
+
+static const struct mlxsw_sp_span_trigger_ops
+mlxsw_sp2_span_trigger_global_ops = {
+ .bind = mlxsw_sp2_span_trigger_global_bind,
+ .unbind = mlxsw_sp2_span_trigger_global_unbind,
+ .matches = mlxsw_sp2_span_trigger_global_matches,
+ .enable = mlxsw_sp2_span_trigger_global_enable,
+ .disable = mlxsw_sp2_span_trigger_global_disable,
+};
+
+static const struct mlxsw_sp_span_trigger_ops *
+mlxsw_sp2_span_trigger_ops_arr[] = {
+ [MLXSW_SP_SPAN_TRIGGER_TYPE_PORT] = &mlxsw_sp_span_trigger_port_ops,
+ [MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL] =
+ &mlxsw_sp2_span_trigger_global_ops,
+};
+
+static void
+mlxsw_sp_span_trigger_ops_set(struct mlxsw_sp_span_trigger_entry *trigger_entry)
+{
+ struct mlxsw_sp_span *span = trigger_entry->span;
+ enum mlxsw_sp_span_trigger_type type;
+
+ switch (trigger_entry->trigger) {
+ case MLXSW_SP_SPAN_TRIGGER_INGRESS: /* fall-through */
+ case MLXSW_SP_SPAN_TRIGGER_EGRESS:
+ type = MLXSW_SP_SPAN_TRIGGER_TYPE_PORT;
+ break;
+ case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP: /* fall-through */
+ case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP: /* fall-through */
+ case MLXSW_SP_SPAN_TRIGGER_ECN:
+ type = MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return;
+ }
+
+ trigger_entry->ops = span->span_trigger_ops_arr[type];
}
static struct mlxsw_sp_span_trigger_entry *
@@ -1106,12 +1555,15 @@ mlxsw_sp_span_trigger_entry_create(struct mlxsw_sp_span *span,
return ERR_PTR(-ENOMEM);
refcount_set(&trigger_entry->ref_count, 1);
- trigger_entry->local_port = mlxsw_sp_port->local_port;
+ trigger_entry->local_port = mlxsw_sp_port ? mlxsw_sp_port->local_port :
+ 0;
trigger_entry->trigger = trigger;
memcpy(&trigger_entry->parms, parms, sizeof(trigger_entry->parms));
+ trigger_entry->span = span;
+ mlxsw_sp_span_trigger_ops_set(trigger_entry);
list_add_tail(&trigger_entry->list, &span->trigger_entries_list);
- err = mlxsw_sp_span_trigger_entry_bind(span, trigger_entry);
+ err = trigger_entry->ops->bind(trigger_entry);
if (err)
goto err_trigger_entry_bind;
@@ -1128,7 +1580,7 @@ mlxsw_sp_span_trigger_entry_destroy(struct mlxsw_sp_span *span,
struct mlxsw_sp_span_trigger_entry *
trigger_entry)
{
- mlxsw_sp_span_trigger_entry_unbind(span, trigger_entry);
+ trigger_entry->ops->unbind(trigger_entry);
list_del(&trigger_entry->list);
kfree(trigger_entry);
}
@@ -1141,8 +1593,8 @@ mlxsw_sp_span_trigger_entry_find(struct mlxsw_sp_span *span,
struct mlxsw_sp_span_trigger_entry *trigger_entry;
list_for_each_entry(trigger_entry, &span->trigger_entries_list, list) {
- if (trigger_entry->trigger == trigger &&
- trigger_entry->local_port == mlxsw_sp_port->local_port)
+ if (trigger_entry->ops->matches(trigger_entry, trigger,
+ mlxsw_sp_port))
return trigger_entry;
}
@@ -1207,3 +1659,138 @@ void mlxsw_sp_span_agent_unbind(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_span_trigger_entry_destroy(mlxsw_sp->span, trigger_entry);
}
+
+int mlxsw_sp_span_trigger_enable(struct mlxsw_sp_port *mlxsw_sp_port,
+ enum mlxsw_sp_span_trigger trigger, u8 tc)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct mlxsw_sp_span_trigger_entry *trigger_entry;
+
+ ASSERT_RTNL();
+
+ trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
+ trigger,
+ mlxsw_sp_port);
+ if (WARN_ON_ONCE(!trigger_entry))
+ return -EINVAL;
+
+ return trigger_entry->ops->enable(trigger_entry, mlxsw_sp_port, tc);
+}
+
+void mlxsw_sp_span_trigger_disable(struct mlxsw_sp_port *mlxsw_sp_port,
+ enum mlxsw_sp_span_trigger trigger, u8 tc)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct mlxsw_sp_span_trigger_entry *trigger_entry;
+
+ ASSERT_RTNL();
+
+ trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
+ trigger,
+ mlxsw_sp_port);
+ if (WARN_ON_ONCE(!trigger_entry))
+ return;
+
+ return trigger_entry->ops->disable(trigger_entry, mlxsw_sp_port, tc);
+}
+
+static int mlxsw_sp1_span_init(struct mlxsw_sp *mlxsw_sp)
+{
+ size_t arr_size = ARRAY_SIZE(mlxsw_sp1_span_entry_ops_arr);
+
+ /* Must be first to avoid NULL pointer dereference by subsequent
+ * can_handle() callbacks.
+ */
+ if (WARN_ON(mlxsw_sp1_span_entry_ops_arr[0] !=
+ &mlxsw_sp1_span_entry_ops_cpu))
+ return -EINVAL;
+
+ mlxsw_sp->span->span_trigger_ops_arr = mlxsw_sp1_span_trigger_ops_arr;
+ mlxsw_sp->span->span_entry_ops_arr = mlxsw_sp1_span_entry_ops_arr;
+ mlxsw_sp->span->span_entry_ops_arr_size = arr_size;
+
+ return 0;
+}
+
+static u32 mlxsw_sp1_span_buffsize_get(int mtu, u32 speed)
+{
+ return mtu * 5 / 2;
+}
+
+static int mlxsw_sp1_span_policer_id_base_set(struct mlxsw_sp *mlxsw_sp,
+ u16 policer_id_base)
+{
+ return -EOPNOTSUPP;
+}
+
+const struct mlxsw_sp_span_ops mlxsw_sp1_span_ops = {
+ .init = mlxsw_sp1_span_init,
+ .buffsize_get = mlxsw_sp1_span_buffsize_get,
+ .policer_id_base_set = mlxsw_sp1_span_policer_id_base_set,
+};
+
+static int mlxsw_sp2_span_init(struct mlxsw_sp *mlxsw_sp)
+{
+ size_t arr_size = ARRAY_SIZE(mlxsw_sp2_span_entry_ops_arr);
+
+ /* Must be first to avoid NULL pointer dereference by subsequent
+ * can_handle() callbacks.
+ */
+ if (WARN_ON(mlxsw_sp2_span_entry_ops_arr[0] !=
+ &mlxsw_sp2_span_entry_ops_cpu))
+ return -EINVAL;
+
+ mlxsw_sp->span->span_trigger_ops_arr = mlxsw_sp2_span_trigger_ops_arr;
+ mlxsw_sp->span->span_entry_ops_arr = mlxsw_sp2_span_entry_ops_arr;
+ mlxsw_sp->span->span_entry_ops_arr_size = arr_size;
+
+ return 0;
+}
+
+#define MLXSW_SP2_SPAN_EG_MIRROR_BUFFER_FACTOR 38
+#define MLXSW_SP3_SPAN_EG_MIRROR_BUFFER_FACTOR 50
+
+static u32 __mlxsw_sp_span_buffsize_get(int mtu, u32 speed, u32 buffer_factor)
+{
+ return 3 * mtu + buffer_factor * speed / 1000;
+}
+
+static u32 mlxsw_sp2_span_buffsize_get(int mtu, u32 speed)
+{
+ int factor = MLXSW_SP2_SPAN_EG_MIRROR_BUFFER_FACTOR;
+
+ return __mlxsw_sp_span_buffsize_get(mtu, speed, factor);
+}
+
+static int mlxsw_sp2_span_policer_id_base_set(struct mlxsw_sp *mlxsw_sp,
+ u16 policer_id_base)
+{
+ char mogcr_pl[MLXSW_REG_MOGCR_LEN];
+ int err;
+
+ err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl);
+ if (err)
+ return err;
+
+ mlxsw_reg_mogcr_mirroring_pid_base_set(mogcr_pl, policer_id_base);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl);
+}
+
+const struct mlxsw_sp_span_ops mlxsw_sp2_span_ops = {
+ .init = mlxsw_sp2_span_init,
+ .buffsize_get = mlxsw_sp2_span_buffsize_get,
+ .policer_id_base_set = mlxsw_sp2_span_policer_id_base_set,
+};
+
+static u32 mlxsw_sp3_span_buffsize_get(int mtu, u32 speed)
+{
+ int factor = MLXSW_SP3_SPAN_EG_MIRROR_BUFFER_FACTOR;
+
+ return __mlxsw_sp_span_buffsize_get(mtu, speed, factor);
+}
+
+const struct mlxsw_sp_span_ops mlxsw_sp3_span_ops = {
+ .init = mlxsw_sp2_span_init,
+ .buffsize_get = mlxsw_sp3_span_buffsize_get,
+ .policer_id_base_set = mlxsw_sp2_span_policer_id_base_set,
+};
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h
index 9f6dd2d0f4e6..1c746dd3b1bd 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h
@@ -21,19 +21,37 @@ struct mlxsw_sp_span_parms {
union mlxsw_sp_l3addr daddr;
union mlxsw_sp_l3addr saddr;
u16 vid;
+ u16 policer_id;
+ bool policer_enable;
};
enum mlxsw_sp_span_trigger {
MLXSW_SP_SPAN_TRIGGER_INGRESS,
MLXSW_SP_SPAN_TRIGGER_EGRESS,
+ MLXSW_SP_SPAN_TRIGGER_TAIL_DROP,
+ MLXSW_SP_SPAN_TRIGGER_EARLY_DROP,
+ MLXSW_SP_SPAN_TRIGGER_ECN,
};
struct mlxsw_sp_span_trigger_parms {
int span_id;
};
+struct mlxsw_sp_span_agent_parms {
+ const struct net_device *to_dev;
+ u16 policer_id;
+ bool policer_enable;
+};
+
struct mlxsw_sp_span_entry_ops;
+struct mlxsw_sp_span_ops {
+ int (*init)(struct mlxsw_sp *mlxsw_sp);
+ u32 (*buffsize_get)(int mtu, u32 speed);
+ int (*policer_id_base_set)(struct mlxsw_sp *mlxsw_sp,
+ u16 policer_id_base);
+};
+
struct mlxsw_sp_span_entry {
const struct net_device *to_dev;
const struct mlxsw_sp_span_entry_ops *ops;
@@ -44,7 +62,8 @@ struct mlxsw_sp_span_entry {
struct mlxsw_sp_span_entry_ops {
bool (*can_handle)(const struct net_device *to_dev);
- int (*parms_set)(const struct net_device *to_dev,
+ int (*parms_set)(struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *to_dev,
struct mlxsw_sp_span_parms *sparmsp);
int (*configure)(struct mlxsw_sp_span_entry *span_entry,
struct mlxsw_sp_span_parms sparms);
@@ -65,8 +84,8 @@ void mlxsw_sp_span_entry_invalidate(struct mlxsw_sp *mlxsw_sp,
int mlxsw_sp_span_port_mtu_update(struct mlxsw_sp_port *port, u16 mtu);
void mlxsw_sp_span_speed_update_work(struct work_struct *work);
-int mlxsw_sp_span_agent_get(struct mlxsw_sp *mlxsw_sp,
- const struct net_device *to_dev, int *p_span_id);
+int mlxsw_sp_span_agent_get(struct mlxsw_sp *mlxsw_sp, int *p_span_id,
+ const struct mlxsw_sp_span_agent_parms *parms);
void mlxsw_sp_span_agent_put(struct mlxsw_sp *mlxsw_sp, int span_id);
int mlxsw_sp_span_analyzed_port_get(struct mlxsw_sp_port *mlxsw_sp_port,
bool ingress);
@@ -81,5 +100,13 @@ mlxsw_sp_span_agent_unbind(struct mlxsw_sp *mlxsw_sp,
enum mlxsw_sp_span_trigger trigger,
struct mlxsw_sp_port *mlxsw_sp_port,
const struct mlxsw_sp_span_trigger_parms *parms);
+int mlxsw_sp_span_trigger_enable(struct mlxsw_sp_port *mlxsw_sp_port,
+ enum mlxsw_sp_span_trigger trigger, u8 tc);
+void mlxsw_sp_span_trigger_disable(struct mlxsw_sp_port *mlxsw_sp_port,
+ enum mlxsw_sp_span_trigger trigger, u8 tc);
+
+extern const struct mlxsw_sp_span_ops mlxsw_sp1_span_ops;
+extern const struct mlxsw_sp_span_ops mlxsw_sp2_span_ops;
+extern const struct mlxsw_sp_span_ops mlxsw_sp3_span_ops;
#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c
index 157a42c63066..2e41c5519c1b 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c
@@ -21,6 +21,7 @@ struct mlxsw_sp_trap_group_item {
struct devlink_trap_group group;
u16 hw_group_id;
u8 priority;
+ u8 fixed_policer:1; /* Whether policer binding can change */
};
#define MLXSW_SP_TRAP_LISTENERS_MAX 3
@@ -28,6 +29,7 @@ struct mlxsw_sp_trap_group_item {
struct mlxsw_sp_trap_item {
struct devlink_trap trap;
struct mlxsw_listener listeners_arr[MLXSW_SP_TRAP_LISTENERS_MAX];
+ u8 is_source:1;
};
/* All driver-specific traps must be documented in
@@ -46,6 +48,11 @@ enum {
#define MLXSW_SP_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT
+enum {
+ /* Packet was early dropped. */
+ MLXSW_SP_MIRROR_REASON_INGRESS_WRED = 9,
+};
+
static int mlxsw_sp_rx_listener(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
u8 local_port,
struct mlxsw_sp_port *mlxsw_sp_port)
@@ -222,6 +229,11 @@ static void mlxsw_sp_rx_sample_listener(struct sk_buff *skb, u8 local_port,
DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
MLXSW_SP_TRAP_METADATA | (_metadata))
+#define MLXSW_SP_TRAP_BUFFER_DROP(_id) \
+ DEVLINK_TRAP_GENERIC(DROP, TRAP, _id, \
+ DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS, \
+ MLXSW_SP_TRAP_METADATA)
+
#define MLXSW_SP_TRAP_DRIVER_DROP(_id, _group_id) \
DEVLINK_TRAP_DRIVER(DROP, DROP, DEVLINK_MLXSW_TRAP_ID_##_id, \
DEVLINK_MLXSW_TRAP_NAME_##_id, \
@@ -248,6 +260,10 @@ static void mlxsw_sp_rx_sample_listener(struct sk_buff *skb, u8 local_port,
TRAP_EXCEPTION_TO_CPU, false, SP_##_en_group_id, \
SET_FW_DEFAULT, SP_##_dis_group_id)
+#define MLXSW_SP_RXL_BUFFER_DISCARD(_mirror_reason) \
+ MLXSW_RXL_MIRROR(mlxsw_sp_rx_drop_listener, 0, SP_BUFFER_DISCARDS, \
+ MLXSW_SP_MIRROR_REASON_##_mirror_reason)
+
#define MLXSW_SP_RXL_EXCEPTION(_id, _group_id, _action) \
MLXSW_RXL(mlxsw_sp_rx_mark_listener, _id, \
_action, false, SP_##_group_id, SET_FW_DEFAULT)
@@ -328,6 +344,12 @@ mlxsw_sp_trap_policer_items_arr[] = {
{
.policer = MLXSW_SP_TRAP_POLICER(18, 1024, 128),
},
+ {
+ .policer = MLXSW_SP_TRAP_POLICER(19, 1024, 512),
+ },
+ {
+ .policer = MLXSW_SP_TRAP_POLICER(20, 10240, 4096),
+ },
};
static const struct mlxsw_sp_trap_group_item mlxsw_sp_trap_group_items_arr[] = {
@@ -422,6 +444,11 @@ static const struct mlxsw_sp_trap_group_item mlxsw_sp_trap_group_items_arr[] = {
.priority = 2,
},
{
+ .group = DEVLINK_TRAP_GROUP_GENERIC(EXTERNAL_DELIVERY, 19),
+ .hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_EXTERNAL_ROUTE,
+ .priority = 1,
+ },
+ {
.group = DEVLINK_TRAP_GROUP_GENERIC(IPV6, 15),
.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6,
.priority = 2,
@@ -882,11 +909,11 @@ static const struct mlxsw_sp_trap_item mlxsw_sp_trap_items_arr[] = {
},
},
{
- .trap = MLXSW_SP_TRAP_CONTROL(EXTERNAL_ROUTE, LOCAL_DELIVERY,
+ .trap = MLXSW_SP_TRAP_CONTROL(EXTERNAL_ROUTE, EXTERNAL_DELIVERY,
TRAP),
.listeners_arr = {
- MLXSW_SP_RXL_MARK(RTR_INGRESS0, IP2ME, TRAP_TO_CPU,
- false),
+ MLXSW_SP_RXL_MARK(RTR_INGRESS0, EXTERNAL_ROUTE,
+ TRAP_TO_CPU, false),
},
},
{
@@ -1055,10 +1082,10 @@ static int mlxsw_sp_trap_dummy_group_init(struct mlxsw_sp *mlxsw_sp)
static int mlxsw_sp_trap_policer_items_arr_init(struct mlxsw_sp *mlxsw_sp)
{
+ size_t arr_size = ARRAY_SIZE(mlxsw_sp_trap_policer_items_arr);
size_t elem_size = sizeof(struct mlxsw_sp_trap_policer_item);
- u64 arr_size = ARRAY_SIZE(mlxsw_sp_trap_policer_items_arr);
struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
- u64 free_policers = 0;
+ size_t free_policers = 0;
u32 last_id;
int i;
@@ -1151,6 +1178,43 @@ static void mlxsw_sp_trap_policers_fini(struct mlxsw_sp *mlxsw_sp)
mlxsw_sp_trap_policer_items_arr_fini(mlxsw_sp);
}
+static int mlxsw_sp_trap_group_items_arr_init(struct mlxsw_sp *mlxsw_sp)
+{
+ size_t common_groups_count = ARRAY_SIZE(mlxsw_sp_trap_group_items_arr);
+ const struct mlxsw_sp_trap_group_item *spec_group_items_arr;
+ size_t elem_size = sizeof(struct mlxsw_sp_trap_group_item);
+ struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
+ size_t groups_count, spec_groups_count;
+ int err;
+
+ err = mlxsw_sp->trap_ops->groups_init(mlxsw_sp, &spec_group_items_arr,
+ &spec_groups_count);
+ if (err)
+ return err;
+
+ /* The group items array is created by concatenating the common trap
+ * group items and the ASIC-specific trap group items.
+ */
+ groups_count = common_groups_count + spec_groups_count;
+ trap->group_items_arr = kcalloc(groups_count, elem_size, GFP_KERNEL);
+ if (!trap->group_items_arr)
+ return -ENOMEM;
+
+ memcpy(trap->group_items_arr, mlxsw_sp_trap_group_items_arr,
+ elem_size * common_groups_count);
+ memcpy(trap->group_items_arr + common_groups_count,
+ spec_group_items_arr, elem_size * spec_groups_count);
+
+ trap->groups_count = groups_count;
+
+ return 0;
+}
+
+static void mlxsw_sp_trap_group_items_arr_fini(struct mlxsw_sp *mlxsw_sp)
+{
+ kfree(mlxsw_sp->trap->group_items_arr);
+}
+
static int mlxsw_sp_trap_groups_init(struct mlxsw_sp *mlxsw_sp)
{
struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
@@ -1158,13 +1222,9 @@ static int mlxsw_sp_trap_groups_init(struct mlxsw_sp *mlxsw_sp)
struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
int err, i;
- trap->group_items_arr = kmemdup(mlxsw_sp_trap_group_items_arr,
- sizeof(mlxsw_sp_trap_group_items_arr),
- GFP_KERNEL);
- if (!trap->group_items_arr)
- return -ENOMEM;
-
- trap->groups_count = ARRAY_SIZE(mlxsw_sp_trap_group_items_arr);
+ err = mlxsw_sp_trap_group_items_arr_init(mlxsw_sp);
+ if (err)
+ return err;
for (i = 0; i < trap->groups_count; i++) {
group_item = &trap->group_items_arr[i];
@@ -1181,7 +1241,7 @@ err_trap_group_register:
group_item = &trap->group_items_arr[i];
devlink_trap_groups_unregister(devlink, &group_item->group, 1);
}
- kfree(trap->group_items_arr);
+ mlxsw_sp_trap_group_items_arr_fini(mlxsw_sp);
return err;
}
@@ -1197,7 +1257,7 @@ static void mlxsw_sp_trap_groups_fini(struct mlxsw_sp *mlxsw_sp)
group_item = &trap->group_items_arr[i];
devlink_trap_groups_unregister(devlink, &group_item->group, 1);
}
- kfree(trap->group_items_arr);
+ mlxsw_sp_trap_group_items_arr_fini(mlxsw_sp);
}
static bool
@@ -1206,6 +1266,43 @@ mlxsw_sp_trap_listener_is_valid(const struct mlxsw_listener *listener)
return listener->trap_id != 0;
}
+static int mlxsw_sp_trap_items_arr_init(struct mlxsw_sp *mlxsw_sp)
+{
+ size_t common_traps_count = ARRAY_SIZE(mlxsw_sp_trap_items_arr);
+ const struct mlxsw_sp_trap_item *spec_trap_items_arr;
+ size_t elem_size = sizeof(struct mlxsw_sp_trap_item);
+ struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
+ size_t traps_count, spec_traps_count;
+ int err;
+
+ err = mlxsw_sp->trap_ops->traps_init(mlxsw_sp, &spec_trap_items_arr,
+ &spec_traps_count);
+ if (err)
+ return err;
+
+ /* The trap items array is created by concatenating the common trap
+ * items and the ASIC-specific trap items.
+ */
+ traps_count = common_traps_count + spec_traps_count;
+ trap->trap_items_arr = kcalloc(traps_count, elem_size, GFP_KERNEL);
+ if (!trap->trap_items_arr)
+ return -ENOMEM;
+
+ memcpy(trap->trap_items_arr, mlxsw_sp_trap_items_arr,
+ elem_size * common_traps_count);
+ memcpy(trap->trap_items_arr + common_traps_count,
+ spec_trap_items_arr, elem_size * spec_traps_count);
+
+ trap->traps_count = traps_count;
+
+ return 0;
+}
+
+static void mlxsw_sp_trap_items_arr_fini(struct mlxsw_sp *mlxsw_sp)
+{
+ kfree(mlxsw_sp->trap->trap_items_arr);
+}
+
static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp)
{
struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
@@ -1213,13 +1310,9 @@ static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp)
const struct mlxsw_sp_trap_item *trap_item;
int err, i;
- trap->trap_items_arr = kmemdup(mlxsw_sp_trap_items_arr,
- sizeof(mlxsw_sp_trap_items_arr),
- GFP_KERNEL);
- if (!trap->trap_items_arr)
- return -ENOMEM;
-
- trap->traps_count = ARRAY_SIZE(mlxsw_sp_trap_items_arr);
+ err = mlxsw_sp_trap_items_arr_init(mlxsw_sp);
+ if (err)
+ return err;
for (i = 0; i < trap->traps_count; i++) {
trap_item = &trap->trap_items_arr[i];
@@ -1236,7 +1329,7 @@ err_trap_register:
trap_item = &trap->trap_items_arr[i];
devlink_traps_unregister(devlink, &trap_item->trap, 1);
}
- kfree(trap->trap_items_arr);
+ mlxsw_sp_trap_items_arr_fini(mlxsw_sp);
return err;
}
@@ -1252,7 +1345,7 @@ static void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp)
trap_item = &trap->trap_items_arr[i];
devlink_traps_unregister(devlink, &trap_item->trap, 1);
}
- kfree(trap->trap_items_arr);
+ mlxsw_sp_trap_items_arr_fini(mlxsw_sp);
}
int mlxsw_sp_devlink_traps_init(struct mlxsw_sp *mlxsw_sp)
@@ -1344,7 +1437,8 @@ void mlxsw_sp_trap_fini(struct mlxsw_core *mlxsw_core,
int mlxsw_sp_trap_action_set(struct mlxsw_core *mlxsw_core,
const struct devlink_trap *trap,
- enum devlink_trap_action action)
+ enum devlink_trap_action action,
+ struct netlink_ext_ack *extack)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
const struct mlxsw_sp_trap_item *trap_item;
@@ -1354,6 +1448,11 @@ int mlxsw_sp_trap_action_set(struct mlxsw_core *mlxsw_core,
if (WARN_ON(!trap_item))
return -EINVAL;
+ if (trap_item->is_source) {
+ NL_SET_ERR_MSG_MOD(extack, "Changing the action of source traps is not supported");
+ return -EOPNOTSUPP;
+ }
+
for (i = 0; i < MLXSW_SP_TRAP_LISTENERS_MAX; i++) {
const struct mlxsw_listener *listener;
bool enabled;
@@ -1384,7 +1483,7 @@ int mlxsw_sp_trap_action_set(struct mlxsw_core *mlxsw_core,
static int
__mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group,
- u32 policer_id)
+ u32 policer_id, struct netlink_ext_ack *extack)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
u16 hw_policer_id = MLXSW_REG_HTGT_INVALID_POLICER;
@@ -1395,6 +1494,11 @@ __mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
if (WARN_ON(!group_item))
return -EINVAL;
+ if (group_item->fixed_policer && policer_id != group->init_policer_id) {
+ NL_SET_ERR_MSG_MOD(extack, "Changing the policer binding of this group is not supported");
+ return -EOPNOTSUPP;
+ }
+
if (policer_id) {
struct mlxsw_sp_trap_policer_item *policer_item;
@@ -1414,16 +1518,18 @@ int mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group)
{
return __mlxsw_sp_trap_group_init(mlxsw_core, group,
- group->init_policer_id);
+ group->init_policer_id, NULL);
}
int mlxsw_sp_trap_group_set(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group,
- const struct devlink_trap_policer *policer)
+ const struct devlink_trap_policer *policer,
+ struct netlink_ext_ack *extack)
{
u32 policer_id = policer ? policer->id : 0;
- return __mlxsw_sp_trap_group_init(mlxsw_core, group, policer_id);
+ return __mlxsw_sp_trap_group_init(mlxsw_core, group, policer_id,
+ extack);
}
static int
@@ -1568,3 +1674,110 @@ mlxsw_sp_trap_policer_counter_get(struct mlxsw_core *mlxsw_core,
return 0;
}
+
+int mlxsw_sp_trap_group_policer_hw_id_get(struct mlxsw_sp *mlxsw_sp, u16 id,
+ bool *p_enabled, u16 *p_hw_id)
+{
+ struct mlxsw_sp_trap_policer_item *pol_item;
+ struct mlxsw_sp_trap_group_item *gr_item;
+ u32 pol_id;
+
+ gr_item = mlxsw_sp_trap_group_item_lookup(mlxsw_sp, id);
+ if (!gr_item)
+ return -ENOENT;
+
+ pol_id = gr_item->group.init_policer_id;
+ if (!pol_id) {
+ *p_enabled = false;
+ return 0;
+ }
+
+ pol_item = mlxsw_sp_trap_policer_item_lookup(mlxsw_sp, pol_id);
+ if (WARN_ON(!pol_item))
+ return -ENOENT;
+
+ *p_enabled = true;
+ *p_hw_id = pol_item->hw_id;
+ return 0;
+}
+
+static const struct mlxsw_sp_trap_group_item
+mlxsw_sp1_trap_group_items_arr[] = {
+};
+
+static const struct mlxsw_sp_trap_item
+mlxsw_sp1_trap_items_arr[] = {
+};
+
+static int
+mlxsw_sp1_trap_groups_init(struct mlxsw_sp *mlxsw_sp,
+ const struct mlxsw_sp_trap_group_item **arr,
+ size_t *p_groups_count)
+{
+ *arr = mlxsw_sp1_trap_group_items_arr;
+ *p_groups_count = ARRAY_SIZE(mlxsw_sp1_trap_group_items_arr);
+
+ return 0;
+}
+
+static int mlxsw_sp1_traps_init(struct mlxsw_sp *mlxsw_sp,
+ const struct mlxsw_sp_trap_item **arr,
+ size_t *p_traps_count)
+{
+ *arr = mlxsw_sp1_trap_items_arr;
+ *p_traps_count = ARRAY_SIZE(mlxsw_sp1_trap_items_arr);
+
+ return 0;
+}
+
+const struct mlxsw_sp_trap_ops mlxsw_sp1_trap_ops = {
+ .groups_init = mlxsw_sp1_trap_groups_init,
+ .traps_init = mlxsw_sp1_traps_init,
+};
+
+static const struct mlxsw_sp_trap_group_item
+mlxsw_sp2_trap_group_items_arr[] = {
+ {
+ .group = DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS, 20),
+ .hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_BUFFER_DISCARDS,
+ .priority = 0,
+ .fixed_policer = true,
+ },
+};
+
+static const struct mlxsw_sp_trap_item
+mlxsw_sp2_trap_items_arr[] = {
+ {
+ .trap = MLXSW_SP_TRAP_BUFFER_DROP(EARLY_DROP),
+ .listeners_arr = {
+ MLXSW_SP_RXL_BUFFER_DISCARD(INGRESS_WRED),
+ },
+ .is_source = true,
+ },
+};
+
+static int
+mlxsw_sp2_trap_groups_init(struct mlxsw_sp *mlxsw_sp,
+ const struct mlxsw_sp_trap_group_item **arr,
+ size_t *p_groups_count)
+{
+ *arr = mlxsw_sp2_trap_group_items_arr;
+ *p_groups_count = ARRAY_SIZE(mlxsw_sp2_trap_group_items_arr);
+
+ return 0;
+}
+
+static int mlxsw_sp2_traps_init(struct mlxsw_sp *mlxsw_sp,
+ const struct mlxsw_sp_trap_item **arr,
+ size_t *p_traps_count)
+{
+ *arr = mlxsw_sp2_trap_items_arr;
+ *p_traps_count = ARRAY_SIZE(mlxsw_sp2_trap_items_arr);
+
+ return 0;
+}
+
+const struct mlxsw_sp_trap_ops mlxsw_sp2_trap_ops = {
+ .groups_init = mlxsw_sp2_trap_groups_init,
+ .traps_init = mlxsw_sp2_traps_init,
+};
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.h
index 13ac412f4d53..b8df684bedef 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.h
@@ -9,13 +9,13 @@
struct mlxsw_sp_trap {
struct mlxsw_sp_trap_policer_item *policer_items_arr;
- u64 policers_count; /* Number of registered policers */
+ size_t policers_count; /* Number of registered policers */
struct mlxsw_sp_trap_group_item *group_items_arr;
- u64 groups_count; /* Number of registered groups */
+ size_t groups_count; /* Number of registered groups */
struct mlxsw_sp_trap_item *trap_items_arr;
- u64 traps_count; /* Number of registered traps */
+ size_t traps_count; /* Number of registered traps */
u16 thin_policer_hw_id;
@@ -23,4 +23,16 @@ struct mlxsw_sp_trap {
unsigned long policers_usage[]; /* Usage bitmap */
};
+struct mlxsw_sp_trap_ops {
+ int (*groups_init)(struct mlxsw_sp *mlxsw_sp,
+ const struct mlxsw_sp_trap_group_item **arr,
+ size_t *p_groups_count);
+ int (*traps_init)(struct mlxsw_sp *mlxsw_sp,
+ const struct mlxsw_sp_trap_item **arr,
+ size_t *p_traps_count);
+};
+
+extern const struct mlxsw_sp_trap_ops mlxsw_sp1_trap_ops;
+extern const struct mlxsw_sp_trap_ops mlxsw_sp2_trap_ops;
+
#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchib.c b/drivers/net/ethernet/mellanox/mlxsw/switchib.c
index 4ff1e623aa76..1e561132eb1e 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/switchib.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/switchib.c
@@ -281,7 +281,7 @@ static int mlxsw_sib_port_create(struct mlxsw_sib *mlxsw_sib, u8 local_port,
int err;
err = mlxsw_core_port_init(mlxsw_sib->core, local_port,
- module + 1, false, 0,
+ module + 1, false, 0, false, 0,
mlxsw_sib->hw_id, sizeof(mlxsw_sib->hw_id));
if (err) {
dev_err(mlxsw_sib->bus_info->dev, "Port %d: Failed to init core port\n",
diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
index b438f5576e18..6f9a725662fb 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
@@ -1107,7 +1107,7 @@ static int mlxsw_sx_port_eth_create(struct mlxsw_sx *mlxsw_sx, u8 local_port,
int err;
err = mlxsw_core_port_init(mlxsw_sx->core, local_port,
- module + 1, false, 0,
+ module + 1, false, 0, false, 0,
mlxsw_sx->hw_id, sizeof(mlxsw_sx->hw_id));
if (err) {
dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to init core port\n",
diff --git a/drivers/net/ethernet/mellanox/mlxsw/trap.h b/drivers/net/ethernet/mellanox/mlxsw/trap.h
index 28e60697d14e..33909887d0ac 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/trap.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/trap.h
@@ -107,8 +107,16 @@ enum {
MLXSW_TRAP_ID_ACL2 = 0x1C2,
MLXSW_TRAP_ID_DISCARD_INGRESS_ACL = 0x1C3,
MLXSW_TRAP_ID_DISCARD_EGRESS_ACL = 0x1C4,
+ MLXSW_TRAP_ID_MIRROR_SESSION0 = 0x220,
+ MLXSW_TRAP_ID_MIRROR_SESSION1 = 0x221,
+ MLXSW_TRAP_ID_MIRROR_SESSION2 = 0x222,
+ MLXSW_TRAP_ID_MIRROR_SESSION3 = 0x223,
+ MLXSW_TRAP_ID_MIRROR_SESSION4 = 0x224,
+ MLXSW_TRAP_ID_MIRROR_SESSION5 = 0x225,
+ MLXSW_TRAP_ID_MIRROR_SESSION6 = 0x226,
+ MLXSW_TRAP_ID_MIRROR_SESSION7 = 0x227,
- MLXSW_TRAP_ID_MAX = 0x1FF
+ MLXSW_TRAP_ID_MAX = 0x3FF,
};
enum mlxsw_event_trap_id {