162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/kernel.h>
562306a36Sopenharmony_ci#include <linux/module.h>
662306a36Sopenharmony_ci#include <linux/types.h>
762306a36Sopenharmony_ci#include <linux/pci.h>
862306a36Sopenharmony_ci#include <linux/netdevice.h>
962306a36Sopenharmony_ci#include <linux/etherdevice.h>
1062306a36Sopenharmony_ci#include <linux/ethtool.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/skbuff.h>
1462306a36Sopenharmony_ci#include <linux/if_vlan.h>
1562306a36Sopenharmony_ci#include <linux/if_bridge.h>
1662306a36Sopenharmony_ci#include <linux/workqueue.h>
1762306a36Sopenharmony_ci#include <linux/jiffies.h>
1862306a36Sopenharmony_ci#include <linux/bitops.h>
1962306a36Sopenharmony_ci#include <linux/list.h>
2062306a36Sopenharmony_ci#include <linux/notifier.h>
2162306a36Sopenharmony_ci#include <linux/dcbnl.h>
2262306a36Sopenharmony_ci#include <linux/inetdevice.h>
2362306a36Sopenharmony_ci#include <linux/netlink.h>
2462306a36Sopenharmony_ci#include <linux/jhash.h>
2562306a36Sopenharmony_ci#include <linux/log2.h>
2662306a36Sopenharmony_ci#include <linux/refcount.h>
2762306a36Sopenharmony_ci#include <linux/rhashtable.h>
2862306a36Sopenharmony_ci#include <net/switchdev.h>
2962306a36Sopenharmony_ci#include <net/pkt_cls.h>
3062306a36Sopenharmony_ci#include <net/netevent.h>
3162306a36Sopenharmony_ci#include <net/addrconf.h>
3262306a36Sopenharmony_ci#include <linux/ptp_classify.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include "spectrum.h"
3562306a36Sopenharmony_ci#include "pci.h"
3662306a36Sopenharmony_ci#include "core.h"
3762306a36Sopenharmony_ci#include "core_env.h"
3862306a36Sopenharmony_ci#include "reg.h"
3962306a36Sopenharmony_ci#include "port.h"
4062306a36Sopenharmony_ci#include "trap.h"
4162306a36Sopenharmony_ci#include "txheader.h"
4262306a36Sopenharmony_ci#include "spectrum_cnt.h"
4362306a36Sopenharmony_ci#include "spectrum_dpipe.h"
4462306a36Sopenharmony_ci#include "spectrum_acl_flex_actions.h"
4562306a36Sopenharmony_ci#include "spectrum_span.h"
4662306a36Sopenharmony_ci#include "spectrum_ptp.h"
4762306a36Sopenharmony_ci#include "spectrum_trap.h"
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define MLXSW_SP_FWREV_MINOR 2010
5062306a36Sopenharmony_ci#define MLXSW_SP_FWREV_SUBMINOR 1006
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define MLXSW_SP1_FWREV_MAJOR 13
5362306a36Sopenharmony_ci#define MLXSW_SP1_FWREV_CAN_RESET_MINOR 1702
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = {
5662306a36Sopenharmony_ci	.major = MLXSW_SP1_FWREV_MAJOR,
5762306a36Sopenharmony_ci	.minor = MLXSW_SP_FWREV_MINOR,
5862306a36Sopenharmony_ci	.subminor = MLXSW_SP_FWREV_SUBMINOR,
5962306a36Sopenharmony_ci	.can_reset_minor = MLXSW_SP1_FWREV_CAN_RESET_MINOR,
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define MLXSW_SP1_FW_FILENAME \
6362306a36Sopenharmony_ci	"mellanox/mlxsw_spectrum-" __stringify(MLXSW_SP1_FWREV_MAJOR) \
6462306a36Sopenharmony_ci	"." __stringify(MLXSW_SP_FWREV_MINOR) \
6562306a36Sopenharmony_ci	"." __stringify(MLXSW_SP_FWREV_SUBMINOR) ".mfa2"
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define MLXSW_SP2_FWREV_MAJOR 29
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic const struct mlxsw_fw_rev mlxsw_sp2_fw_rev = {
7062306a36Sopenharmony_ci	.major = MLXSW_SP2_FWREV_MAJOR,
7162306a36Sopenharmony_ci	.minor = MLXSW_SP_FWREV_MINOR,
7262306a36Sopenharmony_ci	.subminor = MLXSW_SP_FWREV_SUBMINOR,
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define MLXSW_SP2_FW_FILENAME \
7662306a36Sopenharmony_ci	"mellanox/mlxsw_spectrum2-" __stringify(MLXSW_SP2_FWREV_MAJOR) \
7762306a36Sopenharmony_ci	"." __stringify(MLXSW_SP_FWREV_MINOR) \
7862306a36Sopenharmony_ci	"." __stringify(MLXSW_SP_FWREV_SUBMINOR) ".mfa2"
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci#define MLXSW_SP3_FWREV_MAJOR 30
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic const struct mlxsw_fw_rev mlxsw_sp3_fw_rev = {
8362306a36Sopenharmony_ci	.major = MLXSW_SP3_FWREV_MAJOR,
8462306a36Sopenharmony_ci	.minor = MLXSW_SP_FWREV_MINOR,
8562306a36Sopenharmony_ci	.subminor = MLXSW_SP_FWREV_SUBMINOR,
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define MLXSW_SP3_FW_FILENAME \
8962306a36Sopenharmony_ci	"mellanox/mlxsw_spectrum3-" __stringify(MLXSW_SP3_FWREV_MAJOR) \
9062306a36Sopenharmony_ci	"." __stringify(MLXSW_SP_FWREV_MINOR) \
9162306a36Sopenharmony_ci	"." __stringify(MLXSW_SP_FWREV_SUBMINOR) ".mfa2"
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define MLXSW_SP_LINECARDS_INI_BUNDLE_FILENAME \
9462306a36Sopenharmony_ci	"mellanox/lc_ini_bundle_" \
9562306a36Sopenharmony_ci	__stringify(MLXSW_SP_FWREV_MINOR) "_" \
9662306a36Sopenharmony_ci	__stringify(MLXSW_SP_FWREV_SUBMINOR) ".bin"
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic const char mlxsw_sp1_driver_name[] = "mlxsw_spectrum";
9962306a36Sopenharmony_cistatic const char mlxsw_sp2_driver_name[] = "mlxsw_spectrum2";
10062306a36Sopenharmony_cistatic const char mlxsw_sp3_driver_name[] = "mlxsw_spectrum3";
10162306a36Sopenharmony_cistatic const char mlxsw_sp4_driver_name[] = "mlxsw_spectrum4";
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic const unsigned char mlxsw_sp1_mac_mask[ETH_ALEN] = {
10462306a36Sopenharmony_ci	0xff, 0xff, 0xff, 0xff, 0xfc, 0x00
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_cistatic const unsigned char mlxsw_sp2_mac_mask[ETH_ALEN] = {
10762306a36Sopenharmony_ci	0xff, 0xff, 0xff, 0xff, 0xf0, 0x00
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci/* tx_hdr_version
11162306a36Sopenharmony_ci * Tx header version.
11262306a36Sopenharmony_ci * Must be set to 1.
11362306a36Sopenharmony_ci */
11462306a36Sopenharmony_ciMLXSW_ITEM32(tx, hdr, version, 0x00, 28, 4);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/* tx_hdr_ctl
11762306a36Sopenharmony_ci * Packet control type.
11862306a36Sopenharmony_ci * 0 - Ethernet control (e.g. EMADs, LACP)
11962306a36Sopenharmony_ci * 1 - Ethernet data
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_ciMLXSW_ITEM32(tx, hdr, ctl, 0x00, 26, 2);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/* tx_hdr_proto
12462306a36Sopenharmony_ci * Packet protocol type. Must be set to 1 (Ethernet).
12562306a36Sopenharmony_ci */
12662306a36Sopenharmony_ciMLXSW_ITEM32(tx, hdr, proto, 0x00, 21, 3);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/* tx_hdr_rx_is_router
12962306a36Sopenharmony_ci * Packet is sent from the router. Valid for data packets only.
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_ciMLXSW_ITEM32(tx, hdr, rx_is_router, 0x00, 19, 1);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/* tx_hdr_fid_valid
13462306a36Sopenharmony_ci * Indicates if the 'fid' field is valid and should be used for
13562306a36Sopenharmony_ci * forwarding lookup. Valid for data packets only.
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_ciMLXSW_ITEM32(tx, hdr, fid_valid, 0x00, 16, 1);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* tx_hdr_swid
14062306a36Sopenharmony_ci * Switch partition ID. Must be set to 0.
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_ciMLXSW_ITEM32(tx, hdr, swid, 0x00, 12, 3);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/* tx_hdr_control_tclass
14562306a36Sopenharmony_ci * Indicates if the packet should use the control TClass and not one
14662306a36Sopenharmony_ci * of the data TClasses.
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_ciMLXSW_ITEM32(tx, hdr, control_tclass, 0x00, 6, 1);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci/* tx_hdr_etclass
15162306a36Sopenharmony_ci * Egress TClass to be used on the egress device on the egress port.
15262306a36Sopenharmony_ci */
15362306a36Sopenharmony_ciMLXSW_ITEM32(tx, hdr, etclass, 0x00, 0, 4);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/* tx_hdr_port_mid
15662306a36Sopenharmony_ci * Destination local port for unicast packets.
15762306a36Sopenharmony_ci * Destination multicast ID for multicast packets.
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * Control packets are directed to a specific egress port, while data
16062306a36Sopenharmony_ci * packets are transmitted through the CPU port (0) into the switch partition,
16162306a36Sopenharmony_ci * where forwarding rules are applied.
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_ciMLXSW_ITEM32(tx, hdr, port_mid, 0x04, 16, 16);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci/* tx_hdr_fid
16662306a36Sopenharmony_ci * Forwarding ID used for L2 forwarding lookup. Valid only if 'fid_valid' is
16762306a36Sopenharmony_ci * set, otherwise calculated based on the packet's VID using VID to FID mapping.
16862306a36Sopenharmony_ci * Valid for data packets only.
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_ciMLXSW_ITEM32(tx, hdr, fid, 0x08, 16, 16);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci/* tx_hdr_type
17362306a36Sopenharmony_ci * 0 - Data packets
17462306a36Sopenharmony_ci * 6 - Control packets
17562306a36Sopenharmony_ci */
17662306a36Sopenharmony_ciMLXSW_ITEM32(tx, hdr, type, 0x0C, 0, 4);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ciint mlxsw_sp_flow_counter_get(struct mlxsw_sp *mlxsw_sp,
17962306a36Sopenharmony_ci			      unsigned int counter_index, u64 *packets,
18062306a36Sopenharmony_ci			      u64 *bytes)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	char mgpc_pl[MLXSW_REG_MGPC_LEN];
18362306a36Sopenharmony_ci	int err;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	mlxsw_reg_mgpc_pack(mgpc_pl, counter_index, MLXSW_REG_MGPC_OPCODE_NOP,
18662306a36Sopenharmony_ci			    MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS_BYTES);
18762306a36Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mgpc), mgpc_pl);
18862306a36Sopenharmony_ci	if (err)
18962306a36Sopenharmony_ci		return err;
19062306a36Sopenharmony_ci	if (packets)
19162306a36Sopenharmony_ci		*packets = mlxsw_reg_mgpc_packet_counter_get(mgpc_pl);
19262306a36Sopenharmony_ci	if (bytes)
19362306a36Sopenharmony_ci		*bytes = mlxsw_reg_mgpc_byte_counter_get(mgpc_pl);
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int mlxsw_sp_flow_counter_clear(struct mlxsw_sp *mlxsw_sp,
19862306a36Sopenharmony_ci				       unsigned int counter_index)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	char mgpc_pl[MLXSW_REG_MGPC_LEN];
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	mlxsw_reg_mgpc_pack(mgpc_pl, counter_index, MLXSW_REG_MGPC_OPCODE_CLEAR,
20362306a36Sopenharmony_ci			    MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS_BYTES);
20462306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mgpc), mgpc_pl);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ciint mlxsw_sp_flow_counter_alloc(struct mlxsw_sp *mlxsw_sp,
20862306a36Sopenharmony_ci				unsigned int *p_counter_index)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	int err;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_FLOW,
21362306a36Sopenharmony_ci				     p_counter_index);
21462306a36Sopenharmony_ci	if (err)
21562306a36Sopenharmony_ci		return err;
21662306a36Sopenharmony_ci	err = mlxsw_sp_flow_counter_clear(mlxsw_sp, *p_counter_index);
21762306a36Sopenharmony_ci	if (err)
21862306a36Sopenharmony_ci		goto err_counter_clear;
21962306a36Sopenharmony_ci	return 0;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cierr_counter_clear:
22262306a36Sopenharmony_ci	mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_FLOW,
22362306a36Sopenharmony_ci			      *p_counter_index);
22462306a36Sopenharmony_ci	return err;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_civoid mlxsw_sp_flow_counter_free(struct mlxsw_sp *mlxsw_sp,
22862306a36Sopenharmony_ci				unsigned int counter_index)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_FLOW,
23162306a36Sopenharmony_ci			       counter_index);
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_civoid mlxsw_sp_txhdr_construct(struct sk_buff *skb,
23562306a36Sopenharmony_ci			      const struct mlxsw_tx_info *tx_info)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	char *txhdr = skb_push(skb, MLXSW_TXHDR_LEN);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	memset(txhdr, 0, MLXSW_TXHDR_LEN);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_1);
24262306a36Sopenharmony_ci	mlxsw_tx_hdr_ctl_set(txhdr, MLXSW_TXHDR_ETH_CTL);
24362306a36Sopenharmony_ci	mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH);
24462306a36Sopenharmony_ci	mlxsw_tx_hdr_swid_set(txhdr, 0);
24562306a36Sopenharmony_ci	mlxsw_tx_hdr_control_tclass_set(txhdr, 1);
24662306a36Sopenharmony_ci	mlxsw_tx_hdr_port_mid_set(txhdr, tx_info->local_port);
24762306a36Sopenharmony_ci	mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_CONTROL);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ciint
25162306a36Sopenharmony_cimlxsw_sp_txhdr_ptp_data_construct(struct mlxsw_core *mlxsw_core,
25262306a36Sopenharmony_ci				  struct mlxsw_sp_port *mlxsw_sp_port,
25362306a36Sopenharmony_ci				  struct sk_buff *skb,
25462306a36Sopenharmony_ci				  const struct mlxsw_tx_info *tx_info)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	char *txhdr;
25762306a36Sopenharmony_ci	u16 max_fid;
25862306a36Sopenharmony_ci	int err;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (skb_cow_head(skb, MLXSW_TXHDR_LEN)) {
26162306a36Sopenharmony_ci		err = -ENOMEM;
26262306a36Sopenharmony_ci		goto err_skb_cow_head;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_core, FID)) {
26662306a36Sopenharmony_ci		err = -EIO;
26762306a36Sopenharmony_ci		goto err_res_valid;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci	max_fid = MLXSW_CORE_RES_GET(mlxsw_core, FID);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	txhdr = skb_push(skb, MLXSW_TXHDR_LEN);
27262306a36Sopenharmony_ci	memset(txhdr, 0, MLXSW_TXHDR_LEN);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_1);
27562306a36Sopenharmony_ci	mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH);
27662306a36Sopenharmony_ci	mlxsw_tx_hdr_rx_is_router_set(txhdr, true);
27762306a36Sopenharmony_ci	mlxsw_tx_hdr_fid_valid_set(txhdr, true);
27862306a36Sopenharmony_ci	mlxsw_tx_hdr_fid_set(txhdr, max_fid + tx_info->local_port - 1);
27962306a36Sopenharmony_ci	mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_DATA);
28062306a36Sopenharmony_ci	return 0;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cierr_res_valid:
28362306a36Sopenharmony_cierr_skb_cow_head:
28462306a36Sopenharmony_ci	this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
28562306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
28662306a36Sopenharmony_ci	return err;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic bool mlxsw_sp_skb_requires_ts(struct sk_buff *skb)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	unsigned int type;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
29462306a36Sopenharmony_ci		return false;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	type = ptp_classify_raw(skb);
29762306a36Sopenharmony_ci	return !!ptp_parse_header(skb, type);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic int mlxsw_sp_txhdr_handle(struct mlxsw_core *mlxsw_core,
30162306a36Sopenharmony_ci				 struct mlxsw_sp_port *mlxsw_sp_port,
30262306a36Sopenharmony_ci				 struct sk_buff *skb,
30362306a36Sopenharmony_ci				 const struct mlxsw_tx_info *tx_info)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* In Spectrum-2 and Spectrum-3, PTP events that require a time stamp
30862306a36Sopenharmony_ci	 * need special handling and cannot be transmitted as regular control
30962306a36Sopenharmony_ci	 * packets.
31062306a36Sopenharmony_ci	 */
31162306a36Sopenharmony_ci	if (unlikely(mlxsw_sp_skb_requires_ts(skb)))
31262306a36Sopenharmony_ci		return mlxsw_sp->ptp_ops->txhdr_construct(mlxsw_core,
31362306a36Sopenharmony_ci							  mlxsw_sp_port, skb,
31462306a36Sopenharmony_ci							  tx_info);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (skb_cow_head(skb, MLXSW_TXHDR_LEN)) {
31762306a36Sopenharmony_ci		this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
31862306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
31962306a36Sopenharmony_ci		return -ENOMEM;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	mlxsw_sp_txhdr_construct(skb, tx_info);
32362306a36Sopenharmony_ci	return 0;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cienum mlxsw_reg_spms_state mlxsw_sp_stp_spms_state(u8 state)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	switch (state) {
32962306a36Sopenharmony_ci	case BR_STATE_FORWARDING:
33062306a36Sopenharmony_ci		return MLXSW_REG_SPMS_STATE_FORWARDING;
33162306a36Sopenharmony_ci	case BR_STATE_LEARNING:
33262306a36Sopenharmony_ci		return MLXSW_REG_SPMS_STATE_LEARNING;
33362306a36Sopenharmony_ci	case BR_STATE_LISTENING:
33462306a36Sopenharmony_ci	case BR_STATE_DISABLED:
33562306a36Sopenharmony_ci	case BR_STATE_BLOCKING:
33662306a36Sopenharmony_ci		return MLXSW_REG_SPMS_STATE_DISCARDING;
33762306a36Sopenharmony_ci	default:
33862306a36Sopenharmony_ci		BUG();
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ciint mlxsw_sp_port_vid_stp_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid,
34362306a36Sopenharmony_ci			      u8 state)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	enum mlxsw_reg_spms_state spms_state = mlxsw_sp_stp_spms_state(state);
34662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
34762306a36Sopenharmony_ci	char *spms_pl;
34862306a36Sopenharmony_ci	int err;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL);
35162306a36Sopenharmony_ci	if (!spms_pl)
35262306a36Sopenharmony_ci		return -ENOMEM;
35362306a36Sopenharmony_ci	mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port);
35462306a36Sopenharmony_ci	mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl);
35762306a36Sopenharmony_ci	kfree(spms_pl);
35862306a36Sopenharmony_ci	return err;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic int mlxsw_sp_base_mac_get(struct mlxsw_sp *mlxsw_sp)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	char spad_pl[MLXSW_REG_SPAD_LEN] = {0};
36462306a36Sopenharmony_ci	int err;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(spad), spad_pl);
36762306a36Sopenharmony_ci	if (err)
36862306a36Sopenharmony_ci		return err;
36962306a36Sopenharmony_ci	mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_sp->base_mac);
37062306a36Sopenharmony_ci	return 0;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ciint mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port,
37462306a36Sopenharmony_ci				   bool is_up)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
37762306a36Sopenharmony_ci	char paos_pl[MLXSW_REG_PAOS_LEN];
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	mlxsw_reg_paos_pack(paos_pl, mlxsw_sp_port->local_port,
38062306a36Sopenharmony_ci			    is_up ? MLXSW_PORT_ADMIN_STATUS_UP :
38162306a36Sopenharmony_ci			    MLXSW_PORT_ADMIN_STATUS_DOWN);
38262306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(paos), paos_pl);
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic int mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port *mlxsw_sp_port,
38662306a36Sopenharmony_ci				      const unsigned char *addr)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
38962306a36Sopenharmony_ci	char ppad_pl[MLXSW_REG_PPAD_LEN];
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	mlxsw_reg_ppad_pack(ppad_pl, true, mlxsw_sp_port->local_port);
39262306a36Sopenharmony_ci	mlxsw_reg_ppad_mac_memcpy_to(ppad_pl, addr);
39362306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppad), ppad_pl);
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic int mlxsw_sp_port_dev_addr_init(struct mlxsw_sp_port *mlxsw_sp_port)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	eth_hw_addr_gen(mlxsw_sp_port->dev, mlxsw_sp->base_mac,
40162306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
40262306a36Sopenharmony_ci	return mlxsw_sp_port_dev_addr_set(mlxsw_sp_port,
40362306a36Sopenharmony_ci					  mlxsw_sp_port->dev->dev_addr);
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic int mlxsw_sp_port_max_mtu_get(struct mlxsw_sp_port *mlxsw_sp_port, int *p_max_mtu)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
40962306a36Sopenharmony_ci	char pmtu_pl[MLXSW_REG_PMTU_LEN];
41062306a36Sopenharmony_ci	int err;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sp_port->local_port, 0);
41362306a36Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pmtu), pmtu_pl);
41462306a36Sopenharmony_ci	if (err)
41562306a36Sopenharmony_ci		return err;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	*p_max_mtu = mlxsw_reg_pmtu_max_mtu_get(pmtu_pl);
41862306a36Sopenharmony_ci	return 0;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic int mlxsw_sp_port_mtu_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
42462306a36Sopenharmony_ci	char pmtu_pl[MLXSW_REG_PMTU_LEN];
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	mtu += MLXSW_TXHDR_LEN + ETH_HLEN;
42762306a36Sopenharmony_ci	if (mtu > mlxsw_sp_port->max_mtu)
42862306a36Sopenharmony_ci		return -EINVAL;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sp_port->local_port, mtu);
43162306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmtu), pmtu_pl);
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic int mlxsw_sp_port_swid_set(struct mlxsw_sp *mlxsw_sp,
43562306a36Sopenharmony_ci				  u16 local_port, u8 swid)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	char pspa_pl[MLXSW_REG_PSPA_LEN];
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	mlxsw_reg_pspa_pack(pspa_pl, swid, local_port);
44062306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pspa), pspa_pl);
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ciint mlxsw_sp_port_vp_mode_set(struct mlxsw_sp_port *mlxsw_sp_port, bool enable)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
44662306a36Sopenharmony_ci	char svpe_pl[MLXSW_REG_SVPE_LEN];
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	mlxsw_reg_svpe_pack(svpe_pl, mlxsw_sp_port->local_port, enable);
44962306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svpe), svpe_pl);
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ciint mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid,
45362306a36Sopenharmony_ci				   bool learn_enable)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
45662306a36Sopenharmony_ci	char *spvmlr_pl;
45762306a36Sopenharmony_ci	int err;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	spvmlr_pl = kmalloc(MLXSW_REG_SPVMLR_LEN, GFP_KERNEL);
46062306a36Sopenharmony_ci	if (!spvmlr_pl)
46162306a36Sopenharmony_ci		return -ENOMEM;
46262306a36Sopenharmony_ci	mlxsw_reg_spvmlr_pack(spvmlr_pl, mlxsw_sp_port->local_port, vid, vid,
46362306a36Sopenharmony_ci			      learn_enable);
46462306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvmlr), spvmlr_pl);
46562306a36Sopenharmony_ci	kfree(spvmlr_pl);
46662306a36Sopenharmony_ci	return err;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ciint mlxsw_sp_port_security_set(struct mlxsw_sp_port *mlxsw_sp_port, bool enable)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
47262306a36Sopenharmony_ci	char spfsr_pl[MLXSW_REG_SPFSR_LEN];
47362306a36Sopenharmony_ci	int err;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (mlxsw_sp_port->security == enable)
47662306a36Sopenharmony_ci		return 0;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	mlxsw_reg_spfsr_pack(spfsr_pl, mlxsw_sp_port->local_port, enable);
47962306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spfsr), spfsr_pl);
48062306a36Sopenharmony_ci	if (err)
48162306a36Sopenharmony_ci		return err;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	mlxsw_sp_port->security = enable;
48462306a36Sopenharmony_ci	return 0;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ciint mlxsw_sp_ethtype_to_sver_type(u16 ethtype, u8 *p_sver_type)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	switch (ethtype) {
49062306a36Sopenharmony_ci	case ETH_P_8021Q:
49162306a36Sopenharmony_ci		*p_sver_type = 0;
49262306a36Sopenharmony_ci		break;
49362306a36Sopenharmony_ci	case ETH_P_8021AD:
49462306a36Sopenharmony_ci		*p_sver_type = 1;
49562306a36Sopenharmony_ci		break;
49662306a36Sopenharmony_ci	default:
49762306a36Sopenharmony_ci		return -EINVAL;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	return 0;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ciint mlxsw_sp_port_egress_ethtype_set(struct mlxsw_sp_port *mlxsw_sp_port,
50462306a36Sopenharmony_ci				     u16 ethtype)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
50762306a36Sopenharmony_ci	char spevet_pl[MLXSW_REG_SPEVET_LEN];
50862306a36Sopenharmony_ci	u8 sver_type;
50962306a36Sopenharmony_ci	int err;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	err = mlxsw_sp_ethtype_to_sver_type(ethtype, &sver_type);
51262306a36Sopenharmony_ci	if (err)
51362306a36Sopenharmony_ci		return err;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	mlxsw_reg_spevet_pack(spevet_pl, mlxsw_sp_port->local_port, sver_type);
51662306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spevet), spevet_pl);
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic int __mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port,
52062306a36Sopenharmony_ci				    u16 vid, u16 ethtype)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
52362306a36Sopenharmony_ci	char spvid_pl[MLXSW_REG_SPVID_LEN];
52462306a36Sopenharmony_ci	u8 sver_type;
52562306a36Sopenharmony_ci	int err;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	err = mlxsw_sp_ethtype_to_sver_type(ethtype, &sver_type);
52862306a36Sopenharmony_ci	if (err)
52962306a36Sopenharmony_ci		return err;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	mlxsw_reg_spvid_pack(spvid_pl, mlxsw_sp_port->local_port, vid,
53262306a36Sopenharmony_ci			     sver_type);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvid), spvid_pl);
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic int mlxsw_sp_port_allow_untagged_set(struct mlxsw_sp_port *mlxsw_sp_port,
53862306a36Sopenharmony_ci					    bool allow)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
54162306a36Sopenharmony_ci	char spaft_pl[MLXSW_REG_SPAFT_LEN];
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	mlxsw_reg_spaft_pack(spaft_pl, mlxsw_sp_port->local_port, allow);
54462306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spaft), spaft_pl);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ciint mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid,
54862306a36Sopenharmony_ci			   u16 ethtype)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	int err;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (!vid) {
55362306a36Sopenharmony_ci		err = mlxsw_sp_port_allow_untagged_set(mlxsw_sp_port, false);
55462306a36Sopenharmony_ci		if (err)
55562306a36Sopenharmony_ci			return err;
55662306a36Sopenharmony_ci	} else {
55762306a36Sopenharmony_ci		err = __mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid, ethtype);
55862306a36Sopenharmony_ci		if (err)
55962306a36Sopenharmony_ci			return err;
56062306a36Sopenharmony_ci		err = mlxsw_sp_port_allow_untagged_set(mlxsw_sp_port, true);
56162306a36Sopenharmony_ci		if (err)
56262306a36Sopenharmony_ci			goto err_port_allow_untagged_set;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	mlxsw_sp_port->pvid = vid;
56662306a36Sopenharmony_ci	return 0;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cierr_port_allow_untagged_set:
56962306a36Sopenharmony_ci	__mlxsw_sp_port_pvid_set(mlxsw_sp_port, mlxsw_sp_port->pvid, ethtype);
57062306a36Sopenharmony_ci	return err;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic int
57462306a36Sopenharmony_cimlxsw_sp_port_system_port_mapping_set(struct mlxsw_sp_port *mlxsw_sp_port)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
57762306a36Sopenharmony_ci	char sspr_pl[MLXSW_REG_SSPR_LEN];
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	mlxsw_reg_sspr_pack(sspr_pl, mlxsw_sp_port->local_port);
58062306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sspr), sspr_pl);
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic int
58462306a36Sopenharmony_cimlxsw_sp_port_module_info_parse(struct mlxsw_sp *mlxsw_sp,
58562306a36Sopenharmony_ci				u16 local_port, char *pmlp_pl,
58662306a36Sopenharmony_ci				struct mlxsw_sp_port_mapping *port_mapping)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	bool separate_rxtx;
58962306a36Sopenharmony_ci	u8 first_lane;
59062306a36Sopenharmony_ci	u8 slot_index;
59162306a36Sopenharmony_ci	u8 module;
59262306a36Sopenharmony_ci	u8 width;
59362306a36Sopenharmony_ci	int i;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);
59662306a36Sopenharmony_ci	slot_index = mlxsw_reg_pmlp_slot_index_get(pmlp_pl, 0);
59762306a36Sopenharmony_ci	width = mlxsw_reg_pmlp_width_get(pmlp_pl);
59862306a36Sopenharmony_ci	separate_rxtx = mlxsw_reg_pmlp_rxtx_get(pmlp_pl);
59962306a36Sopenharmony_ci	first_lane = mlxsw_reg_pmlp_tx_lane_get(pmlp_pl, 0);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (width && !is_power_of_2(width)) {
60262306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unsupported module config: width value is not power of 2\n",
60362306a36Sopenharmony_ci			local_port);
60462306a36Sopenharmony_ci		return -EINVAL;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	for (i = 0; i < width; i++) {
60862306a36Sopenharmony_ci		if (mlxsw_reg_pmlp_module_get(pmlp_pl, i) != module) {
60962306a36Sopenharmony_ci			dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unsupported module config: contains multiple modules\n",
61062306a36Sopenharmony_ci				local_port);
61162306a36Sopenharmony_ci			return -EINVAL;
61262306a36Sopenharmony_ci		}
61362306a36Sopenharmony_ci		if (mlxsw_reg_pmlp_slot_index_get(pmlp_pl, i) != slot_index) {
61462306a36Sopenharmony_ci			dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unsupported module config: contains multiple slot indexes\n",
61562306a36Sopenharmony_ci				local_port);
61662306a36Sopenharmony_ci			return -EINVAL;
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci		if (separate_rxtx &&
61962306a36Sopenharmony_ci		    mlxsw_reg_pmlp_tx_lane_get(pmlp_pl, i) !=
62062306a36Sopenharmony_ci		    mlxsw_reg_pmlp_rx_lane_get(pmlp_pl, i)) {
62162306a36Sopenharmony_ci			dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unsupported module config: TX and RX lane numbers are different\n",
62262306a36Sopenharmony_ci				local_port);
62362306a36Sopenharmony_ci			return -EINVAL;
62462306a36Sopenharmony_ci		}
62562306a36Sopenharmony_ci		if (mlxsw_reg_pmlp_tx_lane_get(pmlp_pl, i) != i + first_lane) {
62662306a36Sopenharmony_ci			dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unsupported module config: TX and RX lane numbers are not sequential\n",
62762306a36Sopenharmony_ci				local_port);
62862306a36Sopenharmony_ci			return -EINVAL;
62962306a36Sopenharmony_ci		}
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	port_mapping->module = module;
63362306a36Sopenharmony_ci	port_mapping->slot_index = slot_index;
63462306a36Sopenharmony_ci	port_mapping->width = width;
63562306a36Sopenharmony_ci	port_mapping->module_width = width;
63662306a36Sopenharmony_ci	port_mapping->lane = mlxsw_reg_pmlp_tx_lane_get(pmlp_pl, 0);
63762306a36Sopenharmony_ci	return 0;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic int
64162306a36Sopenharmony_cimlxsw_sp_port_module_info_get(struct mlxsw_sp *mlxsw_sp, u16 local_port,
64262306a36Sopenharmony_ci			      struct mlxsw_sp_port_mapping *port_mapping)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	char pmlp_pl[MLXSW_REG_PMLP_LEN];
64562306a36Sopenharmony_ci	int err;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
64862306a36Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl);
64962306a36Sopenharmony_ci	if (err)
65062306a36Sopenharmony_ci		return err;
65162306a36Sopenharmony_ci	return mlxsw_sp_port_module_info_parse(mlxsw_sp, local_port,
65262306a36Sopenharmony_ci					       pmlp_pl, port_mapping);
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic int
65662306a36Sopenharmony_cimlxsw_sp_port_module_map(struct mlxsw_sp *mlxsw_sp, u16 local_port,
65762306a36Sopenharmony_ci			 const struct mlxsw_sp_port_mapping *port_mapping)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	char pmlp_pl[MLXSW_REG_PMLP_LEN];
66062306a36Sopenharmony_ci	int i, err;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	mlxsw_env_module_port_map(mlxsw_sp->core, port_mapping->slot_index,
66362306a36Sopenharmony_ci				  port_mapping->module);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
66662306a36Sopenharmony_ci	mlxsw_reg_pmlp_width_set(pmlp_pl, port_mapping->width);
66762306a36Sopenharmony_ci	for (i = 0; i < port_mapping->width; i++) {
66862306a36Sopenharmony_ci		mlxsw_reg_pmlp_slot_index_set(pmlp_pl, i,
66962306a36Sopenharmony_ci					      port_mapping->slot_index);
67062306a36Sopenharmony_ci		mlxsw_reg_pmlp_module_set(pmlp_pl, i, port_mapping->module);
67162306a36Sopenharmony_ci		mlxsw_reg_pmlp_tx_lane_set(pmlp_pl, i, port_mapping->lane + i); /* Rx & Tx */
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl);
67562306a36Sopenharmony_ci	if (err)
67662306a36Sopenharmony_ci		goto err_pmlp_write;
67762306a36Sopenharmony_ci	return 0;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_cierr_pmlp_write:
68062306a36Sopenharmony_ci	mlxsw_env_module_port_unmap(mlxsw_sp->core, port_mapping->slot_index,
68162306a36Sopenharmony_ci				    port_mapping->module);
68262306a36Sopenharmony_ci	return err;
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_cistatic void mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u16 local_port,
68662306a36Sopenharmony_ci				       u8 slot_index, u8 module)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	char pmlp_pl[MLXSW_REG_PMLP_LEN];
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
69162306a36Sopenharmony_ci	mlxsw_reg_pmlp_width_set(pmlp_pl, 0);
69262306a36Sopenharmony_ci	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl);
69362306a36Sopenharmony_ci	mlxsw_env_module_port_unmap(mlxsw_sp->core, slot_index, module);
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cistatic int mlxsw_sp_port_open(struct net_device *dev)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
69962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
70062306a36Sopenharmony_ci	int err;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	err = mlxsw_env_module_port_up(mlxsw_sp->core,
70362306a36Sopenharmony_ci				       mlxsw_sp_port->mapping.slot_index,
70462306a36Sopenharmony_ci				       mlxsw_sp_port->mapping.module);
70562306a36Sopenharmony_ci	if (err)
70662306a36Sopenharmony_ci		return err;
70762306a36Sopenharmony_ci	err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true);
70862306a36Sopenharmony_ci	if (err)
70962306a36Sopenharmony_ci		goto err_port_admin_status_set;
71062306a36Sopenharmony_ci	netif_start_queue(dev);
71162306a36Sopenharmony_ci	return 0;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cierr_port_admin_status_set:
71462306a36Sopenharmony_ci	mlxsw_env_module_port_down(mlxsw_sp->core,
71562306a36Sopenharmony_ci				   mlxsw_sp_port->mapping.slot_index,
71662306a36Sopenharmony_ci				   mlxsw_sp_port->mapping.module);
71762306a36Sopenharmony_ci	return err;
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic int mlxsw_sp_port_stop(struct net_device *dev)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
72362306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	netif_stop_queue(dev);
72662306a36Sopenharmony_ci	mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
72762306a36Sopenharmony_ci	mlxsw_env_module_port_down(mlxsw_sp->core,
72862306a36Sopenharmony_ci				   mlxsw_sp_port->mapping.slot_index,
72962306a36Sopenharmony_ci				   mlxsw_sp_port->mapping.module);
73062306a36Sopenharmony_ci	return 0;
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_cistatic netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb,
73462306a36Sopenharmony_ci				      struct net_device *dev)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
73762306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
73862306a36Sopenharmony_ci	struct mlxsw_sp_port_pcpu_stats *pcpu_stats;
73962306a36Sopenharmony_ci	const struct mlxsw_tx_info tx_info = {
74062306a36Sopenharmony_ci		.local_port = mlxsw_sp_port->local_port,
74162306a36Sopenharmony_ci		.is_emad = false,
74262306a36Sopenharmony_ci	};
74362306a36Sopenharmony_ci	u64 len;
74462306a36Sopenharmony_ci	int err;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	memset(skb->cb, 0, sizeof(struct mlxsw_skb_cb));
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	if (mlxsw_core_skb_transmit_busy(mlxsw_sp->core, &tx_info))
74962306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	if (eth_skb_pad(skb)) {
75262306a36Sopenharmony_ci		this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
75362306a36Sopenharmony_ci		return NETDEV_TX_OK;
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	err = mlxsw_sp_txhdr_handle(mlxsw_sp->core, mlxsw_sp_port, skb,
75762306a36Sopenharmony_ci				    &tx_info);
75862306a36Sopenharmony_ci	if (err)
75962306a36Sopenharmony_ci		return NETDEV_TX_OK;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	/* TX header is consumed by HW on the way so we shouldn't count its
76262306a36Sopenharmony_ci	 * bytes as being sent.
76362306a36Sopenharmony_ci	 */
76462306a36Sopenharmony_ci	len = skb->len - MLXSW_TXHDR_LEN;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/* Due to a race we might fail here because of a full queue. In that
76762306a36Sopenharmony_ci	 * unlikely case we simply drop the packet.
76862306a36Sopenharmony_ci	 */
76962306a36Sopenharmony_ci	err = mlxsw_core_skb_transmit(mlxsw_sp->core, skb, &tx_info);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (!err) {
77262306a36Sopenharmony_ci		pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats);
77362306a36Sopenharmony_ci		u64_stats_update_begin(&pcpu_stats->syncp);
77462306a36Sopenharmony_ci		pcpu_stats->tx_packets++;
77562306a36Sopenharmony_ci		pcpu_stats->tx_bytes += len;
77662306a36Sopenharmony_ci		u64_stats_update_end(&pcpu_stats->syncp);
77762306a36Sopenharmony_ci	} else {
77862306a36Sopenharmony_ci		this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
77962306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci	return NETDEV_TX_OK;
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_cistatic void mlxsw_sp_set_rx_mode(struct net_device *dev)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic int mlxsw_sp_port_set_mac_address(struct net_device *dev, void *p)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
79162306a36Sopenharmony_ci	struct sockaddr *addr = p;
79262306a36Sopenharmony_ci	int err;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
79562306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	err = mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, addr->sa_data);
79862306a36Sopenharmony_ci	if (err)
79962306a36Sopenharmony_ci		return err;
80062306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr->sa_data);
80162306a36Sopenharmony_ci	return 0;
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic int mlxsw_sp_port_change_mtu(struct net_device *dev, int mtu)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
80762306a36Sopenharmony_ci	struct mlxsw_sp_hdroom orig_hdroom;
80862306a36Sopenharmony_ci	struct mlxsw_sp_hdroom hdroom;
80962306a36Sopenharmony_ci	int err;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	orig_hdroom = *mlxsw_sp_port->hdroom;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	hdroom = orig_hdroom;
81462306a36Sopenharmony_ci	hdroom.mtu = mtu;
81562306a36Sopenharmony_ci	mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
81862306a36Sopenharmony_ci	if (err) {
81962306a36Sopenharmony_ci		netdev_err(dev, "Failed to configure port's headroom\n");
82062306a36Sopenharmony_ci		return err;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, mtu);
82462306a36Sopenharmony_ci	if (err)
82562306a36Sopenharmony_ci		goto err_port_mtu_set;
82662306a36Sopenharmony_ci	dev->mtu = mtu;
82762306a36Sopenharmony_ci	return 0;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cierr_port_mtu_set:
83062306a36Sopenharmony_ci	mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom);
83162306a36Sopenharmony_ci	return err;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cistatic int
83562306a36Sopenharmony_cimlxsw_sp_port_get_sw_stats64(const struct net_device *dev,
83662306a36Sopenharmony_ci			     struct rtnl_link_stats64 *stats)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
83962306a36Sopenharmony_ci	struct mlxsw_sp_port_pcpu_stats *p;
84062306a36Sopenharmony_ci	u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
84162306a36Sopenharmony_ci	u32 tx_dropped = 0;
84262306a36Sopenharmony_ci	unsigned int start;
84362306a36Sopenharmony_ci	int i;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	for_each_possible_cpu(i) {
84662306a36Sopenharmony_ci		p = per_cpu_ptr(mlxsw_sp_port->pcpu_stats, i);
84762306a36Sopenharmony_ci		do {
84862306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&p->syncp);
84962306a36Sopenharmony_ci			rx_packets	= p->rx_packets;
85062306a36Sopenharmony_ci			rx_bytes	= p->rx_bytes;
85162306a36Sopenharmony_ci			tx_packets	= p->tx_packets;
85262306a36Sopenharmony_ci			tx_bytes	= p->tx_bytes;
85362306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&p->syncp, start));
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci		stats->rx_packets	+= rx_packets;
85662306a36Sopenharmony_ci		stats->rx_bytes		+= rx_bytes;
85762306a36Sopenharmony_ci		stats->tx_packets	+= tx_packets;
85862306a36Sopenharmony_ci		stats->tx_bytes		+= tx_bytes;
85962306a36Sopenharmony_ci		/* tx_dropped is u32, updated without syncp protection. */
86062306a36Sopenharmony_ci		tx_dropped	+= p->tx_dropped;
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci	stats->tx_dropped	= tx_dropped;
86362306a36Sopenharmony_ci	return 0;
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_cistatic bool mlxsw_sp_port_has_offload_stats(const struct net_device *dev, int attr_id)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	switch (attr_id) {
86962306a36Sopenharmony_ci	case IFLA_OFFLOAD_XSTATS_CPU_HIT:
87062306a36Sopenharmony_ci		return true;
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	return false;
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_cistatic int mlxsw_sp_port_get_offload_stats(int attr_id, const struct net_device *dev,
87762306a36Sopenharmony_ci					   void *sp)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	switch (attr_id) {
88062306a36Sopenharmony_ci	case IFLA_OFFLOAD_XSTATS_CPU_HIT:
88162306a36Sopenharmony_ci		return mlxsw_sp_port_get_sw_stats64(dev, sp);
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	return -EINVAL;
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ciint mlxsw_sp_port_get_stats_raw(struct net_device *dev, int grp,
88862306a36Sopenharmony_ci				int prio, char *ppcnt_pl)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
89162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port, grp, prio);
89462306a36Sopenharmony_ci	return mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ppcnt), ppcnt_pl);
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cistatic int mlxsw_sp_port_get_hw_stats(struct net_device *dev,
89862306a36Sopenharmony_ci				      struct rtnl_link_stats64 *stats)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
90162306a36Sopenharmony_ci	int err;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	err = mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT,
90462306a36Sopenharmony_ci					  0, ppcnt_pl);
90562306a36Sopenharmony_ci	if (err)
90662306a36Sopenharmony_ci		goto out;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	stats->tx_packets =
90962306a36Sopenharmony_ci		mlxsw_reg_ppcnt_a_frames_transmitted_ok_get(ppcnt_pl);
91062306a36Sopenharmony_ci	stats->rx_packets =
91162306a36Sopenharmony_ci		mlxsw_reg_ppcnt_a_frames_received_ok_get(ppcnt_pl);
91262306a36Sopenharmony_ci	stats->tx_bytes =
91362306a36Sopenharmony_ci		mlxsw_reg_ppcnt_a_octets_transmitted_ok_get(ppcnt_pl);
91462306a36Sopenharmony_ci	stats->rx_bytes =
91562306a36Sopenharmony_ci		mlxsw_reg_ppcnt_a_octets_received_ok_get(ppcnt_pl);
91662306a36Sopenharmony_ci	stats->multicast =
91762306a36Sopenharmony_ci		mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get(ppcnt_pl);
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	stats->rx_crc_errors =
92062306a36Sopenharmony_ci		mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get(ppcnt_pl);
92162306a36Sopenharmony_ci	stats->rx_frame_errors =
92262306a36Sopenharmony_ci		mlxsw_reg_ppcnt_a_alignment_errors_get(ppcnt_pl);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	stats->rx_length_errors = (
92562306a36Sopenharmony_ci		mlxsw_reg_ppcnt_a_in_range_length_errors_get(ppcnt_pl) +
92662306a36Sopenharmony_ci		mlxsw_reg_ppcnt_a_out_of_range_length_field_get(ppcnt_pl) +
92762306a36Sopenharmony_ci		mlxsw_reg_ppcnt_a_frame_too_long_errors_get(ppcnt_pl));
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	stats->rx_errors = (stats->rx_crc_errors +
93062306a36Sopenharmony_ci		stats->rx_frame_errors + stats->rx_length_errors);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ciout:
93362306a36Sopenharmony_ci	return err;
93462306a36Sopenharmony_ci}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_cistatic void
93762306a36Sopenharmony_cimlxsw_sp_port_get_hw_xstats(struct net_device *dev,
93862306a36Sopenharmony_ci			    struct mlxsw_sp_port_xstats *xstats)
93962306a36Sopenharmony_ci{
94062306a36Sopenharmony_ci	char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
94162306a36Sopenharmony_ci	int err, i;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	err = mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_EXT_CNT, 0,
94462306a36Sopenharmony_ci					  ppcnt_pl);
94562306a36Sopenharmony_ci	if (!err)
94662306a36Sopenharmony_ci		xstats->ecn = mlxsw_reg_ppcnt_ecn_marked_get(ppcnt_pl);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	for (i = 0; i < TC_MAX_QUEUE; i++) {
94962306a36Sopenharmony_ci		err = mlxsw_sp_port_get_stats_raw(dev,
95062306a36Sopenharmony_ci						  MLXSW_REG_PPCNT_TC_CONG_CNT,
95162306a36Sopenharmony_ci						  i, ppcnt_pl);
95262306a36Sopenharmony_ci		if (err)
95362306a36Sopenharmony_ci			goto tc_cnt;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci		xstats->wred_drop[i] =
95662306a36Sopenharmony_ci			mlxsw_reg_ppcnt_wred_discard_get(ppcnt_pl);
95762306a36Sopenharmony_ci		xstats->tc_ecn[i] = mlxsw_reg_ppcnt_ecn_marked_tc_get(ppcnt_pl);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_citc_cnt:
96062306a36Sopenharmony_ci		err = mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_TC_CNT,
96162306a36Sopenharmony_ci						  i, ppcnt_pl);
96262306a36Sopenharmony_ci		if (err)
96362306a36Sopenharmony_ci			continue;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci		xstats->backlog[i] =
96662306a36Sopenharmony_ci			mlxsw_reg_ppcnt_tc_transmit_queue_get(ppcnt_pl);
96762306a36Sopenharmony_ci		xstats->tail_drop[i] =
96862306a36Sopenharmony_ci			mlxsw_reg_ppcnt_tc_no_buffer_discard_uc_get(ppcnt_pl);
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
97262306a36Sopenharmony_ci		err = mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_PRIO_CNT,
97362306a36Sopenharmony_ci						  i, ppcnt_pl);
97462306a36Sopenharmony_ci		if (err)
97562306a36Sopenharmony_ci			continue;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci		xstats->tx_packets[i] = mlxsw_reg_ppcnt_tx_frames_get(ppcnt_pl);
97862306a36Sopenharmony_ci		xstats->tx_bytes[i] = mlxsw_reg_ppcnt_tx_octets_get(ppcnt_pl);
97962306a36Sopenharmony_ci	}
98062306a36Sopenharmony_ci}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_cistatic void update_stats_cache(struct work_struct *work)
98362306a36Sopenharmony_ci{
98462306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port =
98562306a36Sopenharmony_ci		container_of(work, struct mlxsw_sp_port,
98662306a36Sopenharmony_ci			     periodic_hw_stats.update_dw.work);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	if (!netif_carrier_ok(mlxsw_sp_port->dev))
98962306a36Sopenharmony_ci		/* Note: mlxsw_sp_port_down_wipe_counters() clears the cache as
99062306a36Sopenharmony_ci		 * necessary when port goes down.
99162306a36Sopenharmony_ci		 */
99262306a36Sopenharmony_ci		goto out;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	mlxsw_sp_port_get_hw_stats(mlxsw_sp_port->dev,
99562306a36Sopenharmony_ci				   &mlxsw_sp_port->periodic_hw_stats.stats);
99662306a36Sopenharmony_ci	mlxsw_sp_port_get_hw_xstats(mlxsw_sp_port->dev,
99762306a36Sopenharmony_ci				    &mlxsw_sp_port->periodic_hw_stats.xstats);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ciout:
100062306a36Sopenharmony_ci	mlxsw_core_schedule_dw(&mlxsw_sp_port->periodic_hw_stats.update_dw,
100162306a36Sopenharmony_ci			       MLXSW_HW_STATS_UPDATE_TIME);
100262306a36Sopenharmony_ci}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci/* Return the stats from a cache that is updated periodically,
100562306a36Sopenharmony_ci * as this function might get called in an atomic context.
100662306a36Sopenharmony_ci */
100762306a36Sopenharmony_cistatic void
100862306a36Sopenharmony_cimlxsw_sp_port_get_stats64(struct net_device *dev,
100962306a36Sopenharmony_ci			  struct rtnl_link_stats64 *stats)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	memcpy(stats, &mlxsw_sp_port->periodic_hw_stats.stats, sizeof(*stats));
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_cistatic int __mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port,
101762306a36Sopenharmony_ci				    u16 vid_begin, u16 vid_end,
101862306a36Sopenharmony_ci				    bool is_member, bool untagged)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
102162306a36Sopenharmony_ci	char *spvm_pl;
102262306a36Sopenharmony_ci	int err;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	spvm_pl = kmalloc(MLXSW_REG_SPVM_LEN, GFP_KERNEL);
102562306a36Sopenharmony_ci	if (!spvm_pl)
102662306a36Sopenharmony_ci		return -ENOMEM;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	mlxsw_reg_spvm_pack(spvm_pl, mlxsw_sp_port->local_port,	vid_begin,
102962306a36Sopenharmony_ci			    vid_end, is_member, untagged);
103062306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvm), spvm_pl);
103162306a36Sopenharmony_ci	kfree(spvm_pl);
103262306a36Sopenharmony_ci	return err;
103362306a36Sopenharmony_ci}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ciint mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin,
103662306a36Sopenharmony_ci			   u16 vid_end, bool is_member, bool untagged)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	u16 vid, vid_e;
103962306a36Sopenharmony_ci	int err;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	for (vid = vid_begin; vid <= vid_end;
104262306a36Sopenharmony_ci	     vid += MLXSW_REG_SPVM_REC_MAX_COUNT) {
104362306a36Sopenharmony_ci		vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1),
104462306a36Sopenharmony_ci			    vid_end);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci		err = __mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e,
104762306a36Sopenharmony_ci					       is_member, untagged);
104862306a36Sopenharmony_ci		if (err)
104962306a36Sopenharmony_ci			return err;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	return 0;
105362306a36Sopenharmony_ci}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_cistatic void mlxsw_sp_port_vlan_flush(struct mlxsw_sp_port *mlxsw_sp_port,
105662306a36Sopenharmony_ci				     bool flush_default)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan, *tmp;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	list_for_each_entry_safe(mlxsw_sp_port_vlan, tmp,
106162306a36Sopenharmony_ci				 &mlxsw_sp_port->vlans_list, list) {
106262306a36Sopenharmony_ci		if (!flush_default &&
106362306a36Sopenharmony_ci		    mlxsw_sp_port_vlan->vid == MLXSW_SP_DEFAULT_VID)
106462306a36Sopenharmony_ci			continue;
106562306a36Sopenharmony_ci		mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_cistatic void
107062306a36Sopenharmony_cimlxsw_sp_port_vlan_cleanup(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
107162306a36Sopenharmony_ci{
107262306a36Sopenharmony_ci	if (mlxsw_sp_port_vlan->bridge_port)
107362306a36Sopenharmony_ci		mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
107462306a36Sopenharmony_ci	else if (mlxsw_sp_port_vlan->fid)
107562306a36Sopenharmony_ci		mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_cistruct mlxsw_sp_port_vlan *
107962306a36Sopenharmony_cimlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
108062306a36Sopenharmony_ci{
108162306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
108262306a36Sopenharmony_ci	bool untagged = vid == MLXSW_SP_DEFAULT_VID;
108362306a36Sopenharmony_ci	int err;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
108662306a36Sopenharmony_ci	if (mlxsw_sp_port_vlan)
108762306a36Sopenharmony_ci		return ERR_PTR(-EEXIST);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, untagged);
109062306a36Sopenharmony_ci	if (err)
109162306a36Sopenharmony_ci		return ERR_PTR(err);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	mlxsw_sp_port_vlan = kzalloc(sizeof(*mlxsw_sp_port_vlan), GFP_KERNEL);
109462306a36Sopenharmony_ci	if (!mlxsw_sp_port_vlan) {
109562306a36Sopenharmony_ci		err = -ENOMEM;
109662306a36Sopenharmony_ci		goto err_port_vlan_alloc;
109762306a36Sopenharmony_ci	}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	mlxsw_sp_port_vlan->mlxsw_sp_port = mlxsw_sp_port;
110062306a36Sopenharmony_ci	mlxsw_sp_port_vlan->vid = vid;
110162306a36Sopenharmony_ci	list_add(&mlxsw_sp_port_vlan->list, &mlxsw_sp_port->vlans_list);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	return mlxsw_sp_port_vlan;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_cierr_port_vlan_alloc:
110662306a36Sopenharmony_ci	mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
110762306a36Sopenharmony_ci	return ERR_PTR(err);
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_civoid mlxsw_sp_port_vlan_destroy(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
111362306a36Sopenharmony_ci	u16 vid = mlxsw_sp_port_vlan->vid;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	mlxsw_sp_port_vlan_cleanup(mlxsw_sp_port_vlan);
111662306a36Sopenharmony_ci	list_del(&mlxsw_sp_port_vlan->list);
111762306a36Sopenharmony_ci	kfree(mlxsw_sp_port_vlan);
111862306a36Sopenharmony_ci	mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
111962306a36Sopenharmony_ci}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_cistatic int mlxsw_sp_port_add_vid(struct net_device *dev,
112262306a36Sopenharmony_ci				 __be16 __always_unused proto, u16 vid)
112362306a36Sopenharmony_ci{
112462306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	/* VLAN 0 is added to HW filter when device goes up, but it is
112762306a36Sopenharmony_ci	 * reserved in our case, so simply return.
112862306a36Sopenharmony_ci	 */
112962306a36Sopenharmony_ci	if (!vid)
113062306a36Sopenharmony_ci		return 0;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(mlxsw_sp_port_vlan_create(mlxsw_sp_port, vid));
113362306a36Sopenharmony_ci}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ciint mlxsw_sp_port_kill_vid(struct net_device *dev,
113662306a36Sopenharmony_ci			   __be16 __always_unused proto, u16 vid)
113762306a36Sopenharmony_ci{
113862306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
113962306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	/* VLAN 0 is removed from HW filter when device goes down, but
114262306a36Sopenharmony_ci	 * it is reserved in our case, so simply return.
114362306a36Sopenharmony_ci	 */
114462306a36Sopenharmony_ci	if (!vid)
114562306a36Sopenharmony_ci		return 0;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
114862306a36Sopenharmony_ci	if (!mlxsw_sp_port_vlan)
114962306a36Sopenharmony_ci		return 0;
115062306a36Sopenharmony_ci	mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	return 0;
115362306a36Sopenharmony_ci}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_cistatic int mlxsw_sp_setup_tc_block(struct mlxsw_sp_port *mlxsw_sp_port,
115662306a36Sopenharmony_ci				   struct flow_block_offload *f)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	switch (f->binder_type) {
115962306a36Sopenharmony_ci	case FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS:
116062306a36Sopenharmony_ci		return mlxsw_sp_setup_tc_block_clsact(mlxsw_sp_port, f, true);
116162306a36Sopenharmony_ci	case FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS:
116262306a36Sopenharmony_ci		return mlxsw_sp_setup_tc_block_clsact(mlxsw_sp_port, f, false);
116362306a36Sopenharmony_ci	case FLOW_BLOCK_BINDER_TYPE_RED_EARLY_DROP:
116462306a36Sopenharmony_ci		return mlxsw_sp_setup_tc_block_qevent_early_drop(mlxsw_sp_port, f);
116562306a36Sopenharmony_ci	case FLOW_BLOCK_BINDER_TYPE_RED_MARK:
116662306a36Sopenharmony_ci		return mlxsw_sp_setup_tc_block_qevent_mark(mlxsw_sp_port, f);
116762306a36Sopenharmony_ci	default:
116862306a36Sopenharmony_ci		return -EOPNOTSUPP;
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_cistatic int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type,
117362306a36Sopenharmony_ci			     void *type_data)
117462306a36Sopenharmony_ci{
117562306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	switch (type) {
117862306a36Sopenharmony_ci	case TC_SETUP_BLOCK:
117962306a36Sopenharmony_ci		return mlxsw_sp_setup_tc_block(mlxsw_sp_port, type_data);
118062306a36Sopenharmony_ci	case TC_SETUP_QDISC_RED:
118162306a36Sopenharmony_ci		return mlxsw_sp_setup_tc_red(mlxsw_sp_port, type_data);
118262306a36Sopenharmony_ci	case TC_SETUP_QDISC_PRIO:
118362306a36Sopenharmony_ci		return mlxsw_sp_setup_tc_prio(mlxsw_sp_port, type_data);
118462306a36Sopenharmony_ci	case TC_SETUP_QDISC_ETS:
118562306a36Sopenharmony_ci		return mlxsw_sp_setup_tc_ets(mlxsw_sp_port, type_data);
118662306a36Sopenharmony_ci	case TC_SETUP_QDISC_TBF:
118762306a36Sopenharmony_ci		return mlxsw_sp_setup_tc_tbf(mlxsw_sp_port, type_data);
118862306a36Sopenharmony_ci	case TC_SETUP_QDISC_FIFO:
118962306a36Sopenharmony_ci		return mlxsw_sp_setup_tc_fifo(mlxsw_sp_port, type_data);
119062306a36Sopenharmony_ci	default:
119162306a36Sopenharmony_ci		return -EOPNOTSUPP;
119262306a36Sopenharmony_ci	}
119362306a36Sopenharmony_ci}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_cistatic int mlxsw_sp_feature_hw_tc(struct net_device *dev, bool enable)
119662306a36Sopenharmony_ci{
119762306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	if (!enable) {
120062306a36Sopenharmony_ci		if (mlxsw_sp_flow_block_rule_count(mlxsw_sp_port->ing_flow_block) ||
120162306a36Sopenharmony_ci		    mlxsw_sp_flow_block_rule_count(mlxsw_sp_port->eg_flow_block)) {
120262306a36Sopenharmony_ci			netdev_err(dev, "Active offloaded tc filters, can't turn hw_tc_offload off\n");
120362306a36Sopenharmony_ci			return -EINVAL;
120462306a36Sopenharmony_ci		}
120562306a36Sopenharmony_ci		mlxsw_sp_flow_block_disable_inc(mlxsw_sp_port->ing_flow_block);
120662306a36Sopenharmony_ci		mlxsw_sp_flow_block_disable_inc(mlxsw_sp_port->eg_flow_block);
120762306a36Sopenharmony_ci	} else {
120862306a36Sopenharmony_ci		mlxsw_sp_flow_block_disable_dec(mlxsw_sp_port->ing_flow_block);
120962306a36Sopenharmony_ci		mlxsw_sp_flow_block_disable_dec(mlxsw_sp_port->eg_flow_block);
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci	return 0;
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_cistatic int mlxsw_sp_feature_loopback(struct net_device *dev, bool enable)
121562306a36Sopenharmony_ci{
121662306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
121762306a36Sopenharmony_ci	char pplr_pl[MLXSW_REG_PPLR_LEN];
121862306a36Sopenharmony_ci	int err;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	if (netif_running(dev))
122162306a36Sopenharmony_ci		mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	mlxsw_reg_pplr_pack(pplr_pl, mlxsw_sp_port->local_port, enable);
122462306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pplr),
122562306a36Sopenharmony_ci			      pplr_pl);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	if (netif_running(dev))
122862306a36Sopenharmony_ci		mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	return err;
123162306a36Sopenharmony_ci}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_citypedef int (*mlxsw_sp_feature_handler)(struct net_device *dev, bool enable);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic int mlxsw_sp_handle_feature(struct net_device *dev,
123662306a36Sopenharmony_ci				   netdev_features_t wanted_features,
123762306a36Sopenharmony_ci				   netdev_features_t feature,
123862306a36Sopenharmony_ci				   mlxsw_sp_feature_handler feature_handler)
123962306a36Sopenharmony_ci{
124062306a36Sopenharmony_ci	netdev_features_t changes = wanted_features ^ dev->features;
124162306a36Sopenharmony_ci	bool enable = !!(wanted_features & feature);
124262306a36Sopenharmony_ci	int err;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	if (!(changes & feature))
124562306a36Sopenharmony_ci		return 0;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	err = feature_handler(dev, enable);
124862306a36Sopenharmony_ci	if (err) {
124962306a36Sopenharmony_ci		netdev_err(dev, "%s feature %pNF failed, err %d\n",
125062306a36Sopenharmony_ci			   enable ? "Enable" : "Disable", &feature, err);
125162306a36Sopenharmony_ci		return err;
125262306a36Sopenharmony_ci	}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	if (enable)
125562306a36Sopenharmony_ci		dev->features |= feature;
125662306a36Sopenharmony_ci	else
125762306a36Sopenharmony_ci		dev->features &= ~feature;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	return 0;
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_cistatic int mlxsw_sp_set_features(struct net_device *dev,
126262306a36Sopenharmony_ci				 netdev_features_t features)
126362306a36Sopenharmony_ci{
126462306a36Sopenharmony_ci	netdev_features_t oper_features = dev->features;
126562306a36Sopenharmony_ci	int err = 0;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	err |= mlxsw_sp_handle_feature(dev, features, NETIF_F_HW_TC,
126862306a36Sopenharmony_ci				       mlxsw_sp_feature_hw_tc);
126962306a36Sopenharmony_ci	err |= mlxsw_sp_handle_feature(dev, features, NETIF_F_LOOPBACK,
127062306a36Sopenharmony_ci				       mlxsw_sp_feature_loopback);
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	if (err) {
127362306a36Sopenharmony_ci		dev->features = oper_features;
127462306a36Sopenharmony_ci		return -EINVAL;
127562306a36Sopenharmony_ci	}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	return 0;
127862306a36Sopenharmony_ci}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_cistatic int mlxsw_sp_port_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
128162306a36Sopenharmony_ci				      struct ifreq *ifr)
128262306a36Sopenharmony_ci{
128362306a36Sopenharmony_ci	struct hwtstamp_config config;
128462306a36Sopenharmony_ci	int err;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
128762306a36Sopenharmony_ci		return -EFAULT;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	err = mlxsw_sp_port->mlxsw_sp->ptp_ops->hwtstamp_set(mlxsw_sp_port,
129062306a36Sopenharmony_ci							     &config);
129162306a36Sopenharmony_ci	if (err)
129262306a36Sopenharmony_ci		return err;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
129562306a36Sopenharmony_ci		return -EFAULT;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	return 0;
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_cistatic int mlxsw_sp_port_hwtstamp_get(struct mlxsw_sp_port *mlxsw_sp_port,
130162306a36Sopenharmony_ci				      struct ifreq *ifr)
130262306a36Sopenharmony_ci{
130362306a36Sopenharmony_ci	struct hwtstamp_config config;
130462306a36Sopenharmony_ci	int err;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	err = mlxsw_sp_port->mlxsw_sp->ptp_ops->hwtstamp_get(mlxsw_sp_port,
130762306a36Sopenharmony_ci							     &config);
130862306a36Sopenharmony_ci	if (err)
130962306a36Sopenharmony_ci		return err;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
131262306a36Sopenharmony_ci		return -EFAULT;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	return 0;
131562306a36Sopenharmony_ci}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_cistatic inline void mlxsw_sp_port_ptp_clear(struct mlxsw_sp_port *mlxsw_sp_port)
131862306a36Sopenharmony_ci{
131962306a36Sopenharmony_ci	struct hwtstamp_config config = {0};
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	mlxsw_sp_port->mlxsw_sp->ptp_ops->hwtstamp_set(mlxsw_sp_port, &config);
132262306a36Sopenharmony_ci}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_cistatic int
132562306a36Sopenharmony_cimlxsw_sp_port_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
132662306a36Sopenharmony_ci{
132762306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	switch (cmd) {
133062306a36Sopenharmony_ci	case SIOCSHWTSTAMP:
133162306a36Sopenharmony_ci		return mlxsw_sp_port_hwtstamp_set(mlxsw_sp_port, ifr);
133262306a36Sopenharmony_ci	case SIOCGHWTSTAMP:
133362306a36Sopenharmony_ci		return mlxsw_sp_port_hwtstamp_get(mlxsw_sp_port, ifr);
133462306a36Sopenharmony_ci	default:
133562306a36Sopenharmony_ci		return -EOPNOTSUPP;
133662306a36Sopenharmony_ci	}
133762306a36Sopenharmony_ci}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_cistatic const struct net_device_ops mlxsw_sp_port_netdev_ops = {
134062306a36Sopenharmony_ci	.ndo_open		= mlxsw_sp_port_open,
134162306a36Sopenharmony_ci	.ndo_stop		= mlxsw_sp_port_stop,
134262306a36Sopenharmony_ci	.ndo_start_xmit		= mlxsw_sp_port_xmit,
134362306a36Sopenharmony_ci	.ndo_setup_tc           = mlxsw_sp_setup_tc,
134462306a36Sopenharmony_ci	.ndo_set_rx_mode	= mlxsw_sp_set_rx_mode,
134562306a36Sopenharmony_ci	.ndo_set_mac_address	= mlxsw_sp_port_set_mac_address,
134662306a36Sopenharmony_ci	.ndo_change_mtu		= mlxsw_sp_port_change_mtu,
134762306a36Sopenharmony_ci	.ndo_get_stats64	= mlxsw_sp_port_get_stats64,
134862306a36Sopenharmony_ci	.ndo_has_offload_stats	= mlxsw_sp_port_has_offload_stats,
134962306a36Sopenharmony_ci	.ndo_get_offload_stats	= mlxsw_sp_port_get_offload_stats,
135062306a36Sopenharmony_ci	.ndo_vlan_rx_add_vid	= mlxsw_sp_port_add_vid,
135162306a36Sopenharmony_ci	.ndo_vlan_rx_kill_vid	= mlxsw_sp_port_kill_vid,
135262306a36Sopenharmony_ci	.ndo_set_features	= mlxsw_sp_set_features,
135362306a36Sopenharmony_ci	.ndo_eth_ioctl		= mlxsw_sp_port_ioctl,
135462306a36Sopenharmony_ci};
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_cistatic int
135762306a36Sopenharmony_cimlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port)
135862306a36Sopenharmony_ci{
135962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
136062306a36Sopenharmony_ci	u32 eth_proto_cap, eth_proto_admin, eth_proto_oper;
136162306a36Sopenharmony_ci	const struct mlxsw_sp_port_type_speed_ops *ops;
136262306a36Sopenharmony_ci	char ptys_pl[MLXSW_REG_PTYS_LEN];
136362306a36Sopenharmony_ci	u32 eth_proto_cap_masked;
136462306a36Sopenharmony_ci	int err;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	ops = mlxsw_sp->port_type_speed_ops;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	/* Set advertised speeds to speeds supported by both the driver
136962306a36Sopenharmony_ci	 * and the device.
137062306a36Sopenharmony_ci	 */
137162306a36Sopenharmony_ci	ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
137262306a36Sopenharmony_ci			       0, false);
137362306a36Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
137462306a36Sopenharmony_ci	if (err)
137562306a36Sopenharmony_ci		return err;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, &eth_proto_cap,
137862306a36Sopenharmony_ci				 &eth_proto_admin, &eth_proto_oper);
137962306a36Sopenharmony_ci	eth_proto_cap_masked = ops->ptys_proto_cap_masked_get(eth_proto_cap);
138062306a36Sopenharmony_ci	ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
138162306a36Sopenharmony_ci			       eth_proto_cap_masked,
138262306a36Sopenharmony_ci			       mlxsw_sp_port->link.autoneg);
138362306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
138462306a36Sopenharmony_ci}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ciint mlxsw_sp_port_speed_get(struct mlxsw_sp_port *mlxsw_sp_port, u32 *speed)
138762306a36Sopenharmony_ci{
138862306a36Sopenharmony_ci	const struct mlxsw_sp_port_type_speed_ops *port_type_speed_ops;
138962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
139062306a36Sopenharmony_ci	char ptys_pl[MLXSW_REG_PTYS_LEN];
139162306a36Sopenharmony_ci	u32 eth_proto_oper;
139262306a36Sopenharmony_ci	int err;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	port_type_speed_ops = mlxsw_sp->port_type_speed_ops;
139562306a36Sopenharmony_ci	port_type_speed_ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl,
139662306a36Sopenharmony_ci					       mlxsw_sp_port->local_port, 0,
139762306a36Sopenharmony_ci					       false);
139862306a36Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
139962306a36Sopenharmony_ci	if (err)
140062306a36Sopenharmony_ci		return err;
140162306a36Sopenharmony_ci	port_type_speed_ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, NULL, NULL,
140262306a36Sopenharmony_ci						 &eth_proto_oper);
140362306a36Sopenharmony_ci	*speed = port_type_speed_ops->from_ptys_speed(mlxsw_sp, eth_proto_oper);
140462306a36Sopenharmony_ci	return 0;
140562306a36Sopenharmony_ci}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ciint mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,
140862306a36Sopenharmony_ci			  enum mlxsw_reg_qeec_hr hr, u8 index, u8 next_index,
140962306a36Sopenharmony_ci			  bool dwrr, u8 dwrr_weight)
141062306a36Sopenharmony_ci{
141162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
141262306a36Sopenharmony_ci	char qeec_pl[MLXSW_REG_QEEC_LEN];
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	mlxsw_reg_qeec_pack(qeec_pl, mlxsw_sp_port->local_port, hr, index,
141562306a36Sopenharmony_ci			    next_index);
141662306a36Sopenharmony_ci	mlxsw_reg_qeec_de_set(qeec_pl, true);
141762306a36Sopenharmony_ci	mlxsw_reg_qeec_dwrr_set(qeec_pl, dwrr);
141862306a36Sopenharmony_ci	mlxsw_reg_qeec_dwrr_weight_set(qeec_pl, dwrr_weight);
141962306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl);
142062306a36Sopenharmony_ci}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ciint mlxsw_sp_port_ets_maxrate_set(struct mlxsw_sp_port *mlxsw_sp_port,
142362306a36Sopenharmony_ci				  enum mlxsw_reg_qeec_hr hr, u8 index,
142462306a36Sopenharmony_ci				  u8 next_index, u32 maxrate, u8 burst_size)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
142762306a36Sopenharmony_ci	char qeec_pl[MLXSW_REG_QEEC_LEN];
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	mlxsw_reg_qeec_pack(qeec_pl, mlxsw_sp_port->local_port, hr, index,
143062306a36Sopenharmony_ci			    next_index);
143162306a36Sopenharmony_ci	mlxsw_reg_qeec_mase_set(qeec_pl, true);
143262306a36Sopenharmony_ci	mlxsw_reg_qeec_max_shaper_rate_set(qeec_pl, maxrate);
143362306a36Sopenharmony_ci	mlxsw_reg_qeec_max_shaper_bs_set(qeec_pl, burst_size);
143462306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl);
143562306a36Sopenharmony_ci}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_cistatic int mlxsw_sp_port_min_bw_set(struct mlxsw_sp_port *mlxsw_sp_port,
143862306a36Sopenharmony_ci				    enum mlxsw_reg_qeec_hr hr, u8 index,
143962306a36Sopenharmony_ci				    u8 next_index, u32 minrate)
144062306a36Sopenharmony_ci{
144162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
144262306a36Sopenharmony_ci	char qeec_pl[MLXSW_REG_QEEC_LEN];
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	mlxsw_reg_qeec_pack(qeec_pl, mlxsw_sp_port->local_port, hr, index,
144562306a36Sopenharmony_ci			    next_index);
144662306a36Sopenharmony_ci	mlxsw_reg_qeec_mise_set(qeec_pl, true);
144762306a36Sopenharmony_ci	mlxsw_reg_qeec_min_shaper_rate_set(qeec_pl, minrate);
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl);
145062306a36Sopenharmony_ci}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ciint mlxsw_sp_port_prio_tc_set(struct mlxsw_sp_port *mlxsw_sp_port,
145362306a36Sopenharmony_ci			      u8 switch_prio, u8 tclass)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
145662306a36Sopenharmony_ci	char qtct_pl[MLXSW_REG_QTCT_LEN];
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	mlxsw_reg_qtct_pack(qtct_pl, mlxsw_sp_port->local_port, switch_prio,
145962306a36Sopenharmony_ci			    tclass);
146062306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qtct), qtct_pl);
146162306a36Sopenharmony_ci}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_cistatic int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port)
146462306a36Sopenharmony_ci{
146562306a36Sopenharmony_ci	int err, i;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	/* Setup the elements hierarcy, so that each TC is linked to
146862306a36Sopenharmony_ci	 * one subgroup, which are all member in the same group.
146962306a36Sopenharmony_ci	 */
147062306a36Sopenharmony_ci	err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
147162306a36Sopenharmony_ci				    MLXSW_REG_QEEC_HR_GROUP, 0, 0, false, 0);
147262306a36Sopenharmony_ci	if (err)
147362306a36Sopenharmony_ci		return err;
147462306a36Sopenharmony_ci	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
147562306a36Sopenharmony_ci		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
147662306a36Sopenharmony_ci					    MLXSW_REG_QEEC_HR_SUBGROUP, i,
147762306a36Sopenharmony_ci					    0, false, 0);
147862306a36Sopenharmony_ci		if (err)
147962306a36Sopenharmony_ci			return err;
148062306a36Sopenharmony_ci	}
148162306a36Sopenharmony_ci	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
148262306a36Sopenharmony_ci		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
148362306a36Sopenharmony_ci					    MLXSW_REG_QEEC_HR_TC, i, i,
148462306a36Sopenharmony_ci					    false, 0);
148562306a36Sopenharmony_ci		if (err)
148662306a36Sopenharmony_ci			return err;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
148962306a36Sopenharmony_ci					    MLXSW_REG_QEEC_HR_TC,
149062306a36Sopenharmony_ci					    i + 8, i,
149162306a36Sopenharmony_ci					    true, 100);
149262306a36Sopenharmony_ci		if (err)
149362306a36Sopenharmony_ci			return err;
149462306a36Sopenharmony_ci	}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	/* Make sure the max shaper is disabled in all hierarchies that support
149762306a36Sopenharmony_ci	 * it. Note that this disables ptps (PTP shaper), but that is intended
149862306a36Sopenharmony_ci	 * for the initial configuration.
149962306a36Sopenharmony_ci	 */
150062306a36Sopenharmony_ci	err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
150162306a36Sopenharmony_ci					    MLXSW_REG_QEEC_HR_PORT, 0, 0,
150262306a36Sopenharmony_ci					    MLXSW_REG_QEEC_MAS_DIS, 0);
150362306a36Sopenharmony_ci	if (err)
150462306a36Sopenharmony_ci		return err;
150562306a36Sopenharmony_ci	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
150662306a36Sopenharmony_ci		err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
150762306a36Sopenharmony_ci						    MLXSW_REG_QEEC_HR_SUBGROUP,
150862306a36Sopenharmony_ci						    i, 0,
150962306a36Sopenharmony_ci						    MLXSW_REG_QEEC_MAS_DIS, 0);
151062306a36Sopenharmony_ci		if (err)
151162306a36Sopenharmony_ci			return err;
151262306a36Sopenharmony_ci	}
151362306a36Sopenharmony_ci	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
151462306a36Sopenharmony_ci		err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
151562306a36Sopenharmony_ci						    MLXSW_REG_QEEC_HR_TC,
151662306a36Sopenharmony_ci						    i, i,
151762306a36Sopenharmony_ci						    MLXSW_REG_QEEC_MAS_DIS, 0);
151862306a36Sopenharmony_ci		if (err)
151962306a36Sopenharmony_ci			return err;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci		err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
152262306a36Sopenharmony_ci						    MLXSW_REG_QEEC_HR_TC,
152362306a36Sopenharmony_ci						    i + 8, i,
152462306a36Sopenharmony_ci						    MLXSW_REG_QEEC_MAS_DIS, 0);
152562306a36Sopenharmony_ci		if (err)
152662306a36Sopenharmony_ci			return err;
152762306a36Sopenharmony_ci	}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	/* Configure the min shaper for multicast TCs. */
153062306a36Sopenharmony_ci	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
153162306a36Sopenharmony_ci		err = mlxsw_sp_port_min_bw_set(mlxsw_sp_port,
153262306a36Sopenharmony_ci					       MLXSW_REG_QEEC_HR_TC,
153362306a36Sopenharmony_ci					       i + 8, i,
153462306a36Sopenharmony_ci					       MLXSW_REG_QEEC_MIS_MIN);
153562306a36Sopenharmony_ci		if (err)
153662306a36Sopenharmony_ci			return err;
153762306a36Sopenharmony_ci	}
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	/* Map all priorities to traffic class 0. */
154062306a36Sopenharmony_ci	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
154162306a36Sopenharmony_ci		err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, 0);
154262306a36Sopenharmony_ci		if (err)
154362306a36Sopenharmony_ci			return err;
154462306a36Sopenharmony_ci	}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	return 0;
154762306a36Sopenharmony_ci}
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_cistatic int mlxsw_sp_port_tc_mc_mode_set(struct mlxsw_sp_port *mlxsw_sp_port,
155062306a36Sopenharmony_ci					bool enable)
155162306a36Sopenharmony_ci{
155262306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
155362306a36Sopenharmony_ci	char qtctm_pl[MLXSW_REG_QTCTM_LEN];
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	mlxsw_reg_qtctm_pack(qtctm_pl, mlxsw_sp_port->local_port, enable);
155662306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qtctm), qtctm_pl);
155762306a36Sopenharmony_ci}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_cistatic int mlxsw_sp_port_overheat_init_val_set(struct mlxsw_sp_port *mlxsw_sp_port)
156062306a36Sopenharmony_ci{
156162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
156262306a36Sopenharmony_ci	u8 slot_index = mlxsw_sp_port->mapping.slot_index;
156362306a36Sopenharmony_ci	u8 module = mlxsw_sp_port->mapping.module;
156462306a36Sopenharmony_ci	u64 overheat_counter;
156562306a36Sopenharmony_ci	int err;
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	err = mlxsw_env_module_overheat_counter_get(mlxsw_sp->core, slot_index,
156862306a36Sopenharmony_ci						    module, &overheat_counter);
156962306a36Sopenharmony_ci	if (err)
157062306a36Sopenharmony_ci		return err;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	mlxsw_sp_port->module_overheat_initial_val = overheat_counter;
157362306a36Sopenharmony_ci	return 0;
157462306a36Sopenharmony_ci}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ciint
157762306a36Sopenharmony_cimlxsw_sp_port_vlan_classification_set(struct mlxsw_sp_port *mlxsw_sp_port,
157862306a36Sopenharmony_ci				      bool is_8021ad_tagged,
157962306a36Sopenharmony_ci				      bool is_8021q_tagged)
158062306a36Sopenharmony_ci{
158162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
158262306a36Sopenharmony_ci	char spvc_pl[MLXSW_REG_SPVC_LEN];
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	mlxsw_reg_spvc_pack(spvc_pl, mlxsw_sp_port->local_port,
158562306a36Sopenharmony_ci			    is_8021ad_tagged, is_8021q_tagged);
158662306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvc), spvc_pl);
158762306a36Sopenharmony_ci}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_cistatic int mlxsw_sp_port_label_info_get(struct mlxsw_sp *mlxsw_sp,
159062306a36Sopenharmony_ci					u16 local_port, u8 *port_number,
159162306a36Sopenharmony_ci					u8 *split_port_subnumber,
159262306a36Sopenharmony_ci					u8 *slot_index)
159362306a36Sopenharmony_ci{
159462306a36Sopenharmony_ci	char pllp_pl[MLXSW_REG_PLLP_LEN];
159562306a36Sopenharmony_ci	int err;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	mlxsw_reg_pllp_pack(pllp_pl, local_port);
159862306a36Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pllp), pllp_pl);
159962306a36Sopenharmony_ci	if (err)
160062306a36Sopenharmony_ci		return err;
160162306a36Sopenharmony_ci	mlxsw_reg_pllp_unpack(pllp_pl, port_number,
160262306a36Sopenharmony_ci			      split_port_subnumber, slot_index);
160362306a36Sopenharmony_ci	return 0;
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_cistatic int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u16 local_port,
160762306a36Sopenharmony_ci				bool split,
160862306a36Sopenharmony_ci				struct mlxsw_sp_port_mapping *port_mapping)
160962306a36Sopenharmony_ci{
161062306a36Sopenharmony_ci	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
161162306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
161262306a36Sopenharmony_ci	u32 lanes = port_mapping->width;
161362306a36Sopenharmony_ci	u8 split_port_subnumber;
161462306a36Sopenharmony_ci	struct net_device *dev;
161562306a36Sopenharmony_ci	u8 port_number;
161662306a36Sopenharmony_ci	u8 slot_index;
161762306a36Sopenharmony_ci	bool splittable;
161862306a36Sopenharmony_ci	int err;
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	err = mlxsw_sp_port_module_map(mlxsw_sp, local_port, port_mapping);
162162306a36Sopenharmony_ci	if (err) {
162262306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to map module\n",
162362306a36Sopenharmony_ci			local_port);
162462306a36Sopenharmony_ci		return err;
162562306a36Sopenharmony_ci	}
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	err = mlxsw_sp_port_swid_set(mlxsw_sp, local_port, 0);
162862306a36Sopenharmony_ci	if (err) {
162962306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set SWID\n",
163062306a36Sopenharmony_ci			local_port);
163162306a36Sopenharmony_ci		goto err_port_swid_set;
163262306a36Sopenharmony_ci	}
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	err = mlxsw_sp_port_label_info_get(mlxsw_sp, local_port, &port_number,
163562306a36Sopenharmony_ci					   &split_port_subnumber, &slot_index);
163662306a36Sopenharmony_ci	if (err) {
163762306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to get port label information\n",
163862306a36Sopenharmony_ci			local_port);
163962306a36Sopenharmony_ci		goto err_port_label_info_get;
164062306a36Sopenharmony_ci	}
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	splittable = lanes > 1 && !split;
164362306a36Sopenharmony_ci	err = mlxsw_core_port_init(mlxsw_sp->core, local_port, slot_index,
164462306a36Sopenharmony_ci				   port_number, split, split_port_subnumber,
164562306a36Sopenharmony_ci				   splittable, lanes, mlxsw_sp->base_mac,
164662306a36Sopenharmony_ci				   sizeof(mlxsw_sp->base_mac));
164762306a36Sopenharmony_ci	if (err) {
164862306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to init core port\n",
164962306a36Sopenharmony_ci			local_port);
165062306a36Sopenharmony_ci		goto err_core_port_init;
165162306a36Sopenharmony_ci	}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct mlxsw_sp_port));
165462306a36Sopenharmony_ci	if (!dev) {
165562306a36Sopenharmony_ci		err = -ENOMEM;
165662306a36Sopenharmony_ci		goto err_alloc_etherdev;
165762306a36Sopenharmony_ci	}
165862306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, mlxsw_sp->bus_info->dev);
165962306a36Sopenharmony_ci	dev_net_set(dev, mlxsw_sp_net(mlxsw_sp));
166062306a36Sopenharmony_ci	mlxsw_sp_port = netdev_priv(dev);
166162306a36Sopenharmony_ci	mlxsw_core_port_netdev_link(mlxsw_sp->core, local_port,
166262306a36Sopenharmony_ci				    mlxsw_sp_port, dev);
166362306a36Sopenharmony_ci	mlxsw_sp_port->dev = dev;
166462306a36Sopenharmony_ci	mlxsw_sp_port->mlxsw_sp = mlxsw_sp;
166562306a36Sopenharmony_ci	mlxsw_sp_port->local_port = local_port;
166662306a36Sopenharmony_ci	mlxsw_sp_port->pvid = MLXSW_SP_DEFAULT_VID;
166762306a36Sopenharmony_ci	mlxsw_sp_port->split = split;
166862306a36Sopenharmony_ci	mlxsw_sp_port->mapping = *port_mapping;
166962306a36Sopenharmony_ci	mlxsw_sp_port->link.autoneg = 1;
167062306a36Sopenharmony_ci	INIT_LIST_HEAD(&mlxsw_sp_port->vlans_list);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	mlxsw_sp_port->pcpu_stats =
167362306a36Sopenharmony_ci		netdev_alloc_pcpu_stats(struct mlxsw_sp_port_pcpu_stats);
167462306a36Sopenharmony_ci	if (!mlxsw_sp_port->pcpu_stats) {
167562306a36Sopenharmony_ci		err = -ENOMEM;
167662306a36Sopenharmony_ci		goto err_alloc_stats;
167762306a36Sopenharmony_ci	}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	INIT_DELAYED_WORK(&mlxsw_sp_port->periodic_hw_stats.update_dw,
168062306a36Sopenharmony_ci			  &update_stats_cache);
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	dev->netdev_ops = &mlxsw_sp_port_netdev_ops;
168362306a36Sopenharmony_ci	dev->ethtool_ops = &mlxsw_sp_port_ethtool_ops;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	err = mlxsw_sp_port_dev_addr_init(mlxsw_sp_port);
168662306a36Sopenharmony_ci	if (err) {
168762306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unable to init port mac address\n",
168862306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
168962306a36Sopenharmony_ci		goto err_dev_addr_init;
169062306a36Sopenharmony_ci	}
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	netif_carrier_off(dev);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_LLTX | NETIF_F_SG |
169562306a36Sopenharmony_ci			 NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC;
169662306a36Sopenharmony_ci	dev->hw_features |= NETIF_F_HW_TC | NETIF_F_LOOPBACK;
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	dev->min_mtu = 0;
169962306a36Sopenharmony_ci	dev->max_mtu = ETH_MAX_MTU;
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	/* Each packet needs to have a Tx header (metadata) on top all other
170262306a36Sopenharmony_ci	 * headers.
170362306a36Sopenharmony_ci	 */
170462306a36Sopenharmony_ci	dev->needed_headroom = MLXSW_TXHDR_LEN;
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	err = mlxsw_sp_port_system_port_mapping_set(mlxsw_sp_port);
170762306a36Sopenharmony_ci	if (err) {
170862306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set system port mapping\n",
170962306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
171062306a36Sopenharmony_ci		goto err_port_system_port_mapping_set;
171162306a36Sopenharmony_ci	}
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	err = mlxsw_sp_port_speed_by_width_set(mlxsw_sp_port);
171462306a36Sopenharmony_ci	if (err) {
171562306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to enable speeds\n",
171662306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
171762306a36Sopenharmony_ci		goto err_port_speed_by_width_set;
171862306a36Sopenharmony_ci	}
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	err = mlxsw_sp->port_type_speed_ops->ptys_max_speed(mlxsw_sp_port,
172162306a36Sopenharmony_ci							    &mlxsw_sp_port->max_speed);
172262306a36Sopenharmony_ci	if (err) {
172362306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to get maximum speed\n",
172462306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
172562306a36Sopenharmony_ci		goto err_max_speed_get;
172662306a36Sopenharmony_ci	}
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	err = mlxsw_sp_port_max_mtu_get(mlxsw_sp_port, &mlxsw_sp_port->max_mtu);
172962306a36Sopenharmony_ci	if (err) {
173062306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to get maximum MTU\n",
173162306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
173262306a36Sopenharmony_ci		goto err_port_max_mtu_get;
173362306a36Sopenharmony_ci	}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, ETH_DATA_LEN);
173662306a36Sopenharmony_ci	if (err) {
173762306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set MTU\n",
173862306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
173962306a36Sopenharmony_ci		goto err_port_mtu_set;
174062306a36Sopenharmony_ci	}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
174362306a36Sopenharmony_ci	if (err)
174462306a36Sopenharmony_ci		goto err_port_admin_status_set;
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	err = mlxsw_sp_port_buffers_init(mlxsw_sp_port);
174762306a36Sopenharmony_ci	if (err) {
174862306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize buffers\n",
174962306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
175062306a36Sopenharmony_ci		goto err_port_buffers_init;
175162306a36Sopenharmony_ci	}
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	err = mlxsw_sp_port_ets_init(mlxsw_sp_port);
175462306a36Sopenharmony_ci	if (err) {
175562306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize ETS\n",
175662306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
175762306a36Sopenharmony_ci		goto err_port_ets_init;
175862306a36Sopenharmony_ci	}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	err = mlxsw_sp_port_tc_mc_mode_set(mlxsw_sp_port, true);
176162306a36Sopenharmony_ci	if (err) {
176262306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize TC MC mode\n",
176362306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
176462306a36Sopenharmony_ci		goto err_port_tc_mc_mode;
176562306a36Sopenharmony_ci	}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	/* ETS and buffers must be initialized before DCB. */
176862306a36Sopenharmony_ci	err = mlxsw_sp_port_dcb_init(mlxsw_sp_port);
176962306a36Sopenharmony_ci	if (err) {
177062306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize DCB\n",
177162306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
177262306a36Sopenharmony_ci		goto err_port_dcb_init;
177362306a36Sopenharmony_ci	}
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	err = mlxsw_sp_port_fids_init(mlxsw_sp_port);
177662306a36Sopenharmony_ci	if (err) {
177762306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize FIDs\n",
177862306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
177962306a36Sopenharmony_ci		goto err_port_fids_init;
178062306a36Sopenharmony_ci	}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	err = mlxsw_sp_tc_qdisc_init(mlxsw_sp_port);
178362306a36Sopenharmony_ci	if (err) {
178462306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize TC qdiscs\n",
178562306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
178662306a36Sopenharmony_ci		goto err_port_qdiscs_init;
178762306a36Sopenharmony_ci	}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, 0, VLAN_N_VID - 1, false,
179062306a36Sopenharmony_ci				     false);
179162306a36Sopenharmony_ci	if (err) {
179262306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to clear VLAN filter\n",
179362306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
179462306a36Sopenharmony_ci		goto err_port_vlan_clear;
179562306a36Sopenharmony_ci	}
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	err = mlxsw_sp_port_nve_init(mlxsw_sp_port);
179862306a36Sopenharmony_ci	if (err) {
179962306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize NVE\n",
180062306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
180162306a36Sopenharmony_ci		goto err_port_nve_init;
180262306a36Sopenharmony_ci	}
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, MLXSW_SP_DEFAULT_VID,
180562306a36Sopenharmony_ci				     ETH_P_8021Q);
180662306a36Sopenharmony_ci	if (err) {
180762306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set PVID\n",
180862306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
180962306a36Sopenharmony_ci		goto err_port_pvid_set;
181062306a36Sopenharmony_ci	}
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_create(mlxsw_sp_port,
181362306a36Sopenharmony_ci						       MLXSW_SP_DEFAULT_VID);
181462306a36Sopenharmony_ci	if (IS_ERR(mlxsw_sp_port_vlan)) {
181562306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to create VID 1\n",
181662306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
181762306a36Sopenharmony_ci		err = PTR_ERR(mlxsw_sp_port_vlan);
181862306a36Sopenharmony_ci		goto err_port_vlan_create;
181962306a36Sopenharmony_ci	}
182062306a36Sopenharmony_ci	mlxsw_sp_port->default_vlan = mlxsw_sp_port_vlan;
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	/* Set SPVC.et0=true and SPVC.et1=false to make the local port to treat
182362306a36Sopenharmony_ci	 * only packets with 802.1q header as tagged packets.
182462306a36Sopenharmony_ci	 */
182562306a36Sopenharmony_ci	err = mlxsw_sp_port_vlan_classification_set(mlxsw_sp_port, false, true);
182662306a36Sopenharmony_ci	if (err) {
182762306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set default VLAN classification\n",
182862306a36Sopenharmony_ci			local_port);
182962306a36Sopenharmony_ci		goto err_port_vlan_classification_set;
183062306a36Sopenharmony_ci	}
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci	INIT_DELAYED_WORK(&mlxsw_sp_port->ptp.shaper_dw,
183362306a36Sopenharmony_ci			  mlxsw_sp->ptp_ops->shaper_work);
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	mlxsw_sp->ports[local_port] = mlxsw_sp_port;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	err = mlxsw_sp_port_overheat_init_val_set(mlxsw_sp_port);
183862306a36Sopenharmony_ci	if (err) {
183962306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set overheat initial value\n",
184062306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
184162306a36Sopenharmony_ci		goto err_port_overheat_init_val_set;
184262306a36Sopenharmony_ci	}
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	err = register_netdev(dev);
184562306a36Sopenharmony_ci	if (err) {
184662306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to register netdev\n",
184762306a36Sopenharmony_ci			mlxsw_sp_port->local_port);
184862306a36Sopenharmony_ci		goto err_register_netdev;
184962306a36Sopenharmony_ci	}
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	mlxsw_core_schedule_dw(&mlxsw_sp_port->periodic_hw_stats.update_dw, 0);
185262306a36Sopenharmony_ci	return 0;
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_cierr_register_netdev:
185562306a36Sopenharmony_cierr_port_overheat_init_val_set:
185662306a36Sopenharmony_ci	mlxsw_sp_port_vlan_classification_set(mlxsw_sp_port, true, true);
185762306a36Sopenharmony_cierr_port_vlan_classification_set:
185862306a36Sopenharmony_ci	mlxsw_sp->ports[local_port] = NULL;
185962306a36Sopenharmony_ci	mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
186062306a36Sopenharmony_cierr_port_vlan_create:
186162306a36Sopenharmony_cierr_port_pvid_set:
186262306a36Sopenharmony_ci	mlxsw_sp_port_nve_fini(mlxsw_sp_port);
186362306a36Sopenharmony_cierr_port_nve_init:
186462306a36Sopenharmony_cierr_port_vlan_clear:
186562306a36Sopenharmony_ci	mlxsw_sp_tc_qdisc_fini(mlxsw_sp_port);
186662306a36Sopenharmony_cierr_port_qdiscs_init:
186762306a36Sopenharmony_ci	mlxsw_sp_port_fids_fini(mlxsw_sp_port);
186862306a36Sopenharmony_cierr_port_fids_init:
186962306a36Sopenharmony_ci	mlxsw_sp_port_dcb_fini(mlxsw_sp_port);
187062306a36Sopenharmony_cierr_port_dcb_init:
187162306a36Sopenharmony_ci	mlxsw_sp_port_tc_mc_mode_set(mlxsw_sp_port, false);
187262306a36Sopenharmony_cierr_port_tc_mc_mode:
187362306a36Sopenharmony_cierr_port_ets_init:
187462306a36Sopenharmony_ci	mlxsw_sp_port_buffers_fini(mlxsw_sp_port);
187562306a36Sopenharmony_cierr_port_buffers_init:
187662306a36Sopenharmony_cierr_port_admin_status_set:
187762306a36Sopenharmony_cierr_port_mtu_set:
187862306a36Sopenharmony_cierr_port_max_mtu_get:
187962306a36Sopenharmony_cierr_max_speed_get:
188062306a36Sopenharmony_cierr_port_speed_by_width_set:
188162306a36Sopenharmony_cierr_port_system_port_mapping_set:
188262306a36Sopenharmony_cierr_dev_addr_init:
188362306a36Sopenharmony_ci	free_percpu(mlxsw_sp_port->pcpu_stats);
188462306a36Sopenharmony_cierr_alloc_stats:
188562306a36Sopenharmony_ci	free_netdev(dev);
188662306a36Sopenharmony_cierr_alloc_etherdev:
188762306a36Sopenharmony_ci	mlxsw_core_port_fini(mlxsw_sp->core, local_port);
188862306a36Sopenharmony_cierr_core_port_init:
188962306a36Sopenharmony_cierr_port_label_info_get:
189062306a36Sopenharmony_ci	mlxsw_sp_port_swid_set(mlxsw_sp, local_port,
189162306a36Sopenharmony_ci			       MLXSW_PORT_SWID_DISABLED_PORT);
189262306a36Sopenharmony_cierr_port_swid_set:
189362306a36Sopenharmony_ci	mlxsw_sp_port_module_unmap(mlxsw_sp, local_port,
189462306a36Sopenharmony_ci				   port_mapping->slot_index,
189562306a36Sopenharmony_ci				   port_mapping->module);
189662306a36Sopenharmony_ci	return err;
189762306a36Sopenharmony_ci}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_cistatic void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u16 local_port)
190062306a36Sopenharmony_ci{
190162306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port];
190262306a36Sopenharmony_ci	u8 slot_index = mlxsw_sp_port->mapping.slot_index;
190362306a36Sopenharmony_ci	u8 module = mlxsw_sp_port->mapping.module;
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	cancel_delayed_work_sync(&mlxsw_sp_port->periodic_hw_stats.update_dw);
190662306a36Sopenharmony_ci	cancel_delayed_work_sync(&mlxsw_sp_port->ptp.shaper_dw);
190762306a36Sopenharmony_ci	unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */
190862306a36Sopenharmony_ci	mlxsw_sp_port_ptp_clear(mlxsw_sp_port);
190962306a36Sopenharmony_ci	mlxsw_sp_port_vlan_classification_set(mlxsw_sp_port, true, true);
191062306a36Sopenharmony_ci	mlxsw_sp->ports[local_port] = NULL;
191162306a36Sopenharmony_ci	mlxsw_sp_port_vlan_flush(mlxsw_sp_port, true);
191262306a36Sopenharmony_ci	mlxsw_sp_port_nve_fini(mlxsw_sp_port);
191362306a36Sopenharmony_ci	mlxsw_sp_tc_qdisc_fini(mlxsw_sp_port);
191462306a36Sopenharmony_ci	mlxsw_sp_port_fids_fini(mlxsw_sp_port);
191562306a36Sopenharmony_ci	mlxsw_sp_port_dcb_fini(mlxsw_sp_port);
191662306a36Sopenharmony_ci	mlxsw_sp_port_tc_mc_mode_set(mlxsw_sp_port, false);
191762306a36Sopenharmony_ci	mlxsw_sp_port_buffers_fini(mlxsw_sp_port);
191862306a36Sopenharmony_ci	free_percpu(mlxsw_sp_port->pcpu_stats);
191962306a36Sopenharmony_ci	WARN_ON_ONCE(!list_empty(&mlxsw_sp_port->vlans_list));
192062306a36Sopenharmony_ci	free_netdev(mlxsw_sp_port->dev);
192162306a36Sopenharmony_ci	mlxsw_core_port_fini(mlxsw_sp->core, local_port);
192262306a36Sopenharmony_ci	mlxsw_sp_port_swid_set(mlxsw_sp, local_port,
192362306a36Sopenharmony_ci			       MLXSW_PORT_SWID_DISABLED_PORT);
192462306a36Sopenharmony_ci	mlxsw_sp_port_module_unmap(mlxsw_sp, local_port, slot_index, module);
192562306a36Sopenharmony_ci}
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_cistatic int mlxsw_sp_cpu_port_create(struct mlxsw_sp *mlxsw_sp)
192862306a36Sopenharmony_ci{
192962306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
193062306a36Sopenharmony_ci	int err;
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	mlxsw_sp_port = kzalloc(sizeof(*mlxsw_sp_port), GFP_KERNEL);
193362306a36Sopenharmony_ci	if (!mlxsw_sp_port)
193462306a36Sopenharmony_ci		return -ENOMEM;
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	mlxsw_sp_port->mlxsw_sp = mlxsw_sp;
193762306a36Sopenharmony_ci	mlxsw_sp_port->local_port = MLXSW_PORT_CPU_PORT;
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci	err = mlxsw_core_cpu_port_init(mlxsw_sp->core,
194062306a36Sopenharmony_ci				       mlxsw_sp_port,
194162306a36Sopenharmony_ci				       mlxsw_sp->base_mac,
194262306a36Sopenharmony_ci				       sizeof(mlxsw_sp->base_mac));
194362306a36Sopenharmony_ci	if (err) {
194462306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize core CPU port\n");
194562306a36Sopenharmony_ci		goto err_core_cpu_port_init;
194662306a36Sopenharmony_ci	}
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci	mlxsw_sp->ports[MLXSW_PORT_CPU_PORT] = mlxsw_sp_port;
194962306a36Sopenharmony_ci	return 0;
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_cierr_core_cpu_port_init:
195262306a36Sopenharmony_ci	kfree(mlxsw_sp_port);
195362306a36Sopenharmony_ci	return err;
195462306a36Sopenharmony_ci}
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_cistatic void mlxsw_sp_cpu_port_remove(struct mlxsw_sp *mlxsw_sp)
195762306a36Sopenharmony_ci{
195862306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port =
195962306a36Sopenharmony_ci				mlxsw_sp->ports[MLXSW_PORT_CPU_PORT];
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	mlxsw_core_cpu_port_fini(mlxsw_sp->core);
196262306a36Sopenharmony_ci	mlxsw_sp->ports[MLXSW_PORT_CPU_PORT] = NULL;
196362306a36Sopenharmony_ci	kfree(mlxsw_sp_port);
196462306a36Sopenharmony_ci}
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_cistatic bool mlxsw_sp_local_port_valid(u16 local_port)
196762306a36Sopenharmony_ci{
196862306a36Sopenharmony_ci	return local_port != MLXSW_PORT_CPU_PORT;
196962306a36Sopenharmony_ci}
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_cistatic bool mlxsw_sp_port_created(struct mlxsw_sp *mlxsw_sp, u16 local_port)
197262306a36Sopenharmony_ci{
197362306a36Sopenharmony_ci	if (!mlxsw_sp_local_port_valid(local_port))
197462306a36Sopenharmony_ci		return false;
197562306a36Sopenharmony_ci	return mlxsw_sp->ports[local_port] != NULL;
197662306a36Sopenharmony_ci}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_cistatic int mlxsw_sp_port_mapping_event_set(struct mlxsw_sp *mlxsw_sp,
197962306a36Sopenharmony_ci					   u16 local_port, bool enable)
198062306a36Sopenharmony_ci{
198162306a36Sopenharmony_ci	char pmecr_pl[MLXSW_REG_PMECR_LEN];
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	mlxsw_reg_pmecr_pack(pmecr_pl, local_port,
198462306a36Sopenharmony_ci			     enable ? MLXSW_REG_PMECR_E_GENERATE_EVENT :
198562306a36Sopenharmony_ci				      MLXSW_REG_PMECR_E_DO_NOT_GENERATE_EVENT);
198662306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmecr), pmecr_pl);
198762306a36Sopenharmony_ci}
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_cistruct mlxsw_sp_port_mapping_event {
199062306a36Sopenharmony_ci	struct list_head list;
199162306a36Sopenharmony_ci	char pmlp_pl[MLXSW_REG_PMLP_LEN];
199262306a36Sopenharmony_ci};
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_cistatic void mlxsw_sp_port_mapping_events_work(struct work_struct *work)
199562306a36Sopenharmony_ci{
199662306a36Sopenharmony_ci	struct mlxsw_sp_port_mapping_event *event, *next_event;
199762306a36Sopenharmony_ci	struct mlxsw_sp_port_mapping_events *events;
199862306a36Sopenharmony_ci	struct mlxsw_sp_port_mapping port_mapping;
199962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
200062306a36Sopenharmony_ci	struct devlink *devlink;
200162306a36Sopenharmony_ci	LIST_HEAD(event_queue);
200262306a36Sopenharmony_ci	u16 local_port;
200362306a36Sopenharmony_ci	int err;
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	events = container_of(work, struct mlxsw_sp_port_mapping_events, work);
200662306a36Sopenharmony_ci	mlxsw_sp = container_of(events, struct mlxsw_sp, port_mapping_events);
200762306a36Sopenharmony_ci	devlink = priv_to_devlink(mlxsw_sp->core);
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	spin_lock_bh(&events->queue_lock);
201062306a36Sopenharmony_ci	list_splice_init(&events->queue, &event_queue);
201162306a36Sopenharmony_ci	spin_unlock_bh(&events->queue_lock);
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	list_for_each_entry_safe(event, next_event, &event_queue, list) {
201462306a36Sopenharmony_ci		local_port = mlxsw_reg_pmlp_local_port_get(event->pmlp_pl);
201562306a36Sopenharmony_ci		err = mlxsw_sp_port_module_info_parse(mlxsw_sp, local_port,
201662306a36Sopenharmony_ci						      event->pmlp_pl, &port_mapping);
201762306a36Sopenharmony_ci		if (err)
201862306a36Sopenharmony_ci			goto out;
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci		if (WARN_ON_ONCE(!port_mapping.width))
202162306a36Sopenharmony_ci			goto out;
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci		devl_lock(devlink);
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci		if (!mlxsw_sp_port_created(mlxsw_sp, local_port))
202662306a36Sopenharmony_ci			mlxsw_sp_port_create(mlxsw_sp, local_port,
202762306a36Sopenharmony_ci					     false, &port_mapping);
202862306a36Sopenharmony_ci		else
202962306a36Sopenharmony_ci			WARN_ON_ONCE(1);
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci		devl_unlock(devlink);
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci		mlxsw_sp->port_mapping[local_port] = port_mapping;
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ciout:
203662306a36Sopenharmony_ci		kfree(event);
203762306a36Sopenharmony_ci	}
203862306a36Sopenharmony_ci}
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_cistatic void
204162306a36Sopenharmony_cimlxsw_sp_port_mapping_listener_func(const struct mlxsw_reg_info *reg,
204262306a36Sopenharmony_ci				    char *pmlp_pl, void *priv)
204362306a36Sopenharmony_ci{
204462306a36Sopenharmony_ci	struct mlxsw_sp_port_mapping_events *events;
204562306a36Sopenharmony_ci	struct mlxsw_sp_port_mapping_event *event;
204662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = priv;
204762306a36Sopenharmony_ci	u16 local_port;
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci	local_port = mlxsw_reg_pmlp_local_port_get(pmlp_pl);
205062306a36Sopenharmony_ci	if (WARN_ON_ONCE(!mlxsw_sp_local_port_is_valid(mlxsw_sp, local_port)))
205162306a36Sopenharmony_ci		return;
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	events = &mlxsw_sp->port_mapping_events;
205462306a36Sopenharmony_ci	event = kmalloc(sizeof(*event), GFP_ATOMIC);
205562306a36Sopenharmony_ci	if (!event)
205662306a36Sopenharmony_ci		return;
205762306a36Sopenharmony_ci	memcpy(event->pmlp_pl, pmlp_pl, sizeof(event->pmlp_pl));
205862306a36Sopenharmony_ci	spin_lock(&events->queue_lock);
205962306a36Sopenharmony_ci	list_add_tail(&event->list, &events->queue);
206062306a36Sopenharmony_ci	spin_unlock(&events->queue_lock);
206162306a36Sopenharmony_ci	mlxsw_core_schedule_work(&events->work);
206262306a36Sopenharmony_ci}
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_cistatic void
206562306a36Sopenharmony_ci__mlxsw_sp_port_mapping_events_cancel(struct mlxsw_sp *mlxsw_sp)
206662306a36Sopenharmony_ci{
206762306a36Sopenharmony_ci	struct mlxsw_sp_port_mapping_event *event, *next_event;
206862306a36Sopenharmony_ci	struct mlxsw_sp_port_mapping_events *events;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	events = &mlxsw_sp->port_mapping_events;
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	/* Caller needs to make sure that no new event is going to appear. */
207362306a36Sopenharmony_ci	cancel_work_sync(&events->work);
207462306a36Sopenharmony_ci	list_for_each_entry_safe(event, next_event, &events->queue, list) {
207562306a36Sopenharmony_ci		list_del(&event->list);
207662306a36Sopenharmony_ci		kfree(event);
207762306a36Sopenharmony_ci	}
207862306a36Sopenharmony_ci}
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_cistatic void mlxsw_sp_ports_remove(struct mlxsw_sp *mlxsw_sp)
208162306a36Sopenharmony_ci{
208262306a36Sopenharmony_ci	unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
208362306a36Sopenharmony_ci	int i;
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	for (i = 1; i < max_ports; i++)
208662306a36Sopenharmony_ci		mlxsw_sp_port_mapping_event_set(mlxsw_sp, i, false);
208762306a36Sopenharmony_ci	/* Make sure all scheduled events are processed */
208862306a36Sopenharmony_ci	__mlxsw_sp_port_mapping_events_cancel(mlxsw_sp);
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	for (i = 1; i < max_ports; i++)
209162306a36Sopenharmony_ci		if (mlxsw_sp_port_created(mlxsw_sp, i))
209262306a36Sopenharmony_ci			mlxsw_sp_port_remove(mlxsw_sp, i);
209362306a36Sopenharmony_ci	mlxsw_sp_cpu_port_remove(mlxsw_sp);
209462306a36Sopenharmony_ci	kfree(mlxsw_sp->ports);
209562306a36Sopenharmony_ci	mlxsw_sp->ports = NULL;
209662306a36Sopenharmony_ci}
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_cistatic void
209962306a36Sopenharmony_cimlxsw_sp_ports_remove_selected(struct mlxsw_core *mlxsw_core,
210062306a36Sopenharmony_ci			       bool (*selector)(void *priv, u16 local_port),
210162306a36Sopenharmony_ci			       void *priv)
210262306a36Sopenharmony_ci{
210362306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
210462306a36Sopenharmony_ci	unsigned int max_ports = mlxsw_core_max_ports(mlxsw_core);
210562306a36Sopenharmony_ci	int i;
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci	for (i = 1; i < max_ports; i++)
210862306a36Sopenharmony_ci		if (mlxsw_sp_port_created(mlxsw_sp, i) && selector(priv, i))
210962306a36Sopenharmony_ci			mlxsw_sp_port_remove(mlxsw_sp, i);
211062306a36Sopenharmony_ci}
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_cistatic int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp)
211362306a36Sopenharmony_ci{
211462306a36Sopenharmony_ci	unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
211562306a36Sopenharmony_ci	struct mlxsw_sp_port_mapping_events *events;
211662306a36Sopenharmony_ci	struct mlxsw_sp_port_mapping *port_mapping;
211762306a36Sopenharmony_ci	size_t alloc_size;
211862306a36Sopenharmony_ci	int i;
211962306a36Sopenharmony_ci	int err;
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	alloc_size = sizeof(struct mlxsw_sp_port *) * max_ports;
212262306a36Sopenharmony_ci	mlxsw_sp->ports = kzalloc(alloc_size, GFP_KERNEL);
212362306a36Sopenharmony_ci	if (!mlxsw_sp->ports)
212462306a36Sopenharmony_ci		return -ENOMEM;
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	events = &mlxsw_sp->port_mapping_events;
212762306a36Sopenharmony_ci	INIT_LIST_HEAD(&events->queue);
212862306a36Sopenharmony_ci	spin_lock_init(&events->queue_lock);
212962306a36Sopenharmony_ci	INIT_WORK(&events->work, mlxsw_sp_port_mapping_events_work);
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	for (i = 1; i < max_ports; i++) {
213262306a36Sopenharmony_ci		err = mlxsw_sp_port_mapping_event_set(mlxsw_sp, i, true);
213362306a36Sopenharmony_ci		if (err)
213462306a36Sopenharmony_ci			goto err_event_enable;
213562306a36Sopenharmony_ci	}
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	err = mlxsw_sp_cpu_port_create(mlxsw_sp);
213862306a36Sopenharmony_ci	if (err)
213962306a36Sopenharmony_ci		goto err_cpu_port_create;
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	for (i = 1; i < max_ports; i++) {
214262306a36Sopenharmony_ci		port_mapping = &mlxsw_sp->port_mapping[i];
214362306a36Sopenharmony_ci		if (!port_mapping->width)
214462306a36Sopenharmony_ci			continue;
214562306a36Sopenharmony_ci		err = mlxsw_sp_port_create(mlxsw_sp, i, false, port_mapping);
214662306a36Sopenharmony_ci		if (err)
214762306a36Sopenharmony_ci			goto err_port_create;
214862306a36Sopenharmony_ci	}
214962306a36Sopenharmony_ci	return 0;
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_cierr_port_create:
215262306a36Sopenharmony_ci	for (i--; i >= 1; i--)
215362306a36Sopenharmony_ci		if (mlxsw_sp_port_created(mlxsw_sp, i))
215462306a36Sopenharmony_ci			mlxsw_sp_port_remove(mlxsw_sp, i);
215562306a36Sopenharmony_ci	i = max_ports;
215662306a36Sopenharmony_ci	mlxsw_sp_cpu_port_remove(mlxsw_sp);
215762306a36Sopenharmony_cierr_cpu_port_create:
215862306a36Sopenharmony_cierr_event_enable:
215962306a36Sopenharmony_ci	for (i--; i >= 1; i--)
216062306a36Sopenharmony_ci		mlxsw_sp_port_mapping_event_set(mlxsw_sp, i, false);
216162306a36Sopenharmony_ci	/* Make sure all scheduled events are processed */
216262306a36Sopenharmony_ci	__mlxsw_sp_port_mapping_events_cancel(mlxsw_sp);
216362306a36Sopenharmony_ci	kfree(mlxsw_sp->ports);
216462306a36Sopenharmony_ci	mlxsw_sp->ports = NULL;
216562306a36Sopenharmony_ci	return err;
216662306a36Sopenharmony_ci}
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_cistatic int mlxsw_sp_port_module_info_init(struct mlxsw_sp *mlxsw_sp)
216962306a36Sopenharmony_ci{
217062306a36Sopenharmony_ci	unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
217162306a36Sopenharmony_ci	struct mlxsw_sp_port_mapping *port_mapping;
217262306a36Sopenharmony_ci	int i;
217362306a36Sopenharmony_ci	int err;
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	mlxsw_sp->port_mapping = kcalloc(max_ports,
217662306a36Sopenharmony_ci					 sizeof(struct mlxsw_sp_port_mapping),
217762306a36Sopenharmony_ci					 GFP_KERNEL);
217862306a36Sopenharmony_ci	if (!mlxsw_sp->port_mapping)
217962306a36Sopenharmony_ci		return -ENOMEM;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	for (i = 1; i < max_ports; i++) {
218262306a36Sopenharmony_ci		port_mapping = &mlxsw_sp->port_mapping[i];
218362306a36Sopenharmony_ci		err = mlxsw_sp_port_module_info_get(mlxsw_sp, i, port_mapping);
218462306a36Sopenharmony_ci		if (err)
218562306a36Sopenharmony_ci			goto err_port_module_info_get;
218662306a36Sopenharmony_ci	}
218762306a36Sopenharmony_ci	return 0;
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_cierr_port_module_info_get:
219062306a36Sopenharmony_ci	kfree(mlxsw_sp->port_mapping);
219162306a36Sopenharmony_ci	return err;
219262306a36Sopenharmony_ci}
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_cistatic void mlxsw_sp_port_module_info_fini(struct mlxsw_sp *mlxsw_sp)
219562306a36Sopenharmony_ci{
219662306a36Sopenharmony_ci	kfree(mlxsw_sp->port_mapping);
219762306a36Sopenharmony_ci}
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_cistatic int
220062306a36Sopenharmony_cimlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp,
220162306a36Sopenharmony_ci			   struct mlxsw_sp_port_mapping *port_mapping,
220262306a36Sopenharmony_ci			   unsigned int count, const char *pmtdb_pl)
220362306a36Sopenharmony_ci{
220462306a36Sopenharmony_ci	struct mlxsw_sp_port_mapping split_port_mapping;
220562306a36Sopenharmony_ci	int err, i;
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci	split_port_mapping = *port_mapping;
220862306a36Sopenharmony_ci	split_port_mapping.width /= count;
220962306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
221062306a36Sopenharmony_ci		u16 s_local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i);
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci		if (!mlxsw_sp_local_port_valid(s_local_port))
221362306a36Sopenharmony_ci			continue;
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci		err = mlxsw_sp_port_create(mlxsw_sp, s_local_port,
221662306a36Sopenharmony_ci					   true, &split_port_mapping);
221762306a36Sopenharmony_ci		if (err)
221862306a36Sopenharmony_ci			goto err_port_create;
221962306a36Sopenharmony_ci		split_port_mapping.lane += split_port_mapping.width;
222062306a36Sopenharmony_ci	}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci	return 0;
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_cierr_port_create:
222562306a36Sopenharmony_ci	for (i--; i >= 0; i--) {
222662306a36Sopenharmony_ci		u16 s_local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i);
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci		if (mlxsw_sp_port_created(mlxsw_sp, s_local_port))
222962306a36Sopenharmony_ci			mlxsw_sp_port_remove(mlxsw_sp, s_local_port);
223062306a36Sopenharmony_ci	}
223162306a36Sopenharmony_ci	return err;
223262306a36Sopenharmony_ci}
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_cistatic void mlxsw_sp_port_unsplit_create(struct mlxsw_sp *mlxsw_sp,
223562306a36Sopenharmony_ci					 unsigned int count,
223662306a36Sopenharmony_ci					 const char *pmtdb_pl)
223762306a36Sopenharmony_ci{
223862306a36Sopenharmony_ci	struct mlxsw_sp_port_mapping *port_mapping;
223962306a36Sopenharmony_ci	int i;
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	/* Go over original unsplit ports in the gap and recreate them. */
224262306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
224362306a36Sopenharmony_ci		u16 local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i);
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci		port_mapping = &mlxsw_sp->port_mapping[local_port];
224662306a36Sopenharmony_ci		if (!port_mapping->width || !mlxsw_sp_local_port_valid(local_port))
224762306a36Sopenharmony_ci			continue;
224862306a36Sopenharmony_ci		mlxsw_sp_port_create(mlxsw_sp, local_port,
224962306a36Sopenharmony_ci				     false, port_mapping);
225062306a36Sopenharmony_ci	}
225162306a36Sopenharmony_ci}
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_cistatic struct mlxsw_sp_port *
225462306a36Sopenharmony_cimlxsw_sp_port_get_by_local_port(struct mlxsw_sp *mlxsw_sp, u16 local_port)
225562306a36Sopenharmony_ci{
225662306a36Sopenharmony_ci	if (mlxsw_sp->ports && mlxsw_sp->ports[local_port])
225762306a36Sopenharmony_ci		return mlxsw_sp->ports[local_port];
225862306a36Sopenharmony_ci	return NULL;
225962306a36Sopenharmony_ci}
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_cistatic int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u16 local_port,
226262306a36Sopenharmony_ci			       unsigned int count,
226362306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
226462306a36Sopenharmony_ci{
226562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
226662306a36Sopenharmony_ci	struct mlxsw_sp_port_mapping port_mapping;
226762306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
226862306a36Sopenharmony_ci	enum mlxsw_reg_pmtdb_status status;
226962306a36Sopenharmony_ci	char pmtdb_pl[MLXSW_REG_PMTDB_LEN];
227062306a36Sopenharmony_ci	int i;
227162306a36Sopenharmony_ci	int err;
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	mlxsw_sp_port = mlxsw_sp_port_get_by_local_port(mlxsw_sp, local_port);
227462306a36Sopenharmony_ci	if (!mlxsw_sp_port) {
227562306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
227662306a36Sopenharmony_ci			local_port);
227762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Port number does not exist");
227862306a36Sopenharmony_ci		return -EINVAL;
227962306a36Sopenharmony_ci	}
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	if (mlxsw_sp_port->split) {
228262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Port is already split");
228362306a36Sopenharmony_ci		return -EINVAL;
228462306a36Sopenharmony_ci	}
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	mlxsw_reg_pmtdb_pack(pmtdb_pl, mlxsw_sp_port->mapping.slot_index,
228762306a36Sopenharmony_ci			     mlxsw_sp_port->mapping.module,
228862306a36Sopenharmony_ci			     mlxsw_sp_port->mapping.module_width / count,
228962306a36Sopenharmony_ci			     count);
229062306a36Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtdb), pmtdb_pl);
229162306a36Sopenharmony_ci	if (err) {
229262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to query split info");
229362306a36Sopenharmony_ci		return err;
229462306a36Sopenharmony_ci	}
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	status = mlxsw_reg_pmtdb_status_get(pmtdb_pl);
229762306a36Sopenharmony_ci	if (status != MLXSW_REG_PMTDB_STATUS_SUCCESS) {
229862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unsupported split configuration");
229962306a36Sopenharmony_ci		return -EINVAL;
230062306a36Sopenharmony_ci	}
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_ci	port_mapping = mlxsw_sp_port->mapping;
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
230562306a36Sopenharmony_ci		u16 s_local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i);
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci		if (mlxsw_sp_port_created(mlxsw_sp, s_local_port))
230862306a36Sopenharmony_ci			mlxsw_sp_port_remove(mlxsw_sp, s_local_port);
230962306a36Sopenharmony_ci	}
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	err = mlxsw_sp_port_split_create(mlxsw_sp, &port_mapping,
231262306a36Sopenharmony_ci					 count, pmtdb_pl);
231362306a36Sopenharmony_ci	if (err) {
231462306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to create split ports\n");
231562306a36Sopenharmony_ci		goto err_port_split_create;
231662306a36Sopenharmony_ci	}
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	return 0;
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_cierr_port_split_create:
232162306a36Sopenharmony_ci	mlxsw_sp_port_unsplit_create(mlxsw_sp, count, pmtdb_pl);
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	return err;
232462306a36Sopenharmony_ci}
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_cistatic int mlxsw_sp_port_unsplit(struct mlxsw_core *mlxsw_core, u16 local_port,
232762306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
232862306a36Sopenharmony_ci{
232962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
233062306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
233162306a36Sopenharmony_ci	char pmtdb_pl[MLXSW_REG_PMTDB_LEN];
233262306a36Sopenharmony_ci	unsigned int count;
233362306a36Sopenharmony_ci	int i;
233462306a36Sopenharmony_ci	int err;
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci	mlxsw_sp_port = mlxsw_sp_port_get_by_local_port(mlxsw_sp, local_port);
233762306a36Sopenharmony_ci	if (!mlxsw_sp_port) {
233862306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
233962306a36Sopenharmony_ci			local_port);
234062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Port number does not exist");
234162306a36Sopenharmony_ci		return -EINVAL;
234262306a36Sopenharmony_ci	}
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci	if (!mlxsw_sp_port->split) {
234562306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Port was not split");
234662306a36Sopenharmony_ci		return -EINVAL;
234762306a36Sopenharmony_ci	}
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci	count = mlxsw_sp_port->mapping.module_width /
235062306a36Sopenharmony_ci		mlxsw_sp_port->mapping.width;
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	mlxsw_reg_pmtdb_pack(pmtdb_pl, mlxsw_sp_port->mapping.slot_index,
235362306a36Sopenharmony_ci			     mlxsw_sp_port->mapping.module,
235462306a36Sopenharmony_ci			     mlxsw_sp_port->mapping.module_width / count,
235562306a36Sopenharmony_ci			     count);
235662306a36Sopenharmony_ci	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtdb), pmtdb_pl);
235762306a36Sopenharmony_ci	if (err) {
235862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to query split info");
235962306a36Sopenharmony_ci		return err;
236062306a36Sopenharmony_ci	}
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
236362306a36Sopenharmony_ci		u16 s_local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i);
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci		if (mlxsw_sp_port_created(mlxsw_sp, s_local_port))
236662306a36Sopenharmony_ci			mlxsw_sp_port_remove(mlxsw_sp, s_local_port);
236762306a36Sopenharmony_ci	}
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	mlxsw_sp_port_unsplit_create(mlxsw_sp, count, pmtdb_pl);
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	return 0;
237262306a36Sopenharmony_ci}
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_cistatic void
237562306a36Sopenharmony_cimlxsw_sp_port_down_wipe_counters(struct mlxsw_sp_port *mlxsw_sp_port)
237662306a36Sopenharmony_ci{
237762306a36Sopenharmony_ci	int i;
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci	for (i = 0; i < TC_MAX_QUEUE; i++)
238062306a36Sopenharmony_ci		mlxsw_sp_port->periodic_hw_stats.xstats.backlog[i] = 0;
238162306a36Sopenharmony_ci}
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_cistatic void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg,
238462306a36Sopenharmony_ci				     char *pude_pl, void *priv)
238562306a36Sopenharmony_ci{
238662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = priv;
238762306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
238862306a36Sopenharmony_ci	enum mlxsw_reg_pude_oper_status status;
238962306a36Sopenharmony_ci	u16 local_port;
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	local_port = mlxsw_reg_pude_local_port_get(pude_pl);
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci	if (WARN_ON_ONCE(!mlxsw_sp_local_port_is_valid(mlxsw_sp, local_port)))
239462306a36Sopenharmony_ci		return;
239562306a36Sopenharmony_ci	mlxsw_sp_port = mlxsw_sp->ports[local_port];
239662306a36Sopenharmony_ci	if (!mlxsw_sp_port)
239762306a36Sopenharmony_ci		return;
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ci	status = mlxsw_reg_pude_oper_status_get(pude_pl);
240062306a36Sopenharmony_ci	if (status == MLXSW_PORT_OPER_STATUS_UP) {
240162306a36Sopenharmony_ci		netdev_info(mlxsw_sp_port->dev, "link up\n");
240262306a36Sopenharmony_ci		netif_carrier_on(mlxsw_sp_port->dev);
240362306a36Sopenharmony_ci		mlxsw_core_schedule_dw(&mlxsw_sp_port->ptp.shaper_dw, 0);
240462306a36Sopenharmony_ci	} else {
240562306a36Sopenharmony_ci		netdev_info(mlxsw_sp_port->dev, "link down\n");
240662306a36Sopenharmony_ci		netif_carrier_off(mlxsw_sp_port->dev);
240762306a36Sopenharmony_ci		mlxsw_sp_port_down_wipe_counters(mlxsw_sp_port);
240862306a36Sopenharmony_ci	}
240962306a36Sopenharmony_ci}
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_cistatic void mlxsw_sp1_ptp_fifo_event_func(struct mlxsw_sp *mlxsw_sp,
241262306a36Sopenharmony_ci					  char *mtpptr_pl, bool ingress)
241362306a36Sopenharmony_ci{
241462306a36Sopenharmony_ci	u16 local_port;
241562306a36Sopenharmony_ci	u8 num_rec;
241662306a36Sopenharmony_ci	int i;
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	local_port = mlxsw_reg_mtpptr_local_port_get(mtpptr_pl);
241962306a36Sopenharmony_ci	num_rec = mlxsw_reg_mtpptr_num_rec_get(mtpptr_pl);
242062306a36Sopenharmony_ci	for (i = 0; i < num_rec; i++) {
242162306a36Sopenharmony_ci		u8 domain_number;
242262306a36Sopenharmony_ci		u8 message_type;
242362306a36Sopenharmony_ci		u16 sequence_id;
242462306a36Sopenharmony_ci		u64 timestamp;
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci		mlxsw_reg_mtpptr_unpack(mtpptr_pl, i, &message_type,
242762306a36Sopenharmony_ci					&domain_number, &sequence_id,
242862306a36Sopenharmony_ci					&timestamp);
242962306a36Sopenharmony_ci		mlxsw_sp1_ptp_got_timestamp(mlxsw_sp, ingress, local_port,
243062306a36Sopenharmony_ci					    message_type, domain_number,
243162306a36Sopenharmony_ci					    sequence_id, timestamp);
243262306a36Sopenharmony_ci	}
243362306a36Sopenharmony_ci}
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_cistatic void mlxsw_sp1_ptp_ing_fifo_event_func(const struct mlxsw_reg_info *reg,
243662306a36Sopenharmony_ci					      char *mtpptr_pl, void *priv)
243762306a36Sopenharmony_ci{
243862306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = priv;
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_ci	mlxsw_sp1_ptp_fifo_event_func(mlxsw_sp, mtpptr_pl, true);
244162306a36Sopenharmony_ci}
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_cistatic void mlxsw_sp1_ptp_egr_fifo_event_func(const struct mlxsw_reg_info *reg,
244462306a36Sopenharmony_ci					      char *mtpptr_pl, void *priv)
244562306a36Sopenharmony_ci{
244662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = priv;
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	mlxsw_sp1_ptp_fifo_event_func(mlxsw_sp, mtpptr_pl, false);
244962306a36Sopenharmony_ci}
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_civoid mlxsw_sp_rx_listener_no_mark_func(struct sk_buff *skb,
245262306a36Sopenharmony_ci				       u16 local_port, void *priv)
245362306a36Sopenharmony_ci{
245462306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = priv;
245562306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port];
245662306a36Sopenharmony_ci	struct mlxsw_sp_port_pcpu_stats *pcpu_stats;
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	if (unlikely(!mlxsw_sp_port)) {
245962306a36Sopenharmony_ci		dev_warn_ratelimited(mlxsw_sp->bus_info->dev, "Port %d: skb received for non-existent port\n",
246062306a36Sopenharmony_ci				     local_port);
246162306a36Sopenharmony_ci		return;
246262306a36Sopenharmony_ci	}
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci	skb->dev = mlxsw_sp_port->dev;
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats);
246762306a36Sopenharmony_ci	u64_stats_update_begin(&pcpu_stats->syncp);
246862306a36Sopenharmony_ci	pcpu_stats->rx_packets++;
246962306a36Sopenharmony_ci	pcpu_stats->rx_bytes += skb->len;
247062306a36Sopenharmony_ci	u64_stats_update_end(&pcpu_stats->syncp);
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, skb->dev);
247362306a36Sopenharmony_ci	netif_receive_skb(skb);
247462306a36Sopenharmony_ci}
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_cistatic void mlxsw_sp_rx_listener_mark_func(struct sk_buff *skb, u16 local_port,
247762306a36Sopenharmony_ci					   void *priv)
247862306a36Sopenharmony_ci{
247962306a36Sopenharmony_ci	skb->offload_fwd_mark = 1;
248062306a36Sopenharmony_ci	return mlxsw_sp_rx_listener_no_mark_func(skb, local_port, priv);
248162306a36Sopenharmony_ci}
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_cistatic void mlxsw_sp_rx_listener_l3_mark_func(struct sk_buff *skb,
248462306a36Sopenharmony_ci					      u16 local_port, void *priv)
248562306a36Sopenharmony_ci{
248662306a36Sopenharmony_ci	skb->offload_l3_fwd_mark = 1;
248762306a36Sopenharmony_ci	skb->offload_fwd_mark = 1;
248862306a36Sopenharmony_ci	return mlxsw_sp_rx_listener_no_mark_func(skb, local_port, priv);
248962306a36Sopenharmony_ci}
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_civoid mlxsw_sp_ptp_receive(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
249262306a36Sopenharmony_ci			  u16 local_port)
249362306a36Sopenharmony_ci{
249462306a36Sopenharmony_ci	mlxsw_sp->ptp_ops->receive(mlxsw_sp, skb, local_port);
249562306a36Sopenharmony_ci}
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci#define MLXSW_SP_RXL_NO_MARK(_trap_id, _action, _trap_group, _is_ctrl)	\
249862306a36Sopenharmony_ci	MLXSW_RXL(mlxsw_sp_rx_listener_no_mark_func, _trap_id, _action,	\
249962306a36Sopenharmony_ci		  _is_ctrl, SP_##_trap_group, DISCARD)
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci#define MLXSW_SP_RXL_MARK(_trap_id, _action, _trap_group, _is_ctrl)	\
250262306a36Sopenharmony_ci	MLXSW_RXL(mlxsw_sp_rx_listener_mark_func, _trap_id, _action,	\
250362306a36Sopenharmony_ci		_is_ctrl, SP_##_trap_group, DISCARD)
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci#define MLXSW_SP_RXL_L3_MARK(_trap_id, _action, _trap_group, _is_ctrl)	\
250662306a36Sopenharmony_ci	MLXSW_RXL(mlxsw_sp_rx_listener_l3_mark_func, _trap_id, _action,	\
250762306a36Sopenharmony_ci		_is_ctrl, SP_##_trap_group, DISCARD)
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_ci#define MLXSW_SP_EVENTL(_func, _trap_id)		\
251062306a36Sopenharmony_ci	MLXSW_EVENTL(_func, _trap_id, SP_EVENT)
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_cistatic const struct mlxsw_listener mlxsw_sp_listener[] = {
251362306a36Sopenharmony_ci	/* Events */
251462306a36Sopenharmony_ci	MLXSW_SP_EVENTL(mlxsw_sp_pude_event_func, PUDE),
251562306a36Sopenharmony_ci	/* L2 traps */
251662306a36Sopenharmony_ci	MLXSW_SP_RXL_NO_MARK(FID_MISS, TRAP_TO_CPU, FID_MISS, false),
251762306a36Sopenharmony_ci	/* L3 traps */
251862306a36Sopenharmony_ci	MLXSW_SP_RXL_MARK(IPV6_UNSPECIFIED_ADDRESS, TRAP_TO_CPU, ROUTER_EXP,
251962306a36Sopenharmony_ci			  false),
252062306a36Sopenharmony_ci	MLXSW_SP_RXL_MARK(IPV6_LINK_LOCAL_SRC, TRAP_TO_CPU, ROUTER_EXP, false),
252162306a36Sopenharmony_ci	MLXSW_SP_RXL_MARK(IPV6_MC_LINK_LOCAL_DEST, TRAP_TO_CPU, ROUTER_EXP,
252262306a36Sopenharmony_ci			  false),
252362306a36Sopenharmony_ci	MLXSW_SP_RXL_NO_MARK(DISCARD_ING_ROUTER_SIP_CLASS_E, FORWARD,
252462306a36Sopenharmony_ci			     ROUTER_EXP, false),
252562306a36Sopenharmony_ci	MLXSW_SP_RXL_NO_MARK(DISCARD_ING_ROUTER_MC_DMAC, FORWARD,
252662306a36Sopenharmony_ci			     ROUTER_EXP, false),
252762306a36Sopenharmony_ci	MLXSW_SP_RXL_NO_MARK(DISCARD_ING_ROUTER_SIP_DIP, FORWARD,
252862306a36Sopenharmony_ci			     ROUTER_EXP, false),
252962306a36Sopenharmony_ci	MLXSW_SP_RXL_NO_MARK(DISCARD_ING_ROUTER_DIP_LINK_LOCAL, FORWARD,
253062306a36Sopenharmony_ci			     ROUTER_EXP, false),
253162306a36Sopenharmony_ci	/* Multicast Router Traps */
253262306a36Sopenharmony_ci	MLXSW_SP_RXL_MARK(ACL1, TRAP_TO_CPU, MULTICAST, false),
253362306a36Sopenharmony_ci	MLXSW_SP_RXL_L3_MARK(ACL2, TRAP_TO_CPU, MULTICAST, false),
253462306a36Sopenharmony_ci	/* NVE traps */
253562306a36Sopenharmony_ci	MLXSW_SP_RXL_MARK(NVE_ENCAP_ARP, TRAP_TO_CPU, NEIGH_DISCOVERY, false),
253662306a36Sopenharmony_ci};
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_cistatic const struct mlxsw_listener mlxsw_sp1_listener[] = {
253962306a36Sopenharmony_ci	/* Events */
254062306a36Sopenharmony_ci	MLXSW_EVENTL(mlxsw_sp1_ptp_egr_fifo_event_func, PTP_EGR_FIFO, SP_PTP0),
254162306a36Sopenharmony_ci	MLXSW_EVENTL(mlxsw_sp1_ptp_ing_fifo_event_func, PTP_ING_FIFO, SP_PTP0),
254262306a36Sopenharmony_ci};
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_cistatic const struct mlxsw_listener mlxsw_sp2_listener[] = {
254562306a36Sopenharmony_ci	/* Events */
254662306a36Sopenharmony_ci	MLXSW_SP_EVENTL(mlxsw_sp_port_mapping_listener_func, PMLPE),
254762306a36Sopenharmony_ci};
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_cistatic int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core)
255062306a36Sopenharmony_ci{
255162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
255262306a36Sopenharmony_ci	char qpcr_pl[MLXSW_REG_QPCR_LEN];
255362306a36Sopenharmony_ci	enum mlxsw_reg_qpcr_ir_units ir_units;
255462306a36Sopenharmony_ci	int max_cpu_policers;
255562306a36Sopenharmony_ci	bool is_bytes;
255662306a36Sopenharmony_ci	u8 burst_size;
255762306a36Sopenharmony_ci	u32 rate;
255862306a36Sopenharmony_ci	int i, err;
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_CPU_POLICERS))
256162306a36Sopenharmony_ci		return -EIO;
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci	max_cpu_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_CPU_POLICERS);
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	ir_units = MLXSW_REG_QPCR_IR_UNITS_M;
256662306a36Sopenharmony_ci	for (i = 0; i < max_cpu_policers; i++) {
256762306a36Sopenharmony_ci		is_bytes = false;
256862306a36Sopenharmony_ci		switch (i) {
256962306a36Sopenharmony_ci		case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP:
257062306a36Sopenharmony_ci		case MLXSW_REG_HTGT_TRAP_GROUP_SP_MULTICAST:
257162306a36Sopenharmony_ci		case MLXSW_REG_HTGT_TRAP_GROUP_SP_FID_MISS:
257262306a36Sopenharmony_ci			rate = 1024;
257362306a36Sopenharmony_ci			burst_size = 7;
257462306a36Sopenharmony_ci			break;
257562306a36Sopenharmony_ci		default:
257662306a36Sopenharmony_ci			continue;
257762306a36Sopenharmony_ci		}
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_ci		__set_bit(i, mlxsw_sp->trap->policers_usage);
258062306a36Sopenharmony_ci		mlxsw_reg_qpcr_pack(qpcr_pl, i, ir_units, is_bytes, rate,
258162306a36Sopenharmony_ci				    burst_size);
258262306a36Sopenharmony_ci		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(qpcr), qpcr_pl);
258362306a36Sopenharmony_ci		if (err)
258462306a36Sopenharmony_ci			return err;
258562306a36Sopenharmony_ci	}
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci	return 0;
258862306a36Sopenharmony_ci}
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_cistatic int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core)
259162306a36Sopenharmony_ci{
259262306a36Sopenharmony_ci	char htgt_pl[MLXSW_REG_HTGT_LEN];
259362306a36Sopenharmony_ci	enum mlxsw_reg_htgt_trap_group i;
259462306a36Sopenharmony_ci	int max_cpu_policers;
259562306a36Sopenharmony_ci	int max_trap_groups;
259662306a36Sopenharmony_ci	u8 priority, tc;
259762306a36Sopenharmony_ci	u16 policer_id;
259862306a36Sopenharmony_ci	int err;
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_TRAP_GROUPS))
260162306a36Sopenharmony_ci		return -EIO;
260262306a36Sopenharmony_ci
260362306a36Sopenharmony_ci	max_trap_groups = MLXSW_CORE_RES_GET(mlxsw_core, MAX_TRAP_GROUPS);
260462306a36Sopenharmony_ci	max_cpu_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_CPU_POLICERS);
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ci	for (i = 0; i < max_trap_groups; i++) {
260762306a36Sopenharmony_ci		policer_id = i;
260862306a36Sopenharmony_ci		switch (i) {
260962306a36Sopenharmony_ci		case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP:
261062306a36Sopenharmony_ci		case MLXSW_REG_HTGT_TRAP_GROUP_SP_MULTICAST:
261162306a36Sopenharmony_ci		case MLXSW_REG_HTGT_TRAP_GROUP_SP_FID_MISS:
261262306a36Sopenharmony_ci			priority = 1;
261362306a36Sopenharmony_ci			tc = 1;
261462306a36Sopenharmony_ci			break;
261562306a36Sopenharmony_ci		case MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT:
261662306a36Sopenharmony_ci			priority = MLXSW_REG_HTGT_DEFAULT_PRIORITY;
261762306a36Sopenharmony_ci			tc = MLXSW_REG_HTGT_DEFAULT_TC;
261862306a36Sopenharmony_ci			policer_id = MLXSW_REG_HTGT_INVALID_POLICER;
261962306a36Sopenharmony_ci			break;
262062306a36Sopenharmony_ci		default:
262162306a36Sopenharmony_ci			continue;
262262306a36Sopenharmony_ci		}
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci		if (max_cpu_policers <= policer_id &&
262562306a36Sopenharmony_ci		    policer_id != MLXSW_REG_HTGT_INVALID_POLICER)
262662306a36Sopenharmony_ci			return -EIO;
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci		mlxsw_reg_htgt_pack(htgt_pl, i, policer_id, priority, tc);
262962306a36Sopenharmony_ci		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl);
263062306a36Sopenharmony_ci		if (err)
263162306a36Sopenharmony_ci			return err;
263262306a36Sopenharmony_ci	}
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	return 0;
263562306a36Sopenharmony_ci}
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_cistatic int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp)
263862306a36Sopenharmony_ci{
263962306a36Sopenharmony_ci	struct mlxsw_sp_trap *trap;
264062306a36Sopenharmony_ci	u64 max_policers;
264162306a36Sopenharmony_ci	int err;
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_CPU_POLICERS))
264462306a36Sopenharmony_ci		return -EIO;
264562306a36Sopenharmony_ci	max_policers = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_CPU_POLICERS);
264662306a36Sopenharmony_ci	trap = kzalloc(struct_size(trap, policers_usage,
264762306a36Sopenharmony_ci				   BITS_TO_LONGS(max_policers)), GFP_KERNEL);
264862306a36Sopenharmony_ci	if (!trap)
264962306a36Sopenharmony_ci		return -ENOMEM;
265062306a36Sopenharmony_ci	trap->max_policers = max_policers;
265162306a36Sopenharmony_ci	mlxsw_sp->trap = trap;
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_ci	err = mlxsw_sp_cpu_policers_set(mlxsw_sp->core);
265462306a36Sopenharmony_ci	if (err)
265562306a36Sopenharmony_ci		goto err_cpu_policers_set;
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci	err = mlxsw_sp_trap_groups_set(mlxsw_sp->core);
265862306a36Sopenharmony_ci	if (err)
265962306a36Sopenharmony_ci		goto err_trap_groups_set;
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ci	err = mlxsw_core_traps_register(mlxsw_sp->core, mlxsw_sp_listener,
266262306a36Sopenharmony_ci					ARRAY_SIZE(mlxsw_sp_listener),
266362306a36Sopenharmony_ci					mlxsw_sp);
266462306a36Sopenharmony_ci	if (err)
266562306a36Sopenharmony_ci		goto err_traps_register;
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci	err = mlxsw_core_traps_register(mlxsw_sp->core, mlxsw_sp->listeners,
266862306a36Sopenharmony_ci					mlxsw_sp->listeners_count, mlxsw_sp);
266962306a36Sopenharmony_ci	if (err)
267062306a36Sopenharmony_ci		goto err_extra_traps_init;
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci	return 0;
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_cierr_extra_traps_init:
267562306a36Sopenharmony_ci	mlxsw_core_traps_unregister(mlxsw_sp->core, mlxsw_sp_listener,
267662306a36Sopenharmony_ci				    ARRAY_SIZE(mlxsw_sp_listener),
267762306a36Sopenharmony_ci				    mlxsw_sp);
267862306a36Sopenharmony_cierr_traps_register:
267962306a36Sopenharmony_cierr_trap_groups_set:
268062306a36Sopenharmony_cierr_cpu_policers_set:
268162306a36Sopenharmony_ci	kfree(trap);
268262306a36Sopenharmony_ci	return err;
268362306a36Sopenharmony_ci}
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_cistatic void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp)
268662306a36Sopenharmony_ci{
268762306a36Sopenharmony_ci	mlxsw_core_traps_unregister(mlxsw_sp->core, mlxsw_sp->listeners,
268862306a36Sopenharmony_ci				    mlxsw_sp->listeners_count,
268962306a36Sopenharmony_ci				    mlxsw_sp);
269062306a36Sopenharmony_ci	mlxsw_core_traps_unregister(mlxsw_sp->core, mlxsw_sp_listener,
269162306a36Sopenharmony_ci				    ARRAY_SIZE(mlxsw_sp_listener), mlxsw_sp);
269262306a36Sopenharmony_ci	kfree(mlxsw_sp->trap);
269362306a36Sopenharmony_ci}
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci#define MLXSW_SP_LAG_SEED_INIT 0xcafecafe
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_cistatic int mlxsw_sp_lag_init(struct mlxsw_sp *mlxsw_sp)
269862306a36Sopenharmony_ci{
269962306a36Sopenharmony_ci	char slcr_pl[MLXSW_REG_SLCR_LEN];
270062306a36Sopenharmony_ci	u16 max_lag;
270162306a36Sopenharmony_ci	u32 seed;
270262306a36Sopenharmony_ci	int err;
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci	seed = jhash(mlxsw_sp->base_mac, sizeof(mlxsw_sp->base_mac),
270562306a36Sopenharmony_ci		     MLXSW_SP_LAG_SEED_INIT);
270662306a36Sopenharmony_ci	mlxsw_reg_slcr_pack(slcr_pl, MLXSW_REG_SLCR_LAG_HASH_SMAC |
270762306a36Sopenharmony_ci				     MLXSW_REG_SLCR_LAG_HASH_DMAC |
270862306a36Sopenharmony_ci				     MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE |
270962306a36Sopenharmony_ci				     MLXSW_REG_SLCR_LAG_HASH_VLANID |
271062306a36Sopenharmony_ci				     MLXSW_REG_SLCR_LAG_HASH_SIP |
271162306a36Sopenharmony_ci				     MLXSW_REG_SLCR_LAG_HASH_DIP |
271262306a36Sopenharmony_ci				     MLXSW_REG_SLCR_LAG_HASH_SPORT |
271362306a36Sopenharmony_ci				     MLXSW_REG_SLCR_LAG_HASH_DPORT |
271462306a36Sopenharmony_ci				     MLXSW_REG_SLCR_LAG_HASH_IPPROTO, seed);
271562306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcr), slcr_pl);
271662306a36Sopenharmony_ci	if (err)
271762306a36Sopenharmony_ci		return err;
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci	err = mlxsw_core_max_lag(mlxsw_sp->core, &max_lag);
272062306a36Sopenharmony_ci	if (err)
272162306a36Sopenharmony_ci		return err;
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LAG_MEMBERS))
272462306a36Sopenharmony_ci		return -EIO;
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	mlxsw_sp->lags = kcalloc(max_lag, sizeof(struct mlxsw_sp_upper),
272762306a36Sopenharmony_ci				 GFP_KERNEL);
272862306a36Sopenharmony_ci	if (!mlxsw_sp->lags)
272962306a36Sopenharmony_ci		return -ENOMEM;
273062306a36Sopenharmony_ci
273162306a36Sopenharmony_ci	return 0;
273262306a36Sopenharmony_ci}
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_cistatic void mlxsw_sp_lag_fini(struct mlxsw_sp *mlxsw_sp)
273562306a36Sopenharmony_ci{
273662306a36Sopenharmony_ci	kfree(mlxsw_sp->lags);
273762306a36Sopenharmony_ci}
273862306a36Sopenharmony_ci
273962306a36Sopenharmony_cistatic const struct mlxsw_sp_ptp_ops mlxsw_sp1_ptp_ops = {
274062306a36Sopenharmony_ci	.clock_init	= mlxsw_sp1_ptp_clock_init,
274162306a36Sopenharmony_ci	.clock_fini	= mlxsw_sp1_ptp_clock_fini,
274262306a36Sopenharmony_ci	.init		= mlxsw_sp1_ptp_init,
274362306a36Sopenharmony_ci	.fini		= mlxsw_sp1_ptp_fini,
274462306a36Sopenharmony_ci	.receive	= mlxsw_sp1_ptp_receive,
274562306a36Sopenharmony_ci	.transmitted	= mlxsw_sp1_ptp_transmitted,
274662306a36Sopenharmony_ci	.hwtstamp_get	= mlxsw_sp1_ptp_hwtstamp_get,
274762306a36Sopenharmony_ci	.hwtstamp_set	= mlxsw_sp1_ptp_hwtstamp_set,
274862306a36Sopenharmony_ci	.shaper_work	= mlxsw_sp1_ptp_shaper_work,
274962306a36Sopenharmony_ci	.get_ts_info	= mlxsw_sp1_ptp_get_ts_info,
275062306a36Sopenharmony_ci	.get_stats_count = mlxsw_sp1_get_stats_count,
275162306a36Sopenharmony_ci	.get_stats_strings = mlxsw_sp1_get_stats_strings,
275262306a36Sopenharmony_ci	.get_stats	= mlxsw_sp1_get_stats,
275362306a36Sopenharmony_ci	.txhdr_construct = mlxsw_sp_ptp_txhdr_construct,
275462306a36Sopenharmony_ci};
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_cistatic const struct mlxsw_sp_ptp_ops mlxsw_sp2_ptp_ops = {
275762306a36Sopenharmony_ci	.clock_init	= mlxsw_sp2_ptp_clock_init,
275862306a36Sopenharmony_ci	.clock_fini	= mlxsw_sp2_ptp_clock_fini,
275962306a36Sopenharmony_ci	.init		= mlxsw_sp2_ptp_init,
276062306a36Sopenharmony_ci	.fini		= mlxsw_sp2_ptp_fini,
276162306a36Sopenharmony_ci	.receive	= mlxsw_sp2_ptp_receive,
276262306a36Sopenharmony_ci	.transmitted	= mlxsw_sp2_ptp_transmitted,
276362306a36Sopenharmony_ci	.hwtstamp_get	= mlxsw_sp2_ptp_hwtstamp_get,
276462306a36Sopenharmony_ci	.hwtstamp_set	= mlxsw_sp2_ptp_hwtstamp_set,
276562306a36Sopenharmony_ci	.shaper_work	= mlxsw_sp2_ptp_shaper_work,
276662306a36Sopenharmony_ci	.get_ts_info	= mlxsw_sp2_ptp_get_ts_info,
276762306a36Sopenharmony_ci	.get_stats_count = mlxsw_sp2_get_stats_count,
276862306a36Sopenharmony_ci	.get_stats_strings = mlxsw_sp2_get_stats_strings,
276962306a36Sopenharmony_ci	.get_stats	= mlxsw_sp2_get_stats,
277062306a36Sopenharmony_ci	.txhdr_construct = mlxsw_sp2_ptp_txhdr_construct,
277162306a36Sopenharmony_ci};
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_cistatic const struct mlxsw_sp_ptp_ops mlxsw_sp4_ptp_ops = {
277462306a36Sopenharmony_ci	.clock_init	= mlxsw_sp2_ptp_clock_init,
277562306a36Sopenharmony_ci	.clock_fini	= mlxsw_sp2_ptp_clock_fini,
277662306a36Sopenharmony_ci	.init		= mlxsw_sp2_ptp_init,
277762306a36Sopenharmony_ci	.fini		= mlxsw_sp2_ptp_fini,
277862306a36Sopenharmony_ci	.receive	= mlxsw_sp2_ptp_receive,
277962306a36Sopenharmony_ci	.transmitted	= mlxsw_sp2_ptp_transmitted,
278062306a36Sopenharmony_ci	.hwtstamp_get	= mlxsw_sp2_ptp_hwtstamp_get,
278162306a36Sopenharmony_ci	.hwtstamp_set	= mlxsw_sp2_ptp_hwtstamp_set,
278262306a36Sopenharmony_ci	.shaper_work	= mlxsw_sp2_ptp_shaper_work,
278362306a36Sopenharmony_ci	.get_ts_info	= mlxsw_sp2_ptp_get_ts_info,
278462306a36Sopenharmony_ci	.get_stats_count = mlxsw_sp2_get_stats_count,
278562306a36Sopenharmony_ci	.get_stats_strings = mlxsw_sp2_get_stats_strings,
278662306a36Sopenharmony_ci	.get_stats	= mlxsw_sp2_get_stats,
278762306a36Sopenharmony_ci	.txhdr_construct = mlxsw_sp_ptp_txhdr_construct,
278862306a36Sopenharmony_ci};
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_cistruct mlxsw_sp_sample_trigger_node {
279162306a36Sopenharmony_ci	struct mlxsw_sp_sample_trigger trigger;
279262306a36Sopenharmony_ci	struct mlxsw_sp_sample_params params;
279362306a36Sopenharmony_ci	struct rhash_head ht_node;
279462306a36Sopenharmony_ci	struct rcu_head rcu;
279562306a36Sopenharmony_ci	refcount_t refcount;
279662306a36Sopenharmony_ci};
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp_sample_trigger_ht_params = {
279962306a36Sopenharmony_ci	.key_offset = offsetof(struct mlxsw_sp_sample_trigger_node, trigger),
280062306a36Sopenharmony_ci	.head_offset = offsetof(struct mlxsw_sp_sample_trigger_node, ht_node),
280162306a36Sopenharmony_ci	.key_len = sizeof(struct mlxsw_sp_sample_trigger),
280262306a36Sopenharmony_ci	.automatic_shrinking = true,
280362306a36Sopenharmony_ci};
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_cistatic void
280662306a36Sopenharmony_cimlxsw_sp_sample_trigger_key_init(struct mlxsw_sp_sample_trigger *key,
280762306a36Sopenharmony_ci				 const struct mlxsw_sp_sample_trigger *trigger)
280862306a36Sopenharmony_ci{
280962306a36Sopenharmony_ci	memset(key, 0, sizeof(*key));
281062306a36Sopenharmony_ci	key->type = trigger->type;
281162306a36Sopenharmony_ci	key->local_port = trigger->local_port;
281262306a36Sopenharmony_ci}
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci/* RCU read lock must be held */
281562306a36Sopenharmony_cistruct mlxsw_sp_sample_params *
281662306a36Sopenharmony_cimlxsw_sp_sample_trigger_params_lookup(struct mlxsw_sp *mlxsw_sp,
281762306a36Sopenharmony_ci				      const struct mlxsw_sp_sample_trigger *trigger)
281862306a36Sopenharmony_ci{
281962306a36Sopenharmony_ci	struct mlxsw_sp_sample_trigger_node *trigger_node;
282062306a36Sopenharmony_ci	struct mlxsw_sp_sample_trigger key;
282162306a36Sopenharmony_ci
282262306a36Sopenharmony_ci	mlxsw_sp_sample_trigger_key_init(&key, trigger);
282362306a36Sopenharmony_ci	trigger_node = rhashtable_lookup(&mlxsw_sp->sample_trigger_ht, &key,
282462306a36Sopenharmony_ci					 mlxsw_sp_sample_trigger_ht_params);
282562306a36Sopenharmony_ci	if (!trigger_node)
282662306a36Sopenharmony_ci		return NULL;
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_ci	return &trigger_node->params;
282962306a36Sopenharmony_ci}
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_cistatic int
283262306a36Sopenharmony_cimlxsw_sp_sample_trigger_node_init(struct mlxsw_sp *mlxsw_sp,
283362306a36Sopenharmony_ci				  const struct mlxsw_sp_sample_trigger *trigger,
283462306a36Sopenharmony_ci				  const struct mlxsw_sp_sample_params *params)
283562306a36Sopenharmony_ci{
283662306a36Sopenharmony_ci	struct mlxsw_sp_sample_trigger_node *trigger_node;
283762306a36Sopenharmony_ci	int err;
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_ci	trigger_node = kzalloc(sizeof(*trigger_node), GFP_KERNEL);
284062306a36Sopenharmony_ci	if (!trigger_node)
284162306a36Sopenharmony_ci		return -ENOMEM;
284262306a36Sopenharmony_ci
284362306a36Sopenharmony_ci	trigger_node->trigger = *trigger;
284462306a36Sopenharmony_ci	trigger_node->params = *params;
284562306a36Sopenharmony_ci	refcount_set(&trigger_node->refcount, 1);
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci	err = rhashtable_insert_fast(&mlxsw_sp->sample_trigger_ht,
284862306a36Sopenharmony_ci				     &trigger_node->ht_node,
284962306a36Sopenharmony_ci				     mlxsw_sp_sample_trigger_ht_params);
285062306a36Sopenharmony_ci	if (err)
285162306a36Sopenharmony_ci		goto err_rhashtable_insert;
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci	return 0;
285462306a36Sopenharmony_ci
285562306a36Sopenharmony_cierr_rhashtable_insert:
285662306a36Sopenharmony_ci	kfree(trigger_node);
285762306a36Sopenharmony_ci	return err;
285862306a36Sopenharmony_ci}
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_cistatic void
286162306a36Sopenharmony_cimlxsw_sp_sample_trigger_node_fini(struct mlxsw_sp *mlxsw_sp,
286262306a36Sopenharmony_ci				  struct mlxsw_sp_sample_trigger_node *trigger_node)
286362306a36Sopenharmony_ci{
286462306a36Sopenharmony_ci	rhashtable_remove_fast(&mlxsw_sp->sample_trigger_ht,
286562306a36Sopenharmony_ci			       &trigger_node->ht_node,
286662306a36Sopenharmony_ci			       mlxsw_sp_sample_trigger_ht_params);
286762306a36Sopenharmony_ci	kfree_rcu(trigger_node, rcu);
286862306a36Sopenharmony_ci}
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ciint
287162306a36Sopenharmony_cimlxsw_sp_sample_trigger_params_set(struct mlxsw_sp *mlxsw_sp,
287262306a36Sopenharmony_ci				   const struct mlxsw_sp_sample_trigger *trigger,
287362306a36Sopenharmony_ci				   const struct mlxsw_sp_sample_params *params,
287462306a36Sopenharmony_ci				   struct netlink_ext_ack *extack)
287562306a36Sopenharmony_ci{
287662306a36Sopenharmony_ci	struct mlxsw_sp_sample_trigger_node *trigger_node;
287762306a36Sopenharmony_ci	struct mlxsw_sp_sample_trigger key;
287862306a36Sopenharmony_ci
287962306a36Sopenharmony_ci	ASSERT_RTNL();
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci	mlxsw_sp_sample_trigger_key_init(&key, trigger);
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_ci	trigger_node = rhashtable_lookup_fast(&mlxsw_sp->sample_trigger_ht,
288462306a36Sopenharmony_ci					      &key,
288562306a36Sopenharmony_ci					      mlxsw_sp_sample_trigger_ht_params);
288662306a36Sopenharmony_ci	if (!trigger_node)
288762306a36Sopenharmony_ci		return mlxsw_sp_sample_trigger_node_init(mlxsw_sp, &key,
288862306a36Sopenharmony_ci							 params);
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_ci	if (trigger_node->trigger.local_port) {
289162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Sampling already enabled on port");
289262306a36Sopenharmony_ci		return -EINVAL;
289362306a36Sopenharmony_ci	}
289462306a36Sopenharmony_ci
289562306a36Sopenharmony_ci	if (trigger_node->params.psample_group != params->psample_group ||
289662306a36Sopenharmony_ci	    trigger_node->params.truncate != params->truncate ||
289762306a36Sopenharmony_ci	    trigger_node->params.rate != params->rate ||
289862306a36Sopenharmony_ci	    trigger_node->params.trunc_size != params->trunc_size) {
289962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Sampling parameters do not match for an existing sampling trigger");
290062306a36Sopenharmony_ci		return -EINVAL;
290162306a36Sopenharmony_ci	}
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_ci	refcount_inc(&trigger_node->refcount);
290462306a36Sopenharmony_ci
290562306a36Sopenharmony_ci	return 0;
290662306a36Sopenharmony_ci}
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_civoid
290962306a36Sopenharmony_cimlxsw_sp_sample_trigger_params_unset(struct mlxsw_sp *mlxsw_sp,
291062306a36Sopenharmony_ci				     const struct mlxsw_sp_sample_trigger *trigger)
291162306a36Sopenharmony_ci{
291262306a36Sopenharmony_ci	struct mlxsw_sp_sample_trigger_node *trigger_node;
291362306a36Sopenharmony_ci	struct mlxsw_sp_sample_trigger key;
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ci	ASSERT_RTNL();
291662306a36Sopenharmony_ci
291762306a36Sopenharmony_ci	mlxsw_sp_sample_trigger_key_init(&key, trigger);
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_ci	trigger_node = rhashtable_lookup_fast(&mlxsw_sp->sample_trigger_ht,
292062306a36Sopenharmony_ci					      &key,
292162306a36Sopenharmony_ci					      mlxsw_sp_sample_trigger_ht_params);
292262306a36Sopenharmony_ci	if (!trigger_node)
292362306a36Sopenharmony_ci		return;
292462306a36Sopenharmony_ci
292562306a36Sopenharmony_ci	if (!refcount_dec_and_test(&trigger_node->refcount))
292662306a36Sopenharmony_ci		return;
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ci	mlxsw_sp_sample_trigger_node_fini(mlxsw_sp, trigger_node);
292962306a36Sopenharmony_ci}
293062306a36Sopenharmony_ci
293162306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_event(struct notifier_block *unused,
293262306a36Sopenharmony_ci				    unsigned long event, void *ptr);
293362306a36Sopenharmony_ci
293462306a36Sopenharmony_ci#define MLXSW_SP_DEFAULT_PARSING_DEPTH 96
293562306a36Sopenharmony_ci#define MLXSW_SP_INCREASED_PARSING_DEPTH 128
293662306a36Sopenharmony_ci#define MLXSW_SP_DEFAULT_VXLAN_UDP_DPORT 4789
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_cistatic void mlxsw_sp_parsing_init(struct mlxsw_sp *mlxsw_sp)
293962306a36Sopenharmony_ci{
294062306a36Sopenharmony_ci	refcount_set(&mlxsw_sp->parsing.parsing_depth_ref, 0);
294162306a36Sopenharmony_ci	mlxsw_sp->parsing.parsing_depth = MLXSW_SP_DEFAULT_PARSING_DEPTH;
294262306a36Sopenharmony_ci	mlxsw_sp->parsing.vxlan_udp_dport = MLXSW_SP_DEFAULT_VXLAN_UDP_DPORT;
294362306a36Sopenharmony_ci	mutex_init(&mlxsw_sp->parsing.lock);
294462306a36Sopenharmony_ci}
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_cistatic void mlxsw_sp_parsing_fini(struct mlxsw_sp *mlxsw_sp)
294762306a36Sopenharmony_ci{
294862306a36Sopenharmony_ci	mutex_destroy(&mlxsw_sp->parsing.lock);
294962306a36Sopenharmony_ci	WARN_ON_ONCE(refcount_read(&mlxsw_sp->parsing.parsing_depth_ref));
295062306a36Sopenharmony_ci}
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_cistruct mlxsw_sp_ipv6_addr_node {
295362306a36Sopenharmony_ci	struct in6_addr key;
295462306a36Sopenharmony_ci	struct rhash_head ht_node;
295562306a36Sopenharmony_ci	u32 kvdl_index;
295662306a36Sopenharmony_ci	refcount_t refcount;
295762306a36Sopenharmony_ci};
295862306a36Sopenharmony_ci
295962306a36Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp_ipv6_addr_ht_params = {
296062306a36Sopenharmony_ci	.key_offset = offsetof(struct mlxsw_sp_ipv6_addr_node, key),
296162306a36Sopenharmony_ci	.head_offset = offsetof(struct mlxsw_sp_ipv6_addr_node, ht_node),
296262306a36Sopenharmony_ci	.key_len = sizeof(struct in6_addr),
296362306a36Sopenharmony_ci	.automatic_shrinking = true,
296462306a36Sopenharmony_ci};
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_cistatic int
296762306a36Sopenharmony_cimlxsw_sp_ipv6_addr_init(struct mlxsw_sp *mlxsw_sp, const struct in6_addr *addr6,
296862306a36Sopenharmony_ci			u32 *p_kvdl_index)
296962306a36Sopenharmony_ci{
297062306a36Sopenharmony_ci	struct mlxsw_sp_ipv6_addr_node *node;
297162306a36Sopenharmony_ci	char rips_pl[MLXSW_REG_RIPS_LEN];
297262306a36Sopenharmony_ci	int err;
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci	err = mlxsw_sp_kvdl_alloc(mlxsw_sp,
297562306a36Sopenharmony_ci				  MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1,
297662306a36Sopenharmony_ci				  p_kvdl_index);
297762306a36Sopenharmony_ci	if (err)
297862306a36Sopenharmony_ci		return err;
297962306a36Sopenharmony_ci
298062306a36Sopenharmony_ci	mlxsw_reg_rips_pack(rips_pl, *p_kvdl_index, addr6);
298162306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rips), rips_pl);
298262306a36Sopenharmony_ci	if (err)
298362306a36Sopenharmony_ci		goto err_rips_write;
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci	node = kzalloc(sizeof(*node), GFP_KERNEL);
298662306a36Sopenharmony_ci	if (!node) {
298762306a36Sopenharmony_ci		err = -ENOMEM;
298862306a36Sopenharmony_ci		goto err_node_alloc;
298962306a36Sopenharmony_ci	}
299062306a36Sopenharmony_ci
299162306a36Sopenharmony_ci	node->key = *addr6;
299262306a36Sopenharmony_ci	node->kvdl_index = *p_kvdl_index;
299362306a36Sopenharmony_ci	refcount_set(&node->refcount, 1);
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_ci	err = rhashtable_insert_fast(&mlxsw_sp->ipv6_addr_ht,
299662306a36Sopenharmony_ci				     &node->ht_node,
299762306a36Sopenharmony_ci				     mlxsw_sp_ipv6_addr_ht_params);
299862306a36Sopenharmony_ci	if (err)
299962306a36Sopenharmony_ci		goto err_rhashtable_insert;
300062306a36Sopenharmony_ci
300162306a36Sopenharmony_ci	return 0;
300262306a36Sopenharmony_ci
300362306a36Sopenharmony_cierr_rhashtable_insert:
300462306a36Sopenharmony_ci	kfree(node);
300562306a36Sopenharmony_cierr_node_alloc:
300662306a36Sopenharmony_cierr_rips_write:
300762306a36Sopenharmony_ci	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1,
300862306a36Sopenharmony_ci			   *p_kvdl_index);
300962306a36Sopenharmony_ci	return err;
301062306a36Sopenharmony_ci}
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_cistatic void mlxsw_sp_ipv6_addr_fini(struct mlxsw_sp *mlxsw_sp,
301362306a36Sopenharmony_ci				    struct mlxsw_sp_ipv6_addr_node *node)
301462306a36Sopenharmony_ci{
301562306a36Sopenharmony_ci	u32 kvdl_index = node->kvdl_index;
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_ci	rhashtable_remove_fast(&mlxsw_sp->ipv6_addr_ht, &node->ht_node,
301862306a36Sopenharmony_ci			       mlxsw_sp_ipv6_addr_ht_params);
301962306a36Sopenharmony_ci	kfree(node);
302062306a36Sopenharmony_ci	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1,
302162306a36Sopenharmony_ci			   kvdl_index);
302262306a36Sopenharmony_ci}
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ciint mlxsw_sp_ipv6_addr_kvdl_index_get(struct mlxsw_sp *mlxsw_sp,
302562306a36Sopenharmony_ci				      const struct in6_addr *addr6,
302662306a36Sopenharmony_ci				      u32 *p_kvdl_index)
302762306a36Sopenharmony_ci{
302862306a36Sopenharmony_ci	struct mlxsw_sp_ipv6_addr_node *node;
302962306a36Sopenharmony_ci	int err = 0;
303062306a36Sopenharmony_ci
303162306a36Sopenharmony_ci	mutex_lock(&mlxsw_sp->ipv6_addr_ht_lock);
303262306a36Sopenharmony_ci	node = rhashtable_lookup_fast(&mlxsw_sp->ipv6_addr_ht, addr6,
303362306a36Sopenharmony_ci				      mlxsw_sp_ipv6_addr_ht_params);
303462306a36Sopenharmony_ci	if (node) {
303562306a36Sopenharmony_ci		refcount_inc(&node->refcount);
303662306a36Sopenharmony_ci		*p_kvdl_index = node->kvdl_index;
303762306a36Sopenharmony_ci		goto out_unlock;
303862306a36Sopenharmony_ci	}
303962306a36Sopenharmony_ci
304062306a36Sopenharmony_ci	err = mlxsw_sp_ipv6_addr_init(mlxsw_sp, addr6, p_kvdl_index);
304162306a36Sopenharmony_ci
304262306a36Sopenharmony_ciout_unlock:
304362306a36Sopenharmony_ci	mutex_unlock(&mlxsw_sp->ipv6_addr_ht_lock);
304462306a36Sopenharmony_ci	return err;
304562306a36Sopenharmony_ci}
304662306a36Sopenharmony_ci
304762306a36Sopenharmony_civoid
304862306a36Sopenharmony_cimlxsw_sp_ipv6_addr_put(struct mlxsw_sp *mlxsw_sp, const struct in6_addr *addr6)
304962306a36Sopenharmony_ci{
305062306a36Sopenharmony_ci	struct mlxsw_sp_ipv6_addr_node *node;
305162306a36Sopenharmony_ci
305262306a36Sopenharmony_ci	mutex_lock(&mlxsw_sp->ipv6_addr_ht_lock);
305362306a36Sopenharmony_ci	node = rhashtable_lookup_fast(&mlxsw_sp->ipv6_addr_ht, addr6,
305462306a36Sopenharmony_ci				      mlxsw_sp_ipv6_addr_ht_params);
305562306a36Sopenharmony_ci	if (WARN_ON(!node))
305662306a36Sopenharmony_ci		goto out_unlock;
305762306a36Sopenharmony_ci
305862306a36Sopenharmony_ci	if (!refcount_dec_and_test(&node->refcount))
305962306a36Sopenharmony_ci		goto out_unlock;
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci	mlxsw_sp_ipv6_addr_fini(mlxsw_sp, node);
306262306a36Sopenharmony_ci
306362306a36Sopenharmony_ciout_unlock:
306462306a36Sopenharmony_ci	mutex_unlock(&mlxsw_sp->ipv6_addr_ht_lock);
306562306a36Sopenharmony_ci}
306662306a36Sopenharmony_ci
306762306a36Sopenharmony_cistatic int mlxsw_sp_ipv6_addr_ht_init(struct mlxsw_sp *mlxsw_sp)
306862306a36Sopenharmony_ci{
306962306a36Sopenharmony_ci	int err;
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci	err = rhashtable_init(&mlxsw_sp->ipv6_addr_ht,
307262306a36Sopenharmony_ci			      &mlxsw_sp_ipv6_addr_ht_params);
307362306a36Sopenharmony_ci	if (err)
307462306a36Sopenharmony_ci		return err;
307562306a36Sopenharmony_ci
307662306a36Sopenharmony_ci	mutex_init(&mlxsw_sp->ipv6_addr_ht_lock);
307762306a36Sopenharmony_ci	return 0;
307862306a36Sopenharmony_ci}
307962306a36Sopenharmony_ci
308062306a36Sopenharmony_cistatic void mlxsw_sp_ipv6_addr_ht_fini(struct mlxsw_sp *mlxsw_sp)
308162306a36Sopenharmony_ci{
308262306a36Sopenharmony_ci	mutex_destroy(&mlxsw_sp->ipv6_addr_ht_lock);
308362306a36Sopenharmony_ci	rhashtable_destroy(&mlxsw_sp->ipv6_addr_ht);
308462306a36Sopenharmony_ci}
308562306a36Sopenharmony_ci
308662306a36Sopenharmony_cistatic int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
308762306a36Sopenharmony_ci			 const struct mlxsw_bus_info *mlxsw_bus_info,
308862306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
308962306a36Sopenharmony_ci{
309062306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
309162306a36Sopenharmony_ci	int err;
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci	mlxsw_sp->core = mlxsw_core;
309462306a36Sopenharmony_ci	mlxsw_sp->bus_info = mlxsw_bus_info;
309562306a36Sopenharmony_ci
309662306a36Sopenharmony_ci	mlxsw_sp_parsing_init(mlxsw_sp);
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci	err = mlxsw_sp_base_mac_get(mlxsw_sp);
309962306a36Sopenharmony_ci	if (err) {
310062306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to get base mac\n");
310162306a36Sopenharmony_ci		return err;
310262306a36Sopenharmony_ci	}
310362306a36Sopenharmony_ci
310462306a36Sopenharmony_ci	err = mlxsw_sp_kvdl_init(mlxsw_sp);
310562306a36Sopenharmony_ci	if (err) {
310662306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize KVDL\n");
310762306a36Sopenharmony_ci		return err;
310862306a36Sopenharmony_ci	}
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_ci	err = mlxsw_sp_pgt_init(mlxsw_sp);
311162306a36Sopenharmony_ci	if (err) {
311262306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize PGT\n");
311362306a36Sopenharmony_ci		goto err_pgt_init;
311462306a36Sopenharmony_ci	}
311562306a36Sopenharmony_ci
311662306a36Sopenharmony_ci	err = mlxsw_sp_fids_init(mlxsw_sp);
311762306a36Sopenharmony_ci	if (err) {
311862306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize FIDs\n");
311962306a36Sopenharmony_ci		goto err_fids_init;
312062306a36Sopenharmony_ci	}
312162306a36Sopenharmony_ci
312262306a36Sopenharmony_ci	err = mlxsw_sp_policers_init(mlxsw_sp);
312362306a36Sopenharmony_ci	if (err) {
312462306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize policers\n");
312562306a36Sopenharmony_ci		goto err_policers_init;
312662306a36Sopenharmony_ci	}
312762306a36Sopenharmony_ci
312862306a36Sopenharmony_ci	err = mlxsw_sp_traps_init(mlxsw_sp);
312962306a36Sopenharmony_ci	if (err) {
313062306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to set traps\n");
313162306a36Sopenharmony_ci		goto err_traps_init;
313262306a36Sopenharmony_ci	}
313362306a36Sopenharmony_ci
313462306a36Sopenharmony_ci	err = mlxsw_sp_devlink_traps_init(mlxsw_sp);
313562306a36Sopenharmony_ci	if (err) {
313662306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize devlink traps\n");
313762306a36Sopenharmony_ci		goto err_devlink_traps_init;
313862306a36Sopenharmony_ci	}
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci	err = mlxsw_sp_buffers_init(mlxsw_sp);
314162306a36Sopenharmony_ci	if (err) {
314262306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize buffers\n");
314362306a36Sopenharmony_ci		goto err_buffers_init;
314462306a36Sopenharmony_ci	}
314562306a36Sopenharmony_ci
314662306a36Sopenharmony_ci	err = mlxsw_sp_lag_init(mlxsw_sp);
314762306a36Sopenharmony_ci	if (err) {
314862306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize LAG\n");
314962306a36Sopenharmony_ci		goto err_lag_init;
315062306a36Sopenharmony_ci	}
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci	/* Initialize SPAN before router and switchdev, so that those components
315362306a36Sopenharmony_ci	 * can call mlxsw_sp_span_respin().
315462306a36Sopenharmony_ci	 */
315562306a36Sopenharmony_ci	err = mlxsw_sp_span_init(mlxsw_sp);
315662306a36Sopenharmony_ci	if (err) {
315762306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to init span system\n");
315862306a36Sopenharmony_ci		goto err_span_init;
315962306a36Sopenharmony_ci	}
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci	err = mlxsw_sp_switchdev_init(mlxsw_sp);
316262306a36Sopenharmony_ci	if (err) {
316362306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize switchdev\n");
316462306a36Sopenharmony_ci		goto err_switchdev_init;
316562306a36Sopenharmony_ci	}
316662306a36Sopenharmony_ci
316762306a36Sopenharmony_ci	err = mlxsw_sp_counter_pool_init(mlxsw_sp);
316862306a36Sopenharmony_ci	if (err) {
316962306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to init counter pool\n");
317062306a36Sopenharmony_ci		goto err_counter_pool_init;
317162306a36Sopenharmony_ci	}
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci	err = mlxsw_sp_afa_init(mlxsw_sp);
317462306a36Sopenharmony_ci	if (err) {
317562306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize ACL actions\n");
317662306a36Sopenharmony_ci		goto err_afa_init;
317762306a36Sopenharmony_ci	}
317862306a36Sopenharmony_ci
317962306a36Sopenharmony_ci	err = mlxsw_sp_ipv6_addr_ht_init(mlxsw_sp);
318062306a36Sopenharmony_ci	if (err) {
318162306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize hash table for IPv6 addresses\n");
318262306a36Sopenharmony_ci		goto err_ipv6_addr_ht_init;
318362306a36Sopenharmony_ci	}
318462306a36Sopenharmony_ci
318562306a36Sopenharmony_ci	err = mlxsw_sp_nve_init(mlxsw_sp);
318662306a36Sopenharmony_ci	if (err) {
318762306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize NVE\n");
318862306a36Sopenharmony_ci		goto err_nve_init;
318962306a36Sopenharmony_ci	}
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	err = mlxsw_sp_port_range_init(mlxsw_sp);
319262306a36Sopenharmony_ci	if (err) {
319362306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize port ranges\n");
319462306a36Sopenharmony_ci		goto err_port_range_init;
319562306a36Sopenharmony_ci	}
319662306a36Sopenharmony_ci
319762306a36Sopenharmony_ci	err = mlxsw_sp_acl_init(mlxsw_sp);
319862306a36Sopenharmony_ci	if (err) {
319962306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize ACL\n");
320062306a36Sopenharmony_ci		goto err_acl_init;
320162306a36Sopenharmony_ci	}
320262306a36Sopenharmony_ci
320362306a36Sopenharmony_ci	err = mlxsw_sp_router_init(mlxsw_sp, extack);
320462306a36Sopenharmony_ci	if (err) {
320562306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize router\n");
320662306a36Sopenharmony_ci		goto err_router_init;
320762306a36Sopenharmony_ci	}
320862306a36Sopenharmony_ci
320962306a36Sopenharmony_ci	if (mlxsw_sp->bus_info->read_clock_capable) {
321062306a36Sopenharmony_ci		/* NULL is a valid return value from clock_init */
321162306a36Sopenharmony_ci		mlxsw_sp->clock =
321262306a36Sopenharmony_ci			mlxsw_sp->ptp_ops->clock_init(mlxsw_sp,
321362306a36Sopenharmony_ci						      mlxsw_sp->bus_info->dev);
321462306a36Sopenharmony_ci		if (IS_ERR(mlxsw_sp->clock)) {
321562306a36Sopenharmony_ci			err = PTR_ERR(mlxsw_sp->clock);
321662306a36Sopenharmony_ci			dev_err(mlxsw_sp->bus_info->dev, "Failed to init ptp clock\n");
321762306a36Sopenharmony_ci			goto err_ptp_clock_init;
321862306a36Sopenharmony_ci		}
321962306a36Sopenharmony_ci	}
322062306a36Sopenharmony_ci
322162306a36Sopenharmony_ci	if (mlxsw_sp->clock) {
322262306a36Sopenharmony_ci		/* NULL is a valid return value from ptp_ops->init */
322362306a36Sopenharmony_ci		mlxsw_sp->ptp_state = mlxsw_sp->ptp_ops->init(mlxsw_sp);
322462306a36Sopenharmony_ci		if (IS_ERR(mlxsw_sp->ptp_state)) {
322562306a36Sopenharmony_ci			err = PTR_ERR(mlxsw_sp->ptp_state);
322662306a36Sopenharmony_ci			dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize PTP\n");
322762306a36Sopenharmony_ci			goto err_ptp_init;
322862306a36Sopenharmony_ci		}
322962306a36Sopenharmony_ci	}
323062306a36Sopenharmony_ci
323162306a36Sopenharmony_ci	/* Initialize netdevice notifier after SPAN is initialized, so that the
323262306a36Sopenharmony_ci	 * event handler can call SPAN respin.
323362306a36Sopenharmony_ci	 */
323462306a36Sopenharmony_ci	mlxsw_sp->netdevice_nb.notifier_call = mlxsw_sp_netdevice_event;
323562306a36Sopenharmony_ci	err = register_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),
323662306a36Sopenharmony_ci					      &mlxsw_sp->netdevice_nb);
323762306a36Sopenharmony_ci	if (err) {
323862306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to register netdev notifier\n");
323962306a36Sopenharmony_ci		goto err_netdev_notifier;
324062306a36Sopenharmony_ci	}
324162306a36Sopenharmony_ci
324262306a36Sopenharmony_ci	err = mlxsw_sp_dpipe_init(mlxsw_sp);
324362306a36Sopenharmony_ci	if (err) {
324462306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to init pipeline debug\n");
324562306a36Sopenharmony_ci		goto err_dpipe_init;
324662306a36Sopenharmony_ci	}
324762306a36Sopenharmony_ci
324862306a36Sopenharmony_ci	err = mlxsw_sp_port_module_info_init(mlxsw_sp);
324962306a36Sopenharmony_ci	if (err) {
325062306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to init port module info\n");
325162306a36Sopenharmony_ci		goto err_port_module_info_init;
325262306a36Sopenharmony_ci	}
325362306a36Sopenharmony_ci
325462306a36Sopenharmony_ci	err = rhashtable_init(&mlxsw_sp->sample_trigger_ht,
325562306a36Sopenharmony_ci			      &mlxsw_sp_sample_trigger_ht_params);
325662306a36Sopenharmony_ci	if (err) {
325762306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to init sampling trigger hashtable\n");
325862306a36Sopenharmony_ci		goto err_sample_trigger_init;
325962306a36Sopenharmony_ci	}
326062306a36Sopenharmony_ci
326162306a36Sopenharmony_ci	err = mlxsw_sp_ports_create(mlxsw_sp);
326262306a36Sopenharmony_ci	if (err) {
326362306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n");
326462306a36Sopenharmony_ci		goto err_ports_create;
326562306a36Sopenharmony_ci	}
326662306a36Sopenharmony_ci
326762306a36Sopenharmony_ci	return 0;
326862306a36Sopenharmony_ci
326962306a36Sopenharmony_cierr_ports_create:
327062306a36Sopenharmony_ci	rhashtable_destroy(&mlxsw_sp->sample_trigger_ht);
327162306a36Sopenharmony_cierr_sample_trigger_init:
327262306a36Sopenharmony_ci	mlxsw_sp_port_module_info_fini(mlxsw_sp);
327362306a36Sopenharmony_cierr_port_module_info_init:
327462306a36Sopenharmony_ci	mlxsw_sp_dpipe_fini(mlxsw_sp);
327562306a36Sopenharmony_cierr_dpipe_init:
327662306a36Sopenharmony_ci	unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),
327762306a36Sopenharmony_ci					  &mlxsw_sp->netdevice_nb);
327862306a36Sopenharmony_cierr_netdev_notifier:
327962306a36Sopenharmony_ci	if (mlxsw_sp->clock)
328062306a36Sopenharmony_ci		mlxsw_sp->ptp_ops->fini(mlxsw_sp->ptp_state);
328162306a36Sopenharmony_cierr_ptp_init:
328262306a36Sopenharmony_ci	if (mlxsw_sp->clock)
328362306a36Sopenharmony_ci		mlxsw_sp->ptp_ops->clock_fini(mlxsw_sp->clock);
328462306a36Sopenharmony_cierr_ptp_clock_init:
328562306a36Sopenharmony_ci	mlxsw_sp_router_fini(mlxsw_sp);
328662306a36Sopenharmony_cierr_router_init:
328762306a36Sopenharmony_ci	mlxsw_sp_acl_fini(mlxsw_sp);
328862306a36Sopenharmony_cierr_acl_init:
328962306a36Sopenharmony_ci	mlxsw_sp_port_range_fini(mlxsw_sp);
329062306a36Sopenharmony_cierr_port_range_init:
329162306a36Sopenharmony_ci	mlxsw_sp_nve_fini(mlxsw_sp);
329262306a36Sopenharmony_cierr_nve_init:
329362306a36Sopenharmony_ci	mlxsw_sp_ipv6_addr_ht_fini(mlxsw_sp);
329462306a36Sopenharmony_cierr_ipv6_addr_ht_init:
329562306a36Sopenharmony_ci	mlxsw_sp_afa_fini(mlxsw_sp);
329662306a36Sopenharmony_cierr_afa_init:
329762306a36Sopenharmony_ci	mlxsw_sp_counter_pool_fini(mlxsw_sp);
329862306a36Sopenharmony_cierr_counter_pool_init:
329962306a36Sopenharmony_ci	mlxsw_sp_switchdev_fini(mlxsw_sp);
330062306a36Sopenharmony_cierr_switchdev_init:
330162306a36Sopenharmony_ci	mlxsw_sp_span_fini(mlxsw_sp);
330262306a36Sopenharmony_cierr_span_init:
330362306a36Sopenharmony_ci	mlxsw_sp_lag_fini(mlxsw_sp);
330462306a36Sopenharmony_cierr_lag_init:
330562306a36Sopenharmony_ci	mlxsw_sp_buffers_fini(mlxsw_sp);
330662306a36Sopenharmony_cierr_buffers_init:
330762306a36Sopenharmony_ci	mlxsw_sp_devlink_traps_fini(mlxsw_sp);
330862306a36Sopenharmony_cierr_devlink_traps_init:
330962306a36Sopenharmony_ci	mlxsw_sp_traps_fini(mlxsw_sp);
331062306a36Sopenharmony_cierr_traps_init:
331162306a36Sopenharmony_ci	mlxsw_sp_policers_fini(mlxsw_sp);
331262306a36Sopenharmony_cierr_policers_init:
331362306a36Sopenharmony_ci	mlxsw_sp_fids_fini(mlxsw_sp);
331462306a36Sopenharmony_cierr_fids_init:
331562306a36Sopenharmony_ci	mlxsw_sp_pgt_fini(mlxsw_sp);
331662306a36Sopenharmony_cierr_pgt_init:
331762306a36Sopenharmony_ci	mlxsw_sp_kvdl_fini(mlxsw_sp);
331862306a36Sopenharmony_ci	mlxsw_sp_parsing_fini(mlxsw_sp);
331962306a36Sopenharmony_ci	return err;
332062306a36Sopenharmony_ci}
332162306a36Sopenharmony_ci
332262306a36Sopenharmony_cistatic int mlxsw_sp1_init(struct mlxsw_core *mlxsw_core,
332362306a36Sopenharmony_ci			  const struct mlxsw_bus_info *mlxsw_bus_info,
332462306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
332562306a36Sopenharmony_ci{
332662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_ci	mlxsw_sp->switchdev_ops = &mlxsw_sp1_switchdev_ops;
332962306a36Sopenharmony_ci	mlxsw_sp->kvdl_ops = &mlxsw_sp1_kvdl_ops;
333062306a36Sopenharmony_ci	mlxsw_sp->afa_ops = &mlxsw_sp1_act_afa_ops;
333162306a36Sopenharmony_ci	mlxsw_sp->afk_ops = &mlxsw_sp1_afk_ops;
333262306a36Sopenharmony_ci	mlxsw_sp->mr_tcam_ops = &mlxsw_sp1_mr_tcam_ops;
333362306a36Sopenharmony_ci	mlxsw_sp->acl_rulei_ops = &mlxsw_sp1_acl_rulei_ops;
333462306a36Sopenharmony_ci	mlxsw_sp->acl_tcam_ops = &mlxsw_sp1_acl_tcam_ops;
333562306a36Sopenharmony_ci	mlxsw_sp->nve_ops_arr = mlxsw_sp1_nve_ops_arr;
333662306a36Sopenharmony_ci	mlxsw_sp->mac_mask = mlxsw_sp1_mac_mask;
333762306a36Sopenharmony_ci	mlxsw_sp->sb_vals = &mlxsw_sp1_sb_vals;
333862306a36Sopenharmony_ci	mlxsw_sp->sb_ops = &mlxsw_sp1_sb_ops;
333962306a36Sopenharmony_ci	mlxsw_sp->port_type_speed_ops = &mlxsw_sp1_port_type_speed_ops;
334062306a36Sopenharmony_ci	mlxsw_sp->ptp_ops = &mlxsw_sp1_ptp_ops;
334162306a36Sopenharmony_ci	mlxsw_sp->span_ops = &mlxsw_sp1_span_ops;
334262306a36Sopenharmony_ci	mlxsw_sp->policer_core_ops = &mlxsw_sp1_policer_core_ops;
334362306a36Sopenharmony_ci	mlxsw_sp->trap_ops = &mlxsw_sp1_trap_ops;
334462306a36Sopenharmony_ci	mlxsw_sp->mall_ops = &mlxsw_sp1_mall_ops;
334562306a36Sopenharmony_ci	mlxsw_sp->router_ops = &mlxsw_sp1_router_ops;
334662306a36Sopenharmony_ci	mlxsw_sp->listeners = mlxsw_sp1_listener;
334762306a36Sopenharmony_ci	mlxsw_sp->listeners_count = ARRAY_SIZE(mlxsw_sp1_listener);
334862306a36Sopenharmony_ci	mlxsw_sp->fid_family_arr = mlxsw_sp1_fid_family_arr;
334962306a36Sopenharmony_ci	mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP1;
335062306a36Sopenharmony_ci	mlxsw_sp->pgt_smpe_index_valid = true;
335162306a36Sopenharmony_ci
335262306a36Sopenharmony_ci	return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack);
335362306a36Sopenharmony_ci}
335462306a36Sopenharmony_ci
335562306a36Sopenharmony_cistatic int mlxsw_sp2_init(struct mlxsw_core *mlxsw_core,
335662306a36Sopenharmony_ci			  const struct mlxsw_bus_info *mlxsw_bus_info,
335762306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
335862306a36Sopenharmony_ci{
335962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
336062306a36Sopenharmony_ci
336162306a36Sopenharmony_ci	mlxsw_sp->switchdev_ops = &mlxsw_sp2_switchdev_ops;
336262306a36Sopenharmony_ci	mlxsw_sp->kvdl_ops = &mlxsw_sp2_kvdl_ops;
336362306a36Sopenharmony_ci	mlxsw_sp->afa_ops = &mlxsw_sp2_act_afa_ops;
336462306a36Sopenharmony_ci	mlxsw_sp->afk_ops = &mlxsw_sp2_afk_ops;
336562306a36Sopenharmony_ci	mlxsw_sp->mr_tcam_ops = &mlxsw_sp2_mr_tcam_ops;
336662306a36Sopenharmony_ci	mlxsw_sp->acl_rulei_ops = &mlxsw_sp2_acl_rulei_ops;
336762306a36Sopenharmony_ci	mlxsw_sp->acl_tcam_ops = &mlxsw_sp2_acl_tcam_ops;
336862306a36Sopenharmony_ci	mlxsw_sp->acl_bf_ops = &mlxsw_sp2_acl_bf_ops;
336962306a36Sopenharmony_ci	mlxsw_sp->nve_ops_arr = mlxsw_sp2_nve_ops_arr;
337062306a36Sopenharmony_ci	mlxsw_sp->mac_mask = mlxsw_sp2_mac_mask;
337162306a36Sopenharmony_ci	mlxsw_sp->sb_vals = &mlxsw_sp2_sb_vals;
337262306a36Sopenharmony_ci	mlxsw_sp->sb_ops = &mlxsw_sp2_sb_ops;
337362306a36Sopenharmony_ci	mlxsw_sp->port_type_speed_ops = &mlxsw_sp2_port_type_speed_ops;
337462306a36Sopenharmony_ci	mlxsw_sp->ptp_ops = &mlxsw_sp2_ptp_ops;
337562306a36Sopenharmony_ci	mlxsw_sp->span_ops = &mlxsw_sp2_span_ops;
337662306a36Sopenharmony_ci	mlxsw_sp->policer_core_ops = &mlxsw_sp2_policer_core_ops;
337762306a36Sopenharmony_ci	mlxsw_sp->trap_ops = &mlxsw_sp2_trap_ops;
337862306a36Sopenharmony_ci	mlxsw_sp->mall_ops = &mlxsw_sp2_mall_ops;
337962306a36Sopenharmony_ci	mlxsw_sp->router_ops = &mlxsw_sp2_router_ops;
338062306a36Sopenharmony_ci	mlxsw_sp->listeners = mlxsw_sp2_listener;
338162306a36Sopenharmony_ci	mlxsw_sp->listeners_count = ARRAY_SIZE(mlxsw_sp2_listener);
338262306a36Sopenharmony_ci	mlxsw_sp->fid_family_arr = mlxsw_sp2_fid_family_arr;
338362306a36Sopenharmony_ci	mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP2;
338462306a36Sopenharmony_ci	mlxsw_sp->pgt_smpe_index_valid = false;
338562306a36Sopenharmony_ci
338662306a36Sopenharmony_ci	return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack);
338762306a36Sopenharmony_ci}
338862306a36Sopenharmony_ci
338962306a36Sopenharmony_cistatic int mlxsw_sp3_init(struct mlxsw_core *mlxsw_core,
339062306a36Sopenharmony_ci			  const struct mlxsw_bus_info *mlxsw_bus_info,
339162306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
339262306a36Sopenharmony_ci{
339362306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci	mlxsw_sp->switchdev_ops = &mlxsw_sp2_switchdev_ops;
339662306a36Sopenharmony_ci	mlxsw_sp->kvdl_ops = &mlxsw_sp2_kvdl_ops;
339762306a36Sopenharmony_ci	mlxsw_sp->afa_ops = &mlxsw_sp2_act_afa_ops;
339862306a36Sopenharmony_ci	mlxsw_sp->afk_ops = &mlxsw_sp2_afk_ops;
339962306a36Sopenharmony_ci	mlxsw_sp->mr_tcam_ops = &mlxsw_sp2_mr_tcam_ops;
340062306a36Sopenharmony_ci	mlxsw_sp->acl_rulei_ops = &mlxsw_sp2_acl_rulei_ops;
340162306a36Sopenharmony_ci	mlxsw_sp->acl_tcam_ops = &mlxsw_sp2_acl_tcam_ops;
340262306a36Sopenharmony_ci	mlxsw_sp->acl_bf_ops = &mlxsw_sp2_acl_bf_ops;
340362306a36Sopenharmony_ci	mlxsw_sp->nve_ops_arr = mlxsw_sp2_nve_ops_arr;
340462306a36Sopenharmony_ci	mlxsw_sp->mac_mask = mlxsw_sp2_mac_mask;
340562306a36Sopenharmony_ci	mlxsw_sp->sb_vals = &mlxsw_sp2_sb_vals;
340662306a36Sopenharmony_ci	mlxsw_sp->sb_ops = &mlxsw_sp3_sb_ops;
340762306a36Sopenharmony_ci	mlxsw_sp->port_type_speed_ops = &mlxsw_sp2_port_type_speed_ops;
340862306a36Sopenharmony_ci	mlxsw_sp->ptp_ops = &mlxsw_sp2_ptp_ops;
340962306a36Sopenharmony_ci	mlxsw_sp->span_ops = &mlxsw_sp3_span_ops;
341062306a36Sopenharmony_ci	mlxsw_sp->policer_core_ops = &mlxsw_sp2_policer_core_ops;
341162306a36Sopenharmony_ci	mlxsw_sp->trap_ops = &mlxsw_sp2_trap_ops;
341262306a36Sopenharmony_ci	mlxsw_sp->mall_ops = &mlxsw_sp2_mall_ops;
341362306a36Sopenharmony_ci	mlxsw_sp->router_ops = &mlxsw_sp2_router_ops;
341462306a36Sopenharmony_ci	mlxsw_sp->listeners = mlxsw_sp2_listener;
341562306a36Sopenharmony_ci	mlxsw_sp->listeners_count = ARRAY_SIZE(mlxsw_sp2_listener);
341662306a36Sopenharmony_ci	mlxsw_sp->fid_family_arr = mlxsw_sp2_fid_family_arr;
341762306a36Sopenharmony_ci	mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP3;
341862306a36Sopenharmony_ci	mlxsw_sp->pgt_smpe_index_valid = false;
341962306a36Sopenharmony_ci
342062306a36Sopenharmony_ci	return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack);
342162306a36Sopenharmony_ci}
342262306a36Sopenharmony_ci
342362306a36Sopenharmony_cistatic int mlxsw_sp4_init(struct mlxsw_core *mlxsw_core,
342462306a36Sopenharmony_ci			  const struct mlxsw_bus_info *mlxsw_bus_info,
342562306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
342662306a36Sopenharmony_ci{
342762306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
342862306a36Sopenharmony_ci
342962306a36Sopenharmony_ci	mlxsw_sp->switchdev_ops = &mlxsw_sp2_switchdev_ops;
343062306a36Sopenharmony_ci	mlxsw_sp->kvdl_ops = &mlxsw_sp2_kvdl_ops;
343162306a36Sopenharmony_ci	mlxsw_sp->afa_ops = &mlxsw_sp2_act_afa_ops;
343262306a36Sopenharmony_ci	mlxsw_sp->afk_ops = &mlxsw_sp4_afk_ops;
343362306a36Sopenharmony_ci	mlxsw_sp->mr_tcam_ops = &mlxsw_sp2_mr_tcam_ops;
343462306a36Sopenharmony_ci	mlxsw_sp->acl_rulei_ops = &mlxsw_sp2_acl_rulei_ops;
343562306a36Sopenharmony_ci	mlxsw_sp->acl_tcam_ops = &mlxsw_sp2_acl_tcam_ops;
343662306a36Sopenharmony_ci	mlxsw_sp->acl_bf_ops = &mlxsw_sp4_acl_bf_ops;
343762306a36Sopenharmony_ci	mlxsw_sp->nve_ops_arr = mlxsw_sp2_nve_ops_arr;
343862306a36Sopenharmony_ci	mlxsw_sp->mac_mask = mlxsw_sp2_mac_mask;
343962306a36Sopenharmony_ci	mlxsw_sp->sb_vals = &mlxsw_sp2_sb_vals;
344062306a36Sopenharmony_ci	mlxsw_sp->sb_ops = &mlxsw_sp3_sb_ops;
344162306a36Sopenharmony_ci	mlxsw_sp->port_type_speed_ops = &mlxsw_sp2_port_type_speed_ops;
344262306a36Sopenharmony_ci	mlxsw_sp->ptp_ops = &mlxsw_sp4_ptp_ops;
344362306a36Sopenharmony_ci	mlxsw_sp->span_ops = &mlxsw_sp3_span_ops;
344462306a36Sopenharmony_ci	mlxsw_sp->policer_core_ops = &mlxsw_sp2_policer_core_ops;
344562306a36Sopenharmony_ci	mlxsw_sp->trap_ops = &mlxsw_sp2_trap_ops;
344662306a36Sopenharmony_ci	mlxsw_sp->mall_ops = &mlxsw_sp2_mall_ops;
344762306a36Sopenharmony_ci	mlxsw_sp->router_ops = &mlxsw_sp2_router_ops;
344862306a36Sopenharmony_ci	mlxsw_sp->listeners = mlxsw_sp2_listener;
344962306a36Sopenharmony_ci	mlxsw_sp->listeners_count = ARRAY_SIZE(mlxsw_sp2_listener);
345062306a36Sopenharmony_ci	mlxsw_sp->fid_family_arr = mlxsw_sp2_fid_family_arr;
345162306a36Sopenharmony_ci	mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP4;
345262306a36Sopenharmony_ci	mlxsw_sp->pgt_smpe_index_valid = false;
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci	return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack);
345562306a36Sopenharmony_ci}
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_cistatic void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
345862306a36Sopenharmony_ci{
345962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
346062306a36Sopenharmony_ci
346162306a36Sopenharmony_ci	mlxsw_sp_ports_remove(mlxsw_sp);
346262306a36Sopenharmony_ci	rhashtable_destroy(&mlxsw_sp->sample_trigger_ht);
346362306a36Sopenharmony_ci	mlxsw_sp_port_module_info_fini(mlxsw_sp);
346462306a36Sopenharmony_ci	mlxsw_sp_dpipe_fini(mlxsw_sp);
346562306a36Sopenharmony_ci	unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),
346662306a36Sopenharmony_ci					  &mlxsw_sp->netdevice_nb);
346762306a36Sopenharmony_ci	if (mlxsw_sp->clock) {
346862306a36Sopenharmony_ci		mlxsw_sp->ptp_ops->fini(mlxsw_sp->ptp_state);
346962306a36Sopenharmony_ci		mlxsw_sp->ptp_ops->clock_fini(mlxsw_sp->clock);
347062306a36Sopenharmony_ci	}
347162306a36Sopenharmony_ci	mlxsw_sp_router_fini(mlxsw_sp);
347262306a36Sopenharmony_ci	mlxsw_sp_acl_fini(mlxsw_sp);
347362306a36Sopenharmony_ci	mlxsw_sp_port_range_fini(mlxsw_sp);
347462306a36Sopenharmony_ci	mlxsw_sp_nve_fini(mlxsw_sp);
347562306a36Sopenharmony_ci	mlxsw_sp_ipv6_addr_ht_fini(mlxsw_sp);
347662306a36Sopenharmony_ci	mlxsw_sp_afa_fini(mlxsw_sp);
347762306a36Sopenharmony_ci	mlxsw_sp_counter_pool_fini(mlxsw_sp);
347862306a36Sopenharmony_ci	mlxsw_sp_switchdev_fini(mlxsw_sp);
347962306a36Sopenharmony_ci	mlxsw_sp_span_fini(mlxsw_sp);
348062306a36Sopenharmony_ci	mlxsw_sp_lag_fini(mlxsw_sp);
348162306a36Sopenharmony_ci	mlxsw_sp_buffers_fini(mlxsw_sp);
348262306a36Sopenharmony_ci	mlxsw_sp_devlink_traps_fini(mlxsw_sp);
348362306a36Sopenharmony_ci	mlxsw_sp_traps_fini(mlxsw_sp);
348462306a36Sopenharmony_ci	mlxsw_sp_policers_fini(mlxsw_sp);
348562306a36Sopenharmony_ci	mlxsw_sp_fids_fini(mlxsw_sp);
348662306a36Sopenharmony_ci	mlxsw_sp_pgt_fini(mlxsw_sp);
348762306a36Sopenharmony_ci	mlxsw_sp_kvdl_fini(mlxsw_sp);
348862306a36Sopenharmony_ci	mlxsw_sp_parsing_fini(mlxsw_sp);
348962306a36Sopenharmony_ci}
349062306a36Sopenharmony_ci
349162306a36Sopenharmony_cistatic const struct mlxsw_config_profile mlxsw_sp1_config_profile = {
349262306a36Sopenharmony_ci	.used_flood_mode                = 1,
349362306a36Sopenharmony_ci	.flood_mode                     = MLXSW_CMD_MBOX_CONFIG_PROFILE_FLOOD_MODE_CONTROLLED,
349462306a36Sopenharmony_ci	.used_max_ib_mc			= 1,
349562306a36Sopenharmony_ci	.max_ib_mc			= 0,
349662306a36Sopenharmony_ci	.used_max_pkey			= 1,
349762306a36Sopenharmony_ci	.max_pkey			= 0,
349862306a36Sopenharmony_ci	.used_ubridge			= 1,
349962306a36Sopenharmony_ci	.ubridge			= 1,
350062306a36Sopenharmony_ci	.used_kvd_sizes			= 1,
350162306a36Sopenharmony_ci	.kvd_hash_single_parts		= 59,
350262306a36Sopenharmony_ci	.kvd_hash_double_parts		= 41,
350362306a36Sopenharmony_ci	.kvd_linear_size		= MLXSW_SP_KVD_LINEAR_SIZE,
350462306a36Sopenharmony_ci	.swid_config			= {
350562306a36Sopenharmony_ci		{
350662306a36Sopenharmony_ci			.used_type	= 1,
350762306a36Sopenharmony_ci			.type		= MLXSW_PORT_SWID_TYPE_ETH,
350862306a36Sopenharmony_ci		}
350962306a36Sopenharmony_ci	},
351062306a36Sopenharmony_ci};
351162306a36Sopenharmony_ci
351262306a36Sopenharmony_cistatic const struct mlxsw_config_profile mlxsw_sp2_config_profile = {
351362306a36Sopenharmony_ci	.used_flood_mode                = 1,
351462306a36Sopenharmony_ci	.flood_mode                     = MLXSW_CMD_MBOX_CONFIG_PROFILE_FLOOD_MODE_CONTROLLED,
351562306a36Sopenharmony_ci	.used_max_ib_mc			= 1,
351662306a36Sopenharmony_ci	.max_ib_mc			= 0,
351762306a36Sopenharmony_ci	.used_max_pkey			= 1,
351862306a36Sopenharmony_ci	.max_pkey			= 0,
351962306a36Sopenharmony_ci	.used_ubridge			= 1,
352062306a36Sopenharmony_ci	.ubridge			= 1,
352162306a36Sopenharmony_ci	.swid_config			= {
352262306a36Sopenharmony_ci		{
352362306a36Sopenharmony_ci			.used_type	= 1,
352462306a36Sopenharmony_ci			.type		= MLXSW_PORT_SWID_TYPE_ETH,
352562306a36Sopenharmony_ci		}
352662306a36Sopenharmony_ci	},
352762306a36Sopenharmony_ci	.used_cqe_time_stamp_type	= 1,
352862306a36Sopenharmony_ci	.cqe_time_stamp_type		= MLXSW_CMD_MBOX_CONFIG_PROFILE_CQE_TIME_STAMP_TYPE_UTC,
352962306a36Sopenharmony_ci};
353062306a36Sopenharmony_ci
353162306a36Sopenharmony_ci/* Reduce number of LAGs from full capacity (256) to the maximum supported LAGs
353262306a36Sopenharmony_ci * in Spectrum-2/3, to avoid regression in number of free entries in the PGT
353362306a36Sopenharmony_ci * table.
353462306a36Sopenharmony_ci */
353562306a36Sopenharmony_ci#define MLXSW_SP4_CONFIG_PROFILE_MAX_LAG 128
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_cistatic const struct mlxsw_config_profile mlxsw_sp4_config_profile = {
353862306a36Sopenharmony_ci	.used_max_lag			= 1,
353962306a36Sopenharmony_ci	.max_lag			= MLXSW_SP4_CONFIG_PROFILE_MAX_LAG,
354062306a36Sopenharmony_ci	.used_flood_mode                = 1,
354162306a36Sopenharmony_ci	.flood_mode                     = MLXSW_CMD_MBOX_CONFIG_PROFILE_FLOOD_MODE_CONTROLLED,
354262306a36Sopenharmony_ci	.used_max_ib_mc			= 1,
354362306a36Sopenharmony_ci	.max_ib_mc			= 0,
354462306a36Sopenharmony_ci	.used_max_pkey			= 1,
354562306a36Sopenharmony_ci	.max_pkey			= 0,
354662306a36Sopenharmony_ci	.used_ubridge			= 1,
354762306a36Sopenharmony_ci	.ubridge			= 1,
354862306a36Sopenharmony_ci	.swid_config			= {
354962306a36Sopenharmony_ci		{
355062306a36Sopenharmony_ci			.used_type	= 1,
355162306a36Sopenharmony_ci			.type		= MLXSW_PORT_SWID_TYPE_ETH,
355262306a36Sopenharmony_ci		}
355362306a36Sopenharmony_ci	},
355462306a36Sopenharmony_ci	.used_cqe_time_stamp_type	= 1,
355562306a36Sopenharmony_ci	.cqe_time_stamp_type		= MLXSW_CMD_MBOX_CONFIG_PROFILE_CQE_TIME_STAMP_TYPE_UTC,
355662306a36Sopenharmony_ci};
355762306a36Sopenharmony_ci
355862306a36Sopenharmony_cistatic void
355962306a36Sopenharmony_cimlxsw_sp_resource_size_params_prepare(struct mlxsw_core *mlxsw_core,
356062306a36Sopenharmony_ci				      struct devlink_resource_size_params *kvd_size_params,
356162306a36Sopenharmony_ci				      struct devlink_resource_size_params *linear_size_params,
356262306a36Sopenharmony_ci				      struct devlink_resource_size_params *hash_double_size_params,
356362306a36Sopenharmony_ci				      struct devlink_resource_size_params *hash_single_size_params)
356462306a36Sopenharmony_ci{
356562306a36Sopenharmony_ci	u32 single_size_min = MLXSW_CORE_RES_GET(mlxsw_core,
356662306a36Sopenharmony_ci						 KVD_SINGLE_MIN_SIZE);
356762306a36Sopenharmony_ci	u32 double_size_min = MLXSW_CORE_RES_GET(mlxsw_core,
356862306a36Sopenharmony_ci						 KVD_DOUBLE_MIN_SIZE);
356962306a36Sopenharmony_ci	u32 kvd_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE);
357062306a36Sopenharmony_ci	u32 linear_size_min = 0;
357162306a36Sopenharmony_ci
357262306a36Sopenharmony_ci	devlink_resource_size_params_init(kvd_size_params, kvd_size, kvd_size,
357362306a36Sopenharmony_ci					  MLXSW_SP_KVD_GRANULARITY,
357462306a36Sopenharmony_ci					  DEVLINK_RESOURCE_UNIT_ENTRY);
357562306a36Sopenharmony_ci	devlink_resource_size_params_init(linear_size_params, linear_size_min,
357662306a36Sopenharmony_ci					  kvd_size - single_size_min -
357762306a36Sopenharmony_ci					  double_size_min,
357862306a36Sopenharmony_ci					  MLXSW_SP_KVD_GRANULARITY,
357962306a36Sopenharmony_ci					  DEVLINK_RESOURCE_UNIT_ENTRY);
358062306a36Sopenharmony_ci	devlink_resource_size_params_init(hash_double_size_params,
358162306a36Sopenharmony_ci					  double_size_min,
358262306a36Sopenharmony_ci					  kvd_size - single_size_min -
358362306a36Sopenharmony_ci					  linear_size_min,
358462306a36Sopenharmony_ci					  MLXSW_SP_KVD_GRANULARITY,
358562306a36Sopenharmony_ci					  DEVLINK_RESOURCE_UNIT_ENTRY);
358662306a36Sopenharmony_ci	devlink_resource_size_params_init(hash_single_size_params,
358762306a36Sopenharmony_ci					  single_size_min,
358862306a36Sopenharmony_ci					  kvd_size - double_size_min -
358962306a36Sopenharmony_ci					  linear_size_min,
359062306a36Sopenharmony_ci					  MLXSW_SP_KVD_GRANULARITY,
359162306a36Sopenharmony_ci					  DEVLINK_RESOURCE_UNIT_ENTRY);
359262306a36Sopenharmony_ci}
359362306a36Sopenharmony_ci
359462306a36Sopenharmony_cistatic int mlxsw_sp1_resources_kvd_register(struct mlxsw_core *mlxsw_core)
359562306a36Sopenharmony_ci{
359662306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(mlxsw_core);
359762306a36Sopenharmony_ci	struct devlink_resource_size_params hash_single_size_params;
359862306a36Sopenharmony_ci	struct devlink_resource_size_params hash_double_size_params;
359962306a36Sopenharmony_ci	struct devlink_resource_size_params linear_size_params;
360062306a36Sopenharmony_ci	struct devlink_resource_size_params kvd_size_params;
360162306a36Sopenharmony_ci	u32 kvd_size, single_size, double_size, linear_size;
360262306a36Sopenharmony_ci	const struct mlxsw_config_profile *profile;
360362306a36Sopenharmony_ci	int err;
360462306a36Sopenharmony_ci
360562306a36Sopenharmony_ci	profile = &mlxsw_sp1_config_profile;
360662306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_core, KVD_SIZE))
360762306a36Sopenharmony_ci		return -EIO;
360862306a36Sopenharmony_ci
360962306a36Sopenharmony_ci	mlxsw_sp_resource_size_params_prepare(mlxsw_core, &kvd_size_params,
361062306a36Sopenharmony_ci					      &linear_size_params,
361162306a36Sopenharmony_ci					      &hash_double_size_params,
361262306a36Sopenharmony_ci					      &hash_single_size_params);
361362306a36Sopenharmony_ci
361462306a36Sopenharmony_ci	kvd_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE);
361562306a36Sopenharmony_ci	err = devl_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD,
361662306a36Sopenharmony_ci				     kvd_size, MLXSW_SP_RESOURCE_KVD,
361762306a36Sopenharmony_ci				     DEVLINK_RESOURCE_ID_PARENT_TOP,
361862306a36Sopenharmony_ci				     &kvd_size_params);
361962306a36Sopenharmony_ci	if (err)
362062306a36Sopenharmony_ci		return err;
362162306a36Sopenharmony_ci
362262306a36Sopenharmony_ci	linear_size = profile->kvd_linear_size;
362362306a36Sopenharmony_ci	err = devl_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR,
362462306a36Sopenharmony_ci				     linear_size,
362562306a36Sopenharmony_ci				     MLXSW_SP_RESOURCE_KVD_LINEAR,
362662306a36Sopenharmony_ci				     MLXSW_SP_RESOURCE_KVD,
362762306a36Sopenharmony_ci				     &linear_size_params);
362862306a36Sopenharmony_ci	if (err)
362962306a36Sopenharmony_ci		return err;
363062306a36Sopenharmony_ci
363162306a36Sopenharmony_ci	err = mlxsw_sp1_kvdl_resources_register(mlxsw_core);
363262306a36Sopenharmony_ci	if  (err)
363362306a36Sopenharmony_ci		return err;
363462306a36Sopenharmony_ci
363562306a36Sopenharmony_ci	double_size = kvd_size - linear_size;
363662306a36Sopenharmony_ci	double_size *= profile->kvd_hash_double_parts;
363762306a36Sopenharmony_ci	double_size /= profile->kvd_hash_double_parts +
363862306a36Sopenharmony_ci		       profile->kvd_hash_single_parts;
363962306a36Sopenharmony_ci	double_size = rounddown(double_size, MLXSW_SP_KVD_GRANULARITY);
364062306a36Sopenharmony_ci	err = devl_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_HASH_DOUBLE,
364162306a36Sopenharmony_ci				     double_size,
364262306a36Sopenharmony_ci				     MLXSW_SP_RESOURCE_KVD_HASH_DOUBLE,
364362306a36Sopenharmony_ci				     MLXSW_SP_RESOURCE_KVD,
364462306a36Sopenharmony_ci				     &hash_double_size_params);
364562306a36Sopenharmony_ci	if (err)
364662306a36Sopenharmony_ci		return err;
364762306a36Sopenharmony_ci
364862306a36Sopenharmony_ci	single_size = kvd_size - double_size - linear_size;
364962306a36Sopenharmony_ci	err = devl_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_HASH_SINGLE,
365062306a36Sopenharmony_ci				     single_size,
365162306a36Sopenharmony_ci				     MLXSW_SP_RESOURCE_KVD_HASH_SINGLE,
365262306a36Sopenharmony_ci				     MLXSW_SP_RESOURCE_KVD,
365362306a36Sopenharmony_ci				     &hash_single_size_params);
365462306a36Sopenharmony_ci	if (err)
365562306a36Sopenharmony_ci		return err;
365662306a36Sopenharmony_ci
365762306a36Sopenharmony_ci	return 0;
365862306a36Sopenharmony_ci}
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_cistatic int mlxsw_sp2_resources_kvd_register(struct mlxsw_core *mlxsw_core)
366162306a36Sopenharmony_ci{
366262306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(mlxsw_core);
366362306a36Sopenharmony_ci	struct devlink_resource_size_params kvd_size_params;
366462306a36Sopenharmony_ci	u32 kvd_size;
366562306a36Sopenharmony_ci
366662306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_core, KVD_SIZE))
366762306a36Sopenharmony_ci		return -EIO;
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_ci	kvd_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE);
367062306a36Sopenharmony_ci	devlink_resource_size_params_init(&kvd_size_params, kvd_size, kvd_size,
367162306a36Sopenharmony_ci					  MLXSW_SP_KVD_GRANULARITY,
367262306a36Sopenharmony_ci					  DEVLINK_RESOURCE_UNIT_ENTRY);
367362306a36Sopenharmony_ci
367462306a36Sopenharmony_ci	return devl_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD,
367562306a36Sopenharmony_ci				      kvd_size, MLXSW_SP_RESOURCE_KVD,
367662306a36Sopenharmony_ci				      DEVLINK_RESOURCE_ID_PARENT_TOP,
367762306a36Sopenharmony_ci				      &kvd_size_params);
367862306a36Sopenharmony_ci}
367962306a36Sopenharmony_ci
368062306a36Sopenharmony_cistatic int mlxsw_sp_resources_span_register(struct mlxsw_core *mlxsw_core)
368162306a36Sopenharmony_ci{
368262306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(mlxsw_core);
368362306a36Sopenharmony_ci	struct devlink_resource_size_params span_size_params;
368462306a36Sopenharmony_ci	u32 max_span;
368562306a36Sopenharmony_ci
368662306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_SPAN))
368762306a36Sopenharmony_ci		return -EIO;
368862306a36Sopenharmony_ci
368962306a36Sopenharmony_ci	max_span = MLXSW_CORE_RES_GET(mlxsw_core, MAX_SPAN);
369062306a36Sopenharmony_ci	devlink_resource_size_params_init(&span_size_params, max_span, max_span,
369162306a36Sopenharmony_ci					  1, DEVLINK_RESOURCE_UNIT_ENTRY);
369262306a36Sopenharmony_ci
369362306a36Sopenharmony_ci	return devl_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_SPAN,
369462306a36Sopenharmony_ci				      max_span, MLXSW_SP_RESOURCE_SPAN,
369562306a36Sopenharmony_ci				      DEVLINK_RESOURCE_ID_PARENT_TOP,
369662306a36Sopenharmony_ci				      &span_size_params);
369762306a36Sopenharmony_ci}
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_cistatic int
370062306a36Sopenharmony_cimlxsw_sp_resources_rif_mac_profile_register(struct mlxsw_core *mlxsw_core)
370162306a36Sopenharmony_ci{
370262306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(mlxsw_core);
370362306a36Sopenharmony_ci	struct devlink_resource_size_params size_params;
370462306a36Sopenharmony_ci	u8 max_rif_mac_profiles;
370562306a36Sopenharmony_ci
370662306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_RIF_MAC_PROFILES))
370762306a36Sopenharmony_ci		max_rif_mac_profiles = 1;
370862306a36Sopenharmony_ci	else
370962306a36Sopenharmony_ci		max_rif_mac_profiles = MLXSW_CORE_RES_GET(mlxsw_core,
371062306a36Sopenharmony_ci							  MAX_RIF_MAC_PROFILES);
371162306a36Sopenharmony_ci	devlink_resource_size_params_init(&size_params, max_rif_mac_profiles,
371262306a36Sopenharmony_ci					  max_rif_mac_profiles, 1,
371362306a36Sopenharmony_ci					  DEVLINK_RESOURCE_UNIT_ENTRY);
371462306a36Sopenharmony_ci
371562306a36Sopenharmony_ci	return devl_resource_register(devlink,
371662306a36Sopenharmony_ci				      "rif_mac_profiles",
371762306a36Sopenharmony_ci				      max_rif_mac_profiles,
371862306a36Sopenharmony_ci				      MLXSW_SP_RESOURCE_RIF_MAC_PROFILES,
371962306a36Sopenharmony_ci				      DEVLINK_RESOURCE_ID_PARENT_TOP,
372062306a36Sopenharmony_ci				      &size_params);
372162306a36Sopenharmony_ci}
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_cistatic int mlxsw_sp_resources_rifs_register(struct mlxsw_core *mlxsw_core)
372462306a36Sopenharmony_ci{
372562306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(mlxsw_core);
372662306a36Sopenharmony_ci	struct devlink_resource_size_params size_params;
372762306a36Sopenharmony_ci	u64 max_rifs;
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_RIFS))
373062306a36Sopenharmony_ci		return -EIO;
373162306a36Sopenharmony_ci
373262306a36Sopenharmony_ci	max_rifs = MLXSW_CORE_RES_GET(mlxsw_core, MAX_RIFS);
373362306a36Sopenharmony_ci	devlink_resource_size_params_init(&size_params, max_rifs, max_rifs,
373462306a36Sopenharmony_ci					  1, DEVLINK_RESOURCE_UNIT_ENTRY);
373562306a36Sopenharmony_ci
373662306a36Sopenharmony_ci	return devl_resource_register(devlink, "rifs", max_rifs,
373762306a36Sopenharmony_ci				      MLXSW_SP_RESOURCE_RIFS,
373862306a36Sopenharmony_ci				      DEVLINK_RESOURCE_ID_PARENT_TOP,
373962306a36Sopenharmony_ci				      &size_params);
374062306a36Sopenharmony_ci}
374162306a36Sopenharmony_ci
374262306a36Sopenharmony_cistatic int
374362306a36Sopenharmony_cimlxsw_sp_resources_port_range_register(struct mlxsw_core *mlxsw_core)
374462306a36Sopenharmony_ci{
374562306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(mlxsw_core);
374662306a36Sopenharmony_ci	struct devlink_resource_size_params size_params;
374762306a36Sopenharmony_ci	u64 max;
374862306a36Sopenharmony_ci
374962306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_core, ACL_MAX_L4_PORT_RANGE))
375062306a36Sopenharmony_ci		return -EIO;
375162306a36Sopenharmony_ci
375262306a36Sopenharmony_ci	max = MLXSW_CORE_RES_GET(mlxsw_core, ACL_MAX_L4_PORT_RANGE);
375362306a36Sopenharmony_ci	devlink_resource_size_params_init(&size_params, max, max, 1,
375462306a36Sopenharmony_ci					  DEVLINK_RESOURCE_UNIT_ENTRY);
375562306a36Sopenharmony_ci
375662306a36Sopenharmony_ci	return devl_resource_register(devlink, "port_range_registers", max,
375762306a36Sopenharmony_ci				      MLXSW_SP_RESOURCE_PORT_RANGE_REGISTERS,
375862306a36Sopenharmony_ci				      DEVLINK_RESOURCE_ID_PARENT_TOP,
375962306a36Sopenharmony_ci				      &size_params);
376062306a36Sopenharmony_ci}
376162306a36Sopenharmony_ci
376262306a36Sopenharmony_cistatic int mlxsw_sp1_resources_register(struct mlxsw_core *mlxsw_core)
376362306a36Sopenharmony_ci{
376462306a36Sopenharmony_ci	int err;
376562306a36Sopenharmony_ci
376662306a36Sopenharmony_ci	err = mlxsw_sp1_resources_kvd_register(mlxsw_core);
376762306a36Sopenharmony_ci	if (err)
376862306a36Sopenharmony_ci		return err;
376962306a36Sopenharmony_ci
377062306a36Sopenharmony_ci	err = mlxsw_sp_resources_span_register(mlxsw_core);
377162306a36Sopenharmony_ci	if (err)
377262306a36Sopenharmony_ci		goto err_resources_span_register;
377362306a36Sopenharmony_ci
377462306a36Sopenharmony_ci	err = mlxsw_sp_counter_resources_register(mlxsw_core);
377562306a36Sopenharmony_ci	if (err)
377662306a36Sopenharmony_ci		goto err_resources_counter_register;
377762306a36Sopenharmony_ci
377862306a36Sopenharmony_ci	err = mlxsw_sp_policer_resources_register(mlxsw_core);
377962306a36Sopenharmony_ci	if (err)
378062306a36Sopenharmony_ci		goto err_policer_resources_register;
378162306a36Sopenharmony_ci
378262306a36Sopenharmony_ci	err = mlxsw_sp_resources_rif_mac_profile_register(mlxsw_core);
378362306a36Sopenharmony_ci	if (err)
378462306a36Sopenharmony_ci		goto err_resources_rif_mac_profile_register;
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_ci	err = mlxsw_sp_resources_rifs_register(mlxsw_core);
378762306a36Sopenharmony_ci	if (err)
378862306a36Sopenharmony_ci		goto err_resources_rifs_register;
378962306a36Sopenharmony_ci
379062306a36Sopenharmony_ci	err = mlxsw_sp_resources_port_range_register(mlxsw_core);
379162306a36Sopenharmony_ci	if (err)
379262306a36Sopenharmony_ci		goto err_resources_port_range_register;
379362306a36Sopenharmony_ci
379462306a36Sopenharmony_ci	return 0;
379562306a36Sopenharmony_ci
379662306a36Sopenharmony_cierr_resources_port_range_register:
379762306a36Sopenharmony_cierr_resources_rifs_register:
379862306a36Sopenharmony_cierr_resources_rif_mac_profile_register:
379962306a36Sopenharmony_cierr_policer_resources_register:
380062306a36Sopenharmony_cierr_resources_counter_register:
380162306a36Sopenharmony_cierr_resources_span_register:
380262306a36Sopenharmony_ci	devl_resources_unregister(priv_to_devlink(mlxsw_core));
380362306a36Sopenharmony_ci	return err;
380462306a36Sopenharmony_ci}
380562306a36Sopenharmony_ci
380662306a36Sopenharmony_cistatic int mlxsw_sp2_resources_register(struct mlxsw_core *mlxsw_core)
380762306a36Sopenharmony_ci{
380862306a36Sopenharmony_ci	int err;
380962306a36Sopenharmony_ci
381062306a36Sopenharmony_ci	err = mlxsw_sp2_resources_kvd_register(mlxsw_core);
381162306a36Sopenharmony_ci	if (err)
381262306a36Sopenharmony_ci		return err;
381362306a36Sopenharmony_ci
381462306a36Sopenharmony_ci	err = mlxsw_sp_resources_span_register(mlxsw_core);
381562306a36Sopenharmony_ci	if (err)
381662306a36Sopenharmony_ci		goto err_resources_span_register;
381762306a36Sopenharmony_ci
381862306a36Sopenharmony_ci	err = mlxsw_sp_counter_resources_register(mlxsw_core);
381962306a36Sopenharmony_ci	if (err)
382062306a36Sopenharmony_ci		goto err_resources_counter_register;
382162306a36Sopenharmony_ci
382262306a36Sopenharmony_ci	err = mlxsw_sp_policer_resources_register(mlxsw_core);
382362306a36Sopenharmony_ci	if (err)
382462306a36Sopenharmony_ci		goto err_policer_resources_register;
382562306a36Sopenharmony_ci
382662306a36Sopenharmony_ci	err = mlxsw_sp_resources_rif_mac_profile_register(mlxsw_core);
382762306a36Sopenharmony_ci	if (err)
382862306a36Sopenharmony_ci		goto err_resources_rif_mac_profile_register;
382962306a36Sopenharmony_ci
383062306a36Sopenharmony_ci	err = mlxsw_sp_resources_rifs_register(mlxsw_core);
383162306a36Sopenharmony_ci	if (err)
383262306a36Sopenharmony_ci		goto err_resources_rifs_register;
383362306a36Sopenharmony_ci
383462306a36Sopenharmony_ci	err = mlxsw_sp_resources_port_range_register(mlxsw_core);
383562306a36Sopenharmony_ci	if (err)
383662306a36Sopenharmony_ci		goto err_resources_port_range_register;
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_ci	return 0;
383962306a36Sopenharmony_ci
384062306a36Sopenharmony_cierr_resources_port_range_register:
384162306a36Sopenharmony_cierr_resources_rifs_register:
384262306a36Sopenharmony_cierr_resources_rif_mac_profile_register:
384362306a36Sopenharmony_cierr_policer_resources_register:
384462306a36Sopenharmony_cierr_resources_counter_register:
384562306a36Sopenharmony_cierr_resources_span_register:
384662306a36Sopenharmony_ci	devl_resources_unregister(priv_to_devlink(mlxsw_core));
384762306a36Sopenharmony_ci	return err;
384862306a36Sopenharmony_ci}
384962306a36Sopenharmony_ci
385062306a36Sopenharmony_cistatic int mlxsw_sp_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
385162306a36Sopenharmony_ci				  const struct mlxsw_config_profile *profile,
385262306a36Sopenharmony_ci				  u64 *p_single_size, u64 *p_double_size,
385362306a36Sopenharmony_ci				  u64 *p_linear_size)
385462306a36Sopenharmony_ci{
385562306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(mlxsw_core);
385662306a36Sopenharmony_ci	u32 double_size;
385762306a36Sopenharmony_ci	int err;
385862306a36Sopenharmony_ci
385962306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_core, KVD_SINGLE_MIN_SIZE) ||
386062306a36Sopenharmony_ci	    !MLXSW_CORE_RES_VALID(mlxsw_core, KVD_DOUBLE_MIN_SIZE))
386162306a36Sopenharmony_ci		return -EIO;
386262306a36Sopenharmony_ci
386362306a36Sopenharmony_ci	/* The hash part is what left of the kvd without the
386462306a36Sopenharmony_ci	 * linear part. It is split to the single size and
386562306a36Sopenharmony_ci	 * double size by the parts ratio from the profile.
386662306a36Sopenharmony_ci	 * Both sizes must be a multiplications of the
386762306a36Sopenharmony_ci	 * granularity from the profile. In case the user
386862306a36Sopenharmony_ci	 * provided the sizes they are obtained via devlink.
386962306a36Sopenharmony_ci	 */
387062306a36Sopenharmony_ci	err = devl_resource_size_get(devlink,
387162306a36Sopenharmony_ci				     MLXSW_SP_RESOURCE_KVD_LINEAR,
387262306a36Sopenharmony_ci				     p_linear_size);
387362306a36Sopenharmony_ci	if (err)
387462306a36Sopenharmony_ci		*p_linear_size = profile->kvd_linear_size;
387562306a36Sopenharmony_ci
387662306a36Sopenharmony_ci	err = devl_resource_size_get(devlink,
387762306a36Sopenharmony_ci				     MLXSW_SP_RESOURCE_KVD_HASH_DOUBLE,
387862306a36Sopenharmony_ci				     p_double_size);
387962306a36Sopenharmony_ci	if (err) {
388062306a36Sopenharmony_ci		double_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) -
388162306a36Sopenharmony_ci			      *p_linear_size;
388262306a36Sopenharmony_ci		double_size *= profile->kvd_hash_double_parts;
388362306a36Sopenharmony_ci		double_size /= profile->kvd_hash_double_parts +
388462306a36Sopenharmony_ci			       profile->kvd_hash_single_parts;
388562306a36Sopenharmony_ci		*p_double_size = rounddown(double_size,
388662306a36Sopenharmony_ci					   MLXSW_SP_KVD_GRANULARITY);
388762306a36Sopenharmony_ci	}
388862306a36Sopenharmony_ci
388962306a36Sopenharmony_ci	err = devl_resource_size_get(devlink,
389062306a36Sopenharmony_ci				     MLXSW_SP_RESOURCE_KVD_HASH_SINGLE,
389162306a36Sopenharmony_ci				     p_single_size);
389262306a36Sopenharmony_ci	if (err)
389362306a36Sopenharmony_ci		*p_single_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) -
389462306a36Sopenharmony_ci				 *p_double_size - *p_linear_size;
389562306a36Sopenharmony_ci
389662306a36Sopenharmony_ci	/* Check results are legal. */
389762306a36Sopenharmony_ci	if (*p_single_size < MLXSW_CORE_RES_GET(mlxsw_core, KVD_SINGLE_MIN_SIZE) ||
389862306a36Sopenharmony_ci	    *p_double_size < MLXSW_CORE_RES_GET(mlxsw_core, KVD_DOUBLE_MIN_SIZE) ||
389962306a36Sopenharmony_ci	    MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) < *p_linear_size)
390062306a36Sopenharmony_ci		return -EIO;
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci	return 0;
390362306a36Sopenharmony_ci}
390462306a36Sopenharmony_ci
390562306a36Sopenharmony_cistatic void mlxsw_sp_ptp_transmitted(struct mlxsw_core *mlxsw_core,
390662306a36Sopenharmony_ci				     struct sk_buff *skb, u16 local_port)
390762306a36Sopenharmony_ci{
390862306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
390962306a36Sopenharmony_ci
391062306a36Sopenharmony_ci	skb_pull(skb, MLXSW_TXHDR_LEN);
391162306a36Sopenharmony_ci	mlxsw_sp->ptp_ops->transmitted(mlxsw_sp, skb, local_port);
391262306a36Sopenharmony_ci}
391362306a36Sopenharmony_ci
391462306a36Sopenharmony_cistatic struct mlxsw_driver mlxsw_sp1_driver = {
391562306a36Sopenharmony_ci	.kind				= mlxsw_sp1_driver_name,
391662306a36Sopenharmony_ci	.priv_size			= sizeof(struct mlxsw_sp),
391762306a36Sopenharmony_ci	.fw_req_rev			= &mlxsw_sp1_fw_rev,
391862306a36Sopenharmony_ci	.fw_filename			= MLXSW_SP1_FW_FILENAME,
391962306a36Sopenharmony_ci	.init				= mlxsw_sp1_init,
392062306a36Sopenharmony_ci	.fini				= mlxsw_sp_fini,
392162306a36Sopenharmony_ci	.port_split			= mlxsw_sp_port_split,
392262306a36Sopenharmony_ci	.port_unsplit			= mlxsw_sp_port_unsplit,
392362306a36Sopenharmony_ci	.sb_pool_get			= mlxsw_sp_sb_pool_get,
392462306a36Sopenharmony_ci	.sb_pool_set			= mlxsw_sp_sb_pool_set,
392562306a36Sopenharmony_ci	.sb_port_pool_get		= mlxsw_sp_sb_port_pool_get,
392662306a36Sopenharmony_ci	.sb_port_pool_set		= mlxsw_sp_sb_port_pool_set,
392762306a36Sopenharmony_ci	.sb_tc_pool_bind_get		= mlxsw_sp_sb_tc_pool_bind_get,
392862306a36Sopenharmony_ci	.sb_tc_pool_bind_set		= mlxsw_sp_sb_tc_pool_bind_set,
392962306a36Sopenharmony_ci	.sb_occ_snapshot		= mlxsw_sp_sb_occ_snapshot,
393062306a36Sopenharmony_ci	.sb_occ_max_clear		= mlxsw_sp_sb_occ_max_clear,
393162306a36Sopenharmony_ci	.sb_occ_port_pool_get		= mlxsw_sp_sb_occ_port_pool_get,
393262306a36Sopenharmony_ci	.sb_occ_tc_port_bind_get	= mlxsw_sp_sb_occ_tc_port_bind_get,
393362306a36Sopenharmony_ci	.trap_init			= mlxsw_sp_trap_init,
393462306a36Sopenharmony_ci	.trap_fini			= mlxsw_sp_trap_fini,
393562306a36Sopenharmony_ci	.trap_action_set		= mlxsw_sp_trap_action_set,
393662306a36Sopenharmony_ci	.trap_group_init		= mlxsw_sp_trap_group_init,
393762306a36Sopenharmony_ci	.trap_group_set			= mlxsw_sp_trap_group_set,
393862306a36Sopenharmony_ci	.trap_policer_init		= mlxsw_sp_trap_policer_init,
393962306a36Sopenharmony_ci	.trap_policer_fini		= mlxsw_sp_trap_policer_fini,
394062306a36Sopenharmony_ci	.trap_policer_set		= mlxsw_sp_trap_policer_set,
394162306a36Sopenharmony_ci	.trap_policer_counter_get	= mlxsw_sp_trap_policer_counter_get,
394262306a36Sopenharmony_ci	.txhdr_construct		= mlxsw_sp_txhdr_construct,
394362306a36Sopenharmony_ci	.resources_register		= mlxsw_sp1_resources_register,
394462306a36Sopenharmony_ci	.kvd_sizes_get			= mlxsw_sp_kvd_sizes_get,
394562306a36Sopenharmony_ci	.ptp_transmitted		= mlxsw_sp_ptp_transmitted,
394662306a36Sopenharmony_ci	.txhdr_len			= MLXSW_TXHDR_LEN,
394762306a36Sopenharmony_ci	.profile			= &mlxsw_sp1_config_profile,
394862306a36Sopenharmony_ci	.sdq_supports_cqe_v2		= false,
394962306a36Sopenharmony_ci};
395062306a36Sopenharmony_ci
395162306a36Sopenharmony_cistatic struct mlxsw_driver mlxsw_sp2_driver = {
395262306a36Sopenharmony_ci	.kind				= mlxsw_sp2_driver_name,
395362306a36Sopenharmony_ci	.priv_size			= sizeof(struct mlxsw_sp),
395462306a36Sopenharmony_ci	.fw_req_rev			= &mlxsw_sp2_fw_rev,
395562306a36Sopenharmony_ci	.fw_filename			= MLXSW_SP2_FW_FILENAME,
395662306a36Sopenharmony_ci	.init				= mlxsw_sp2_init,
395762306a36Sopenharmony_ci	.fini				= mlxsw_sp_fini,
395862306a36Sopenharmony_ci	.port_split			= mlxsw_sp_port_split,
395962306a36Sopenharmony_ci	.port_unsplit			= mlxsw_sp_port_unsplit,
396062306a36Sopenharmony_ci	.ports_remove_selected		= mlxsw_sp_ports_remove_selected,
396162306a36Sopenharmony_ci	.sb_pool_get			= mlxsw_sp_sb_pool_get,
396262306a36Sopenharmony_ci	.sb_pool_set			= mlxsw_sp_sb_pool_set,
396362306a36Sopenharmony_ci	.sb_port_pool_get		= mlxsw_sp_sb_port_pool_get,
396462306a36Sopenharmony_ci	.sb_port_pool_set		= mlxsw_sp_sb_port_pool_set,
396562306a36Sopenharmony_ci	.sb_tc_pool_bind_get		= mlxsw_sp_sb_tc_pool_bind_get,
396662306a36Sopenharmony_ci	.sb_tc_pool_bind_set		= mlxsw_sp_sb_tc_pool_bind_set,
396762306a36Sopenharmony_ci	.sb_occ_snapshot		= mlxsw_sp_sb_occ_snapshot,
396862306a36Sopenharmony_ci	.sb_occ_max_clear		= mlxsw_sp_sb_occ_max_clear,
396962306a36Sopenharmony_ci	.sb_occ_port_pool_get		= mlxsw_sp_sb_occ_port_pool_get,
397062306a36Sopenharmony_ci	.sb_occ_tc_port_bind_get	= mlxsw_sp_sb_occ_tc_port_bind_get,
397162306a36Sopenharmony_ci	.trap_init			= mlxsw_sp_trap_init,
397262306a36Sopenharmony_ci	.trap_fini			= mlxsw_sp_trap_fini,
397362306a36Sopenharmony_ci	.trap_action_set		= mlxsw_sp_trap_action_set,
397462306a36Sopenharmony_ci	.trap_group_init		= mlxsw_sp_trap_group_init,
397562306a36Sopenharmony_ci	.trap_group_set			= mlxsw_sp_trap_group_set,
397662306a36Sopenharmony_ci	.trap_policer_init		= mlxsw_sp_trap_policer_init,
397762306a36Sopenharmony_ci	.trap_policer_fini		= mlxsw_sp_trap_policer_fini,
397862306a36Sopenharmony_ci	.trap_policer_set		= mlxsw_sp_trap_policer_set,
397962306a36Sopenharmony_ci	.trap_policer_counter_get	= mlxsw_sp_trap_policer_counter_get,
398062306a36Sopenharmony_ci	.txhdr_construct		= mlxsw_sp_txhdr_construct,
398162306a36Sopenharmony_ci	.resources_register		= mlxsw_sp2_resources_register,
398262306a36Sopenharmony_ci	.ptp_transmitted		= mlxsw_sp_ptp_transmitted,
398362306a36Sopenharmony_ci	.txhdr_len			= MLXSW_TXHDR_LEN,
398462306a36Sopenharmony_ci	.profile			= &mlxsw_sp2_config_profile,
398562306a36Sopenharmony_ci	.sdq_supports_cqe_v2		= true,
398662306a36Sopenharmony_ci};
398762306a36Sopenharmony_ci
398862306a36Sopenharmony_cistatic struct mlxsw_driver mlxsw_sp3_driver = {
398962306a36Sopenharmony_ci	.kind				= mlxsw_sp3_driver_name,
399062306a36Sopenharmony_ci	.priv_size			= sizeof(struct mlxsw_sp),
399162306a36Sopenharmony_ci	.fw_req_rev			= &mlxsw_sp3_fw_rev,
399262306a36Sopenharmony_ci	.fw_filename			= MLXSW_SP3_FW_FILENAME,
399362306a36Sopenharmony_ci	.init				= mlxsw_sp3_init,
399462306a36Sopenharmony_ci	.fini				= mlxsw_sp_fini,
399562306a36Sopenharmony_ci	.port_split			= mlxsw_sp_port_split,
399662306a36Sopenharmony_ci	.port_unsplit			= mlxsw_sp_port_unsplit,
399762306a36Sopenharmony_ci	.ports_remove_selected		= mlxsw_sp_ports_remove_selected,
399862306a36Sopenharmony_ci	.sb_pool_get			= mlxsw_sp_sb_pool_get,
399962306a36Sopenharmony_ci	.sb_pool_set			= mlxsw_sp_sb_pool_set,
400062306a36Sopenharmony_ci	.sb_port_pool_get		= mlxsw_sp_sb_port_pool_get,
400162306a36Sopenharmony_ci	.sb_port_pool_set		= mlxsw_sp_sb_port_pool_set,
400262306a36Sopenharmony_ci	.sb_tc_pool_bind_get		= mlxsw_sp_sb_tc_pool_bind_get,
400362306a36Sopenharmony_ci	.sb_tc_pool_bind_set		= mlxsw_sp_sb_tc_pool_bind_set,
400462306a36Sopenharmony_ci	.sb_occ_snapshot		= mlxsw_sp_sb_occ_snapshot,
400562306a36Sopenharmony_ci	.sb_occ_max_clear		= mlxsw_sp_sb_occ_max_clear,
400662306a36Sopenharmony_ci	.sb_occ_port_pool_get		= mlxsw_sp_sb_occ_port_pool_get,
400762306a36Sopenharmony_ci	.sb_occ_tc_port_bind_get	= mlxsw_sp_sb_occ_tc_port_bind_get,
400862306a36Sopenharmony_ci	.trap_init			= mlxsw_sp_trap_init,
400962306a36Sopenharmony_ci	.trap_fini			= mlxsw_sp_trap_fini,
401062306a36Sopenharmony_ci	.trap_action_set		= mlxsw_sp_trap_action_set,
401162306a36Sopenharmony_ci	.trap_group_init		= mlxsw_sp_trap_group_init,
401262306a36Sopenharmony_ci	.trap_group_set			= mlxsw_sp_trap_group_set,
401362306a36Sopenharmony_ci	.trap_policer_init		= mlxsw_sp_trap_policer_init,
401462306a36Sopenharmony_ci	.trap_policer_fini		= mlxsw_sp_trap_policer_fini,
401562306a36Sopenharmony_ci	.trap_policer_set		= mlxsw_sp_trap_policer_set,
401662306a36Sopenharmony_ci	.trap_policer_counter_get	= mlxsw_sp_trap_policer_counter_get,
401762306a36Sopenharmony_ci	.txhdr_construct		= mlxsw_sp_txhdr_construct,
401862306a36Sopenharmony_ci	.resources_register		= mlxsw_sp2_resources_register,
401962306a36Sopenharmony_ci	.ptp_transmitted		= mlxsw_sp_ptp_transmitted,
402062306a36Sopenharmony_ci	.txhdr_len			= MLXSW_TXHDR_LEN,
402162306a36Sopenharmony_ci	.profile			= &mlxsw_sp2_config_profile,
402262306a36Sopenharmony_ci	.sdq_supports_cqe_v2		= true,
402362306a36Sopenharmony_ci};
402462306a36Sopenharmony_ci
402562306a36Sopenharmony_cistatic struct mlxsw_driver mlxsw_sp4_driver = {
402662306a36Sopenharmony_ci	.kind				= mlxsw_sp4_driver_name,
402762306a36Sopenharmony_ci	.priv_size			= sizeof(struct mlxsw_sp),
402862306a36Sopenharmony_ci	.init				= mlxsw_sp4_init,
402962306a36Sopenharmony_ci	.fini				= mlxsw_sp_fini,
403062306a36Sopenharmony_ci	.port_split			= mlxsw_sp_port_split,
403162306a36Sopenharmony_ci	.port_unsplit			= mlxsw_sp_port_unsplit,
403262306a36Sopenharmony_ci	.ports_remove_selected		= mlxsw_sp_ports_remove_selected,
403362306a36Sopenharmony_ci	.sb_pool_get			= mlxsw_sp_sb_pool_get,
403462306a36Sopenharmony_ci	.sb_pool_set			= mlxsw_sp_sb_pool_set,
403562306a36Sopenharmony_ci	.sb_port_pool_get		= mlxsw_sp_sb_port_pool_get,
403662306a36Sopenharmony_ci	.sb_port_pool_set		= mlxsw_sp_sb_port_pool_set,
403762306a36Sopenharmony_ci	.sb_tc_pool_bind_get		= mlxsw_sp_sb_tc_pool_bind_get,
403862306a36Sopenharmony_ci	.sb_tc_pool_bind_set		= mlxsw_sp_sb_tc_pool_bind_set,
403962306a36Sopenharmony_ci	.sb_occ_snapshot		= mlxsw_sp_sb_occ_snapshot,
404062306a36Sopenharmony_ci	.sb_occ_max_clear		= mlxsw_sp_sb_occ_max_clear,
404162306a36Sopenharmony_ci	.sb_occ_port_pool_get		= mlxsw_sp_sb_occ_port_pool_get,
404262306a36Sopenharmony_ci	.sb_occ_tc_port_bind_get	= mlxsw_sp_sb_occ_tc_port_bind_get,
404362306a36Sopenharmony_ci	.trap_init			= mlxsw_sp_trap_init,
404462306a36Sopenharmony_ci	.trap_fini			= mlxsw_sp_trap_fini,
404562306a36Sopenharmony_ci	.trap_action_set		= mlxsw_sp_trap_action_set,
404662306a36Sopenharmony_ci	.trap_group_init		= mlxsw_sp_trap_group_init,
404762306a36Sopenharmony_ci	.trap_group_set			= mlxsw_sp_trap_group_set,
404862306a36Sopenharmony_ci	.trap_policer_init		= mlxsw_sp_trap_policer_init,
404962306a36Sopenharmony_ci	.trap_policer_fini		= mlxsw_sp_trap_policer_fini,
405062306a36Sopenharmony_ci	.trap_policer_set		= mlxsw_sp_trap_policer_set,
405162306a36Sopenharmony_ci	.trap_policer_counter_get	= mlxsw_sp_trap_policer_counter_get,
405262306a36Sopenharmony_ci	.txhdr_construct		= mlxsw_sp_txhdr_construct,
405362306a36Sopenharmony_ci	.resources_register		= mlxsw_sp2_resources_register,
405462306a36Sopenharmony_ci	.ptp_transmitted		= mlxsw_sp_ptp_transmitted,
405562306a36Sopenharmony_ci	.txhdr_len			= MLXSW_TXHDR_LEN,
405662306a36Sopenharmony_ci	.profile			= &mlxsw_sp4_config_profile,
405762306a36Sopenharmony_ci	.sdq_supports_cqe_v2		= true,
405862306a36Sopenharmony_ci};
405962306a36Sopenharmony_ci
406062306a36Sopenharmony_cibool mlxsw_sp_port_dev_check(const struct net_device *dev)
406162306a36Sopenharmony_ci{
406262306a36Sopenharmony_ci	return dev->netdev_ops == &mlxsw_sp_port_netdev_ops;
406362306a36Sopenharmony_ci}
406462306a36Sopenharmony_ci
406562306a36Sopenharmony_cistatic int mlxsw_sp_lower_dev_walk(struct net_device *lower_dev,
406662306a36Sopenharmony_ci				   struct netdev_nested_priv *priv)
406762306a36Sopenharmony_ci{
406862306a36Sopenharmony_ci	int ret = 0;
406962306a36Sopenharmony_ci
407062306a36Sopenharmony_ci	if (mlxsw_sp_port_dev_check(lower_dev)) {
407162306a36Sopenharmony_ci		priv->data = (void *)netdev_priv(lower_dev);
407262306a36Sopenharmony_ci		ret = 1;
407362306a36Sopenharmony_ci	}
407462306a36Sopenharmony_ci
407562306a36Sopenharmony_ci	return ret;
407662306a36Sopenharmony_ci}
407762306a36Sopenharmony_ci
407862306a36Sopenharmony_cistruct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find(struct net_device *dev)
407962306a36Sopenharmony_ci{
408062306a36Sopenharmony_ci	struct netdev_nested_priv priv = {
408162306a36Sopenharmony_ci		.data = NULL,
408262306a36Sopenharmony_ci	};
408362306a36Sopenharmony_ci
408462306a36Sopenharmony_ci	if (mlxsw_sp_port_dev_check(dev))
408562306a36Sopenharmony_ci		return netdev_priv(dev);
408662306a36Sopenharmony_ci
408762306a36Sopenharmony_ci	netdev_walk_all_lower_dev(dev, mlxsw_sp_lower_dev_walk, &priv);
408862306a36Sopenharmony_ci
408962306a36Sopenharmony_ci	return (struct mlxsw_sp_port *)priv.data;
409062306a36Sopenharmony_ci}
409162306a36Sopenharmony_ci
409262306a36Sopenharmony_cistruct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev)
409362306a36Sopenharmony_ci{
409462306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
409562306a36Sopenharmony_ci
409662306a36Sopenharmony_ci	mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(dev);
409762306a36Sopenharmony_ci	return mlxsw_sp_port ? mlxsw_sp_port->mlxsw_sp : NULL;
409862306a36Sopenharmony_ci}
409962306a36Sopenharmony_ci
410062306a36Sopenharmony_cistruct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find_rcu(struct net_device *dev)
410162306a36Sopenharmony_ci{
410262306a36Sopenharmony_ci	struct netdev_nested_priv priv = {
410362306a36Sopenharmony_ci		.data = NULL,
410462306a36Sopenharmony_ci	};
410562306a36Sopenharmony_ci
410662306a36Sopenharmony_ci	if (mlxsw_sp_port_dev_check(dev))
410762306a36Sopenharmony_ci		return netdev_priv(dev);
410862306a36Sopenharmony_ci
410962306a36Sopenharmony_ci	netdev_walk_all_lower_dev_rcu(dev, mlxsw_sp_lower_dev_walk,
411062306a36Sopenharmony_ci				      &priv);
411162306a36Sopenharmony_ci
411262306a36Sopenharmony_ci	return (struct mlxsw_sp_port *)priv.data;
411362306a36Sopenharmony_ci}
411462306a36Sopenharmony_ci
411562306a36Sopenharmony_ciint mlxsw_sp_parsing_depth_inc(struct mlxsw_sp *mlxsw_sp)
411662306a36Sopenharmony_ci{
411762306a36Sopenharmony_ci	char mprs_pl[MLXSW_REG_MPRS_LEN];
411862306a36Sopenharmony_ci	int err = 0;
411962306a36Sopenharmony_ci
412062306a36Sopenharmony_ci	mutex_lock(&mlxsw_sp->parsing.lock);
412162306a36Sopenharmony_ci
412262306a36Sopenharmony_ci	if (refcount_inc_not_zero(&mlxsw_sp->parsing.parsing_depth_ref))
412362306a36Sopenharmony_ci		goto out_unlock;
412462306a36Sopenharmony_ci
412562306a36Sopenharmony_ci	mlxsw_reg_mprs_pack(mprs_pl, MLXSW_SP_INCREASED_PARSING_DEPTH,
412662306a36Sopenharmony_ci			    mlxsw_sp->parsing.vxlan_udp_dport);
412762306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mprs), mprs_pl);
412862306a36Sopenharmony_ci	if (err)
412962306a36Sopenharmony_ci		goto out_unlock;
413062306a36Sopenharmony_ci
413162306a36Sopenharmony_ci	mlxsw_sp->parsing.parsing_depth = MLXSW_SP_INCREASED_PARSING_DEPTH;
413262306a36Sopenharmony_ci	refcount_set(&mlxsw_sp->parsing.parsing_depth_ref, 1);
413362306a36Sopenharmony_ci
413462306a36Sopenharmony_ciout_unlock:
413562306a36Sopenharmony_ci	mutex_unlock(&mlxsw_sp->parsing.lock);
413662306a36Sopenharmony_ci	return err;
413762306a36Sopenharmony_ci}
413862306a36Sopenharmony_ci
413962306a36Sopenharmony_civoid mlxsw_sp_parsing_depth_dec(struct mlxsw_sp *mlxsw_sp)
414062306a36Sopenharmony_ci{
414162306a36Sopenharmony_ci	char mprs_pl[MLXSW_REG_MPRS_LEN];
414262306a36Sopenharmony_ci
414362306a36Sopenharmony_ci	mutex_lock(&mlxsw_sp->parsing.lock);
414462306a36Sopenharmony_ci
414562306a36Sopenharmony_ci	if (!refcount_dec_and_test(&mlxsw_sp->parsing.parsing_depth_ref))
414662306a36Sopenharmony_ci		goto out_unlock;
414762306a36Sopenharmony_ci
414862306a36Sopenharmony_ci	mlxsw_reg_mprs_pack(mprs_pl, MLXSW_SP_DEFAULT_PARSING_DEPTH,
414962306a36Sopenharmony_ci			    mlxsw_sp->parsing.vxlan_udp_dport);
415062306a36Sopenharmony_ci	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mprs), mprs_pl);
415162306a36Sopenharmony_ci	mlxsw_sp->parsing.parsing_depth = MLXSW_SP_DEFAULT_PARSING_DEPTH;
415262306a36Sopenharmony_ci
415362306a36Sopenharmony_ciout_unlock:
415462306a36Sopenharmony_ci	mutex_unlock(&mlxsw_sp->parsing.lock);
415562306a36Sopenharmony_ci}
415662306a36Sopenharmony_ci
415762306a36Sopenharmony_ciint mlxsw_sp_parsing_vxlan_udp_dport_set(struct mlxsw_sp *mlxsw_sp,
415862306a36Sopenharmony_ci					 __be16 udp_dport)
415962306a36Sopenharmony_ci{
416062306a36Sopenharmony_ci	char mprs_pl[MLXSW_REG_MPRS_LEN];
416162306a36Sopenharmony_ci	int err;
416262306a36Sopenharmony_ci
416362306a36Sopenharmony_ci	mutex_lock(&mlxsw_sp->parsing.lock);
416462306a36Sopenharmony_ci
416562306a36Sopenharmony_ci	mlxsw_reg_mprs_pack(mprs_pl, mlxsw_sp->parsing.parsing_depth,
416662306a36Sopenharmony_ci			    be16_to_cpu(udp_dport));
416762306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mprs), mprs_pl);
416862306a36Sopenharmony_ci	if (err)
416962306a36Sopenharmony_ci		goto out_unlock;
417062306a36Sopenharmony_ci
417162306a36Sopenharmony_ci	mlxsw_sp->parsing.vxlan_udp_dport = be16_to_cpu(udp_dport);
417262306a36Sopenharmony_ci
417362306a36Sopenharmony_ciout_unlock:
417462306a36Sopenharmony_ci	mutex_unlock(&mlxsw_sp->parsing.lock);
417562306a36Sopenharmony_ci	return err;
417662306a36Sopenharmony_ci}
417762306a36Sopenharmony_ci
417862306a36Sopenharmony_cistatic void
417962306a36Sopenharmony_cimlxsw_sp_port_lag_uppers_cleanup(struct mlxsw_sp_port *mlxsw_sp_port,
418062306a36Sopenharmony_ci				 struct net_device *lag_dev)
418162306a36Sopenharmony_ci{
418262306a36Sopenharmony_ci	struct net_device *br_dev = netdev_master_upper_dev_get(lag_dev);
418362306a36Sopenharmony_ci	struct net_device *upper_dev;
418462306a36Sopenharmony_ci	struct list_head *iter;
418562306a36Sopenharmony_ci
418662306a36Sopenharmony_ci	if (netif_is_bridge_port(lag_dev))
418762306a36Sopenharmony_ci		mlxsw_sp_port_bridge_leave(mlxsw_sp_port, lag_dev, br_dev);
418862306a36Sopenharmony_ci
418962306a36Sopenharmony_ci	netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {
419062306a36Sopenharmony_ci		if (!netif_is_bridge_port(upper_dev))
419162306a36Sopenharmony_ci			continue;
419262306a36Sopenharmony_ci		br_dev = netdev_master_upper_dev_get(upper_dev);
419362306a36Sopenharmony_ci		mlxsw_sp_port_bridge_leave(mlxsw_sp_port, upper_dev, br_dev);
419462306a36Sopenharmony_ci	}
419562306a36Sopenharmony_ci}
419662306a36Sopenharmony_ci
419762306a36Sopenharmony_cistatic int mlxsw_sp_lag_create(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
419862306a36Sopenharmony_ci{
419962306a36Sopenharmony_ci	char sldr_pl[MLXSW_REG_SLDR_LEN];
420062306a36Sopenharmony_ci
420162306a36Sopenharmony_ci	mlxsw_reg_sldr_lag_create_pack(sldr_pl, lag_id);
420262306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
420362306a36Sopenharmony_ci}
420462306a36Sopenharmony_ci
420562306a36Sopenharmony_cistatic int mlxsw_sp_lag_destroy(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
420662306a36Sopenharmony_ci{
420762306a36Sopenharmony_ci	char sldr_pl[MLXSW_REG_SLDR_LEN];
420862306a36Sopenharmony_ci
420962306a36Sopenharmony_ci	mlxsw_reg_sldr_lag_destroy_pack(sldr_pl, lag_id);
421062306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
421162306a36Sopenharmony_ci}
421262306a36Sopenharmony_ci
421362306a36Sopenharmony_cistatic int mlxsw_sp_lag_col_port_add(struct mlxsw_sp_port *mlxsw_sp_port,
421462306a36Sopenharmony_ci				     u16 lag_id, u8 port_index)
421562306a36Sopenharmony_ci{
421662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
421762306a36Sopenharmony_ci	char slcor_pl[MLXSW_REG_SLCOR_LEN];
421862306a36Sopenharmony_ci
421962306a36Sopenharmony_ci	mlxsw_reg_slcor_port_add_pack(slcor_pl, mlxsw_sp_port->local_port,
422062306a36Sopenharmony_ci				      lag_id, port_index);
422162306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
422262306a36Sopenharmony_ci}
422362306a36Sopenharmony_ci
422462306a36Sopenharmony_cistatic int mlxsw_sp_lag_col_port_remove(struct mlxsw_sp_port *mlxsw_sp_port,
422562306a36Sopenharmony_ci					u16 lag_id)
422662306a36Sopenharmony_ci{
422762306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
422862306a36Sopenharmony_ci	char slcor_pl[MLXSW_REG_SLCOR_LEN];
422962306a36Sopenharmony_ci
423062306a36Sopenharmony_ci	mlxsw_reg_slcor_port_remove_pack(slcor_pl, mlxsw_sp_port->local_port,
423162306a36Sopenharmony_ci					 lag_id);
423262306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
423362306a36Sopenharmony_ci}
423462306a36Sopenharmony_ci
423562306a36Sopenharmony_cistatic int mlxsw_sp_lag_col_port_enable(struct mlxsw_sp_port *mlxsw_sp_port,
423662306a36Sopenharmony_ci					u16 lag_id)
423762306a36Sopenharmony_ci{
423862306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
423962306a36Sopenharmony_ci	char slcor_pl[MLXSW_REG_SLCOR_LEN];
424062306a36Sopenharmony_ci
424162306a36Sopenharmony_ci	mlxsw_reg_slcor_col_enable_pack(slcor_pl, mlxsw_sp_port->local_port,
424262306a36Sopenharmony_ci					lag_id);
424362306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
424462306a36Sopenharmony_ci}
424562306a36Sopenharmony_ci
424662306a36Sopenharmony_cistatic int mlxsw_sp_lag_col_port_disable(struct mlxsw_sp_port *mlxsw_sp_port,
424762306a36Sopenharmony_ci					 u16 lag_id)
424862306a36Sopenharmony_ci{
424962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
425062306a36Sopenharmony_ci	char slcor_pl[MLXSW_REG_SLCOR_LEN];
425162306a36Sopenharmony_ci
425262306a36Sopenharmony_ci	mlxsw_reg_slcor_col_disable_pack(slcor_pl, mlxsw_sp_port->local_port,
425362306a36Sopenharmony_ci					 lag_id);
425462306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
425562306a36Sopenharmony_ci}
425662306a36Sopenharmony_ci
425762306a36Sopenharmony_cistatic int mlxsw_sp_lag_index_get(struct mlxsw_sp *mlxsw_sp,
425862306a36Sopenharmony_ci				  struct net_device *lag_dev,
425962306a36Sopenharmony_ci				  u16 *p_lag_id)
426062306a36Sopenharmony_ci{
426162306a36Sopenharmony_ci	struct mlxsw_sp_upper *lag;
426262306a36Sopenharmony_ci	int free_lag_id = -1;
426362306a36Sopenharmony_ci	u16 max_lag;
426462306a36Sopenharmony_ci	int err, i;
426562306a36Sopenharmony_ci
426662306a36Sopenharmony_ci	err = mlxsw_core_max_lag(mlxsw_sp->core, &max_lag);
426762306a36Sopenharmony_ci	if (err)
426862306a36Sopenharmony_ci		return err;
426962306a36Sopenharmony_ci
427062306a36Sopenharmony_ci	for (i = 0; i < max_lag; i++) {
427162306a36Sopenharmony_ci		lag = mlxsw_sp_lag_get(mlxsw_sp, i);
427262306a36Sopenharmony_ci		if (lag->ref_count) {
427362306a36Sopenharmony_ci			if (lag->dev == lag_dev) {
427462306a36Sopenharmony_ci				*p_lag_id = i;
427562306a36Sopenharmony_ci				return 0;
427662306a36Sopenharmony_ci			}
427762306a36Sopenharmony_ci		} else if (free_lag_id < 0) {
427862306a36Sopenharmony_ci			free_lag_id = i;
427962306a36Sopenharmony_ci		}
428062306a36Sopenharmony_ci	}
428162306a36Sopenharmony_ci	if (free_lag_id < 0)
428262306a36Sopenharmony_ci		return -EBUSY;
428362306a36Sopenharmony_ci	*p_lag_id = free_lag_id;
428462306a36Sopenharmony_ci	return 0;
428562306a36Sopenharmony_ci}
428662306a36Sopenharmony_ci
428762306a36Sopenharmony_cistatic bool
428862306a36Sopenharmony_cimlxsw_sp_master_lag_check(struct mlxsw_sp *mlxsw_sp,
428962306a36Sopenharmony_ci			  struct net_device *lag_dev,
429062306a36Sopenharmony_ci			  struct netdev_lag_upper_info *lag_upper_info,
429162306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
429262306a36Sopenharmony_ci{
429362306a36Sopenharmony_ci	u16 lag_id;
429462306a36Sopenharmony_ci
429562306a36Sopenharmony_ci	if (mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id) != 0) {
429662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported LAG devices");
429762306a36Sopenharmony_ci		return false;
429862306a36Sopenharmony_ci	}
429962306a36Sopenharmony_ci	if (lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
430062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type");
430162306a36Sopenharmony_ci		return false;
430262306a36Sopenharmony_ci	}
430362306a36Sopenharmony_ci	return true;
430462306a36Sopenharmony_ci}
430562306a36Sopenharmony_ci
430662306a36Sopenharmony_cistatic int mlxsw_sp_port_lag_index_get(struct mlxsw_sp *mlxsw_sp,
430762306a36Sopenharmony_ci				       u16 lag_id, u8 *p_port_index)
430862306a36Sopenharmony_ci{
430962306a36Sopenharmony_ci	u64 max_lag_members;
431062306a36Sopenharmony_ci	int i;
431162306a36Sopenharmony_ci
431262306a36Sopenharmony_ci	max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core,
431362306a36Sopenharmony_ci					     MAX_LAG_MEMBERS);
431462306a36Sopenharmony_ci	for (i = 0; i < max_lag_members; i++) {
431562306a36Sopenharmony_ci		if (!mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i)) {
431662306a36Sopenharmony_ci			*p_port_index = i;
431762306a36Sopenharmony_ci			return 0;
431862306a36Sopenharmony_ci		}
431962306a36Sopenharmony_ci	}
432062306a36Sopenharmony_ci	return -EBUSY;
432162306a36Sopenharmony_ci}
432262306a36Sopenharmony_ci
432362306a36Sopenharmony_cistatic int mlxsw_sp_lag_uppers_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port,
432462306a36Sopenharmony_ci					   struct net_device *lag_dev,
432562306a36Sopenharmony_ci					   struct netlink_ext_ack *extack)
432662306a36Sopenharmony_ci{
432762306a36Sopenharmony_ci	struct net_device *upper_dev;
432862306a36Sopenharmony_ci	struct net_device *master;
432962306a36Sopenharmony_ci	struct list_head *iter;
433062306a36Sopenharmony_ci	int done = 0;
433162306a36Sopenharmony_ci	int err;
433262306a36Sopenharmony_ci
433362306a36Sopenharmony_ci	master = netdev_master_upper_dev_get(lag_dev);
433462306a36Sopenharmony_ci	if (master && netif_is_bridge_master(master)) {
433562306a36Sopenharmony_ci		err = mlxsw_sp_port_bridge_join(mlxsw_sp_port, lag_dev, master,
433662306a36Sopenharmony_ci						extack);
433762306a36Sopenharmony_ci		if (err)
433862306a36Sopenharmony_ci			return err;
433962306a36Sopenharmony_ci	}
434062306a36Sopenharmony_ci
434162306a36Sopenharmony_ci	netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {
434262306a36Sopenharmony_ci		if (!is_vlan_dev(upper_dev))
434362306a36Sopenharmony_ci			continue;
434462306a36Sopenharmony_ci
434562306a36Sopenharmony_ci		master = netdev_master_upper_dev_get(upper_dev);
434662306a36Sopenharmony_ci		if (master && netif_is_bridge_master(master)) {
434762306a36Sopenharmony_ci			err = mlxsw_sp_port_bridge_join(mlxsw_sp_port,
434862306a36Sopenharmony_ci							upper_dev, master,
434962306a36Sopenharmony_ci							extack);
435062306a36Sopenharmony_ci			if (err)
435162306a36Sopenharmony_ci				goto err_port_bridge_join;
435262306a36Sopenharmony_ci		}
435362306a36Sopenharmony_ci
435462306a36Sopenharmony_ci		++done;
435562306a36Sopenharmony_ci	}
435662306a36Sopenharmony_ci
435762306a36Sopenharmony_ci	return 0;
435862306a36Sopenharmony_ci
435962306a36Sopenharmony_cierr_port_bridge_join:
436062306a36Sopenharmony_ci	netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {
436162306a36Sopenharmony_ci		if (!is_vlan_dev(upper_dev))
436262306a36Sopenharmony_ci			continue;
436362306a36Sopenharmony_ci
436462306a36Sopenharmony_ci		master = netdev_master_upper_dev_get(upper_dev);
436562306a36Sopenharmony_ci		if (!master || !netif_is_bridge_master(master))
436662306a36Sopenharmony_ci			continue;
436762306a36Sopenharmony_ci
436862306a36Sopenharmony_ci		if (!done--)
436962306a36Sopenharmony_ci			break;
437062306a36Sopenharmony_ci
437162306a36Sopenharmony_ci		mlxsw_sp_port_bridge_leave(mlxsw_sp_port, upper_dev, master);
437262306a36Sopenharmony_ci	}
437362306a36Sopenharmony_ci
437462306a36Sopenharmony_ci	master = netdev_master_upper_dev_get(lag_dev);
437562306a36Sopenharmony_ci	if (master && netif_is_bridge_master(master))
437662306a36Sopenharmony_ci		mlxsw_sp_port_bridge_leave(mlxsw_sp_port, lag_dev, master);
437762306a36Sopenharmony_ci
437862306a36Sopenharmony_ci	return err;
437962306a36Sopenharmony_ci}
438062306a36Sopenharmony_ci
438162306a36Sopenharmony_cistatic void
438262306a36Sopenharmony_cimlxsw_sp_lag_uppers_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port,
438362306a36Sopenharmony_ci				 struct net_device *lag_dev)
438462306a36Sopenharmony_ci{
438562306a36Sopenharmony_ci	struct net_device *upper_dev;
438662306a36Sopenharmony_ci	struct net_device *master;
438762306a36Sopenharmony_ci	struct list_head *iter;
438862306a36Sopenharmony_ci
438962306a36Sopenharmony_ci	netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {
439062306a36Sopenharmony_ci		if (!is_vlan_dev(upper_dev))
439162306a36Sopenharmony_ci			continue;
439262306a36Sopenharmony_ci
439362306a36Sopenharmony_ci		master = netdev_master_upper_dev_get(upper_dev);
439462306a36Sopenharmony_ci		if (!master)
439562306a36Sopenharmony_ci			continue;
439662306a36Sopenharmony_ci
439762306a36Sopenharmony_ci		mlxsw_sp_port_bridge_leave(mlxsw_sp_port, upper_dev, master);
439862306a36Sopenharmony_ci	}
439962306a36Sopenharmony_ci
440062306a36Sopenharmony_ci	master = netdev_master_upper_dev_get(lag_dev);
440162306a36Sopenharmony_ci	if (master)
440262306a36Sopenharmony_ci		mlxsw_sp_port_bridge_leave(mlxsw_sp_port, lag_dev, master);
440362306a36Sopenharmony_ci}
440462306a36Sopenharmony_ci
440562306a36Sopenharmony_cistatic int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
440662306a36Sopenharmony_ci				  struct net_device *lag_dev,
440762306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
440862306a36Sopenharmony_ci{
440962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
441062306a36Sopenharmony_ci	struct mlxsw_sp_upper *lag;
441162306a36Sopenharmony_ci	u16 lag_id;
441262306a36Sopenharmony_ci	u8 port_index;
441362306a36Sopenharmony_ci	int err;
441462306a36Sopenharmony_ci
441562306a36Sopenharmony_ci	err = mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id);
441662306a36Sopenharmony_ci	if (err)
441762306a36Sopenharmony_ci		return err;
441862306a36Sopenharmony_ci	lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id);
441962306a36Sopenharmony_ci	if (!lag->ref_count) {
442062306a36Sopenharmony_ci		err = mlxsw_sp_lag_create(mlxsw_sp, lag_id);
442162306a36Sopenharmony_ci		if (err)
442262306a36Sopenharmony_ci			return err;
442362306a36Sopenharmony_ci		lag->dev = lag_dev;
442462306a36Sopenharmony_ci	}
442562306a36Sopenharmony_ci
442662306a36Sopenharmony_ci	err = mlxsw_sp_port_lag_index_get(mlxsw_sp, lag_id, &port_index);
442762306a36Sopenharmony_ci	if (err)
442862306a36Sopenharmony_ci		return err;
442962306a36Sopenharmony_ci
443062306a36Sopenharmony_ci	err = mlxsw_sp_lag_uppers_bridge_join(mlxsw_sp_port, lag_dev,
443162306a36Sopenharmony_ci					      extack);
443262306a36Sopenharmony_ci	if (err)
443362306a36Sopenharmony_ci		goto err_lag_uppers_bridge_join;
443462306a36Sopenharmony_ci
443562306a36Sopenharmony_ci	err = mlxsw_sp_lag_col_port_add(mlxsw_sp_port, lag_id, port_index);
443662306a36Sopenharmony_ci	if (err)
443762306a36Sopenharmony_ci		goto err_col_port_add;
443862306a36Sopenharmony_ci
443962306a36Sopenharmony_ci	mlxsw_core_lag_mapping_set(mlxsw_sp->core, lag_id, port_index,
444062306a36Sopenharmony_ci				   mlxsw_sp_port->local_port);
444162306a36Sopenharmony_ci	mlxsw_sp_port->lag_id = lag_id;
444262306a36Sopenharmony_ci	mlxsw_sp_port->lagged = 1;
444362306a36Sopenharmony_ci	lag->ref_count++;
444462306a36Sopenharmony_ci
444562306a36Sopenharmony_ci	/* Port is no longer usable as a router interface */
444662306a36Sopenharmony_ci	if (mlxsw_sp_port->default_vlan->fid)
444762306a36Sopenharmony_ci		mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port->default_vlan);
444862306a36Sopenharmony_ci
444962306a36Sopenharmony_ci	/* Join a router interface configured on the LAG, if exists */
445062306a36Sopenharmony_ci	err = mlxsw_sp_router_port_join_lag(mlxsw_sp_port, lag_dev,
445162306a36Sopenharmony_ci					    extack);
445262306a36Sopenharmony_ci	if (err)
445362306a36Sopenharmony_ci		goto err_router_join;
445462306a36Sopenharmony_ci
445562306a36Sopenharmony_ci	err = mlxsw_sp_netdevice_enslavement_replay(mlxsw_sp, lag_dev, extack);
445662306a36Sopenharmony_ci	if (err)
445762306a36Sopenharmony_ci		goto err_replay;
445862306a36Sopenharmony_ci
445962306a36Sopenharmony_ci	return 0;
446062306a36Sopenharmony_ci
446162306a36Sopenharmony_cierr_replay:
446262306a36Sopenharmony_ci	mlxsw_sp_router_port_leave_lag(mlxsw_sp_port, lag_dev);
446362306a36Sopenharmony_cierr_router_join:
446462306a36Sopenharmony_ci	lag->ref_count--;
446562306a36Sopenharmony_ci	mlxsw_sp_port->lagged = 0;
446662306a36Sopenharmony_ci	mlxsw_core_lag_mapping_clear(mlxsw_sp->core, lag_id,
446762306a36Sopenharmony_ci				     mlxsw_sp_port->local_port);
446862306a36Sopenharmony_ci	mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
446962306a36Sopenharmony_cierr_col_port_add:
447062306a36Sopenharmony_ci	mlxsw_sp_lag_uppers_bridge_leave(mlxsw_sp_port, lag_dev);
447162306a36Sopenharmony_cierr_lag_uppers_bridge_join:
447262306a36Sopenharmony_ci	if (!lag->ref_count)
447362306a36Sopenharmony_ci		mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
447462306a36Sopenharmony_ci	return err;
447562306a36Sopenharmony_ci}
447662306a36Sopenharmony_ci
447762306a36Sopenharmony_cistatic void mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port,
447862306a36Sopenharmony_ci				    struct net_device *lag_dev)
447962306a36Sopenharmony_ci{
448062306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
448162306a36Sopenharmony_ci	u16 lag_id = mlxsw_sp_port->lag_id;
448262306a36Sopenharmony_ci	struct mlxsw_sp_upper *lag;
448362306a36Sopenharmony_ci
448462306a36Sopenharmony_ci	if (!mlxsw_sp_port->lagged)
448562306a36Sopenharmony_ci		return;
448662306a36Sopenharmony_ci	lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id);
448762306a36Sopenharmony_ci	WARN_ON(lag->ref_count == 0);
448862306a36Sopenharmony_ci
448962306a36Sopenharmony_ci	mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
449062306a36Sopenharmony_ci
449162306a36Sopenharmony_ci	/* Any VLANs configured on the port are no longer valid */
449262306a36Sopenharmony_ci	mlxsw_sp_port_vlan_flush(mlxsw_sp_port, false);
449362306a36Sopenharmony_ci	mlxsw_sp_port_vlan_cleanup(mlxsw_sp_port->default_vlan);
449462306a36Sopenharmony_ci	/* Make the LAG and its directly linked uppers leave bridges they
449562306a36Sopenharmony_ci	 * are memeber in
449662306a36Sopenharmony_ci	 */
449762306a36Sopenharmony_ci	mlxsw_sp_port_lag_uppers_cleanup(mlxsw_sp_port, lag_dev);
449862306a36Sopenharmony_ci
449962306a36Sopenharmony_ci	if (lag->ref_count == 1)
450062306a36Sopenharmony_ci		mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
450162306a36Sopenharmony_ci
450262306a36Sopenharmony_ci	mlxsw_core_lag_mapping_clear(mlxsw_sp->core, lag_id,
450362306a36Sopenharmony_ci				     mlxsw_sp_port->local_port);
450462306a36Sopenharmony_ci	mlxsw_sp_port->lagged = 0;
450562306a36Sopenharmony_ci	lag->ref_count--;
450662306a36Sopenharmony_ci
450762306a36Sopenharmony_ci	/* Make sure untagged frames are allowed to ingress */
450862306a36Sopenharmony_ci	mlxsw_sp_port_pvid_set(mlxsw_sp_port, MLXSW_SP_DEFAULT_VID,
450962306a36Sopenharmony_ci			       ETH_P_8021Q);
451062306a36Sopenharmony_ci}
451162306a36Sopenharmony_ci
451262306a36Sopenharmony_cistatic int mlxsw_sp_lag_dist_port_add(struct mlxsw_sp_port *mlxsw_sp_port,
451362306a36Sopenharmony_ci				      u16 lag_id)
451462306a36Sopenharmony_ci{
451562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
451662306a36Sopenharmony_ci	char sldr_pl[MLXSW_REG_SLDR_LEN];
451762306a36Sopenharmony_ci
451862306a36Sopenharmony_ci	mlxsw_reg_sldr_lag_add_port_pack(sldr_pl, lag_id,
451962306a36Sopenharmony_ci					 mlxsw_sp_port->local_port);
452062306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
452162306a36Sopenharmony_ci}
452262306a36Sopenharmony_ci
452362306a36Sopenharmony_cistatic int mlxsw_sp_lag_dist_port_remove(struct mlxsw_sp_port *mlxsw_sp_port,
452462306a36Sopenharmony_ci					 u16 lag_id)
452562306a36Sopenharmony_ci{
452662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
452762306a36Sopenharmony_ci	char sldr_pl[MLXSW_REG_SLDR_LEN];
452862306a36Sopenharmony_ci
452962306a36Sopenharmony_ci	mlxsw_reg_sldr_lag_remove_port_pack(sldr_pl, lag_id,
453062306a36Sopenharmony_ci					    mlxsw_sp_port->local_port);
453162306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
453262306a36Sopenharmony_ci}
453362306a36Sopenharmony_ci
453462306a36Sopenharmony_cistatic int
453562306a36Sopenharmony_cimlxsw_sp_port_lag_col_dist_enable(struct mlxsw_sp_port *mlxsw_sp_port)
453662306a36Sopenharmony_ci{
453762306a36Sopenharmony_ci	int err;
453862306a36Sopenharmony_ci
453962306a36Sopenharmony_ci	err = mlxsw_sp_lag_col_port_enable(mlxsw_sp_port,
454062306a36Sopenharmony_ci					   mlxsw_sp_port->lag_id);
454162306a36Sopenharmony_ci	if (err)
454262306a36Sopenharmony_ci		return err;
454362306a36Sopenharmony_ci
454462306a36Sopenharmony_ci	err = mlxsw_sp_lag_dist_port_add(mlxsw_sp_port, mlxsw_sp_port->lag_id);
454562306a36Sopenharmony_ci	if (err)
454662306a36Sopenharmony_ci		goto err_dist_port_add;
454762306a36Sopenharmony_ci
454862306a36Sopenharmony_ci	return 0;
454962306a36Sopenharmony_ci
455062306a36Sopenharmony_cierr_dist_port_add:
455162306a36Sopenharmony_ci	mlxsw_sp_lag_col_port_disable(mlxsw_sp_port, mlxsw_sp_port->lag_id);
455262306a36Sopenharmony_ci	return err;
455362306a36Sopenharmony_ci}
455462306a36Sopenharmony_ci
455562306a36Sopenharmony_cistatic int
455662306a36Sopenharmony_cimlxsw_sp_port_lag_col_dist_disable(struct mlxsw_sp_port *mlxsw_sp_port)
455762306a36Sopenharmony_ci{
455862306a36Sopenharmony_ci	int err;
455962306a36Sopenharmony_ci
456062306a36Sopenharmony_ci	err = mlxsw_sp_lag_dist_port_remove(mlxsw_sp_port,
456162306a36Sopenharmony_ci					    mlxsw_sp_port->lag_id);
456262306a36Sopenharmony_ci	if (err)
456362306a36Sopenharmony_ci		return err;
456462306a36Sopenharmony_ci
456562306a36Sopenharmony_ci	err = mlxsw_sp_lag_col_port_disable(mlxsw_sp_port,
456662306a36Sopenharmony_ci					    mlxsw_sp_port->lag_id);
456762306a36Sopenharmony_ci	if (err)
456862306a36Sopenharmony_ci		goto err_col_port_disable;
456962306a36Sopenharmony_ci
457062306a36Sopenharmony_ci	return 0;
457162306a36Sopenharmony_ci
457262306a36Sopenharmony_cierr_col_port_disable:
457362306a36Sopenharmony_ci	mlxsw_sp_lag_dist_port_add(mlxsw_sp_port, mlxsw_sp_port->lag_id);
457462306a36Sopenharmony_ci	return err;
457562306a36Sopenharmony_ci}
457662306a36Sopenharmony_ci
457762306a36Sopenharmony_cistatic int mlxsw_sp_port_lag_changed(struct mlxsw_sp_port *mlxsw_sp_port,
457862306a36Sopenharmony_ci				     struct netdev_lag_lower_state_info *info)
457962306a36Sopenharmony_ci{
458062306a36Sopenharmony_ci	if (info->tx_enabled)
458162306a36Sopenharmony_ci		return mlxsw_sp_port_lag_col_dist_enable(mlxsw_sp_port);
458262306a36Sopenharmony_ci	else
458362306a36Sopenharmony_ci		return mlxsw_sp_port_lag_col_dist_disable(mlxsw_sp_port);
458462306a36Sopenharmony_ci}
458562306a36Sopenharmony_ci
458662306a36Sopenharmony_cistatic int mlxsw_sp_port_stp_set(struct mlxsw_sp_port *mlxsw_sp_port,
458762306a36Sopenharmony_ci				 bool enable)
458862306a36Sopenharmony_ci{
458962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
459062306a36Sopenharmony_ci	enum mlxsw_reg_spms_state spms_state;
459162306a36Sopenharmony_ci	char *spms_pl;
459262306a36Sopenharmony_ci	u16 vid;
459362306a36Sopenharmony_ci	int err;
459462306a36Sopenharmony_ci
459562306a36Sopenharmony_ci	spms_state = enable ? MLXSW_REG_SPMS_STATE_FORWARDING :
459662306a36Sopenharmony_ci			      MLXSW_REG_SPMS_STATE_DISCARDING;
459762306a36Sopenharmony_ci
459862306a36Sopenharmony_ci	spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL);
459962306a36Sopenharmony_ci	if (!spms_pl)
460062306a36Sopenharmony_ci		return -ENOMEM;
460162306a36Sopenharmony_ci	mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port);
460262306a36Sopenharmony_ci
460362306a36Sopenharmony_ci	for (vid = 0; vid < VLAN_N_VID; vid++)
460462306a36Sopenharmony_ci		mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state);
460562306a36Sopenharmony_ci
460662306a36Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl);
460762306a36Sopenharmony_ci	kfree(spms_pl);
460862306a36Sopenharmony_ci	return err;
460962306a36Sopenharmony_ci}
461062306a36Sopenharmony_ci
461162306a36Sopenharmony_cistatic int mlxsw_sp_port_ovs_join(struct mlxsw_sp_port *mlxsw_sp_port)
461262306a36Sopenharmony_ci{
461362306a36Sopenharmony_ci	u16 vid = 1;
461462306a36Sopenharmony_ci	int err;
461562306a36Sopenharmony_ci
461662306a36Sopenharmony_ci	err = mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, true);
461762306a36Sopenharmony_ci	if (err)
461862306a36Sopenharmony_ci		return err;
461962306a36Sopenharmony_ci	err = mlxsw_sp_port_stp_set(mlxsw_sp_port, true);
462062306a36Sopenharmony_ci	if (err)
462162306a36Sopenharmony_ci		goto err_port_stp_set;
462262306a36Sopenharmony_ci	err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, 1, VLAN_N_VID - 2,
462362306a36Sopenharmony_ci				     true, false);
462462306a36Sopenharmony_ci	if (err)
462562306a36Sopenharmony_ci		goto err_port_vlan_set;
462662306a36Sopenharmony_ci
462762306a36Sopenharmony_ci	for (; vid <= VLAN_N_VID - 1; vid++) {
462862306a36Sopenharmony_ci		err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port,
462962306a36Sopenharmony_ci						     vid, false);
463062306a36Sopenharmony_ci		if (err)
463162306a36Sopenharmony_ci			goto err_vid_learning_set;
463262306a36Sopenharmony_ci	}
463362306a36Sopenharmony_ci
463462306a36Sopenharmony_ci	return 0;
463562306a36Sopenharmony_ci
463662306a36Sopenharmony_cierr_vid_learning_set:
463762306a36Sopenharmony_ci	for (vid--; vid >= 1; vid--)
463862306a36Sopenharmony_ci		mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
463962306a36Sopenharmony_cierr_port_vlan_set:
464062306a36Sopenharmony_ci	mlxsw_sp_port_stp_set(mlxsw_sp_port, false);
464162306a36Sopenharmony_cierr_port_stp_set:
464262306a36Sopenharmony_ci	mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false);
464362306a36Sopenharmony_ci	return err;
464462306a36Sopenharmony_ci}
464562306a36Sopenharmony_ci
464662306a36Sopenharmony_cistatic void mlxsw_sp_port_ovs_leave(struct mlxsw_sp_port *mlxsw_sp_port)
464762306a36Sopenharmony_ci{
464862306a36Sopenharmony_ci	u16 vid;
464962306a36Sopenharmony_ci
465062306a36Sopenharmony_ci	for (vid = VLAN_N_VID - 1; vid >= 1; vid--)
465162306a36Sopenharmony_ci		mlxsw_sp_port_vid_learning_set(mlxsw_sp_port,
465262306a36Sopenharmony_ci					       vid, true);
465362306a36Sopenharmony_ci
465462306a36Sopenharmony_ci	mlxsw_sp_port_vlan_set(mlxsw_sp_port, 1, VLAN_N_VID - 2,
465562306a36Sopenharmony_ci			       false, false);
465662306a36Sopenharmony_ci	mlxsw_sp_port_stp_set(mlxsw_sp_port, false);
465762306a36Sopenharmony_ci	mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false);
465862306a36Sopenharmony_ci}
465962306a36Sopenharmony_ci
466062306a36Sopenharmony_cistatic bool mlxsw_sp_bridge_has_multiple_vxlans(struct net_device *br_dev)
466162306a36Sopenharmony_ci{
466262306a36Sopenharmony_ci	unsigned int num_vxlans = 0;
466362306a36Sopenharmony_ci	struct net_device *dev;
466462306a36Sopenharmony_ci	struct list_head *iter;
466562306a36Sopenharmony_ci
466662306a36Sopenharmony_ci	netdev_for_each_lower_dev(br_dev, dev, iter) {
466762306a36Sopenharmony_ci		if (netif_is_vxlan(dev))
466862306a36Sopenharmony_ci			num_vxlans++;
466962306a36Sopenharmony_ci	}
467062306a36Sopenharmony_ci
467162306a36Sopenharmony_ci	return num_vxlans > 1;
467262306a36Sopenharmony_ci}
467362306a36Sopenharmony_ci
467462306a36Sopenharmony_cistatic bool mlxsw_sp_bridge_vxlan_vlan_is_valid(struct net_device *br_dev)
467562306a36Sopenharmony_ci{
467662306a36Sopenharmony_ci	DECLARE_BITMAP(vlans, VLAN_N_VID) = {0};
467762306a36Sopenharmony_ci	struct net_device *dev;
467862306a36Sopenharmony_ci	struct list_head *iter;
467962306a36Sopenharmony_ci
468062306a36Sopenharmony_ci	netdev_for_each_lower_dev(br_dev, dev, iter) {
468162306a36Sopenharmony_ci		u16 pvid;
468262306a36Sopenharmony_ci		int err;
468362306a36Sopenharmony_ci
468462306a36Sopenharmony_ci		if (!netif_is_vxlan(dev))
468562306a36Sopenharmony_ci			continue;
468662306a36Sopenharmony_ci
468762306a36Sopenharmony_ci		err = mlxsw_sp_vxlan_mapped_vid(dev, &pvid);
468862306a36Sopenharmony_ci		if (err || !pvid)
468962306a36Sopenharmony_ci			continue;
469062306a36Sopenharmony_ci
469162306a36Sopenharmony_ci		if (test_and_set_bit(pvid, vlans))
469262306a36Sopenharmony_ci			return false;
469362306a36Sopenharmony_ci	}
469462306a36Sopenharmony_ci
469562306a36Sopenharmony_ci	return true;
469662306a36Sopenharmony_ci}
469762306a36Sopenharmony_ci
469862306a36Sopenharmony_cistatic bool mlxsw_sp_bridge_vxlan_is_valid(struct net_device *br_dev,
469962306a36Sopenharmony_ci					   struct netlink_ext_ack *extack)
470062306a36Sopenharmony_ci{
470162306a36Sopenharmony_ci	if (br_multicast_enabled(br_dev)) {
470262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Multicast can not be enabled on a bridge with a VxLAN device");
470362306a36Sopenharmony_ci		return false;
470462306a36Sopenharmony_ci	}
470562306a36Sopenharmony_ci
470662306a36Sopenharmony_ci	if (!br_vlan_enabled(br_dev) &&
470762306a36Sopenharmony_ci	    mlxsw_sp_bridge_has_multiple_vxlans(br_dev)) {
470862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Multiple VxLAN devices are not supported in a VLAN-unaware bridge");
470962306a36Sopenharmony_ci		return false;
471062306a36Sopenharmony_ci	}
471162306a36Sopenharmony_ci
471262306a36Sopenharmony_ci	if (br_vlan_enabled(br_dev) &&
471362306a36Sopenharmony_ci	    !mlxsw_sp_bridge_vxlan_vlan_is_valid(br_dev)) {
471462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Multiple VxLAN devices cannot have the same VLAN as PVID and egress untagged");
471562306a36Sopenharmony_ci		return false;
471662306a36Sopenharmony_ci	}
471762306a36Sopenharmony_ci
471862306a36Sopenharmony_ci	return true;
471962306a36Sopenharmony_ci}
472062306a36Sopenharmony_ci
472162306a36Sopenharmony_cistatic bool mlxsw_sp_netdev_is_master(struct net_device *upper_dev,
472262306a36Sopenharmony_ci				      struct net_device *dev)
472362306a36Sopenharmony_ci{
472462306a36Sopenharmony_ci	return upper_dev == netdev_master_upper_dev_get(dev);
472562306a36Sopenharmony_ci}
472662306a36Sopenharmony_ci
472762306a36Sopenharmony_cistatic int __mlxsw_sp_netdevice_event(struct mlxsw_sp *mlxsw_sp,
472862306a36Sopenharmony_ci				      unsigned long event, void *ptr,
472962306a36Sopenharmony_ci				      bool process_foreign);
473062306a36Sopenharmony_ci
473162306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_validate_uppers(struct mlxsw_sp *mlxsw_sp,
473262306a36Sopenharmony_ci					      struct net_device *dev,
473362306a36Sopenharmony_ci					      struct netlink_ext_ack *extack)
473462306a36Sopenharmony_ci{
473562306a36Sopenharmony_ci	struct net_device *upper_dev;
473662306a36Sopenharmony_ci	struct list_head *iter;
473762306a36Sopenharmony_ci	int err;
473862306a36Sopenharmony_ci
473962306a36Sopenharmony_ci	netdev_for_each_upper_dev_rcu(dev, upper_dev, iter) {
474062306a36Sopenharmony_ci		struct netdev_notifier_changeupper_info info = {
474162306a36Sopenharmony_ci			.info = {
474262306a36Sopenharmony_ci				.dev = dev,
474362306a36Sopenharmony_ci				.extack = extack,
474462306a36Sopenharmony_ci			},
474562306a36Sopenharmony_ci			.master = mlxsw_sp_netdev_is_master(upper_dev, dev),
474662306a36Sopenharmony_ci			.upper_dev = upper_dev,
474762306a36Sopenharmony_ci			.linking = true,
474862306a36Sopenharmony_ci
474962306a36Sopenharmony_ci			/* upper_info is relevant for LAG devices. But we would
475062306a36Sopenharmony_ci			 * only need this if LAG were a valid upper above
475162306a36Sopenharmony_ci			 * another upper (e.g. a bridge that is a member of a
475262306a36Sopenharmony_ci			 * LAG), and that is never a valid configuration. So we
475362306a36Sopenharmony_ci			 * can keep this as NULL.
475462306a36Sopenharmony_ci			 */
475562306a36Sopenharmony_ci			.upper_info = NULL,
475662306a36Sopenharmony_ci		};
475762306a36Sopenharmony_ci
475862306a36Sopenharmony_ci		err = __mlxsw_sp_netdevice_event(mlxsw_sp,
475962306a36Sopenharmony_ci						 NETDEV_PRECHANGEUPPER,
476062306a36Sopenharmony_ci						 &info, true);
476162306a36Sopenharmony_ci		if (err)
476262306a36Sopenharmony_ci			return err;
476362306a36Sopenharmony_ci
476462306a36Sopenharmony_ci		err = mlxsw_sp_netdevice_validate_uppers(mlxsw_sp, upper_dev,
476562306a36Sopenharmony_ci							 extack);
476662306a36Sopenharmony_ci		if (err)
476762306a36Sopenharmony_ci			return err;
476862306a36Sopenharmony_ci	}
476962306a36Sopenharmony_ci
477062306a36Sopenharmony_ci	return 0;
477162306a36Sopenharmony_ci}
477262306a36Sopenharmony_ci
477362306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
477462306a36Sopenharmony_ci					       struct net_device *dev,
477562306a36Sopenharmony_ci					       unsigned long event, void *ptr,
477662306a36Sopenharmony_ci					       bool replay_deslavement)
477762306a36Sopenharmony_ci{
477862306a36Sopenharmony_ci	struct netdev_notifier_changeupper_info *info;
477962306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
478062306a36Sopenharmony_ci	struct netlink_ext_ack *extack;
478162306a36Sopenharmony_ci	struct net_device *upper_dev;
478262306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
478362306a36Sopenharmony_ci	int err = 0;
478462306a36Sopenharmony_ci	u16 proto;
478562306a36Sopenharmony_ci
478662306a36Sopenharmony_ci	mlxsw_sp_port = netdev_priv(dev);
478762306a36Sopenharmony_ci	mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
478862306a36Sopenharmony_ci	info = ptr;
478962306a36Sopenharmony_ci	extack = netdev_notifier_info_to_extack(&info->info);
479062306a36Sopenharmony_ci
479162306a36Sopenharmony_ci	switch (event) {
479262306a36Sopenharmony_ci	case NETDEV_PRECHANGEUPPER:
479362306a36Sopenharmony_ci		upper_dev = info->upper_dev;
479462306a36Sopenharmony_ci		if (!is_vlan_dev(upper_dev) &&
479562306a36Sopenharmony_ci		    !netif_is_lag_master(upper_dev) &&
479662306a36Sopenharmony_ci		    !netif_is_bridge_master(upper_dev) &&
479762306a36Sopenharmony_ci		    !netif_is_ovs_master(upper_dev) &&
479862306a36Sopenharmony_ci		    !netif_is_macvlan(upper_dev) &&
479962306a36Sopenharmony_ci		    !netif_is_l3_master(upper_dev)) {
480062306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
480162306a36Sopenharmony_ci			return -EINVAL;
480262306a36Sopenharmony_ci		}
480362306a36Sopenharmony_ci		if (!info->linking)
480462306a36Sopenharmony_ci			break;
480562306a36Sopenharmony_ci		if (netif_is_bridge_master(upper_dev) &&
480662306a36Sopenharmony_ci		    !mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp, upper_dev) &&
480762306a36Sopenharmony_ci		    mlxsw_sp_bridge_has_vxlan(upper_dev) &&
480862306a36Sopenharmony_ci		    !mlxsw_sp_bridge_vxlan_is_valid(upper_dev, extack))
480962306a36Sopenharmony_ci			return -EOPNOTSUPP;
481062306a36Sopenharmony_ci		if (netdev_has_any_upper_dev(upper_dev) &&
481162306a36Sopenharmony_ci		    (!netif_is_bridge_master(upper_dev) ||
481262306a36Sopenharmony_ci		     !mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp,
481362306a36Sopenharmony_ci							  upper_dev))) {
481462306a36Sopenharmony_ci			err = mlxsw_sp_netdevice_validate_uppers(mlxsw_sp,
481562306a36Sopenharmony_ci								 upper_dev,
481662306a36Sopenharmony_ci								 extack);
481762306a36Sopenharmony_ci			if (err)
481862306a36Sopenharmony_ci				return err;
481962306a36Sopenharmony_ci		}
482062306a36Sopenharmony_ci		if (netif_is_lag_master(upper_dev) &&
482162306a36Sopenharmony_ci		    !mlxsw_sp_master_lag_check(mlxsw_sp, upper_dev,
482262306a36Sopenharmony_ci					       info->upper_info, extack))
482362306a36Sopenharmony_ci			return -EINVAL;
482462306a36Sopenharmony_ci		if (netif_is_lag_master(upper_dev) && vlan_uses_dev(dev)) {
482562306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Master device is a LAG master and this device has a VLAN");
482662306a36Sopenharmony_ci			return -EINVAL;
482762306a36Sopenharmony_ci		}
482862306a36Sopenharmony_ci		if (netif_is_lag_port(dev) && is_vlan_dev(upper_dev) &&
482962306a36Sopenharmony_ci		    !netif_is_lag_master(vlan_dev_real_dev(upper_dev))) {
483062306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Can not put a VLAN on a LAG port");
483162306a36Sopenharmony_ci			return -EINVAL;
483262306a36Sopenharmony_ci		}
483362306a36Sopenharmony_ci		if (netif_is_ovs_master(upper_dev) && vlan_uses_dev(dev)) {
483462306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Master device is an OVS master and this device has a VLAN");
483562306a36Sopenharmony_ci			return -EINVAL;
483662306a36Sopenharmony_ci		}
483762306a36Sopenharmony_ci		if (netif_is_ovs_port(dev) && is_vlan_dev(upper_dev)) {
483862306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Can not put a VLAN on an OVS port");
483962306a36Sopenharmony_ci			return -EINVAL;
484062306a36Sopenharmony_ci		}
484162306a36Sopenharmony_ci		if (netif_is_bridge_master(upper_dev)) {
484262306a36Sopenharmony_ci			br_vlan_get_proto(upper_dev, &proto);
484362306a36Sopenharmony_ci			if (br_vlan_enabled(upper_dev) &&
484462306a36Sopenharmony_ci			    proto != ETH_P_8021Q && proto != ETH_P_8021AD) {
484562306a36Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Enslaving a port to a bridge with unknown VLAN protocol is not supported");
484662306a36Sopenharmony_ci				return -EOPNOTSUPP;
484762306a36Sopenharmony_ci			}
484862306a36Sopenharmony_ci			if (vlan_uses_dev(lower_dev) &&
484962306a36Sopenharmony_ci			    br_vlan_enabled(upper_dev) &&
485062306a36Sopenharmony_ci			    proto == ETH_P_8021AD) {
485162306a36Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Enslaving a port that already has a VLAN upper to an 802.1ad bridge is not supported");
485262306a36Sopenharmony_ci				return -EOPNOTSUPP;
485362306a36Sopenharmony_ci			}
485462306a36Sopenharmony_ci		}
485562306a36Sopenharmony_ci		if (netif_is_bridge_port(lower_dev) && is_vlan_dev(upper_dev)) {
485662306a36Sopenharmony_ci			struct net_device *br_dev = netdev_master_upper_dev_get(lower_dev);
485762306a36Sopenharmony_ci
485862306a36Sopenharmony_ci			if (br_vlan_enabled(br_dev)) {
485962306a36Sopenharmony_ci				br_vlan_get_proto(br_dev, &proto);
486062306a36Sopenharmony_ci				if (proto == ETH_P_8021AD) {
486162306a36Sopenharmony_ci					NL_SET_ERR_MSG_MOD(extack, "VLAN uppers are not supported on a port enslaved to an 802.1ad bridge");
486262306a36Sopenharmony_ci					return -EOPNOTSUPP;
486362306a36Sopenharmony_ci				}
486462306a36Sopenharmony_ci			}
486562306a36Sopenharmony_ci		}
486662306a36Sopenharmony_ci		if (is_vlan_dev(upper_dev) &&
486762306a36Sopenharmony_ci		    ntohs(vlan_dev_vlan_proto(upper_dev)) != ETH_P_8021Q) {
486862306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "VLAN uppers are only supported with 802.1q VLAN protocol");
486962306a36Sopenharmony_ci			return -EOPNOTSUPP;
487062306a36Sopenharmony_ci		}
487162306a36Sopenharmony_ci		if (is_vlan_dev(upper_dev) && mlxsw_sp_port->security) {
487262306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "VLAN uppers are not supported on a locked port");
487362306a36Sopenharmony_ci			return -EOPNOTSUPP;
487462306a36Sopenharmony_ci		}
487562306a36Sopenharmony_ci		break;
487662306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
487762306a36Sopenharmony_ci		upper_dev = info->upper_dev;
487862306a36Sopenharmony_ci		if (netif_is_bridge_master(upper_dev)) {
487962306a36Sopenharmony_ci			if (info->linking) {
488062306a36Sopenharmony_ci				err = mlxsw_sp_port_bridge_join(mlxsw_sp_port,
488162306a36Sopenharmony_ci								lower_dev,
488262306a36Sopenharmony_ci								upper_dev,
488362306a36Sopenharmony_ci								extack);
488462306a36Sopenharmony_ci			} else {
488562306a36Sopenharmony_ci				mlxsw_sp_port_bridge_leave(mlxsw_sp_port,
488662306a36Sopenharmony_ci							   lower_dev,
488762306a36Sopenharmony_ci							   upper_dev);
488862306a36Sopenharmony_ci				if (!replay_deslavement)
488962306a36Sopenharmony_ci					break;
489062306a36Sopenharmony_ci				mlxsw_sp_netdevice_deslavement_replay(mlxsw_sp,
489162306a36Sopenharmony_ci								      lower_dev);
489262306a36Sopenharmony_ci			}
489362306a36Sopenharmony_ci		} else if (netif_is_lag_master(upper_dev)) {
489462306a36Sopenharmony_ci			if (info->linking) {
489562306a36Sopenharmony_ci				err = mlxsw_sp_port_lag_join(mlxsw_sp_port,
489662306a36Sopenharmony_ci							     upper_dev, extack);
489762306a36Sopenharmony_ci			} else {
489862306a36Sopenharmony_ci				mlxsw_sp_port_lag_col_dist_disable(mlxsw_sp_port);
489962306a36Sopenharmony_ci				mlxsw_sp_port_lag_leave(mlxsw_sp_port,
490062306a36Sopenharmony_ci							upper_dev);
490162306a36Sopenharmony_ci				mlxsw_sp_netdevice_deslavement_replay(mlxsw_sp,
490262306a36Sopenharmony_ci								      dev);
490362306a36Sopenharmony_ci			}
490462306a36Sopenharmony_ci		} else if (netif_is_ovs_master(upper_dev)) {
490562306a36Sopenharmony_ci			if (info->linking)
490662306a36Sopenharmony_ci				err = mlxsw_sp_port_ovs_join(mlxsw_sp_port);
490762306a36Sopenharmony_ci			else
490862306a36Sopenharmony_ci				mlxsw_sp_port_ovs_leave(mlxsw_sp_port);
490962306a36Sopenharmony_ci		} else if (netif_is_macvlan(upper_dev)) {
491062306a36Sopenharmony_ci			if (!info->linking)
491162306a36Sopenharmony_ci				mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
491262306a36Sopenharmony_ci		} else if (is_vlan_dev(upper_dev)) {
491362306a36Sopenharmony_ci			struct net_device *br_dev;
491462306a36Sopenharmony_ci
491562306a36Sopenharmony_ci			if (!netif_is_bridge_port(upper_dev))
491662306a36Sopenharmony_ci				break;
491762306a36Sopenharmony_ci			if (info->linking)
491862306a36Sopenharmony_ci				break;
491962306a36Sopenharmony_ci			br_dev = netdev_master_upper_dev_get(upper_dev);
492062306a36Sopenharmony_ci			mlxsw_sp_port_bridge_leave(mlxsw_sp_port, upper_dev,
492162306a36Sopenharmony_ci						   br_dev);
492262306a36Sopenharmony_ci		}
492362306a36Sopenharmony_ci		break;
492462306a36Sopenharmony_ci	}
492562306a36Sopenharmony_ci
492662306a36Sopenharmony_ci	return err;
492762306a36Sopenharmony_ci}
492862306a36Sopenharmony_ci
492962306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_port_lower_event(struct net_device *dev,
493062306a36Sopenharmony_ci					       unsigned long event, void *ptr)
493162306a36Sopenharmony_ci{
493262306a36Sopenharmony_ci	struct netdev_notifier_changelowerstate_info *info;
493362306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
493462306a36Sopenharmony_ci	int err;
493562306a36Sopenharmony_ci
493662306a36Sopenharmony_ci	mlxsw_sp_port = netdev_priv(dev);
493762306a36Sopenharmony_ci	info = ptr;
493862306a36Sopenharmony_ci
493962306a36Sopenharmony_ci	switch (event) {
494062306a36Sopenharmony_ci	case NETDEV_CHANGELOWERSTATE:
494162306a36Sopenharmony_ci		if (netif_is_lag_port(dev) && mlxsw_sp_port->lagged) {
494262306a36Sopenharmony_ci			err = mlxsw_sp_port_lag_changed(mlxsw_sp_port,
494362306a36Sopenharmony_ci							info->lower_state_info);
494462306a36Sopenharmony_ci			if (err)
494562306a36Sopenharmony_ci				netdev_err(dev, "Failed to reflect link aggregation lower state change\n");
494662306a36Sopenharmony_ci		}
494762306a36Sopenharmony_ci		break;
494862306a36Sopenharmony_ci	}
494962306a36Sopenharmony_ci
495062306a36Sopenharmony_ci	return 0;
495162306a36Sopenharmony_ci}
495262306a36Sopenharmony_ci
495362306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_port_event(struct net_device *lower_dev,
495462306a36Sopenharmony_ci					 struct net_device *port_dev,
495562306a36Sopenharmony_ci					 unsigned long event, void *ptr,
495662306a36Sopenharmony_ci					 bool replay_deslavement)
495762306a36Sopenharmony_ci{
495862306a36Sopenharmony_ci	switch (event) {
495962306a36Sopenharmony_ci	case NETDEV_PRECHANGEUPPER:
496062306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
496162306a36Sopenharmony_ci		return mlxsw_sp_netdevice_port_upper_event(lower_dev, port_dev,
496262306a36Sopenharmony_ci							   event, ptr,
496362306a36Sopenharmony_ci							   replay_deslavement);
496462306a36Sopenharmony_ci	case NETDEV_CHANGELOWERSTATE:
496562306a36Sopenharmony_ci		return mlxsw_sp_netdevice_port_lower_event(port_dev, event,
496662306a36Sopenharmony_ci							   ptr);
496762306a36Sopenharmony_ci	}
496862306a36Sopenharmony_ci
496962306a36Sopenharmony_ci	return 0;
497062306a36Sopenharmony_ci}
497162306a36Sopenharmony_ci
497262306a36Sopenharmony_ci/* Called for LAG or its upper VLAN after the per-LAG-lower processing was done,
497362306a36Sopenharmony_ci * to do any per-LAG / per-LAG-upper processing.
497462306a36Sopenharmony_ci */
497562306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_post_lag_event(struct net_device *dev,
497662306a36Sopenharmony_ci					     unsigned long event,
497762306a36Sopenharmony_ci					     void *ptr)
497862306a36Sopenharmony_ci{
497962306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(dev);
498062306a36Sopenharmony_ci	struct netdev_notifier_changeupper_info *info = ptr;
498162306a36Sopenharmony_ci
498262306a36Sopenharmony_ci	if (!mlxsw_sp)
498362306a36Sopenharmony_ci		return 0;
498462306a36Sopenharmony_ci
498562306a36Sopenharmony_ci	switch (event) {
498662306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
498762306a36Sopenharmony_ci		if (info->linking)
498862306a36Sopenharmony_ci			break;
498962306a36Sopenharmony_ci		if (netif_is_bridge_master(info->upper_dev))
499062306a36Sopenharmony_ci			mlxsw_sp_netdevice_deslavement_replay(mlxsw_sp, dev);
499162306a36Sopenharmony_ci		break;
499262306a36Sopenharmony_ci	}
499362306a36Sopenharmony_ci	return 0;
499462306a36Sopenharmony_ci}
499562306a36Sopenharmony_ci
499662306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_lag_event(struct net_device *lag_dev,
499762306a36Sopenharmony_ci					unsigned long event, void *ptr)
499862306a36Sopenharmony_ci{
499962306a36Sopenharmony_ci	struct net_device *dev;
500062306a36Sopenharmony_ci	struct list_head *iter;
500162306a36Sopenharmony_ci	int ret;
500262306a36Sopenharmony_ci
500362306a36Sopenharmony_ci	netdev_for_each_lower_dev(lag_dev, dev, iter) {
500462306a36Sopenharmony_ci		if (mlxsw_sp_port_dev_check(dev)) {
500562306a36Sopenharmony_ci			ret = mlxsw_sp_netdevice_port_event(lag_dev, dev, event,
500662306a36Sopenharmony_ci							    ptr, false);
500762306a36Sopenharmony_ci			if (ret)
500862306a36Sopenharmony_ci				return ret;
500962306a36Sopenharmony_ci		}
501062306a36Sopenharmony_ci	}
501162306a36Sopenharmony_ci
501262306a36Sopenharmony_ci	return mlxsw_sp_netdevice_post_lag_event(lag_dev, event, ptr);
501362306a36Sopenharmony_ci}
501462306a36Sopenharmony_ci
501562306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
501662306a36Sopenharmony_ci					      struct net_device *dev,
501762306a36Sopenharmony_ci					      unsigned long event, void *ptr,
501862306a36Sopenharmony_ci					      u16 vid, bool replay_deslavement)
501962306a36Sopenharmony_ci{
502062306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
502162306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
502262306a36Sopenharmony_ci	struct netdev_notifier_changeupper_info *info = ptr;
502362306a36Sopenharmony_ci	struct netlink_ext_ack *extack;
502462306a36Sopenharmony_ci	struct net_device *upper_dev;
502562306a36Sopenharmony_ci	int err = 0;
502662306a36Sopenharmony_ci
502762306a36Sopenharmony_ci	extack = netdev_notifier_info_to_extack(&info->info);
502862306a36Sopenharmony_ci
502962306a36Sopenharmony_ci	switch (event) {
503062306a36Sopenharmony_ci	case NETDEV_PRECHANGEUPPER:
503162306a36Sopenharmony_ci		upper_dev = info->upper_dev;
503262306a36Sopenharmony_ci		if (!netif_is_bridge_master(upper_dev) &&
503362306a36Sopenharmony_ci		    !netif_is_macvlan(upper_dev) &&
503462306a36Sopenharmony_ci		    !netif_is_l3_master(upper_dev)) {
503562306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
503662306a36Sopenharmony_ci			return -EINVAL;
503762306a36Sopenharmony_ci		}
503862306a36Sopenharmony_ci		if (!info->linking)
503962306a36Sopenharmony_ci			break;
504062306a36Sopenharmony_ci		if (netif_is_bridge_master(upper_dev) &&
504162306a36Sopenharmony_ci		    !mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp, upper_dev) &&
504262306a36Sopenharmony_ci		    mlxsw_sp_bridge_has_vxlan(upper_dev) &&
504362306a36Sopenharmony_ci		    !mlxsw_sp_bridge_vxlan_is_valid(upper_dev, extack))
504462306a36Sopenharmony_ci			return -EOPNOTSUPP;
504562306a36Sopenharmony_ci		if (netdev_has_any_upper_dev(upper_dev) &&
504662306a36Sopenharmony_ci		    (!netif_is_bridge_master(upper_dev) ||
504762306a36Sopenharmony_ci		     !mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp,
504862306a36Sopenharmony_ci							  upper_dev))) {
504962306a36Sopenharmony_ci			err = mlxsw_sp_netdevice_validate_uppers(mlxsw_sp,
505062306a36Sopenharmony_ci								 upper_dev,
505162306a36Sopenharmony_ci								 extack);
505262306a36Sopenharmony_ci			if (err)
505362306a36Sopenharmony_ci				return err;
505462306a36Sopenharmony_ci		}
505562306a36Sopenharmony_ci		break;
505662306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
505762306a36Sopenharmony_ci		upper_dev = info->upper_dev;
505862306a36Sopenharmony_ci		if (netif_is_bridge_master(upper_dev)) {
505962306a36Sopenharmony_ci			if (info->linking) {
506062306a36Sopenharmony_ci				err = mlxsw_sp_port_bridge_join(mlxsw_sp_port,
506162306a36Sopenharmony_ci								vlan_dev,
506262306a36Sopenharmony_ci								upper_dev,
506362306a36Sopenharmony_ci								extack);
506462306a36Sopenharmony_ci			} else {
506562306a36Sopenharmony_ci				mlxsw_sp_port_bridge_leave(mlxsw_sp_port,
506662306a36Sopenharmony_ci							   vlan_dev,
506762306a36Sopenharmony_ci							   upper_dev);
506862306a36Sopenharmony_ci				if (!replay_deslavement)
506962306a36Sopenharmony_ci					break;
507062306a36Sopenharmony_ci				mlxsw_sp_netdevice_deslavement_replay(mlxsw_sp,
507162306a36Sopenharmony_ci								      vlan_dev);
507262306a36Sopenharmony_ci			}
507362306a36Sopenharmony_ci		} else if (netif_is_macvlan(upper_dev)) {
507462306a36Sopenharmony_ci			if (!info->linking)
507562306a36Sopenharmony_ci				mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
507662306a36Sopenharmony_ci		}
507762306a36Sopenharmony_ci		break;
507862306a36Sopenharmony_ci	}
507962306a36Sopenharmony_ci
508062306a36Sopenharmony_ci	return err;
508162306a36Sopenharmony_ci}
508262306a36Sopenharmony_ci
508362306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_lag_port_vlan_event(struct net_device *vlan_dev,
508462306a36Sopenharmony_ci						  struct net_device *lag_dev,
508562306a36Sopenharmony_ci						  unsigned long event,
508662306a36Sopenharmony_ci						  void *ptr, u16 vid)
508762306a36Sopenharmony_ci{
508862306a36Sopenharmony_ci	struct net_device *dev;
508962306a36Sopenharmony_ci	struct list_head *iter;
509062306a36Sopenharmony_ci	int ret;
509162306a36Sopenharmony_ci
509262306a36Sopenharmony_ci	netdev_for_each_lower_dev(lag_dev, dev, iter) {
509362306a36Sopenharmony_ci		if (mlxsw_sp_port_dev_check(dev)) {
509462306a36Sopenharmony_ci			ret = mlxsw_sp_netdevice_port_vlan_event(vlan_dev, dev,
509562306a36Sopenharmony_ci								 event, ptr,
509662306a36Sopenharmony_ci								 vid, false);
509762306a36Sopenharmony_ci			if (ret)
509862306a36Sopenharmony_ci				return ret;
509962306a36Sopenharmony_ci		}
510062306a36Sopenharmony_ci	}
510162306a36Sopenharmony_ci
510262306a36Sopenharmony_ci	return mlxsw_sp_netdevice_post_lag_event(vlan_dev, event, ptr);
510362306a36Sopenharmony_ci}
510462306a36Sopenharmony_ci
510562306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_bridge_vlan_event(struct mlxsw_sp *mlxsw_sp,
510662306a36Sopenharmony_ci						struct net_device *vlan_dev,
510762306a36Sopenharmony_ci						struct net_device *br_dev,
510862306a36Sopenharmony_ci						unsigned long event, void *ptr,
510962306a36Sopenharmony_ci						u16 vid, bool process_foreign)
511062306a36Sopenharmony_ci{
511162306a36Sopenharmony_ci	struct netdev_notifier_changeupper_info *info = ptr;
511262306a36Sopenharmony_ci	struct netlink_ext_ack *extack;
511362306a36Sopenharmony_ci	struct net_device *upper_dev;
511462306a36Sopenharmony_ci
511562306a36Sopenharmony_ci	if (!process_foreign && !mlxsw_sp_lower_get(vlan_dev))
511662306a36Sopenharmony_ci		return 0;
511762306a36Sopenharmony_ci
511862306a36Sopenharmony_ci	extack = netdev_notifier_info_to_extack(&info->info);
511962306a36Sopenharmony_ci
512062306a36Sopenharmony_ci	switch (event) {
512162306a36Sopenharmony_ci	case NETDEV_PRECHANGEUPPER:
512262306a36Sopenharmony_ci		upper_dev = info->upper_dev;
512362306a36Sopenharmony_ci		if (!netif_is_macvlan(upper_dev) &&
512462306a36Sopenharmony_ci		    !netif_is_l3_master(upper_dev)) {
512562306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
512662306a36Sopenharmony_ci			return -EOPNOTSUPP;
512762306a36Sopenharmony_ci		}
512862306a36Sopenharmony_ci		break;
512962306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
513062306a36Sopenharmony_ci		upper_dev = info->upper_dev;
513162306a36Sopenharmony_ci		if (info->linking)
513262306a36Sopenharmony_ci			break;
513362306a36Sopenharmony_ci		if (netif_is_macvlan(upper_dev))
513462306a36Sopenharmony_ci			mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
513562306a36Sopenharmony_ci		break;
513662306a36Sopenharmony_ci	}
513762306a36Sopenharmony_ci
513862306a36Sopenharmony_ci	return 0;
513962306a36Sopenharmony_ci}
514062306a36Sopenharmony_ci
514162306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_vlan_event(struct mlxsw_sp *mlxsw_sp,
514262306a36Sopenharmony_ci					 struct net_device *vlan_dev,
514362306a36Sopenharmony_ci					 unsigned long event, void *ptr,
514462306a36Sopenharmony_ci					 bool process_foreign)
514562306a36Sopenharmony_ci{
514662306a36Sopenharmony_ci	struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
514762306a36Sopenharmony_ci	u16 vid = vlan_dev_vlan_id(vlan_dev);
514862306a36Sopenharmony_ci
514962306a36Sopenharmony_ci	if (mlxsw_sp_port_dev_check(real_dev))
515062306a36Sopenharmony_ci		return mlxsw_sp_netdevice_port_vlan_event(vlan_dev, real_dev,
515162306a36Sopenharmony_ci							  event, ptr, vid,
515262306a36Sopenharmony_ci							  true);
515362306a36Sopenharmony_ci	else if (netif_is_lag_master(real_dev))
515462306a36Sopenharmony_ci		return mlxsw_sp_netdevice_lag_port_vlan_event(vlan_dev,
515562306a36Sopenharmony_ci							      real_dev, event,
515662306a36Sopenharmony_ci							      ptr, vid);
515762306a36Sopenharmony_ci	else if (netif_is_bridge_master(real_dev))
515862306a36Sopenharmony_ci		return mlxsw_sp_netdevice_bridge_vlan_event(mlxsw_sp, vlan_dev,
515962306a36Sopenharmony_ci							    real_dev, event,
516062306a36Sopenharmony_ci							    ptr, vid,
516162306a36Sopenharmony_ci							    process_foreign);
516262306a36Sopenharmony_ci
516362306a36Sopenharmony_ci	return 0;
516462306a36Sopenharmony_ci}
516562306a36Sopenharmony_ci
516662306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_bridge_event(struct mlxsw_sp *mlxsw_sp,
516762306a36Sopenharmony_ci					   struct net_device *br_dev,
516862306a36Sopenharmony_ci					   unsigned long event, void *ptr,
516962306a36Sopenharmony_ci					   bool process_foreign)
517062306a36Sopenharmony_ci{
517162306a36Sopenharmony_ci	struct netdev_notifier_changeupper_info *info = ptr;
517262306a36Sopenharmony_ci	struct netlink_ext_ack *extack;
517362306a36Sopenharmony_ci	struct net_device *upper_dev;
517462306a36Sopenharmony_ci	u16 proto;
517562306a36Sopenharmony_ci
517662306a36Sopenharmony_ci	if (!process_foreign && !mlxsw_sp_lower_get(br_dev))
517762306a36Sopenharmony_ci		return 0;
517862306a36Sopenharmony_ci
517962306a36Sopenharmony_ci	extack = netdev_notifier_info_to_extack(&info->info);
518062306a36Sopenharmony_ci
518162306a36Sopenharmony_ci	switch (event) {
518262306a36Sopenharmony_ci	case NETDEV_PRECHANGEUPPER:
518362306a36Sopenharmony_ci		upper_dev = info->upper_dev;
518462306a36Sopenharmony_ci		if (!is_vlan_dev(upper_dev) &&
518562306a36Sopenharmony_ci		    !netif_is_macvlan(upper_dev) &&
518662306a36Sopenharmony_ci		    !netif_is_l3_master(upper_dev)) {
518762306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
518862306a36Sopenharmony_ci			return -EOPNOTSUPP;
518962306a36Sopenharmony_ci		}
519062306a36Sopenharmony_ci		if (!info->linking)
519162306a36Sopenharmony_ci			break;
519262306a36Sopenharmony_ci		if (br_vlan_enabled(br_dev)) {
519362306a36Sopenharmony_ci			br_vlan_get_proto(br_dev, &proto);
519462306a36Sopenharmony_ci			if (proto == ETH_P_8021AD) {
519562306a36Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Upper devices are not supported on top of an 802.1ad bridge");
519662306a36Sopenharmony_ci				return -EOPNOTSUPP;
519762306a36Sopenharmony_ci			}
519862306a36Sopenharmony_ci		}
519962306a36Sopenharmony_ci		if (is_vlan_dev(upper_dev) &&
520062306a36Sopenharmony_ci		    ntohs(vlan_dev_vlan_proto(upper_dev)) != ETH_P_8021Q) {
520162306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "VLAN uppers are only supported with 802.1q VLAN protocol");
520262306a36Sopenharmony_ci			return -EOPNOTSUPP;
520362306a36Sopenharmony_ci		}
520462306a36Sopenharmony_ci		break;
520562306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
520662306a36Sopenharmony_ci		upper_dev = info->upper_dev;
520762306a36Sopenharmony_ci		if (info->linking)
520862306a36Sopenharmony_ci			break;
520962306a36Sopenharmony_ci		if (is_vlan_dev(upper_dev))
521062306a36Sopenharmony_ci			mlxsw_sp_rif_destroy_by_dev(mlxsw_sp, upper_dev);
521162306a36Sopenharmony_ci		if (netif_is_macvlan(upper_dev))
521262306a36Sopenharmony_ci			mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
521362306a36Sopenharmony_ci		break;
521462306a36Sopenharmony_ci	}
521562306a36Sopenharmony_ci
521662306a36Sopenharmony_ci	return 0;
521762306a36Sopenharmony_ci}
521862306a36Sopenharmony_ci
521962306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_macvlan_event(struct net_device *macvlan_dev,
522062306a36Sopenharmony_ci					    unsigned long event, void *ptr)
522162306a36Sopenharmony_ci{
522262306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(macvlan_dev);
522362306a36Sopenharmony_ci	struct netdev_notifier_changeupper_info *info = ptr;
522462306a36Sopenharmony_ci	struct netlink_ext_ack *extack;
522562306a36Sopenharmony_ci	struct net_device *upper_dev;
522662306a36Sopenharmony_ci
522762306a36Sopenharmony_ci	if (!mlxsw_sp || event != NETDEV_PRECHANGEUPPER)
522862306a36Sopenharmony_ci		return 0;
522962306a36Sopenharmony_ci
523062306a36Sopenharmony_ci	extack = netdev_notifier_info_to_extack(&info->info);
523162306a36Sopenharmony_ci	upper_dev = info->upper_dev;
523262306a36Sopenharmony_ci
523362306a36Sopenharmony_ci	if (!netif_is_l3_master(upper_dev)) {
523462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
523562306a36Sopenharmony_ci		return -EOPNOTSUPP;
523662306a36Sopenharmony_ci	}
523762306a36Sopenharmony_ci
523862306a36Sopenharmony_ci	return 0;
523962306a36Sopenharmony_ci}
524062306a36Sopenharmony_ci
524162306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_vxlan_event(struct mlxsw_sp *mlxsw_sp,
524262306a36Sopenharmony_ci					  struct net_device *dev,
524362306a36Sopenharmony_ci					  unsigned long event, void *ptr)
524462306a36Sopenharmony_ci{
524562306a36Sopenharmony_ci	struct netdev_notifier_changeupper_info *cu_info;
524662306a36Sopenharmony_ci	struct netdev_notifier_info *info = ptr;
524762306a36Sopenharmony_ci	struct netlink_ext_ack *extack;
524862306a36Sopenharmony_ci	struct net_device *upper_dev;
524962306a36Sopenharmony_ci
525062306a36Sopenharmony_ci	extack = netdev_notifier_info_to_extack(info);
525162306a36Sopenharmony_ci
525262306a36Sopenharmony_ci	switch (event) {
525362306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
525462306a36Sopenharmony_ci		cu_info = container_of(info,
525562306a36Sopenharmony_ci				       struct netdev_notifier_changeupper_info,
525662306a36Sopenharmony_ci				       info);
525762306a36Sopenharmony_ci		upper_dev = cu_info->upper_dev;
525862306a36Sopenharmony_ci		if (!netif_is_bridge_master(upper_dev))
525962306a36Sopenharmony_ci			return 0;
526062306a36Sopenharmony_ci		if (!mlxsw_sp_lower_get(upper_dev))
526162306a36Sopenharmony_ci			return 0;
526262306a36Sopenharmony_ci		if (!mlxsw_sp_bridge_vxlan_is_valid(upper_dev, extack))
526362306a36Sopenharmony_ci			return -EOPNOTSUPP;
526462306a36Sopenharmony_ci		if (cu_info->linking) {
526562306a36Sopenharmony_ci			if (!netif_running(dev))
526662306a36Sopenharmony_ci				return 0;
526762306a36Sopenharmony_ci			/* When the bridge is VLAN-aware, the VNI of the VxLAN
526862306a36Sopenharmony_ci			 * device needs to be mapped to a VLAN, but at this
526962306a36Sopenharmony_ci			 * point no VLANs are configured on the VxLAN device
527062306a36Sopenharmony_ci			 */
527162306a36Sopenharmony_ci			if (br_vlan_enabled(upper_dev))
527262306a36Sopenharmony_ci				return 0;
527362306a36Sopenharmony_ci			return mlxsw_sp_bridge_vxlan_join(mlxsw_sp, upper_dev,
527462306a36Sopenharmony_ci							  dev, 0, extack);
527562306a36Sopenharmony_ci		} else {
527662306a36Sopenharmony_ci			/* VLANs were already flushed, which triggered the
527762306a36Sopenharmony_ci			 * necessary cleanup
527862306a36Sopenharmony_ci			 */
527962306a36Sopenharmony_ci			if (br_vlan_enabled(upper_dev))
528062306a36Sopenharmony_ci				return 0;
528162306a36Sopenharmony_ci			mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, dev);
528262306a36Sopenharmony_ci		}
528362306a36Sopenharmony_ci		break;
528462306a36Sopenharmony_ci	case NETDEV_PRE_UP:
528562306a36Sopenharmony_ci		upper_dev = netdev_master_upper_dev_get(dev);
528662306a36Sopenharmony_ci		if (!upper_dev)
528762306a36Sopenharmony_ci			return 0;
528862306a36Sopenharmony_ci		if (!netif_is_bridge_master(upper_dev))
528962306a36Sopenharmony_ci			return 0;
529062306a36Sopenharmony_ci		if (!mlxsw_sp_lower_get(upper_dev))
529162306a36Sopenharmony_ci			return 0;
529262306a36Sopenharmony_ci		return mlxsw_sp_bridge_vxlan_join(mlxsw_sp, upper_dev, dev, 0,
529362306a36Sopenharmony_ci						  extack);
529462306a36Sopenharmony_ci	case NETDEV_DOWN:
529562306a36Sopenharmony_ci		upper_dev = netdev_master_upper_dev_get(dev);
529662306a36Sopenharmony_ci		if (!upper_dev)
529762306a36Sopenharmony_ci			return 0;
529862306a36Sopenharmony_ci		if (!netif_is_bridge_master(upper_dev))
529962306a36Sopenharmony_ci			return 0;
530062306a36Sopenharmony_ci		if (!mlxsw_sp_lower_get(upper_dev))
530162306a36Sopenharmony_ci			return 0;
530262306a36Sopenharmony_ci		mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, dev);
530362306a36Sopenharmony_ci		break;
530462306a36Sopenharmony_ci	}
530562306a36Sopenharmony_ci
530662306a36Sopenharmony_ci	return 0;
530762306a36Sopenharmony_ci}
530862306a36Sopenharmony_ci
530962306a36Sopenharmony_cistatic int __mlxsw_sp_netdevice_event(struct mlxsw_sp *mlxsw_sp,
531062306a36Sopenharmony_ci				      unsigned long event, void *ptr,
531162306a36Sopenharmony_ci				      bool process_foreign)
531262306a36Sopenharmony_ci{
531362306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
531462306a36Sopenharmony_ci	struct mlxsw_sp_span_entry *span_entry;
531562306a36Sopenharmony_ci	int err = 0;
531662306a36Sopenharmony_ci
531762306a36Sopenharmony_ci	if (event == NETDEV_UNREGISTER) {
531862306a36Sopenharmony_ci		span_entry = mlxsw_sp_span_entry_find_by_port(mlxsw_sp, dev);
531962306a36Sopenharmony_ci		if (span_entry)
532062306a36Sopenharmony_ci			mlxsw_sp_span_entry_invalidate(mlxsw_sp, span_entry);
532162306a36Sopenharmony_ci	}
532262306a36Sopenharmony_ci
532362306a36Sopenharmony_ci	if (netif_is_vxlan(dev))
532462306a36Sopenharmony_ci		err = mlxsw_sp_netdevice_vxlan_event(mlxsw_sp, dev, event, ptr);
532562306a36Sopenharmony_ci	else if (mlxsw_sp_port_dev_check(dev))
532662306a36Sopenharmony_ci		err = mlxsw_sp_netdevice_port_event(dev, dev, event, ptr, true);
532762306a36Sopenharmony_ci	else if (netif_is_lag_master(dev))
532862306a36Sopenharmony_ci		err = mlxsw_sp_netdevice_lag_event(dev, event, ptr);
532962306a36Sopenharmony_ci	else if (is_vlan_dev(dev))
533062306a36Sopenharmony_ci		err = mlxsw_sp_netdevice_vlan_event(mlxsw_sp, dev, event, ptr,
533162306a36Sopenharmony_ci						    process_foreign);
533262306a36Sopenharmony_ci	else if (netif_is_bridge_master(dev))
533362306a36Sopenharmony_ci		err = mlxsw_sp_netdevice_bridge_event(mlxsw_sp, dev, event, ptr,
533462306a36Sopenharmony_ci						      process_foreign);
533562306a36Sopenharmony_ci	else if (netif_is_macvlan(dev))
533662306a36Sopenharmony_ci		err = mlxsw_sp_netdevice_macvlan_event(dev, event, ptr);
533762306a36Sopenharmony_ci
533862306a36Sopenharmony_ci	return err;
533962306a36Sopenharmony_ci}
534062306a36Sopenharmony_ci
534162306a36Sopenharmony_cistatic int mlxsw_sp_netdevice_event(struct notifier_block *nb,
534262306a36Sopenharmony_ci				    unsigned long event, void *ptr)
534362306a36Sopenharmony_ci{
534462306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
534562306a36Sopenharmony_ci	int err;
534662306a36Sopenharmony_ci
534762306a36Sopenharmony_ci	mlxsw_sp = container_of(nb, struct mlxsw_sp, netdevice_nb);
534862306a36Sopenharmony_ci	mlxsw_sp_span_respin(mlxsw_sp);
534962306a36Sopenharmony_ci	err = __mlxsw_sp_netdevice_event(mlxsw_sp, event, ptr, false);
535062306a36Sopenharmony_ci
535162306a36Sopenharmony_ci	return notifier_from_errno(err);
535262306a36Sopenharmony_ci}
535362306a36Sopenharmony_ci
535462306a36Sopenharmony_cistatic const struct pci_device_id mlxsw_sp1_pci_id_table[] = {
535562306a36Sopenharmony_ci	{PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM), 0},
535662306a36Sopenharmony_ci	{0, },
535762306a36Sopenharmony_ci};
535862306a36Sopenharmony_ci
535962306a36Sopenharmony_cistatic struct pci_driver mlxsw_sp1_pci_driver = {
536062306a36Sopenharmony_ci	.name = mlxsw_sp1_driver_name,
536162306a36Sopenharmony_ci	.id_table = mlxsw_sp1_pci_id_table,
536262306a36Sopenharmony_ci};
536362306a36Sopenharmony_ci
536462306a36Sopenharmony_cistatic const struct pci_device_id mlxsw_sp2_pci_id_table[] = {
536562306a36Sopenharmony_ci	{PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM2), 0},
536662306a36Sopenharmony_ci	{0, },
536762306a36Sopenharmony_ci};
536862306a36Sopenharmony_ci
536962306a36Sopenharmony_cistatic struct pci_driver mlxsw_sp2_pci_driver = {
537062306a36Sopenharmony_ci	.name = mlxsw_sp2_driver_name,
537162306a36Sopenharmony_ci	.id_table = mlxsw_sp2_pci_id_table,
537262306a36Sopenharmony_ci};
537362306a36Sopenharmony_ci
537462306a36Sopenharmony_cistatic const struct pci_device_id mlxsw_sp3_pci_id_table[] = {
537562306a36Sopenharmony_ci	{PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM3), 0},
537662306a36Sopenharmony_ci	{0, },
537762306a36Sopenharmony_ci};
537862306a36Sopenharmony_ci
537962306a36Sopenharmony_cistatic struct pci_driver mlxsw_sp3_pci_driver = {
538062306a36Sopenharmony_ci	.name = mlxsw_sp3_driver_name,
538162306a36Sopenharmony_ci	.id_table = mlxsw_sp3_pci_id_table,
538262306a36Sopenharmony_ci};
538362306a36Sopenharmony_ci
538462306a36Sopenharmony_cistatic const struct pci_device_id mlxsw_sp4_pci_id_table[] = {
538562306a36Sopenharmony_ci	{PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM4), 0},
538662306a36Sopenharmony_ci	{0, },
538762306a36Sopenharmony_ci};
538862306a36Sopenharmony_ci
538962306a36Sopenharmony_cistatic struct pci_driver mlxsw_sp4_pci_driver = {
539062306a36Sopenharmony_ci	.name = mlxsw_sp4_driver_name,
539162306a36Sopenharmony_ci	.id_table = mlxsw_sp4_pci_id_table,
539262306a36Sopenharmony_ci};
539362306a36Sopenharmony_ci
539462306a36Sopenharmony_cistatic int __init mlxsw_sp_module_init(void)
539562306a36Sopenharmony_ci{
539662306a36Sopenharmony_ci	int err;
539762306a36Sopenharmony_ci
539862306a36Sopenharmony_ci	err = mlxsw_core_driver_register(&mlxsw_sp1_driver);
539962306a36Sopenharmony_ci	if (err)
540062306a36Sopenharmony_ci		return err;
540162306a36Sopenharmony_ci
540262306a36Sopenharmony_ci	err = mlxsw_core_driver_register(&mlxsw_sp2_driver);
540362306a36Sopenharmony_ci	if (err)
540462306a36Sopenharmony_ci		goto err_sp2_core_driver_register;
540562306a36Sopenharmony_ci
540662306a36Sopenharmony_ci	err = mlxsw_core_driver_register(&mlxsw_sp3_driver);
540762306a36Sopenharmony_ci	if (err)
540862306a36Sopenharmony_ci		goto err_sp3_core_driver_register;
540962306a36Sopenharmony_ci
541062306a36Sopenharmony_ci	err = mlxsw_core_driver_register(&mlxsw_sp4_driver);
541162306a36Sopenharmony_ci	if (err)
541262306a36Sopenharmony_ci		goto err_sp4_core_driver_register;
541362306a36Sopenharmony_ci
541462306a36Sopenharmony_ci	err = mlxsw_pci_driver_register(&mlxsw_sp1_pci_driver);
541562306a36Sopenharmony_ci	if (err)
541662306a36Sopenharmony_ci		goto err_sp1_pci_driver_register;
541762306a36Sopenharmony_ci
541862306a36Sopenharmony_ci	err = mlxsw_pci_driver_register(&mlxsw_sp2_pci_driver);
541962306a36Sopenharmony_ci	if (err)
542062306a36Sopenharmony_ci		goto err_sp2_pci_driver_register;
542162306a36Sopenharmony_ci
542262306a36Sopenharmony_ci	err = mlxsw_pci_driver_register(&mlxsw_sp3_pci_driver);
542362306a36Sopenharmony_ci	if (err)
542462306a36Sopenharmony_ci		goto err_sp3_pci_driver_register;
542562306a36Sopenharmony_ci
542662306a36Sopenharmony_ci	err = mlxsw_pci_driver_register(&mlxsw_sp4_pci_driver);
542762306a36Sopenharmony_ci	if (err)
542862306a36Sopenharmony_ci		goto err_sp4_pci_driver_register;
542962306a36Sopenharmony_ci
543062306a36Sopenharmony_ci	return 0;
543162306a36Sopenharmony_ci
543262306a36Sopenharmony_cierr_sp4_pci_driver_register:
543362306a36Sopenharmony_ci	mlxsw_pci_driver_unregister(&mlxsw_sp3_pci_driver);
543462306a36Sopenharmony_cierr_sp3_pci_driver_register:
543562306a36Sopenharmony_ci	mlxsw_pci_driver_unregister(&mlxsw_sp2_pci_driver);
543662306a36Sopenharmony_cierr_sp2_pci_driver_register:
543762306a36Sopenharmony_ci	mlxsw_pci_driver_unregister(&mlxsw_sp1_pci_driver);
543862306a36Sopenharmony_cierr_sp1_pci_driver_register:
543962306a36Sopenharmony_ci	mlxsw_core_driver_unregister(&mlxsw_sp4_driver);
544062306a36Sopenharmony_cierr_sp4_core_driver_register:
544162306a36Sopenharmony_ci	mlxsw_core_driver_unregister(&mlxsw_sp3_driver);
544262306a36Sopenharmony_cierr_sp3_core_driver_register:
544362306a36Sopenharmony_ci	mlxsw_core_driver_unregister(&mlxsw_sp2_driver);
544462306a36Sopenharmony_cierr_sp2_core_driver_register:
544562306a36Sopenharmony_ci	mlxsw_core_driver_unregister(&mlxsw_sp1_driver);
544662306a36Sopenharmony_ci	return err;
544762306a36Sopenharmony_ci}
544862306a36Sopenharmony_ci
544962306a36Sopenharmony_cistatic void __exit mlxsw_sp_module_exit(void)
545062306a36Sopenharmony_ci{
545162306a36Sopenharmony_ci	mlxsw_pci_driver_unregister(&mlxsw_sp4_pci_driver);
545262306a36Sopenharmony_ci	mlxsw_pci_driver_unregister(&mlxsw_sp3_pci_driver);
545362306a36Sopenharmony_ci	mlxsw_pci_driver_unregister(&mlxsw_sp2_pci_driver);
545462306a36Sopenharmony_ci	mlxsw_pci_driver_unregister(&mlxsw_sp1_pci_driver);
545562306a36Sopenharmony_ci	mlxsw_core_driver_unregister(&mlxsw_sp4_driver);
545662306a36Sopenharmony_ci	mlxsw_core_driver_unregister(&mlxsw_sp3_driver);
545762306a36Sopenharmony_ci	mlxsw_core_driver_unregister(&mlxsw_sp2_driver);
545862306a36Sopenharmony_ci	mlxsw_core_driver_unregister(&mlxsw_sp1_driver);
545962306a36Sopenharmony_ci}
546062306a36Sopenharmony_ci
546162306a36Sopenharmony_cimodule_init(mlxsw_sp_module_init);
546262306a36Sopenharmony_cimodule_exit(mlxsw_sp_module_exit);
546362306a36Sopenharmony_ci
546462306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
546562306a36Sopenharmony_ciMODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
546662306a36Sopenharmony_ciMODULE_DESCRIPTION("Mellanox Spectrum driver");
546762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mlxsw_sp1_pci_id_table);
546862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mlxsw_sp2_pci_id_table);
546962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mlxsw_sp3_pci_id_table);
547062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mlxsw_sp4_pci_id_table);
547162306a36Sopenharmony_ciMODULE_FIRMWARE(MLXSW_SP1_FW_FILENAME);
547262306a36Sopenharmony_ciMODULE_FIRMWARE(MLXSW_SP2_FW_FILENAME);
547362306a36Sopenharmony_ciMODULE_FIRMWARE(MLXSW_SP3_FW_FILENAME);
547462306a36Sopenharmony_ciMODULE_FIRMWARE(MLXSW_SP_LINECARDS_INI_BUNDLE_FILENAME);
5475