xref: /OK3568_Linux_fs/kernel/drivers/net/ethernet/mellanox/mlxsw/spectrum.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2*4882a593Smuzhiyun /* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved */
3*4882a593Smuzhiyun 
4*4882a593Smuzhiyun #include <linux/kernel.h>
5*4882a593Smuzhiyun #include <linux/module.h>
6*4882a593Smuzhiyun #include <linux/types.h>
7*4882a593Smuzhiyun #include <linux/pci.h>
8*4882a593Smuzhiyun #include <linux/netdevice.h>
9*4882a593Smuzhiyun #include <linux/etherdevice.h>
10*4882a593Smuzhiyun #include <linux/ethtool.h>
11*4882a593Smuzhiyun #include <linux/slab.h>
12*4882a593Smuzhiyun #include <linux/device.h>
13*4882a593Smuzhiyun #include <linux/skbuff.h>
14*4882a593Smuzhiyun #include <linux/if_vlan.h>
15*4882a593Smuzhiyun #include <linux/if_bridge.h>
16*4882a593Smuzhiyun #include <linux/workqueue.h>
17*4882a593Smuzhiyun #include <linux/jiffies.h>
18*4882a593Smuzhiyun #include <linux/bitops.h>
19*4882a593Smuzhiyun #include <linux/list.h>
20*4882a593Smuzhiyun #include <linux/notifier.h>
21*4882a593Smuzhiyun #include <linux/dcbnl.h>
22*4882a593Smuzhiyun #include <linux/inetdevice.h>
23*4882a593Smuzhiyun #include <linux/netlink.h>
24*4882a593Smuzhiyun #include <linux/jhash.h>
25*4882a593Smuzhiyun #include <linux/log2.h>
26*4882a593Smuzhiyun #include <net/switchdev.h>
27*4882a593Smuzhiyun #include <net/pkt_cls.h>
28*4882a593Smuzhiyun #include <net/netevent.h>
29*4882a593Smuzhiyun #include <net/addrconf.h>
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #include "spectrum.h"
32*4882a593Smuzhiyun #include "pci.h"
33*4882a593Smuzhiyun #include "core.h"
34*4882a593Smuzhiyun #include "core_env.h"
35*4882a593Smuzhiyun #include "reg.h"
36*4882a593Smuzhiyun #include "port.h"
37*4882a593Smuzhiyun #include "trap.h"
38*4882a593Smuzhiyun #include "txheader.h"
39*4882a593Smuzhiyun #include "spectrum_cnt.h"
40*4882a593Smuzhiyun #include "spectrum_dpipe.h"
41*4882a593Smuzhiyun #include "spectrum_acl_flex_actions.h"
42*4882a593Smuzhiyun #include "spectrum_span.h"
43*4882a593Smuzhiyun #include "spectrum_ptp.h"
44*4882a593Smuzhiyun #include "spectrum_trap.h"
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun #define MLXSW_SP1_FWREV_MAJOR 13
47*4882a593Smuzhiyun #define MLXSW_SP1_FWREV_MINOR 2008
48*4882a593Smuzhiyun #define MLXSW_SP1_FWREV_SUBMINOR 1310
49*4882a593Smuzhiyun #define MLXSW_SP1_FWREV_CAN_RESET_MINOR 1702
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun static const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = {
52*4882a593Smuzhiyun 	.major = MLXSW_SP1_FWREV_MAJOR,
53*4882a593Smuzhiyun 	.minor = MLXSW_SP1_FWREV_MINOR,
54*4882a593Smuzhiyun 	.subminor = MLXSW_SP1_FWREV_SUBMINOR,
55*4882a593Smuzhiyun 	.can_reset_minor = MLXSW_SP1_FWREV_CAN_RESET_MINOR,
56*4882a593Smuzhiyun };
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun #define MLXSW_SP1_FW_FILENAME \
59*4882a593Smuzhiyun 	"mellanox/mlxsw_spectrum-" __stringify(MLXSW_SP1_FWREV_MAJOR) \
60*4882a593Smuzhiyun 	"." __stringify(MLXSW_SP1_FWREV_MINOR) \
61*4882a593Smuzhiyun 	"." __stringify(MLXSW_SP1_FWREV_SUBMINOR) ".mfa2"
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun #define MLXSW_SP2_FWREV_MAJOR 29
64*4882a593Smuzhiyun #define MLXSW_SP2_FWREV_MINOR 2008
65*4882a593Smuzhiyun #define MLXSW_SP2_FWREV_SUBMINOR 1310
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun static const struct mlxsw_fw_rev mlxsw_sp2_fw_rev = {
68*4882a593Smuzhiyun 	.major = MLXSW_SP2_FWREV_MAJOR,
69*4882a593Smuzhiyun 	.minor = MLXSW_SP2_FWREV_MINOR,
70*4882a593Smuzhiyun 	.subminor = MLXSW_SP2_FWREV_SUBMINOR,
71*4882a593Smuzhiyun };
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun #define MLXSW_SP2_FW_FILENAME \
74*4882a593Smuzhiyun 	"mellanox/mlxsw_spectrum2-" __stringify(MLXSW_SP2_FWREV_MAJOR) \
75*4882a593Smuzhiyun 	"." __stringify(MLXSW_SP2_FWREV_MINOR) \
76*4882a593Smuzhiyun 	"." __stringify(MLXSW_SP2_FWREV_SUBMINOR) ".mfa2"
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun #define MLXSW_SP3_FWREV_MAJOR 30
79*4882a593Smuzhiyun #define MLXSW_SP3_FWREV_MINOR 2008
80*4882a593Smuzhiyun #define MLXSW_SP3_FWREV_SUBMINOR 1310
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun static const struct mlxsw_fw_rev mlxsw_sp3_fw_rev = {
83*4882a593Smuzhiyun 	.major = MLXSW_SP3_FWREV_MAJOR,
84*4882a593Smuzhiyun 	.minor = MLXSW_SP3_FWREV_MINOR,
85*4882a593Smuzhiyun 	.subminor = MLXSW_SP3_FWREV_SUBMINOR,
86*4882a593Smuzhiyun };
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun #define MLXSW_SP3_FW_FILENAME \
89*4882a593Smuzhiyun 	"mellanox/mlxsw_spectrum3-" __stringify(MLXSW_SP3_FWREV_MAJOR) \
90*4882a593Smuzhiyun 	"." __stringify(MLXSW_SP3_FWREV_MINOR) \
91*4882a593Smuzhiyun 	"." __stringify(MLXSW_SP3_FWREV_SUBMINOR) ".mfa2"
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun static const char mlxsw_sp1_driver_name[] = "mlxsw_spectrum";
94*4882a593Smuzhiyun static const char mlxsw_sp2_driver_name[] = "mlxsw_spectrum2";
95*4882a593Smuzhiyun static const char mlxsw_sp3_driver_name[] = "mlxsw_spectrum3";
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun static const unsigned char mlxsw_sp1_mac_mask[ETH_ALEN] = {
98*4882a593Smuzhiyun 	0xff, 0xff, 0xff, 0xff, 0xfc, 0x00
99*4882a593Smuzhiyun };
100*4882a593Smuzhiyun static const unsigned char mlxsw_sp2_mac_mask[ETH_ALEN] = {
101*4882a593Smuzhiyun 	0xff, 0xff, 0xff, 0xff, 0xf0, 0x00
102*4882a593Smuzhiyun };
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun /* tx_hdr_version
105*4882a593Smuzhiyun  * Tx header version.
106*4882a593Smuzhiyun  * Must be set to 1.
107*4882a593Smuzhiyun  */
108*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, version, 0x00, 28, 4);
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun /* tx_hdr_ctl
111*4882a593Smuzhiyun  * Packet control type.
112*4882a593Smuzhiyun  * 0 - Ethernet control (e.g. EMADs, LACP)
113*4882a593Smuzhiyun  * 1 - Ethernet data
114*4882a593Smuzhiyun  */
115*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, ctl, 0x00, 26, 2);
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun /* tx_hdr_proto
118*4882a593Smuzhiyun  * Packet protocol type. Must be set to 1 (Ethernet).
119*4882a593Smuzhiyun  */
120*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, proto, 0x00, 21, 3);
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun /* tx_hdr_rx_is_router
123*4882a593Smuzhiyun  * Packet is sent from the router. Valid for data packets only.
124*4882a593Smuzhiyun  */
125*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, rx_is_router, 0x00, 19, 1);
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun /* tx_hdr_fid_valid
128*4882a593Smuzhiyun  * Indicates if the 'fid' field is valid and should be used for
129*4882a593Smuzhiyun  * forwarding lookup. Valid for data packets only.
130*4882a593Smuzhiyun  */
131*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, fid_valid, 0x00, 16, 1);
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun /* tx_hdr_swid
134*4882a593Smuzhiyun  * Switch partition ID. Must be set to 0.
135*4882a593Smuzhiyun  */
136*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, swid, 0x00, 12, 3);
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun /* tx_hdr_control_tclass
139*4882a593Smuzhiyun  * Indicates if the packet should use the control TClass and not one
140*4882a593Smuzhiyun  * of the data TClasses.
141*4882a593Smuzhiyun  */
142*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, control_tclass, 0x00, 6, 1);
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun /* tx_hdr_etclass
145*4882a593Smuzhiyun  * Egress TClass to be used on the egress device on the egress port.
146*4882a593Smuzhiyun  */
147*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, etclass, 0x00, 0, 4);
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun /* tx_hdr_port_mid
150*4882a593Smuzhiyun  * Destination local port for unicast packets.
151*4882a593Smuzhiyun  * Destination multicast ID for multicast packets.
152*4882a593Smuzhiyun  *
153*4882a593Smuzhiyun  * Control packets are directed to a specific egress port, while data
154*4882a593Smuzhiyun  * packets are transmitted through the CPU port (0) into the switch partition,
155*4882a593Smuzhiyun  * where forwarding rules are applied.
156*4882a593Smuzhiyun  */
157*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, port_mid, 0x04, 16, 16);
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun /* tx_hdr_fid
160*4882a593Smuzhiyun  * Forwarding ID used for L2 forwarding lookup. Valid only if 'fid_valid' is
161*4882a593Smuzhiyun  * set, otherwise calculated based on the packet's VID using VID to FID mapping.
162*4882a593Smuzhiyun  * Valid for data packets only.
163*4882a593Smuzhiyun  */
164*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, fid, 0x08, 0, 16);
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun /* tx_hdr_type
167*4882a593Smuzhiyun  * 0 - Data packets
168*4882a593Smuzhiyun  * 6 - Control packets
169*4882a593Smuzhiyun  */
170*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, type, 0x0C, 0, 4);
171*4882a593Smuzhiyun 
mlxsw_sp_flow_counter_get(struct mlxsw_sp * mlxsw_sp,unsigned int counter_index,u64 * packets,u64 * bytes)172*4882a593Smuzhiyun int mlxsw_sp_flow_counter_get(struct mlxsw_sp *mlxsw_sp,
173*4882a593Smuzhiyun 			      unsigned int counter_index, u64 *packets,
174*4882a593Smuzhiyun 			      u64 *bytes)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun 	char mgpc_pl[MLXSW_REG_MGPC_LEN];
177*4882a593Smuzhiyun 	int err;
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	mlxsw_reg_mgpc_pack(mgpc_pl, counter_index, MLXSW_REG_MGPC_OPCODE_NOP,
180*4882a593Smuzhiyun 			    MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS_BYTES);
181*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mgpc), mgpc_pl);
182*4882a593Smuzhiyun 	if (err)
183*4882a593Smuzhiyun 		return err;
184*4882a593Smuzhiyun 	if (packets)
185*4882a593Smuzhiyun 		*packets = mlxsw_reg_mgpc_packet_counter_get(mgpc_pl);
186*4882a593Smuzhiyun 	if (bytes)
187*4882a593Smuzhiyun 		*bytes = mlxsw_reg_mgpc_byte_counter_get(mgpc_pl);
188*4882a593Smuzhiyun 	return 0;
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun 
mlxsw_sp_flow_counter_clear(struct mlxsw_sp * mlxsw_sp,unsigned int counter_index)191*4882a593Smuzhiyun static int mlxsw_sp_flow_counter_clear(struct mlxsw_sp *mlxsw_sp,
192*4882a593Smuzhiyun 				       unsigned int counter_index)
193*4882a593Smuzhiyun {
194*4882a593Smuzhiyun 	char mgpc_pl[MLXSW_REG_MGPC_LEN];
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	mlxsw_reg_mgpc_pack(mgpc_pl, counter_index, MLXSW_REG_MGPC_OPCODE_CLEAR,
197*4882a593Smuzhiyun 			    MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS_BYTES);
198*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mgpc), mgpc_pl);
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun 
mlxsw_sp_flow_counter_alloc(struct mlxsw_sp * mlxsw_sp,unsigned int * p_counter_index)201*4882a593Smuzhiyun int mlxsw_sp_flow_counter_alloc(struct mlxsw_sp *mlxsw_sp,
202*4882a593Smuzhiyun 				unsigned int *p_counter_index)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun 	int err;
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_FLOW,
207*4882a593Smuzhiyun 				     p_counter_index);
208*4882a593Smuzhiyun 	if (err)
209*4882a593Smuzhiyun 		return err;
210*4882a593Smuzhiyun 	err = mlxsw_sp_flow_counter_clear(mlxsw_sp, *p_counter_index);
211*4882a593Smuzhiyun 	if (err)
212*4882a593Smuzhiyun 		goto err_counter_clear;
213*4882a593Smuzhiyun 	return 0;
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun err_counter_clear:
216*4882a593Smuzhiyun 	mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_FLOW,
217*4882a593Smuzhiyun 			      *p_counter_index);
218*4882a593Smuzhiyun 	return err;
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun 
mlxsw_sp_flow_counter_free(struct mlxsw_sp * mlxsw_sp,unsigned int counter_index)221*4882a593Smuzhiyun void mlxsw_sp_flow_counter_free(struct mlxsw_sp *mlxsw_sp,
222*4882a593Smuzhiyun 				unsigned int counter_index)
223*4882a593Smuzhiyun {
224*4882a593Smuzhiyun 	 mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_FLOW,
225*4882a593Smuzhiyun 			       counter_index);
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun 
mlxsw_sp_txhdr_construct(struct sk_buff * skb,const struct mlxsw_tx_info * tx_info)228*4882a593Smuzhiyun static void mlxsw_sp_txhdr_construct(struct sk_buff *skb,
229*4882a593Smuzhiyun 				     const struct mlxsw_tx_info *tx_info)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun 	char *txhdr = skb_push(skb, MLXSW_TXHDR_LEN);
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	memset(txhdr, 0, MLXSW_TXHDR_LEN);
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_1);
236*4882a593Smuzhiyun 	mlxsw_tx_hdr_ctl_set(txhdr, MLXSW_TXHDR_ETH_CTL);
237*4882a593Smuzhiyun 	mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH);
238*4882a593Smuzhiyun 	mlxsw_tx_hdr_swid_set(txhdr, 0);
239*4882a593Smuzhiyun 	mlxsw_tx_hdr_control_tclass_set(txhdr, 1);
240*4882a593Smuzhiyun 	mlxsw_tx_hdr_port_mid_set(txhdr, tx_info->local_port);
241*4882a593Smuzhiyun 	mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_CONTROL);
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun 
mlxsw_sp_stp_spms_state(u8 state)244*4882a593Smuzhiyun enum mlxsw_reg_spms_state mlxsw_sp_stp_spms_state(u8 state)
245*4882a593Smuzhiyun {
246*4882a593Smuzhiyun 	switch (state) {
247*4882a593Smuzhiyun 	case BR_STATE_FORWARDING:
248*4882a593Smuzhiyun 		return MLXSW_REG_SPMS_STATE_FORWARDING;
249*4882a593Smuzhiyun 	case BR_STATE_LEARNING:
250*4882a593Smuzhiyun 		return MLXSW_REG_SPMS_STATE_LEARNING;
251*4882a593Smuzhiyun 	case BR_STATE_LISTENING:
252*4882a593Smuzhiyun 	case BR_STATE_DISABLED:
253*4882a593Smuzhiyun 	case BR_STATE_BLOCKING:
254*4882a593Smuzhiyun 		return MLXSW_REG_SPMS_STATE_DISCARDING;
255*4882a593Smuzhiyun 	default:
256*4882a593Smuzhiyun 		BUG();
257*4882a593Smuzhiyun 	}
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun 
mlxsw_sp_port_vid_stp_set(struct mlxsw_sp_port * mlxsw_sp_port,u16 vid,u8 state)260*4882a593Smuzhiyun int mlxsw_sp_port_vid_stp_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid,
261*4882a593Smuzhiyun 			      u8 state)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun 	enum mlxsw_reg_spms_state spms_state = mlxsw_sp_stp_spms_state(state);
264*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
265*4882a593Smuzhiyun 	char *spms_pl;
266*4882a593Smuzhiyun 	int err;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL);
269*4882a593Smuzhiyun 	if (!spms_pl)
270*4882a593Smuzhiyun 		return -ENOMEM;
271*4882a593Smuzhiyun 	mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port);
272*4882a593Smuzhiyun 	mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state);
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl);
275*4882a593Smuzhiyun 	kfree(spms_pl);
276*4882a593Smuzhiyun 	return err;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun 
mlxsw_sp_base_mac_get(struct mlxsw_sp * mlxsw_sp)279*4882a593Smuzhiyun static int mlxsw_sp_base_mac_get(struct mlxsw_sp *mlxsw_sp)
280*4882a593Smuzhiyun {
281*4882a593Smuzhiyun 	char spad_pl[MLXSW_REG_SPAD_LEN] = {0};
282*4882a593Smuzhiyun 	int err;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(spad), spad_pl);
285*4882a593Smuzhiyun 	if (err)
286*4882a593Smuzhiyun 		return err;
287*4882a593Smuzhiyun 	mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_sp->base_mac);
288*4882a593Smuzhiyun 	return 0;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun 
mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port * mlxsw_sp_port,bool is_up)291*4882a593Smuzhiyun int mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port,
292*4882a593Smuzhiyun 				   bool is_up)
293*4882a593Smuzhiyun {
294*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
295*4882a593Smuzhiyun 	char paos_pl[MLXSW_REG_PAOS_LEN];
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	mlxsw_reg_paos_pack(paos_pl, mlxsw_sp_port->local_port,
298*4882a593Smuzhiyun 			    is_up ? MLXSW_PORT_ADMIN_STATUS_UP :
299*4882a593Smuzhiyun 			    MLXSW_PORT_ADMIN_STATUS_DOWN);
300*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(paos), paos_pl);
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun 
mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port * mlxsw_sp_port,unsigned char * addr)303*4882a593Smuzhiyun static int mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port *mlxsw_sp_port,
304*4882a593Smuzhiyun 				      unsigned char *addr)
305*4882a593Smuzhiyun {
306*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
307*4882a593Smuzhiyun 	char ppad_pl[MLXSW_REG_PPAD_LEN];
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	mlxsw_reg_ppad_pack(ppad_pl, true, mlxsw_sp_port->local_port);
310*4882a593Smuzhiyun 	mlxsw_reg_ppad_mac_memcpy_to(ppad_pl, addr);
311*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppad), ppad_pl);
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun 
mlxsw_sp_port_dev_addr_init(struct mlxsw_sp_port * mlxsw_sp_port)314*4882a593Smuzhiyun static int mlxsw_sp_port_dev_addr_init(struct mlxsw_sp_port *mlxsw_sp_port)
315*4882a593Smuzhiyun {
316*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
317*4882a593Smuzhiyun 	unsigned char *addr = mlxsw_sp_port->dev->dev_addr;
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	ether_addr_copy(addr, mlxsw_sp->base_mac);
320*4882a593Smuzhiyun 	addr[ETH_ALEN - 1] += mlxsw_sp_port->local_port;
321*4882a593Smuzhiyun 	return mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, addr);
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun 
mlxsw_sp_port_max_mtu_get(struct mlxsw_sp_port * mlxsw_sp_port,int * p_max_mtu)324*4882a593Smuzhiyun static int mlxsw_sp_port_max_mtu_get(struct mlxsw_sp_port *mlxsw_sp_port, int *p_max_mtu)
325*4882a593Smuzhiyun {
326*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
327*4882a593Smuzhiyun 	char pmtu_pl[MLXSW_REG_PMTU_LEN];
328*4882a593Smuzhiyun 	int err;
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sp_port->local_port, 0);
331*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pmtu), pmtu_pl);
332*4882a593Smuzhiyun 	if (err)
333*4882a593Smuzhiyun 		return err;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	*p_max_mtu = mlxsw_reg_pmtu_max_mtu_get(pmtu_pl);
336*4882a593Smuzhiyun 	return 0;
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun 
mlxsw_sp_port_mtu_set(struct mlxsw_sp_port * mlxsw_sp_port,u16 mtu)339*4882a593Smuzhiyun static int mlxsw_sp_port_mtu_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu)
340*4882a593Smuzhiyun {
341*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
342*4882a593Smuzhiyun 	char pmtu_pl[MLXSW_REG_PMTU_LEN];
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	mtu += MLXSW_TXHDR_LEN + ETH_HLEN;
345*4882a593Smuzhiyun 	if (mtu > mlxsw_sp_port->max_mtu)
346*4882a593Smuzhiyun 		return -EINVAL;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sp_port->local_port, mtu);
349*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmtu), pmtu_pl);
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun 
mlxsw_sp_port_swid_set(struct mlxsw_sp_port * mlxsw_sp_port,u8 swid)352*4882a593Smuzhiyun static int mlxsw_sp_port_swid_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 swid)
353*4882a593Smuzhiyun {
354*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
355*4882a593Smuzhiyun 	char pspa_pl[MLXSW_REG_PSPA_LEN];
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	mlxsw_reg_pspa_pack(pspa_pl, swid, mlxsw_sp_port->local_port);
358*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pspa), pspa_pl);
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun 
mlxsw_sp_port_vp_mode_set(struct mlxsw_sp_port * mlxsw_sp_port,bool enable)361*4882a593Smuzhiyun int mlxsw_sp_port_vp_mode_set(struct mlxsw_sp_port *mlxsw_sp_port, bool enable)
362*4882a593Smuzhiyun {
363*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
364*4882a593Smuzhiyun 	char svpe_pl[MLXSW_REG_SVPE_LEN];
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	mlxsw_reg_svpe_pack(svpe_pl, mlxsw_sp_port->local_port, enable);
367*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svpe), svpe_pl);
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun 
mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port * mlxsw_sp_port,u16 vid,bool learn_enable)370*4882a593Smuzhiyun int mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid,
371*4882a593Smuzhiyun 				   bool learn_enable)
372*4882a593Smuzhiyun {
373*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
374*4882a593Smuzhiyun 	char *spvmlr_pl;
375*4882a593Smuzhiyun 	int err;
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	spvmlr_pl = kmalloc(MLXSW_REG_SPVMLR_LEN, GFP_KERNEL);
378*4882a593Smuzhiyun 	if (!spvmlr_pl)
379*4882a593Smuzhiyun 		return -ENOMEM;
380*4882a593Smuzhiyun 	mlxsw_reg_spvmlr_pack(spvmlr_pl, mlxsw_sp_port->local_port, vid, vid,
381*4882a593Smuzhiyun 			      learn_enable);
382*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvmlr), spvmlr_pl);
383*4882a593Smuzhiyun 	kfree(spvmlr_pl);
384*4882a593Smuzhiyun 	return err;
385*4882a593Smuzhiyun }
386*4882a593Smuzhiyun 
__mlxsw_sp_port_pvid_set(struct mlxsw_sp_port * mlxsw_sp_port,u16 vid)387*4882a593Smuzhiyun static int __mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port,
388*4882a593Smuzhiyun 				    u16 vid)
389*4882a593Smuzhiyun {
390*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
391*4882a593Smuzhiyun 	char spvid_pl[MLXSW_REG_SPVID_LEN];
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	mlxsw_reg_spvid_pack(spvid_pl, mlxsw_sp_port->local_port, vid);
394*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvid), spvid_pl);
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun 
mlxsw_sp_port_allow_untagged_set(struct mlxsw_sp_port * mlxsw_sp_port,bool allow)397*4882a593Smuzhiyun static int mlxsw_sp_port_allow_untagged_set(struct mlxsw_sp_port *mlxsw_sp_port,
398*4882a593Smuzhiyun 					    bool allow)
399*4882a593Smuzhiyun {
400*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
401*4882a593Smuzhiyun 	char spaft_pl[MLXSW_REG_SPAFT_LEN];
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	mlxsw_reg_spaft_pack(spaft_pl, mlxsw_sp_port->local_port, allow);
404*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spaft), spaft_pl);
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun 
mlxsw_sp_port_pvid_set(struct mlxsw_sp_port * mlxsw_sp_port,u16 vid)407*4882a593Smuzhiyun int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun 	int err;
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	if (!vid) {
412*4882a593Smuzhiyun 		err = mlxsw_sp_port_allow_untagged_set(mlxsw_sp_port, false);
413*4882a593Smuzhiyun 		if (err)
414*4882a593Smuzhiyun 			return err;
415*4882a593Smuzhiyun 	} else {
416*4882a593Smuzhiyun 		err = __mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid);
417*4882a593Smuzhiyun 		if (err)
418*4882a593Smuzhiyun 			return err;
419*4882a593Smuzhiyun 		err = mlxsw_sp_port_allow_untagged_set(mlxsw_sp_port, true);
420*4882a593Smuzhiyun 		if (err)
421*4882a593Smuzhiyun 			goto err_port_allow_untagged_set;
422*4882a593Smuzhiyun 	}
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	mlxsw_sp_port->pvid = vid;
425*4882a593Smuzhiyun 	return 0;
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun err_port_allow_untagged_set:
428*4882a593Smuzhiyun 	__mlxsw_sp_port_pvid_set(mlxsw_sp_port, mlxsw_sp_port->pvid);
429*4882a593Smuzhiyun 	return err;
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun static int
mlxsw_sp_port_system_port_mapping_set(struct mlxsw_sp_port * mlxsw_sp_port)433*4882a593Smuzhiyun mlxsw_sp_port_system_port_mapping_set(struct mlxsw_sp_port *mlxsw_sp_port)
434*4882a593Smuzhiyun {
435*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
436*4882a593Smuzhiyun 	char sspr_pl[MLXSW_REG_SSPR_LEN];
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 	mlxsw_reg_sspr_pack(sspr_pl, mlxsw_sp_port->local_port);
439*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sspr), sspr_pl);
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun static int
mlxsw_sp_port_module_info_get(struct mlxsw_sp * mlxsw_sp,u8 local_port,struct mlxsw_sp_port_mapping * port_mapping)443*4882a593Smuzhiyun mlxsw_sp_port_module_info_get(struct mlxsw_sp *mlxsw_sp, u8 local_port,
444*4882a593Smuzhiyun 			      struct mlxsw_sp_port_mapping *port_mapping)
445*4882a593Smuzhiyun {
446*4882a593Smuzhiyun 	char pmlp_pl[MLXSW_REG_PMLP_LEN];
447*4882a593Smuzhiyun 	bool separate_rxtx;
448*4882a593Smuzhiyun 	u8 module;
449*4882a593Smuzhiyun 	u8 width;
450*4882a593Smuzhiyun 	int err;
451*4882a593Smuzhiyun 	int i;
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
454*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl);
455*4882a593Smuzhiyun 	if (err)
456*4882a593Smuzhiyun 		return err;
457*4882a593Smuzhiyun 	module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);
458*4882a593Smuzhiyun 	width = mlxsw_reg_pmlp_width_get(pmlp_pl);
459*4882a593Smuzhiyun 	separate_rxtx = mlxsw_reg_pmlp_rxtx_get(pmlp_pl);
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun 	if (width && !is_power_of_2(width)) {
462*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unsupported module config: width value is not power of 2\n",
463*4882a593Smuzhiyun 			local_port);
464*4882a593Smuzhiyun 		return -EINVAL;
465*4882a593Smuzhiyun 	}
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun 	for (i = 0; i < width; i++) {
468*4882a593Smuzhiyun 		if (mlxsw_reg_pmlp_module_get(pmlp_pl, i) != module) {
469*4882a593Smuzhiyun 			dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unsupported module config: contains multiple modules\n",
470*4882a593Smuzhiyun 				local_port);
471*4882a593Smuzhiyun 			return -EINVAL;
472*4882a593Smuzhiyun 		}
473*4882a593Smuzhiyun 		if (separate_rxtx &&
474*4882a593Smuzhiyun 		    mlxsw_reg_pmlp_tx_lane_get(pmlp_pl, i) !=
475*4882a593Smuzhiyun 		    mlxsw_reg_pmlp_rx_lane_get(pmlp_pl, i)) {
476*4882a593Smuzhiyun 			dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unsupported module config: TX and RX lane numbers are different\n",
477*4882a593Smuzhiyun 				local_port);
478*4882a593Smuzhiyun 			return -EINVAL;
479*4882a593Smuzhiyun 		}
480*4882a593Smuzhiyun 		if (mlxsw_reg_pmlp_tx_lane_get(pmlp_pl, i) != i) {
481*4882a593Smuzhiyun 			dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unsupported module config: TX and RX lane numbers are not sequential\n",
482*4882a593Smuzhiyun 				local_port);
483*4882a593Smuzhiyun 			return -EINVAL;
484*4882a593Smuzhiyun 		}
485*4882a593Smuzhiyun 	}
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun 	port_mapping->module = module;
488*4882a593Smuzhiyun 	port_mapping->width = width;
489*4882a593Smuzhiyun 	port_mapping->lane = mlxsw_reg_pmlp_tx_lane_get(pmlp_pl, 0);
490*4882a593Smuzhiyun 	return 0;
491*4882a593Smuzhiyun }
492*4882a593Smuzhiyun 
mlxsw_sp_port_module_map(struct mlxsw_sp_port * mlxsw_sp_port)493*4882a593Smuzhiyun static int mlxsw_sp_port_module_map(struct mlxsw_sp_port *mlxsw_sp_port)
494*4882a593Smuzhiyun {
495*4882a593Smuzhiyun 	struct mlxsw_sp_port_mapping *port_mapping = &mlxsw_sp_port->mapping;
496*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
497*4882a593Smuzhiyun 	char pmlp_pl[MLXSW_REG_PMLP_LEN];
498*4882a593Smuzhiyun 	int i;
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun 	mlxsw_reg_pmlp_pack(pmlp_pl, mlxsw_sp_port->local_port);
501*4882a593Smuzhiyun 	mlxsw_reg_pmlp_width_set(pmlp_pl, port_mapping->width);
502*4882a593Smuzhiyun 	for (i = 0; i < port_mapping->width; i++) {
503*4882a593Smuzhiyun 		mlxsw_reg_pmlp_module_set(pmlp_pl, i, port_mapping->module);
504*4882a593Smuzhiyun 		mlxsw_reg_pmlp_tx_lane_set(pmlp_pl, i, port_mapping->lane + i); /* Rx & Tx */
505*4882a593Smuzhiyun 	}
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl);
508*4882a593Smuzhiyun }
509*4882a593Smuzhiyun 
mlxsw_sp_port_module_unmap(struct mlxsw_sp_port * mlxsw_sp_port)510*4882a593Smuzhiyun static int mlxsw_sp_port_module_unmap(struct mlxsw_sp_port *mlxsw_sp_port)
511*4882a593Smuzhiyun {
512*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
513*4882a593Smuzhiyun 	char pmlp_pl[MLXSW_REG_PMLP_LEN];
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 	mlxsw_reg_pmlp_pack(pmlp_pl, mlxsw_sp_port->local_port);
516*4882a593Smuzhiyun 	mlxsw_reg_pmlp_width_set(pmlp_pl, 0);
517*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl);
518*4882a593Smuzhiyun }
519*4882a593Smuzhiyun 
mlxsw_sp_port_open(struct net_device * dev)520*4882a593Smuzhiyun static int mlxsw_sp_port_open(struct net_device *dev)
521*4882a593Smuzhiyun {
522*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
523*4882a593Smuzhiyun 	int err;
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true);
526*4882a593Smuzhiyun 	if (err)
527*4882a593Smuzhiyun 		return err;
528*4882a593Smuzhiyun 	netif_start_queue(dev);
529*4882a593Smuzhiyun 	return 0;
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun 
mlxsw_sp_port_stop(struct net_device * dev)532*4882a593Smuzhiyun static int mlxsw_sp_port_stop(struct net_device *dev)
533*4882a593Smuzhiyun {
534*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	netif_stop_queue(dev);
537*4882a593Smuzhiyun 	return mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
538*4882a593Smuzhiyun }
539*4882a593Smuzhiyun 
mlxsw_sp_port_xmit(struct sk_buff * skb,struct net_device * dev)540*4882a593Smuzhiyun static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb,
541*4882a593Smuzhiyun 				      struct net_device *dev)
542*4882a593Smuzhiyun {
543*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
544*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
545*4882a593Smuzhiyun 	struct mlxsw_sp_port_pcpu_stats *pcpu_stats;
546*4882a593Smuzhiyun 	const struct mlxsw_tx_info tx_info = {
547*4882a593Smuzhiyun 		.local_port = mlxsw_sp_port->local_port,
548*4882a593Smuzhiyun 		.is_emad = false,
549*4882a593Smuzhiyun 	};
550*4882a593Smuzhiyun 	u64 len;
551*4882a593Smuzhiyun 	int err;
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	if (skb_cow_head(skb, MLXSW_TXHDR_LEN)) {
554*4882a593Smuzhiyun 		this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
555*4882a593Smuzhiyun 		dev_kfree_skb_any(skb);
556*4882a593Smuzhiyun 		return NETDEV_TX_OK;
557*4882a593Smuzhiyun 	}
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 	memset(skb->cb, 0, sizeof(struct mlxsw_skb_cb));
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	if (mlxsw_core_skb_transmit_busy(mlxsw_sp->core, &tx_info))
562*4882a593Smuzhiyun 		return NETDEV_TX_BUSY;
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	if (eth_skb_pad(skb)) {
565*4882a593Smuzhiyun 		this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
566*4882a593Smuzhiyun 		return NETDEV_TX_OK;
567*4882a593Smuzhiyun 	}
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 	mlxsw_sp_txhdr_construct(skb, &tx_info);
570*4882a593Smuzhiyun 	/* TX header is consumed by HW on the way so we shouldn't count its
571*4882a593Smuzhiyun 	 * bytes as being sent.
572*4882a593Smuzhiyun 	 */
573*4882a593Smuzhiyun 	len = skb->len - MLXSW_TXHDR_LEN;
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun 	/* Due to a race we might fail here because of a full queue. In that
576*4882a593Smuzhiyun 	 * unlikely case we simply drop the packet.
577*4882a593Smuzhiyun 	 */
578*4882a593Smuzhiyun 	err = mlxsw_core_skb_transmit(mlxsw_sp->core, skb, &tx_info);
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	if (!err) {
581*4882a593Smuzhiyun 		pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats);
582*4882a593Smuzhiyun 		u64_stats_update_begin(&pcpu_stats->syncp);
583*4882a593Smuzhiyun 		pcpu_stats->tx_packets++;
584*4882a593Smuzhiyun 		pcpu_stats->tx_bytes += len;
585*4882a593Smuzhiyun 		u64_stats_update_end(&pcpu_stats->syncp);
586*4882a593Smuzhiyun 	} else {
587*4882a593Smuzhiyun 		this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
588*4882a593Smuzhiyun 		dev_kfree_skb_any(skb);
589*4882a593Smuzhiyun 	}
590*4882a593Smuzhiyun 	return NETDEV_TX_OK;
591*4882a593Smuzhiyun }
592*4882a593Smuzhiyun 
mlxsw_sp_set_rx_mode(struct net_device * dev)593*4882a593Smuzhiyun static void mlxsw_sp_set_rx_mode(struct net_device *dev)
594*4882a593Smuzhiyun {
595*4882a593Smuzhiyun }
596*4882a593Smuzhiyun 
mlxsw_sp_port_set_mac_address(struct net_device * dev,void * p)597*4882a593Smuzhiyun static int mlxsw_sp_port_set_mac_address(struct net_device *dev, void *p)
598*4882a593Smuzhiyun {
599*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
600*4882a593Smuzhiyun 	struct sockaddr *addr = p;
601*4882a593Smuzhiyun 	int err;
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 	if (!is_valid_ether_addr(addr->sa_data))
604*4882a593Smuzhiyun 		return -EADDRNOTAVAIL;
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	err = mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, addr->sa_data);
607*4882a593Smuzhiyun 	if (err)
608*4882a593Smuzhiyun 		return err;
609*4882a593Smuzhiyun 	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
610*4882a593Smuzhiyun 	return 0;
611*4882a593Smuzhiyun }
612*4882a593Smuzhiyun 
mlxsw_sp_port_change_mtu(struct net_device * dev,int mtu)613*4882a593Smuzhiyun static int mlxsw_sp_port_change_mtu(struct net_device *dev, int mtu)
614*4882a593Smuzhiyun {
615*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
616*4882a593Smuzhiyun 	struct mlxsw_sp_hdroom orig_hdroom;
617*4882a593Smuzhiyun 	struct mlxsw_sp_hdroom hdroom;
618*4882a593Smuzhiyun 	int err;
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun 	orig_hdroom = *mlxsw_sp_port->hdroom;
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun 	hdroom = orig_hdroom;
623*4882a593Smuzhiyun 	hdroom.mtu = mtu;
624*4882a593Smuzhiyun 	mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
627*4882a593Smuzhiyun 	if (err) {
628*4882a593Smuzhiyun 		netdev_err(dev, "Failed to configure port's headroom\n");
629*4882a593Smuzhiyun 		return err;
630*4882a593Smuzhiyun 	}
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 	err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, mtu);
633*4882a593Smuzhiyun 	if (err)
634*4882a593Smuzhiyun 		goto err_port_mtu_set;
635*4882a593Smuzhiyun 	dev->mtu = mtu;
636*4882a593Smuzhiyun 	return 0;
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun err_port_mtu_set:
639*4882a593Smuzhiyun 	mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom);
640*4882a593Smuzhiyun 	return err;
641*4882a593Smuzhiyun }
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun static int
mlxsw_sp_port_get_sw_stats64(const struct net_device * dev,struct rtnl_link_stats64 * stats)644*4882a593Smuzhiyun mlxsw_sp_port_get_sw_stats64(const struct net_device *dev,
645*4882a593Smuzhiyun 			     struct rtnl_link_stats64 *stats)
646*4882a593Smuzhiyun {
647*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
648*4882a593Smuzhiyun 	struct mlxsw_sp_port_pcpu_stats *p;
649*4882a593Smuzhiyun 	u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
650*4882a593Smuzhiyun 	u32 tx_dropped = 0;
651*4882a593Smuzhiyun 	unsigned int start;
652*4882a593Smuzhiyun 	int i;
653*4882a593Smuzhiyun 
654*4882a593Smuzhiyun 	for_each_possible_cpu(i) {
655*4882a593Smuzhiyun 		p = per_cpu_ptr(mlxsw_sp_port->pcpu_stats, i);
656*4882a593Smuzhiyun 		do {
657*4882a593Smuzhiyun 			start = u64_stats_fetch_begin_irq(&p->syncp);
658*4882a593Smuzhiyun 			rx_packets	= p->rx_packets;
659*4882a593Smuzhiyun 			rx_bytes	= p->rx_bytes;
660*4882a593Smuzhiyun 			tx_packets	= p->tx_packets;
661*4882a593Smuzhiyun 			tx_bytes	= p->tx_bytes;
662*4882a593Smuzhiyun 		} while (u64_stats_fetch_retry_irq(&p->syncp, start));
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun 		stats->rx_packets	+= rx_packets;
665*4882a593Smuzhiyun 		stats->rx_bytes		+= rx_bytes;
666*4882a593Smuzhiyun 		stats->tx_packets	+= tx_packets;
667*4882a593Smuzhiyun 		stats->tx_bytes		+= tx_bytes;
668*4882a593Smuzhiyun 		/* tx_dropped is u32, updated without syncp protection. */
669*4882a593Smuzhiyun 		tx_dropped	+= p->tx_dropped;
670*4882a593Smuzhiyun 	}
671*4882a593Smuzhiyun 	stats->tx_dropped	= tx_dropped;
672*4882a593Smuzhiyun 	return 0;
673*4882a593Smuzhiyun }
674*4882a593Smuzhiyun 
mlxsw_sp_port_has_offload_stats(const struct net_device * dev,int attr_id)675*4882a593Smuzhiyun static bool mlxsw_sp_port_has_offload_stats(const struct net_device *dev, int attr_id)
676*4882a593Smuzhiyun {
677*4882a593Smuzhiyun 	switch (attr_id) {
678*4882a593Smuzhiyun 	case IFLA_OFFLOAD_XSTATS_CPU_HIT:
679*4882a593Smuzhiyun 		return true;
680*4882a593Smuzhiyun 	}
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun 	return false;
683*4882a593Smuzhiyun }
684*4882a593Smuzhiyun 
mlxsw_sp_port_get_offload_stats(int attr_id,const struct net_device * dev,void * sp)685*4882a593Smuzhiyun static int mlxsw_sp_port_get_offload_stats(int attr_id, const struct net_device *dev,
686*4882a593Smuzhiyun 					   void *sp)
687*4882a593Smuzhiyun {
688*4882a593Smuzhiyun 	switch (attr_id) {
689*4882a593Smuzhiyun 	case IFLA_OFFLOAD_XSTATS_CPU_HIT:
690*4882a593Smuzhiyun 		return mlxsw_sp_port_get_sw_stats64(dev, sp);
691*4882a593Smuzhiyun 	}
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun 	return -EINVAL;
694*4882a593Smuzhiyun }
695*4882a593Smuzhiyun 
mlxsw_sp_port_get_stats_raw(struct net_device * dev,int grp,int prio,char * ppcnt_pl)696*4882a593Smuzhiyun int mlxsw_sp_port_get_stats_raw(struct net_device *dev, int grp,
697*4882a593Smuzhiyun 				int prio, char *ppcnt_pl)
698*4882a593Smuzhiyun {
699*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
700*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun 	mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port, grp, prio);
703*4882a593Smuzhiyun 	return mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ppcnt), ppcnt_pl);
704*4882a593Smuzhiyun }
705*4882a593Smuzhiyun 
mlxsw_sp_port_get_hw_stats(struct net_device * dev,struct rtnl_link_stats64 * stats)706*4882a593Smuzhiyun static int mlxsw_sp_port_get_hw_stats(struct net_device *dev,
707*4882a593Smuzhiyun 				      struct rtnl_link_stats64 *stats)
708*4882a593Smuzhiyun {
709*4882a593Smuzhiyun 	char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
710*4882a593Smuzhiyun 	int err;
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun 	err = mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT,
713*4882a593Smuzhiyun 					  0, ppcnt_pl);
714*4882a593Smuzhiyun 	if (err)
715*4882a593Smuzhiyun 		goto out;
716*4882a593Smuzhiyun 
717*4882a593Smuzhiyun 	stats->tx_packets =
718*4882a593Smuzhiyun 		mlxsw_reg_ppcnt_a_frames_transmitted_ok_get(ppcnt_pl);
719*4882a593Smuzhiyun 	stats->rx_packets =
720*4882a593Smuzhiyun 		mlxsw_reg_ppcnt_a_frames_received_ok_get(ppcnt_pl);
721*4882a593Smuzhiyun 	stats->tx_bytes =
722*4882a593Smuzhiyun 		mlxsw_reg_ppcnt_a_octets_transmitted_ok_get(ppcnt_pl);
723*4882a593Smuzhiyun 	stats->rx_bytes =
724*4882a593Smuzhiyun 		mlxsw_reg_ppcnt_a_octets_received_ok_get(ppcnt_pl);
725*4882a593Smuzhiyun 	stats->multicast =
726*4882a593Smuzhiyun 		mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get(ppcnt_pl);
727*4882a593Smuzhiyun 
728*4882a593Smuzhiyun 	stats->rx_crc_errors =
729*4882a593Smuzhiyun 		mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get(ppcnt_pl);
730*4882a593Smuzhiyun 	stats->rx_frame_errors =
731*4882a593Smuzhiyun 		mlxsw_reg_ppcnt_a_alignment_errors_get(ppcnt_pl);
732*4882a593Smuzhiyun 
733*4882a593Smuzhiyun 	stats->rx_length_errors = (
734*4882a593Smuzhiyun 		mlxsw_reg_ppcnt_a_in_range_length_errors_get(ppcnt_pl) +
735*4882a593Smuzhiyun 		mlxsw_reg_ppcnt_a_out_of_range_length_field_get(ppcnt_pl) +
736*4882a593Smuzhiyun 		mlxsw_reg_ppcnt_a_frame_too_long_errors_get(ppcnt_pl));
737*4882a593Smuzhiyun 
738*4882a593Smuzhiyun 	stats->rx_errors = (stats->rx_crc_errors +
739*4882a593Smuzhiyun 		stats->rx_frame_errors + stats->rx_length_errors);
740*4882a593Smuzhiyun 
741*4882a593Smuzhiyun out:
742*4882a593Smuzhiyun 	return err;
743*4882a593Smuzhiyun }
744*4882a593Smuzhiyun 
745*4882a593Smuzhiyun static void
mlxsw_sp_port_get_hw_xstats(struct net_device * dev,struct mlxsw_sp_port_xstats * xstats)746*4882a593Smuzhiyun mlxsw_sp_port_get_hw_xstats(struct net_device *dev,
747*4882a593Smuzhiyun 			    struct mlxsw_sp_port_xstats *xstats)
748*4882a593Smuzhiyun {
749*4882a593Smuzhiyun 	char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
750*4882a593Smuzhiyun 	int err, i;
751*4882a593Smuzhiyun 
752*4882a593Smuzhiyun 	err = mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_EXT_CNT, 0,
753*4882a593Smuzhiyun 					  ppcnt_pl);
754*4882a593Smuzhiyun 	if (!err)
755*4882a593Smuzhiyun 		xstats->ecn = mlxsw_reg_ppcnt_ecn_marked_get(ppcnt_pl);
756*4882a593Smuzhiyun 
757*4882a593Smuzhiyun 	for (i = 0; i < TC_MAX_QUEUE; i++) {
758*4882a593Smuzhiyun 		err = mlxsw_sp_port_get_stats_raw(dev,
759*4882a593Smuzhiyun 						  MLXSW_REG_PPCNT_TC_CONG_TC,
760*4882a593Smuzhiyun 						  i, ppcnt_pl);
761*4882a593Smuzhiyun 		if (!err)
762*4882a593Smuzhiyun 			xstats->wred_drop[i] =
763*4882a593Smuzhiyun 				mlxsw_reg_ppcnt_wred_discard_get(ppcnt_pl);
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun 		err = mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_TC_CNT,
766*4882a593Smuzhiyun 						  i, ppcnt_pl);
767*4882a593Smuzhiyun 		if (err)
768*4882a593Smuzhiyun 			continue;
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun 		xstats->backlog[i] =
771*4882a593Smuzhiyun 			mlxsw_reg_ppcnt_tc_transmit_queue_get(ppcnt_pl);
772*4882a593Smuzhiyun 		xstats->tail_drop[i] =
773*4882a593Smuzhiyun 			mlxsw_reg_ppcnt_tc_no_buffer_discard_uc_get(ppcnt_pl);
774*4882a593Smuzhiyun 	}
775*4882a593Smuzhiyun 
776*4882a593Smuzhiyun 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
777*4882a593Smuzhiyun 		err = mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_PRIO_CNT,
778*4882a593Smuzhiyun 						  i, ppcnt_pl);
779*4882a593Smuzhiyun 		if (err)
780*4882a593Smuzhiyun 			continue;
781*4882a593Smuzhiyun 
782*4882a593Smuzhiyun 		xstats->tx_packets[i] = mlxsw_reg_ppcnt_tx_frames_get(ppcnt_pl);
783*4882a593Smuzhiyun 		xstats->tx_bytes[i] = mlxsw_reg_ppcnt_tx_octets_get(ppcnt_pl);
784*4882a593Smuzhiyun 	}
785*4882a593Smuzhiyun }
786*4882a593Smuzhiyun 
update_stats_cache(struct work_struct * work)787*4882a593Smuzhiyun static void update_stats_cache(struct work_struct *work)
788*4882a593Smuzhiyun {
789*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port =
790*4882a593Smuzhiyun 		container_of(work, struct mlxsw_sp_port,
791*4882a593Smuzhiyun 			     periodic_hw_stats.update_dw.work);
792*4882a593Smuzhiyun 
793*4882a593Smuzhiyun 	if (!netif_carrier_ok(mlxsw_sp_port->dev))
794*4882a593Smuzhiyun 		/* Note: mlxsw_sp_port_down_wipe_counters() clears the cache as
795*4882a593Smuzhiyun 		 * necessary when port goes down.
796*4882a593Smuzhiyun 		 */
797*4882a593Smuzhiyun 		goto out;
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun 	mlxsw_sp_port_get_hw_stats(mlxsw_sp_port->dev,
800*4882a593Smuzhiyun 				   &mlxsw_sp_port->periodic_hw_stats.stats);
801*4882a593Smuzhiyun 	mlxsw_sp_port_get_hw_xstats(mlxsw_sp_port->dev,
802*4882a593Smuzhiyun 				    &mlxsw_sp_port->periodic_hw_stats.xstats);
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun out:
805*4882a593Smuzhiyun 	mlxsw_core_schedule_dw(&mlxsw_sp_port->periodic_hw_stats.update_dw,
806*4882a593Smuzhiyun 			       MLXSW_HW_STATS_UPDATE_TIME);
807*4882a593Smuzhiyun }
808*4882a593Smuzhiyun 
809*4882a593Smuzhiyun /* Return the stats from a cache that is updated periodically,
810*4882a593Smuzhiyun  * as this function might get called in an atomic context.
811*4882a593Smuzhiyun  */
812*4882a593Smuzhiyun static void
mlxsw_sp_port_get_stats64(struct net_device * dev,struct rtnl_link_stats64 * stats)813*4882a593Smuzhiyun mlxsw_sp_port_get_stats64(struct net_device *dev,
814*4882a593Smuzhiyun 			  struct rtnl_link_stats64 *stats)
815*4882a593Smuzhiyun {
816*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
817*4882a593Smuzhiyun 
818*4882a593Smuzhiyun 	memcpy(stats, &mlxsw_sp_port->periodic_hw_stats.stats, sizeof(*stats));
819*4882a593Smuzhiyun }
820*4882a593Smuzhiyun 
__mlxsw_sp_port_vlan_set(struct mlxsw_sp_port * mlxsw_sp_port,u16 vid_begin,u16 vid_end,bool is_member,bool untagged)821*4882a593Smuzhiyun static int __mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port,
822*4882a593Smuzhiyun 				    u16 vid_begin, u16 vid_end,
823*4882a593Smuzhiyun 				    bool is_member, bool untagged)
824*4882a593Smuzhiyun {
825*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
826*4882a593Smuzhiyun 	char *spvm_pl;
827*4882a593Smuzhiyun 	int err;
828*4882a593Smuzhiyun 
829*4882a593Smuzhiyun 	spvm_pl = kmalloc(MLXSW_REG_SPVM_LEN, GFP_KERNEL);
830*4882a593Smuzhiyun 	if (!spvm_pl)
831*4882a593Smuzhiyun 		return -ENOMEM;
832*4882a593Smuzhiyun 
833*4882a593Smuzhiyun 	mlxsw_reg_spvm_pack(spvm_pl, mlxsw_sp_port->local_port,	vid_begin,
834*4882a593Smuzhiyun 			    vid_end, is_member, untagged);
835*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvm), spvm_pl);
836*4882a593Smuzhiyun 	kfree(spvm_pl);
837*4882a593Smuzhiyun 	return err;
838*4882a593Smuzhiyun }
839*4882a593Smuzhiyun 
mlxsw_sp_port_vlan_set(struct mlxsw_sp_port * mlxsw_sp_port,u16 vid_begin,u16 vid_end,bool is_member,bool untagged)840*4882a593Smuzhiyun int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin,
841*4882a593Smuzhiyun 			   u16 vid_end, bool is_member, bool untagged)
842*4882a593Smuzhiyun {
843*4882a593Smuzhiyun 	u16 vid, vid_e;
844*4882a593Smuzhiyun 	int err;
845*4882a593Smuzhiyun 
846*4882a593Smuzhiyun 	for (vid = vid_begin; vid <= vid_end;
847*4882a593Smuzhiyun 	     vid += MLXSW_REG_SPVM_REC_MAX_COUNT) {
848*4882a593Smuzhiyun 		vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1),
849*4882a593Smuzhiyun 			    vid_end);
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun 		err = __mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e,
852*4882a593Smuzhiyun 					       is_member, untagged);
853*4882a593Smuzhiyun 		if (err)
854*4882a593Smuzhiyun 			return err;
855*4882a593Smuzhiyun 	}
856*4882a593Smuzhiyun 
857*4882a593Smuzhiyun 	return 0;
858*4882a593Smuzhiyun }
859*4882a593Smuzhiyun 
mlxsw_sp_port_vlan_flush(struct mlxsw_sp_port * mlxsw_sp_port,bool flush_default)860*4882a593Smuzhiyun static void mlxsw_sp_port_vlan_flush(struct mlxsw_sp_port *mlxsw_sp_port,
861*4882a593Smuzhiyun 				     bool flush_default)
862*4882a593Smuzhiyun {
863*4882a593Smuzhiyun 	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan, *tmp;
864*4882a593Smuzhiyun 
865*4882a593Smuzhiyun 	list_for_each_entry_safe(mlxsw_sp_port_vlan, tmp,
866*4882a593Smuzhiyun 				 &mlxsw_sp_port->vlans_list, list) {
867*4882a593Smuzhiyun 		if (!flush_default &&
868*4882a593Smuzhiyun 		    mlxsw_sp_port_vlan->vid == MLXSW_SP_DEFAULT_VID)
869*4882a593Smuzhiyun 			continue;
870*4882a593Smuzhiyun 		mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
871*4882a593Smuzhiyun 	}
872*4882a593Smuzhiyun }
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun static void
mlxsw_sp_port_vlan_cleanup(struct mlxsw_sp_port_vlan * mlxsw_sp_port_vlan)875*4882a593Smuzhiyun mlxsw_sp_port_vlan_cleanup(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
876*4882a593Smuzhiyun {
877*4882a593Smuzhiyun 	if (mlxsw_sp_port_vlan->bridge_port)
878*4882a593Smuzhiyun 		mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan);
879*4882a593Smuzhiyun 	else if (mlxsw_sp_port_vlan->fid)
880*4882a593Smuzhiyun 		mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan);
881*4882a593Smuzhiyun }
882*4882a593Smuzhiyun 
883*4882a593Smuzhiyun struct mlxsw_sp_port_vlan *
mlxsw_sp_port_vlan_create(struct mlxsw_sp_port * mlxsw_sp_port,u16 vid)884*4882a593Smuzhiyun mlxsw_sp_port_vlan_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
885*4882a593Smuzhiyun {
886*4882a593Smuzhiyun 	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
887*4882a593Smuzhiyun 	bool untagged = vid == MLXSW_SP_DEFAULT_VID;
888*4882a593Smuzhiyun 	int err;
889*4882a593Smuzhiyun 
890*4882a593Smuzhiyun 	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
891*4882a593Smuzhiyun 	if (mlxsw_sp_port_vlan)
892*4882a593Smuzhiyun 		return ERR_PTR(-EEXIST);
893*4882a593Smuzhiyun 
894*4882a593Smuzhiyun 	err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, untagged);
895*4882a593Smuzhiyun 	if (err)
896*4882a593Smuzhiyun 		return ERR_PTR(err);
897*4882a593Smuzhiyun 
898*4882a593Smuzhiyun 	mlxsw_sp_port_vlan = kzalloc(sizeof(*mlxsw_sp_port_vlan), GFP_KERNEL);
899*4882a593Smuzhiyun 	if (!mlxsw_sp_port_vlan) {
900*4882a593Smuzhiyun 		err = -ENOMEM;
901*4882a593Smuzhiyun 		goto err_port_vlan_alloc;
902*4882a593Smuzhiyun 	}
903*4882a593Smuzhiyun 
904*4882a593Smuzhiyun 	mlxsw_sp_port_vlan->mlxsw_sp_port = mlxsw_sp_port;
905*4882a593Smuzhiyun 	mlxsw_sp_port_vlan->vid = vid;
906*4882a593Smuzhiyun 	list_add(&mlxsw_sp_port_vlan->list, &mlxsw_sp_port->vlans_list);
907*4882a593Smuzhiyun 
908*4882a593Smuzhiyun 	return mlxsw_sp_port_vlan;
909*4882a593Smuzhiyun 
910*4882a593Smuzhiyun err_port_vlan_alloc:
911*4882a593Smuzhiyun 	mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
912*4882a593Smuzhiyun 	return ERR_PTR(err);
913*4882a593Smuzhiyun }
914*4882a593Smuzhiyun 
mlxsw_sp_port_vlan_destroy(struct mlxsw_sp_port_vlan * mlxsw_sp_port_vlan)915*4882a593Smuzhiyun void mlxsw_sp_port_vlan_destroy(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan)
916*4882a593Smuzhiyun {
917*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port;
918*4882a593Smuzhiyun 	u16 vid = mlxsw_sp_port_vlan->vid;
919*4882a593Smuzhiyun 
920*4882a593Smuzhiyun 	mlxsw_sp_port_vlan_cleanup(mlxsw_sp_port_vlan);
921*4882a593Smuzhiyun 	list_del(&mlxsw_sp_port_vlan->list);
922*4882a593Smuzhiyun 	kfree(mlxsw_sp_port_vlan);
923*4882a593Smuzhiyun 	mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
924*4882a593Smuzhiyun }
925*4882a593Smuzhiyun 
mlxsw_sp_port_add_vid(struct net_device * dev,__be16 __always_unused proto,u16 vid)926*4882a593Smuzhiyun static int mlxsw_sp_port_add_vid(struct net_device *dev,
927*4882a593Smuzhiyun 				 __be16 __always_unused proto, u16 vid)
928*4882a593Smuzhiyun {
929*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
930*4882a593Smuzhiyun 
931*4882a593Smuzhiyun 	/* VLAN 0 is added to HW filter when device goes up, but it is
932*4882a593Smuzhiyun 	 * reserved in our case, so simply return.
933*4882a593Smuzhiyun 	 */
934*4882a593Smuzhiyun 	if (!vid)
935*4882a593Smuzhiyun 		return 0;
936*4882a593Smuzhiyun 
937*4882a593Smuzhiyun 	return PTR_ERR_OR_ZERO(mlxsw_sp_port_vlan_create(mlxsw_sp_port, vid));
938*4882a593Smuzhiyun }
939*4882a593Smuzhiyun 
mlxsw_sp_port_kill_vid(struct net_device * dev,__be16 __always_unused proto,u16 vid)940*4882a593Smuzhiyun static int mlxsw_sp_port_kill_vid(struct net_device *dev,
941*4882a593Smuzhiyun 				  __be16 __always_unused proto, u16 vid)
942*4882a593Smuzhiyun {
943*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
944*4882a593Smuzhiyun 	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
945*4882a593Smuzhiyun 
946*4882a593Smuzhiyun 	/* VLAN 0 is removed from HW filter when device goes down, but
947*4882a593Smuzhiyun 	 * it is reserved in our case, so simply return.
948*4882a593Smuzhiyun 	 */
949*4882a593Smuzhiyun 	if (!vid)
950*4882a593Smuzhiyun 		return 0;
951*4882a593Smuzhiyun 
952*4882a593Smuzhiyun 	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid);
953*4882a593Smuzhiyun 	if (!mlxsw_sp_port_vlan)
954*4882a593Smuzhiyun 		return 0;
955*4882a593Smuzhiyun 	mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
956*4882a593Smuzhiyun 
957*4882a593Smuzhiyun 	return 0;
958*4882a593Smuzhiyun }
959*4882a593Smuzhiyun 
mlxsw_sp_setup_tc_block(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f)960*4882a593Smuzhiyun static int mlxsw_sp_setup_tc_block(struct mlxsw_sp_port *mlxsw_sp_port,
961*4882a593Smuzhiyun 				   struct flow_block_offload *f)
962*4882a593Smuzhiyun {
963*4882a593Smuzhiyun 	switch (f->binder_type) {
964*4882a593Smuzhiyun 	case FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS:
965*4882a593Smuzhiyun 		return mlxsw_sp_setup_tc_block_clsact(mlxsw_sp_port, f, true);
966*4882a593Smuzhiyun 	case FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS:
967*4882a593Smuzhiyun 		return mlxsw_sp_setup_tc_block_clsact(mlxsw_sp_port, f, false);
968*4882a593Smuzhiyun 	case FLOW_BLOCK_BINDER_TYPE_RED_EARLY_DROP:
969*4882a593Smuzhiyun 		return mlxsw_sp_setup_tc_block_qevent_early_drop(mlxsw_sp_port, f);
970*4882a593Smuzhiyun 	default:
971*4882a593Smuzhiyun 		return -EOPNOTSUPP;
972*4882a593Smuzhiyun 	}
973*4882a593Smuzhiyun }
974*4882a593Smuzhiyun 
mlxsw_sp_setup_tc(struct net_device * dev,enum tc_setup_type type,void * type_data)975*4882a593Smuzhiyun static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type,
976*4882a593Smuzhiyun 			     void *type_data)
977*4882a593Smuzhiyun {
978*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
979*4882a593Smuzhiyun 
980*4882a593Smuzhiyun 	switch (type) {
981*4882a593Smuzhiyun 	case TC_SETUP_BLOCK:
982*4882a593Smuzhiyun 		return mlxsw_sp_setup_tc_block(mlxsw_sp_port, type_data);
983*4882a593Smuzhiyun 	case TC_SETUP_QDISC_RED:
984*4882a593Smuzhiyun 		return mlxsw_sp_setup_tc_red(mlxsw_sp_port, type_data);
985*4882a593Smuzhiyun 	case TC_SETUP_QDISC_PRIO:
986*4882a593Smuzhiyun 		return mlxsw_sp_setup_tc_prio(mlxsw_sp_port, type_data);
987*4882a593Smuzhiyun 	case TC_SETUP_QDISC_ETS:
988*4882a593Smuzhiyun 		return mlxsw_sp_setup_tc_ets(mlxsw_sp_port, type_data);
989*4882a593Smuzhiyun 	case TC_SETUP_QDISC_TBF:
990*4882a593Smuzhiyun 		return mlxsw_sp_setup_tc_tbf(mlxsw_sp_port, type_data);
991*4882a593Smuzhiyun 	case TC_SETUP_QDISC_FIFO:
992*4882a593Smuzhiyun 		return mlxsw_sp_setup_tc_fifo(mlxsw_sp_port, type_data);
993*4882a593Smuzhiyun 	default:
994*4882a593Smuzhiyun 		return -EOPNOTSUPP;
995*4882a593Smuzhiyun 	}
996*4882a593Smuzhiyun }
997*4882a593Smuzhiyun 
mlxsw_sp_feature_hw_tc(struct net_device * dev,bool enable)998*4882a593Smuzhiyun static int mlxsw_sp_feature_hw_tc(struct net_device *dev, bool enable)
999*4882a593Smuzhiyun {
1000*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
1001*4882a593Smuzhiyun 
1002*4882a593Smuzhiyun 	if (!enable) {
1003*4882a593Smuzhiyun 		if (mlxsw_sp_flow_block_rule_count(mlxsw_sp_port->ing_flow_block) ||
1004*4882a593Smuzhiyun 		    mlxsw_sp_flow_block_rule_count(mlxsw_sp_port->eg_flow_block)) {
1005*4882a593Smuzhiyun 			netdev_err(dev, "Active offloaded tc filters, can't turn hw_tc_offload off\n");
1006*4882a593Smuzhiyun 			return -EINVAL;
1007*4882a593Smuzhiyun 		}
1008*4882a593Smuzhiyun 		mlxsw_sp_flow_block_disable_inc(mlxsw_sp_port->ing_flow_block);
1009*4882a593Smuzhiyun 		mlxsw_sp_flow_block_disable_inc(mlxsw_sp_port->eg_flow_block);
1010*4882a593Smuzhiyun 	} else {
1011*4882a593Smuzhiyun 		mlxsw_sp_flow_block_disable_dec(mlxsw_sp_port->ing_flow_block);
1012*4882a593Smuzhiyun 		mlxsw_sp_flow_block_disable_dec(mlxsw_sp_port->eg_flow_block);
1013*4882a593Smuzhiyun 	}
1014*4882a593Smuzhiyun 	return 0;
1015*4882a593Smuzhiyun }
1016*4882a593Smuzhiyun 
mlxsw_sp_feature_loopback(struct net_device * dev,bool enable)1017*4882a593Smuzhiyun static int mlxsw_sp_feature_loopback(struct net_device *dev, bool enable)
1018*4882a593Smuzhiyun {
1019*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
1020*4882a593Smuzhiyun 	char pplr_pl[MLXSW_REG_PPLR_LEN];
1021*4882a593Smuzhiyun 	int err;
1022*4882a593Smuzhiyun 
1023*4882a593Smuzhiyun 	if (netif_running(dev))
1024*4882a593Smuzhiyun 		mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
1025*4882a593Smuzhiyun 
1026*4882a593Smuzhiyun 	mlxsw_reg_pplr_pack(pplr_pl, mlxsw_sp_port->local_port, enable);
1027*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pplr),
1028*4882a593Smuzhiyun 			      pplr_pl);
1029*4882a593Smuzhiyun 
1030*4882a593Smuzhiyun 	if (netif_running(dev))
1031*4882a593Smuzhiyun 		mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true);
1032*4882a593Smuzhiyun 
1033*4882a593Smuzhiyun 	return err;
1034*4882a593Smuzhiyun }
1035*4882a593Smuzhiyun 
1036*4882a593Smuzhiyun typedef int (*mlxsw_sp_feature_handler)(struct net_device *dev, bool enable);
1037*4882a593Smuzhiyun 
mlxsw_sp_handle_feature(struct net_device * dev,netdev_features_t wanted_features,netdev_features_t feature,mlxsw_sp_feature_handler feature_handler)1038*4882a593Smuzhiyun static int mlxsw_sp_handle_feature(struct net_device *dev,
1039*4882a593Smuzhiyun 				   netdev_features_t wanted_features,
1040*4882a593Smuzhiyun 				   netdev_features_t feature,
1041*4882a593Smuzhiyun 				   mlxsw_sp_feature_handler feature_handler)
1042*4882a593Smuzhiyun {
1043*4882a593Smuzhiyun 	netdev_features_t changes = wanted_features ^ dev->features;
1044*4882a593Smuzhiyun 	bool enable = !!(wanted_features & feature);
1045*4882a593Smuzhiyun 	int err;
1046*4882a593Smuzhiyun 
1047*4882a593Smuzhiyun 	if (!(changes & feature))
1048*4882a593Smuzhiyun 		return 0;
1049*4882a593Smuzhiyun 
1050*4882a593Smuzhiyun 	err = feature_handler(dev, enable);
1051*4882a593Smuzhiyun 	if (err) {
1052*4882a593Smuzhiyun 		netdev_err(dev, "%s feature %pNF failed, err %d\n",
1053*4882a593Smuzhiyun 			   enable ? "Enable" : "Disable", &feature, err);
1054*4882a593Smuzhiyun 		return err;
1055*4882a593Smuzhiyun 	}
1056*4882a593Smuzhiyun 
1057*4882a593Smuzhiyun 	if (enable)
1058*4882a593Smuzhiyun 		dev->features |= feature;
1059*4882a593Smuzhiyun 	else
1060*4882a593Smuzhiyun 		dev->features &= ~feature;
1061*4882a593Smuzhiyun 
1062*4882a593Smuzhiyun 	return 0;
1063*4882a593Smuzhiyun }
mlxsw_sp_set_features(struct net_device * dev,netdev_features_t features)1064*4882a593Smuzhiyun static int mlxsw_sp_set_features(struct net_device *dev,
1065*4882a593Smuzhiyun 				 netdev_features_t features)
1066*4882a593Smuzhiyun {
1067*4882a593Smuzhiyun 	netdev_features_t oper_features = dev->features;
1068*4882a593Smuzhiyun 	int err = 0;
1069*4882a593Smuzhiyun 
1070*4882a593Smuzhiyun 	err |= mlxsw_sp_handle_feature(dev, features, NETIF_F_HW_TC,
1071*4882a593Smuzhiyun 				       mlxsw_sp_feature_hw_tc);
1072*4882a593Smuzhiyun 	err |= mlxsw_sp_handle_feature(dev, features, NETIF_F_LOOPBACK,
1073*4882a593Smuzhiyun 				       mlxsw_sp_feature_loopback);
1074*4882a593Smuzhiyun 
1075*4882a593Smuzhiyun 	if (err) {
1076*4882a593Smuzhiyun 		dev->features = oper_features;
1077*4882a593Smuzhiyun 		return -EINVAL;
1078*4882a593Smuzhiyun 	}
1079*4882a593Smuzhiyun 
1080*4882a593Smuzhiyun 	return 0;
1081*4882a593Smuzhiyun }
1082*4882a593Smuzhiyun 
1083*4882a593Smuzhiyun static struct devlink_port *
mlxsw_sp_port_get_devlink_port(struct net_device * dev)1084*4882a593Smuzhiyun mlxsw_sp_port_get_devlink_port(struct net_device *dev)
1085*4882a593Smuzhiyun {
1086*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
1087*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1088*4882a593Smuzhiyun 
1089*4882a593Smuzhiyun 	return mlxsw_core_port_devlink_port_get(mlxsw_sp->core,
1090*4882a593Smuzhiyun 						mlxsw_sp_port->local_port);
1091*4882a593Smuzhiyun }
1092*4882a593Smuzhiyun 
mlxsw_sp_port_hwtstamp_set(struct mlxsw_sp_port * mlxsw_sp_port,struct ifreq * ifr)1093*4882a593Smuzhiyun static int mlxsw_sp_port_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
1094*4882a593Smuzhiyun 				      struct ifreq *ifr)
1095*4882a593Smuzhiyun {
1096*4882a593Smuzhiyun 	struct hwtstamp_config config;
1097*4882a593Smuzhiyun 	int err;
1098*4882a593Smuzhiyun 
1099*4882a593Smuzhiyun 	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
1100*4882a593Smuzhiyun 		return -EFAULT;
1101*4882a593Smuzhiyun 
1102*4882a593Smuzhiyun 	err = mlxsw_sp_port->mlxsw_sp->ptp_ops->hwtstamp_set(mlxsw_sp_port,
1103*4882a593Smuzhiyun 							     &config);
1104*4882a593Smuzhiyun 	if (err)
1105*4882a593Smuzhiyun 		return err;
1106*4882a593Smuzhiyun 
1107*4882a593Smuzhiyun 	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
1108*4882a593Smuzhiyun 		return -EFAULT;
1109*4882a593Smuzhiyun 
1110*4882a593Smuzhiyun 	return 0;
1111*4882a593Smuzhiyun }
1112*4882a593Smuzhiyun 
mlxsw_sp_port_hwtstamp_get(struct mlxsw_sp_port * mlxsw_sp_port,struct ifreq * ifr)1113*4882a593Smuzhiyun static int mlxsw_sp_port_hwtstamp_get(struct mlxsw_sp_port *mlxsw_sp_port,
1114*4882a593Smuzhiyun 				      struct ifreq *ifr)
1115*4882a593Smuzhiyun {
1116*4882a593Smuzhiyun 	struct hwtstamp_config config;
1117*4882a593Smuzhiyun 	int err;
1118*4882a593Smuzhiyun 
1119*4882a593Smuzhiyun 	err = mlxsw_sp_port->mlxsw_sp->ptp_ops->hwtstamp_get(mlxsw_sp_port,
1120*4882a593Smuzhiyun 							     &config);
1121*4882a593Smuzhiyun 	if (err)
1122*4882a593Smuzhiyun 		return err;
1123*4882a593Smuzhiyun 
1124*4882a593Smuzhiyun 	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
1125*4882a593Smuzhiyun 		return -EFAULT;
1126*4882a593Smuzhiyun 
1127*4882a593Smuzhiyun 	return 0;
1128*4882a593Smuzhiyun }
1129*4882a593Smuzhiyun 
mlxsw_sp_port_ptp_clear(struct mlxsw_sp_port * mlxsw_sp_port)1130*4882a593Smuzhiyun static inline void mlxsw_sp_port_ptp_clear(struct mlxsw_sp_port *mlxsw_sp_port)
1131*4882a593Smuzhiyun {
1132*4882a593Smuzhiyun 	struct hwtstamp_config config = {0};
1133*4882a593Smuzhiyun 
1134*4882a593Smuzhiyun 	mlxsw_sp_port->mlxsw_sp->ptp_ops->hwtstamp_set(mlxsw_sp_port, &config);
1135*4882a593Smuzhiyun }
1136*4882a593Smuzhiyun 
1137*4882a593Smuzhiyun static int
mlxsw_sp_port_ioctl(struct net_device * dev,struct ifreq * ifr,int cmd)1138*4882a593Smuzhiyun mlxsw_sp_port_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
1139*4882a593Smuzhiyun {
1140*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
1141*4882a593Smuzhiyun 
1142*4882a593Smuzhiyun 	switch (cmd) {
1143*4882a593Smuzhiyun 	case SIOCSHWTSTAMP:
1144*4882a593Smuzhiyun 		return mlxsw_sp_port_hwtstamp_set(mlxsw_sp_port, ifr);
1145*4882a593Smuzhiyun 	case SIOCGHWTSTAMP:
1146*4882a593Smuzhiyun 		return mlxsw_sp_port_hwtstamp_get(mlxsw_sp_port, ifr);
1147*4882a593Smuzhiyun 	default:
1148*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1149*4882a593Smuzhiyun 	}
1150*4882a593Smuzhiyun }
1151*4882a593Smuzhiyun 
1152*4882a593Smuzhiyun static const struct net_device_ops mlxsw_sp_port_netdev_ops = {
1153*4882a593Smuzhiyun 	.ndo_open		= mlxsw_sp_port_open,
1154*4882a593Smuzhiyun 	.ndo_stop		= mlxsw_sp_port_stop,
1155*4882a593Smuzhiyun 	.ndo_start_xmit		= mlxsw_sp_port_xmit,
1156*4882a593Smuzhiyun 	.ndo_setup_tc           = mlxsw_sp_setup_tc,
1157*4882a593Smuzhiyun 	.ndo_set_rx_mode	= mlxsw_sp_set_rx_mode,
1158*4882a593Smuzhiyun 	.ndo_set_mac_address	= mlxsw_sp_port_set_mac_address,
1159*4882a593Smuzhiyun 	.ndo_change_mtu		= mlxsw_sp_port_change_mtu,
1160*4882a593Smuzhiyun 	.ndo_get_stats64	= mlxsw_sp_port_get_stats64,
1161*4882a593Smuzhiyun 	.ndo_has_offload_stats	= mlxsw_sp_port_has_offload_stats,
1162*4882a593Smuzhiyun 	.ndo_get_offload_stats	= mlxsw_sp_port_get_offload_stats,
1163*4882a593Smuzhiyun 	.ndo_vlan_rx_add_vid	= mlxsw_sp_port_add_vid,
1164*4882a593Smuzhiyun 	.ndo_vlan_rx_kill_vid	= mlxsw_sp_port_kill_vid,
1165*4882a593Smuzhiyun 	.ndo_set_features	= mlxsw_sp_set_features,
1166*4882a593Smuzhiyun 	.ndo_get_devlink_port	= mlxsw_sp_port_get_devlink_port,
1167*4882a593Smuzhiyun 	.ndo_do_ioctl		= mlxsw_sp_port_ioctl,
1168*4882a593Smuzhiyun };
1169*4882a593Smuzhiyun 
1170*4882a593Smuzhiyun static int
mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port * mlxsw_sp_port)1171*4882a593Smuzhiyun mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port)
1172*4882a593Smuzhiyun {
1173*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1174*4882a593Smuzhiyun 	u32 eth_proto_cap, eth_proto_admin, eth_proto_oper;
1175*4882a593Smuzhiyun 	const struct mlxsw_sp_port_type_speed_ops *ops;
1176*4882a593Smuzhiyun 	char ptys_pl[MLXSW_REG_PTYS_LEN];
1177*4882a593Smuzhiyun 	u32 eth_proto_cap_masked;
1178*4882a593Smuzhiyun 	int err;
1179*4882a593Smuzhiyun 
1180*4882a593Smuzhiyun 	ops = mlxsw_sp->port_type_speed_ops;
1181*4882a593Smuzhiyun 
1182*4882a593Smuzhiyun 	/* Set advertised speeds to speeds supported by both the driver
1183*4882a593Smuzhiyun 	 * and the device.
1184*4882a593Smuzhiyun 	 */
1185*4882a593Smuzhiyun 	ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
1186*4882a593Smuzhiyun 			       0, false);
1187*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
1188*4882a593Smuzhiyun 	if (err)
1189*4882a593Smuzhiyun 		return err;
1190*4882a593Smuzhiyun 
1191*4882a593Smuzhiyun 	ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, &eth_proto_cap,
1192*4882a593Smuzhiyun 				 &eth_proto_admin, &eth_proto_oper);
1193*4882a593Smuzhiyun 	eth_proto_cap_masked = ops->ptys_proto_cap_masked_get(eth_proto_cap);
1194*4882a593Smuzhiyun 	ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
1195*4882a593Smuzhiyun 			       eth_proto_cap_masked,
1196*4882a593Smuzhiyun 			       mlxsw_sp_port->link.autoneg);
1197*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
1198*4882a593Smuzhiyun }
1199*4882a593Smuzhiyun 
mlxsw_sp_port_speed_get(struct mlxsw_sp_port * mlxsw_sp_port,u32 * speed)1200*4882a593Smuzhiyun int mlxsw_sp_port_speed_get(struct mlxsw_sp_port *mlxsw_sp_port, u32 *speed)
1201*4882a593Smuzhiyun {
1202*4882a593Smuzhiyun 	const struct mlxsw_sp_port_type_speed_ops *port_type_speed_ops;
1203*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1204*4882a593Smuzhiyun 	char ptys_pl[MLXSW_REG_PTYS_LEN];
1205*4882a593Smuzhiyun 	u32 eth_proto_oper;
1206*4882a593Smuzhiyun 	int err;
1207*4882a593Smuzhiyun 
1208*4882a593Smuzhiyun 	port_type_speed_ops = mlxsw_sp->port_type_speed_ops;
1209*4882a593Smuzhiyun 	port_type_speed_ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl,
1210*4882a593Smuzhiyun 					       mlxsw_sp_port->local_port, 0,
1211*4882a593Smuzhiyun 					       false);
1212*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
1213*4882a593Smuzhiyun 	if (err)
1214*4882a593Smuzhiyun 		return err;
1215*4882a593Smuzhiyun 	port_type_speed_ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, NULL, NULL,
1216*4882a593Smuzhiyun 						 &eth_proto_oper);
1217*4882a593Smuzhiyun 	*speed = port_type_speed_ops->from_ptys_speed(mlxsw_sp, eth_proto_oper);
1218*4882a593Smuzhiyun 	return 0;
1219*4882a593Smuzhiyun }
1220*4882a593Smuzhiyun 
mlxsw_sp_port_ets_set(struct mlxsw_sp_port * mlxsw_sp_port,enum mlxsw_reg_qeec_hr hr,u8 index,u8 next_index,bool dwrr,u8 dwrr_weight)1221*4882a593Smuzhiyun int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,
1222*4882a593Smuzhiyun 			  enum mlxsw_reg_qeec_hr hr, u8 index, u8 next_index,
1223*4882a593Smuzhiyun 			  bool dwrr, u8 dwrr_weight)
1224*4882a593Smuzhiyun {
1225*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1226*4882a593Smuzhiyun 	char qeec_pl[MLXSW_REG_QEEC_LEN];
1227*4882a593Smuzhiyun 
1228*4882a593Smuzhiyun 	mlxsw_reg_qeec_pack(qeec_pl, mlxsw_sp_port->local_port, hr, index,
1229*4882a593Smuzhiyun 			    next_index);
1230*4882a593Smuzhiyun 	mlxsw_reg_qeec_de_set(qeec_pl, true);
1231*4882a593Smuzhiyun 	mlxsw_reg_qeec_dwrr_set(qeec_pl, dwrr);
1232*4882a593Smuzhiyun 	mlxsw_reg_qeec_dwrr_weight_set(qeec_pl, dwrr_weight);
1233*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl);
1234*4882a593Smuzhiyun }
1235*4882a593Smuzhiyun 
mlxsw_sp_port_ets_maxrate_set(struct mlxsw_sp_port * mlxsw_sp_port,enum mlxsw_reg_qeec_hr hr,u8 index,u8 next_index,u32 maxrate,u8 burst_size)1236*4882a593Smuzhiyun int mlxsw_sp_port_ets_maxrate_set(struct mlxsw_sp_port *mlxsw_sp_port,
1237*4882a593Smuzhiyun 				  enum mlxsw_reg_qeec_hr hr, u8 index,
1238*4882a593Smuzhiyun 				  u8 next_index, u32 maxrate, u8 burst_size)
1239*4882a593Smuzhiyun {
1240*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1241*4882a593Smuzhiyun 	char qeec_pl[MLXSW_REG_QEEC_LEN];
1242*4882a593Smuzhiyun 
1243*4882a593Smuzhiyun 	mlxsw_reg_qeec_pack(qeec_pl, mlxsw_sp_port->local_port, hr, index,
1244*4882a593Smuzhiyun 			    next_index);
1245*4882a593Smuzhiyun 	mlxsw_reg_qeec_mase_set(qeec_pl, true);
1246*4882a593Smuzhiyun 	mlxsw_reg_qeec_max_shaper_rate_set(qeec_pl, maxrate);
1247*4882a593Smuzhiyun 	mlxsw_reg_qeec_max_shaper_bs_set(qeec_pl, burst_size);
1248*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl);
1249*4882a593Smuzhiyun }
1250*4882a593Smuzhiyun 
mlxsw_sp_port_min_bw_set(struct mlxsw_sp_port * mlxsw_sp_port,enum mlxsw_reg_qeec_hr hr,u8 index,u8 next_index,u32 minrate)1251*4882a593Smuzhiyun static int mlxsw_sp_port_min_bw_set(struct mlxsw_sp_port *mlxsw_sp_port,
1252*4882a593Smuzhiyun 				    enum mlxsw_reg_qeec_hr hr, u8 index,
1253*4882a593Smuzhiyun 				    u8 next_index, u32 minrate)
1254*4882a593Smuzhiyun {
1255*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1256*4882a593Smuzhiyun 	char qeec_pl[MLXSW_REG_QEEC_LEN];
1257*4882a593Smuzhiyun 
1258*4882a593Smuzhiyun 	mlxsw_reg_qeec_pack(qeec_pl, mlxsw_sp_port->local_port, hr, index,
1259*4882a593Smuzhiyun 			    next_index);
1260*4882a593Smuzhiyun 	mlxsw_reg_qeec_mise_set(qeec_pl, true);
1261*4882a593Smuzhiyun 	mlxsw_reg_qeec_min_shaper_rate_set(qeec_pl, minrate);
1262*4882a593Smuzhiyun 
1263*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl);
1264*4882a593Smuzhiyun }
1265*4882a593Smuzhiyun 
mlxsw_sp_port_prio_tc_set(struct mlxsw_sp_port * mlxsw_sp_port,u8 switch_prio,u8 tclass)1266*4882a593Smuzhiyun int mlxsw_sp_port_prio_tc_set(struct mlxsw_sp_port *mlxsw_sp_port,
1267*4882a593Smuzhiyun 			      u8 switch_prio, u8 tclass)
1268*4882a593Smuzhiyun {
1269*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1270*4882a593Smuzhiyun 	char qtct_pl[MLXSW_REG_QTCT_LEN];
1271*4882a593Smuzhiyun 
1272*4882a593Smuzhiyun 	mlxsw_reg_qtct_pack(qtct_pl, mlxsw_sp_port->local_port, switch_prio,
1273*4882a593Smuzhiyun 			    tclass);
1274*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qtct), qtct_pl);
1275*4882a593Smuzhiyun }
1276*4882a593Smuzhiyun 
mlxsw_sp_port_ets_init(struct mlxsw_sp_port * mlxsw_sp_port)1277*4882a593Smuzhiyun static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port)
1278*4882a593Smuzhiyun {
1279*4882a593Smuzhiyun 	int err, i;
1280*4882a593Smuzhiyun 
1281*4882a593Smuzhiyun 	/* Setup the elements hierarcy, so that each TC is linked to
1282*4882a593Smuzhiyun 	 * one subgroup, which are all member in the same group.
1283*4882a593Smuzhiyun 	 */
1284*4882a593Smuzhiyun 	err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
1285*4882a593Smuzhiyun 				    MLXSW_REG_QEEC_HR_GROUP, 0, 0, false, 0);
1286*4882a593Smuzhiyun 	if (err)
1287*4882a593Smuzhiyun 		return err;
1288*4882a593Smuzhiyun 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
1289*4882a593Smuzhiyun 		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
1290*4882a593Smuzhiyun 					    MLXSW_REG_QEEC_HR_SUBGROUP, i,
1291*4882a593Smuzhiyun 					    0, false, 0);
1292*4882a593Smuzhiyun 		if (err)
1293*4882a593Smuzhiyun 			return err;
1294*4882a593Smuzhiyun 	}
1295*4882a593Smuzhiyun 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
1296*4882a593Smuzhiyun 		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
1297*4882a593Smuzhiyun 					    MLXSW_REG_QEEC_HR_TC, i, i,
1298*4882a593Smuzhiyun 					    false, 0);
1299*4882a593Smuzhiyun 		if (err)
1300*4882a593Smuzhiyun 			return err;
1301*4882a593Smuzhiyun 
1302*4882a593Smuzhiyun 		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
1303*4882a593Smuzhiyun 					    MLXSW_REG_QEEC_HR_TC,
1304*4882a593Smuzhiyun 					    i + 8, i,
1305*4882a593Smuzhiyun 					    true, 100);
1306*4882a593Smuzhiyun 		if (err)
1307*4882a593Smuzhiyun 			return err;
1308*4882a593Smuzhiyun 	}
1309*4882a593Smuzhiyun 
1310*4882a593Smuzhiyun 	/* Make sure the max shaper is disabled in all hierarchies that support
1311*4882a593Smuzhiyun 	 * it. Note that this disables ptps (PTP shaper), but that is intended
1312*4882a593Smuzhiyun 	 * for the initial configuration.
1313*4882a593Smuzhiyun 	 */
1314*4882a593Smuzhiyun 	err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
1315*4882a593Smuzhiyun 					    MLXSW_REG_QEEC_HR_PORT, 0, 0,
1316*4882a593Smuzhiyun 					    MLXSW_REG_QEEC_MAS_DIS, 0);
1317*4882a593Smuzhiyun 	if (err)
1318*4882a593Smuzhiyun 		return err;
1319*4882a593Smuzhiyun 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
1320*4882a593Smuzhiyun 		err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
1321*4882a593Smuzhiyun 						    MLXSW_REG_QEEC_HR_SUBGROUP,
1322*4882a593Smuzhiyun 						    i, 0,
1323*4882a593Smuzhiyun 						    MLXSW_REG_QEEC_MAS_DIS, 0);
1324*4882a593Smuzhiyun 		if (err)
1325*4882a593Smuzhiyun 			return err;
1326*4882a593Smuzhiyun 	}
1327*4882a593Smuzhiyun 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
1328*4882a593Smuzhiyun 		err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
1329*4882a593Smuzhiyun 						    MLXSW_REG_QEEC_HR_TC,
1330*4882a593Smuzhiyun 						    i, i,
1331*4882a593Smuzhiyun 						    MLXSW_REG_QEEC_MAS_DIS, 0);
1332*4882a593Smuzhiyun 		if (err)
1333*4882a593Smuzhiyun 			return err;
1334*4882a593Smuzhiyun 
1335*4882a593Smuzhiyun 		err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
1336*4882a593Smuzhiyun 						    MLXSW_REG_QEEC_HR_TC,
1337*4882a593Smuzhiyun 						    i + 8, i,
1338*4882a593Smuzhiyun 						    MLXSW_REG_QEEC_MAS_DIS, 0);
1339*4882a593Smuzhiyun 		if (err)
1340*4882a593Smuzhiyun 			return err;
1341*4882a593Smuzhiyun 	}
1342*4882a593Smuzhiyun 
1343*4882a593Smuzhiyun 	/* Configure the min shaper for multicast TCs. */
1344*4882a593Smuzhiyun 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
1345*4882a593Smuzhiyun 		err = mlxsw_sp_port_min_bw_set(mlxsw_sp_port,
1346*4882a593Smuzhiyun 					       MLXSW_REG_QEEC_HR_TC,
1347*4882a593Smuzhiyun 					       i + 8, i,
1348*4882a593Smuzhiyun 					       MLXSW_REG_QEEC_MIS_MIN);
1349*4882a593Smuzhiyun 		if (err)
1350*4882a593Smuzhiyun 			return err;
1351*4882a593Smuzhiyun 	}
1352*4882a593Smuzhiyun 
1353*4882a593Smuzhiyun 	/* Map all priorities to traffic class 0. */
1354*4882a593Smuzhiyun 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
1355*4882a593Smuzhiyun 		err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, 0);
1356*4882a593Smuzhiyun 		if (err)
1357*4882a593Smuzhiyun 			return err;
1358*4882a593Smuzhiyun 	}
1359*4882a593Smuzhiyun 
1360*4882a593Smuzhiyun 	return 0;
1361*4882a593Smuzhiyun }
1362*4882a593Smuzhiyun 
mlxsw_sp_port_tc_mc_mode_set(struct mlxsw_sp_port * mlxsw_sp_port,bool enable)1363*4882a593Smuzhiyun static int mlxsw_sp_port_tc_mc_mode_set(struct mlxsw_sp_port *mlxsw_sp_port,
1364*4882a593Smuzhiyun 					bool enable)
1365*4882a593Smuzhiyun {
1366*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1367*4882a593Smuzhiyun 	char qtctm_pl[MLXSW_REG_QTCTM_LEN];
1368*4882a593Smuzhiyun 
1369*4882a593Smuzhiyun 	mlxsw_reg_qtctm_pack(qtctm_pl, mlxsw_sp_port->local_port, enable);
1370*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qtctm), qtctm_pl);
1371*4882a593Smuzhiyun }
1372*4882a593Smuzhiyun 
mlxsw_sp_port_overheat_init_val_set(struct mlxsw_sp_port * mlxsw_sp_port)1373*4882a593Smuzhiyun static int mlxsw_sp_port_overheat_init_val_set(struct mlxsw_sp_port *mlxsw_sp_port)
1374*4882a593Smuzhiyun {
1375*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1376*4882a593Smuzhiyun 	u8 module = mlxsw_sp_port->mapping.module;
1377*4882a593Smuzhiyun 	u64 overheat_counter;
1378*4882a593Smuzhiyun 	int err;
1379*4882a593Smuzhiyun 
1380*4882a593Smuzhiyun 	err = mlxsw_env_module_overheat_counter_get(mlxsw_sp->core, module,
1381*4882a593Smuzhiyun 						    &overheat_counter);
1382*4882a593Smuzhiyun 	if (err)
1383*4882a593Smuzhiyun 		return err;
1384*4882a593Smuzhiyun 
1385*4882a593Smuzhiyun 	mlxsw_sp_port->module_overheat_initial_val = overheat_counter;
1386*4882a593Smuzhiyun 	return 0;
1387*4882a593Smuzhiyun }
1388*4882a593Smuzhiyun 
mlxsw_sp_port_create(struct mlxsw_sp * mlxsw_sp,u8 local_port,u8 split_base_local_port,struct mlxsw_sp_port_mapping * port_mapping)1389*4882a593Smuzhiyun static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
1390*4882a593Smuzhiyun 				u8 split_base_local_port,
1391*4882a593Smuzhiyun 				struct mlxsw_sp_port_mapping *port_mapping)
1392*4882a593Smuzhiyun {
1393*4882a593Smuzhiyun 	struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
1394*4882a593Smuzhiyun 	bool split = !!split_base_local_port;
1395*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port;
1396*4882a593Smuzhiyun 	u32 lanes = port_mapping->width;
1397*4882a593Smuzhiyun 	struct net_device *dev;
1398*4882a593Smuzhiyun 	bool splittable;
1399*4882a593Smuzhiyun 	int err;
1400*4882a593Smuzhiyun 
1401*4882a593Smuzhiyun 	splittable = lanes > 1 && !split;
1402*4882a593Smuzhiyun 	err = mlxsw_core_port_init(mlxsw_sp->core, local_port,
1403*4882a593Smuzhiyun 				   port_mapping->module + 1, split,
1404*4882a593Smuzhiyun 				   port_mapping->lane / lanes,
1405*4882a593Smuzhiyun 				   splittable, lanes,
1406*4882a593Smuzhiyun 				   mlxsw_sp->base_mac,
1407*4882a593Smuzhiyun 				   sizeof(mlxsw_sp->base_mac));
1408*4882a593Smuzhiyun 	if (err) {
1409*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to init core port\n",
1410*4882a593Smuzhiyun 			local_port);
1411*4882a593Smuzhiyun 		return err;
1412*4882a593Smuzhiyun 	}
1413*4882a593Smuzhiyun 
1414*4882a593Smuzhiyun 	dev = alloc_etherdev(sizeof(struct mlxsw_sp_port));
1415*4882a593Smuzhiyun 	if (!dev) {
1416*4882a593Smuzhiyun 		err = -ENOMEM;
1417*4882a593Smuzhiyun 		goto err_alloc_etherdev;
1418*4882a593Smuzhiyun 	}
1419*4882a593Smuzhiyun 	SET_NETDEV_DEV(dev, mlxsw_sp->bus_info->dev);
1420*4882a593Smuzhiyun 	dev_net_set(dev, mlxsw_sp_net(mlxsw_sp));
1421*4882a593Smuzhiyun 	mlxsw_sp_port = netdev_priv(dev);
1422*4882a593Smuzhiyun 	mlxsw_sp_port->dev = dev;
1423*4882a593Smuzhiyun 	mlxsw_sp_port->mlxsw_sp = mlxsw_sp;
1424*4882a593Smuzhiyun 	mlxsw_sp_port->local_port = local_port;
1425*4882a593Smuzhiyun 	mlxsw_sp_port->pvid = MLXSW_SP_DEFAULT_VID;
1426*4882a593Smuzhiyun 	mlxsw_sp_port->split = split;
1427*4882a593Smuzhiyun 	mlxsw_sp_port->split_base_local_port = split_base_local_port;
1428*4882a593Smuzhiyun 	mlxsw_sp_port->mapping = *port_mapping;
1429*4882a593Smuzhiyun 	mlxsw_sp_port->link.autoneg = 1;
1430*4882a593Smuzhiyun 	INIT_LIST_HEAD(&mlxsw_sp_port->vlans_list);
1431*4882a593Smuzhiyun 
1432*4882a593Smuzhiyun 	mlxsw_sp_port->pcpu_stats =
1433*4882a593Smuzhiyun 		netdev_alloc_pcpu_stats(struct mlxsw_sp_port_pcpu_stats);
1434*4882a593Smuzhiyun 	if (!mlxsw_sp_port->pcpu_stats) {
1435*4882a593Smuzhiyun 		err = -ENOMEM;
1436*4882a593Smuzhiyun 		goto err_alloc_stats;
1437*4882a593Smuzhiyun 	}
1438*4882a593Smuzhiyun 
1439*4882a593Smuzhiyun 	INIT_DELAYED_WORK(&mlxsw_sp_port->periodic_hw_stats.update_dw,
1440*4882a593Smuzhiyun 			  &update_stats_cache);
1441*4882a593Smuzhiyun 
1442*4882a593Smuzhiyun 	dev->netdev_ops = &mlxsw_sp_port_netdev_ops;
1443*4882a593Smuzhiyun 	dev->ethtool_ops = &mlxsw_sp_port_ethtool_ops;
1444*4882a593Smuzhiyun 
1445*4882a593Smuzhiyun 	err = mlxsw_sp_port_module_map(mlxsw_sp_port);
1446*4882a593Smuzhiyun 	if (err) {
1447*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to map module\n",
1448*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1449*4882a593Smuzhiyun 		goto err_port_module_map;
1450*4882a593Smuzhiyun 	}
1451*4882a593Smuzhiyun 
1452*4882a593Smuzhiyun 	err = mlxsw_sp_port_swid_set(mlxsw_sp_port, 0);
1453*4882a593Smuzhiyun 	if (err) {
1454*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set SWID\n",
1455*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1456*4882a593Smuzhiyun 		goto err_port_swid_set;
1457*4882a593Smuzhiyun 	}
1458*4882a593Smuzhiyun 
1459*4882a593Smuzhiyun 	err = mlxsw_sp_port_dev_addr_init(mlxsw_sp_port);
1460*4882a593Smuzhiyun 	if (err) {
1461*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unable to init port mac address\n",
1462*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1463*4882a593Smuzhiyun 		goto err_dev_addr_init;
1464*4882a593Smuzhiyun 	}
1465*4882a593Smuzhiyun 
1466*4882a593Smuzhiyun 	netif_carrier_off(dev);
1467*4882a593Smuzhiyun 
1468*4882a593Smuzhiyun 	dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_LLTX | NETIF_F_SG |
1469*4882a593Smuzhiyun 			 NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC;
1470*4882a593Smuzhiyun 	dev->hw_features |= NETIF_F_HW_TC | NETIF_F_LOOPBACK;
1471*4882a593Smuzhiyun 
1472*4882a593Smuzhiyun 	dev->min_mtu = 0;
1473*4882a593Smuzhiyun 	dev->max_mtu = ETH_MAX_MTU;
1474*4882a593Smuzhiyun 
1475*4882a593Smuzhiyun 	/* Each packet needs to have a Tx header (metadata) on top all other
1476*4882a593Smuzhiyun 	 * headers.
1477*4882a593Smuzhiyun 	 */
1478*4882a593Smuzhiyun 	dev->needed_headroom = MLXSW_TXHDR_LEN;
1479*4882a593Smuzhiyun 
1480*4882a593Smuzhiyun 	err = mlxsw_sp_port_system_port_mapping_set(mlxsw_sp_port);
1481*4882a593Smuzhiyun 	if (err) {
1482*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set system port mapping\n",
1483*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1484*4882a593Smuzhiyun 		goto err_port_system_port_mapping_set;
1485*4882a593Smuzhiyun 	}
1486*4882a593Smuzhiyun 
1487*4882a593Smuzhiyun 	err = mlxsw_sp_port_speed_by_width_set(mlxsw_sp_port);
1488*4882a593Smuzhiyun 	if (err) {
1489*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to enable speeds\n",
1490*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1491*4882a593Smuzhiyun 		goto err_port_speed_by_width_set;
1492*4882a593Smuzhiyun 	}
1493*4882a593Smuzhiyun 
1494*4882a593Smuzhiyun 	err = mlxsw_sp->port_type_speed_ops->ptys_max_speed(mlxsw_sp_port,
1495*4882a593Smuzhiyun 							    &mlxsw_sp_port->max_speed);
1496*4882a593Smuzhiyun 	if (err) {
1497*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to get maximum speed\n",
1498*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1499*4882a593Smuzhiyun 		goto err_max_speed_get;
1500*4882a593Smuzhiyun 	}
1501*4882a593Smuzhiyun 
1502*4882a593Smuzhiyun 	err = mlxsw_sp_port_max_mtu_get(mlxsw_sp_port, &mlxsw_sp_port->max_mtu);
1503*4882a593Smuzhiyun 	if (err) {
1504*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to get maximum MTU\n",
1505*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1506*4882a593Smuzhiyun 		goto err_port_max_mtu_get;
1507*4882a593Smuzhiyun 	}
1508*4882a593Smuzhiyun 
1509*4882a593Smuzhiyun 	err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, ETH_DATA_LEN);
1510*4882a593Smuzhiyun 	if (err) {
1511*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set MTU\n",
1512*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1513*4882a593Smuzhiyun 		goto err_port_mtu_set;
1514*4882a593Smuzhiyun 	}
1515*4882a593Smuzhiyun 
1516*4882a593Smuzhiyun 	err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
1517*4882a593Smuzhiyun 	if (err)
1518*4882a593Smuzhiyun 		goto err_port_admin_status_set;
1519*4882a593Smuzhiyun 
1520*4882a593Smuzhiyun 	err = mlxsw_sp_port_buffers_init(mlxsw_sp_port);
1521*4882a593Smuzhiyun 	if (err) {
1522*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize buffers\n",
1523*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1524*4882a593Smuzhiyun 		goto err_port_buffers_init;
1525*4882a593Smuzhiyun 	}
1526*4882a593Smuzhiyun 
1527*4882a593Smuzhiyun 	err = mlxsw_sp_port_ets_init(mlxsw_sp_port);
1528*4882a593Smuzhiyun 	if (err) {
1529*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize ETS\n",
1530*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1531*4882a593Smuzhiyun 		goto err_port_ets_init;
1532*4882a593Smuzhiyun 	}
1533*4882a593Smuzhiyun 
1534*4882a593Smuzhiyun 	err = mlxsw_sp_port_tc_mc_mode_set(mlxsw_sp_port, true);
1535*4882a593Smuzhiyun 	if (err) {
1536*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize TC MC mode\n",
1537*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1538*4882a593Smuzhiyun 		goto err_port_tc_mc_mode;
1539*4882a593Smuzhiyun 	}
1540*4882a593Smuzhiyun 
1541*4882a593Smuzhiyun 	/* ETS and buffers must be initialized before DCB. */
1542*4882a593Smuzhiyun 	err = mlxsw_sp_port_dcb_init(mlxsw_sp_port);
1543*4882a593Smuzhiyun 	if (err) {
1544*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize DCB\n",
1545*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1546*4882a593Smuzhiyun 		goto err_port_dcb_init;
1547*4882a593Smuzhiyun 	}
1548*4882a593Smuzhiyun 
1549*4882a593Smuzhiyun 	err = mlxsw_sp_port_fids_init(mlxsw_sp_port);
1550*4882a593Smuzhiyun 	if (err) {
1551*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize FIDs\n",
1552*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1553*4882a593Smuzhiyun 		goto err_port_fids_init;
1554*4882a593Smuzhiyun 	}
1555*4882a593Smuzhiyun 
1556*4882a593Smuzhiyun 	err = mlxsw_sp_tc_qdisc_init(mlxsw_sp_port);
1557*4882a593Smuzhiyun 	if (err) {
1558*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize TC qdiscs\n",
1559*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1560*4882a593Smuzhiyun 		goto err_port_qdiscs_init;
1561*4882a593Smuzhiyun 	}
1562*4882a593Smuzhiyun 
1563*4882a593Smuzhiyun 	err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, 0, VLAN_N_VID - 1, false,
1564*4882a593Smuzhiyun 				     false);
1565*4882a593Smuzhiyun 	if (err) {
1566*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to clear VLAN filter\n",
1567*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1568*4882a593Smuzhiyun 		goto err_port_vlan_clear;
1569*4882a593Smuzhiyun 	}
1570*4882a593Smuzhiyun 
1571*4882a593Smuzhiyun 	err = mlxsw_sp_port_nve_init(mlxsw_sp_port);
1572*4882a593Smuzhiyun 	if (err) {
1573*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize NVE\n",
1574*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1575*4882a593Smuzhiyun 		goto err_port_nve_init;
1576*4882a593Smuzhiyun 	}
1577*4882a593Smuzhiyun 
1578*4882a593Smuzhiyun 	err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, MLXSW_SP_DEFAULT_VID);
1579*4882a593Smuzhiyun 	if (err) {
1580*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set PVID\n",
1581*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1582*4882a593Smuzhiyun 		goto err_port_pvid_set;
1583*4882a593Smuzhiyun 	}
1584*4882a593Smuzhiyun 
1585*4882a593Smuzhiyun 	mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_create(mlxsw_sp_port,
1586*4882a593Smuzhiyun 						       MLXSW_SP_DEFAULT_VID);
1587*4882a593Smuzhiyun 	if (IS_ERR(mlxsw_sp_port_vlan)) {
1588*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to create VID 1\n",
1589*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1590*4882a593Smuzhiyun 		err = PTR_ERR(mlxsw_sp_port_vlan);
1591*4882a593Smuzhiyun 		goto err_port_vlan_create;
1592*4882a593Smuzhiyun 	}
1593*4882a593Smuzhiyun 	mlxsw_sp_port->default_vlan = mlxsw_sp_port_vlan;
1594*4882a593Smuzhiyun 
1595*4882a593Smuzhiyun 	INIT_DELAYED_WORK(&mlxsw_sp_port->ptp.shaper_dw,
1596*4882a593Smuzhiyun 			  mlxsw_sp->ptp_ops->shaper_work);
1597*4882a593Smuzhiyun 
1598*4882a593Smuzhiyun 	mlxsw_sp->ports[local_port] = mlxsw_sp_port;
1599*4882a593Smuzhiyun 
1600*4882a593Smuzhiyun 	err = mlxsw_sp_port_overheat_init_val_set(mlxsw_sp_port);
1601*4882a593Smuzhiyun 	if (err) {
1602*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set overheat initial value\n",
1603*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1604*4882a593Smuzhiyun 		goto err_port_overheat_init_val_set;
1605*4882a593Smuzhiyun 	}
1606*4882a593Smuzhiyun 
1607*4882a593Smuzhiyun 	err = register_netdev(dev);
1608*4882a593Smuzhiyun 	if (err) {
1609*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to register netdev\n",
1610*4882a593Smuzhiyun 			mlxsw_sp_port->local_port);
1611*4882a593Smuzhiyun 		goto err_register_netdev;
1612*4882a593Smuzhiyun 	}
1613*4882a593Smuzhiyun 
1614*4882a593Smuzhiyun 	mlxsw_core_port_eth_set(mlxsw_sp->core, mlxsw_sp_port->local_port,
1615*4882a593Smuzhiyun 				mlxsw_sp_port, dev);
1616*4882a593Smuzhiyun 	mlxsw_core_schedule_dw(&mlxsw_sp_port->periodic_hw_stats.update_dw, 0);
1617*4882a593Smuzhiyun 	return 0;
1618*4882a593Smuzhiyun 
1619*4882a593Smuzhiyun err_register_netdev:
1620*4882a593Smuzhiyun err_port_overheat_init_val_set:
1621*4882a593Smuzhiyun 	mlxsw_sp->ports[local_port] = NULL;
1622*4882a593Smuzhiyun 	mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan);
1623*4882a593Smuzhiyun err_port_vlan_create:
1624*4882a593Smuzhiyun err_port_pvid_set:
1625*4882a593Smuzhiyun 	mlxsw_sp_port_nve_fini(mlxsw_sp_port);
1626*4882a593Smuzhiyun err_port_nve_init:
1627*4882a593Smuzhiyun err_port_vlan_clear:
1628*4882a593Smuzhiyun 	mlxsw_sp_tc_qdisc_fini(mlxsw_sp_port);
1629*4882a593Smuzhiyun err_port_qdiscs_init:
1630*4882a593Smuzhiyun 	mlxsw_sp_port_fids_fini(mlxsw_sp_port);
1631*4882a593Smuzhiyun err_port_fids_init:
1632*4882a593Smuzhiyun 	mlxsw_sp_port_dcb_fini(mlxsw_sp_port);
1633*4882a593Smuzhiyun err_port_dcb_init:
1634*4882a593Smuzhiyun 	mlxsw_sp_port_tc_mc_mode_set(mlxsw_sp_port, false);
1635*4882a593Smuzhiyun err_port_tc_mc_mode:
1636*4882a593Smuzhiyun err_port_ets_init:
1637*4882a593Smuzhiyun 	mlxsw_sp_port_buffers_fini(mlxsw_sp_port);
1638*4882a593Smuzhiyun err_port_buffers_init:
1639*4882a593Smuzhiyun err_port_admin_status_set:
1640*4882a593Smuzhiyun err_port_mtu_set:
1641*4882a593Smuzhiyun err_port_max_mtu_get:
1642*4882a593Smuzhiyun err_max_speed_get:
1643*4882a593Smuzhiyun err_port_speed_by_width_set:
1644*4882a593Smuzhiyun err_port_system_port_mapping_set:
1645*4882a593Smuzhiyun err_dev_addr_init:
1646*4882a593Smuzhiyun 	mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT);
1647*4882a593Smuzhiyun err_port_swid_set:
1648*4882a593Smuzhiyun 	mlxsw_sp_port_module_unmap(mlxsw_sp_port);
1649*4882a593Smuzhiyun err_port_module_map:
1650*4882a593Smuzhiyun 	free_percpu(mlxsw_sp_port->pcpu_stats);
1651*4882a593Smuzhiyun err_alloc_stats:
1652*4882a593Smuzhiyun 	free_netdev(dev);
1653*4882a593Smuzhiyun err_alloc_etherdev:
1654*4882a593Smuzhiyun 	mlxsw_core_port_fini(mlxsw_sp->core, local_port);
1655*4882a593Smuzhiyun 	return err;
1656*4882a593Smuzhiyun }
1657*4882a593Smuzhiyun 
mlxsw_sp_port_remove(struct mlxsw_sp * mlxsw_sp,u8 local_port)1658*4882a593Smuzhiyun static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
1659*4882a593Smuzhiyun {
1660*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port];
1661*4882a593Smuzhiyun 
1662*4882a593Smuzhiyun 	cancel_delayed_work_sync(&mlxsw_sp_port->periodic_hw_stats.update_dw);
1663*4882a593Smuzhiyun 	cancel_delayed_work_sync(&mlxsw_sp_port->ptp.shaper_dw);
1664*4882a593Smuzhiyun 	mlxsw_sp_port_ptp_clear(mlxsw_sp_port);
1665*4882a593Smuzhiyun 	mlxsw_core_port_clear(mlxsw_sp->core, local_port, mlxsw_sp);
1666*4882a593Smuzhiyun 	unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */
1667*4882a593Smuzhiyun 	mlxsw_sp->ports[local_port] = NULL;
1668*4882a593Smuzhiyun 	mlxsw_sp_port_vlan_flush(mlxsw_sp_port, true);
1669*4882a593Smuzhiyun 	mlxsw_sp_port_nve_fini(mlxsw_sp_port);
1670*4882a593Smuzhiyun 	mlxsw_sp_tc_qdisc_fini(mlxsw_sp_port);
1671*4882a593Smuzhiyun 	mlxsw_sp_port_fids_fini(mlxsw_sp_port);
1672*4882a593Smuzhiyun 	mlxsw_sp_port_dcb_fini(mlxsw_sp_port);
1673*4882a593Smuzhiyun 	mlxsw_sp_port_tc_mc_mode_set(mlxsw_sp_port, false);
1674*4882a593Smuzhiyun 	mlxsw_sp_port_buffers_fini(mlxsw_sp_port);
1675*4882a593Smuzhiyun 	mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT);
1676*4882a593Smuzhiyun 	mlxsw_sp_port_module_unmap(mlxsw_sp_port);
1677*4882a593Smuzhiyun 	free_percpu(mlxsw_sp_port->pcpu_stats);
1678*4882a593Smuzhiyun 	WARN_ON_ONCE(!list_empty(&mlxsw_sp_port->vlans_list));
1679*4882a593Smuzhiyun 	free_netdev(mlxsw_sp_port->dev);
1680*4882a593Smuzhiyun 	mlxsw_core_port_fini(mlxsw_sp->core, local_port);
1681*4882a593Smuzhiyun }
1682*4882a593Smuzhiyun 
mlxsw_sp_cpu_port_create(struct mlxsw_sp * mlxsw_sp)1683*4882a593Smuzhiyun static int mlxsw_sp_cpu_port_create(struct mlxsw_sp *mlxsw_sp)
1684*4882a593Smuzhiyun {
1685*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port;
1686*4882a593Smuzhiyun 	int err;
1687*4882a593Smuzhiyun 
1688*4882a593Smuzhiyun 	mlxsw_sp_port = kzalloc(sizeof(*mlxsw_sp_port), GFP_KERNEL);
1689*4882a593Smuzhiyun 	if (!mlxsw_sp_port)
1690*4882a593Smuzhiyun 		return -ENOMEM;
1691*4882a593Smuzhiyun 
1692*4882a593Smuzhiyun 	mlxsw_sp_port->mlxsw_sp = mlxsw_sp;
1693*4882a593Smuzhiyun 	mlxsw_sp_port->local_port = MLXSW_PORT_CPU_PORT;
1694*4882a593Smuzhiyun 
1695*4882a593Smuzhiyun 	err = mlxsw_core_cpu_port_init(mlxsw_sp->core,
1696*4882a593Smuzhiyun 				       mlxsw_sp_port,
1697*4882a593Smuzhiyun 				       mlxsw_sp->base_mac,
1698*4882a593Smuzhiyun 				       sizeof(mlxsw_sp->base_mac));
1699*4882a593Smuzhiyun 	if (err) {
1700*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize core CPU port\n");
1701*4882a593Smuzhiyun 		goto err_core_cpu_port_init;
1702*4882a593Smuzhiyun 	}
1703*4882a593Smuzhiyun 
1704*4882a593Smuzhiyun 	mlxsw_sp->ports[MLXSW_PORT_CPU_PORT] = mlxsw_sp_port;
1705*4882a593Smuzhiyun 	return 0;
1706*4882a593Smuzhiyun 
1707*4882a593Smuzhiyun err_core_cpu_port_init:
1708*4882a593Smuzhiyun 	kfree(mlxsw_sp_port);
1709*4882a593Smuzhiyun 	return err;
1710*4882a593Smuzhiyun }
1711*4882a593Smuzhiyun 
mlxsw_sp_cpu_port_remove(struct mlxsw_sp * mlxsw_sp)1712*4882a593Smuzhiyun static void mlxsw_sp_cpu_port_remove(struct mlxsw_sp *mlxsw_sp)
1713*4882a593Smuzhiyun {
1714*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port =
1715*4882a593Smuzhiyun 				mlxsw_sp->ports[MLXSW_PORT_CPU_PORT];
1716*4882a593Smuzhiyun 
1717*4882a593Smuzhiyun 	mlxsw_core_cpu_port_fini(mlxsw_sp->core);
1718*4882a593Smuzhiyun 	mlxsw_sp->ports[MLXSW_PORT_CPU_PORT] = NULL;
1719*4882a593Smuzhiyun 	kfree(mlxsw_sp_port);
1720*4882a593Smuzhiyun }
1721*4882a593Smuzhiyun 
mlxsw_sp_port_created(struct mlxsw_sp * mlxsw_sp,u8 local_port)1722*4882a593Smuzhiyun static bool mlxsw_sp_port_created(struct mlxsw_sp *mlxsw_sp, u8 local_port)
1723*4882a593Smuzhiyun {
1724*4882a593Smuzhiyun 	return mlxsw_sp->ports[local_port] != NULL;
1725*4882a593Smuzhiyun }
1726*4882a593Smuzhiyun 
mlxsw_sp_ports_remove(struct mlxsw_sp * mlxsw_sp)1727*4882a593Smuzhiyun static void mlxsw_sp_ports_remove(struct mlxsw_sp *mlxsw_sp)
1728*4882a593Smuzhiyun {
1729*4882a593Smuzhiyun 	int i;
1730*4882a593Smuzhiyun 
1731*4882a593Smuzhiyun 	for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++)
1732*4882a593Smuzhiyun 		if (mlxsw_sp_port_created(mlxsw_sp, i))
1733*4882a593Smuzhiyun 			mlxsw_sp_port_remove(mlxsw_sp, i);
1734*4882a593Smuzhiyun 	mlxsw_sp_cpu_port_remove(mlxsw_sp);
1735*4882a593Smuzhiyun 	kfree(mlxsw_sp->ports);
1736*4882a593Smuzhiyun 	mlxsw_sp->ports = NULL;
1737*4882a593Smuzhiyun }
1738*4882a593Smuzhiyun 
mlxsw_sp_ports_create(struct mlxsw_sp * mlxsw_sp)1739*4882a593Smuzhiyun static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp)
1740*4882a593Smuzhiyun {
1741*4882a593Smuzhiyun 	unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
1742*4882a593Smuzhiyun 	struct mlxsw_sp_port_mapping *port_mapping;
1743*4882a593Smuzhiyun 	size_t alloc_size;
1744*4882a593Smuzhiyun 	int i;
1745*4882a593Smuzhiyun 	int err;
1746*4882a593Smuzhiyun 
1747*4882a593Smuzhiyun 	alloc_size = sizeof(struct mlxsw_sp_port *) * max_ports;
1748*4882a593Smuzhiyun 	mlxsw_sp->ports = kzalloc(alloc_size, GFP_KERNEL);
1749*4882a593Smuzhiyun 	if (!mlxsw_sp->ports)
1750*4882a593Smuzhiyun 		return -ENOMEM;
1751*4882a593Smuzhiyun 
1752*4882a593Smuzhiyun 	err = mlxsw_sp_cpu_port_create(mlxsw_sp);
1753*4882a593Smuzhiyun 	if (err)
1754*4882a593Smuzhiyun 		goto err_cpu_port_create;
1755*4882a593Smuzhiyun 
1756*4882a593Smuzhiyun 	for (i = 1; i < max_ports; i++) {
1757*4882a593Smuzhiyun 		port_mapping = mlxsw_sp->port_mapping[i];
1758*4882a593Smuzhiyun 		if (!port_mapping)
1759*4882a593Smuzhiyun 			continue;
1760*4882a593Smuzhiyun 		err = mlxsw_sp_port_create(mlxsw_sp, i, 0, port_mapping);
1761*4882a593Smuzhiyun 		if (err)
1762*4882a593Smuzhiyun 			goto err_port_create;
1763*4882a593Smuzhiyun 	}
1764*4882a593Smuzhiyun 	return 0;
1765*4882a593Smuzhiyun 
1766*4882a593Smuzhiyun err_port_create:
1767*4882a593Smuzhiyun 	for (i--; i >= 1; i--)
1768*4882a593Smuzhiyun 		if (mlxsw_sp_port_created(mlxsw_sp, i))
1769*4882a593Smuzhiyun 			mlxsw_sp_port_remove(mlxsw_sp, i);
1770*4882a593Smuzhiyun 	mlxsw_sp_cpu_port_remove(mlxsw_sp);
1771*4882a593Smuzhiyun err_cpu_port_create:
1772*4882a593Smuzhiyun 	kfree(mlxsw_sp->ports);
1773*4882a593Smuzhiyun 	mlxsw_sp->ports = NULL;
1774*4882a593Smuzhiyun 	return err;
1775*4882a593Smuzhiyun }
1776*4882a593Smuzhiyun 
mlxsw_sp_port_module_info_init(struct mlxsw_sp * mlxsw_sp)1777*4882a593Smuzhiyun static int mlxsw_sp_port_module_info_init(struct mlxsw_sp *mlxsw_sp)
1778*4882a593Smuzhiyun {
1779*4882a593Smuzhiyun 	unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
1780*4882a593Smuzhiyun 	struct mlxsw_sp_port_mapping port_mapping;
1781*4882a593Smuzhiyun 	int i;
1782*4882a593Smuzhiyun 	int err;
1783*4882a593Smuzhiyun 
1784*4882a593Smuzhiyun 	mlxsw_sp->port_mapping = kcalloc(max_ports,
1785*4882a593Smuzhiyun 					 sizeof(struct mlxsw_sp_port_mapping *),
1786*4882a593Smuzhiyun 					 GFP_KERNEL);
1787*4882a593Smuzhiyun 	if (!mlxsw_sp->port_mapping)
1788*4882a593Smuzhiyun 		return -ENOMEM;
1789*4882a593Smuzhiyun 
1790*4882a593Smuzhiyun 	for (i = 1; i < max_ports; i++) {
1791*4882a593Smuzhiyun 		err = mlxsw_sp_port_module_info_get(mlxsw_sp, i, &port_mapping);
1792*4882a593Smuzhiyun 		if (err)
1793*4882a593Smuzhiyun 			goto err_port_module_info_get;
1794*4882a593Smuzhiyun 		if (!port_mapping.width)
1795*4882a593Smuzhiyun 			continue;
1796*4882a593Smuzhiyun 
1797*4882a593Smuzhiyun 		mlxsw_sp->port_mapping[i] = kmemdup(&port_mapping,
1798*4882a593Smuzhiyun 						    sizeof(port_mapping),
1799*4882a593Smuzhiyun 						    GFP_KERNEL);
1800*4882a593Smuzhiyun 		if (!mlxsw_sp->port_mapping[i]) {
1801*4882a593Smuzhiyun 			err = -ENOMEM;
1802*4882a593Smuzhiyun 			goto err_port_module_info_dup;
1803*4882a593Smuzhiyun 		}
1804*4882a593Smuzhiyun 	}
1805*4882a593Smuzhiyun 	return 0;
1806*4882a593Smuzhiyun 
1807*4882a593Smuzhiyun err_port_module_info_get:
1808*4882a593Smuzhiyun err_port_module_info_dup:
1809*4882a593Smuzhiyun 	for (i--; i >= 1; i--)
1810*4882a593Smuzhiyun 		kfree(mlxsw_sp->port_mapping[i]);
1811*4882a593Smuzhiyun 	kfree(mlxsw_sp->port_mapping);
1812*4882a593Smuzhiyun 	return err;
1813*4882a593Smuzhiyun }
1814*4882a593Smuzhiyun 
mlxsw_sp_port_module_info_fini(struct mlxsw_sp * mlxsw_sp)1815*4882a593Smuzhiyun static void mlxsw_sp_port_module_info_fini(struct mlxsw_sp *mlxsw_sp)
1816*4882a593Smuzhiyun {
1817*4882a593Smuzhiyun 	int i;
1818*4882a593Smuzhiyun 
1819*4882a593Smuzhiyun 	for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++)
1820*4882a593Smuzhiyun 		kfree(mlxsw_sp->port_mapping[i]);
1821*4882a593Smuzhiyun 	kfree(mlxsw_sp->port_mapping);
1822*4882a593Smuzhiyun }
1823*4882a593Smuzhiyun 
mlxsw_sp_cluster_base_port_get(u8 local_port,unsigned int max_width)1824*4882a593Smuzhiyun static u8 mlxsw_sp_cluster_base_port_get(u8 local_port, unsigned int max_width)
1825*4882a593Smuzhiyun {
1826*4882a593Smuzhiyun 	u8 offset = (local_port - 1) % max_width;
1827*4882a593Smuzhiyun 
1828*4882a593Smuzhiyun 	return local_port - offset;
1829*4882a593Smuzhiyun }
1830*4882a593Smuzhiyun 
1831*4882a593Smuzhiyun static int
mlxsw_sp_port_split_create(struct mlxsw_sp * mlxsw_sp,u8 base_port,struct mlxsw_sp_port_mapping * port_mapping,unsigned int count,u8 offset)1832*4882a593Smuzhiyun mlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp, u8 base_port,
1833*4882a593Smuzhiyun 			   struct mlxsw_sp_port_mapping *port_mapping,
1834*4882a593Smuzhiyun 			   unsigned int count, u8 offset)
1835*4882a593Smuzhiyun {
1836*4882a593Smuzhiyun 	struct mlxsw_sp_port_mapping split_port_mapping;
1837*4882a593Smuzhiyun 	int err, i;
1838*4882a593Smuzhiyun 
1839*4882a593Smuzhiyun 	split_port_mapping = *port_mapping;
1840*4882a593Smuzhiyun 	split_port_mapping.width /= count;
1841*4882a593Smuzhiyun 	for (i = 0; i < count; i++) {
1842*4882a593Smuzhiyun 		err = mlxsw_sp_port_create(mlxsw_sp, base_port + i * offset,
1843*4882a593Smuzhiyun 					   base_port, &split_port_mapping);
1844*4882a593Smuzhiyun 		if (err)
1845*4882a593Smuzhiyun 			goto err_port_create;
1846*4882a593Smuzhiyun 		split_port_mapping.lane += split_port_mapping.width;
1847*4882a593Smuzhiyun 	}
1848*4882a593Smuzhiyun 
1849*4882a593Smuzhiyun 	return 0;
1850*4882a593Smuzhiyun 
1851*4882a593Smuzhiyun err_port_create:
1852*4882a593Smuzhiyun 	for (i--; i >= 0; i--)
1853*4882a593Smuzhiyun 		if (mlxsw_sp_port_created(mlxsw_sp, base_port + i * offset))
1854*4882a593Smuzhiyun 			mlxsw_sp_port_remove(mlxsw_sp, base_port + i * offset);
1855*4882a593Smuzhiyun 	return err;
1856*4882a593Smuzhiyun }
1857*4882a593Smuzhiyun 
mlxsw_sp_port_unsplit_create(struct mlxsw_sp * mlxsw_sp,u8 base_port,unsigned int count,u8 offset)1858*4882a593Smuzhiyun static void mlxsw_sp_port_unsplit_create(struct mlxsw_sp *mlxsw_sp,
1859*4882a593Smuzhiyun 					 u8 base_port,
1860*4882a593Smuzhiyun 					 unsigned int count, u8 offset)
1861*4882a593Smuzhiyun {
1862*4882a593Smuzhiyun 	struct mlxsw_sp_port_mapping *port_mapping;
1863*4882a593Smuzhiyun 	int i;
1864*4882a593Smuzhiyun 
1865*4882a593Smuzhiyun 	/* Go over original unsplit ports in the gap and recreate them. */
1866*4882a593Smuzhiyun 	for (i = 0; i < count * offset; i++) {
1867*4882a593Smuzhiyun 		port_mapping = mlxsw_sp->port_mapping[base_port + i];
1868*4882a593Smuzhiyun 		if (!port_mapping)
1869*4882a593Smuzhiyun 			continue;
1870*4882a593Smuzhiyun 		mlxsw_sp_port_create(mlxsw_sp, base_port + i, 0, port_mapping);
1871*4882a593Smuzhiyun 	}
1872*4882a593Smuzhiyun }
1873*4882a593Smuzhiyun 
mlxsw_sp_local_ports_offset(struct mlxsw_core * mlxsw_core,unsigned int count,unsigned int max_width)1874*4882a593Smuzhiyun static int mlxsw_sp_local_ports_offset(struct mlxsw_core *mlxsw_core,
1875*4882a593Smuzhiyun 				       unsigned int count,
1876*4882a593Smuzhiyun 				       unsigned int max_width)
1877*4882a593Smuzhiyun {
1878*4882a593Smuzhiyun 	enum mlxsw_res_id local_ports_in_x_res_id;
1879*4882a593Smuzhiyun 	int split_width = max_width / count;
1880*4882a593Smuzhiyun 
1881*4882a593Smuzhiyun 	if (split_width == 1)
1882*4882a593Smuzhiyun 		local_ports_in_x_res_id = MLXSW_RES_ID_LOCAL_PORTS_IN_1X;
1883*4882a593Smuzhiyun 	else if (split_width == 2)
1884*4882a593Smuzhiyun 		local_ports_in_x_res_id = MLXSW_RES_ID_LOCAL_PORTS_IN_2X;
1885*4882a593Smuzhiyun 	else if (split_width == 4)
1886*4882a593Smuzhiyun 		local_ports_in_x_res_id = MLXSW_RES_ID_LOCAL_PORTS_IN_4X;
1887*4882a593Smuzhiyun 	else
1888*4882a593Smuzhiyun 		return -EINVAL;
1889*4882a593Smuzhiyun 
1890*4882a593Smuzhiyun 	if (!mlxsw_core_res_valid(mlxsw_core, local_ports_in_x_res_id))
1891*4882a593Smuzhiyun 		return -EINVAL;
1892*4882a593Smuzhiyun 	return mlxsw_core_res_get(mlxsw_core, local_ports_in_x_res_id);
1893*4882a593Smuzhiyun }
1894*4882a593Smuzhiyun 
1895*4882a593Smuzhiyun static struct mlxsw_sp_port *
mlxsw_sp_port_get_by_local_port(struct mlxsw_sp * mlxsw_sp,u8 local_port)1896*4882a593Smuzhiyun mlxsw_sp_port_get_by_local_port(struct mlxsw_sp *mlxsw_sp, u8 local_port)
1897*4882a593Smuzhiyun {
1898*4882a593Smuzhiyun 	if (mlxsw_sp->ports && mlxsw_sp->ports[local_port])
1899*4882a593Smuzhiyun 		return mlxsw_sp->ports[local_port];
1900*4882a593Smuzhiyun 	return NULL;
1901*4882a593Smuzhiyun }
1902*4882a593Smuzhiyun 
mlxsw_sp_port_split(struct mlxsw_core * mlxsw_core,u8 local_port,unsigned int count,struct netlink_ext_ack * extack)1903*4882a593Smuzhiyun static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
1904*4882a593Smuzhiyun 			       unsigned int count,
1905*4882a593Smuzhiyun 			       struct netlink_ext_ack *extack)
1906*4882a593Smuzhiyun {
1907*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1908*4882a593Smuzhiyun 	struct mlxsw_sp_port_mapping port_mapping;
1909*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port;
1910*4882a593Smuzhiyun 	int max_width;
1911*4882a593Smuzhiyun 	u8 base_port;
1912*4882a593Smuzhiyun 	int offset;
1913*4882a593Smuzhiyun 	int i;
1914*4882a593Smuzhiyun 	int err;
1915*4882a593Smuzhiyun 
1916*4882a593Smuzhiyun 	mlxsw_sp_port = mlxsw_sp_port_get_by_local_port(mlxsw_sp, local_port);
1917*4882a593Smuzhiyun 	if (!mlxsw_sp_port) {
1918*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
1919*4882a593Smuzhiyun 			local_port);
1920*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Port number does not exist");
1921*4882a593Smuzhiyun 		return -EINVAL;
1922*4882a593Smuzhiyun 	}
1923*4882a593Smuzhiyun 
1924*4882a593Smuzhiyun 	max_width = mlxsw_core_module_max_width(mlxsw_core,
1925*4882a593Smuzhiyun 						mlxsw_sp_port->mapping.module);
1926*4882a593Smuzhiyun 	if (max_width < 0) {
1927*4882a593Smuzhiyun 		netdev_err(mlxsw_sp_port->dev, "Cannot get max width of port module\n");
1928*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Cannot get max width of port module");
1929*4882a593Smuzhiyun 		return max_width;
1930*4882a593Smuzhiyun 	}
1931*4882a593Smuzhiyun 
1932*4882a593Smuzhiyun 	/* Split port with non-max cannot be split. */
1933*4882a593Smuzhiyun 	if (mlxsw_sp_port->mapping.width != max_width) {
1934*4882a593Smuzhiyun 		netdev_err(mlxsw_sp_port->dev, "Port cannot be split\n");
1935*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Port cannot be split");
1936*4882a593Smuzhiyun 		return -EINVAL;
1937*4882a593Smuzhiyun 	}
1938*4882a593Smuzhiyun 
1939*4882a593Smuzhiyun 	offset = mlxsw_sp_local_ports_offset(mlxsw_core, count, max_width);
1940*4882a593Smuzhiyun 	if (offset < 0) {
1941*4882a593Smuzhiyun 		netdev_err(mlxsw_sp_port->dev, "Cannot obtain local port offset\n");
1942*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Cannot obtain local port offset");
1943*4882a593Smuzhiyun 		return -EINVAL;
1944*4882a593Smuzhiyun 	}
1945*4882a593Smuzhiyun 
1946*4882a593Smuzhiyun 	/* Only in case max split is being done, the local port and
1947*4882a593Smuzhiyun 	 * base port may differ.
1948*4882a593Smuzhiyun 	 */
1949*4882a593Smuzhiyun 	base_port = count == max_width ?
1950*4882a593Smuzhiyun 		    mlxsw_sp_cluster_base_port_get(local_port, max_width) :
1951*4882a593Smuzhiyun 		    local_port;
1952*4882a593Smuzhiyun 
1953*4882a593Smuzhiyun 	for (i = 0; i < count * offset; i++) {
1954*4882a593Smuzhiyun 		/* Expect base port to exist and also the one in the middle in
1955*4882a593Smuzhiyun 		 * case of maximal split count.
1956*4882a593Smuzhiyun 		 */
1957*4882a593Smuzhiyun 		if (i == 0 || (count == max_width && i == count / 2))
1958*4882a593Smuzhiyun 			continue;
1959*4882a593Smuzhiyun 
1960*4882a593Smuzhiyun 		if (mlxsw_sp_port_created(mlxsw_sp, base_port + i)) {
1961*4882a593Smuzhiyun 			netdev_err(mlxsw_sp_port->dev, "Invalid split configuration\n");
1962*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "Invalid split configuration");
1963*4882a593Smuzhiyun 			return -EINVAL;
1964*4882a593Smuzhiyun 		}
1965*4882a593Smuzhiyun 	}
1966*4882a593Smuzhiyun 
1967*4882a593Smuzhiyun 	port_mapping = mlxsw_sp_port->mapping;
1968*4882a593Smuzhiyun 
1969*4882a593Smuzhiyun 	for (i = 0; i < count; i++)
1970*4882a593Smuzhiyun 		if (mlxsw_sp_port_created(mlxsw_sp, base_port + i * offset))
1971*4882a593Smuzhiyun 			mlxsw_sp_port_remove(mlxsw_sp, base_port + i * offset);
1972*4882a593Smuzhiyun 
1973*4882a593Smuzhiyun 	err = mlxsw_sp_port_split_create(mlxsw_sp, base_port, &port_mapping,
1974*4882a593Smuzhiyun 					 count, offset);
1975*4882a593Smuzhiyun 	if (err) {
1976*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to create split ports\n");
1977*4882a593Smuzhiyun 		goto err_port_split_create;
1978*4882a593Smuzhiyun 	}
1979*4882a593Smuzhiyun 
1980*4882a593Smuzhiyun 	return 0;
1981*4882a593Smuzhiyun 
1982*4882a593Smuzhiyun err_port_split_create:
1983*4882a593Smuzhiyun 	mlxsw_sp_port_unsplit_create(mlxsw_sp, base_port, count, offset);
1984*4882a593Smuzhiyun 	return err;
1985*4882a593Smuzhiyun }
1986*4882a593Smuzhiyun 
mlxsw_sp_port_unsplit(struct mlxsw_core * mlxsw_core,u8 local_port,struct netlink_ext_ack * extack)1987*4882a593Smuzhiyun static int mlxsw_sp_port_unsplit(struct mlxsw_core *mlxsw_core, u8 local_port,
1988*4882a593Smuzhiyun 				 struct netlink_ext_ack *extack)
1989*4882a593Smuzhiyun {
1990*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
1991*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port;
1992*4882a593Smuzhiyun 	unsigned int count;
1993*4882a593Smuzhiyun 	int max_width;
1994*4882a593Smuzhiyun 	u8 base_port;
1995*4882a593Smuzhiyun 	int offset;
1996*4882a593Smuzhiyun 	int i;
1997*4882a593Smuzhiyun 
1998*4882a593Smuzhiyun 	mlxsw_sp_port = mlxsw_sp_port_get_by_local_port(mlxsw_sp, local_port);
1999*4882a593Smuzhiyun 	if (!mlxsw_sp_port) {
2000*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
2001*4882a593Smuzhiyun 			local_port);
2002*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Port number does not exist");
2003*4882a593Smuzhiyun 		return -EINVAL;
2004*4882a593Smuzhiyun 	}
2005*4882a593Smuzhiyun 
2006*4882a593Smuzhiyun 	if (!mlxsw_sp_port->split) {
2007*4882a593Smuzhiyun 		netdev_err(mlxsw_sp_port->dev, "Port was not split\n");
2008*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Port was not split");
2009*4882a593Smuzhiyun 		return -EINVAL;
2010*4882a593Smuzhiyun 	}
2011*4882a593Smuzhiyun 
2012*4882a593Smuzhiyun 	max_width = mlxsw_core_module_max_width(mlxsw_core,
2013*4882a593Smuzhiyun 						mlxsw_sp_port->mapping.module);
2014*4882a593Smuzhiyun 	if (max_width < 0) {
2015*4882a593Smuzhiyun 		netdev_err(mlxsw_sp_port->dev, "Cannot get max width of port module\n");
2016*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Cannot get max width of port module");
2017*4882a593Smuzhiyun 		return max_width;
2018*4882a593Smuzhiyun 	}
2019*4882a593Smuzhiyun 
2020*4882a593Smuzhiyun 	count = max_width / mlxsw_sp_port->mapping.width;
2021*4882a593Smuzhiyun 
2022*4882a593Smuzhiyun 	offset = mlxsw_sp_local_ports_offset(mlxsw_core, count, max_width);
2023*4882a593Smuzhiyun 	if (WARN_ON(offset < 0)) {
2024*4882a593Smuzhiyun 		netdev_err(mlxsw_sp_port->dev, "Cannot obtain local port offset\n");
2025*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Cannot obtain local port offset");
2026*4882a593Smuzhiyun 		return -EINVAL;
2027*4882a593Smuzhiyun 	}
2028*4882a593Smuzhiyun 
2029*4882a593Smuzhiyun 	base_port = mlxsw_sp_port->split_base_local_port;
2030*4882a593Smuzhiyun 
2031*4882a593Smuzhiyun 	for (i = 0; i < count; i++)
2032*4882a593Smuzhiyun 		if (mlxsw_sp_port_created(mlxsw_sp, base_port + i * offset))
2033*4882a593Smuzhiyun 			mlxsw_sp_port_remove(mlxsw_sp, base_port + i * offset);
2034*4882a593Smuzhiyun 
2035*4882a593Smuzhiyun 	mlxsw_sp_port_unsplit_create(mlxsw_sp, base_port, count, offset);
2036*4882a593Smuzhiyun 
2037*4882a593Smuzhiyun 	return 0;
2038*4882a593Smuzhiyun }
2039*4882a593Smuzhiyun 
2040*4882a593Smuzhiyun static void
mlxsw_sp_port_down_wipe_counters(struct mlxsw_sp_port * mlxsw_sp_port)2041*4882a593Smuzhiyun mlxsw_sp_port_down_wipe_counters(struct mlxsw_sp_port *mlxsw_sp_port)
2042*4882a593Smuzhiyun {
2043*4882a593Smuzhiyun 	int i;
2044*4882a593Smuzhiyun 
2045*4882a593Smuzhiyun 	for (i = 0; i < TC_MAX_QUEUE; i++)
2046*4882a593Smuzhiyun 		mlxsw_sp_port->periodic_hw_stats.xstats.backlog[i] = 0;
2047*4882a593Smuzhiyun }
2048*4882a593Smuzhiyun 
mlxsw_sp_pude_event_func(const struct mlxsw_reg_info * reg,char * pude_pl,void * priv)2049*4882a593Smuzhiyun static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg,
2050*4882a593Smuzhiyun 				     char *pude_pl, void *priv)
2051*4882a593Smuzhiyun {
2052*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = priv;
2053*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port;
2054*4882a593Smuzhiyun 	enum mlxsw_reg_pude_oper_status status;
2055*4882a593Smuzhiyun 	unsigned int max_ports;
2056*4882a593Smuzhiyun 	u8 local_port;
2057*4882a593Smuzhiyun 
2058*4882a593Smuzhiyun 	max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
2059*4882a593Smuzhiyun 	local_port = mlxsw_reg_pude_local_port_get(pude_pl);
2060*4882a593Smuzhiyun 
2061*4882a593Smuzhiyun 	if (WARN_ON_ONCE(!local_port || local_port >= max_ports))
2062*4882a593Smuzhiyun 		return;
2063*4882a593Smuzhiyun 	mlxsw_sp_port = mlxsw_sp->ports[local_port];
2064*4882a593Smuzhiyun 	if (!mlxsw_sp_port)
2065*4882a593Smuzhiyun 		return;
2066*4882a593Smuzhiyun 
2067*4882a593Smuzhiyun 	status = mlxsw_reg_pude_oper_status_get(pude_pl);
2068*4882a593Smuzhiyun 	if (status == MLXSW_PORT_OPER_STATUS_UP) {
2069*4882a593Smuzhiyun 		netdev_info(mlxsw_sp_port->dev, "link up\n");
2070*4882a593Smuzhiyun 		netif_carrier_on(mlxsw_sp_port->dev);
2071*4882a593Smuzhiyun 		mlxsw_core_schedule_dw(&mlxsw_sp_port->ptp.shaper_dw, 0);
2072*4882a593Smuzhiyun 	} else {
2073*4882a593Smuzhiyun 		netdev_info(mlxsw_sp_port->dev, "link down\n");
2074*4882a593Smuzhiyun 		netif_carrier_off(mlxsw_sp_port->dev);
2075*4882a593Smuzhiyun 		mlxsw_sp_port_down_wipe_counters(mlxsw_sp_port);
2076*4882a593Smuzhiyun 	}
2077*4882a593Smuzhiyun }
2078*4882a593Smuzhiyun 
mlxsw_sp1_ptp_fifo_event_func(struct mlxsw_sp * mlxsw_sp,char * mtpptr_pl,bool ingress)2079*4882a593Smuzhiyun static void mlxsw_sp1_ptp_fifo_event_func(struct mlxsw_sp *mlxsw_sp,
2080*4882a593Smuzhiyun 					  char *mtpptr_pl, bool ingress)
2081*4882a593Smuzhiyun {
2082*4882a593Smuzhiyun 	u8 local_port;
2083*4882a593Smuzhiyun 	u8 num_rec;
2084*4882a593Smuzhiyun 	int i;
2085*4882a593Smuzhiyun 
2086*4882a593Smuzhiyun 	local_port = mlxsw_reg_mtpptr_local_port_get(mtpptr_pl);
2087*4882a593Smuzhiyun 	num_rec = mlxsw_reg_mtpptr_num_rec_get(mtpptr_pl);
2088*4882a593Smuzhiyun 	for (i = 0; i < num_rec; i++) {
2089*4882a593Smuzhiyun 		u8 domain_number;
2090*4882a593Smuzhiyun 		u8 message_type;
2091*4882a593Smuzhiyun 		u16 sequence_id;
2092*4882a593Smuzhiyun 		u64 timestamp;
2093*4882a593Smuzhiyun 
2094*4882a593Smuzhiyun 		mlxsw_reg_mtpptr_unpack(mtpptr_pl, i, &message_type,
2095*4882a593Smuzhiyun 					&domain_number, &sequence_id,
2096*4882a593Smuzhiyun 					&timestamp);
2097*4882a593Smuzhiyun 		mlxsw_sp1_ptp_got_timestamp(mlxsw_sp, ingress, local_port,
2098*4882a593Smuzhiyun 					    message_type, domain_number,
2099*4882a593Smuzhiyun 					    sequence_id, timestamp);
2100*4882a593Smuzhiyun 	}
2101*4882a593Smuzhiyun }
2102*4882a593Smuzhiyun 
mlxsw_sp1_ptp_ing_fifo_event_func(const struct mlxsw_reg_info * reg,char * mtpptr_pl,void * priv)2103*4882a593Smuzhiyun static void mlxsw_sp1_ptp_ing_fifo_event_func(const struct mlxsw_reg_info *reg,
2104*4882a593Smuzhiyun 					      char *mtpptr_pl, void *priv)
2105*4882a593Smuzhiyun {
2106*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = priv;
2107*4882a593Smuzhiyun 
2108*4882a593Smuzhiyun 	mlxsw_sp1_ptp_fifo_event_func(mlxsw_sp, mtpptr_pl, true);
2109*4882a593Smuzhiyun }
2110*4882a593Smuzhiyun 
mlxsw_sp1_ptp_egr_fifo_event_func(const struct mlxsw_reg_info * reg,char * mtpptr_pl,void * priv)2111*4882a593Smuzhiyun static void mlxsw_sp1_ptp_egr_fifo_event_func(const struct mlxsw_reg_info *reg,
2112*4882a593Smuzhiyun 					      char *mtpptr_pl, void *priv)
2113*4882a593Smuzhiyun {
2114*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = priv;
2115*4882a593Smuzhiyun 
2116*4882a593Smuzhiyun 	mlxsw_sp1_ptp_fifo_event_func(mlxsw_sp, mtpptr_pl, false);
2117*4882a593Smuzhiyun }
2118*4882a593Smuzhiyun 
mlxsw_sp_rx_listener_no_mark_func(struct sk_buff * skb,u8 local_port,void * priv)2119*4882a593Smuzhiyun void mlxsw_sp_rx_listener_no_mark_func(struct sk_buff *skb,
2120*4882a593Smuzhiyun 				       u8 local_port, void *priv)
2121*4882a593Smuzhiyun {
2122*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = priv;
2123*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port];
2124*4882a593Smuzhiyun 	struct mlxsw_sp_port_pcpu_stats *pcpu_stats;
2125*4882a593Smuzhiyun 
2126*4882a593Smuzhiyun 	if (unlikely(!mlxsw_sp_port)) {
2127*4882a593Smuzhiyun 		dev_warn_ratelimited(mlxsw_sp->bus_info->dev, "Port %d: skb received for non-existent port\n",
2128*4882a593Smuzhiyun 				     local_port);
2129*4882a593Smuzhiyun 		return;
2130*4882a593Smuzhiyun 	}
2131*4882a593Smuzhiyun 
2132*4882a593Smuzhiyun 	skb->dev = mlxsw_sp_port->dev;
2133*4882a593Smuzhiyun 
2134*4882a593Smuzhiyun 	pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats);
2135*4882a593Smuzhiyun 	u64_stats_update_begin(&pcpu_stats->syncp);
2136*4882a593Smuzhiyun 	pcpu_stats->rx_packets++;
2137*4882a593Smuzhiyun 	pcpu_stats->rx_bytes += skb->len;
2138*4882a593Smuzhiyun 	u64_stats_update_end(&pcpu_stats->syncp);
2139*4882a593Smuzhiyun 
2140*4882a593Smuzhiyun 	skb->protocol = eth_type_trans(skb, skb->dev);
2141*4882a593Smuzhiyun 	netif_receive_skb(skb);
2142*4882a593Smuzhiyun }
2143*4882a593Smuzhiyun 
mlxsw_sp_rx_listener_mark_func(struct sk_buff * skb,u8 local_port,void * priv)2144*4882a593Smuzhiyun static void mlxsw_sp_rx_listener_mark_func(struct sk_buff *skb, u8 local_port,
2145*4882a593Smuzhiyun 					   void *priv)
2146*4882a593Smuzhiyun {
2147*4882a593Smuzhiyun 	skb->offload_fwd_mark = 1;
2148*4882a593Smuzhiyun 	return mlxsw_sp_rx_listener_no_mark_func(skb, local_port, priv);
2149*4882a593Smuzhiyun }
2150*4882a593Smuzhiyun 
mlxsw_sp_rx_listener_l3_mark_func(struct sk_buff * skb,u8 local_port,void * priv)2151*4882a593Smuzhiyun static void mlxsw_sp_rx_listener_l3_mark_func(struct sk_buff *skb,
2152*4882a593Smuzhiyun 					      u8 local_port, void *priv)
2153*4882a593Smuzhiyun {
2154*4882a593Smuzhiyun 	skb->offload_l3_fwd_mark = 1;
2155*4882a593Smuzhiyun 	skb->offload_fwd_mark = 1;
2156*4882a593Smuzhiyun 	return mlxsw_sp_rx_listener_no_mark_func(skb, local_port, priv);
2157*4882a593Smuzhiyun }
2158*4882a593Smuzhiyun 
mlxsw_sp_ptp_receive(struct mlxsw_sp * mlxsw_sp,struct sk_buff * skb,u8 local_port)2159*4882a593Smuzhiyun void mlxsw_sp_ptp_receive(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
2160*4882a593Smuzhiyun 			  u8 local_port)
2161*4882a593Smuzhiyun {
2162*4882a593Smuzhiyun 	mlxsw_sp->ptp_ops->receive(mlxsw_sp, skb, local_port);
2163*4882a593Smuzhiyun }
2164*4882a593Smuzhiyun 
mlxsw_sp_sample_receive(struct mlxsw_sp * mlxsw_sp,struct sk_buff * skb,u8 local_port)2165*4882a593Smuzhiyun void mlxsw_sp_sample_receive(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
2166*4882a593Smuzhiyun 			     u8 local_port)
2167*4882a593Smuzhiyun {
2168*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port];
2169*4882a593Smuzhiyun 	struct mlxsw_sp_port_sample *sample;
2170*4882a593Smuzhiyun 	u32 size;
2171*4882a593Smuzhiyun 
2172*4882a593Smuzhiyun 	if (unlikely(!mlxsw_sp_port)) {
2173*4882a593Smuzhiyun 		dev_warn_ratelimited(mlxsw_sp->bus_info->dev, "Port %d: sample skb received for non-existent port\n",
2174*4882a593Smuzhiyun 				     local_port);
2175*4882a593Smuzhiyun 		goto out;
2176*4882a593Smuzhiyun 	}
2177*4882a593Smuzhiyun 
2178*4882a593Smuzhiyun 	rcu_read_lock();
2179*4882a593Smuzhiyun 	sample = rcu_dereference(mlxsw_sp_port->sample);
2180*4882a593Smuzhiyun 	if (!sample)
2181*4882a593Smuzhiyun 		goto out_unlock;
2182*4882a593Smuzhiyun 	size = sample->truncate ? sample->trunc_size : skb->len;
2183*4882a593Smuzhiyun 	psample_sample_packet(sample->psample_group, skb, size,
2184*4882a593Smuzhiyun 			      mlxsw_sp_port->dev->ifindex, 0, sample->rate);
2185*4882a593Smuzhiyun out_unlock:
2186*4882a593Smuzhiyun 	rcu_read_unlock();
2187*4882a593Smuzhiyun out:
2188*4882a593Smuzhiyun 	consume_skb(skb);
2189*4882a593Smuzhiyun }
2190*4882a593Smuzhiyun 
2191*4882a593Smuzhiyun #define MLXSW_SP_RXL_NO_MARK(_trap_id, _action, _trap_group, _is_ctrl)	\
2192*4882a593Smuzhiyun 	MLXSW_RXL(mlxsw_sp_rx_listener_no_mark_func, _trap_id, _action,	\
2193*4882a593Smuzhiyun 		  _is_ctrl, SP_##_trap_group, DISCARD)
2194*4882a593Smuzhiyun 
2195*4882a593Smuzhiyun #define MLXSW_SP_RXL_MARK(_trap_id, _action, _trap_group, _is_ctrl)	\
2196*4882a593Smuzhiyun 	MLXSW_RXL(mlxsw_sp_rx_listener_mark_func, _trap_id, _action,	\
2197*4882a593Smuzhiyun 		_is_ctrl, SP_##_trap_group, DISCARD)
2198*4882a593Smuzhiyun 
2199*4882a593Smuzhiyun #define MLXSW_SP_RXL_L3_MARK(_trap_id, _action, _trap_group, _is_ctrl)	\
2200*4882a593Smuzhiyun 	MLXSW_RXL(mlxsw_sp_rx_listener_l3_mark_func, _trap_id, _action,	\
2201*4882a593Smuzhiyun 		_is_ctrl, SP_##_trap_group, DISCARD)
2202*4882a593Smuzhiyun 
2203*4882a593Smuzhiyun #define MLXSW_SP_EVENTL(_func, _trap_id)		\
2204*4882a593Smuzhiyun 	MLXSW_EVENTL(_func, _trap_id, SP_EVENT)
2205*4882a593Smuzhiyun 
2206*4882a593Smuzhiyun static const struct mlxsw_listener mlxsw_sp_listener[] = {
2207*4882a593Smuzhiyun 	/* Events */
2208*4882a593Smuzhiyun 	MLXSW_SP_EVENTL(mlxsw_sp_pude_event_func, PUDE),
2209*4882a593Smuzhiyun 	/* L2 traps */
2210*4882a593Smuzhiyun 	MLXSW_SP_RXL_NO_MARK(FID_MISS, TRAP_TO_CPU, FID_MISS, false),
2211*4882a593Smuzhiyun 	/* L3 traps */
2212*4882a593Smuzhiyun 	MLXSW_SP_RXL_MARK(IPV6_UNSPECIFIED_ADDRESS, TRAP_TO_CPU, ROUTER_EXP,
2213*4882a593Smuzhiyun 			  false),
2214*4882a593Smuzhiyun 	MLXSW_SP_RXL_MARK(IPV6_LINK_LOCAL_SRC, TRAP_TO_CPU, ROUTER_EXP, false),
2215*4882a593Smuzhiyun 	MLXSW_SP_RXL_MARK(IPV6_MC_LINK_LOCAL_DEST, TRAP_TO_CPU, ROUTER_EXP,
2216*4882a593Smuzhiyun 			  false),
2217*4882a593Smuzhiyun 	MLXSW_SP_RXL_NO_MARK(DISCARD_ING_ROUTER_SIP_CLASS_E, FORWARD,
2218*4882a593Smuzhiyun 			     ROUTER_EXP, false),
2219*4882a593Smuzhiyun 	MLXSW_SP_RXL_NO_MARK(DISCARD_ING_ROUTER_MC_DMAC, FORWARD,
2220*4882a593Smuzhiyun 			     ROUTER_EXP, false),
2221*4882a593Smuzhiyun 	MLXSW_SP_RXL_NO_MARK(DISCARD_ING_ROUTER_SIP_DIP, FORWARD,
2222*4882a593Smuzhiyun 			     ROUTER_EXP, false),
2223*4882a593Smuzhiyun 	MLXSW_SP_RXL_NO_MARK(DISCARD_ING_ROUTER_DIP_LINK_LOCAL, FORWARD,
2224*4882a593Smuzhiyun 			     ROUTER_EXP, false),
2225*4882a593Smuzhiyun 	/* Multicast Router Traps */
2226*4882a593Smuzhiyun 	MLXSW_SP_RXL_MARK(ACL1, TRAP_TO_CPU, MULTICAST, false),
2227*4882a593Smuzhiyun 	MLXSW_SP_RXL_L3_MARK(ACL2, TRAP_TO_CPU, MULTICAST, false),
2228*4882a593Smuzhiyun 	/* NVE traps */
2229*4882a593Smuzhiyun 	MLXSW_SP_RXL_MARK(NVE_ENCAP_ARP, TRAP_TO_CPU, NEIGH_DISCOVERY, false),
2230*4882a593Smuzhiyun };
2231*4882a593Smuzhiyun 
2232*4882a593Smuzhiyun static const struct mlxsw_listener mlxsw_sp1_listener[] = {
2233*4882a593Smuzhiyun 	/* Events */
2234*4882a593Smuzhiyun 	MLXSW_EVENTL(mlxsw_sp1_ptp_egr_fifo_event_func, PTP_EGR_FIFO, SP_PTP0),
2235*4882a593Smuzhiyun 	MLXSW_EVENTL(mlxsw_sp1_ptp_ing_fifo_event_func, PTP_ING_FIFO, SP_PTP0),
2236*4882a593Smuzhiyun };
2237*4882a593Smuzhiyun 
mlxsw_sp_cpu_policers_set(struct mlxsw_core * mlxsw_core)2238*4882a593Smuzhiyun static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core)
2239*4882a593Smuzhiyun {
2240*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
2241*4882a593Smuzhiyun 	char qpcr_pl[MLXSW_REG_QPCR_LEN];
2242*4882a593Smuzhiyun 	enum mlxsw_reg_qpcr_ir_units ir_units;
2243*4882a593Smuzhiyun 	int max_cpu_policers;
2244*4882a593Smuzhiyun 	bool is_bytes;
2245*4882a593Smuzhiyun 	u8 burst_size;
2246*4882a593Smuzhiyun 	u32 rate;
2247*4882a593Smuzhiyun 	int i, err;
2248*4882a593Smuzhiyun 
2249*4882a593Smuzhiyun 	if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_CPU_POLICERS))
2250*4882a593Smuzhiyun 		return -EIO;
2251*4882a593Smuzhiyun 
2252*4882a593Smuzhiyun 	max_cpu_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_CPU_POLICERS);
2253*4882a593Smuzhiyun 
2254*4882a593Smuzhiyun 	ir_units = MLXSW_REG_QPCR_IR_UNITS_M;
2255*4882a593Smuzhiyun 	for (i = 0; i < max_cpu_policers; i++) {
2256*4882a593Smuzhiyun 		is_bytes = false;
2257*4882a593Smuzhiyun 		switch (i) {
2258*4882a593Smuzhiyun 		case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP:
2259*4882a593Smuzhiyun 		case MLXSW_REG_HTGT_TRAP_GROUP_SP_MULTICAST:
2260*4882a593Smuzhiyun 		case MLXSW_REG_HTGT_TRAP_GROUP_SP_FID_MISS:
2261*4882a593Smuzhiyun 			rate = 1024;
2262*4882a593Smuzhiyun 			burst_size = 7;
2263*4882a593Smuzhiyun 			break;
2264*4882a593Smuzhiyun 		default:
2265*4882a593Smuzhiyun 			continue;
2266*4882a593Smuzhiyun 		}
2267*4882a593Smuzhiyun 
2268*4882a593Smuzhiyun 		__set_bit(i, mlxsw_sp->trap->policers_usage);
2269*4882a593Smuzhiyun 		mlxsw_reg_qpcr_pack(qpcr_pl, i, ir_units, is_bytes, rate,
2270*4882a593Smuzhiyun 				    burst_size);
2271*4882a593Smuzhiyun 		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(qpcr), qpcr_pl);
2272*4882a593Smuzhiyun 		if (err)
2273*4882a593Smuzhiyun 			return err;
2274*4882a593Smuzhiyun 	}
2275*4882a593Smuzhiyun 
2276*4882a593Smuzhiyun 	return 0;
2277*4882a593Smuzhiyun }
2278*4882a593Smuzhiyun 
mlxsw_sp_trap_groups_set(struct mlxsw_core * mlxsw_core)2279*4882a593Smuzhiyun static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core)
2280*4882a593Smuzhiyun {
2281*4882a593Smuzhiyun 	char htgt_pl[MLXSW_REG_HTGT_LEN];
2282*4882a593Smuzhiyun 	enum mlxsw_reg_htgt_trap_group i;
2283*4882a593Smuzhiyun 	int max_cpu_policers;
2284*4882a593Smuzhiyun 	int max_trap_groups;
2285*4882a593Smuzhiyun 	u8 priority, tc;
2286*4882a593Smuzhiyun 	u16 policer_id;
2287*4882a593Smuzhiyun 	int err;
2288*4882a593Smuzhiyun 
2289*4882a593Smuzhiyun 	if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_TRAP_GROUPS))
2290*4882a593Smuzhiyun 		return -EIO;
2291*4882a593Smuzhiyun 
2292*4882a593Smuzhiyun 	max_trap_groups = MLXSW_CORE_RES_GET(mlxsw_core, MAX_TRAP_GROUPS);
2293*4882a593Smuzhiyun 	max_cpu_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_CPU_POLICERS);
2294*4882a593Smuzhiyun 
2295*4882a593Smuzhiyun 	for (i = 0; i < max_trap_groups; i++) {
2296*4882a593Smuzhiyun 		policer_id = i;
2297*4882a593Smuzhiyun 		switch (i) {
2298*4882a593Smuzhiyun 		case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP:
2299*4882a593Smuzhiyun 		case MLXSW_REG_HTGT_TRAP_GROUP_SP_MULTICAST:
2300*4882a593Smuzhiyun 		case MLXSW_REG_HTGT_TRAP_GROUP_SP_FID_MISS:
2301*4882a593Smuzhiyun 			priority = 1;
2302*4882a593Smuzhiyun 			tc = 1;
2303*4882a593Smuzhiyun 			break;
2304*4882a593Smuzhiyun 		case MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT:
2305*4882a593Smuzhiyun 			priority = MLXSW_REG_HTGT_DEFAULT_PRIORITY;
2306*4882a593Smuzhiyun 			tc = MLXSW_REG_HTGT_DEFAULT_TC;
2307*4882a593Smuzhiyun 			policer_id = MLXSW_REG_HTGT_INVALID_POLICER;
2308*4882a593Smuzhiyun 			break;
2309*4882a593Smuzhiyun 		default:
2310*4882a593Smuzhiyun 			continue;
2311*4882a593Smuzhiyun 		}
2312*4882a593Smuzhiyun 
2313*4882a593Smuzhiyun 		if (max_cpu_policers <= policer_id &&
2314*4882a593Smuzhiyun 		    policer_id != MLXSW_REG_HTGT_INVALID_POLICER)
2315*4882a593Smuzhiyun 			return -EIO;
2316*4882a593Smuzhiyun 
2317*4882a593Smuzhiyun 		mlxsw_reg_htgt_pack(htgt_pl, i, policer_id, priority, tc);
2318*4882a593Smuzhiyun 		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl);
2319*4882a593Smuzhiyun 		if (err)
2320*4882a593Smuzhiyun 			return err;
2321*4882a593Smuzhiyun 	}
2322*4882a593Smuzhiyun 
2323*4882a593Smuzhiyun 	return 0;
2324*4882a593Smuzhiyun }
2325*4882a593Smuzhiyun 
mlxsw_sp_traps_register(struct mlxsw_sp * mlxsw_sp,const struct mlxsw_listener listeners[],size_t listeners_count)2326*4882a593Smuzhiyun static int mlxsw_sp_traps_register(struct mlxsw_sp *mlxsw_sp,
2327*4882a593Smuzhiyun 				   const struct mlxsw_listener listeners[],
2328*4882a593Smuzhiyun 				   size_t listeners_count)
2329*4882a593Smuzhiyun {
2330*4882a593Smuzhiyun 	int i;
2331*4882a593Smuzhiyun 	int err;
2332*4882a593Smuzhiyun 
2333*4882a593Smuzhiyun 	for (i = 0; i < listeners_count; i++) {
2334*4882a593Smuzhiyun 		err = mlxsw_core_trap_register(mlxsw_sp->core,
2335*4882a593Smuzhiyun 					       &listeners[i],
2336*4882a593Smuzhiyun 					       mlxsw_sp);
2337*4882a593Smuzhiyun 		if (err)
2338*4882a593Smuzhiyun 			goto err_listener_register;
2339*4882a593Smuzhiyun 
2340*4882a593Smuzhiyun 	}
2341*4882a593Smuzhiyun 	return 0;
2342*4882a593Smuzhiyun 
2343*4882a593Smuzhiyun err_listener_register:
2344*4882a593Smuzhiyun 	for (i--; i >= 0; i--) {
2345*4882a593Smuzhiyun 		mlxsw_core_trap_unregister(mlxsw_sp->core,
2346*4882a593Smuzhiyun 					   &listeners[i],
2347*4882a593Smuzhiyun 					   mlxsw_sp);
2348*4882a593Smuzhiyun 	}
2349*4882a593Smuzhiyun 	return err;
2350*4882a593Smuzhiyun }
2351*4882a593Smuzhiyun 
mlxsw_sp_traps_unregister(struct mlxsw_sp * mlxsw_sp,const struct mlxsw_listener listeners[],size_t listeners_count)2352*4882a593Smuzhiyun static void mlxsw_sp_traps_unregister(struct mlxsw_sp *mlxsw_sp,
2353*4882a593Smuzhiyun 				      const struct mlxsw_listener listeners[],
2354*4882a593Smuzhiyun 				      size_t listeners_count)
2355*4882a593Smuzhiyun {
2356*4882a593Smuzhiyun 	int i;
2357*4882a593Smuzhiyun 
2358*4882a593Smuzhiyun 	for (i = 0; i < listeners_count; i++) {
2359*4882a593Smuzhiyun 		mlxsw_core_trap_unregister(mlxsw_sp->core,
2360*4882a593Smuzhiyun 					   &listeners[i],
2361*4882a593Smuzhiyun 					   mlxsw_sp);
2362*4882a593Smuzhiyun 	}
2363*4882a593Smuzhiyun }
2364*4882a593Smuzhiyun 
mlxsw_sp_traps_init(struct mlxsw_sp * mlxsw_sp)2365*4882a593Smuzhiyun static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp)
2366*4882a593Smuzhiyun {
2367*4882a593Smuzhiyun 	struct mlxsw_sp_trap *trap;
2368*4882a593Smuzhiyun 	u64 max_policers;
2369*4882a593Smuzhiyun 	int err;
2370*4882a593Smuzhiyun 
2371*4882a593Smuzhiyun 	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_CPU_POLICERS))
2372*4882a593Smuzhiyun 		return -EIO;
2373*4882a593Smuzhiyun 	max_policers = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_CPU_POLICERS);
2374*4882a593Smuzhiyun 	trap = kzalloc(struct_size(trap, policers_usage,
2375*4882a593Smuzhiyun 				   BITS_TO_LONGS(max_policers)), GFP_KERNEL);
2376*4882a593Smuzhiyun 	if (!trap)
2377*4882a593Smuzhiyun 		return -ENOMEM;
2378*4882a593Smuzhiyun 	trap->max_policers = max_policers;
2379*4882a593Smuzhiyun 	mlxsw_sp->trap = trap;
2380*4882a593Smuzhiyun 
2381*4882a593Smuzhiyun 	err = mlxsw_sp_cpu_policers_set(mlxsw_sp->core);
2382*4882a593Smuzhiyun 	if (err)
2383*4882a593Smuzhiyun 		goto err_cpu_policers_set;
2384*4882a593Smuzhiyun 
2385*4882a593Smuzhiyun 	err = mlxsw_sp_trap_groups_set(mlxsw_sp->core);
2386*4882a593Smuzhiyun 	if (err)
2387*4882a593Smuzhiyun 		goto err_trap_groups_set;
2388*4882a593Smuzhiyun 
2389*4882a593Smuzhiyun 	err = mlxsw_sp_traps_register(mlxsw_sp, mlxsw_sp_listener,
2390*4882a593Smuzhiyun 				      ARRAY_SIZE(mlxsw_sp_listener));
2391*4882a593Smuzhiyun 	if (err)
2392*4882a593Smuzhiyun 		goto err_traps_register;
2393*4882a593Smuzhiyun 
2394*4882a593Smuzhiyun 	err = mlxsw_sp_traps_register(mlxsw_sp, mlxsw_sp->listeners,
2395*4882a593Smuzhiyun 				      mlxsw_sp->listeners_count);
2396*4882a593Smuzhiyun 	if (err)
2397*4882a593Smuzhiyun 		goto err_extra_traps_init;
2398*4882a593Smuzhiyun 
2399*4882a593Smuzhiyun 	return 0;
2400*4882a593Smuzhiyun 
2401*4882a593Smuzhiyun err_extra_traps_init:
2402*4882a593Smuzhiyun 	mlxsw_sp_traps_unregister(mlxsw_sp, mlxsw_sp_listener,
2403*4882a593Smuzhiyun 				  ARRAY_SIZE(mlxsw_sp_listener));
2404*4882a593Smuzhiyun err_traps_register:
2405*4882a593Smuzhiyun err_trap_groups_set:
2406*4882a593Smuzhiyun err_cpu_policers_set:
2407*4882a593Smuzhiyun 	kfree(trap);
2408*4882a593Smuzhiyun 	return err;
2409*4882a593Smuzhiyun }
2410*4882a593Smuzhiyun 
mlxsw_sp_traps_fini(struct mlxsw_sp * mlxsw_sp)2411*4882a593Smuzhiyun static void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp)
2412*4882a593Smuzhiyun {
2413*4882a593Smuzhiyun 	mlxsw_sp_traps_unregister(mlxsw_sp, mlxsw_sp->listeners,
2414*4882a593Smuzhiyun 				  mlxsw_sp->listeners_count);
2415*4882a593Smuzhiyun 	mlxsw_sp_traps_unregister(mlxsw_sp, mlxsw_sp_listener,
2416*4882a593Smuzhiyun 				  ARRAY_SIZE(mlxsw_sp_listener));
2417*4882a593Smuzhiyun 	kfree(mlxsw_sp->trap);
2418*4882a593Smuzhiyun }
2419*4882a593Smuzhiyun 
2420*4882a593Smuzhiyun #define MLXSW_SP_LAG_SEED_INIT 0xcafecafe
2421*4882a593Smuzhiyun 
mlxsw_sp_lag_init(struct mlxsw_sp * mlxsw_sp)2422*4882a593Smuzhiyun static int mlxsw_sp_lag_init(struct mlxsw_sp *mlxsw_sp)
2423*4882a593Smuzhiyun {
2424*4882a593Smuzhiyun 	char slcr_pl[MLXSW_REG_SLCR_LEN];
2425*4882a593Smuzhiyun 	u32 seed;
2426*4882a593Smuzhiyun 	int err;
2427*4882a593Smuzhiyun 
2428*4882a593Smuzhiyun 	seed = jhash(mlxsw_sp->base_mac, sizeof(mlxsw_sp->base_mac),
2429*4882a593Smuzhiyun 		     MLXSW_SP_LAG_SEED_INIT);
2430*4882a593Smuzhiyun 	mlxsw_reg_slcr_pack(slcr_pl, MLXSW_REG_SLCR_LAG_HASH_SMAC |
2431*4882a593Smuzhiyun 				     MLXSW_REG_SLCR_LAG_HASH_DMAC |
2432*4882a593Smuzhiyun 				     MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE |
2433*4882a593Smuzhiyun 				     MLXSW_REG_SLCR_LAG_HASH_VLANID |
2434*4882a593Smuzhiyun 				     MLXSW_REG_SLCR_LAG_HASH_SIP |
2435*4882a593Smuzhiyun 				     MLXSW_REG_SLCR_LAG_HASH_DIP |
2436*4882a593Smuzhiyun 				     MLXSW_REG_SLCR_LAG_HASH_SPORT |
2437*4882a593Smuzhiyun 				     MLXSW_REG_SLCR_LAG_HASH_DPORT |
2438*4882a593Smuzhiyun 				     MLXSW_REG_SLCR_LAG_HASH_IPPROTO, seed);
2439*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcr), slcr_pl);
2440*4882a593Smuzhiyun 	if (err)
2441*4882a593Smuzhiyun 		return err;
2442*4882a593Smuzhiyun 
2443*4882a593Smuzhiyun 	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LAG) ||
2444*4882a593Smuzhiyun 	    !MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LAG_MEMBERS))
2445*4882a593Smuzhiyun 		return -EIO;
2446*4882a593Smuzhiyun 
2447*4882a593Smuzhiyun 	mlxsw_sp->lags = kcalloc(MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LAG),
2448*4882a593Smuzhiyun 				 sizeof(struct mlxsw_sp_upper),
2449*4882a593Smuzhiyun 				 GFP_KERNEL);
2450*4882a593Smuzhiyun 	if (!mlxsw_sp->lags)
2451*4882a593Smuzhiyun 		return -ENOMEM;
2452*4882a593Smuzhiyun 
2453*4882a593Smuzhiyun 	return 0;
2454*4882a593Smuzhiyun }
2455*4882a593Smuzhiyun 
mlxsw_sp_lag_fini(struct mlxsw_sp * mlxsw_sp)2456*4882a593Smuzhiyun static void mlxsw_sp_lag_fini(struct mlxsw_sp *mlxsw_sp)
2457*4882a593Smuzhiyun {
2458*4882a593Smuzhiyun 	kfree(mlxsw_sp->lags);
2459*4882a593Smuzhiyun }
2460*4882a593Smuzhiyun 
mlxsw_sp_basic_trap_groups_set(struct mlxsw_core * mlxsw_core)2461*4882a593Smuzhiyun static int mlxsw_sp_basic_trap_groups_set(struct mlxsw_core *mlxsw_core)
2462*4882a593Smuzhiyun {
2463*4882a593Smuzhiyun 	char htgt_pl[MLXSW_REG_HTGT_LEN];
2464*4882a593Smuzhiyun 	int err;
2465*4882a593Smuzhiyun 
2466*4882a593Smuzhiyun 	mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD,
2467*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_INVALID_POLICER,
2468*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_PRIORITY,
2469*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_TC);
2470*4882a593Smuzhiyun 	err =  mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl);
2471*4882a593Smuzhiyun 	if (err)
2472*4882a593Smuzhiyun 		return err;
2473*4882a593Smuzhiyun 
2474*4882a593Smuzhiyun 	mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_MFDE,
2475*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_INVALID_POLICER,
2476*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_PRIORITY,
2477*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_TC);
2478*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl);
2479*4882a593Smuzhiyun 	if (err)
2480*4882a593Smuzhiyun 		return err;
2481*4882a593Smuzhiyun 
2482*4882a593Smuzhiyun 	mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_MTWE,
2483*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_INVALID_POLICER,
2484*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_PRIORITY,
2485*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_TC);
2486*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl);
2487*4882a593Smuzhiyun 	if (err)
2488*4882a593Smuzhiyun 		return err;
2489*4882a593Smuzhiyun 
2490*4882a593Smuzhiyun 	mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_PMPE,
2491*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_INVALID_POLICER,
2492*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_PRIORITY,
2493*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_TC);
2494*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl);
2495*4882a593Smuzhiyun }
2496*4882a593Smuzhiyun 
2497*4882a593Smuzhiyun static const struct mlxsw_sp_ptp_ops mlxsw_sp1_ptp_ops = {
2498*4882a593Smuzhiyun 	.clock_init	= mlxsw_sp1_ptp_clock_init,
2499*4882a593Smuzhiyun 	.clock_fini	= mlxsw_sp1_ptp_clock_fini,
2500*4882a593Smuzhiyun 	.init		= mlxsw_sp1_ptp_init,
2501*4882a593Smuzhiyun 	.fini		= mlxsw_sp1_ptp_fini,
2502*4882a593Smuzhiyun 	.receive	= mlxsw_sp1_ptp_receive,
2503*4882a593Smuzhiyun 	.transmitted	= mlxsw_sp1_ptp_transmitted,
2504*4882a593Smuzhiyun 	.hwtstamp_get	= mlxsw_sp1_ptp_hwtstamp_get,
2505*4882a593Smuzhiyun 	.hwtstamp_set	= mlxsw_sp1_ptp_hwtstamp_set,
2506*4882a593Smuzhiyun 	.shaper_work	= mlxsw_sp1_ptp_shaper_work,
2507*4882a593Smuzhiyun 	.get_ts_info	= mlxsw_sp1_ptp_get_ts_info,
2508*4882a593Smuzhiyun 	.get_stats_count = mlxsw_sp1_get_stats_count,
2509*4882a593Smuzhiyun 	.get_stats_strings = mlxsw_sp1_get_stats_strings,
2510*4882a593Smuzhiyun 	.get_stats	= mlxsw_sp1_get_stats,
2511*4882a593Smuzhiyun };
2512*4882a593Smuzhiyun 
2513*4882a593Smuzhiyun static const struct mlxsw_sp_ptp_ops mlxsw_sp2_ptp_ops = {
2514*4882a593Smuzhiyun 	.clock_init	= mlxsw_sp2_ptp_clock_init,
2515*4882a593Smuzhiyun 	.clock_fini	= mlxsw_sp2_ptp_clock_fini,
2516*4882a593Smuzhiyun 	.init		= mlxsw_sp2_ptp_init,
2517*4882a593Smuzhiyun 	.fini		= mlxsw_sp2_ptp_fini,
2518*4882a593Smuzhiyun 	.receive	= mlxsw_sp2_ptp_receive,
2519*4882a593Smuzhiyun 	.transmitted	= mlxsw_sp2_ptp_transmitted,
2520*4882a593Smuzhiyun 	.hwtstamp_get	= mlxsw_sp2_ptp_hwtstamp_get,
2521*4882a593Smuzhiyun 	.hwtstamp_set	= mlxsw_sp2_ptp_hwtstamp_set,
2522*4882a593Smuzhiyun 	.shaper_work	= mlxsw_sp2_ptp_shaper_work,
2523*4882a593Smuzhiyun 	.get_ts_info	= mlxsw_sp2_ptp_get_ts_info,
2524*4882a593Smuzhiyun 	.get_stats_count = mlxsw_sp2_get_stats_count,
2525*4882a593Smuzhiyun 	.get_stats_strings = mlxsw_sp2_get_stats_strings,
2526*4882a593Smuzhiyun 	.get_stats	= mlxsw_sp2_get_stats,
2527*4882a593Smuzhiyun };
2528*4882a593Smuzhiyun 
2529*4882a593Smuzhiyun static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
2530*4882a593Smuzhiyun 				    unsigned long event, void *ptr);
2531*4882a593Smuzhiyun 
mlxsw_sp_init(struct mlxsw_core * mlxsw_core,const struct mlxsw_bus_info * mlxsw_bus_info,struct netlink_ext_ack * extack)2532*4882a593Smuzhiyun static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
2533*4882a593Smuzhiyun 			 const struct mlxsw_bus_info *mlxsw_bus_info,
2534*4882a593Smuzhiyun 			 struct netlink_ext_ack *extack)
2535*4882a593Smuzhiyun {
2536*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
2537*4882a593Smuzhiyun 	int err;
2538*4882a593Smuzhiyun 
2539*4882a593Smuzhiyun 	mlxsw_sp->core = mlxsw_core;
2540*4882a593Smuzhiyun 	mlxsw_sp->bus_info = mlxsw_bus_info;
2541*4882a593Smuzhiyun 
2542*4882a593Smuzhiyun 	mlxsw_core_emad_string_tlv_enable(mlxsw_core);
2543*4882a593Smuzhiyun 
2544*4882a593Smuzhiyun 	err = mlxsw_sp_base_mac_get(mlxsw_sp);
2545*4882a593Smuzhiyun 	if (err) {
2546*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to get base mac\n");
2547*4882a593Smuzhiyun 		return err;
2548*4882a593Smuzhiyun 	}
2549*4882a593Smuzhiyun 
2550*4882a593Smuzhiyun 	err = mlxsw_sp_kvdl_init(mlxsw_sp);
2551*4882a593Smuzhiyun 	if (err) {
2552*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize KVDL\n");
2553*4882a593Smuzhiyun 		return err;
2554*4882a593Smuzhiyun 	}
2555*4882a593Smuzhiyun 
2556*4882a593Smuzhiyun 	err = mlxsw_sp_fids_init(mlxsw_sp);
2557*4882a593Smuzhiyun 	if (err) {
2558*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize FIDs\n");
2559*4882a593Smuzhiyun 		goto err_fids_init;
2560*4882a593Smuzhiyun 	}
2561*4882a593Smuzhiyun 
2562*4882a593Smuzhiyun 	err = mlxsw_sp_policers_init(mlxsw_sp);
2563*4882a593Smuzhiyun 	if (err) {
2564*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize policers\n");
2565*4882a593Smuzhiyun 		goto err_policers_init;
2566*4882a593Smuzhiyun 	}
2567*4882a593Smuzhiyun 
2568*4882a593Smuzhiyun 	err = mlxsw_sp_traps_init(mlxsw_sp);
2569*4882a593Smuzhiyun 	if (err) {
2570*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to set traps\n");
2571*4882a593Smuzhiyun 		goto err_traps_init;
2572*4882a593Smuzhiyun 	}
2573*4882a593Smuzhiyun 
2574*4882a593Smuzhiyun 	err = mlxsw_sp_devlink_traps_init(mlxsw_sp);
2575*4882a593Smuzhiyun 	if (err) {
2576*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize devlink traps\n");
2577*4882a593Smuzhiyun 		goto err_devlink_traps_init;
2578*4882a593Smuzhiyun 	}
2579*4882a593Smuzhiyun 
2580*4882a593Smuzhiyun 	err = mlxsw_sp_buffers_init(mlxsw_sp);
2581*4882a593Smuzhiyun 	if (err) {
2582*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize buffers\n");
2583*4882a593Smuzhiyun 		goto err_buffers_init;
2584*4882a593Smuzhiyun 	}
2585*4882a593Smuzhiyun 
2586*4882a593Smuzhiyun 	err = mlxsw_sp_lag_init(mlxsw_sp);
2587*4882a593Smuzhiyun 	if (err) {
2588*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize LAG\n");
2589*4882a593Smuzhiyun 		goto err_lag_init;
2590*4882a593Smuzhiyun 	}
2591*4882a593Smuzhiyun 
2592*4882a593Smuzhiyun 	/* Initialize SPAN before router and switchdev, so that those components
2593*4882a593Smuzhiyun 	 * can call mlxsw_sp_span_respin().
2594*4882a593Smuzhiyun 	 */
2595*4882a593Smuzhiyun 	err = mlxsw_sp_span_init(mlxsw_sp);
2596*4882a593Smuzhiyun 	if (err) {
2597*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to init span system\n");
2598*4882a593Smuzhiyun 		goto err_span_init;
2599*4882a593Smuzhiyun 	}
2600*4882a593Smuzhiyun 
2601*4882a593Smuzhiyun 	err = mlxsw_sp_switchdev_init(mlxsw_sp);
2602*4882a593Smuzhiyun 	if (err) {
2603*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize switchdev\n");
2604*4882a593Smuzhiyun 		goto err_switchdev_init;
2605*4882a593Smuzhiyun 	}
2606*4882a593Smuzhiyun 
2607*4882a593Smuzhiyun 	err = mlxsw_sp_counter_pool_init(mlxsw_sp);
2608*4882a593Smuzhiyun 	if (err) {
2609*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to init counter pool\n");
2610*4882a593Smuzhiyun 		goto err_counter_pool_init;
2611*4882a593Smuzhiyun 	}
2612*4882a593Smuzhiyun 
2613*4882a593Smuzhiyun 	err = mlxsw_sp_afa_init(mlxsw_sp);
2614*4882a593Smuzhiyun 	if (err) {
2615*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize ACL actions\n");
2616*4882a593Smuzhiyun 		goto err_afa_init;
2617*4882a593Smuzhiyun 	}
2618*4882a593Smuzhiyun 
2619*4882a593Smuzhiyun 	err = mlxsw_sp_nve_init(mlxsw_sp);
2620*4882a593Smuzhiyun 	if (err) {
2621*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize NVE\n");
2622*4882a593Smuzhiyun 		goto err_nve_init;
2623*4882a593Smuzhiyun 	}
2624*4882a593Smuzhiyun 
2625*4882a593Smuzhiyun 	err = mlxsw_sp_acl_init(mlxsw_sp);
2626*4882a593Smuzhiyun 	if (err) {
2627*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize ACL\n");
2628*4882a593Smuzhiyun 		goto err_acl_init;
2629*4882a593Smuzhiyun 	}
2630*4882a593Smuzhiyun 
2631*4882a593Smuzhiyun 	err = mlxsw_sp_router_init(mlxsw_sp, extack);
2632*4882a593Smuzhiyun 	if (err) {
2633*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize router\n");
2634*4882a593Smuzhiyun 		goto err_router_init;
2635*4882a593Smuzhiyun 	}
2636*4882a593Smuzhiyun 
2637*4882a593Smuzhiyun 	if (mlxsw_sp->bus_info->read_frc_capable) {
2638*4882a593Smuzhiyun 		/* NULL is a valid return value from clock_init */
2639*4882a593Smuzhiyun 		mlxsw_sp->clock =
2640*4882a593Smuzhiyun 			mlxsw_sp->ptp_ops->clock_init(mlxsw_sp,
2641*4882a593Smuzhiyun 						      mlxsw_sp->bus_info->dev);
2642*4882a593Smuzhiyun 		if (IS_ERR(mlxsw_sp->clock)) {
2643*4882a593Smuzhiyun 			err = PTR_ERR(mlxsw_sp->clock);
2644*4882a593Smuzhiyun 			dev_err(mlxsw_sp->bus_info->dev, "Failed to init ptp clock\n");
2645*4882a593Smuzhiyun 			goto err_ptp_clock_init;
2646*4882a593Smuzhiyun 		}
2647*4882a593Smuzhiyun 	}
2648*4882a593Smuzhiyun 
2649*4882a593Smuzhiyun 	if (mlxsw_sp->clock) {
2650*4882a593Smuzhiyun 		/* NULL is a valid return value from ptp_ops->init */
2651*4882a593Smuzhiyun 		mlxsw_sp->ptp_state = mlxsw_sp->ptp_ops->init(mlxsw_sp);
2652*4882a593Smuzhiyun 		if (IS_ERR(mlxsw_sp->ptp_state)) {
2653*4882a593Smuzhiyun 			err = PTR_ERR(mlxsw_sp->ptp_state);
2654*4882a593Smuzhiyun 			dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize PTP\n");
2655*4882a593Smuzhiyun 			goto err_ptp_init;
2656*4882a593Smuzhiyun 		}
2657*4882a593Smuzhiyun 	}
2658*4882a593Smuzhiyun 
2659*4882a593Smuzhiyun 	/* Initialize netdevice notifier after router and SPAN is initialized,
2660*4882a593Smuzhiyun 	 * so that the event handler can use router structures and call SPAN
2661*4882a593Smuzhiyun 	 * respin.
2662*4882a593Smuzhiyun 	 */
2663*4882a593Smuzhiyun 	mlxsw_sp->netdevice_nb.notifier_call = mlxsw_sp_netdevice_event;
2664*4882a593Smuzhiyun 	err = register_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),
2665*4882a593Smuzhiyun 					      &mlxsw_sp->netdevice_nb);
2666*4882a593Smuzhiyun 	if (err) {
2667*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to register netdev notifier\n");
2668*4882a593Smuzhiyun 		goto err_netdev_notifier;
2669*4882a593Smuzhiyun 	}
2670*4882a593Smuzhiyun 
2671*4882a593Smuzhiyun 	err = mlxsw_sp_dpipe_init(mlxsw_sp);
2672*4882a593Smuzhiyun 	if (err) {
2673*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to init pipeline debug\n");
2674*4882a593Smuzhiyun 		goto err_dpipe_init;
2675*4882a593Smuzhiyun 	}
2676*4882a593Smuzhiyun 
2677*4882a593Smuzhiyun 	err = mlxsw_sp_port_module_info_init(mlxsw_sp);
2678*4882a593Smuzhiyun 	if (err) {
2679*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to init port module info\n");
2680*4882a593Smuzhiyun 		goto err_port_module_info_init;
2681*4882a593Smuzhiyun 	}
2682*4882a593Smuzhiyun 
2683*4882a593Smuzhiyun 	err = mlxsw_sp_ports_create(mlxsw_sp);
2684*4882a593Smuzhiyun 	if (err) {
2685*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n");
2686*4882a593Smuzhiyun 		goto err_ports_create;
2687*4882a593Smuzhiyun 	}
2688*4882a593Smuzhiyun 
2689*4882a593Smuzhiyun 	return 0;
2690*4882a593Smuzhiyun 
2691*4882a593Smuzhiyun err_ports_create:
2692*4882a593Smuzhiyun 	mlxsw_sp_port_module_info_fini(mlxsw_sp);
2693*4882a593Smuzhiyun err_port_module_info_init:
2694*4882a593Smuzhiyun 	mlxsw_sp_dpipe_fini(mlxsw_sp);
2695*4882a593Smuzhiyun err_dpipe_init:
2696*4882a593Smuzhiyun 	unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),
2697*4882a593Smuzhiyun 					  &mlxsw_sp->netdevice_nb);
2698*4882a593Smuzhiyun err_netdev_notifier:
2699*4882a593Smuzhiyun 	if (mlxsw_sp->clock)
2700*4882a593Smuzhiyun 		mlxsw_sp->ptp_ops->fini(mlxsw_sp->ptp_state);
2701*4882a593Smuzhiyun err_ptp_init:
2702*4882a593Smuzhiyun 	if (mlxsw_sp->clock)
2703*4882a593Smuzhiyun 		mlxsw_sp->ptp_ops->clock_fini(mlxsw_sp->clock);
2704*4882a593Smuzhiyun err_ptp_clock_init:
2705*4882a593Smuzhiyun 	mlxsw_sp_router_fini(mlxsw_sp);
2706*4882a593Smuzhiyun err_router_init:
2707*4882a593Smuzhiyun 	mlxsw_sp_acl_fini(mlxsw_sp);
2708*4882a593Smuzhiyun err_acl_init:
2709*4882a593Smuzhiyun 	mlxsw_sp_nve_fini(mlxsw_sp);
2710*4882a593Smuzhiyun err_nve_init:
2711*4882a593Smuzhiyun 	mlxsw_sp_afa_fini(mlxsw_sp);
2712*4882a593Smuzhiyun err_afa_init:
2713*4882a593Smuzhiyun 	mlxsw_sp_counter_pool_fini(mlxsw_sp);
2714*4882a593Smuzhiyun err_counter_pool_init:
2715*4882a593Smuzhiyun 	mlxsw_sp_switchdev_fini(mlxsw_sp);
2716*4882a593Smuzhiyun err_switchdev_init:
2717*4882a593Smuzhiyun 	mlxsw_sp_span_fini(mlxsw_sp);
2718*4882a593Smuzhiyun err_span_init:
2719*4882a593Smuzhiyun 	mlxsw_sp_lag_fini(mlxsw_sp);
2720*4882a593Smuzhiyun err_lag_init:
2721*4882a593Smuzhiyun 	mlxsw_sp_buffers_fini(mlxsw_sp);
2722*4882a593Smuzhiyun err_buffers_init:
2723*4882a593Smuzhiyun 	mlxsw_sp_devlink_traps_fini(mlxsw_sp);
2724*4882a593Smuzhiyun err_devlink_traps_init:
2725*4882a593Smuzhiyun 	mlxsw_sp_traps_fini(mlxsw_sp);
2726*4882a593Smuzhiyun err_traps_init:
2727*4882a593Smuzhiyun 	mlxsw_sp_policers_fini(mlxsw_sp);
2728*4882a593Smuzhiyun err_policers_init:
2729*4882a593Smuzhiyun 	mlxsw_sp_fids_fini(mlxsw_sp);
2730*4882a593Smuzhiyun err_fids_init:
2731*4882a593Smuzhiyun 	mlxsw_sp_kvdl_fini(mlxsw_sp);
2732*4882a593Smuzhiyun 	return err;
2733*4882a593Smuzhiyun }
2734*4882a593Smuzhiyun 
mlxsw_sp1_init(struct mlxsw_core * mlxsw_core,const struct mlxsw_bus_info * mlxsw_bus_info,struct netlink_ext_ack * extack)2735*4882a593Smuzhiyun static int mlxsw_sp1_init(struct mlxsw_core *mlxsw_core,
2736*4882a593Smuzhiyun 			  const struct mlxsw_bus_info *mlxsw_bus_info,
2737*4882a593Smuzhiyun 			  struct netlink_ext_ack *extack)
2738*4882a593Smuzhiyun {
2739*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
2740*4882a593Smuzhiyun 
2741*4882a593Smuzhiyun 	mlxsw_sp->kvdl_ops = &mlxsw_sp1_kvdl_ops;
2742*4882a593Smuzhiyun 	mlxsw_sp->afa_ops = &mlxsw_sp1_act_afa_ops;
2743*4882a593Smuzhiyun 	mlxsw_sp->afk_ops = &mlxsw_sp1_afk_ops;
2744*4882a593Smuzhiyun 	mlxsw_sp->mr_tcam_ops = &mlxsw_sp1_mr_tcam_ops;
2745*4882a593Smuzhiyun 	mlxsw_sp->acl_rulei_ops = &mlxsw_sp1_acl_rulei_ops;
2746*4882a593Smuzhiyun 	mlxsw_sp->acl_tcam_ops = &mlxsw_sp1_acl_tcam_ops;
2747*4882a593Smuzhiyun 	mlxsw_sp->nve_ops_arr = mlxsw_sp1_nve_ops_arr;
2748*4882a593Smuzhiyun 	mlxsw_sp->mac_mask = mlxsw_sp1_mac_mask;
2749*4882a593Smuzhiyun 	mlxsw_sp->rif_ops_arr = mlxsw_sp1_rif_ops_arr;
2750*4882a593Smuzhiyun 	mlxsw_sp->sb_vals = &mlxsw_sp1_sb_vals;
2751*4882a593Smuzhiyun 	mlxsw_sp->sb_ops = &mlxsw_sp1_sb_ops;
2752*4882a593Smuzhiyun 	mlxsw_sp->port_type_speed_ops = &mlxsw_sp1_port_type_speed_ops;
2753*4882a593Smuzhiyun 	mlxsw_sp->ptp_ops = &mlxsw_sp1_ptp_ops;
2754*4882a593Smuzhiyun 	mlxsw_sp->span_ops = &mlxsw_sp1_span_ops;
2755*4882a593Smuzhiyun 	mlxsw_sp->policer_core_ops = &mlxsw_sp1_policer_core_ops;
2756*4882a593Smuzhiyun 	mlxsw_sp->trap_ops = &mlxsw_sp1_trap_ops;
2757*4882a593Smuzhiyun 	mlxsw_sp->listeners = mlxsw_sp1_listener;
2758*4882a593Smuzhiyun 	mlxsw_sp->listeners_count = ARRAY_SIZE(mlxsw_sp1_listener);
2759*4882a593Smuzhiyun 	mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP1;
2760*4882a593Smuzhiyun 
2761*4882a593Smuzhiyun 	return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack);
2762*4882a593Smuzhiyun }
2763*4882a593Smuzhiyun 
mlxsw_sp2_init(struct mlxsw_core * mlxsw_core,const struct mlxsw_bus_info * mlxsw_bus_info,struct netlink_ext_ack * extack)2764*4882a593Smuzhiyun static int mlxsw_sp2_init(struct mlxsw_core *mlxsw_core,
2765*4882a593Smuzhiyun 			  const struct mlxsw_bus_info *mlxsw_bus_info,
2766*4882a593Smuzhiyun 			  struct netlink_ext_ack *extack)
2767*4882a593Smuzhiyun {
2768*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
2769*4882a593Smuzhiyun 
2770*4882a593Smuzhiyun 	mlxsw_sp->kvdl_ops = &mlxsw_sp2_kvdl_ops;
2771*4882a593Smuzhiyun 	mlxsw_sp->afa_ops = &mlxsw_sp2_act_afa_ops;
2772*4882a593Smuzhiyun 	mlxsw_sp->afk_ops = &mlxsw_sp2_afk_ops;
2773*4882a593Smuzhiyun 	mlxsw_sp->mr_tcam_ops = &mlxsw_sp2_mr_tcam_ops;
2774*4882a593Smuzhiyun 	mlxsw_sp->acl_rulei_ops = &mlxsw_sp2_acl_rulei_ops;
2775*4882a593Smuzhiyun 	mlxsw_sp->acl_tcam_ops = &mlxsw_sp2_acl_tcam_ops;
2776*4882a593Smuzhiyun 	mlxsw_sp->nve_ops_arr = mlxsw_sp2_nve_ops_arr;
2777*4882a593Smuzhiyun 	mlxsw_sp->mac_mask = mlxsw_sp2_mac_mask;
2778*4882a593Smuzhiyun 	mlxsw_sp->rif_ops_arr = mlxsw_sp2_rif_ops_arr;
2779*4882a593Smuzhiyun 	mlxsw_sp->sb_vals = &mlxsw_sp2_sb_vals;
2780*4882a593Smuzhiyun 	mlxsw_sp->sb_ops = &mlxsw_sp2_sb_ops;
2781*4882a593Smuzhiyun 	mlxsw_sp->port_type_speed_ops = &mlxsw_sp2_port_type_speed_ops;
2782*4882a593Smuzhiyun 	mlxsw_sp->ptp_ops = &mlxsw_sp2_ptp_ops;
2783*4882a593Smuzhiyun 	mlxsw_sp->span_ops = &mlxsw_sp2_span_ops;
2784*4882a593Smuzhiyun 	mlxsw_sp->policer_core_ops = &mlxsw_sp2_policer_core_ops;
2785*4882a593Smuzhiyun 	mlxsw_sp->trap_ops = &mlxsw_sp2_trap_ops;
2786*4882a593Smuzhiyun 	mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP2;
2787*4882a593Smuzhiyun 
2788*4882a593Smuzhiyun 	return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack);
2789*4882a593Smuzhiyun }
2790*4882a593Smuzhiyun 
mlxsw_sp3_init(struct mlxsw_core * mlxsw_core,const struct mlxsw_bus_info * mlxsw_bus_info,struct netlink_ext_ack * extack)2791*4882a593Smuzhiyun static int mlxsw_sp3_init(struct mlxsw_core *mlxsw_core,
2792*4882a593Smuzhiyun 			  const struct mlxsw_bus_info *mlxsw_bus_info,
2793*4882a593Smuzhiyun 			  struct netlink_ext_ack *extack)
2794*4882a593Smuzhiyun {
2795*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
2796*4882a593Smuzhiyun 
2797*4882a593Smuzhiyun 	mlxsw_sp->kvdl_ops = &mlxsw_sp2_kvdl_ops;
2798*4882a593Smuzhiyun 	mlxsw_sp->afa_ops = &mlxsw_sp2_act_afa_ops;
2799*4882a593Smuzhiyun 	mlxsw_sp->afk_ops = &mlxsw_sp2_afk_ops;
2800*4882a593Smuzhiyun 	mlxsw_sp->mr_tcam_ops = &mlxsw_sp2_mr_tcam_ops;
2801*4882a593Smuzhiyun 	mlxsw_sp->acl_rulei_ops = &mlxsw_sp2_acl_rulei_ops;
2802*4882a593Smuzhiyun 	mlxsw_sp->acl_tcam_ops = &mlxsw_sp2_acl_tcam_ops;
2803*4882a593Smuzhiyun 	mlxsw_sp->nve_ops_arr = mlxsw_sp2_nve_ops_arr;
2804*4882a593Smuzhiyun 	mlxsw_sp->mac_mask = mlxsw_sp2_mac_mask;
2805*4882a593Smuzhiyun 	mlxsw_sp->rif_ops_arr = mlxsw_sp2_rif_ops_arr;
2806*4882a593Smuzhiyun 	mlxsw_sp->sb_vals = &mlxsw_sp2_sb_vals;
2807*4882a593Smuzhiyun 	mlxsw_sp->sb_ops = &mlxsw_sp3_sb_ops;
2808*4882a593Smuzhiyun 	mlxsw_sp->port_type_speed_ops = &mlxsw_sp2_port_type_speed_ops;
2809*4882a593Smuzhiyun 	mlxsw_sp->ptp_ops = &mlxsw_sp2_ptp_ops;
2810*4882a593Smuzhiyun 	mlxsw_sp->span_ops = &mlxsw_sp3_span_ops;
2811*4882a593Smuzhiyun 	mlxsw_sp->policer_core_ops = &mlxsw_sp2_policer_core_ops;
2812*4882a593Smuzhiyun 	mlxsw_sp->trap_ops = &mlxsw_sp2_trap_ops;
2813*4882a593Smuzhiyun 	mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP3;
2814*4882a593Smuzhiyun 
2815*4882a593Smuzhiyun 	return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack);
2816*4882a593Smuzhiyun }
2817*4882a593Smuzhiyun 
mlxsw_sp_fini(struct mlxsw_core * mlxsw_core)2818*4882a593Smuzhiyun static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
2819*4882a593Smuzhiyun {
2820*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
2821*4882a593Smuzhiyun 
2822*4882a593Smuzhiyun 	mlxsw_sp_ports_remove(mlxsw_sp);
2823*4882a593Smuzhiyun 	mlxsw_sp_port_module_info_fini(mlxsw_sp);
2824*4882a593Smuzhiyun 	mlxsw_sp_dpipe_fini(mlxsw_sp);
2825*4882a593Smuzhiyun 	unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),
2826*4882a593Smuzhiyun 					  &mlxsw_sp->netdevice_nb);
2827*4882a593Smuzhiyun 	if (mlxsw_sp->clock) {
2828*4882a593Smuzhiyun 		mlxsw_sp->ptp_ops->fini(mlxsw_sp->ptp_state);
2829*4882a593Smuzhiyun 		mlxsw_sp->ptp_ops->clock_fini(mlxsw_sp->clock);
2830*4882a593Smuzhiyun 	}
2831*4882a593Smuzhiyun 	mlxsw_sp_router_fini(mlxsw_sp);
2832*4882a593Smuzhiyun 	mlxsw_sp_acl_fini(mlxsw_sp);
2833*4882a593Smuzhiyun 	mlxsw_sp_nve_fini(mlxsw_sp);
2834*4882a593Smuzhiyun 	mlxsw_sp_afa_fini(mlxsw_sp);
2835*4882a593Smuzhiyun 	mlxsw_sp_counter_pool_fini(mlxsw_sp);
2836*4882a593Smuzhiyun 	mlxsw_sp_switchdev_fini(mlxsw_sp);
2837*4882a593Smuzhiyun 	mlxsw_sp_span_fini(mlxsw_sp);
2838*4882a593Smuzhiyun 	mlxsw_sp_lag_fini(mlxsw_sp);
2839*4882a593Smuzhiyun 	mlxsw_sp_buffers_fini(mlxsw_sp);
2840*4882a593Smuzhiyun 	mlxsw_sp_devlink_traps_fini(mlxsw_sp);
2841*4882a593Smuzhiyun 	mlxsw_sp_traps_fini(mlxsw_sp);
2842*4882a593Smuzhiyun 	mlxsw_sp_policers_fini(mlxsw_sp);
2843*4882a593Smuzhiyun 	mlxsw_sp_fids_fini(mlxsw_sp);
2844*4882a593Smuzhiyun 	mlxsw_sp_kvdl_fini(mlxsw_sp);
2845*4882a593Smuzhiyun }
2846*4882a593Smuzhiyun 
2847*4882a593Smuzhiyun /* Per-FID flood tables are used for both "true" 802.1D FIDs and emulated
2848*4882a593Smuzhiyun  * 802.1Q FIDs
2849*4882a593Smuzhiyun  */
2850*4882a593Smuzhiyun #define MLXSW_SP_FID_FLOOD_TABLE_SIZE	(MLXSW_SP_FID_8021D_MAX + \
2851*4882a593Smuzhiyun 					 VLAN_VID_MASK - 1)
2852*4882a593Smuzhiyun 
2853*4882a593Smuzhiyun static const struct mlxsw_config_profile mlxsw_sp1_config_profile = {
2854*4882a593Smuzhiyun 	.used_max_mid			= 1,
2855*4882a593Smuzhiyun 	.max_mid			= MLXSW_SP_MID_MAX,
2856*4882a593Smuzhiyun 	.used_flood_tables		= 1,
2857*4882a593Smuzhiyun 	.used_flood_mode		= 1,
2858*4882a593Smuzhiyun 	.flood_mode			= 3,
2859*4882a593Smuzhiyun 	.max_fid_flood_tables		= 3,
2860*4882a593Smuzhiyun 	.fid_flood_table_size		= MLXSW_SP_FID_FLOOD_TABLE_SIZE,
2861*4882a593Smuzhiyun 	.used_max_ib_mc			= 1,
2862*4882a593Smuzhiyun 	.max_ib_mc			= 0,
2863*4882a593Smuzhiyun 	.used_max_pkey			= 1,
2864*4882a593Smuzhiyun 	.max_pkey			= 0,
2865*4882a593Smuzhiyun 	.used_kvd_sizes			= 1,
2866*4882a593Smuzhiyun 	.kvd_hash_single_parts		= 59,
2867*4882a593Smuzhiyun 	.kvd_hash_double_parts		= 41,
2868*4882a593Smuzhiyun 	.kvd_linear_size		= MLXSW_SP_KVD_LINEAR_SIZE,
2869*4882a593Smuzhiyun 	.swid_config			= {
2870*4882a593Smuzhiyun 		{
2871*4882a593Smuzhiyun 			.used_type	= 1,
2872*4882a593Smuzhiyun 			.type		= MLXSW_PORT_SWID_TYPE_ETH,
2873*4882a593Smuzhiyun 		}
2874*4882a593Smuzhiyun 	},
2875*4882a593Smuzhiyun };
2876*4882a593Smuzhiyun 
2877*4882a593Smuzhiyun static const struct mlxsw_config_profile mlxsw_sp2_config_profile = {
2878*4882a593Smuzhiyun 	.used_max_mid			= 1,
2879*4882a593Smuzhiyun 	.max_mid			= MLXSW_SP_MID_MAX,
2880*4882a593Smuzhiyun 	.used_flood_tables		= 1,
2881*4882a593Smuzhiyun 	.used_flood_mode		= 1,
2882*4882a593Smuzhiyun 	.flood_mode			= 3,
2883*4882a593Smuzhiyun 	.max_fid_flood_tables		= 3,
2884*4882a593Smuzhiyun 	.fid_flood_table_size		= MLXSW_SP_FID_FLOOD_TABLE_SIZE,
2885*4882a593Smuzhiyun 	.used_max_ib_mc			= 1,
2886*4882a593Smuzhiyun 	.max_ib_mc			= 0,
2887*4882a593Smuzhiyun 	.used_max_pkey			= 1,
2888*4882a593Smuzhiyun 	.max_pkey			= 0,
2889*4882a593Smuzhiyun 	.swid_config			= {
2890*4882a593Smuzhiyun 		{
2891*4882a593Smuzhiyun 			.used_type	= 1,
2892*4882a593Smuzhiyun 			.type		= MLXSW_PORT_SWID_TYPE_ETH,
2893*4882a593Smuzhiyun 		}
2894*4882a593Smuzhiyun 	},
2895*4882a593Smuzhiyun };
2896*4882a593Smuzhiyun 
2897*4882a593Smuzhiyun static void
mlxsw_sp_resource_size_params_prepare(struct mlxsw_core * mlxsw_core,struct devlink_resource_size_params * kvd_size_params,struct devlink_resource_size_params * linear_size_params,struct devlink_resource_size_params * hash_double_size_params,struct devlink_resource_size_params * hash_single_size_params)2898*4882a593Smuzhiyun mlxsw_sp_resource_size_params_prepare(struct mlxsw_core *mlxsw_core,
2899*4882a593Smuzhiyun 				      struct devlink_resource_size_params *kvd_size_params,
2900*4882a593Smuzhiyun 				      struct devlink_resource_size_params *linear_size_params,
2901*4882a593Smuzhiyun 				      struct devlink_resource_size_params *hash_double_size_params,
2902*4882a593Smuzhiyun 				      struct devlink_resource_size_params *hash_single_size_params)
2903*4882a593Smuzhiyun {
2904*4882a593Smuzhiyun 	u32 single_size_min = MLXSW_CORE_RES_GET(mlxsw_core,
2905*4882a593Smuzhiyun 						 KVD_SINGLE_MIN_SIZE);
2906*4882a593Smuzhiyun 	u32 double_size_min = MLXSW_CORE_RES_GET(mlxsw_core,
2907*4882a593Smuzhiyun 						 KVD_DOUBLE_MIN_SIZE);
2908*4882a593Smuzhiyun 	u32 kvd_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE);
2909*4882a593Smuzhiyun 	u32 linear_size_min = 0;
2910*4882a593Smuzhiyun 
2911*4882a593Smuzhiyun 	devlink_resource_size_params_init(kvd_size_params, kvd_size, kvd_size,
2912*4882a593Smuzhiyun 					  MLXSW_SP_KVD_GRANULARITY,
2913*4882a593Smuzhiyun 					  DEVLINK_RESOURCE_UNIT_ENTRY);
2914*4882a593Smuzhiyun 	devlink_resource_size_params_init(linear_size_params, linear_size_min,
2915*4882a593Smuzhiyun 					  kvd_size - single_size_min -
2916*4882a593Smuzhiyun 					  double_size_min,
2917*4882a593Smuzhiyun 					  MLXSW_SP_KVD_GRANULARITY,
2918*4882a593Smuzhiyun 					  DEVLINK_RESOURCE_UNIT_ENTRY);
2919*4882a593Smuzhiyun 	devlink_resource_size_params_init(hash_double_size_params,
2920*4882a593Smuzhiyun 					  double_size_min,
2921*4882a593Smuzhiyun 					  kvd_size - single_size_min -
2922*4882a593Smuzhiyun 					  linear_size_min,
2923*4882a593Smuzhiyun 					  MLXSW_SP_KVD_GRANULARITY,
2924*4882a593Smuzhiyun 					  DEVLINK_RESOURCE_UNIT_ENTRY);
2925*4882a593Smuzhiyun 	devlink_resource_size_params_init(hash_single_size_params,
2926*4882a593Smuzhiyun 					  single_size_min,
2927*4882a593Smuzhiyun 					  kvd_size - double_size_min -
2928*4882a593Smuzhiyun 					  linear_size_min,
2929*4882a593Smuzhiyun 					  MLXSW_SP_KVD_GRANULARITY,
2930*4882a593Smuzhiyun 					  DEVLINK_RESOURCE_UNIT_ENTRY);
2931*4882a593Smuzhiyun }
2932*4882a593Smuzhiyun 
mlxsw_sp1_resources_kvd_register(struct mlxsw_core * mlxsw_core)2933*4882a593Smuzhiyun static int mlxsw_sp1_resources_kvd_register(struct mlxsw_core *mlxsw_core)
2934*4882a593Smuzhiyun {
2935*4882a593Smuzhiyun 	struct devlink *devlink = priv_to_devlink(mlxsw_core);
2936*4882a593Smuzhiyun 	struct devlink_resource_size_params hash_single_size_params;
2937*4882a593Smuzhiyun 	struct devlink_resource_size_params hash_double_size_params;
2938*4882a593Smuzhiyun 	struct devlink_resource_size_params linear_size_params;
2939*4882a593Smuzhiyun 	struct devlink_resource_size_params kvd_size_params;
2940*4882a593Smuzhiyun 	u32 kvd_size, single_size, double_size, linear_size;
2941*4882a593Smuzhiyun 	const struct mlxsw_config_profile *profile;
2942*4882a593Smuzhiyun 	int err;
2943*4882a593Smuzhiyun 
2944*4882a593Smuzhiyun 	profile = &mlxsw_sp1_config_profile;
2945*4882a593Smuzhiyun 	if (!MLXSW_CORE_RES_VALID(mlxsw_core, KVD_SIZE))
2946*4882a593Smuzhiyun 		return -EIO;
2947*4882a593Smuzhiyun 
2948*4882a593Smuzhiyun 	mlxsw_sp_resource_size_params_prepare(mlxsw_core, &kvd_size_params,
2949*4882a593Smuzhiyun 					      &linear_size_params,
2950*4882a593Smuzhiyun 					      &hash_double_size_params,
2951*4882a593Smuzhiyun 					      &hash_single_size_params);
2952*4882a593Smuzhiyun 
2953*4882a593Smuzhiyun 	kvd_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE);
2954*4882a593Smuzhiyun 	err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD,
2955*4882a593Smuzhiyun 					kvd_size, MLXSW_SP_RESOURCE_KVD,
2956*4882a593Smuzhiyun 					DEVLINK_RESOURCE_ID_PARENT_TOP,
2957*4882a593Smuzhiyun 					&kvd_size_params);
2958*4882a593Smuzhiyun 	if (err)
2959*4882a593Smuzhiyun 		return err;
2960*4882a593Smuzhiyun 
2961*4882a593Smuzhiyun 	linear_size = profile->kvd_linear_size;
2962*4882a593Smuzhiyun 	err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR,
2963*4882a593Smuzhiyun 					linear_size,
2964*4882a593Smuzhiyun 					MLXSW_SP_RESOURCE_KVD_LINEAR,
2965*4882a593Smuzhiyun 					MLXSW_SP_RESOURCE_KVD,
2966*4882a593Smuzhiyun 					&linear_size_params);
2967*4882a593Smuzhiyun 	if (err)
2968*4882a593Smuzhiyun 		return err;
2969*4882a593Smuzhiyun 
2970*4882a593Smuzhiyun 	err = mlxsw_sp1_kvdl_resources_register(mlxsw_core);
2971*4882a593Smuzhiyun 	if  (err)
2972*4882a593Smuzhiyun 		return err;
2973*4882a593Smuzhiyun 
2974*4882a593Smuzhiyun 	double_size = kvd_size - linear_size;
2975*4882a593Smuzhiyun 	double_size *= profile->kvd_hash_double_parts;
2976*4882a593Smuzhiyun 	double_size /= profile->kvd_hash_double_parts +
2977*4882a593Smuzhiyun 		       profile->kvd_hash_single_parts;
2978*4882a593Smuzhiyun 	double_size = rounddown(double_size, MLXSW_SP_KVD_GRANULARITY);
2979*4882a593Smuzhiyun 	err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_HASH_DOUBLE,
2980*4882a593Smuzhiyun 					double_size,
2981*4882a593Smuzhiyun 					MLXSW_SP_RESOURCE_KVD_HASH_DOUBLE,
2982*4882a593Smuzhiyun 					MLXSW_SP_RESOURCE_KVD,
2983*4882a593Smuzhiyun 					&hash_double_size_params);
2984*4882a593Smuzhiyun 	if (err)
2985*4882a593Smuzhiyun 		return err;
2986*4882a593Smuzhiyun 
2987*4882a593Smuzhiyun 	single_size = kvd_size - double_size - linear_size;
2988*4882a593Smuzhiyun 	err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_HASH_SINGLE,
2989*4882a593Smuzhiyun 					single_size,
2990*4882a593Smuzhiyun 					MLXSW_SP_RESOURCE_KVD_HASH_SINGLE,
2991*4882a593Smuzhiyun 					MLXSW_SP_RESOURCE_KVD,
2992*4882a593Smuzhiyun 					&hash_single_size_params);
2993*4882a593Smuzhiyun 	if (err)
2994*4882a593Smuzhiyun 		return err;
2995*4882a593Smuzhiyun 
2996*4882a593Smuzhiyun 	return 0;
2997*4882a593Smuzhiyun }
2998*4882a593Smuzhiyun 
mlxsw_sp2_resources_kvd_register(struct mlxsw_core * mlxsw_core)2999*4882a593Smuzhiyun static int mlxsw_sp2_resources_kvd_register(struct mlxsw_core *mlxsw_core)
3000*4882a593Smuzhiyun {
3001*4882a593Smuzhiyun 	struct devlink *devlink = priv_to_devlink(mlxsw_core);
3002*4882a593Smuzhiyun 	struct devlink_resource_size_params kvd_size_params;
3003*4882a593Smuzhiyun 	u32 kvd_size;
3004*4882a593Smuzhiyun 
3005*4882a593Smuzhiyun 	if (!MLXSW_CORE_RES_VALID(mlxsw_core, KVD_SIZE))
3006*4882a593Smuzhiyun 		return -EIO;
3007*4882a593Smuzhiyun 
3008*4882a593Smuzhiyun 	kvd_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE);
3009*4882a593Smuzhiyun 	devlink_resource_size_params_init(&kvd_size_params, kvd_size, kvd_size,
3010*4882a593Smuzhiyun 					  MLXSW_SP_KVD_GRANULARITY,
3011*4882a593Smuzhiyun 					  DEVLINK_RESOURCE_UNIT_ENTRY);
3012*4882a593Smuzhiyun 
3013*4882a593Smuzhiyun 	return devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD,
3014*4882a593Smuzhiyun 					 kvd_size, MLXSW_SP_RESOURCE_KVD,
3015*4882a593Smuzhiyun 					 DEVLINK_RESOURCE_ID_PARENT_TOP,
3016*4882a593Smuzhiyun 					 &kvd_size_params);
3017*4882a593Smuzhiyun }
3018*4882a593Smuzhiyun 
mlxsw_sp_resources_span_register(struct mlxsw_core * mlxsw_core)3019*4882a593Smuzhiyun static int mlxsw_sp_resources_span_register(struct mlxsw_core *mlxsw_core)
3020*4882a593Smuzhiyun {
3021*4882a593Smuzhiyun 	struct devlink *devlink = priv_to_devlink(mlxsw_core);
3022*4882a593Smuzhiyun 	struct devlink_resource_size_params span_size_params;
3023*4882a593Smuzhiyun 	u32 max_span;
3024*4882a593Smuzhiyun 
3025*4882a593Smuzhiyun 	if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_SPAN))
3026*4882a593Smuzhiyun 		return -EIO;
3027*4882a593Smuzhiyun 
3028*4882a593Smuzhiyun 	max_span = MLXSW_CORE_RES_GET(mlxsw_core, MAX_SPAN);
3029*4882a593Smuzhiyun 	devlink_resource_size_params_init(&span_size_params, max_span, max_span,
3030*4882a593Smuzhiyun 					  1, DEVLINK_RESOURCE_UNIT_ENTRY);
3031*4882a593Smuzhiyun 
3032*4882a593Smuzhiyun 	return devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_SPAN,
3033*4882a593Smuzhiyun 					 max_span, MLXSW_SP_RESOURCE_SPAN,
3034*4882a593Smuzhiyun 					 DEVLINK_RESOURCE_ID_PARENT_TOP,
3035*4882a593Smuzhiyun 					 &span_size_params);
3036*4882a593Smuzhiyun }
3037*4882a593Smuzhiyun 
mlxsw_sp1_resources_register(struct mlxsw_core * mlxsw_core)3038*4882a593Smuzhiyun static int mlxsw_sp1_resources_register(struct mlxsw_core *mlxsw_core)
3039*4882a593Smuzhiyun {
3040*4882a593Smuzhiyun 	int err;
3041*4882a593Smuzhiyun 
3042*4882a593Smuzhiyun 	err = mlxsw_sp1_resources_kvd_register(mlxsw_core);
3043*4882a593Smuzhiyun 	if (err)
3044*4882a593Smuzhiyun 		return err;
3045*4882a593Smuzhiyun 
3046*4882a593Smuzhiyun 	err = mlxsw_sp_resources_span_register(mlxsw_core);
3047*4882a593Smuzhiyun 	if (err)
3048*4882a593Smuzhiyun 		goto err_resources_span_register;
3049*4882a593Smuzhiyun 
3050*4882a593Smuzhiyun 	err = mlxsw_sp_counter_resources_register(mlxsw_core);
3051*4882a593Smuzhiyun 	if (err)
3052*4882a593Smuzhiyun 		goto err_resources_counter_register;
3053*4882a593Smuzhiyun 
3054*4882a593Smuzhiyun 	err = mlxsw_sp_policer_resources_register(mlxsw_core);
3055*4882a593Smuzhiyun 	if (err)
3056*4882a593Smuzhiyun 		goto err_resources_counter_register;
3057*4882a593Smuzhiyun 
3058*4882a593Smuzhiyun 	return 0;
3059*4882a593Smuzhiyun 
3060*4882a593Smuzhiyun err_resources_counter_register:
3061*4882a593Smuzhiyun err_resources_span_register:
3062*4882a593Smuzhiyun 	devlink_resources_unregister(priv_to_devlink(mlxsw_core), NULL);
3063*4882a593Smuzhiyun 	return err;
3064*4882a593Smuzhiyun }
3065*4882a593Smuzhiyun 
mlxsw_sp2_resources_register(struct mlxsw_core * mlxsw_core)3066*4882a593Smuzhiyun static int mlxsw_sp2_resources_register(struct mlxsw_core *mlxsw_core)
3067*4882a593Smuzhiyun {
3068*4882a593Smuzhiyun 	int err;
3069*4882a593Smuzhiyun 
3070*4882a593Smuzhiyun 	err = mlxsw_sp2_resources_kvd_register(mlxsw_core);
3071*4882a593Smuzhiyun 	if (err)
3072*4882a593Smuzhiyun 		return err;
3073*4882a593Smuzhiyun 
3074*4882a593Smuzhiyun 	err = mlxsw_sp_resources_span_register(mlxsw_core);
3075*4882a593Smuzhiyun 	if (err)
3076*4882a593Smuzhiyun 		goto err_resources_span_register;
3077*4882a593Smuzhiyun 
3078*4882a593Smuzhiyun 	err = mlxsw_sp_counter_resources_register(mlxsw_core);
3079*4882a593Smuzhiyun 	if (err)
3080*4882a593Smuzhiyun 		goto err_resources_counter_register;
3081*4882a593Smuzhiyun 
3082*4882a593Smuzhiyun 	err = mlxsw_sp_policer_resources_register(mlxsw_core);
3083*4882a593Smuzhiyun 	if (err)
3084*4882a593Smuzhiyun 		goto err_resources_counter_register;
3085*4882a593Smuzhiyun 
3086*4882a593Smuzhiyun 	return 0;
3087*4882a593Smuzhiyun 
3088*4882a593Smuzhiyun err_resources_counter_register:
3089*4882a593Smuzhiyun err_resources_span_register:
3090*4882a593Smuzhiyun 	devlink_resources_unregister(priv_to_devlink(mlxsw_core), NULL);
3091*4882a593Smuzhiyun 	return err;
3092*4882a593Smuzhiyun }
3093*4882a593Smuzhiyun 
mlxsw_sp_kvd_sizes_get(struct mlxsw_core * mlxsw_core,const struct mlxsw_config_profile * profile,u64 * p_single_size,u64 * p_double_size,u64 * p_linear_size)3094*4882a593Smuzhiyun static int mlxsw_sp_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
3095*4882a593Smuzhiyun 				  const struct mlxsw_config_profile *profile,
3096*4882a593Smuzhiyun 				  u64 *p_single_size, u64 *p_double_size,
3097*4882a593Smuzhiyun 				  u64 *p_linear_size)
3098*4882a593Smuzhiyun {
3099*4882a593Smuzhiyun 	struct devlink *devlink = priv_to_devlink(mlxsw_core);
3100*4882a593Smuzhiyun 	u32 double_size;
3101*4882a593Smuzhiyun 	int err;
3102*4882a593Smuzhiyun 
3103*4882a593Smuzhiyun 	if (!MLXSW_CORE_RES_VALID(mlxsw_core, KVD_SINGLE_MIN_SIZE) ||
3104*4882a593Smuzhiyun 	    !MLXSW_CORE_RES_VALID(mlxsw_core, KVD_DOUBLE_MIN_SIZE))
3105*4882a593Smuzhiyun 		return -EIO;
3106*4882a593Smuzhiyun 
3107*4882a593Smuzhiyun 	/* The hash part is what left of the kvd without the
3108*4882a593Smuzhiyun 	 * linear part. It is split to the single size and
3109*4882a593Smuzhiyun 	 * double size by the parts ratio from the profile.
3110*4882a593Smuzhiyun 	 * Both sizes must be a multiplications of the
3111*4882a593Smuzhiyun 	 * granularity from the profile. In case the user
3112*4882a593Smuzhiyun 	 * provided the sizes they are obtained via devlink.
3113*4882a593Smuzhiyun 	 */
3114*4882a593Smuzhiyun 	err = devlink_resource_size_get(devlink,
3115*4882a593Smuzhiyun 					MLXSW_SP_RESOURCE_KVD_LINEAR,
3116*4882a593Smuzhiyun 					p_linear_size);
3117*4882a593Smuzhiyun 	if (err)
3118*4882a593Smuzhiyun 		*p_linear_size = profile->kvd_linear_size;
3119*4882a593Smuzhiyun 
3120*4882a593Smuzhiyun 	err = devlink_resource_size_get(devlink,
3121*4882a593Smuzhiyun 					MLXSW_SP_RESOURCE_KVD_HASH_DOUBLE,
3122*4882a593Smuzhiyun 					p_double_size);
3123*4882a593Smuzhiyun 	if (err) {
3124*4882a593Smuzhiyun 		double_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) -
3125*4882a593Smuzhiyun 			      *p_linear_size;
3126*4882a593Smuzhiyun 		double_size *= profile->kvd_hash_double_parts;
3127*4882a593Smuzhiyun 		double_size /= profile->kvd_hash_double_parts +
3128*4882a593Smuzhiyun 			       profile->kvd_hash_single_parts;
3129*4882a593Smuzhiyun 		*p_double_size = rounddown(double_size,
3130*4882a593Smuzhiyun 					   MLXSW_SP_KVD_GRANULARITY);
3131*4882a593Smuzhiyun 	}
3132*4882a593Smuzhiyun 
3133*4882a593Smuzhiyun 	err = devlink_resource_size_get(devlink,
3134*4882a593Smuzhiyun 					MLXSW_SP_RESOURCE_KVD_HASH_SINGLE,
3135*4882a593Smuzhiyun 					p_single_size);
3136*4882a593Smuzhiyun 	if (err)
3137*4882a593Smuzhiyun 		*p_single_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) -
3138*4882a593Smuzhiyun 				 *p_double_size - *p_linear_size;
3139*4882a593Smuzhiyun 
3140*4882a593Smuzhiyun 	/* Check results are legal. */
3141*4882a593Smuzhiyun 	if (*p_single_size < MLXSW_CORE_RES_GET(mlxsw_core, KVD_SINGLE_MIN_SIZE) ||
3142*4882a593Smuzhiyun 	    *p_double_size < MLXSW_CORE_RES_GET(mlxsw_core, KVD_DOUBLE_MIN_SIZE) ||
3143*4882a593Smuzhiyun 	    MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) < *p_linear_size)
3144*4882a593Smuzhiyun 		return -EIO;
3145*4882a593Smuzhiyun 
3146*4882a593Smuzhiyun 	return 0;
3147*4882a593Smuzhiyun }
3148*4882a593Smuzhiyun 
3149*4882a593Smuzhiyun static int
mlxsw_sp_params_acl_region_rehash_intrvl_get(struct devlink * devlink,u32 id,struct devlink_param_gset_ctx * ctx)3150*4882a593Smuzhiyun mlxsw_sp_params_acl_region_rehash_intrvl_get(struct devlink *devlink, u32 id,
3151*4882a593Smuzhiyun 					     struct devlink_param_gset_ctx *ctx)
3152*4882a593Smuzhiyun {
3153*4882a593Smuzhiyun 	struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
3154*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
3155*4882a593Smuzhiyun 
3156*4882a593Smuzhiyun 	ctx->val.vu32 = mlxsw_sp_acl_region_rehash_intrvl_get(mlxsw_sp);
3157*4882a593Smuzhiyun 	return 0;
3158*4882a593Smuzhiyun }
3159*4882a593Smuzhiyun 
3160*4882a593Smuzhiyun static int
mlxsw_sp_params_acl_region_rehash_intrvl_set(struct devlink * devlink,u32 id,struct devlink_param_gset_ctx * ctx)3161*4882a593Smuzhiyun mlxsw_sp_params_acl_region_rehash_intrvl_set(struct devlink *devlink, u32 id,
3162*4882a593Smuzhiyun 					     struct devlink_param_gset_ctx *ctx)
3163*4882a593Smuzhiyun {
3164*4882a593Smuzhiyun 	struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
3165*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
3166*4882a593Smuzhiyun 
3167*4882a593Smuzhiyun 	return mlxsw_sp_acl_region_rehash_intrvl_set(mlxsw_sp, ctx->val.vu32);
3168*4882a593Smuzhiyun }
3169*4882a593Smuzhiyun 
3170*4882a593Smuzhiyun static const struct devlink_param mlxsw_sp2_devlink_params[] = {
3171*4882a593Smuzhiyun 	DEVLINK_PARAM_DRIVER(MLXSW_DEVLINK_PARAM_ID_ACL_REGION_REHASH_INTERVAL,
3172*4882a593Smuzhiyun 			     "acl_region_rehash_interval",
3173*4882a593Smuzhiyun 			     DEVLINK_PARAM_TYPE_U32,
3174*4882a593Smuzhiyun 			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
3175*4882a593Smuzhiyun 			     mlxsw_sp_params_acl_region_rehash_intrvl_get,
3176*4882a593Smuzhiyun 			     mlxsw_sp_params_acl_region_rehash_intrvl_set,
3177*4882a593Smuzhiyun 			     NULL),
3178*4882a593Smuzhiyun };
3179*4882a593Smuzhiyun 
mlxsw_sp2_params_register(struct mlxsw_core * mlxsw_core)3180*4882a593Smuzhiyun static int mlxsw_sp2_params_register(struct mlxsw_core *mlxsw_core)
3181*4882a593Smuzhiyun {
3182*4882a593Smuzhiyun 	struct devlink *devlink = priv_to_devlink(mlxsw_core);
3183*4882a593Smuzhiyun 	union devlink_param_value value;
3184*4882a593Smuzhiyun 	int err;
3185*4882a593Smuzhiyun 
3186*4882a593Smuzhiyun 	err = devlink_params_register(devlink, mlxsw_sp2_devlink_params,
3187*4882a593Smuzhiyun 				      ARRAY_SIZE(mlxsw_sp2_devlink_params));
3188*4882a593Smuzhiyun 	if (err)
3189*4882a593Smuzhiyun 		return err;
3190*4882a593Smuzhiyun 
3191*4882a593Smuzhiyun 	value.vu32 = 0;
3192*4882a593Smuzhiyun 	devlink_param_driverinit_value_set(devlink,
3193*4882a593Smuzhiyun 					   MLXSW_DEVLINK_PARAM_ID_ACL_REGION_REHASH_INTERVAL,
3194*4882a593Smuzhiyun 					   value);
3195*4882a593Smuzhiyun 	return 0;
3196*4882a593Smuzhiyun }
3197*4882a593Smuzhiyun 
mlxsw_sp2_params_unregister(struct mlxsw_core * mlxsw_core)3198*4882a593Smuzhiyun static void mlxsw_sp2_params_unregister(struct mlxsw_core *mlxsw_core)
3199*4882a593Smuzhiyun {
3200*4882a593Smuzhiyun 	devlink_params_unregister(priv_to_devlink(mlxsw_core),
3201*4882a593Smuzhiyun 				  mlxsw_sp2_devlink_params,
3202*4882a593Smuzhiyun 				  ARRAY_SIZE(mlxsw_sp2_devlink_params));
3203*4882a593Smuzhiyun }
3204*4882a593Smuzhiyun 
mlxsw_sp_ptp_transmitted(struct mlxsw_core * mlxsw_core,struct sk_buff * skb,u8 local_port)3205*4882a593Smuzhiyun static void mlxsw_sp_ptp_transmitted(struct mlxsw_core *mlxsw_core,
3206*4882a593Smuzhiyun 				     struct sk_buff *skb, u8 local_port)
3207*4882a593Smuzhiyun {
3208*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
3209*4882a593Smuzhiyun 
3210*4882a593Smuzhiyun 	skb_pull(skb, MLXSW_TXHDR_LEN);
3211*4882a593Smuzhiyun 	mlxsw_sp->ptp_ops->transmitted(mlxsw_sp, skb, local_port);
3212*4882a593Smuzhiyun }
3213*4882a593Smuzhiyun 
3214*4882a593Smuzhiyun static struct mlxsw_driver mlxsw_sp1_driver = {
3215*4882a593Smuzhiyun 	.kind				= mlxsw_sp1_driver_name,
3216*4882a593Smuzhiyun 	.priv_size			= sizeof(struct mlxsw_sp),
3217*4882a593Smuzhiyun 	.fw_req_rev			= &mlxsw_sp1_fw_rev,
3218*4882a593Smuzhiyun 	.fw_filename			= MLXSW_SP1_FW_FILENAME,
3219*4882a593Smuzhiyun 	.init				= mlxsw_sp1_init,
3220*4882a593Smuzhiyun 	.fini				= mlxsw_sp_fini,
3221*4882a593Smuzhiyun 	.basic_trap_groups_set		= mlxsw_sp_basic_trap_groups_set,
3222*4882a593Smuzhiyun 	.port_split			= mlxsw_sp_port_split,
3223*4882a593Smuzhiyun 	.port_unsplit			= mlxsw_sp_port_unsplit,
3224*4882a593Smuzhiyun 	.sb_pool_get			= mlxsw_sp_sb_pool_get,
3225*4882a593Smuzhiyun 	.sb_pool_set			= mlxsw_sp_sb_pool_set,
3226*4882a593Smuzhiyun 	.sb_port_pool_get		= mlxsw_sp_sb_port_pool_get,
3227*4882a593Smuzhiyun 	.sb_port_pool_set		= mlxsw_sp_sb_port_pool_set,
3228*4882a593Smuzhiyun 	.sb_tc_pool_bind_get		= mlxsw_sp_sb_tc_pool_bind_get,
3229*4882a593Smuzhiyun 	.sb_tc_pool_bind_set		= mlxsw_sp_sb_tc_pool_bind_set,
3230*4882a593Smuzhiyun 	.sb_occ_snapshot		= mlxsw_sp_sb_occ_snapshot,
3231*4882a593Smuzhiyun 	.sb_occ_max_clear		= mlxsw_sp_sb_occ_max_clear,
3232*4882a593Smuzhiyun 	.sb_occ_port_pool_get		= mlxsw_sp_sb_occ_port_pool_get,
3233*4882a593Smuzhiyun 	.sb_occ_tc_port_bind_get	= mlxsw_sp_sb_occ_tc_port_bind_get,
3234*4882a593Smuzhiyun 	.trap_init			= mlxsw_sp_trap_init,
3235*4882a593Smuzhiyun 	.trap_fini			= mlxsw_sp_trap_fini,
3236*4882a593Smuzhiyun 	.trap_action_set		= mlxsw_sp_trap_action_set,
3237*4882a593Smuzhiyun 	.trap_group_init		= mlxsw_sp_trap_group_init,
3238*4882a593Smuzhiyun 	.trap_group_set			= mlxsw_sp_trap_group_set,
3239*4882a593Smuzhiyun 	.trap_policer_init		= mlxsw_sp_trap_policer_init,
3240*4882a593Smuzhiyun 	.trap_policer_fini		= mlxsw_sp_trap_policer_fini,
3241*4882a593Smuzhiyun 	.trap_policer_set		= mlxsw_sp_trap_policer_set,
3242*4882a593Smuzhiyun 	.trap_policer_counter_get	= mlxsw_sp_trap_policer_counter_get,
3243*4882a593Smuzhiyun 	.txhdr_construct		= mlxsw_sp_txhdr_construct,
3244*4882a593Smuzhiyun 	.resources_register		= mlxsw_sp1_resources_register,
3245*4882a593Smuzhiyun 	.kvd_sizes_get			= mlxsw_sp_kvd_sizes_get,
3246*4882a593Smuzhiyun 	.ptp_transmitted		= mlxsw_sp_ptp_transmitted,
3247*4882a593Smuzhiyun 	.txhdr_len			= MLXSW_TXHDR_LEN,
3248*4882a593Smuzhiyun 	.profile			= &mlxsw_sp1_config_profile,
3249*4882a593Smuzhiyun 	.res_query_enabled		= true,
3250*4882a593Smuzhiyun 	.fw_fatal_enabled		= true,
3251*4882a593Smuzhiyun 	.temp_warn_enabled		= true,
3252*4882a593Smuzhiyun };
3253*4882a593Smuzhiyun 
3254*4882a593Smuzhiyun static struct mlxsw_driver mlxsw_sp2_driver = {
3255*4882a593Smuzhiyun 	.kind				= mlxsw_sp2_driver_name,
3256*4882a593Smuzhiyun 	.priv_size			= sizeof(struct mlxsw_sp),
3257*4882a593Smuzhiyun 	.fw_req_rev			= &mlxsw_sp2_fw_rev,
3258*4882a593Smuzhiyun 	.fw_filename			= MLXSW_SP2_FW_FILENAME,
3259*4882a593Smuzhiyun 	.init				= mlxsw_sp2_init,
3260*4882a593Smuzhiyun 	.fini				= mlxsw_sp_fini,
3261*4882a593Smuzhiyun 	.basic_trap_groups_set		= mlxsw_sp_basic_trap_groups_set,
3262*4882a593Smuzhiyun 	.port_split			= mlxsw_sp_port_split,
3263*4882a593Smuzhiyun 	.port_unsplit			= mlxsw_sp_port_unsplit,
3264*4882a593Smuzhiyun 	.sb_pool_get			= mlxsw_sp_sb_pool_get,
3265*4882a593Smuzhiyun 	.sb_pool_set			= mlxsw_sp_sb_pool_set,
3266*4882a593Smuzhiyun 	.sb_port_pool_get		= mlxsw_sp_sb_port_pool_get,
3267*4882a593Smuzhiyun 	.sb_port_pool_set		= mlxsw_sp_sb_port_pool_set,
3268*4882a593Smuzhiyun 	.sb_tc_pool_bind_get		= mlxsw_sp_sb_tc_pool_bind_get,
3269*4882a593Smuzhiyun 	.sb_tc_pool_bind_set		= mlxsw_sp_sb_tc_pool_bind_set,
3270*4882a593Smuzhiyun 	.sb_occ_snapshot		= mlxsw_sp_sb_occ_snapshot,
3271*4882a593Smuzhiyun 	.sb_occ_max_clear		= mlxsw_sp_sb_occ_max_clear,
3272*4882a593Smuzhiyun 	.sb_occ_port_pool_get		= mlxsw_sp_sb_occ_port_pool_get,
3273*4882a593Smuzhiyun 	.sb_occ_tc_port_bind_get	= mlxsw_sp_sb_occ_tc_port_bind_get,
3274*4882a593Smuzhiyun 	.trap_init			= mlxsw_sp_trap_init,
3275*4882a593Smuzhiyun 	.trap_fini			= mlxsw_sp_trap_fini,
3276*4882a593Smuzhiyun 	.trap_action_set		= mlxsw_sp_trap_action_set,
3277*4882a593Smuzhiyun 	.trap_group_init		= mlxsw_sp_trap_group_init,
3278*4882a593Smuzhiyun 	.trap_group_set			= mlxsw_sp_trap_group_set,
3279*4882a593Smuzhiyun 	.trap_policer_init		= mlxsw_sp_trap_policer_init,
3280*4882a593Smuzhiyun 	.trap_policer_fini		= mlxsw_sp_trap_policer_fini,
3281*4882a593Smuzhiyun 	.trap_policer_set		= mlxsw_sp_trap_policer_set,
3282*4882a593Smuzhiyun 	.trap_policer_counter_get	= mlxsw_sp_trap_policer_counter_get,
3283*4882a593Smuzhiyun 	.txhdr_construct		= mlxsw_sp_txhdr_construct,
3284*4882a593Smuzhiyun 	.resources_register		= mlxsw_sp2_resources_register,
3285*4882a593Smuzhiyun 	.params_register		= mlxsw_sp2_params_register,
3286*4882a593Smuzhiyun 	.params_unregister		= mlxsw_sp2_params_unregister,
3287*4882a593Smuzhiyun 	.ptp_transmitted		= mlxsw_sp_ptp_transmitted,
3288*4882a593Smuzhiyun 	.txhdr_len			= MLXSW_TXHDR_LEN,
3289*4882a593Smuzhiyun 	.profile			= &mlxsw_sp2_config_profile,
3290*4882a593Smuzhiyun 	.res_query_enabled		= true,
3291*4882a593Smuzhiyun 	.fw_fatal_enabled		= true,
3292*4882a593Smuzhiyun 	.temp_warn_enabled		= true,
3293*4882a593Smuzhiyun };
3294*4882a593Smuzhiyun 
3295*4882a593Smuzhiyun static struct mlxsw_driver mlxsw_sp3_driver = {
3296*4882a593Smuzhiyun 	.kind				= mlxsw_sp3_driver_name,
3297*4882a593Smuzhiyun 	.priv_size			= sizeof(struct mlxsw_sp),
3298*4882a593Smuzhiyun 	.fw_req_rev			= &mlxsw_sp3_fw_rev,
3299*4882a593Smuzhiyun 	.fw_filename			= MLXSW_SP3_FW_FILENAME,
3300*4882a593Smuzhiyun 	.init				= mlxsw_sp3_init,
3301*4882a593Smuzhiyun 	.fini				= mlxsw_sp_fini,
3302*4882a593Smuzhiyun 	.basic_trap_groups_set		= mlxsw_sp_basic_trap_groups_set,
3303*4882a593Smuzhiyun 	.port_split			= mlxsw_sp_port_split,
3304*4882a593Smuzhiyun 	.port_unsplit			= mlxsw_sp_port_unsplit,
3305*4882a593Smuzhiyun 	.sb_pool_get			= mlxsw_sp_sb_pool_get,
3306*4882a593Smuzhiyun 	.sb_pool_set			= mlxsw_sp_sb_pool_set,
3307*4882a593Smuzhiyun 	.sb_port_pool_get		= mlxsw_sp_sb_port_pool_get,
3308*4882a593Smuzhiyun 	.sb_port_pool_set		= mlxsw_sp_sb_port_pool_set,
3309*4882a593Smuzhiyun 	.sb_tc_pool_bind_get		= mlxsw_sp_sb_tc_pool_bind_get,
3310*4882a593Smuzhiyun 	.sb_tc_pool_bind_set		= mlxsw_sp_sb_tc_pool_bind_set,
3311*4882a593Smuzhiyun 	.sb_occ_snapshot		= mlxsw_sp_sb_occ_snapshot,
3312*4882a593Smuzhiyun 	.sb_occ_max_clear		= mlxsw_sp_sb_occ_max_clear,
3313*4882a593Smuzhiyun 	.sb_occ_port_pool_get		= mlxsw_sp_sb_occ_port_pool_get,
3314*4882a593Smuzhiyun 	.sb_occ_tc_port_bind_get	= mlxsw_sp_sb_occ_tc_port_bind_get,
3315*4882a593Smuzhiyun 	.trap_init			= mlxsw_sp_trap_init,
3316*4882a593Smuzhiyun 	.trap_fini			= mlxsw_sp_trap_fini,
3317*4882a593Smuzhiyun 	.trap_action_set		= mlxsw_sp_trap_action_set,
3318*4882a593Smuzhiyun 	.trap_group_init		= mlxsw_sp_trap_group_init,
3319*4882a593Smuzhiyun 	.trap_group_set			= mlxsw_sp_trap_group_set,
3320*4882a593Smuzhiyun 	.trap_policer_init		= mlxsw_sp_trap_policer_init,
3321*4882a593Smuzhiyun 	.trap_policer_fini		= mlxsw_sp_trap_policer_fini,
3322*4882a593Smuzhiyun 	.trap_policer_set		= mlxsw_sp_trap_policer_set,
3323*4882a593Smuzhiyun 	.trap_policer_counter_get	= mlxsw_sp_trap_policer_counter_get,
3324*4882a593Smuzhiyun 	.txhdr_construct		= mlxsw_sp_txhdr_construct,
3325*4882a593Smuzhiyun 	.resources_register		= mlxsw_sp2_resources_register,
3326*4882a593Smuzhiyun 	.params_register		= mlxsw_sp2_params_register,
3327*4882a593Smuzhiyun 	.params_unregister		= mlxsw_sp2_params_unregister,
3328*4882a593Smuzhiyun 	.ptp_transmitted		= mlxsw_sp_ptp_transmitted,
3329*4882a593Smuzhiyun 	.txhdr_len			= MLXSW_TXHDR_LEN,
3330*4882a593Smuzhiyun 	.profile			= &mlxsw_sp2_config_profile,
3331*4882a593Smuzhiyun 	.res_query_enabled		= true,
3332*4882a593Smuzhiyun 	.fw_fatal_enabled		= true,
3333*4882a593Smuzhiyun 	.temp_warn_enabled		= true,
3334*4882a593Smuzhiyun };
3335*4882a593Smuzhiyun 
mlxsw_sp_port_dev_check(const struct net_device * dev)3336*4882a593Smuzhiyun bool mlxsw_sp_port_dev_check(const struct net_device *dev)
3337*4882a593Smuzhiyun {
3338*4882a593Smuzhiyun 	return dev->netdev_ops == &mlxsw_sp_port_netdev_ops;
3339*4882a593Smuzhiyun }
3340*4882a593Smuzhiyun 
mlxsw_sp_lower_dev_walk(struct net_device * lower_dev,struct netdev_nested_priv * priv)3341*4882a593Smuzhiyun static int mlxsw_sp_lower_dev_walk(struct net_device *lower_dev,
3342*4882a593Smuzhiyun 				   struct netdev_nested_priv *priv)
3343*4882a593Smuzhiyun {
3344*4882a593Smuzhiyun 	int ret = 0;
3345*4882a593Smuzhiyun 
3346*4882a593Smuzhiyun 	if (mlxsw_sp_port_dev_check(lower_dev)) {
3347*4882a593Smuzhiyun 		priv->data = (void *)netdev_priv(lower_dev);
3348*4882a593Smuzhiyun 		ret = 1;
3349*4882a593Smuzhiyun 	}
3350*4882a593Smuzhiyun 
3351*4882a593Smuzhiyun 	return ret;
3352*4882a593Smuzhiyun }
3353*4882a593Smuzhiyun 
mlxsw_sp_port_dev_lower_find(struct net_device * dev)3354*4882a593Smuzhiyun struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find(struct net_device *dev)
3355*4882a593Smuzhiyun {
3356*4882a593Smuzhiyun 	struct netdev_nested_priv priv = {
3357*4882a593Smuzhiyun 		.data = NULL,
3358*4882a593Smuzhiyun 	};
3359*4882a593Smuzhiyun 
3360*4882a593Smuzhiyun 	if (mlxsw_sp_port_dev_check(dev))
3361*4882a593Smuzhiyun 		return netdev_priv(dev);
3362*4882a593Smuzhiyun 
3363*4882a593Smuzhiyun 	netdev_walk_all_lower_dev(dev, mlxsw_sp_lower_dev_walk, &priv);
3364*4882a593Smuzhiyun 
3365*4882a593Smuzhiyun 	return (struct mlxsw_sp_port *)priv.data;
3366*4882a593Smuzhiyun }
3367*4882a593Smuzhiyun 
mlxsw_sp_lower_get(struct net_device * dev)3368*4882a593Smuzhiyun struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev)
3369*4882a593Smuzhiyun {
3370*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port;
3371*4882a593Smuzhiyun 
3372*4882a593Smuzhiyun 	mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(dev);
3373*4882a593Smuzhiyun 	return mlxsw_sp_port ? mlxsw_sp_port->mlxsw_sp : NULL;
3374*4882a593Smuzhiyun }
3375*4882a593Smuzhiyun 
mlxsw_sp_port_dev_lower_find_rcu(struct net_device * dev)3376*4882a593Smuzhiyun struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find_rcu(struct net_device *dev)
3377*4882a593Smuzhiyun {
3378*4882a593Smuzhiyun 	struct netdev_nested_priv priv = {
3379*4882a593Smuzhiyun 		.data = NULL,
3380*4882a593Smuzhiyun 	};
3381*4882a593Smuzhiyun 
3382*4882a593Smuzhiyun 	if (mlxsw_sp_port_dev_check(dev))
3383*4882a593Smuzhiyun 		return netdev_priv(dev);
3384*4882a593Smuzhiyun 
3385*4882a593Smuzhiyun 	netdev_walk_all_lower_dev_rcu(dev, mlxsw_sp_lower_dev_walk,
3386*4882a593Smuzhiyun 				      &priv);
3387*4882a593Smuzhiyun 
3388*4882a593Smuzhiyun 	return (struct mlxsw_sp_port *)priv.data;
3389*4882a593Smuzhiyun }
3390*4882a593Smuzhiyun 
mlxsw_sp_port_lower_dev_hold(struct net_device * dev)3391*4882a593Smuzhiyun struct mlxsw_sp_port *mlxsw_sp_port_lower_dev_hold(struct net_device *dev)
3392*4882a593Smuzhiyun {
3393*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port;
3394*4882a593Smuzhiyun 
3395*4882a593Smuzhiyun 	rcu_read_lock();
3396*4882a593Smuzhiyun 	mlxsw_sp_port = mlxsw_sp_port_dev_lower_find_rcu(dev);
3397*4882a593Smuzhiyun 	if (mlxsw_sp_port)
3398*4882a593Smuzhiyun 		dev_hold(mlxsw_sp_port->dev);
3399*4882a593Smuzhiyun 	rcu_read_unlock();
3400*4882a593Smuzhiyun 	return mlxsw_sp_port;
3401*4882a593Smuzhiyun }
3402*4882a593Smuzhiyun 
mlxsw_sp_port_dev_put(struct mlxsw_sp_port * mlxsw_sp_port)3403*4882a593Smuzhiyun void mlxsw_sp_port_dev_put(struct mlxsw_sp_port *mlxsw_sp_port)
3404*4882a593Smuzhiyun {
3405*4882a593Smuzhiyun 	dev_put(mlxsw_sp_port->dev);
3406*4882a593Smuzhiyun }
3407*4882a593Smuzhiyun 
3408*4882a593Smuzhiyun static void
mlxsw_sp_port_lag_uppers_cleanup(struct mlxsw_sp_port * mlxsw_sp_port,struct net_device * lag_dev)3409*4882a593Smuzhiyun mlxsw_sp_port_lag_uppers_cleanup(struct mlxsw_sp_port *mlxsw_sp_port,
3410*4882a593Smuzhiyun 				 struct net_device *lag_dev)
3411*4882a593Smuzhiyun {
3412*4882a593Smuzhiyun 	struct net_device *br_dev = netdev_master_upper_dev_get(lag_dev);
3413*4882a593Smuzhiyun 	struct net_device *upper_dev;
3414*4882a593Smuzhiyun 	struct list_head *iter;
3415*4882a593Smuzhiyun 
3416*4882a593Smuzhiyun 	if (netif_is_bridge_port(lag_dev))
3417*4882a593Smuzhiyun 		mlxsw_sp_port_bridge_leave(mlxsw_sp_port, lag_dev, br_dev);
3418*4882a593Smuzhiyun 
3419*4882a593Smuzhiyun 	netdev_for_each_upper_dev_rcu(lag_dev, upper_dev, iter) {
3420*4882a593Smuzhiyun 		if (!netif_is_bridge_port(upper_dev))
3421*4882a593Smuzhiyun 			continue;
3422*4882a593Smuzhiyun 		br_dev = netdev_master_upper_dev_get(upper_dev);
3423*4882a593Smuzhiyun 		mlxsw_sp_port_bridge_leave(mlxsw_sp_port, upper_dev, br_dev);
3424*4882a593Smuzhiyun 	}
3425*4882a593Smuzhiyun }
3426*4882a593Smuzhiyun 
mlxsw_sp_lag_create(struct mlxsw_sp * mlxsw_sp,u16 lag_id)3427*4882a593Smuzhiyun static int mlxsw_sp_lag_create(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
3428*4882a593Smuzhiyun {
3429*4882a593Smuzhiyun 	char sldr_pl[MLXSW_REG_SLDR_LEN];
3430*4882a593Smuzhiyun 
3431*4882a593Smuzhiyun 	mlxsw_reg_sldr_lag_create_pack(sldr_pl, lag_id);
3432*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
3433*4882a593Smuzhiyun }
3434*4882a593Smuzhiyun 
mlxsw_sp_lag_destroy(struct mlxsw_sp * mlxsw_sp,u16 lag_id)3435*4882a593Smuzhiyun static int mlxsw_sp_lag_destroy(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
3436*4882a593Smuzhiyun {
3437*4882a593Smuzhiyun 	char sldr_pl[MLXSW_REG_SLDR_LEN];
3438*4882a593Smuzhiyun 
3439*4882a593Smuzhiyun 	mlxsw_reg_sldr_lag_destroy_pack(sldr_pl, lag_id);
3440*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
3441*4882a593Smuzhiyun }
3442*4882a593Smuzhiyun 
mlxsw_sp_lag_col_port_add(struct mlxsw_sp_port * mlxsw_sp_port,u16 lag_id,u8 port_index)3443*4882a593Smuzhiyun static int mlxsw_sp_lag_col_port_add(struct mlxsw_sp_port *mlxsw_sp_port,
3444*4882a593Smuzhiyun 				     u16 lag_id, u8 port_index)
3445*4882a593Smuzhiyun {
3446*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
3447*4882a593Smuzhiyun 	char slcor_pl[MLXSW_REG_SLCOR_LEN];
3448*4882a593Smuzhiyun 
3449*4882a593Smuzhiyun 	mlxsw_reg_slcor_port_add_pack(slcor_pl, mlxsw_sp_port->local_port,
3450*4882a593Smuzhiyun 				      lag_id, port_index);
3451*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
3452*4882a593Smuzhiyun }
3453*4882a593Smuzhiyun 
mlxsw_sp_lag_col_port_remove(struct mlxsw_sp_port * mlxsw_sp_port,u16 lag_id)3454*4882a593Smuzhiyun static int mlxsw_sp_lag_col_port_remove(struct mlxsw_sp_port *mlxsw_sp_port,
3455*4882a593Smuzhiyun 					u16 lag_id)
3456*4882a593Smuzhiyun {
3457*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
3458*4882a593Smuzhiyun 	char slcor_pl[MLXSW_REG_SLCOR_LEN];
3459*4882a593Smuzhiyun 
3460*4882a593Smuzhiyun 	mlxsw_reg_slcor_port_remove_pack(slcor_pl, mlxsw_sp_port->local_port,
3461*4882a593Smuzhiyun 					 lag_id);
3462*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
3463*4882a593Smuzhiyun }
3464*4882a593Smuzhiyun 
mlxsw_sp_lag_col_port_enable(struct mlxsw_sp_port * mlxsw_sp_port,u16 lag_id)3465*4882a593Smuzhiyun static int mlxsw_sp_lag_col_port_enable(struct mlxsw_sp_port *mlxsw_sp_port,
3466*4882a593Smuzhiyun 					u16 lag_id)
3467*4882a593Smuzhiyun {
3468*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
3469*4882a593Smuzhiyun 	char slcor_pl[MLXSW_REG_SLCOR_LEN];
3470*4882a593Smuzhiyun 
3471*4882a593Smuzhiyun 	mlxsw_reg_slcor_col_enable_pack(slcor_pl, mlxsw_sp_port->local_port,
3472*4882a593Smuzhiyun 					lag_id);
3473*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
3474*4882a593Smuzhiyun }
3475*4882a593Smuzhiyun 
mlxsw_sp_lag_col_port_disable(struct mlxsw_sp_port * mlxsw_sp_port,u16 lag_id)3476*4882a593Smuzhiyun static int mlxsw_sp_lag_col_port_disable(struct mlxsw_sp_port *mlxsw_sp_port,
3477*4882a593Smuzhiyun 					 u16 lag_id)
3478*4882a593Smuzhiyun {
3479*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
3480*4882a593Smuzhiyun 	char slcor_pl[MLXSW_REG_SLCOR_LEN];
3481*4882a593Smuzhiyun 
3482*4882a593Smuzhiyun 	mlxsw_reg_slcor_col_disable_pack(slcor_pl, mlxsw_sp_port->local_port,
3483*4882a593Smuzhiyun 					 lag_id);
3484*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
3485*4882a593Smuzhiyun }
3486*4882a593Smuzhiyun 
mlxsw_sp_lag_index_get(struct mlxsw_sp * mlxsw_sp,struct net_device * lag_dev,u16 * p_lag_id)3487*4882a593Smuzhiyun static int mlxsw_sp_lag_index_get(struct mlxsw_sp *mlxsw_sp,
3488*4882a593Smuzhiyun 				  struct net_device *lag_dev,
3489*4882a593Smuzhiyun 				  u16 *p_lag_id)
3490*4882a593Smuzhiyun {
3491*4882a593Smuzhiyun 	struct mlxsw_sp_upper *lag;
3492*4882a593Smuzhiyun 	int free_lag_id = -1;
3493*4882a593Smuzhiyun 	u64 max_lag;
3494*4882a593Smuzhiyun 	int i;
3495*4882a593Smuzhiyun 
3496*4882a593Smuzhiyun 	max_lag = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LAG);
3497*4882a593Smuzhiyun 	for (i = 0; i < max_lag; i++) {
3498*4882a593Smuzhiyun 		lag = mlxsw_sp_lag_get(mlxsw_sp, i);
3499*4882a593Smuzhiyun 		if (lag->ref_count) {
3500*4882a593Smuzhiyun 			if (lag->dev == lag_dev) {
3501*4882a593Smuzhiyun 				*p_lag_id = i;
3502*4882a593Smuzhiyun 				return 0;
3503*4882a593Smuzhiyun 			}
3504*4882a593Smuzhiyun 		} else if (free_lag_id < 0) {
3505*4882a593Smuzhiyun 			free_lag_id = i;
3506*4882a593Smuzhiyun 		}
3507*4882a593Smuzhiyun 	}
3508*4882a593Smuzhiyun 	if (free_lag_id < 0)
3509*4882a593Smuzhiyun 		return -EBUSY;
3510*4882a593Smuzhiyun 	*p_lag_id = free_lag_id;
3511*4882a593Smuzhiyun 	return 0;
3512*4882a593Smuzhiyun }
3513*4882a593Smuzhiyun 
3514*4882a593Smuzhiyun static bool
mlxsw_sp_master_lag_check(struct mlxsw_sp * mlxsw_sp,struct net_device * lag_dev,struct netdev_lag_upper_info * lag_upper_info,struct netlink_ext_ack * extack)3515*4882a593Smuzhiyun mlxsw_sp_master_lag_check(struct mlxsw_sp *mlxsw_sp,
3516*4882a593Smuzhiyun 			  struct net_device *lag_dev,
3517*4882a593Smuzhiyun 			  struct netdev_lag_upper_info *lag_upper_info,
3518*4882a593Smuzhiyun 			  struct netlink_ext_ack *extack)
3519*4882a593Smuzhiyun {
3520*4882a593Smuzhiyun 	u16 lag_id;
3521*4882a593Smuzhiyun 
3522*4882a593Smuzhiyun 	if (mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id) != 0) {
3523*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported LAG devices");
3524*4882a593Smuzhiyun 		return false;
3525*4882a593Smuzhiyun 	}
3526*4882a593Smuzhiyun 	if (lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
3527*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type");
3528*4882a593Smuzhiyun 		return false;
3529*4882a593Smuzhiyun 	}
3530*4882a593Smuzhiyun 	return true;
3531*4882a593Smuzhiyun }
3532*4882a593Smuzhiyun 
mlxsw_sp_port_lag_index_get(struct mlxsw_sp * mlxsw_sp,u16 lag_id,u8 * p_port_index)3533*4882a593Smuzhiyun static int mlxsw_sp_port_lag_index_get(struct mlxsw_sp *mlxsw_sp,
3534*4882a593Smuzhiyun 				       u16 lag_id, u8 *p_port_index)
3535*4882a593Smuzhiyun {
3536*4882a593Smuzhiyun 	u64 max_lag_members;
3537*4882a593Smuzhiyun 	int i;
3538*4882a593Smuzhiyun 
3539*4882a593Smuzhiyun 	max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core,
3540*4882a593Smuzhiyun 					     MAX_LAG_MEMBERS);
3541*4882a593Smuzhiyun 	for (i = 0; i < max_lag_members; i++) {
3542*4882a593Smuzhiyun 		if (!mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i)) {
3543*4882a593Smuzhiyun 			*p_port_index = i;
3544*4882a593Smuzhiyun 			return 0;
3545*4882a593Smuzhiyun 		}
3546*4882a593Smuzhiyun 	}
3547*4882a593Smuzhiyun 	return -EBUSY;
3548*4882a593Smuzhiyun }
3549*4882a593Smuzhiyun 
mlxsw_sp_port_lag_join(struct mlxsw_sp_port * mlxsw_sp_port,struct net_device * lag_dev)3550*4882a593Smuzhiyun static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
3551*4882a593Smuzhiyun 				  struct net_device *lag_dev)
3552*4882a593Smuzhiyun {
3553*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
3554*4882a593Smuzhiyun 	struct mlxsw_sp_upper *lag;
3555*4882a593Smuzhiyun 	u16 lag_id;
3556*4882a593Smuzhiyun 	u8 port_index;
3557*4882a593Smuzhiyun 	int err;
3558*4882a593Smuzhiyun 
3559*4882a593Smuzhiyun 	err = mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id);
3560*4882a593Smuzhiyun 	if (err)
3561*4882a593Smuzhiyun 		return err;
3562*4882a593Smuzhiyun 	lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id);
3563*4882a593Smuzhiyun 	if (!lag->ref_count) {
3564*4882a593Smuzhiyun 		err = mlxsw_sp_lag_create(mlxsw_sp, lag_id);
3565*4882a593Smuzhiyun 		if (err)
3566*4882a593Smuzhiyun 			return err;
3567*4882a593Smuzhiyun 		lag->dev = lag_dev;
3568*4882a593Smuzhiyun 	}
3569*4882a593Smuzhiyun 
3570*4882a593Smuzhiyun 	err = mlxsw_sp_port_lag_index_get(mlxsw_sp, lag_id, &port_index);
3571*4882a593Smuzhiyun 	if (err)
3572*4882a593Smuzhiyun 		return err;
3573*4882a593Smuzhiyun 	err = mlxsw_sp_lag_col_port_add(mlxsw_sp_port, lag_id, port_index);
3574*4882a593Smuzhiyun 	if (err)
3575*4882a593Smuzhiyun 		goto err_col_port_add;
3576*4882a593Smuzhiyun 
3577*4882a593Smuzhiyun 	mlxsw_core_lag_mapping_set(mlxsw_sp->core, lag_id, port_index,
3578*4882a593Smuzhiyun 				   mlxsw_sp_port->local_port);
3579*4882a593Smuzhiyun 	mlxsw_sp_port->lag_id = lag_id;
3580*4882a593Smuzhiyun 	mlxsw_sp_port->lagged = 1;
3581*4882a593Smuzhiyun 	lag->ref_count++;
3582*4882a593Smuzhiyun 
3583*4882a593Smuzhiyun 	/* Port is no longer usable as a router interface */
3584*4882a593Smuzhiyun 	if (mlxsw_sp_port->default_vlan->fid)
3585*4882a593Smuzhiyun 		mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port->default_vlan);
3586*4882a593Smuzhiyun 
3587*4882a593Smuzhiyun 	return 0;
3588*4882a593Smuzhiyun 
3589*4882a593Smuzhiyun err_col_port_add:
3590*4882a593Smuzhiyun 	if (!lag->ref_count)
3591*4882a593Smuzhiyun 		mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
3592*4882a593Smuzhiyun 	return err;
3593*4882a593Smuzhiyun }
3594*4882a593Smuzhiyun 
mlxsw_sp_port_lag_leave(struct mlxsw_sp_port * mlxsw_sp_port,struct net_device * lag_dev)3595*4882a593Smuzhiyun static void mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port,
3596*4882a593Smuzhiyun 				    struct net_device *lag_dev)
3597*4882a593Smuzhiyun {
3598*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
3599*4882a593Smuzhiyun 	u16 lag_id = mlxsw_sp_port->lag_id;
3600*4882a593Smuzhiyun 	struct mlxsw_sp_upper *lag;
3601*4882a593Smuzhiyun 
3602*4882a593Smuzhiyun 	if (!mlxsw_sp_port->lagged)
3603*4882a593Smuzhiyun 		return;
3604*4882a593Smuzhiyun 	lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id);
3605*4882a593Smuzhiyun 	WARN_ON(lag->ref_count == 0);
3606*4882a593Smuzhiyun 
3607*4882a593Smuzhiyun 	mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
3608*4882a593Smuzhiyun 
3609*4882a593Smuzhiyun 	/* Any VLANs configured on the port are no longer valid */
3610*4882a593Smuzhiyun 	mlxsw_sp_port_vlan_flush(mlxsw_sp_port, false);
3611*4882a593Smuzhiyun 	mlxsw_sp_port_vlan_cleanup(mlxsw_sp_port->default_vlan);
3612*4882a593Smuzhiyun 	/* Make the LAG and its directly linked uppers leave bridges they
3613*4882a593Smuzhiyun 	 * are memeber in
3614*4882a593Smuzhiyun 	 */
3615*4882a593Smuzhiyun 	mlxsw_sp_port_lag_uppers_cleanup(mlxsw_sp_port, lag_dev);
3616*4882a593Smuzhiyun 
3617*4882a593Smuzhiyun 	if (lag->ref_count == 1)
3618*4882a593Smuzhiyun 		mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
3619*4882a593Smuzhiyun 
3620*4882a593Smuzhiyun 	mlxsw_core_lag_mapping_clear(mlxsw_sp->core, lag_id,
3621*4882a593Smuzhiyun 				     mlxsw_sp_port->local_port);
3622*4882a593Smuzhiyun 	mlxsw_sp_port->lagged = 0;
3623*4882a593Smuzhiyun 	lag->ref_count--;
3624*4882a593Smuzhiyun 
3625*4882a593Smuzhiyun 	/* Make sure untagged frames are allowed to ingress */
3626*4882a593Smuzhiyun 	mlxsw_sp_port_pvid_set(mlxsw_sp_port, MLXSW_SP_DEFAULT_VID);
3627*4882a593Smuzhiyun }
3628*4882a593Smuzhiyun 
mlxsw_sp_lag_dist_port_add(struct mlxsw_sp_port * mlxsw_sp_port,u16 lag_id)3629*4882a593Smuzhiyun static int mlxsw_sp_lag_dist_port_add(struct mlxsw_sp_port *mlxsw_sp_port,
3630*4882a593Smuzhiyun 				      u16 lag_id)
3631*4882a593Smuzhiyun {
3632*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
3633*4882a593Smuzhiyun 	char sldr_pl[MLXSW_REG_SLDR_LEN];
3634*4882a593Smuzhiyun 
3635*4882a593Smuzhiyun 	mlxsw_reg_sldr_lag_add_port_pack(sldr_pl, lag_id,
3636*4882a593Smuzhiyun 					 mlxsw_sp_port->local_port);
3637*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
3638*4882a593Smuzhiyun }
3639*4882a593Smuzhiyun 
mlxsw_sp_lag_dist_port_remove(struct mlxsw_sp_port * mlxsw_sp_port,u16 lag_id)3640*4882a593Smuzhiyun static int mlxsw_sp_lag_dist_port_remove(struct mlxsw_sp_port *mlxsw_sp_port,
3641*4882a593Smuzhiyun 					 u16 lag_id)
3642*4882a593Smuzhiyun {
3643*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
3644*4882a593Smuzhiyun 	char sldr_pl[MLXSW_REG_SLDR_LEN];
3645*4882a593Smuzhiyun 
3646*4882a593Smuzhiyun 	mlxsw_reg_sldr_lag_remove_port_pack(sldr_pl, lag_id,
3647*4882a593Smuzhiyun 					    mlxsw_sp_port->local_port);
3648*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
3649*4882a593Smuzhiyun }
3650*4882a593Smuzhiyun 
3651*4882a593Smuzhiyun static int
mlxsw_sp_port_lag_col_dist_enable(struct mlxsw_sp_port * mlxsw_sp_port)3652*4882a593Smuzhiyun mlxsw_sp_port_lag_col_dist_enable(struct mlxsw_sp_port *mlxsw_sp_port)
3653*4882a593Smuzhiyun {
3654*4882a593Smuzhiyun 	int err;
3655*4882a593Smuzhiyun 
3656*4882a593Smuzhiyun 	err = mlxsw_sp_lag_col_port_enable(mlxsw_sp_port,
3657*4882a593Smuzhiyun 					   mlxsw_sp_port->lag_id);
3658*4882a593Smuzhiyun 	if (err)
3659*4882a593Smuzhiyun 		return err;
3660*4882a593Smuzhiyun 
3661*4882a593Smuzhiyun 	err = mlxsw_sp_lag_dist_port_add(mlxsw_sp_port, mlxsw_sp_port->lag_id);
3662*4882a593Smuzhiyun 	if (err)
3663*4882a593Smuzhiyun 		goto err_dist_port_add;
3664*4882a593Smuzhiyun 
3665*4882a593Smuzhiyun 	return 0;
3666*4882a593Smuzhiyun 
3667*4882a593Smuzhiyun err_dist_port_add:
3668*4882a593Smuzhiyun 	mlxsw_sp_lag_col_port_disable(mlxsw_sp_port, mlxsw_sp_port->lag_id);
3669*4882a593Smuzhiyun 	return err;
3670*4882a593Smuzhiyun }
3671*4882a593Smuzhiyun 
3672*4882a593Smuzhiyun static int
mlxsw_sp_port_lag_col_dist_disable(struct mlxsw_sp_port * mlxsw_sp_port)3673*4882a593Smuzhiyun mlxsw_sp_port_lag_col_dist_disable(struct mlxsw_sp_port *mlxsw_sp_port)
3674*4882a593Smuzhiyun {
3675*4882a593Smuzhiyun 	int err;
3676*4882a593Smuzhiyun 
3677*4882a593Smuzhiyun 	err = mlxsw_sp_lag_dist_port_remove(mlxsw_sp_port,
3678*4882a593Smuzhiyun 					    mlxsw_sp_port->lag_id);
3679*4882a593Smuzhiyun 	if (err)
3680*4882a593Smuzhiyun 		return err;
3681*4882a593Smuzhiyun 
3682*4882a593Smuzhiyun 	err = mlxsw_sp_lag_col_port_disable(mlxsw_sp_port,
3683*4882a593Smuzhiyun 					    mlxsw_sp_port->lag_id);
3684*4882a593Smuzhiyun 	if (err)
3685*4882a593Smuzhiyun 		goto err_col_port_disable;
3686*4882a593Smuzhiyun 
3687*4882a593Smuzhiyun 	return 0;
3688*4882a593Smuzhiyun 
3689*4882a593Smuzhiyun err_col_port_disable:
3690*4882a593Smuzhiyun 	mlxsw_sp_lag_dist_port_add(mlxsw_sp_port, mlxsw_sp_port->lag_id);
3691*4882a593Smuzhiyun 	return err;
3692*4882a593Smuzhiyun }
3693*4882a593Smuzhiyun 
mlxsw_sp_port_lag_changed(struct mlxsw_sp_port * mlxsw_sp_port,struct netdev_lag_lower_state_info * info)3694*4882a593Smuzhiyun static int mlxsw_sp_port_lag_changed(struct mlxsw_sp_port *mlxsw_sp_port,
3695*4882a593Smuzhiyun 				     struct netdev_lag_lower_state_info *info)
3696*4882a593Smuzhiyun {
3697*4882a593Smuzhiyun 	if (info->tx_enabled)
3698*4882a593Smuzhiyun 		return mlxsw_sp_port_lag_col_dist_enable(mlxsw_sp_port);
3699*4882a593Smuzhiyun 	else
3700*4882a593Smuzhiyun 		return mlxsw_sp_port_lag_col_dist_disable(mlxsw_sp_port);
3701*4882a593Smuzhiyun }
3702*4882a593Smuzhiyun 
mlxsw_sp_port_stp_set(struct mlxsw_sp_port * mlxsw_sp_port,bool enable)3703*4882a593Smuzhiyun static int mlxsw_sp_port_stp_set(struct mlxsw_sp_port *mlxsw_sp_port,
3704*4882a593Smuzhiyun 				 bool enable)
3705*4882a593Smuzhiyun {
3706*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
3707*4882a593Smuzhiyun 	enum mlxsw_reg_spms_state spms_state;
3708*4882a593Smuzhiyun 	char *spms_pl;
3709*4882a593Smuzhiyun 	u16 vid;
3710*4882a593Smuzhiyun 	int err;
3711*4882a593Smuzhiyun 
3712*4882a593Smuzhiyun 	spms_state = enable ? MLXSW_REG_SPMS_STATE_FORWARDING :
3713*4882a593Smuzhiyun 			      MLXSW_REG_SPMS_STATE_DISCARDING;
3714*4882a593Smuzhiyun 
3715*4882a593Smuzhiyun 	spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL);
3716*4882a593Smuzhiyun 	if (!spms_pl)
3717*4882a593Smuzhiyun 		return -ENOMEM;
3718*4882a593Smuzhiyun 	mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port);
3719*4882a593Smuzhiyun 
3720*4882a593Smuzhiyun 	for (vid = 0; vid < VLAN_N_VID; vid++)
3721*4882a593Smuzhiyun 		mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state);
3722*4882a593Smuzhiyun 
3723*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl);
3724*4882a593Smuzhiyun 	kfree(spms_pl);
3725*4882a593Smuzhiyun 	return err;
3726*4882a593Smuzhiyun }
3727*4882a593Smuzhiyun 
mlxsw_sp_port_ovs_join(struct mlxsw_sp_port * mlxsw_sp_port)3728*4882a593Smuzhiyun static int mlxsw_sp_port_ovs_join(struct mlxsw_sp_port *mlxsw_sp_port)
3729*4882a593Smuzhiyun {
3730*4882a593Smuzhiyun 	u16 vid = 1;
3731*4882a593Smuzhiyun 	int err;
3732*4882a593Smuzhiyun 
3733*4882a593Smuzhiyun 	err = mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, true);
3734*4882a593Smuzhiyun 	if (err)
3735*4882a593Smuzhiyun 		return err;
3736*4882a593Smuzhiyun 	err = mlxsw_sp_port_stp_set(mlxsw_sp_port, true);
3737*4882a593Smuzhiyun 	if (err)
3738*4882a593Smuzhiyun 		goto err_port_stp_set;
3739*4882a593Smuzhiyun 	err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, 1, VLAN_N_VID - 2,
3740*4882a593Smuzhiyun 				     true, false);
3741*4882a593Smuzhiyun 	if (err)
3742*4882a593Smuzhiyun 		goto err_port_vlan_set;
3743*4882a593Smuzhiyun 
3744*4882a593Smuzhiyun 	for (; vid <= VLAN_N_VID - 1; vid++) {
3745*4882a593Smuzhiyun 		err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port,
3746*4882a593Smuzhiyun 						     vid, false);
3747*4882a593Smuzhiyun 		if (err)
3748*4882a593Smuzhiyun 			goto err_vid_learning_set;
3749*4882a593Smuzhiyun 	}
3750*4882a593Smuzhiyun 
3751*4882a593Smuzhiyun 	return 0;
3752*4882a593Smuzhiyun 
3753*4882a593Smuzhiyun err_vid_learning_set:
3754*4882a593Smuzhiyun 	for (vid--; vid >= 1; vid--)
3755*4882a593Smuzhiyun 		mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
3756*4882a593Smuzhiyun err_port_vlan_set:
3757*4882a593Smuzhiyun 	mlxsw_sp_port_stp_set(mlxsw_sp_port, false);
3758*4882a593Smuzhiyun err_port_stp_set:
3759*4882a593Smuzhiyun 	mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false);
3760*4882a593Smuzhiyun 	return err;
3761*4882a593Smuzhiyun }
3762*4882a593Smuzhiyun 
mlxsw_sp_port_ovs_leave(struct mlxsw_sp_port * mlxsw_sp_port)3763*4882a593Smuzhiyun static void mlxsw_sp_port_ovs_leave(struct mlxsw_sp_port *mlxsw_sp_port)
3764*4882a593Smuzhiyun {
3765*4882a593Smuzhiyun 	u16 vid;
3766*4882a593Smuzhiyun 
3767*4882a593Smuzhiyun 	for (vid = VLAN_N_VID - 1; vid >= 1; vid--)
3768*4882a593Smuzhiyun 		mlxsw_sp_port_vid_learning_set(mlxsw_sp_port,
3769*4882a593Smuzhiyun 					       vid, true);
3770*4882a593Smuzhiyun 
3771*4882a593Smuzhiyun 	mlxsw_sp_port_vlan_set(mlxsw_sp_port, 1, VLAN_N_VID - 2,
3772*4882a593Smuzhiyun 			       false, false);
3773*4882a593Smuzhiyun 	mlxsw_sp_port_stp_set(mlxsw_sp_port, false);
3774*4882a593Smuzhiyun 	mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false);
3775*4882a593Smuzhiyun }
3776*4882a593Smuzhiyun 
mlxsw_sp_bridge_has_multiple_vxlans(struct net_device * br_dev)3777*4882a593Smuzhiyun static bool mlxsw_sp_bridge_has_multiple_vxlans(struct net_device *br_dev)
3778*4882a593Smuzhiyun {
3779*4882a593Smuzhiyun 	unsigned int num_vxlans = 0;
3780*4882a593Smuzhiyun 	struct net_device *dev;
3781*4882a593Smuzhiyun 	struct list_head *iter;
3782*4882a593Smuzhiyun 
3783*4882a593Smuzhiyun 	netdev_for_each_lower_dev(br_dev, dev, iter) {
3784*4882a593Smuzhiyun 		if (netif_is_vxlan(dev))
3785*4882a593Smuzhiyun 			num_vxlans++;
3786*4882a593Smuzhiyun 	}
3787*4882a593Smuzhiyun 
3788*4882a593Smuzhiyun 	return num_vxlans > 1;
3789*4882a593Smuzhiyun }
3790*4882a593Smuzhiyun 
mlxsw_sp_bridge_vxlan_vlan_is_valid(struct net_device * br_dev)3791*4882a593Smuzhiyun static bool mlxsw_sp_bridge_vxlan_vlan_is_valid(struct net_device *br_dev)
3792*4882a593Smuzhiyun {
3793*4882a593Smuzhiyun 	DECLARE_BITMAP(vlans, VLAN_N_VID) = {0};
3794*4882a593Smuzhiyun 	struct net_device *dev;
3795*4882a593Smuzhiyun 	struct list_head *iter;
3796*4882a593Smuzhiyun 
3797*4882a593Smuzhiyun 	netdev_for_each_lower_dev(br_dev, dev, iter) {
3798*4882a593Smuzhiyun 		u16 pvid;
3799*4882a593Smuzhiyun 		int err;
3800*4882a593Smuzhiyun 
3801*4882a593Smuzhiyun 		if (!netif_is_vxlan(dev))
3802*4882a593Smuzhiyun 			continue;
3803*4882a593Smuzhiyun 
3804*4882a593Smuzhiyun 		err = mlxsw_sp_vxlan_mapped_vid(dev, &pvid);
3805*4882a593Smuzhiyun 		if (err || !pvid)
3806*4882a593Smuzhiyun 			continue;
3807*4882a593Smuzhiyun 
3808*4882a593Smuzhiyun 		if (test_and_set_bit(pvid, vlans))
3809*4882a593Smuzhiyun 			return false;
3810*4882a593Smuzhiyun 	}
3811*4882a593Smuzhiyun 
3812*4882a593Smuzhiyun 	return true;
3813*4882a593Smuzhiyun }
3814*4882a593Smuzhiyun 
mlxsw_sp_bridge_vxlan_is_valid(struct net_device * br_dev,struct netlink_ext_ack * extack)3815*4882a593Smuzhiyun static bool mlxsw_sp_bridge_vxlan_is_valid(struct net_device *br_dev,
3816*4882a593Smuzhiyun 					   struct netlink_ext_ack *extack)
3817*4882a593Smuzhiyun {
3818*4882a593Smuzhiyun 	if (br_multicast_enabled(br_dev)) {
3819*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Multicast can not be enabled on a bridge with a VxLAN device");
3820*4882a593Smuzhiyun 		return false;
3821*4882a593Smuzhiyun 	}
3822*4882a593Smuzhiyun 
3823*4882a593Smuzhiyun 	if (!br_vlan_enabled(br_dev) &&
3824*4882a593Smuzhiyun 	    mlxsw_sp_bridge_has_multiple_vxlans(br_dev)) {
3825*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Multiple VxLAN devices are not supported in a VLAN-unaware bridge");
3826*4882a593Smuzhiyun 		return false;
3827*4882a593Smuzhiyun 	}
3828*4882a593Smuzhiyun 
3829*4882a593Smuzhiyun 	if (br_vlan_enabled(br_dev) &&
3830*4882a593Smuzhiyun 	    !mlxsw_sp_bridge_vxlan_vlan_is_valid(br_dev)) {
3831*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Multiple VxLAN devices cannot have the same VLAN as PVID and egress untagged");
3832*4882a593Smuzhiyun 		return false;
3833*4882a593Smuzhiyun 	}
3834*4882a593Smuzhiyun 
3835*4882a593Smuzhiyun 	return true;
3836*4882a593Smuzhiyun }
3837*4882a593Smuzhiyun 
mlxsw_sp_netdevice_port_upper_event(struct net_device * lower_dev,struct net_device * dev,unsigned long event,void * ptr)3838*4882a593Smuzhiyun static int mlxsw_sp_netdevice_port_upper_event(struct net_device *lower_dev,
3839*4882a593Smuzhiyun 					       struct net_device *dev,
3840*4882a593Smuzhiyun 					       unsigned long event, void *ptr)
3841*4882a593Smuzhiyun {
3842*4882a593Smuzhiyun 	struct netdev_notifier_changeupper_info *info;
3843*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port;
3844*4882a593Smuzhiyun 	struct netlink_ext_ack *extack;
3845*4882a593Smuzhiyun 	struct net_device *upper_dev;
3846*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp;
3847*4882a593Smuzhiyun 	int err = 0;
3848*4882a593Smuzhiyun 
3849*4882a593Smuzhiyun 	mlxsw_sp_port = netdev_priv(dev);
3850*4882a593Smuzhiyun 	mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
3851*4882a593Smuzhiyun 	info = ptr;
3852*4882a593Smuzhiyun 	extack = netdev_notifier_info_to_extack(&info->info);
3853*4882a593Smuzhiyun 
3854*4882a593Smuzhiyun 	switch (event) {
3855*4882a593Smuzhiyun 	case NETDEV_PRECHANGEUPPER:
3856*4882a593Smuzhiyun 		upper_dev = info->upper_dev;
3857*4882a593Smuzhiyun 		if (!is_vlan_dev(upper_dev) &&
3858*4882a593Smuzhiyun 		    !netif_is_lag_master(upper_dev) &&
3859*4882a593Smuzhiyun 		    !netif_is_bridge_master(upper_dev) &&
3860*4882a593Smuzhiyun 		    !netif_is_ovs_master(upper_dev) &&
3861*4882a593Smuzhiyun 		    !netif_is_macvlan(upper_dev)) {
3862*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
3863*4882a593Smuzhiyun 			return -EINVAL;
3864*4882a593Smuzhiyun 		}
3865*4882a593Smuzhiyun 		if (!info->linking)
3866*4882a593Smuzhiyun 			break;
3867*4882a593Smuzhiyun 		if (netif_is_bridge_master(upper_dev) &&
3868*4882a593Smuzhiyun 		    !mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp, upper_dev) &&
3869*4882a593Smuzhiyun 		    mlxsw_sp_bridge_has_vxlan(upper_dev) &&
3870*4882a593Smuzhiyun 		    !mlxsw_sp_bridge_vxlan_is_valid(upper_dev, extack))
3871*4882a593Smuzhiyun 			return -EOPNOTSUPP;
3872*4882a593Smuzhiyun 		if (netdev_has_any_upper_dev(upper_dev) &&
3873*4882a593Smuzhiyun 		    (!netif_is_bridge_master(upper_dev) ||
3874*4882a593Smuzhiyun 		     !mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp,
3875*4882a593Smuzhiyun 							  upper_dev))) {
3876*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "Enslaving a port to a device that already has an upper device is not supported");
3877*4882a593Smuzhiyun 			return -EINVAL;
3878*4882a593Smuzhiyun 		}
3879*4882a593Smuzhiyun 		if (netif_is_lag_master(upper_dev) &&
3880*4882a593Smuzhiyun 		    !mlxsw_sp_master_lag_check(mlxsw_sp, upper_dev,
3881*4882a593Smuzhiyun 					       info->upper_info, extack))
3882*4882a593Smuzhiyun 			return -EINVAL;
3883*4882a593Smuzhiyun 		if (netif_is_lag_master(upper_dev) && vlan_uses_dev(dev)) {
3884*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "Master device is a LAG master and this device has a VLAN");
3885*4882a593Smuzhiyun 			return -EINVAL;
3886*4882a593Smuzhiyun 		}
3887*4882a593Smuzhiyun 		if (netif_is_lag_port(dev) && is_vlan_dev(upper_dev) &&
3888*4882a593Smuzhiyun 		    !netif_is_lag_master(vlan_dev_real_dev(upper_dev))) {
3889*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "Can not put a VLAN on a LAG port");
3890*4882a593Smuzhiyun 			return -EINVAL;
3891*4882a593Smuzhiyun 		}
3892*4882a593Smuzhiyun 		if (netif_is_macvlan(upper_dev) &&
3893*4882a593Smuzhiyun 		    !mlxsw_sp_rif_exists(mlxsw_sp, lower_dev)) {
3894*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "macvlan is only supported on top of router interfaces");
3895*4882a593Smuzhiyun 			return -EOPNOTSUPP;
3896*4882a593Smuzhiyun 		}
3897*4882a593Smuzhiyun 		if (netif_is_ovs_master(upper_dev) && vlan_uses_dev(dev)) {
3898*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "Master device is an OVS master and this device has a VLAN");
3899*4882a593Smuzhiyun 			return -EINVAL;
3900*4882a593Smuzhiyun 		}
3901*4882a593Smuzhiyun 		if (netif_is_ovs_port(dev) && is_vlan_dev(upper_dev)) {
3902*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "Can not put a VLAN on an OVS port");
3903*4882a593Smuzhiyun 			return -EINVAL;
3904*4882a593Smuzhiyun 		}
3905*4882a593Smuzhiyun 		break;
3906*4882a593Smuzhiyun 	case NETDEV_CHANGEUPPER:
3907*4882a593Smuzhiyun 		upper_dev = info->upper_dev;
3908*4882a593Smuzhiyun 		if (netif_is_bridge_master(upper_dev)) {
3909*4882a593Smuzhiyun 			if (info->linking)
3910*4882a593Smuzhiyun 				err = mlxsw_sp_port_bridge_join(mlxsw_sp_port,
3911*4882a593Smuzhiyun 								lower_dev,
3912*4882a593Smuzhiyun 								upper_dev,
3913*4882a593Smuzhiyun 								extack);
3914*4882a593Smuzhiyun 			else
3915*4882a593Smuzhiyun 				mlxsw_sp_port_bridge_leave(mlxsw_sp_port,
3916*4882a593Smuzhiyun 							   lower_dev,
3917*4882a593Smuzhiyun 							   upper_dev);
3918*4882a593Smuzhiyun 		} else if (netif_is_lag_master(upper_dev)) {
3919*4882a593Smuzhiyun 			if (info->linking) {
3920*4882a593Smuzhiyun 				err = mlxsw_sp_port_lag_join(mlxsw_sp_port,
3921*4882a593Smuzhiyun 							     upper_dev);
3922*4882a593Smuzhiyun 			} else {
3923*4882a593Smuzhiyun 				mlxsw_sp_port_lag_col_dist_disable(mlxsw_sp_port);
3924*4882a593Smuzhiyun 				mlxsw_sp_port_lag_leave(mlxsw_sp_port,
3925*4882a593Smuzhiyun 							upper_dev);
3926*4882a593Smuzhiyun 			}
3927*4882a593Smuzhiyun 		} else if (netif_is_ovs_master(upper_dev)) {
3928*4882a593Smuzhiyun 			if (info->linking)
3929*4882a593Smuzhiyun 				err = mlxsw_sp_port_ovs_join(mlxsw_sp_port);
3930*4882a593Smuzhiyun 			else
3931*4882a593Smuzhiyun 				mlxsw_sp_port_ovs_leave(mlxsw_sp_port);
3932*4882a593Smuzhiyun 		} else if (netif_is_macvlan(upper_dev)) {
3933*4882a593Smuzhiyun 			if (!info->linking)
3934*4882a593Smuzhiyun 				mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
3935*4882a593Smuzhiyun 		} else if (is_vlan_dev(upper_dev)) {
3936*4882a593Smuzhiyun 			struct net_device *br_dev;
3937*4882a593Smuzhiyun 
3938*4882a593Smuzhiyun 			if (!netif_is_bridge_port(upper_dev))
3939*4882a593Smuzhiyun 				break;
3940*4882a593Smuzhiyun 			if (info->linking)
3941*4882a593Smuzhiyun 				break;
3942*4882a593Smuzhiyun 			br_dev = netdev_master_upper_dev_get(upper_dev);
3943*4882a593Smuzhiyun 			mlxsw_sp_port_bridge_leave(mlxsw_sp_port, upper_dev,
3944*4882a593Smuzhiyun 						   br_dev);
3945*4882a593Smuzhiyun 		}
3946*4882a593Smuzhiyun 		break;
3947*4882a593Smuzhiyun 	}
3948*4882a593Smuzhiyun 
3949*4882a593Smuzhiyun 	return err;
3950*4882a593Smuzhiyun }
3951*4882a593Smuzhiyun 
mlxsw_sp_netdevice_port_lower_event(struct net_device * dev,unsigned long event,void * ptr)3952*4882a593Smuzhiyun static int mlxsw_sp_netdevice_port_lower_event(struct net_device *dev,
3953*4882a593Smuzhiyun 					       unsigned long event, void *ptr)
3954*4882a593Smuzhiyun {
3955*4882a593Smuzhiyun 	struct netdev_notifier_changelowerstate_info *info;
3956*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port;
3957*4882a593Smuzhiyun 	int err;
3958*4882a593Smuzhiyun 
3959*4882a593Smuzhiyun 	mlxsw_sp_port = netdev_priv(dev);
3960*4882a593Smuzhiyun 	info = ptr;
3961*4882a593Smuzhiyun 
3962*4882a593Smuzhiyun 	switch (event) {
3963*4882a593Smuzhiyun 	case NETDEV_CHANGELOWERSTATE:
3964*4882a593Smuzhiyun 		if (netif_is_lag_port(dev) && mlxsw_sp_port->lagged) {
3965*4882a593Smuzhiyun 			err = mlxsw_sp_port_lag_changed(mlxsw_sp_port,
3966*4882a593Smuzhiyun 							info->lower_state_info);
3967*4882a593Smuzhiyun 			if (err)
3968*4882a593Smuzhiyun 				netdev_err(dev, "Failed to reflect link aggregation lower state change\n");
3969*4882a593Smuzhiyun 		}
3970*4882a593Smuzhiyun 		break;
3971*4882a593Smuzhiyun 	}
3972*4882a593Smuzhiyun 
3973*4882a593Smuzhiyun 	return 0;
3974*4882a593Smuzhiyun }
3975*4882a593Smuzhiyun 
mlxsw_sp_netdevice_port_event(struct net_device * lower_dev,struct net_device * port_dev,unsigned long event,void * ptr)3976*4882a593Smuzhiyun static int mlxsw_sp_netdevice_port_event(struct net_device *lower_dev,
3977*4882a593Smuzhiyun 					 struct net_device *port_dev,
3978*4882a593Smuzhiyun 					 unsigned long event, void *ptr)
3979*4882a593Smuzhiyun {
3980*4882a593Smuzhiyun 	switch (event) {
3981*4882a593Smuzhiyun 	case NETDEV_PRECHANGEUPPER:
3982*4882a593Smuzhiyun 	case NETDEV_CHANGEUPPER:
3983*4882a593Smuzhiyun 		return mlxsw_sp_netdevice_port_upper_event(lower_dev, port_dev,
3984*4882a593Smuzhiyun 							   event, ptr);
3985*4882a593Smuzhiyun 	case NETDEV_CHANGELOWERSTATE:
3986*4882a593Smuzhiyun 		return mlxsw_sp_netdevice_port_lower_event(port_dev, event,
3987*4882a593Smuzhiyun 							   ptr);
3988*4882a593Smuzhiyun 	}
3989*4882a593Smuzhiyun 
3990*4882a593Smuzhiyun 	return 0;
3991*4882a593Smuzhiyun }
3992*4882a593Smuzhiyun 
mlxsw_sp_netdevice_lag_event(struct net_device * lag_dev,unsigned long event,void * ptr)3993*4882a593Smuzhiyun static int mlxsw_sp_netdevice_lag_event(struct net_device *lag_dev,
3994*4882a593Smuzhiyun 					unsigned long event, void *ptr)
3995*4882a593Smuzhiyun {
3996*4882a593Smuzhiyun 	struct net_device *dev;
3997*4882a593Smuzhiyun 	struct list_head *iter;
3998*4882a593Smuzhiyun 	int ret;
3999*4882a593Smuzhiyun 
4000*4882a593Smuzhiyun 	netdev_for_each_lower_dev(lag_dev, dev, iter) {
4001*4882a593Smuzhiyun 		if (mlxsw_sp_port_dev_check(dev)) {
4002*4882a593Smuzhiyun 			ret = mlxsw_sp_netdevice_port_event(lag_dev, dev, event,
4003*4882a593Smuzhiyun 							    ptr);
4004*4882a593Smuzhiyun 			if (ret)
4005*4882a593Smuzhiyun 				return ret;
4006*4882a593Smuzhiyun 		}
4007*4882a593Smuzhiyun 	}
4008*4882a593Smuzhiyun 
4009*4882a593Smuzhiyun 	return 0;
4010*4882a593Smuzhiyun }
4011*4882a593Smuzhiyun 
mlxsw_sp_netdevice_port_vlan_event(struct net_device * vlan_dev,struct net_device * dev,unsigned long event,void * ptr,u16 vid)4012*4882a593Smuzhiyun static int mlxsw_sp_netdevice_port_vlan_event(struct net_device *vlan_dev,
4013*4882a593Smuzhiyun 					      struct net_device *dev,
4014*4882a593Smuzhiyun 					      unsigned long event, void *ptr,
4015*4882a593Smuzhiyun 					      u16 vid)
4016*4882a593Smuzhiyun {
4017*4882a593Smuzhiyun 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
4018*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
4019*4882a593Smuzhiyun 	struct netdev_notifier_changeupper_info *info = ptr;
4020*4882a593Smuzhiyun 	struct netlink_ext_ack *extack;
4021*4882a593Smuzhiyun 	struct net_device *upper_dev;
4022*4882a593Smuzhiyun 	int err = 0;
4023*4882a593Smuzhiyun 
4024*4882a593Smuzhiyun 	extack = netdev_notifier_info_to_extack(&info->info);
4025*4882a593Smuzhiyun 
4026*4882a593Smuzhiyun 	switch (event) {
4027*4882a593Smuzhiyun 	case NETDEV_PRECHANGEUPPER:
4028*4882a593Smuzhiyun 		upper_dev = info->upper_dev;
4029*4882a593Smuzhiyun 		if (!netif_is_bridge_master(upper_dev) &&
4030*4882a593Smuzhiyun 		    !netif_is_macvlan(upper_dev)) {
4031*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
4032*4882a593Smuzhiyun 			return -EINVAL;
4033*4882a593Smuzhiyun 		}
4034*4882a593Smuzhiyun 		if (!info->linking)
4035*4882a593Smuzhiyun 			break;
4036*4882a593Smuzhiyun 		if (netif_is_bridge_master(upper_dev) &&
4037*4882a593Smuzhiyun 		    !mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp, upper_dev) &&
4038*4882a593Smuzhiyun 		    mlxsw_sp_bridge_has_vxlan(upper_dev) &&
4039*4882a593Smuzhiyun 		    !mlxsw_sp_bridge_vxlan_is_valid(upper_dev, extack))
4040*4882a593Smuzhiyun 			return -EOPNOTSUPP;
4041*4882a593Smuzhiyun 		if (netdev_has_any_upper_dev(upper_dev) &&
4042*4882a593Smuzhiyun 		    (!netif_is_bridge_master(upper_dev) ||
4043*4882a593Smuzhiyun 		     !mlxsw_sp_bridge_device_is_offloaded(mlxsw_sp,
4044*4882a593Smuzhiyun 							  upper_dev))) {
4045*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "Enslaving a port to a device that already has an upper device is not supported");
4046*4882a593Smuzhiyun 			return -EINVAL;
4047*4882a593Smuzhiyun 		}
4048*4882a593Smuzhiyun 		if (netif_is_macvlan(upper_dev) &&
4049*4882a593Smuzhiyun 		    !mlxsw_sp_rif_exists(mlxsw_sp, vlan_dev)) {
4050*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "macvlan is only supported on top of router interfaces");
4051*4882a593Smuzhiyun 			return -EOPNOTSUPP;
4052*4882a593Smuzhiyun 		}
4053*4882a593Smuzhiyun 		break;
4054*4882a593Smuzhiyun 	case NETDEV_CHANGEUPPER:
4055*4882a593Smuzhiyun 		upper_dev = info->upper_dev;
4056*4882a593Smuzhiyun 		if (netif_is_bridge_master(upper_dev)) {
4057*4882a593Smuzhiyun 			if (info->linking)
4058*4882a593Smuzhiyun 				err = mlxsw_sp_port_bridge_join(mlxsw_sp_port,
4059*4882a593Smuzhiyun 								vlan_dev,
4060*4882a593Smuzhiyun 								upper_dev,
4061*4882a593Smuzhiyun 								extack);
4062*4882a593Smuzhiyun 			else
4063*4882a593Smuzhiyun 				mlxsw_sp_port_bridge_leave(mlxsw_sp_port,
4064*4882a593Smuzhiyun 							   vlan_dev,
4065*4882a593Smuzhiyun 							   upper_dev);
4066*4882a593Smuzhiyun 		} else if (netif_is_macvlan(upper_dev)) {
4067*4882a593Smuzhiyun 			if (!info->linking)
4068*4882a593Smuzhiyun 				mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
4069*4882a593Smuzhiyun 		} else {
4070*4882a593Smuzhiyun 			err = -EINVAL;
4071*4882a593Smuzhiyun 			WARN_ON(1);
4072*4882a593Smuzhiyun 		}
4073*4882a593Smuzhiyun 		break;
4074*4882a593Smuzhiyun 	}
4075*4882a593Smuzhiyun 
4076*4882a593Smuzhiyun 	return err;
4077*4882a593Smuzhiyun }
4078*4882a593Smuzhiyun 
mlxsw_sp_netdevice_lag_port_vlan_event(struct net_device * vlan_dev,struct net_device * lag_dev,unsigned long event,void * ptr,u16 vid)4079*4882a593Smuzhiyun static int mlxsw_sp_netdevice_lag_port_vlan_event(struct net_device *vlan_dev,
4080*4882a593Smuzhiyun 						  struct net_device *lag_dev,
4081*4882a593Smuzhiyun 						  unsigned long event,
4082*4882a593Smuzhiyun 						  void *ptr, u16 vid)
4083*4882a593Smuzhiyun {
4084*4882a593Smuzhiyun 	struct net_device *dev;
4085*4882a593Smuzhiyun 	struct list_head *iter;
4086*4882a593Smuzhiyun 	int ret;
4087*4882a593Smuzhiyun 
4088*4882a593Smuzhiyun 	netdev_for_each_lower_dev(lag_dev, dev, iter) {
4089*4882a593Smuzhiyun 		if (mlxsw_sp_port_dev_check(dev)) {
4090*4882a593Smuzhiyun 			ret = mlxsw_sp_netdevice_port_vlan_event(vlan_dev, dev,
4091*4882a593Smuzhiyun 								 event, ptr,
4092*4882a593Smuzhiyun 								 vid);
4093*4882a593Smuzhiyun 			if (ret)
4094*4882a593Smuzhiyun 				return ret;
4095*4882a593Smuzhiyun 		}
4096*4882a593Smuzhiyun 	}
4097*4882a593Smuzhiyun 
4098*4882a593Smuzhiyun 	return 0;
4099*4882a593Smuzhiyun }
4100*4882a593Smuzhiyun 
mlxsw_sp_netdevice_bridge_vlan_event(struct net_device * vlan_dev,struct net_device * br_dev,unsigned long event,void * ptr,u16 vid)4101*4882a593Smuzhiyun static int mlxsw_sp_netdevice_bridge_vlan_event(struct net_device *vlan_dev,
4102*4882a593Smuzhiyun 						struct net_device *br_dev,
4103*4882a593Smuzhiyun 						unsigned long event, void *ptr,
4104*4882a593Smuzhiyun 						u16 vid)
4105*4882a593Smuzhiyun {
4106*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(vlan_dev);
4107*4882a593Smuzhiyun 	struct netdev_notifier_changeupper_info *info = ptr;
4108*4882a593Smuzhiyun 	struct netlink_ext_ack *extack;
4109*4882a593Smuzhiyun 	struct net_device *upper_dev;
4110*4882a593Smuzhiyun 
4111*4882a593Smuzhiyun 	if (!mlxsw_sp)
4112*4882a593Smuzhiyun 		return 0;
4113*4882a593Smuzhiyun 
4114*4882a593Smuzhiyun 	extack = netdev_notifier_info_to_extack(&info->info);
4115*4882a593Smuzhiyun 
4116*4882a593Smuzhiyun 	switch (event) {
4117*4882a593Smuzhiyun 	case NETDEV_PRECHANGEUPPER:
4118*4882a593Smuzhiyun 		upper_dev = info->upper_dev;
4119*4882a593Smuzhiyun 		if (!netif_is_macvlan(upper_dev)) {
4120*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
4121*4882a593Smuzhiyun 			return -EOPNOTSUPP;
4122*4882a593Smuzhiyun 		}
4123*4882a593Smuzhiyun 		if (!info->linking)
4124*4882a593Smuzhiyun 			break;
4125*4882a593Smuzhiyun 		if (netif_is_macvlan(upper_dev) &&
4126*4882a593Smuzhiyun 		    !mlxsw_sp_rif_exists(mlxsw_sp, vlan_dev)) {
4127*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "macvlan is only supported on top of router interfaces");
4128*4882a593Smuzhiyun 			return -EOPNOTSUPP;
4129*4882a593Smuzhiyun 		}
4130*4882a593Smuzhiyun 		break;
4131*4882a593Smuzhiyun 	case NETDEV_CHANGEUPPER:
4132*4882a593Smuzhiyun 		upper_dev = info->upper_dev;
4133*4882a593Smuzhiyun 		if (info->linking)
4134*4882a593Smuzhiyun 			break;
4135*4882a593Smuzhiyun 		if (netif_is_macvlan(upper_dev))
4136*4882a593Smuzhiyun 			mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
4137*4882a593Smuzhiyun 		break;
4138*4882a593Smuzhiyun 	}
4139*4882a593Smuzhiyun 
4140*4882a593Smuzhiyun 	return 0;
4141*4882a593Smuzhiyun }
4142*4882a593Smuzhiyun 
mlxsw_sp_netdevice_vlan_event(struct net_device * vlan_dev,unsigned long event,void * ptr)4143*4882a593Smuzhiyun static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev,
4144*4882a593Smuzhiyun 					 unsigned long event, void *ptr)
4145*4882a593Smuzhiyun {
4146*4882a593Smuzhiyun 	struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
4147*4882a593Smuzhiyun 	u16 vid = vlan_dev_vlan_id(vlan_dev);
4148*4882a593Smuzhiyun 
4149*4882a593Smuzhiyun 	if (mlxsw_sp_port_dev_check(real_dev))
4150*4882a593Smuzhiyun 		return mlxsw_sp_netdevice_port_vlan_event(vlan_dev, real_dev,
4151*4882a593Smuzhiyun 							  event, ptr, vid);
4152*4882a593Smuzhiyun 	else if (netif_is_lag_master(real_dev))
4153*4882a593Smuzhiyun 		return mlxsw_sp_netdevice_lag_port_vlan_event(vlan_dev,
4154*4882a593Smuzhiyun 							      real_dev, event,
4155*4882a593Smuzhiyun 							      ptr, vid);
4156*4882a593Smuzhiyun 	else if (netif_is_bridge_master(real_dev))
4157*4882a593Smuzhiyun 		return mlxsw_sp_netdevice_bridge_vlan_event(vlan_dev, real_dev,
4158*4882a593Smuzhiyun 							    event, ptr, vid);
4159*4882a593Smuzhiyun 
4160*4882a593Smuzhiyun 	return 0;
4161*4882a593Smuzhiyun }
4162*4882a593Smuzhiyun 
mlxsw_sp_netdevice_bridge_event(struct net_device * br_dev,unsigned long event,void * ptr)4163*4882a593Smuzhiyun static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev,
4164*4882a593Smuzhiyun 					   unsigned long event, void *ptr)
4165*4882a593Smuzhiyun {
4166*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(br_dev);
4167*4882a593Smuzhiyun 	struct netdev_notifier_changeupper_info *info = ptr;
4168*4882a593Smuzhiyun 	struct netlink_ext_ack *extack;
4169*4882a593Smuzhiyun 	struct net_device *upper_dev;
4170*4882a593Smuzhiyun 
4171*4882a593Smuzhiyun 	if (!mlxsw_sp)
4172*4882a593Smuzhiyun 		return 0;
4173*4882a593Smuzhiyun 
4174*4882a593Smuzhiyun 	extack = netdev_notifier_info_to_extack(&info->info);
4175*4882a593Smuzhiyun 
4176*4882a593Smuzhiyun 	switch (event) {
4177*4882a593Smuzhiyun 	case NETDEV_PRECHANGEUPPER:
4178*4882a593Smuzhiyun 		upper_dev = info->upper_dev;
4179*4882a593Smuzhiyun 		if (!is_vlan_dev(upper_dev) && !netif_is_macvlan(upper_dev)) {
4180*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
4181*4882a593Smuzhiyun 			return -EOPNOTSUPP;
4182*4882a593Smuzhiyun 		}
4183*4882a593Smuzhiyun 		if (!info->linking)
4184*4882a593Smuzhiyun 			break;
4185*4882a593Smuzhiyun 		if (netif_is_macvlan(upper_dev) &&
4186*4882a593Smuzhiyun 		    !mlxsw_sp_rif_exists(mlxsw_sp, br_dev)) {
4187*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "macvlan is only supported on top of router interfaces");
4188*4882a593Smuzhiyun 			return -EOPNOTSUPP;
4189*4882a593Smuzhiyun 		}
4190*4882a593Smuzhiyun 		break;
4191*4882a593Smuzhiyun 	case NETDEV_CHANGEUPPER:
4192*4882a593Smuzhiyun 		upper_dev = info->upper_dev;
4193*4882a593Smuzhiyun 		if (info->linking)
4194*4882a593Smuzhiyun 			break;
4195*4882a593Smuzhiyun 		if (is_vlan_dev(upper_dev))
4196*4882a593Smuzhiyun 			mlxsw_sp_rif_destroy_by_dev(mlxsw_sp, upper_dev);
4197*4882a593Smuzhiyun 		if (netif_is_macvlan(upper_dev))
4198*4882a593Smuzhiyun 			mlxsw_sp_rif_macvlan_del(mlxsw_sp, upper_dev);
4199*4882a593Smuzhiyun 		break;
4200*4882a593Smuzhiyun 	}
4201*4882a593Smuzhiyun 
4202*4882a593Smuzhiyun 	return 0;
4203*4882a593Smuzhiyun }
4204*4882a593Smuzhiyun 
mlxsw_sp_netdevice_macvlan_event(struct net_device * macvlan_dev,unsigned long event,void * ptr)4205*4882a593Smuzhiyun static int mlxsw_sp_netdevice_macvlan_event(struct net_device *macvlan_dev,
4206*4882a593Smuzhiyun 					    unsigned long event, void *ptr)
4207*4882a593Smuzhiyun {
4208*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(macvlan_dev);
4209*4882a593Smuzhiyun 	struct netdev_notifier_changeupper_info *info = ptr;
4210*4882a593Smuzhiyun 	struct netlink_ext_ack *extack;
4211*4882a593Smuzhiyun 
4212*4882a593Smuzhiyun 	if (!mlxsw_sp || event != NETDEV_PRECHANGEUPPER)
4213*4882a593Smuzhiyun 		return 0;
4214*4882a593Smuzhiyun 
4215*4882a593Smuzhiyun 	extack = netdev_notifier_info_to_extack(&info->info);
4216*4882a593Smuzhiyun 
4217*4882a593Smuzhiyun 	/* VRF enslavement is handled in mlxsw_sp_netdevice_vrf_event() */
4218*4882a593Smuzhiyun 	NL_SET_ERR_MSG_MOD(extack, "Unknown upper device type");
4219*4882a593Smuzhiyun 
4220*4882a593Smuzhiyun 	return -EOPNOTSUPP;
4221*4882a593Smuzhiyun }
4222*4882a593Smuzhiyun 
mlxsw_sp_is_vrf_event(unsigned long event,void * ptr)4223*4882a593Smuzhiyun static bool mlxsw_sp_is_vrf_event(unsigned long event, void *ptr)
4224*4882a593Smuzhiyun {
4225*4882a593Smuzhiyun 	struct netdev_notifier_changeupper_info *info = ptr;
4226*4882a593Smuzhiyun 
4227*4882a593Smuzhiyun 	if (event != NETDEV_PRECHANGEUPPER && event != NETDEV_CHANGEUPPER)
4228*4882a593Smuzhiyun 		return false;
4229*4882a593Smuzhiyun 	return netif_is_l3_master(info->upper_dev);
4230*4882a593Smuzhiyun }
4231*4882a593Smuzhiyun 
mlxsw_sp_netdevice_vxlan_event(struct mlxsw_sp * mlxsw_sp,struct net_device * dev,unsigned long event,void * ptr)4232*4882a593Smuzhiyun static int mlxsw_sp_netdevice_vxlan_event(struct mlxsw_sp *mlxsw_sp,
4233*4882a593Smuzhiyun 					  struct net_device *dev,
4234*4882a593Smuzhiyun 					  unsigned long event, void *ptr)
4235*4882a593Smuzhiyun {
4236*4882a593Smuzhiyun 	struct netdev_notifier_changeupper_info *cu_info;
4237*4882a593Smuzhiyun 	struct netdev_notifier_info *info = ptr;
4238*4882a593Smuzhiyun 	struct netlink_ext_ack *extack;
4239*4882a593Smuzhiyun 	struct net_device *upper_dev;
4240*4882a593Smuzhiyun 
4241*4882a593Smuzhiyun 	extack = netdev_notifier_info_to_extack(info);
4242*4882a593Smuzhiyun 
4243*4882a593Smuzhiyun 	switch (event) {
4244*4882a593Smuzhiyun 	case NETDEV_CHANGEUPPER:
4245*4882a593Smuzhiyun 		cu_info = container_of(info,
4246*4882a593Smuzhiyun 				       struct netdev_notifier_changeupper_info,
4247*4882a593Smuzhiyun 				       info);
4248*4882a593Smuzhiyun 		upper_dev = cu_info->upper_dev;
4249*4882a593Smuzhiyun 		if (!netif_is_bridge_master(upper_dev))
4250*4882a593Smuzhiyun 			return 0;
4251*4882a593Smuzhiyun 		if (!mlxsw_sp_lower_get(upper_dev))
4252*4882a593Smuzhiyun 			return 0;
4253*4882a593Smuzhiyun 		if (!mlxsw_sp_bridge_vxlan_is_valid(upper_dev, extack))
4254*4882a593Smuzhiyun 			return -EOPNOTSUPP;
4255*4882a593Smuzhiyun 		if (cu_info->linking) {
4256*4882a593Smuzhiyun 			if (!netif_running(dev))
4257*4882a593Smuzhiyun 				return 0;
4258*4882a593Smuzhiyun 			/* When the bridge is VLAN-aware, the VNI of the VxLAN
4259*4882a593Smuzhiyun 			 * device needs to be mapped to a VLAN, but at this
4260*4882a593Smuzhiyun 			 * point no VLANs are configured on the VxLAN device
4261*4882a593Smuzhiyun 			 */
4262*4882a593Smuzhiyun 			if (br_vlan_enabled(upper_dev))
4263*4882a593Smuzhiyun 				return 0;
4264*4882a593Smuzhiyun 			return mlxsw_sp_bridge_vxlan_join(mlxsw_sp, upper_dev,
4265*4882a593Smuzhiyun 							  dev, 0, extack);
4266*4882a593Smuzhiyun 		} else {
4267*4882a593Smuzhiyun 			/* VLANs were already flushed, which triggered the
4268*4882a593Smuzhiyun 			 * necessary cleanup
4269*4882a593Smuzhiyun 			 */
4270*4882a593Smuzhiyun 			if (br_vlan_enabled(upper_dev))
4271*4882a593Smuzhiyun 				return 0;
4272*4882a593Smuzhiyun 			mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, dev);
4273*4882a593Smuzhiyun 		}
4274*4882a593Smuzhiyun 		break;
4275*4882a593Smuzhiyun 	case NETDEV_PRE_UP:
4276*4882a593Smuzhiyun 		upper_dev = netdev_master_upper_dev_get(dev);
4277*4882a593Smuzhiyun 		if (!upper_dev)
4278*4882a593Smuzhiyun 			return 0;
4279*4882a593Smuzhiyun 		if (!netif_is_bridge_master(upper_dev))
4280*4882a593Smuzhiyun 			return 0;
4281*4882a593Smuzhiyun 		if (!mlxsw_sp_lower_get(upper_dev))
4282*4882a593Smuzhiyun 			return 0;
4283*4882a593Smuzhiyun 		return mlxsw_sp_bridge_vxlan_join(mlxsw_sp, upper_dev, dev, 0,
4284*4882a593Smuzhiyun 						  extack);
4285*4882a593Smuzhiyun 	case NETDEV_DOWN:
4286*4882a593Smuzhiyun 		upper_dev = netdev_master_upper_dev_get(dev);
4287*4882a593Smuzhiyun 		if (!upper_dev)
4288*4882a593Smuzhiyun 			return 0;
4289*4882a593Smuzhiyun 		if (!netif_is_bridge_master(upper_dev))
4290*4882a593Smuzhiyun 			return 0;
4291*4882a593Smuzhiyun 		if (!mlxsw_sp_lower_get(upper_dev))
4292*4882a593Smuzhiyun 			return 0;
4293*4882a593Smuzhiyun 		mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, dev);
4294*4882a593Smuzhiyun 		break;
4295*4882a593Smuzhiyun 	}
4296*4882a593Smuzhiyun 
4297*4882a593Smuzhiyun 	return 0;
4298*4882a593Smuzhiyun }
4299*4882a593Smuzhiyun 
mlxsw_sp_netdevice_event(struct notifier_block * nb,unsigned long event,void * ptr)4300*4882a593Smuzhiyun static int mlxsw_sp_netdevice_event(struct notifier_block *nb,
4301*4882a593Smuzhiyun 				    unsigned long event, void *ptr)
4302*4882a593Smuzhiyun {
4303*4882a593Smuzhiyun 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
4304*4882a593Smuzhiyun 	struct mlxsw_sp_span_entry *span_entry;
4305*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp;
4306*4882a593Smuzhiyun 	int err = 0;
4307*4882a593Smuzhiyun 
4308*4882a593Smuzhiyun 	mlxsw_sp = container_of(nb, struct mlxsw_sp, netdevice_nb);
4309*4882a593Smuzhiyun 	if (event == NETDEV_UNREGISTER) {
4310*4882a593Smuzhiyun 		span_entry = mlxsw_sp_span_entry_find_by_port(mlxsw_sp, dev);
4311*4882a593Smuzhiyun 		if (span_entry)
4312*4882a593Smuzhiyun 			mlxsw_sp_span_entry_invalidate(mlxsw_sp, span_entry);
4313*4882a593Smuzhiyun 	}
4314*4882a593Smuzhiyun 	mlxsw_sp_span_respin(mlxsw_sp);
4315*4882a593Smuzhiyun 
4316*4882a593Smuzhiyun 	if (netif_is_vxlan(dev))
4317*4882a593Smuzhiyun 		err = mlxsw_sp_netdevice_vxlan_event(mlxsw_sp, dev, event, ptr);
4318*4882a593Smuzhiyun 	if (mlxsw_sp_netdev_is_ipip_ol(mlxsw_sp, dev))
4319*4882a593Smuzhiyun 		err = mlxsw_sp_netdevice_ipip_ol_event(mlxsw_sp, dev,
4320*4882a593Smuzhiyun 						       event, ptr);
4321*4882a593Smuzhiyun 	else if (mlxsw_sp_netdev_is_ipip_ul(mlxsw_sp, dev))
4322*4882a593Smuzhiyun 		err = mlxsw_sp_netdevice_ipip_ul_event(mlxsw_sp, dev,
4323*4882a593Smuzhiyun 						       event, ptr);
4324*4882a593Smuzhiyun 	else if (event == NETDEV_PRE_CHANGEADDR ||
4325*4882a593Smuzhiyun 		 event == NETDEV_CHANGEADDR ||
4326*4882a593Smuzhiyun 		 event == NETDEV_CHANGEMTU)
4327*4882a593Smuzhiyun 		err = mlxsw_sp_netdevice_router_port_event(dev, event, ptr);
4328*4882a593Smuzhiyun 	else if (mlxsw_sp_is_vrf_event(event, ptr))
4329*4882a593Smuzhiyun 		err = mlxsw_sp_netdevice_vrf_event(dev, event, ptr);
4330*4882a593Smuzhiyun 	else if (mlxsw_sp_port_dev_check(dev))
4331*4882a593Smuzhiyun 		err = mlxsw_sp_netdevice_port_event(dev, dev, event, ptr);
4332*4882a593Smuzhiyun 	else if (netif_is_lag_master(dev))
4333*4882a593Smuzhiyun 		err = mlxsw_sp_netdevice_lag_event(dev, event, ptr);
4334*4882a593Smuzhiyun 	else if (is_vlan_dev(dev))
4335*4882a593Smuzhiyun 		err = mlxsw_sp_netdevice_vlan_event(dev, event, ptr);
4336*4882a593Smuzhiyun 	else if (netif_is_bridge_master(dev))
4337*4882a593Smuzhiyun 		err = mlxsw_sp_netdevice_bridge_event(dev, event, ptr);
4338*4882a593Smuzhiyun 	else if (netif_is_macvlan(dev))
4339*4882a593Smuzhiyun 		err = mlxsw_sp_netdevice_macvlan_event(dev, event, ptr);
4340*4882a593Smuzhiyun 
4341*4882a593Smuzhiyun 	return notifier_from_errno(err);
4342*4882a593Smuzhiyun }
4343*4882a593Smuzhiyun 
4344*4882a593Smuzhiyun static struct notifier_block mlxsw_sp_inetaddr_valid_nb __read_mostly = {
4345*4882a593Smuzhiyun 	.notifier_call = mlxsw_sp_inetaddr_valid_event,
4346*4882a593Smuzhiyun };
4347*4882a593Smuzhiyun 
4348*4882a593Smuzhiyun static struct notifier_block mlxsw_sp_inet6addr_valid_nb __read_mostly = {
4349*4882a593Smuzhiyun 	.notifier_call = mlxsw_sp_inet6addr_valid_event,
4350*4882a593Smuzhiyun };
4351*4882a593Smuzhiyun 
4352*4882a593Smuzhiyun static const struct pci_device_id mlxsw_sp1_pci_id_table[] = {
4353*4882a593Smuzhiyun 	{PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM), 0},
4354*4882a593Smuzhiyun 	{0, },
4355*4882a593Smuzhiyun };
4356*4882a593Smuzhiyun 
4357*4882a593Smuzhiyun static struct pci_driver mlxsw_sp1_pci_driver = {
4358*4882a593Smuzhiyun 	.name = mlxsw_sp1_driver_name,
4359*4882a593Smuzhiyun 	.id_table = mlxsw_sp1_pci_id_table,
4360*4882a593Smuzhiyun };
4361*4882a593Smuzhiyun 
4362*4882a593Smuzhiyun static const struct pci_device_id mlxsw_sp2_pci_id_table[] = {
4363*4882a593Smuzhiyun 	{PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM2), 0},
4364*4882a593Smuzhiyun 	{0, },
4365*4882a593Smuzhiyun };
4366*4882a593Smuzhiyun 
4367*4882a593Smuzhiyun static struct pci_driver mlxsw_sp2_pci_driver = {
4368*4882a593Smuzhiyun 	.name = mlxsw_sp2_driver_name,
4369*4882a593Smuzhiyun 	.id_table = mlxsw_sp2_pci_id_table,
4370*4882a593Smuzhiyun };
4371*4882a593Smuzhiyun 
4372*4882a593Smuzhiyun static const struct pci_device_id mlxsw_sp3_pci_id_table[] = {
4373*4882a593Smuzhiyun 	{PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM3), 0},
4374*4882a593Smuzhiyun 	{0, },
4375*4882a593Smuzhiyun };
4376*4882a593Smuzhiyun 
4377*4882a593Smuzhiyun static struct pci_driver mlxsw_sp3_pci_driver = {
4378*4882a593Smuzhiyun 	.name = mlxsw_sp3_driver_name,
4379*4882a593Smuzhiyun 	.id_table = mlxsw_sp3_pci_id_table,
4380*4882a593Smuzhiyun };
4381*4882a593Smuzhiyun 
mlxsw_sp_module_init(void)4382*4882a593Smuzhiyun static int __init mlxsw_sp_module_init(void)
4383*4882a593Smuzhiyun {
4384*4882a593Smuzhiyun 	int err;
4385*4882a593Smuzhiyun 
4386*4882a593Smuzhiyun 	register_inetaddr_validator_notifier(&mlxsw_sp_inetaddr_valid_nb);
4387*4882a593Smuzhiyun 	register_inet6addr_validator_notifier(&mlxsw_sp_inet6addr_valid_nb);
4388*4882a593Smuzhiyun 
4389*4882a593Smuzhiyun 	err = mlxsw_core_driver_register(&mlxsw_sp1_driver);
4390*4882a593Smuzhiyun 	if (err)
4391*4882a593Smuzhiyun 		goto err_sp1_core_driver_register;
4392*4882a593Smuzhiyun 
4393*4882a593Smuzhiyun 	err = mlxsw_core_driver_register(&mlxsw_sp2_driver);
4394*4882a593Smuzhiyun 	if (err)
4395*4882a593Smuzhiyun 		goto err_sp2_core_driver_register;
4396*4882a593Smuzhiyun 
4397*4882a593Smuzhiyun 	err = mlxsw_core_driver_register(&mlxsw_sp3_driver);
4398*4882a593Smuzhiyun 	if (err)
4399*4882a593Smuzhiyun 		goto err_sp3_core_driver_register;
4400*4882a593Smuzhiyun 
4401*4882a593Smuzhiyun 	err = mlxsw_pci_driver_register(&mlxsw_sp1_pci_driver);
4402*4882a593Smuzhiyun 	if (err)
4403*4882a593Smuzhiyun 		goto err_sp1_pci_driver_register;
4404*4882a593Smuzhiyun 
4405*4882a593Smuzhiyun 	err = mlxsw_pci_driver_register(&mlxsw_sp2_pci_driver);
4406*4882a593Smuzhiyun 	if (err)
4407*4882a593Smuzhiyun 		goto err_sp2_pci_driver_register;
4408*4882a593Smuzhiyun 
4409*4882a593Smuzhiyun 	err = mlxsw_pci_driver_register(&mlxsw_sp3_pci_driver);
4410*4882a593Smuzhiyun 	if (err)
4411*4882a593Smuzhiyun 		goto err_sp3_pci_driver_register;
4412*4882a593Smuzhiyun 
4413*4882a593Smuzhiyun 	return 0;
4414*4882a593Smuzhiyun 
4415*4882a593Smuzhiyun err_sp3_pci_driver_register:
4416*4882a593Smuzhiyun 	mlxsw_pci_driver_unregister(&mlxsw_sp2_pci_driver);
4417*4882a593Smuzhiyun err_sp2_pci_driver_register:
4418*4882a593Smuzhiyun 	mlxsw_pci_driver_unregister(&mlxsw_sp1_pci_driver);
4419*4882a593Smuzhiyun err_sp1_pci_driver_register:
4420*4882a593Smuzhiyun 	mlxsw_core_driver_unregister(&mlxsw_sp3_driver);
4421*4882a593Smuzhiyun err_sp3_core_driver_register:
4422*4882a593Smuzhiyun 	mlxsw_core_driver_unregister(&mlxsw_sp2_driver);
4423*4882a593Smuzhiyun err_sp2_core_driver_register:
4424*4882a593Smuzhiyun 	mlxsw_core_driver_unregister(&mlxsw_sp1_driver);
4425*4882a593Smuzhiyun err_sp1_core_driver_register:
4426*4882a593Smuzhiyun 	unregister_inet6addr_validator_notifier(&mlxsw_sp_inet6addr_valid_nb);
4427*4882a593Smuzhiyun 	unregister_inetaddr_validator_notifier(&mlxsw_sp_inetaddr_valid_nb);
4428*4882a593Smuzhiyun 	return err;
4429*4882a593Smuzhiyun }
4430*4882a593Smuzhiyun 
mlxsw_sp_module_exit(void)4431*4882a593Smuzhiyun static void __exit mlxsw_sp_module_exit(void)
4432*4882a593Smuzhiyun {
4433*4882a593Smuzhiyun 	mlxsw_pci_driver_unregister(&mlxsw_sp3_pci_driver);
4434*4882a593Smuzhiyun 	mlxsw_pci_driver_unregister(&mlxsw_sp2_pci_driver);
4435*4882a593Smuzhiyun 	mlxsw_pci_driver_unregister(&mlxsw_sp1_pci_driver);
4436*4882a593Smuzhiyun 	mlxsw_core_driver_unregister(&mlxsw_sp3_driver);
4437*4882a593Smuzhiyun 	mlxsw_core_driver_unregister(&mlxsw_sp2_driver);
4438*4882a593Smuzhiyun 	mlxsw_core_driver_unregister(&mlxsw_sp1_driver);
4439*4882a593Smuzhiyun 	unregister_inet6addr_validator_notifier(&mlxsw_sp_inet6addr_valid_nb);
4440*4882a593Smuzhiyun 	unregister_inetaddr_validator_notifier(&mlxsw_sp_inetaddr_valid_nb);
4441*4882a593Smuzhiyun }
4442*4882a593Smuzhiyun 
4443*4882a593Smuzhiyun module_init(mlxsw_sp_module_init);
4444*4882a593Smuzhiyun module_exit(mlxsw_sp_module_exit);
4445*4882a593Smuzhiyun 
4446*4882a593Smuzhiyun MODULE_LICENSE("Dual BSD/GPL");
4447*4882a593Smuzhiyun MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
4448*4882a593Smuzhiyun MODULE_DESCRIPTION("Mellanox Spectrum driver");
4449*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, mlxsw_sp1_pci_id_table);
4450*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, mlxsw_sp2_pci_id_table);
4451*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, mlxsw_sp3_pci_id_table);
4452*4882a593Smuzhiyun MODULE_FIRMWARE(MLXSW_SP1_FW_FILENAME);
4453*4882a593Smuzhiyun MODULE_FIRMWARE(MLXSW_SP2_FW_FILENAME);
4454*4882a593Smuzhiyun MODULE_FIRMWARE(MLXSW_SP3_FW_FILENAME);
4455