162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/if_bridge.h>
562306a36Sopenharmony_ci#include <linux/list.h>
662306a36Sopenharmony_ci#include <linux/mutex.h>
762306a36Sopenharmony_ci#include <linux/refcount.h>
862306a36Sopenharmony_ci#include <linux/rtnetlink.h>
962306a36Sopenharmony_ci#include <linux/workqueue.h>
1062306a36Sopenharmony_ci#include <net/arp.h>
1162306a36Sopenharmony_ci#include <net/gre.h>
1262306a36Sopenharmony_ci#include <net/lag.h>
1362306a36Sopenharmony_ci#include <net/ndisc.h>
1462306a36Sopenharmony_ci#include <net/ip6_tunnel.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "spectrum.h"
1762306a36Sopenharmony_ci#include "spectrum_ipip.h"
1862306a36Sopenharmony_ci#include "spectrum_span.h"
1962306a36Sopenharmony_ci#include "spectrum_switchdev.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistruct mlxsw_sp_span {
2262306a36Sopenharmony_ci	struct work_struct work;
2362306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
2462306a36Sopenharmony_ci	const struct mlxsw_sp_span_trigger_ops **span_trigger_ops_arr;
2562306a36Sopenharmony_ci	const struct mlxsw_sp_span_entry_ops **span_entry_ops_arr;
2662306a36Sopenharmony_ci	size_t span_entry_ops_arr_size;
2762306a36Sopenharmony_ci	struct list_head analyzed_ports_list;
2862306a36Sopenharmony_ci	struct mutex analyzed_ports_lock; /* Protects analyzed_ports_list */
2962306a36Sopenharmony_ci	struct list_head trigger_entries_list;
3062306a36Sopenharmony_ci	u16 policer_id_base;
3162306a36Sopenharmony_ci	refcount_t policer_id_base_ref_count;
3262306a36Sopenharmony_ci	atomic_t active_entries_count;
3362306a36Sopenharmony_ci	int entries_count;
3462306a36Sopenharmony_ci	struct mlxsw_sp_span_entry entries[];
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct mlxsw_sp_span_analyzed_port {
3862306a36Sopenharmony_ci	struct list_head list; /* Member of analyzed_ports_list */
3962306a36Sopenharmony_ci	refcount_t ref_count;
4062306a36Sopenharmony_ci	u16 local_port;
4162306a36Sopenharmony_ci	bool ingress;
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct mlxsw_sp_span_trigger_entry {
4562306a36Sopenharmony_ci	struct list_head list; /* Member of trigger_entries_list */
4662306a36Sopenharmony_ci	struct mlxsw_sp_span *span;
4762306a36Sopenharmony_ci	const struct mlxsw_sp_span_trigger_ops *ops;
4862306a36Sopenharmony_ci	refcount_t ref_count;
4962306a36Sopenharmony_ci	u16 local_port;
5062306a36Sopenharmony_ci	enum mlxsw_sp_span_trigger trigger;
5162306a36Sopenharmony_ci	struct mlxsw_sp_span_trigger_parms parms;
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cienum mlxsw_sp_span_trigger_type {
5562306a36Sopenharmony_ci	MLXSW_SP_SPAN_TRIGGER_TYPE_PORT,
5662306a36Sopenharmony_ci	MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL,
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct mlxsw_sp_span_trigger_ops {
6062306a36Sopenharmony_ci	int (*bind)(struct mlxsw_sp_span_trigger_entry *trigger_entry);
6162306a36Sopenharmony_ci	void (*unbind)(struct mlxsw_sp_span_trigger_entry *trigger_entry);
6262306a36Sopenharmony_ci	bool (*matches)(struct mlxsw_sp_span_trigger_entry *trigger_entry,
6362306a36Sopenharmony_ci			enum mlxsw_sp_span_trigger trigger,
6462306a36Sopenharmony_ci			struct mlxsw_sp_port *mlxsw_sp_port);
6562306a36Sopenharmony_ci	int (*enable)(struct mlxsw_sp_span_trigger_entry *trigger_entry,
6662306a36Sopenharmony_ci		      struct mlxsw_sp_port *mlxsw_sp_port, u8 tc);
6762306a36Sopenharmony_ci	void (*disable)(struct mlxsw_sp_span_trigger_entry *trigger_entry,
6862306a36Sopenharmony_ci			struct mlxsw_sp_port *mlxsw_sp_port, u8 tc);
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic void mlxsw_sp_span_respin_work(struct work_struct *work);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic u64 mlxsw_sp_span_occ_get(void *priv)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	const struct mlxsw_sp *mlxsw_sp = priv;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return atomic_read(&mlxsw_sp->span->active_entries_count);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ciint mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
8362306a36Sopenharmony_ci	struct mlxsw_sp_span *span;
8462306a36Sopenharmony_ci	int i, entries_count, err;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_SPAN))
8762306a36Sopenharmony_ci		return -EIO;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	entries_count = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_SPAN);
9062306a36Sopenharmony_ci	span = kzalloc(struct_size(span, entries, entries_count), GFP_KERNEL);
9162306a36Sopenharmony_ci	if (!span)
9262306a36Sopenharmony_ci		return -ENOMEM;
9362306a36Sopenharmony_ci	refcount_set(&span->policer_id_base_ref_count, 0);
9462306a36Sopenharmony_ci	span->entries_count = entries_count;
9562306a36Sopenharmony_ci	atomic_set(&span->active_entries_count, 0);
9662306a36Sopenharmony_ci	mutex_init(&span->analyzed_ports_lock);
9762306a36Sopenharmony_ci	INIT_LIST_HEAD(&span->analyzed_ports_list);
9862306a36Sopenharmony_ci	INIT_LIST_HEAD(&span->trigger_entries_list);
9962306a36Sopenharmony_ci	span->mlxsw_sp = mlxsw_sp;
10062306a36Sopenharmony_ci	mlxsw_sp->span = span;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	for (i = 0; i < mlxsw_sp->span->entries_count; i++)
10362306a36Sopenharmony_ci		mlxsw_sp->span->entries[i].id = i;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	err = mlxsw_sp->span_ops->init(mlxsw_sp);
10662306a36Sopenharmony_ci	if (err)
10762306a36Sopenharmony_ci		goto err_init;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	devl_resource_occ_get_register(devlink, MLXSW_SP_RESOURCE_SPAN,
11062306a36Sopenharmony_ci				       mlxsw_sp_span_occ_get, mlxsw_sp);
11162306a36Sopenharmony_ci	INIT_WORK(&span->work, mlxsw_sp_span_respin_work);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cierr_init:
11662306a36Sopenharmony_ci	mutex_destroy(&mlxsw_sp->span->analyzed_ports_lock);
11762306a36Sopenharmony_ci	kfree(mlxsw_sp->span);
11862306a36Sopenharmony_ci	return err;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_civoid mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	cancel_work_sync(&mlxsw_sp->span->work);
12662306a36Sopenharmony_ci	devl_resource_occ_get_unregister(devlink, MLXSW_SP_RESOURCE_SPAN);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	WARN_ON_ONCE(!list_empty(&mlxsw_sp->span->trigger_entries_list));
12962306a36Sopenharmony_ci	WARN_ON_ONCE(!list_empty(&mlxsw_sp->span->analyzed_ports_list));
13062306a36Sopenharmony_ci	mutex_destroy(&mlxsw_sp->span->analyzed_ports_lock);
13162306a36Sopenharmony_ci	kfree(mlxsw_sp->span);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic bool mlxsw_sp1_span_cpu_can_handle(const struct net_device *dev)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	return !dev;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic int mlxsw_sp1_span_entry_cpu_parms(struct mlxsw_sp *mlxsw_sp,
14062306a36Sopenharmony_ci					  const struct net_device *to_dev,
14162306a36Sopenharmony_ci					  struct mlxsw_sp_span_parms *sparmsp)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	return -EOPNOTSUPP;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int
14762306a36Sopenharmony_cimlxsw_sp1_span_entry_cpu_configure(struct mlxsw_sp_span_entry *span_entry,
14862306a36Sopenharmony_ci				   struct mlxsw_sp_span_parms sparms)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	return -EOPNOTSUPP;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic void
15462306a36Sopenharmony_cimlxsw_sp1_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic const
15962306a36Sopenharmony_cistruct mlxsw_sp_span_entry_ops mlxsw_sp1_span_entry_ops_cpu = {
16062306a36Sopenharmony_ci	.is_static = true,
16162306a36Sopenharmony_ci	.can_handle = mlxsw_sp1_span_cpu_can_handle,
16262306a36Sopenharmony_ci	.parms_set = mlxsw_sp1_span_entry_cpu_parms,
16362306a36Sopenharmony_ci	.configure = mlxsw_sp1_span_entry_cpu_configure,
16462306a36Sopenharmony_ci	.deconfigure = mlxsw_sp1_span_entry_cpu_deconfigure,
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic int
16862306a36Sopenharmony_cimlxsw_sp_span_entry_phys_parms(struct mlxsw_sp *mlxsw_sp,
16962306a36Sopenharmony_ci			       const struct net_device *to_dev,
17062306a36Sopenharmony_ci			       struct mlxsw_sp_span_parms *sparmsp)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	sparmsp->dest_port = netdev_priv(to_dev);
17362306a36Sopenharmony_ci	return 0;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int
17762306a36Sopenharmony_cimlxsw_sp_span_entry_phys_configure(struct mlxsw_sp_span_entry *span_entry,
17862306a36Sopenharmony_ci				   struct mlxsw_sp_span_parms sparms)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	struct mlxsw_sp_port *dest_port = sparms.dest_port;
18162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
18262306a36Sopenharmony_ci	u16 local_port = dest_port->local_port;
18362306a36Sopenharmony_ci	char mpat_pl[MLXSW_REG_MPAT_LEN];
18462306a36Sopenharmony_ci	int pa_id = span_entry->id;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* Create a new port analayzer entry for local_port. */
18762306a36Sopenharmony_ci	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
18862306a36Sopenharmony_ci			    MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
18962306a36Sopenharmony_ci	mlxsw_reg_mpat_session_id_set(mpat_pl, sparms.session_id);
19062306a36Sopenharmony_ci	mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
19162306a36Sopenharmony_ci	mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void
19762306a36Sopenharmony_cimlxsw_sp_span_entry_deconfigure_common(struct mlxsw_sp_span_entry *span_entry,
19862306a36Sopenharmony_ci				       enum mlxsw_reg_mpat_span_type span_type)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct mlxsw_sp_port *dest_port = span_entry->parms.dest_port;
20162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
20262306a36Sopenharmony_ci	u16 local_port = dest_port->local_port;
20362306a36Sopenharmony_ci	char mpat_pl[MLXSW_REG_MPAT_LEN];
20462306a36Sopenharmony_ci	int pa_id = span_entry->id;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, false, span_type);
20762306a36Sopenharmony_ci	mlxsw_reg_mpat_session_id_set(mpat_pl, span_entry->parms.session_id);
20862306a36Sopenharmony_ci	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic void
21262306a36Sopenharmony_cimlxsw_sp_span_entry_phys_deconfigure(struct mlxsw_sp_span_entry *span_entry)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	mlxsw_sp_span_entry_deconfigure_common(span_entry,
21562306a36Sopenharmony_ci					    MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic const
21962306a36Sopenharmony_cistruct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_phys = {
22062306a36Sopenharmony_ci	.is_static = true,
22162306a36Sopenharmony_ci	.can_handle = mlxsw_sp_port_dev_check,
22262306a36Sopenharmony_ci	.parms_set = mlxsw_sp_span_entry_phys_parms,
22362306a36Sopenharmony_ci	.configure = mlxsw_sp_span_entry_phys_configure,
22462306a36Sopenharmony_ci	.deconfigure = mlxsw_sp_span_entry_phys_deconfigure,
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic int mlxsw_sp_span_dmac(struct neigh_table *tbl,
22862306a36Sopenharmony_ci			      const void *pkey,
22962306a36Sopenharmony_ci			      struct net_device *dev,
23062306a36Sopenharmony_ci			      unsigned char dmac[ETH_ALEN])
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct neighbour *neigh = neigh_lookup(tbl, pkey, dev);
23362306a36Sopenharmony_ci	int err = 0;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (!neigh) {
23662306a36Sopenharmony_ci		neigh = neigh_create(tbl, pkey, dev);
23762306a36Sopenharmony_ci		if (IS_ERR(neigh))
23862306a36Sopenharmony_ci			return PTR_ERR(neigh);
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	neigh_event_send(neigh, NULL);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	read_lock_bh(&neigh->lock);
24462306a36Sopenharmony_ci	if ((neigh->nud_state & NUD_VALID) && !neigh->dead)
24562306a36Sopenharmony_ci		memcpy(dmac, neigh->ha, ETH_ALEN);
24662306a36Sopenharmony_ci	else
24762306a36Sopenharmony_ci		err = -ENOENT;
24862306a36Sopenharmony_ci	read_unlock_bh(&neigh->lock);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	neigh_release(neigh);
25162306a36Sopenharmony_ci	return err;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic int
25562306a36Sopenharmony_cimlxsw_sp_span_entry_unoffloadable(struct mlxsw_sp_span_parms *sparmsp)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	sparmsp->dest_port = NULL;
25862306a36Sopenharmony_ci	return 0;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic struct net_device *
26262306a36Sopenharmony_cimlxsw_sp_span_entry_bridge_8021q(const struct net_device *br_dev,
26362306a36Sopenharmony_ci				 unsigned char *dmac,
26462306a36Sopenharmony_ci				 u16 *p_vid)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct bridge_vlan_info vinfo;
26762306a36Sopenharmony_ci	struct net_device *edev;
26862306a36Sopenharmony_ci	u16 vid = *p_vid;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (!vid && WARN_ON(br_vlan_get_pvid(br_dev, &vid)))
27162306a36Sopenharmony_ci		return NULL;
27262306a36Sopenharmony_ci	if (!vid || br_vlan_get_info(br_dev, vid, &vinfo) ||
27362306a36Sopenharmony_ci	    !(vinfo.flags & BRIDGE_VLAN_INFO_BRENTRY))
27462306a36Sopenharmony_ci		return NULL;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	edev = br_fdb_find_port(br_dev, dmac, vid);
27762306a36Sopenharmony_ci	if (!edev)
27862306a36Sopenharmony_ci		return NULL;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (br_vlan_get_info(edev, vid, &vinfo))
28162306a36Sopenharmony_ci		return NULL;
28262306a36Sopenharmony_ci	if (vinfo.flags & BRIDGE_VLAN_INFO_UNTAGGED)
28362306a36Sopenharmony_ci		*p_vid = 0;
28462306a36Sopenharmony_ci	else
28562306a36Sopenharmony_ci		*p_vid = vid;
28662306a36Sopenharmony_ci	return edev;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic struct net_device *
29062306a36Sopenharmony_cimlxsw_sp_span_entry_bridge_8021d(const struct net_device *br_dev,
29162306a36Sopenharmony_ci				 unsigned char *dmac)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	return br_fdb_find_port(br_dev, dmac, 0);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic struct net_device *
29762306a36Sopenharmony_cimlxsw_sp_span_entry_bridge(const struct net_device *br_dev,
29862306a36Sopenharmony_ci			   unsigned char dmac[ETH_ALEN],
29962306a36Sopenharmony_ci			   u16 *p_vid)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct mlxsw_sp_bridge_port *bridge_port;
30262306a36Sopenharmony_ci	enum mlxsw_reg_spms_state spms_state;
30362306a36Sopenharmony_ci	struct net_device *dev = NULL;
30462306a36Sopenharmony_ci	struct mlxsw_sp_port *port;
30562306a36Sopenharmony_ci	u8 stp_state;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (br_vlan_enabled(br_dev))
30862306a36Sopenharmony_ci		dev = mlxsw_sp_span_entry_bridge_8021q(br_dev, dmac, p_vid);
30962306a36Sopenharmony_ci	else if (!*p_vid)
31062306a36Sopenharmony_ci		dev = mlxsw_sp_span_entry_bridge_8021d(br_dev, dmac);
31162306a36Sopenharmony_ci	if (!dev)
31262306a36Sopenharmony_ci		return NULL;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	port = mlxsw_sp_port_dev_lower_find(dev);
31562306a36Sopenharmony_ci	if (!port)
31662306a36Sopenharmony_ci		return NULL;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	bridge_port = mlxsw_sp_bridge_port_find(port->mlxsw_sp->bridge, dev);
31962306a36Sopenharmony_ci	if (!bridge_port)
32062306a36Sopenharmony_ci		return NULL;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	stp_state = mlxsw_sp_bridge_port_stp_state(bridge_port);
32362306a36Sopenharmony_ci	spms_state = mlxsw_sp_stp_spms_state(stp_state);
32462306a36Sopenharmony_ci	if (spms_state != MLXSW_REG_SPMS_STATE_FORWARDING)
32562306a36Sopenharmony_ci		return NULL;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return dev;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic struct net_device *
33162306a36Sopenharmony_cimlxsw_sp_span_entry_vlan(const struct net_device *vlan_dev,
33262306a36Sopenharmony_ci			 u16 *p_vid)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	*p_vid = vlan_dev_vlan_id(vlan_dev);
33562306a36Sopenharmony_ci	return vlan_dev_real_dev(vlan_dev);
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic struct net_device *
33962306a36Sopenharmony_cimlxsw_sp_span_entry_lag(struct net_device *lag_dev)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct net_device *dev;
34262306a36Sopenharmony_ci	struct list_head *iter;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	netdev_for_each_lower_dev(lag_dev, dev, iter)
34562306a36Sopenharmony_ci		if (netif_carrier_ok(dev) &&
34662306a36Sopenharmony_ci		    net_lag_port_dev_txable(dev) &&
34762306a36Sopenharmony_ci		    mlxsw_sp_port_dev_check(dev))
34862306a36Sopenharmony_ci			return dev;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	return NULL;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic __maybe_unused int
35462306a36Sopenharmony_cimlxsw_sp_span_entry_tunnel_parms_common(struct net_device *edev,
35562306a36Sopenharmony_ci					union mlxsw_sp_l3addr saddr,
35662306a36Sopenharmony_ci					union mlxsw_sp_l3addr daddr,
35762306a36Sopenharmony_ci					union mlxsw_sp_l3addr gw,
35862306a36Sopenharmony_ci					__u8 ttl,
35962306a36Sopenharmony_ci					struct neigh_table *tbl,
36062306a36Sopenharmony_ci					struct mlxsw_sp_span_parms *sparmsp)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	unsigned char dmac[ETH_ALEN];
36362306a36Sopenharmony_ci	u16 vid = 0;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (mlxsw_sp_l3addr_is_zero(gw))
36662306a36Sopenharmony_ci		gw = daddr;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (!edev || mlxsw_sp_span_dmac(tbl, &gw, edev, dmac))
36962306a36Sopenharmony_ci		goto unoffloadable;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (is_vlan_dev(edev))
37262306a36Sopenharmony_ci		edev = mlxsw_sp_span_entry_vlan(edev, &vid);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (netif_is_bridge_master(edev)) {
37562306a36Sopenharmony_ci		edev = mlxsw_sp_span_entry_bridge(edev, dmac, &vid);
37662306a36Sopenharmony_ci		if (!edev)
37762306a36Sopenharmony_ci			goto unoffloadable;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (is_vlan_dev(edev)) {
38162306a36Sopenharmony_ci		if (vid || !(edev->flags & IFF_UP))
38262306a36Sopenharmony_ci			goto unoffloadable;
38362306a36Sopenharmony_ci		edev = mlxsw_sp_span_entry_vlan(edev, &vid);
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (netif_is_lag_master(edev)) {
38762306a36Sopenharmony_ci		if (!(edev->flags & IFF_UP))
38862306a36Sopenharmony_ci			goto unoffloadable;
38962306a36Sopenharmony_ci		edev = mlxsw_sp_span_entry_lag(edev);
39062306a36Sopenharmony_ci		if (!edev)
39162306a36Sopenharmony_ci			goto unoffloadable;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (!mlxsw_sp_port_dev_check(edev))
39562306a36Sopenharmony_ci		goto unoffloadable;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	sparmsp->dest_port = netdev_priv(edev);
39862306a36Sopenharmony_ci	sparmsp->ttl = ttl;
39962306a36Sopenharmony_ci	memcpy(sparmsp->dmac, dmac, ETH_ALEN);
40062306a36Sopenharmony_ci	memcpy(sparmsp->smac, edev->dev_addr, ETH_ALEN);
40162306a36Sopenharmony_ci	sparmsp->saddr = saddr;
40262306a36Sopenharmony_ci	sparmsp->daddr = daddr;
40362306a36Sopenharmony_ci	sparmsp->vid = vid;
40462306a36Sopenharmony_ci	return 0;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ciunoffloadable:
40762306a36Sopenharmony_ci	return mlxsw_sp_span_entry_unoffloadable(sparmsp);
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_IPGRE)
41162306a36Sopenharmony_cistatic struct net_device *
41262306a36Sopenharmony_cimlxsw_sp_span_gretap4_route(const struct net_device *to_dev,
41362306a36Sopenharmony_ci			    __be32 *saddrp, __be32 *daddrp)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	struct ip_tunnel *tun = netdev_priv(to_dev);
41662306a36Sopenharmony_ci	struct net_device *dev = NULL;
41762306a36Sopenharmony_ci	struct ip_tunnel_parm parms;
41862306a36Sopenharmony_ci	struct rtable *rt = NULL;
41962306a36Sopenharmony_ci	struct flowi4 fl4;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* We assume "dev" stays valid after rt is put. */
42262306a36Sopenharmony_ci	ASSERT_RTNL();
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	parms = mlxsw_sp_ipip_netdev_parms4(to_dev);
42562306a36Sopenharmony_ci	ip_tunnel_init_flow(&fl4, parms.iph.protocol, *daddrp, *saddrp,
42662306a36Sopenharmony_ci			    0, 0, dev_net(to_dev), parms.link, tun->fwmark, 0,
42762306a36Sopenharmony_ci			    0);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	rt = ip_route_output_key(tun->net, &fl4);
43062306a36Sopenharmony_ci	if (IS_ERR(rt))
43162306a36Sopenharmony_ci		return NULL;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (rt->rt_type != RTN_UNICAST)
43462306a36Sopenharmony_ci		goto out;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	dev = rt->dst.dev;
43762306a36Sopenharmony_ci	*saddrp = fl4.saddr;
43862306a36Sopenharmony_ci	if (rt->rt_gw_family == AF_INET)
43962306a36Sopenharmony_ci		*daddrp = rt->rt_gw4;
44062306a36Sopenharmony_ci	/* can not offload if route has an IPv6 gateway */
44162306a36Sopenharmony_ci	else if (rt->rt_gw_family == AF_INET6)
44262306a36Sopenharmony_ci		dev = NULL;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ciout:
44562306a36Sopenharmony_ci	ip_rt_put(rt);
44662306a36Sopenharmony_ci	return dev;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic int
45062306a36Sopenharmony_cimlxsw_sp_span_entry_gretap4_parms(struct mlxsw_sp *mlxsw_sp,
45162306a36Sopenharmony_ci				  const struct net_device *to_dev,
45262306a36Sopenharmony_ci				  struct mlxsw_sp_span_parms *sparmsp)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct ip_tunnel_parm tparm = mlxsw_sp_ipip_netdev_parms4(to_dev);
45562306a36Sopenharmony_ci	union mlxsw_sp_l3addr saddr = { .addr4 = tparm.iph.saddr };
45662306a36Sopenharmony_ci	union mlxsw_sp_l3addr daddr = { .addr4 = tparm.iph.daddr };
45762306a36Sopenharmony_ci	bool inherit_tos = tparm.iph.tos & 0x1;
45862306a36Sopenharmony_ci	bool inherit_ttl = !tparm.iph.ttl;
45962306a36Sopenharmony_ci	union mlxsw_sp_l3addr gw = daddr;
46062306a36Sopenharmony_ci	struct net_device *l3edev;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	if (!(to_dev->flags & IFF_UP) ||
46362306a36Sopenharmony_ci	    /* Reject tunnels with GRE keys, checksums, etc. */
46462306a36Sopenharmony_ci	    tparm.i_flags || tparm.o_flags ||
46562306a36Sopenharmony_ci	    /* Require a fixed TTL and a TOS copied from the mirrored packet. */
46662306a36Sopenharmony_ci	    inherit_ttl || !inherit_tos ||
46762306a36Sopenharmony_ci	    /* A destination address may not be "any". */
46862306a36Sopenharmony_ci	    mlxsw_sp_l3addr_is_zero(daddr))
46962306a36Sopenharmony_ci		return mlxsw_sp_span_entry_unoffloadable(sparmsp);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	l3edev = mlxsw_sp_span_gretap4_route(to_dev, &saddr.addr4, &gw.addr4);
47262306a36Sopenharmony_ci	return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
47362306a36Sopenharmony_ci						       tparm.iph.ttl,
47462306a36Sopenharmony_ci						       &arp_tbl, sparmsp);
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic int
47862306a36Sopenharmony_cimlxsw_sp_span_entry_gretap4_configure(struct mlxsw_sp_span_entry *span_entry,
47962306a36Sopenharmony_ci				      struct mlxsw_sp_span_parms sparms)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	struct mlxsw_sp_port *dest_port = sparms.dest_port;
48262306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
48362306a36Sopenharmony_ci	u16 local_port = dest_port->local_port;
48462306a36Sopenharmony_ci	char mpat_pl[MLXSW_REG_MPAT_LEN];
48562306a36Sopenharmony_ci	int pa_id = span_entry->id;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* Create a new port analayzer entry for local_port. */
48862306a36Sopenharmony_ci	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
48962306a36Sopenharmony_ci			    MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
49062306a36Sopenharmony_ci	mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
49162306a36Sopenharmony_ci	mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
49262306a36Sopenharmony_ci	mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
49362306a36Sopenharmony_ci	mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
49462306a36Sopenharmony_ci				    MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
49562306a36Sopenharmony_ci				    sparms.dmac, !!sparms.vid);
49662306a36Sopenharmony_ci	mlxsw_reg_mpat_eth_rspan_l3_ipv4_pack(mpat_pl,
49762306a36Sopenharmony_ci					      sparms.ttl, sparms.smac,
49862306a36Sopenharmony_ci					      be32_to_cpu(sparms.saddr.addr4),
49962306a36Sopenharmony_ci					      be32_to_cpu(sparms.daddr.addr4));
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic void
50562306a36Sopenharmony_cimlxsw_sp_span_entry_gretap4_deconfigure(struct mlxsw_sp_span_entry *span_entry)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	mlxsw_sp_span_entry_deconfigure_common(span_entry,
50862306a36Sopenharmony_ci					MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap4 = {
51262306a36Sopenharmony_ci	.can_handle = netif_is_gretap,
51362306a36Sopenharmony_ci	.parms_set = mlxsw_sp_span_entry_gretap4_parms,
51462306a36Sopenharmony_ci	.configure = mlxsw_sp_span_entry_gretap4_configure,
51562306a36Sopenharmony_ci	.deconfigure = mlxsw_sp_span_entry_gretap4_deconfigure,
51662306a36Sopenharmony_ci};
51762306a36Sopenharmony_ci#endif
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_GRE)
52062306a36Sopenharmony_cistatic struct net_device *
52162306a36Sopenharmony_cimlxsw_sp_span_gretap6_route(const struct net_device *to_dev,
52262306a36Sopenharmony_ci			    struct in6_addr *saddrp,
52362306a36Sopenharmony_ci			    struct in6_addr *daddrp)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(to_dev);
52662306a36Sopenharmony_ci	struct flowi6 fl6 = t->fl.u.ip6;
52762306a36Sopenharmony_ci	struct net_device *dev = NULL;
52862306a36Sopenharmony_ci	struct dst_entry *dst;
52962306a36Sopenharmony_ci	struct rt6_info *rt6;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/* We assume "dev" stays valid after dst is released. */
53262306a36Sopenharmony_ci	ASSERT_RTNL();
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	fl6.flowi6_mark = t->parms.fwmark;
53562306a36Sopenharmony_ci	if (!ip6_tnl_xmit_ctl(t, &fl6.saddr, &fl6.daddr))
53662306a36Sopenharmony_ci		return NULL;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	dst = ip6_route_output(t->net, NULL, &fl6);
53962306a36Sopenharmony_ci	if (!dst || dst->error)
54062306a36Sopenharmony_ci		goto out;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	rt6 = container_of(dst, struct rt6_info, dst);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	dev = dst->dev;
54562306a36Sopenharmony_ci	*saddrp = fl6.saddr;
54662306a36Sopenharmony_ci	*daddrp = rt6->rt6i_gateway;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ciout:
54962306a36Sopenharmony_ci	dst_release(dst);
55062306a36Sopenharmony_ci	return dev;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic int
55462306a36Sopenharmony_cimlxsw_sp_span_entry_gretap6_parms(struct mlxsw_sp *mlxsw_sp,
55562306a36Sopenharmony_ci				  const struct net_device *to_dev,
55662306a36Sopenharmony_ci				  struct mlxsw_sp_span_parms *sparmsp)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct __ip6_tnl_parm tparm = mlxsw_sp_ipip_netdev_parms6(to_dev);
55962306a36Sopenharmony_ci	bool inherit_tos = tparm.flags & IP6_TNL_F_USE_ORIG_TCLASS;
56062306a36Sopenharmony_ci	union mlxsw_sp_l3addr saddr = { .addr6 = tparm.laddr };
56162306a36Sopenharmony_ci	union mlxsw_sp_l3addr daddr = { .addr6 = tparm.raddr };
56262306a36Sopenharmony_ci	bool inherit_ttl = !tparm.hop_limit;
56362306a36Sopenharmony_ci	union mlxsw_sp_l3addr gw = daddr;
56462306a36Sopenharmony_ci	struct net_device *l3edev;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if (!(to_dev->flags & IFF_UP) ||
56762306a36Sopenharmony_ci	    /* Reject tunnels with GRE keys, checksums, etc. */
56862306a36Sopenharmony_ci	    tparm.i_flags || tparm.o_flags ||
56962306a36Sopenharmony_ci	    /* Require a fixed TTL and a TOS copied from the mirrored packet. */
57062306a36Sopenharmony_ci	    inherit_ttl || !inherit_tos ||
57162306a36Sopenharmony_ci	    /* A destination address may not be "any". */
57262306a36Sopenharmony_ci	    mlxsw_sp_l3addr_is_zero(daddr))
57362306a36Sopenharmony_ci		return mlxsw_sp_span_entry_unoffloadable(sparmsp);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	l3edev = mlxsw_sp_span_gretap6_route(to_dev, &saddr.addr6, &gw.addr6);
57662306a36Sopenharmony_ci	return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
57762306a36Sopenharmony_ci						       tparm.hop_limit,
57862306a36Sopenharmony_ci						       &nd_tbl, sparmsp);
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic int
58262306a36Sopenharmony_cimlxsw_sp_span_entry_gretap6_configure(struct mlxsw_sp_span_entry *span_entry,
58362306a36Sopenharmony_ci				      struct mlxsw_sp_span_parms sparms)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct mlxsw_sp_port *dest_port = sparms.dest_port;
58662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
58762306a36Sopenharmony_ci	u16 local_port = dest_port->local_port;
58862306a36Sopenharmony_ci	char mpat_pl[MLXSW_REG_MPAT_LEN];
58962306a36Sopenharmony_ci	int pa_id = span_entry->id;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	/* Create a new port analayzer entry for local_port. */
59262306a36Sopenharmony_ci	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
59362306a36Sopenharmony_ci			    MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
59462306a36Sopenharmony_ci	mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
59562306a36Sopenharmony_ci	mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
59662306a36Sopenharmony_ci	mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
59762306a36Sopenharmony_ci	mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
59862306a36Sopenharmony_ci				    MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
59962306a36Sopenharmony_ci				    sparms.dmac, !!sparms.vid);
60062306a36Sopenharmony_ci	mlxsw_reg_mpat_eth_rspan_l3_ipv6_pack(mpat_pl, sparms.ttl, sparms.smac,
60162306a36Sopenharmony_ci					      sparms.saddr.addr6,
60262306a36Sopenharmony_ci					      sparms.daddr.addr6);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic void
60862306a36Sopenharmony_cimlxsw_sp_span_entry_gretap6_deconfigure(struct mlxsw_sp_span_entry *span_entry)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	mlxsw_sp_span_entry_deconfigure_common(span_entry,
61162306a36Sopenharmony_ci					MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic const
61562306a36Sopenharmony_cistruct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap6 = {
61662306a36Sopenharmony_ci	.can_handle = netif_is_ip6gretap,
61762306a36Sopenharmony_ci	.parms_set = mlxsw_sp_span_entry_gretap6_parms,
61862306a36Sopenharmony_ci	.configure = mlxsw_sp_span_entry_gretap6_configure,
61962306a36Sopenharmony_ci	.deconfigure = mlxsw_sp_span_entry_gretap6_deconfigure,
62062306a36Sopenharmony_ci};
62162306a36Sopenharmony_ci#endif
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic bool
62462306a36Sopenharmony_cimlxsw_sp_span_vlan_can_handle(const struct net_device *dev)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	return is_vlan_dev(dev) &&
62762306a36Sopenharmony_ci	       mlxsw_sp_port_dev_check(vlan_dev_real_dev(dev));
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic int
63162306a36Sopenharmony_cimlxsw_sp_span_entry_vlan_parms(struct mlxsw_sp *mlxsw_sp,
63262306a36Sopenharmony_ci			       const struct net_device *to_dev,
63362306a36Sopenharmony_ci			       struct mlxsw_sp_span_parms *sparmsp)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	struct net_device *real_dev;
63662306a36Sopenharmony_ci	u16 vid;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	if (!(to_dev->flags & IFF_UP))
63962306a36Sopenharmony_ci		return mlxsw_sp_span_entry_unoffloadable(sparmsp);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	real_dev = mlxsw_sp_span_entry_vlan(to_dev, &vid);
64262306a36Sopenharmony_ci	sparmsp->dest_port = netdev_priv(real_dev);
64362306a36Sopenharmony_ci	sparmsp->vid = vid;
64462306a36Sopenharmony_ci	return 0;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic int
64862306a36Sopenharmony_cimlxsw_sp_span_entry_vlan_configure(struct mlxsw_sp_span_entry *span_entry,
64962306a36Sopenharmony_ci				   struct mlxsw_sp_span_parms sparms)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	struct mlxsw_sp_port *dest_port = sparms.dest_port;
65262306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
65362306a36Sopenharmony_ci	u16 local_port = dest_port->local_port;
65462306a36Sopenharmony_ci	char mpat_pl[MLXSW_REG_MPAT_LEN];
65562306a36Sopenharmony_ci	int pa_id = span_entry->id;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
65862306a36Sopenharmony_ci			    MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH);
65962306a36Sopenharmony_ci	mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
66062306a36Sopenharmony_ci	mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
66162306a36Sopenharmony_ci	mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic void
66762306a36Sopenharmony_cimlxsw_sp_span_entry_vlan_deconfigure(struct mlxsw_sp_span_entry *span_entry)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	mlxsw_sp_span_entry_deconfigure_common(span_entry,
67062306a36Sopenharmony_ci					MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH);
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic const
67462306a36Sopenharmony_cistruct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_vlan = {
67562306a36Sopenharmony_ci	.can_handle = mlxsw_sp_span_vlan_can_handle,
67662306a36Sopenharmony_ci	.parms_set = mlxsw_sp_span_entry_vlan_parms,
67762306a36Sopenharmony_ci	.configure = mlxsw_sp_span_entry_vlan_configure,
67862306a36Sopenharmony_ci	.deconfigure = mlxsw_sp_span_entry_vlan_deconfigure,
67962306a36Sopenharmony_ci};
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_cistatic const
68262306a36Sopenharmony_cistruct mlxsw_sp_span_entry_ops *mlxsw_sp1_span_entry_ops_arr[] = {
68362306a36Sopenharmony_ci	&mlxsw_sp1_span_entry_ops_cpu,
68462306a36Sopenharmony_ci	&mlxsw_sp_span_entry_ops_phys,
68562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_IPGRE)
68662306a36Sopenharmony_ci	&mlxsw_sp_span_entry_ops_gretap4,
68762306a36Sopenharmony_ci#endif
68862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_GRE)
68962306a36Sopenharmony_ci	&mlxsw_sp_span_entry_ops_gretap6,
69062306a36Sopenharmony_ci#endif
69162306a36Sopenharmony_ci	&mlxsw_sp_span_entry_ops_vlan,
69262306a36Sopenharmony_ci};
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic bool mlxsw_sp2_span_cpu_can_handle(const struct net_device *dev)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	return !dev;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic int mlxsw_sp2_span_entry_cpu_parms(struct mlxsw_sp *mlxsw_sp,
70062306a36Sopenharmony_ci					  const struct net_device *to_dev,
70162306a36Sopenharmony_ci					  struct mlxsw_sp_span_parms *sparmsp)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	sparmsp->dest_port = mlxsw_sp->ports[MLXSW_PORT_CPU_PORT];
70462306a36Sopenharmony_ci	return 0;
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic int
70862306a36Sopenharmony_cimlxsw_sp2_span_entry_cpu_configure(struct mlxsw_sp_span_entry *span_entry,
70962306a36Sopenharmony_ci				   struct mlxsw_sp_span_parms sparms)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	/* Mirroring to the CPU port is like mirroring to any other physical
71262306a36Sopenharmony_ci	 * port. Its local port is used instead of that of the physical port.
71362306a36Sopenharmony_ci	 */
71462306a36Sopenharmony_ci	return mlxsw_sp_span_entry_phys_configure(span_entry, sparms);
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic void
71862306a36Sopenharmony_cimlxsw_sp2_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	enum mlxsw_reg_mpat_span_type span_type;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	span_type = MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH;
72362306a36Sopenharmony_ci	mlxsw_sp_span_entry_deconfigure_common(span_entry, span_type);
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic const
72762306a36Sopenharmony_cistruct mlxsw_sp_span_entry_ops mlxsw_sp2_span_entry_ops_cpu = {
72862306a36Sopenharmony_ci	.is_static = true,
72962306a36Sopenharmony_ci	.can_handle = mlxsw_sp2_span_cpu_can_handle,
73062306a36Sopenharmony_ci	.parms_set = mlxsw_sp2_span_entry_cpu_parms,
73162306a36Sopenharmony_ci	.configure = mlxsw_sp2_span_entry_cpu_configure,
73262306a36Sopenharmony_ci	.deconfigure = mlxsw_sp2_span_entry_cpu_deconfigure,
73362306a36Sopenharmony_ci};
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_cistatic const
73662306a36Sopenharmony_cistruct mlxsw_sp_span_entry_ops *mlxsw_sp2_span_entry_ops_arr[] = {
73762306a36Sopenharmony_ci	&mlxsw_sp2_span_entry_ops_cpu,
73862306a36Sopenharmony_ci	&mlxsw_sp_span_entry_ops_phys,
73962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_IPGRE)
74062306a36Sopenharmony_ci	&mlxsw_sp_span_entry_ops_gretap4,
74162306a36Sopenharmony_ci#endif
74262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_GRE)
74362306a36Sopenharmony_ci	&mlxsw_sp_span_entry_ops_gretap6,
74462306a36Sopenharmony_ci#endif
74562306a36Sopenharmony_ci	&mlxsw_sp_span_entry_ops_vlan,
74662306a36Sopenharmony_ci};
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic int
74962306a36Sopenharmony_cimlxsw_sp_span_entry_nop_parms(struct mlxsw_sp *mlxsw_sp,
75062306a36Sopenharmony_ci			      const struct net_device *to_dev,
75162306a36Sopenharmony_ci			      struct mlxsw_sp_span_parms *sparmsp)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	return mlxsw_sp_span_entry_unoffloadable(sparmsp);
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic int
75762306a36Sopenharmony_cimlxsw_sp_span_entry_nop_configure(struct mlxsw_sp_span_entry *span_entry,
75862306a36Sopenharmony_ci				  struct mlxsw_sp_span_parms sparms)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	return 0;
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_cistatic void
76462306a36Sopenharmony_cimlxsw_sp_span_entry_nop_deconfigure(struct mlxsw_sp_span_entry *span_entry)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_nop = {
76962306a36Sopenharmony_ci	.parms_set = mlxsw_sp_span_entry_nop_parms,
77062306a36Sopenharmony_ci	.configure = mlxsw_sp_span_entry_nop_configure,
77162306a36Sopenharmony_ci	.deconfigure = mlxsw_sp_span_entry_nop_deconfigure,
77262306a36Sopenharmony_ci};
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_cistatic void
77562306a36Sopenharmony_cimlxsw_sp_span_entry_configure(struct mlxsw_sp *mlxsw_sp,
77662306a36Sopenharmony_ci			      struct mlxsw_sp_span_entry *span_entry,
77762306a36Sopenharmony_ci			      struct mlxsw_sp_span_parms sparms)
77862306a36Sopenharmony_ci{
77962306a36Sopenharmony_ci	int err;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	if (!sparms.dest_port)
78262306a36Sopenharmony_ci		goto set_parms;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (sparms.dest_port->mlxsw_sp != mlxsw_sp) {
78562306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev,
78662306a36Sopenharmony_ci			"Cannot mirror to a port which belongs to a different mlxsw instance\n");
78762306a36Sopenharmony_ci		sparms.dest_port = NULL;
78862306a36Sopenharmony_ci		goto set_parms;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	err = span_entry->ops->configure(span_entry, sparms);
79262306a36Sopenharmony_ci	if (err) {
79362306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to offload mirror\n");
79462306a36Sopenharmony_ci		sparms.dest_port = NULL;
79562306a36Sopenharmony_ci		goto set_parms;
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ciset_parms:
79962306a36Sopenharmony_ci	span_entry->parms = sparms;
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic void
80362306a36Sopenharmony_cimlxsw_sp_span_entry_deconfigure(struct mlxsw_sp_span_entry *span_entry)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	if (span_entry->parms.dest_port)
80662306a36Sopenharmony_ci		span_entry->ops->deconfigure(span_entry);
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cistatic int mlxsw_sp_span_policer_id_base_set(struct mlxsw_sp_span *span,
81062306a36Sopenharmony_ci					     u16 policer_id)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = span->mlxsw_sp;
81362306a36Sopenharmony_ci	u16 policer_id_base;
81462306a36Sopenharmony_ci	int err;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	/* Policers set on SPAN agents must be in the range of
81762306a36Sopenharmony_ci	 * `policer_id_base .. policer_id_base + max_span_agents - 1`. If the
81862306a36Sopenharmony_ci	 * base is set and the new policer is not within the range, then we
81962306a36Sopenharmony_ci	 * must error out.
82062306a36Sopenharmony_ci	 */
82162306a36Sopenharmony_ci	if (refcount_read(&span->policer_id_base_ref_count)) {
82262306a36Sopenharmony_ci		if (policer_id < span->policer_id_base ||
82362306a36Sopenharmony_ci		    policer_id >= span->policer_id_base + span->entries_count)
82462306a36Sopenharmony_ci			return -EINVAL;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci		refcount_inc(&span->policer_id_base_ref_count);
82762306a36Sopenharmony_ci		return 0;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	/* Base must be even. */
83162306a36Sopenharmony_ci	policer_id_base = policer_id % 2 == 0 ? policer_id : policer_id - 1;
83262306a36Sopenharmony_ci	err = mlxsw_sp->span_ops->policer_id_base_set(mlxsw_sp,
83362306a36Sopenharmony_ci						      policer_id_base);
83462306a36Sopenharmony_ci	if (err)
83562306a36Sopenharmony_ci		return err;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	span->policer_id_base = policer_id_base;
83862306a36Sopenharmony_ci	refcount_set(&span->policer_id_base_ref_count, 1);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	return 0;
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic void mlxsw_sp_span_policer_id_base_unset(struct mlxsw_sp_span *span)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	if (refcount_dec_and_test(&span->policer_id_base_ref_count))
84662306a36Sopenharmony_ci		span->policer_id_base = 0;
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cistatic struct mlxsw_sp_span_entry *
85062306a36Sopenharmony_cimlxsw_sp_span_entry_create(struct mlxsw_sp *mlxsw_sp,
85162306a36Sopenharmony_ci			   const struct net_device *to_dev,
85262306a36Sopenharmony_ci			   const struct mlxsw_sp_span_entry_ops *ops,
85362306a36Sopenharmony_ci			   struct mlxsw_sp_span_parms sparms)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	struct mlxsw_sp_span_entry *span_entry = NULL;
85662306a36Sopenharmony_ci	int i;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	/* find a free entry to use */
85962306a36Sopenharmony_ci	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
86062306a36Sopenharmony_ci		if (!refcount_read(&mlxsw_sp->span->entries[i].ref_count)) {
86162306a36Sopenharmony_ci			span_entry = &mlxsw_sp->span->entries[i];
86262306a36Sopenharmony_ci			break;
86362306a36Sopenharmony_ci		}
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci	if (!span_entry)
86662306a36Sopenharmony_ci		return NULL;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	if (sparms.policer_enable) {
86962306a36Sopenharmony_ci		int err;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		err = mlxsw_sp_span_policer_id_base_set(mlxsw_sp->span,
87262306a36Sopenharmony_ci							sparms.policer_id);
87362306a36Sopenharmony_ci		if (err)
87462306a36Sopenharmony_ci			return NULL;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	atomic_inc(&mlxsw_sp->span->active_entries_count);
87862306a36Sopenharmony_ci	span_entry->ops = ops;
87962306a36Sopenharmony_ci	refcount_set(&span_entry->ref_count, 1);
88062306a36Sopenharmony_ci	span_entry->to_dev = to_dev;
88162306a36Sopenharmony_ci	mlxsw_sp_span_entry_configure(mlxsw_sp, span_entry, sparms);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	return span_entry;
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_cistatic void mlxsw_sp_span_entry_destroy(struct mlxsw_sp *mlxsw_sp,
88762306a36Sopenharmony_ci					struct mlxsw_sp_span_entry *span_entry)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	mlxsw_sp_span_entry_deconfigure(span_entry);
89062306a36Sopenharmony_ci	atomic_dec(&mlxsw_sp->span->active_entries_count);
89162306a36Sopenharmony_ci	if (span_entry->parms.policer_enable)
89262306a36Sopenharmony_ci		mlxsw_sp_span_policer_id_base_unset(mlxsw_sp->span);
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistruct mlxsw_sp_span_entry *
89662306a36Sopenharmony_cimlxsw_sp_span_entry_find_by_port(struct mlxsw_sp *mlxsw_sp,
89762306a36Sopenharmony_ci				 const struct net_device *to_dev)
89862306a36Sopenharmony_ci{
89962306a36Sopenharmony_ci	int i;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
90262306a36Sopenharmony_ci		struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci		if (refcount_read(&curr->ref_count) && curr->to_dev == to_dev)
90562306a36Sopenharmony_ci			return curr;
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci	return NULL;
90862306a36Sopenharmony_ci}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_civoid mlxsw_sp_span_entry_invalidate(struct mlxsw_sp *mlxsw_sp,
91162306a36Sopenharmony_ci				    struct mlxsw_sp_span_entry *span_entry)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	mlxsw_sp_span_entry_deconfigure(span_entry);
91462306a36Sopenharmony_ci	span_entry->ops = &mlxsw_sp_span_entry_ops_nop;
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_cistatic struct mlxsw_sp_span_entry *
91862306a36Sopenharmony_cimlxsw_sp_span_entry_find_by_id(struct mlxsw_sp *mlxsw_sp, int span_id)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	int i;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
92362306a36Sopenharmony_ci		struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci		if (refcount_read(&curr->ref_count) && curr->id == span_id)
92662306a36Sopenharmony_ci			return curr;
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci	return NULL;
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_cistatic struct mlxsw_sp_span_entry *
93262306a36Sopenharmony_cimlxsw_sp_span_entry_find_by_parms(struct mlxsw_sp *mlxsw_sp,
93362306a36Sopenharmony_ci				  const struct net_device *to_dev,
93462306a36Sopenharmony_ci				  const struct mlxsw_sp_span_parms *sparms)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	int i;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
93962306a36Sopenharmony_ci		struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci		if (refcount_read(&curr->ref_count) && curr->to_dev == to_dev &&
94262306a36Sopenharmony_ci		    curr->parms.policer_enable == sparms->policer_enable &&
94362306a36Sopenharmony_ci		    curr->parms.policer_id == sparms->policer_id &&
94462306a36Sopenharmony_ci		    curr->parms.session_id == sparms->session_id)
94562306a36Sopenharmony_ci			return curr;
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci	return NULL;
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_cistatic struct mlxsw_sp_span_entry *
95162306a36Sopenharmony_cimlxsw_sp_span_entry_get(struct mlxsw_sp *mlxsw_sp,
95262306a36Sopenharmony_ci			const struct net_device *to_dev,
95362306a36Sopenharmony_ci			const struct mlxsw_sp_span_entry_ops *ops,
95462306a36Sopenharmony_ci			struct mlxsw_sp_span_parms sparms)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	struct mlxsw_sp_span_entry *span_entry;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	span_entry = mlxsw_sp_span_entry_find_by_parms(mlxsw_sp, to_dev,
95962306a36Sopenharmony_ci						       &sparms);
96062306a36Sopenharmony_ci	if (span_entry) {
96162306a36Sopenharmony_ci		/* Already exists, just take a reference */
96262306a36Sopenharmony_ci		refcount_inc(&span_entry->ref_count);
96362306a36Sopenharmony_ci		return span_entry;
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	return mlxsw_sp_span_entry_create(mlxsw_sp, to_dev, ops, sparms);
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic int mlxsw_sp_span_entry_put(struct mlxsw_sp *mlxsw_sp,
97062306a36Sopenharmony_ci				   struct mlxsw_sp_span_entry *span_entry)
97162306a36Sopenharmony_ci{
97262306a36Sopenharmony_ci	if (refcount_dec_and_test(&span_entry->ref_count))
97362306a36Sopenharmony_ci		mlxsw_sp_span_entry_destroy(mlxsw_sp, span_entry);
97462306a36Sopenharmony_ci	return 0;
97562306a36Sopenharmony_ci}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_cistatic int mlxsw_sp_span_port_buffer_update(struct mlxsw_sp_port *mlxsw_sp_port, bool enable)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	struct mlxsw_sp_hdroom hdroom;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	hdroom = *mlxsw_sp_port->hdroom;
98262306a36Sopenharmony_ci	hdroom.int_buf.enable = enable;
98362306a36Sopenharmony_ci	mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	return mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic int
98962306a36Sopenharmony_cimlxsw_sp_span_port_buffer_enable(struct mlxsw_sp_port *mlxsw_sp_port)
99062306a36Sopenharmony_ci{
99162306a36Sopenharmony_ci	return mlxsw_sp_span_port_buffer_update(mlxsw_sp_port, true);
99262306a36Sopenharmony_ci}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cistatic void mlxsw_sp_span_port_buffer_disable(struct mlxsw_sp_port *mlxsw_sp_port)
99562306a36Sopenharmony_ci{
99662306a36Sopenharmony_ci	mlxsw_sp_span_port_buffer_update(mlxsw_sp_port, false);
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_cistatic struct mlxsw_sp_span_analyzed_port *
100062306a36Sopenharmony_cimlxsw_sp_span_analyzed_port_find(struct mlxsw_sp_span *span, u16 local_port,
100162306a36Sopenharmony_ci				 bool ingress)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	struct mlxsw_sp_span_analyzed_port *analyzed_port;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	list_for_each_entry(analyzed_port, &span->analyzed_ports_list, list) {
100662306a36Sopenharmony_ci		if (analyzed_port->local_port == local_port &&
100762306a36Sopenharmony_ci		    analyzed_port->ingress == ingress)
100862306a36Sopenharmony_ci			return analyzed_port;
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	return NULL;
101262306a36Sopenharmony_ci}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_cistatic const struct mlxsw_sp_span_entry_ops *
101562306a36Sopenharmony_cimlxsw_sp_span_entry_ops(struct mlxsw_sp *mlxsw_sp,
101662306a36Sopenharmony_ci			const struct net_device *to_dev)
101762306a36Sopenharmony_ci{
101862306a36Sopenharmony_ci	struct mlxsw_sp_span *span = mlxsw_sp->span;
101962306a36Sopenharmony_ci	size_t i;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	for (i = 0; i < span->span_entry_ops_arr_size; ++i)
102262306a36Sopenharmony_ci		if (span->span_entry_ops_arr[i]->can_handle(to_dev))
102362306a36Sopenharmony_ci			return span->span_entry_ops_arr[i];
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	return NULL;
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic void mlxsw_sp_span_respin_work(struct work_struct *work)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	struct mlxsw_sp_span *span;
103162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
103262306a36Sopenharmony_ci	int i, err;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	span = container_of(work, struct mlxsw_sp_span, work);
103562306a36Sopenharmony_ci	mlxsw_sp = span->mlxsw_sp;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	rtnl_lock();
103862306a36Sopenharmony_ci	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
103962306a36Sopenharmony_ci		struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
104062306a36Sopenharmony_ci		struct mlxsw_sp_span_parms sparms = {NULL};
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci		if (!refcount_read(&curr->ref_count))
104362306a36Sopenharmony_ci			continue;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci		if (curr->ops->is_static)
104662306a36Sopenharmony_ci			continue;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci		err = curr->ops->parms_set(mlxsw_sp, curr->to_dev, &sparms);
104962306a36Sopenharmony_ci		if (err)
105062306a36Sopenharmony_ci			continue;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		if (memcmp(&sparms, &curr->parms, sizeof(sparms))) {
105362306a36Sopenharmony_ci			mlxsw_sp_span_entry_deconfigure(curr);
105462306a36Sopenharmony_ci			mlxsw_sp_span_entry_configure(mlxsw_sp, curr, sparms);
105562306a36Sopenharmony_ci		}
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci	rtnl_unlock();
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_civoid mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp)
106162306a36Sopenharmony_ci{
106262306a36Sopenharmony_ci	if (atomic_read(&mlxsw_sp->span->active_entries_count) == 0)
106362306a36Sopenharmony_ci		return;
106462306a36Sopenharmony_ci	mlxsw_core_schedule_work(&mlxsw_sp->span->work);
106562306a36Sopenharmony_ci}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ciint mlxsw_sp_span_agent_get(struct mlxsw_sp *mlxsw_sp, int *p_span_id,
106862306a36Sopenharmony_ci			    const struct mlxsw_sp_span_agent_parms *parms)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	const struct net_device *to_dev = parms->to_dev;
107162306a36Sopenharmony_ci	const struct mlxsw_sp_span_entry_ops *ops;
107262306a36Sopenharmony_ci	struct mlxsw_sp_span_entry *span_entry;
107362306a36Sopenharmony_ci	struct mlxsw_sp_span_parms sparms;
107462306a36Sopenharmony_ci	int err;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	ASSERT_RTNL();
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	ops = mlxsw_sp_span_entry_ops(mlxsw_sp, to_dev);
107962306a36Sopenharmony_ci	if (!ops) {
108062306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Cannot mirror to requested destination\n");
108162306a36Sopenharmony_ci		return -EOPNOTSUPP;
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	memset(&sparms, 0, sizeof(sparms));
108562306a36Sopenharmony_ci	err = ops->parms_set(mlxsw_sp, to_dev, &sparms);
108662306a36Sopenharmony_ci	if (err)
108762306a36Sopenharmony_ci		return err;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	sparms.policer_id = parms->policer_id;
109062306a36Sopenharmony_ci	sparms.policer_enable = parms->policer_enable;
109162306a36Sopenharmony_ci	sparms.session_id = parms->session_id;
109262306a36Sopenharmony_ci	span_entry = mlxsw_sp_span_entry_get(mlxsw_sp, to_dev, ops, sparms);
109362306a36Sopenharmony_ci	if (!span_entry)
109462306a36Sopenharmony_ci		return -ENOBUFS;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	*p_span_id = span_entry->id;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	return 0;
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_civoid mlxsw_sp_span_agent_put(struct mlxsw_sp *mlxsw_sp, int span_id)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	struct mlxsw_sp_span_entry *span_entry;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	ASSERT_RTNL();
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	span_entry = mlxsw_sp_span_entry_find_by_id(mlxsw_sp, span_id);
110862306a36Sopenharmony_ci	if (WARN_ON_ONCE(!span_entry))
110962306a36Sopenharmony_ci		return;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cistatic struct mlxsw_sp_span_analyzed_port *
111562306a36Sopenharmony_cimlxsw_sp_span_analyzed_port_create(struct mlxsw_sp_span *span,
111662306a36Sopenharmony_ci				   struct mlxsw_sp_port *mlxsw_sp_port,
111762306a36Sopenharmony_ci				   bool ingress)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	struct mlxsw_sp_span_analyzed_port *analyzed_port;
112062306a36Sopenharmony_ci	int err;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	analyzed_port = kzalloc(sizeof(*analyzed_port), GFP_KERNEL);
112362306a36Sopenharmony_ci	if (!analyzed_port)
112462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	refcount_set(&analyzed_port->ref_count, 1);
112762306a36Sopenharmony_ci	analyzed_port->local_port = mlxsw_sp_port->local_port;
112862306a36Sopenharmony_ci	analyzed_port->ingress = ingress;
112962306a36Sopenharmony_ci	list_add_tail(&analyzed_port->list, &span->analyzed_ports_list);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	/* An egress mirror buffer should be allocated on the egress port which
113262306a36Sopenharmony_ci	 * does the mirroring.
113362306a36Sopenharmony_ci	 */
113462306a36Sopenharmony_ci	if (!ingress) {
113562306a36Sopenharmony_ci		err = mlxsw_sp_span_port_buffer_enable(mlxsw_sp_port);
113662306a36Sopenharmony_ci		if (err)
113762306a36Sopenharmony_ci			goto err_buffer_update;
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	return analyzed_port;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_cierr_buffer_update:
114362306a36Sopenharmony_ci	list_del(&analyzed_port->list);
114462306a36Sopenharmony_ci	kfree(analyzed_port);
114562306a36Sopenharmony_ci	return ERR_PTR(err);
114662306a36Sopenharmony_ci}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_cistatic void
114962306a36Sopenharmony_cimlxsw_sp_span_analyzed_port_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
115062306a36Sopenharmony_ci				    struct mlxsw_sp_span_analyzed_port *
115162306a36Sopenharmony_ci				    analyzed_port)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci	/* Remove egress mirror buffer now that port is no longer analyzed
115462306a36Sopenharmony_ci	 * at egress.
115562306a36Sopenharmony_ci	 */
115662306a36Sopenharmony_ci	if (!analyzed_port->ingress)
115762306a36Sopenharmony_ci		mlxsw_sp_span_port_buffer_disable(mlxsw_sp_port);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	list_del(&analyzed_port->list);
116062306a36Sopenharmony_ci	kfree(analyzed_port);
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ciint mlxsw_sp_span_analyzed_port_get(struct mlxsw_sp_port *mlxsw_sp_port,
116462306a36Sopenharmony_ci				    bool ingress)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
116762306a36Sopenharmony_ci	struct mlxsw_sp_span_analyzed_port *analyzed_port;
116862306a36Sopenharmony_ci	u16 local_port = mlxsw_sp_port->local_port;
116962306a36Sopenharmony_ci	int err = 0;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	mutex_lock(&mlxsw_sp->span->analyzed_ports_lock);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	analyzed_port = mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span,
117462306a36Sopenharmony_ci							 local_port, ingress);
117562306a36Sopenharmony_ci	if (analyzed_port) {
117662306a36Sopenharmony_ci		refcount_inc(&analyzed_port->ref_count);
117762306a36Sopenharmony_ci		goto out_unlock;
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	analyzed_port = mlxsw_sp_span_analyzed_port_create(mlxsw_sp->span,
118162306a36Sopenharmony_ci							   mlxsw_sp_port,
118262306a36Sopenharmony_ci							   ingress);
118362306a36Sopenharmony_ci	if (IS_ERR(analyzed_port))
118462306a36Sopenharmony_ci		err = PTR_ERR(analyzed_port);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ciout_unlock:
118762306a36Sopenharmony_ci	mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock);
118862306a36Sopenharmony_ci	return err;
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_civoid mlxsw_sp_span_analyzed_port_put(struct mlxsw_sp_port *mlxsw_sp_port,
119262306a36Sopenharmony_ci				     bool ingress)
119362306a36Sopenharmony_ci{
119462306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
119562306a36Sopenharmony_ci	struct mlxsw_sp_span_analyzed_port *analyzed_port;
119662306a36Sopenharmony_ci	u16 local_port = mlxsw_sp_port->local_port;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	mutex_lock(&mlxsw_sp->span->analyzed_ports_lock);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	analyzed_port = mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span,
120162306a36Sopenharmony_ci							 local_port, ingress);
120262306a36Sopenharmony_ci	if (WARN_ON_ONCE(!analyzed_port))
120362306a36Sopenharmony_ci		goto out_unlock;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	if (!refcount_dec_and_test(&analyzed_port->ref_count))
120662306a36Sopenharmony_ci		goto out_unlock;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	mlxsw_sp_span_analyzed_port_destroy(mlxsw_sp_port, analyzed_port);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ciout_unlock:
121162306a36Sopenharmony_ci	mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock);
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_cistatic int
121562306a36Sopenharmony_ci__mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span *span,
121662306a36Sopenharmony_ci				  struct mlxsw_sp_span_trigger_entry *
121762306a36Sopenharmony_ci				  trigger_entry, bool enable)
121862306a36Sopenharmony_ci{
121962306a36Sopenharmony_ci	char mpar_pl[MLXSW_REG_MPAR_LEN];
122062306a36Sopenharmony_ci	enum mlxsw_reg_mpar_i_e i_e;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	switch (trigger_entry->trigger) {
122362306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_INGRESS:
122462306a36Sopenharmony_ci		i_e = MLXSW_REG_MPAR_TYPE_INGRESS;
122562306a36Sopenharmony_ci		break;
122662306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_EGRESS:
122762306a36Sopenharmony_ci		i_e = MLXSW_REG_MPAR_TYPE_EGRESS;
122862306a36Sopenharmony_ci		break;
122962306a36Sopenharmony_ci	default:
123062306a36Sopenharmony_ci		WARN_ON_ONCE(1);
123162306a36Sopenharmony_ci		return -EINVAL;
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	if (trigger_entry->parms.probability_rate > MLXSW_REG_MPAR_RATE_MAX)
123562306a36Sopenharmony_ci		return -EINVAL;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	mlxsw_reg_mpar_pack(mpar_pl, trigger_entry->local_port, i_e, enable,
123862306a36Sopenharmony_ci			    trigger_entry->parms.span_id,
123962306a36Sopenharmony_ci			    trigger_entry->parms.probability_rate);
124062306a36Sopenharmony_ci	return mlxsw_reg_write(span->mlxsw_sp->core, MLXSW_REG(mpar), mpar_pl);
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_cistatic int
124462306a36Sopenharmony_cimlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span_trigger_entry *
124562306a36Sopenharmony_ci				trigger_entry)
124662306a36Sopenharmony_ci{
124762306a36Sopenharmony_ci	return __mlxsw_sp_span_trigger_port_bind(trigger_entry->span,
124862306a36Sopenharmony_ci						 trigger_entry, true);
124962306a36Sopenharmony_ci}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_cistatic void
125262306a36Sopenharmony_cimlxsw_sp_span_trigger_port_unbind(struct mlxsw_sp_span_trigger_entry *
125362306a36Sopenharmony_ci				  trigger_entry)
125462306a36Sopenharmony_ci{
125562306a36Sopenharmony_ci	__mlxsw_sp_span_trigger_port_bind(trigger_entry->span, trigger_entry,
125662306a36Sopenharmony_ci					  false);
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_cistatic bool
126062306a36Sopenharmony_cimlxsw_sp_span_trigger_port_matches(struct mlxsw_sp_span_trigger_entry *
126162306a36Sopenharmony_ci				   trigger_entry,
126262306a36Sopenharmony_ci				   enum mlxsw_sp_span_trigger trigger,
126362306a36Sopenharmony_ci				   struct mlxsw_sp_port *mlxsw_sp_port)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	return trigger_entry->trigger == trigger &&
126662306a36Sopenharmony_ci	       trigger_entry->local_port == mlxsw_sp_port->local_port;
126762306a36Sopenharmony_ci}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_cistatic int
127062306a36Sopenharmony_cimlxsw_sp_span_trigger_port_enable(struct mlxsw_sp_span_trigger_entry *
127162306a36Sopenharmony_ci				  trigger_entry,
127262306a36Sopenharmony_ci				  struct mlxsw_sp_port *mlxsw_sp_port, u8 tc)
127362306a36Sopenharmony_ci{
127462306a36Sopenharmony_ci	/* Port trigger are enabled during binding. */
127562306a36Sopenharmony_ci	return 0;
127662306a36Sopenharmony_ci}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_cistatic void
127962306a36Sopenharmony_cimlxsw_sp_span_trigger_port_disable(struct mlxsw_sp_span_trigger_entry *
128062306a36Sopenharmony_ci				   trigger_entry,
128162306a36Sopenharmony_ci				   struct mlxsw_sp_port *mlxsw_sp_port, u8 tc)
128262306a36Sopenharmony_ci{
128362306a36Sopenharmony_ci}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_cistatic const struct mlxsw_sp_span_trigger_ops
128662306a36Sopenharmony_cimlxsw_sp_span_trigger_port_ops = {
128762306a36Sopenharmony_ci	.bind = mlxsw_sp_span_trigger_port_bind,
128862306a36Sopenharmony_ci	.unbind = mlxsw_sp_span_trigger_port_unbind,
128962306a36Sopenharmony_ci	.matches = mlxsw_sp_span_trigger_port_matches,
129062306a36Sopenharmony_ci	.enable = mlxsw_sp_span_trigger_port_enable,
129162306a36Sopenharmony_ci	.disable = mlxsw_sp_span_trigger_port_disable,
129262306a36Sopenharmony_ci};
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_cistatic int
129562306a36Sopenharmony_cimlxsw_sp1_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry *
129662306a36Sopenharmony_ci				   trigger_entry)
129762306a36Sopenharmony_ci{
129862306a36Sopenharmony_ci	return -EOPNOTSUPP;
129962306a36Sopenharmony_ci}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_cistatic void
130262306a36Sopenharmony_cimlxsw_sp1_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry *
130362306a36Sopenharmony_ci				     trigger_entry)
130462306a36Sopenharmony_ci{
130562306a36Sopenharmony_ci}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_cistatic bool
130862306a36Sopenharmony_cimlxsw_sp1_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry *
130962306a36Sopenharmony_ci				      trigger_entry,
131062306a36Sopenharmony_ci				      enum mlxsw_sp_span_trigger trigger,
131162306a36Sopenharmony_ci				      struct mlxsw_sp_port *mlxsw_sp_port)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	WARN_ON_ONCE(1);
131462306a36Sopenharmony_ci	return false;
131562306a36Sopenharmony_ci}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_cistatic int
131862306a36Sopenharmony_cimlxsw_sp1_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry *
131962306a36Sopenharmony_ci				     trigger_entry,
132062306a36Sopenharmony_ci				     struct mlxsw_sp_port *mlxsw_sp_port,
132162306a36Sopenharmony_ci				     u8 tc)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	return -EOPNOTSUPP;
132462306a36Sopenharmony_ci}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_cistatic void
132762306a36Sopenharmony_cimlxsw_sp1_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry *
132862306a36Sopenharmony_ci				      trigger_entry,
132962306a36Sopenharmony_ci				      struct mlxsw_sp_port *mlxsw_sp_port,
133062306a36Sopenharmony_ci				      u8 tc)
133162306a36Sopenharmony_ci{
133262306a36Sopenharmony_ci}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_cistatic const struct mlxsw_sp_span_trigger_ops
133562306a36Sopenharmony_cimlxsw_sp1_span_trigger_global_ops = {
133662306a36Sopenharmony_ci	.bind = mlxsw_sp1_span_trigger_global_bind,
133762306a36Sopenharmony_ci	.unbind = mlxsw_sp1_span_trigger_global_unbind,
133862306a36Sopenharmony_ci	.matches = mlxsw_sp1_span_trigger_global_matches,
133962306a36Sopenharmony_ci	.enable = mlxsw_sp1_span_trigger_global_enable,
134062306a36Sopenharmony_ci	.disable = mlxsw_sp1_span_trigger_global_disable,
134162306a36Sopenharmony_ci};
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_cistatic const struct mlxsw_sp_span_trigger_ops *
134462306a36Sopenharmony_cimlxsw_sp1_span_trigger_ops_arr[] = {
134562306a36Sopenharmony_ci	[MLXSW_SP_SPAN_TRIGGER_TYPE_PORT] = &mlxsw_sp_span_trigger_port_ops,
134662306a36Sopenharmony_ci	[MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL] =
134762306a36Sopenharmony_ci		&mlxsw_sp1_span_trigger_global_ops,
134862306a36Sopenharmony_ci};
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_cistatic int
135162306a36Sopenharmony_cimlxsw_sp2_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry *
135262306a36Sopenharmony_ci				   trigger_entry)
135362306a36Sopenharmony_ci{
135462306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = trigger_entry->span->mlxsw_sp;
135562306a36Sopenharmony_ci	enum mlxsw_reg_mpagr_trigger trigger;
135662306a36Sopenharmony_ci	char mpagr_pl[MLXSW_REG_MPAGR_LEN];
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	switch (trigger_entry->trigger) {
135962306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP:
136062306a36Sopenharmony_ci		trigger = MLXSW_REG_MPAGR_TRIGGER_INGRESS_SHARED_BUFFER;
136162306a36Sopenharmony_ci		break;
136262306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP:
136362306a36Sopenharmony_ci		trigger = MLXSW_REG_MPAGR_TRIGGER_INGRESS_WRED;
136462306a36Sopenharmony_ci		break;
136562306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_ECN:
136662306a36Sopenharmony_ci		trigger = MLXSW_REG_MPAGR_TRIGGER_EGRESS_ECN;
136762306a36Sopenharmony_ci		break;
136862306a36Sopenharmony_ci	default:
136962306a36Sopenharmony_ci		WARN_ON_ONCE(1);
137062306a36Sopenharmony_ci		return -EINVAL;
137162306a36Sopenharmony_ci	}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (trigger_entry->parms.probability_rate > MLXSW_REG_MPAGR_RATE_MAX)
137462306a36Sopenharmony_ci		return -EINVAL;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	mlxsw_reg_mpagr_pack(mpagr_pl, trigger, trigger_entry->parms.span_id,
137762306a36Sopenharmony_ci			     trigger_entry->parms.probability_rate);
137862306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpagr), mpagr_pl);
137962306a36Sopenharmony_ci}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_cistatic void
138262306a36Sopenharmony_cimlxsw_sp2_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry *
138362306a36Sopenharmony_ci				     trigger_entry)
138462306a36Sopenharmony_ci{
138562306a36Sopenharmony_ci	/* There is no unbinding for global triggers. The trigger should be
138662306a36Sopenharmony_ci	 * disabled on all ports by now.
138762306a36Sopenharmony_ci	 */
138862306a36Sopenharmony_ci}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_cistatic bool
139162306a36Sopenharmony_cimlxsw_sp2_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry *
139262306a36Sopenharmony_ci				      trigger_entry,
139362306a36Sopenharmony_ci				      enum mlxsw_sp_span_trigger trigger,
139462306a36Sopenharmony_ci				      struct mlxsw_sp_port *mlxsw_sp_port)
139562306a36Sopenharmony_ci{
139662306a36Sopenharmony_ci	return trigger_entry->trigger == trigger;
139762306a36Sopenharmony_ci}
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_cistatic int
140062306a36Sopenharmony_ci__mlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry *
140162306a36Sopenharmony_ci				       trigger_entry,
140262306a36Sopenharmony_ci				       struct mlxsw_sp_port *mlxsw_sp_port,
140362306a36Sopenharmony_ci				       u8 tc, bool enable)
140462306a36Sopenharmony_ci{
140562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = trigger_entry->span->mlxsw_sp;
140662306a36Sopenharmony_ci	char momte_pl[MLXSW_REG_MOMTE_LEN];
140762306a36Sopenharmony_ci	enum mlxsw_reg_momte_type type;
140862306a36Sopenharmony_ci	int err;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	switch (trigger_entry->trigger) {
141162306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP:
141262306a36Sopenharmony_ci		type = MLXSW_REG_MOMTE_TYPE_SHARED_BUFFER_TCLASS;
141362306a36Sopenharmony_ci		break;
141462306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP:
141562306a36Sopenharmony_ci		type = MLXSW_REG_MOMTE_TYPE_WRED;
141662306a36Sopenharmony_ci		break;
141762306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_ECN:
141862306a36Sopenharmony_ci		type = MLXSW_REG_MOMTE_TYPE_ECN;
141962306a36Sopenharmony_ci		break;
142062306a36Sopenharmony_ci	default:
142162306a36Sopenharmony_ci		WARN_ON_ONCE(1);
142262306a36Sopenharmony_ci		return -EINVAL;
142362306a36Sopenharmony_ci	}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	/* Query existing configuration in order to only change the state of
142662306a36Sopenharmony_ci	 * the specified traffic class.
142762306a36Sopenharmony_ci	 */
142862306a36Sopenharmony_ci	mlxsw_reg_momte_pack(momte_pl, mlxsw_sp_port->local_port, type);
142962306a36Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(momte), momte_pl);
143062306a36Sopenharmony_ci	if (err)
143162306a36Sopenharmony_ci		return err;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	mlxsw_reg_momte_tclass_en_set(momte_pl, tc, enable);
143462306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(momte), momte_pl);
143562306a36Sopenharmony_ci}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_cistatic int
143862306a36Sopenharmony_cimlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry *
143962306a36Sopenharmony_ci				     trigger_entry,
144062306a36Sopenharmony_ci				     struct mlxsw_sp_port *mlxsw_sp_port,
144162306a36Sopenharmony_ci				     u8 tc)
144262306a36Sopenharmony_ci{
144362306a36Sopenharmony_ci	return __mlxsw_sp2_span_trigger_global_enable(trigger_entry,
144462306a36Sopenharmony_ci						      mlxsw_sp_port, tc, true);
144562306a36Sopenharmony_ci}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_cistatic void
144862306a36Sopenharmony_cimlxsw_sp2_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry *
144962306a36Sopenharmony_ci				      trigger_entry,
145062306a36Sopenharmony_ci				      struct mlxsw_sp_port *mlxsw_sp_port,
145162306a36Sopenharmony_ci				      u8 tc)
145262306a36Sopenharmony_ci{
145362306a36Sopenharmony_ci	__mlxsw_sp2_span_trigger_global_enable(trigger_entry, mlxsw_sp_port, tc,
145462306a36Sopenharmony_ci					       false);
145562306a36Sopenharmony_ci}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_cistatic const struct mlxsw_sp_span_trigger_ops
145862306a36Sopenharmony_cimlxsw_sp2_span_trigger_global_ops = {
145962306a36Sopenharmony_ci	.bind = mlxsw_sp2_span_trigger_global_bind,
146062306a36Sopenharmony_ci	.unbind = mlxsw_sp2_span_trigger_global_unbind,
146162306a36Sopenharmony_ci	.matches = mlxsw_sp2_span_trigger_global_matches,
146262306a36Sopenharmony_ci	.enable = mlxsw_sp2_span_trigger_global_enable,
146362306a36Sopenharmony_ci	.disable = mlxsw_sp2_span_trigger_global_disable,
146462306a36Sopenharmony_ci};
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_cistatic const struct mlxsw_sp_span_trigger_ops *
146762306a36Sopenharmony_cimlxsw_sp2_span_trigger_ops_arr[] = {
146862306a36Sopenharmony_ci	[MLXSW_SP_SPAN_TRIGGER_TYPE_PORT] = &mlxsw_sp_span_trigger_port_ops,
146962306a36Sopenharmony_ci	[MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL] =
147062306a36Sopenharmony_ci		&mlxsw_sp2_span_trigger_global_ops,
147162306a36Sopenharmony_ci};
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_cistatic void
147462306a36Sopenharmony_cimlxsw_sp_span_trigger_ops_set(struct mlxsw_sp_span_trigger_entry *trigger_entry)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	struct mlxsw_sp_span *span = trigger_entry->span;
147762306a36Sopenharmony_ci	enum mlxsw_sp_span_trigger_type type;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	switch (trigger_entry->trigger) {
148062306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_INGRESS:
148162306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_EGRESS:
148262306a36Sopenharmony_ci		type = MLXSW_SP_SPAN_TRIGGER_TYPE_PORT;
148362306a36Sopenharmony_ci		break;
148462306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP:
148562306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP:
148662306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_ECN:
148762306a36Sopenharmony_ci		type = MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL;
148862306a36Sopenharmony_ci		break;
148962306a36Sopenharmony_ci	default:
149062306a36Sopenharmony_ci		WARN_ON_ONCE(1);
149162306a36Sopenharmony_ci		return;
149262306a36Sopenharmony_ci	}
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	trigger_entry->ops = span->span_trigger_ops_arr[type];
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_cistatic struct mlxsw_sp_span_trigger_entry *
149862306a36Sopenharmony_cimlxsw_sp_span_trigger_entry_create(struct mlxsw_sp_span *span,
149962306a36Sopenharmony_ci				   enum mlxsw_sp_span_trigger trigger,
150062306a36Sopenharmony_ci				   struct mlxsw_sp_port *mlxsw_sp_port,
150162306a36Sopenharmony_ci				   const struct mlxsw_sp_span_trigger_parms
150262306a36Sopenharmony_ci				   *parms)
150362306a36Sopenharmony_ci{
150462306a36Sopenharmony_ci	struct mlxsw_sp_span_trigger_entry *trigger_entry;
150562306a36Sopenharmony_ci	int err;
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	trigger_entry = kzalloc(sizeof(*trigger_entry), GFP_KERNEL);
150862306a36Sopenharmony_ci	if (!trigger_entry)
150962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	refcount_set(&trigger_entry->ref_count, 1);
151262306a36Sopenharmony_ci	trigger_entry->local_port = mlxsw_sp_port ? mlxsw_sp_port->local_port :
151362306a36Sopenharmony_ci						    0;
151462306a36Sopenharmony_ci	trigger_entry->trigger = trigger;
151562306a36Sopenharmony_ci	memcpy(&trigger_entry->parms, parms, sizeof(trigger_entry->parms));
151662306a36Sopenharmony_ci	trigger_entry->span = span;
151762306a36Sopenharmony_ci	mlxsw_sp_span_trigger_ops_set(trigger_entry);
151862306a36Sopenharmony_ci	list_add_tail(&trigger_entry->list, &span->trigger_entries_list);
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	err = trigger_entry->ops->bind(trigger_entry);
152162306a36Sopenharmony_ci	if (err)
152262306a36Sopenharmony_ci		goto err_trigger_entry_bind;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	return trigger_entry;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_cierr_trigger_entry_bind:
152762306a36Sopenharmony_ci	list_del(&trigger_entry->list);
152862306a36Sopenharmony_ci	kfree(trigger_entry);
152962306a36Sopenharmony_ci	return ERR_PTR(err);
153062306a36Sopenharmony_ci}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_cistatic void
153362306a36Sopenharmony_cimlxsw_sp_span_trigger_entry_destroy(struct mlxsw_sp_span *span,
153462306a36Sopenharmony_ci				    struct mlxsw_sp_span_trigger_entry *
153562306a36Sopenharmony_ci				    trigger_entry)
153662306a36Sopenharmony_ci{
153762306a36Sopenharmony_ci	trigger_entry->ops->unbind(trigger_entry);
153862306a36Sopenharmony_ci	list_del(&trigger_entry->list);
153962306a36Sopenharmony_ci	kfree(trigger_entry);
154062306a36Sopenharmony_ci}
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_cistatic struct mlxsw_sp_span_trigger_entry *
154362306a36Sopenharmony_cimlxsw_sp_span_trigger_entry_find(struct mlxsw_sp_span *span,
154462306a36Sopenharmony_ci				 enum mlxsw_sp_span_trigger trigger,
154562306a36Sopenharmony_ci				 struct mlxsw_sp_port *mlxsw_sp_port)
154662306a36Sopenharmony_ci{
154762306a36Sopenharmony_ci	struct mlxsw_sp_span_trigger_entry *trigger_entry;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	list_for_each_entry(trigger_entry, &span->trigger_entries_list, list) {
155062306a36Sopenharmony_ci		if (trigger_entry->ops->matches(trigger_entry, trigger,
155162306a36Sopenharmony_ci						mlxsw_sp_port))
155262306a36Sopenharmony_ci			return trigger_entry;
155362306a36Sopenharmony_ci	}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	return NULL;
155662306a36Sopenharmony_ci}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ciint mlxsw_sp_span_agent_bind(struct mlxsw_sp *mlxsw_sp,
155962306a36Sopenharmony_ci			     enum mlxsw_sp_span_trigger trigger,
156062306a36Sopenharmony_ci			     struct mlxsw_sp_port *mlxsw_sp_port,
156162306a36Sopenharmony_ci			     const struct mlxsw_sp_span_trigger_parms *parms)
156262306a36Sopenharmony_ci{
156362306a36Sopenharmony_ci	struct mlxsw_sp_span_trigger_entry *trigger_entry;
156462306a36Sopenharmony_ci	int err = 0;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	ASSERT_RTNL();
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	if (!mlxsw_sp_span_entry_find_by_id(mlxsw_sp, parms->span_id))
156962306a36Sopenharmony_ci		return -EINVAL;
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
157262306a36Sopenharmony_ci							 trigger,
157362306a36Sopenharmony_ci							 mlxsw_sp_port);
157462306a36Sopenharmony_ci	if (trigger_entry) {
157562306a36Sopenharmony_ci		if (trigger_entry->parms.span_id != parms->span_id ||
157662306a36Sopenharmony_ci		    trigger_entry->parms.probability_rate !=
157762306a36Sopenharmony_ci		    parms->probability_rate)
157862306a36Sopenharmony_ci			return -EINVAL;
157962306a36Sopenharmony_ci		refcount_inc(&trigger_entry->ref_count);
158062306a36Sopenharmony_ci		goto out;
158162306a36Sopenharmony_ci	}
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	trigger_entry = mlxsw_sp_span_trigger_entry_create(mlxsw_sp->span,
158462306a36Sopenharmony_ci							   trigger,
158562306a36Sopenharmony_ci							   mlxsw_sp_port,
158662306a36Sopenharmony_ci							   parms);
158762306a36Sopenharmony_ci	if (IS_ERR(trigger_entry))
158862306a36Sopenharmony_ci		err = PTR_ERR(trigger_entry);
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ciout:
159162306a36Sopenharmony_ci	return err;
159262306a36Sopenharmony_ci}
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_civoid mlxsw_sp_span_agent_unbind(struct mlxsw_sp *mlxsw_sp,
159562306a36Sopenharmony_ci				enum mlxsw_sp_span_trigger trigger,
159662306a36Sopenharmony_ci				struct mlxsw_sp_port *mlxsw_sp_port,
159762306a36Sopenharmony_ci				const struct mlxsw_sp_span_trigger_parms *parms)
159862306a36Sopenharmony_ci{
159962306a36Sopenharmony_ci	struct mlxsw_sp_span_trigger_entry *trigger_entry;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	ASSERT_RTNL();
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	if (WARN_ON_ONCE(!mlxsw_sp_span_entry_find_by_id(mlxsw_sp,
160462306a36Sopenharmony_ci							 parms->span_id)))
160562306a36Sopenharmony_ci		return;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
160862306a36Sopenharmony_ci							 trigger,
160962306a36Sopenharmony_ci							 mlxsw_sp_port);
161062306a36Sopenharmony_ci	if (WARN_ON_ONCE(!trigger_entry))
161162306a36Sopenharmony_ci		return;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	if (!refcount_dec_and_test(&trigger_entry->ref_count))
161462306a36Sopenharmony_ci		return;
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	mlxsw_sp_span_trigger_entry_destroy(mlxsw_sp->span, trigger_entry);
161762306a36Sopenharmony_ci}
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ciint mlxsw_sp_span_trigger_enable(struct mlxsw_sp_port *mlxsw_sp_port,
162062306a36Sopenharmony_ci				 enum mlxsw_sp_span_trigger trigger, u8 tc)
162162306a36Sopenharmony_ci{
162262306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
162362306a36Sopenharmony_ci	struct mlxsw_sp_span_trigger_entry *trigger_entry;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	ASSERT_RTNL();
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
162862306a36Sopenharmony_ci							 trigger,
162962306a36Sopenharmony_ci							 mlxsw_sp_port);
163062306a36Sopenharmony_ci	if (WARN_ON_ONCE(!trigger_entry))
163162306a36Sopenharmony_ci		return -EINVAL;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	return trigger_entry->ops->enable(trigger_entry, mlxsw_sp_port, tc);
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_civoid mlxsw_sp_span_trigger_disable(struct mlxsw_sp_port *mlxsw_sp_port,
163762306a36Sopenharmony_ci				   enum mlxsw_sp_span_trigger trigger, u8 tc)
163862306a36Sopenharmony_ci{
163962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
164062306a36Sopenharmony_ci	struct mlxsw_sp_span_trigger_entry *trigger_entry;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	ASSERT_RTNL();
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
164562306a36Sopenharmony_ci							 trigger,
164662306a36Sopenharmony_ci							 mlxsw_sp_port);
164762306a36Sopenharmony_ci	if (WARN_ON_ONCE(!trigger_entry))
164862306a36Sopenharmony_ci		return;
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	return trigger_entry->ops->disable(trigger_entry, mlxsw_sp_port, tc);
165162306a36Sopenharmony_ci}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_cibool mlxsw_sp_span_trigger_is_ingress(enum mlxsw_sp_span_trigger trigger)
165462306a36Sopenharmony_ci{
165562306a36Sopenharmony_ci	switch (trigger) {
165662306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_INGRESS:
165762306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP:
165862306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP:
165962306a36Sopenharmony_ci		return true;
166062306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_EGRESS:
166162306a36Sopenharmony_ci	case MLXSW_SP_SPAN_TRIGGER_ECN:
166262306a36Sopenharmony_ci		return false;
166362306a36Sopenharmony_ci	}
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	WARN_ON_ONCE(1);
166662306a36Sopenharmony_ci	return false;
166762306a36Sopenharmony_ci}
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_cistatic int mlxsw_sp1_span_init(struct mlxsw_sp *mlxsw_sp)
167062306a36Sopenharmony_ci{
167162306a36Sopenharmony_ci	size_t arr_size = ARRAY_SIZE(mlxsw_sp1_span_entry_ops_arr);
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	/* Must be first to avoid NULL pointer dereference by subsequent
167462306a36Sopenharmony_ci	 * can_handle() callbacks.
167562306a36Sopenharmony_ci	 */
167662306a36Sopenharmony_ci	if (WARN_ON(mlxsw_sp1_span_entry_ops_arr[0] !=
167762306a36Sopenharmony_ci		    &mlxsw_sp1_span_entry_ops_cpu))
167862306a36Sopenharmony_ci		return -EINVAL;
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	mlxsw_sp->span->span_trigger_ops_arr = mlxsw_sp1_span_trigger_ops_arr;
168162306a36Sopenharmony_ci	mlxsw_sp->span->span_entry_ops_arr = mlxsw_sp1_span_entry_ops_arr;
168262306a36Sopenharmony_ci	mlxsw_sp->span->span_entry_ops_arr_size = arr_size;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	return 0;
168562306a36Sopenharmony_ci}
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_cistatic int mlxsw_sp1_span_policer_id_base_set(struct mlxsw_sp *mlxsw_sp,
168862306a36Sopenharmony_ci					      u16 policer_id_base)
168962306a36Sopenharmony_ci{
169062306a36Sopenharmony_ci	return -EOPNOTSUPP;
169162306a36Sopenharmony_ci}
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ciconst struct mlxsw_sp_span_ops mlxsw_sp1_span_ops = {
169462306a36Sopenharmony_ci	.init = mlxsw_sp1_span_init,
169562306a36Sopenharmony_ci	.policer_id_base_set = mlxsw_sp1_span_policer_id_base_set,
169662306a36Sopenharmony_ci};
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_cistatic int mlxsw_sp2_span_init(struct mlxsw_sp *mlxsw_sp)
169962306a36Sopenharmony_ci{
170062306a36Sopenharmony_ci	size_t arr_size = ARRAY_SIZE(mlxsw_sp2_span_entry_ops_arr);
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	/* Must be first to avoid NULL pointer dereference by subsequent
170362306a36Sopenharmony_ci	 * can_handle() callbacks.
170462306a36Sopenharmony_ci	 */
170562306a36Sopenharmony_ci	if (WARN_ON(mlxsw_sp2_span_entry_ops_arr[0] !=
170662306a36Sopenharmony_ci		    &mlxsw_sp2_span_entry_ops_cpu))
170762306a36Sopenharmony_ci		return -EINVAL;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	mlxsw_sp->span->span_trigger_ops_arr = mlxsw_sp2_span_trigger_ops_arr;
171062306a36Sopenharmony_ci	mlxsw_sp->span->span_entry_ops_arr = mlxsw_sp2_span_entry_ops_arr;
171162306a36Sopenharmony_ci	mlxsw_sp->span->span_entry_ops_arr_size = arr_size;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	return 0;
171462306a36Sopenharmony_ci}
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci#define MLXSW_SP2_SPAN_EG_MIRROR_BUFFER_FACTOR 38
171762306a36Sopenharmony_ci#define MLXSW_SP3_SPAN_EG_MIRROR_BUFFER_FACTOR 50
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_cistatic int mlxsw_sp2_span_policer_id_base_set(struct mlxsw_sp *mlxsw_sp,
172062306a36Sopenharmony_ci					      u16 policer_id_base)
172162306a36Sopenharmony_ci{
172262306a36Sopenharmony_ci	char mogcr_pl[MLXSW_REG_MOGCR_LEN];
172362306a36Sopenharmony_ci	int err;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl);
172662306a36Sopenharmony_ci	if (err)
172762306a36Sopenharmony_ci		return err;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	mlxsw_reg_mogcr_mirroring_pid_base_set(mogcr_pl, policer_id_base);
173062306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl);
173162306a36Sopenharmony_ci}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ciconst struct mlxsw_sp_span_ops mlxsw_sp2_span_ops = {
173462306a36Sopenharmony_ci	.init = mlxsw_sp2_span_init,
173562306a36Sopenharmony_ci	.policer_id_base_set = mlxsw_sp2_span_policer_id_base_set,
173662306a36Sopenharmony_ci};
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ciconst struct mlxsw_sp_span_ops mlxsw_sp3_span_ops = {
173962306a36Sopenharmony_ci	.init = mlxsw_sp2_span_init,
174062306a36Sopenharmony_ci	.policer_id_base_set = mlxsw_sp2_span_policer_id_base_set,
174162306a36Sopenharmony_ci};
1742