xref: /OK3568_Linux_fs/kernel/drivers/net/ethernet/mellanox/mlxsw/switchx2.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/slab.h>
11*4882a593Smuzhiyun #include <linux/device.h>
12*4882a593Smuzhiyun #include <linux/skbuff.h>
13*4882a593Smuzhiyun #include <linux/if_vlan.h>
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include "pci.h"
16*4882a593Smuzhiyun #include "core.h"
17*4882a593Smuzhiyun #include "reg.h"
18*4882a593Smuzhiyun #include "port.h"
19*4882a593Smuzhiyun #include "trap.h"
20*4882a593Smuzhiyun #include "txheader.h"
21*4882a593Smuzhiyun #include "ib.h"
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun static const char mlxsw_sx_driver_name[] = "mlxsw_switchx2";
24*4882a593Smuzhiyun static const char mlxsw_sx_driver_version[] = "1.0";
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun struct mlxsw_sx_port;
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun struct mlxsw_sx {
29*4882a593Smuzhiyun 	struct mlxsw_sx_port **ports;
30*4882a593Smuzhiyun 	struct mlxsw_core *core;
31*4882a593Smuzhiyun 	const struct mlxsw_bus_info *bus_info;
32*4882a593Smuzhiyun 	u8 hw_id[ETH_ALEN];
33*4882a593Smuzhiyun };
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun struct mlxsw_sx_port_pcpu_stats {
36*4882a593Smuzhiyun 	u64			rx_packets;
37*4882a593Smuzhiyun 	u64			rx_bytes;
38*4882a593Smuzhiyun 	u64			tx_packets;
39*4882a593Smuzhiyun 	u64			tx_bytes;
40*4882a593Smuzhiyun 	struct u64_stats_sync	syncp;
41*4882a593Smuzhiyun 	u32			tx_dropped;
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun struct mlxsw_sx_port {
45*4882a593Smuzhiyun 	struct net_device *dev;
46*4882a593Smuzhiyun 	struct mlxsw_sx_port_pcpu_stats __percpu *pcpu_stats;
47*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx;
48*4882a593Smuzhiyun 	u8 local_port;
49*4882a593Smuzhiyun 	struct {
50*4882a593Smuzhiyun 		u8 module;
51*4882a593Smuzhiyun 	} mapping;
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun /* tx_hdr_version
55*4882a593Smuzhiyun  * Tx header version.
56*4882a593Smuzhiyun  * Must be set to 0.
57*4882a593Smuzhiyun  */
58*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, version, 0x00, 28, 4);
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun /* tx_hdr_ctl
61*4882a593Smuzhiyun  * Packet control type.
62*4882a593Smuzhiyun  * 0 - Ethernet control (e.g. EMADs, LACP)
63*4882a593Smuzhiyun  * 1 - Ethernet data
64*4882a593Smuzhiyun  */
65*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, ctl, 0x00, 26, 2);
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun /* tx_hdr_proto
68*4882a593Smuzhiyun  * Packet protocol type. Must be set to 1 (Ethernet).
69*4882a593Smuzhiyun  */
70*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, proto, 0x00, 21, 3);
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun /* tx_hdr_etclass
73*4882a593Smuzhiyun  * Egress TClass to be used on the egress device on the egress port.
74*4882a593Smuzhiyun  * The MSB is specified in the 'ctclass3' field.
75*4882a593Smuzhiyun  * Range is 0-15, where 15 is the highest priority.
76*4882a593Smuzhiyun  */
77*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, etclass, 0x00, 18, 3);
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun /* tx_hdr_swid
80*4882a593Smuzhiyun  * Switch partition ID.
81*4882a593Smuzhiyun  */
82*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, swid, 0x00, 12, 3);
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun /* tx_hdr_port_mid
85*4882a593Smuzhiyun  * Destination local port for unicast packets.
86*4882a593Smuzhiyun  * Destination multicast ID for multicast packets.
87*4882a593Smuzhiyun  *
88*4882a593Smuzhiyun  * Control packets are directed to a specific egress port, while data
89*4882a593Smuzhiyun  * packets are transmitted through the CPU port (0) into the switch partition,
90*4882a593Smuzhiyun  * where forwarding rules are applied.
91*4882a593Smuzhiyun  */
92*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, port_mid, 0x04, 16, 16);
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun /* tx_hdr_ctclass3
95*4882a593Smuzhiyun  * See field 'etclass'.
96*4882a593Smuzhiyun  */
97*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, ctclass3, 0x04, 14, 1);
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun /* tx_hdr_rdq
100*4882a593Smuzhiyun  * RDQ for control packets sent to remote CPU.
101*4882a593Smuzhiyun  * Must be set to 0x1F for EMADs, otherwise 0.
102*4882a593Smuzhiyun  */
103*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, rdq, 0x04, 9, 5);
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun /* tx_hdr_cpu_sig
106*4882a593Smuzhiyun  * Signature control for packets going to CPU. Must be set to 0.
107*4882a593Smuzhiyun  */
108*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, cpu_sig, 0x04, 0, 9);
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun /* tx_hdr_sig
111*4882a593Smuzhiyun  * Stacking protocl signature. Must be set to 0xE0E0.
112*4882a593Smuzhiyun  */
113*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, sig, 0x0C, 16, 16);
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun /* tx_hdr_stclass
116*4882a593Smuzhiyun  * Stacking TClass.
117*4882a593Smuzhiyun  */
118*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, stclass, 0x0C, 13, 3);
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun /* tx_hdr_emad
121*4882a593Smuzhiyun  * EMAD bit. Must be set for EMADs.
122*4882a593Smuzhiyun  */
123*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, emad, 0x0C, 5, 1);
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun /* tx_hdr_type
126*4882a593Smuzhiyun  * 0 - Data packets
127*4882a593Smuzhiyun  * 6 - Control packets
128*4882a593Smuzhiyun  */
129*4882a593Smuzhiyun MLXSW_ITEM32(tx, hdr, type, 0x0C, 0, 4);
130*4882a593Smuzhiyun 
mlxsw_sx_txhdr_construct(struct sk_buff * skb,const struct mlxsw_tx_info * tx_info)131*4882a593Smuzhiyun static void mlxsw_sx_txhdr_construct(struct sk_buff *skb,
132*4882a593Smuzhiyun 				     const struct mlxsw_tx_info *tx_info)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun 	char *txhdr = skb_push(skb, MLXSW_TXHDR_LEN);
135*4882a593Smuzhiyun 	bool is_emad = tx_info->is_emad;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	memset(txhdr, 0, MLXSW_TXHDR_LEN);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	/* We currently set default values for the egress tclass (QoS). */
140*4882a593Smuzhiyun 	mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_0);
141*4882a593Smuzhiyun 	mlxsw_tx_hdr_ctl_set(txhdr, MLXSW_TXHDR_ETH_CTL);
142*4882a593Smuzhiyun 	mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH);
143*4882a593Smuzhiyun 	mlxsw_tx_hdr_etclass_set(txhdr, is_emad ? MLXSW_TXHDR_ETCLASS_6 :
144*4882a593Smuzhiyun 						  MLXSW_TXHDR_ETCLASS_5);
145*4882a593Smuzhiyun 	mlxsw_tx_hdr_swid_set(txhdr, 0);
146*4882a593Smuzhiyun 	mlxsw_tx_hdr_port_mid_set(txhdr, tx_info->local_port);
147*4882a593Smuzhiyun 	mlxsw_tx_hdr_ctclass3_set(txhdr, MLXSW_TXHDR_CTCLASS3);
148*4882a593Smuzhiyun 	mlxsw_tx_hdr_rdq_set(txhdr, is_emad ? MLXSW_TXHDR_RDQ_EMAD :
149*4882a593Smuzhiyun 					      MLXSW_TXHDR_RDQ_OTHER);
150*4882a593Smuzhiyun 	mlxsw_tx_hdr_cpu_sig_set(txhdr, MLXSW_TXHDR_CPU_SIG);
151*4882a593Smuzhiyun 	mlxsw_tx_hdr_sig_set(txhdr, MLXSW_TXHDR_SIG);
152*4882a593Smuzhiyun 	mlxsw_tx_hdr_stclass_set(txhdr, MLXSW_TXHDR_STCLASS_NONE);
153*4882a593Smuzhiyun 	mlxsw_tx_hdr_emad_set(txhdr, is_emad ? MLXSW_TXHDR_EMAD :
154*4882a593Smuzhiyun 					       MLXSW_TXHDR_NOT_EMAD);
155*4882a593Smuzhiyun 	mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_CONTROL);
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun 
mlxsw_sx_port_admin_status_set(struct mlxsw_sx_port * mlxsw_sx_port,bool is_up)158*4882a593Smuzhiyun static int mlxsw_sx_port_admin_status_set(struct mlxsw_sx_port *mlxsw_sx_port,
159*4882a593Smuzhiyun 					  bool is_up)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
162*4882a593Smuzhiyun 	char paos_pl[MLXSW_REG_PAOS_LEN];
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	mlxsw_reg_paos_pack(paos_pl, mlxsw_sx_port->local_port,
165*4882a593Smuzhiyun 			    is_up ? MLXSW_PORT_ADMIN_STATUS_UP :
166*4882a593Smuzhiyun 			    MLXSW_PORT_ADMIN_STATUS_DOWN);
167*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(paos), paos_pl);
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun 
mlxsw_sx_port_oper_status_get(struct mlxsw_sx_port * mlxsw_sx_port,bool * p_is_up)170*4882a593Smuzhiyun static int mlxsw_sx_port_oper_status_get(struct mlxsw_sx_port *mlxsw_sx_port,
171*4882a593Smuzhiyun 					 bool *p_is_up)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
174*4882a593Smuzhiyun 	char paos_pl[MLXSW_REG_PAOS_LEN];
175*4882a593Smuzhiyun 	u8 oper_status;
176*4882a593Smuzhiyun 	int err;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	mlxsw_reg_paos_pack(paos_pl, mlxsw_sx_port->local_port, 0);
179*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(paos), paos_pl);
180*4882a593Smuzhiyun 	if (err)
181*4882a593Smuzhiyun 		return err;
182*4882a593Smuzhiyun 	oper_status = mlxsw_reg_paos_oper_status_get(paos_pl);
183*4882a593Smuzhiyun 	*p_is_up = oper_status == MLXSW_PORT_ADMIN_STATUS_UP;
184*4882a593Smuzhiyun 	return 0;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun 
__mlxsw_sx_port_mtu_set(struct mlxsw_sx_port * mlxsw_sx_port,u16 mtu)187*4882a593Smuzhiyun static int __mlxsw_sx_port_mtu_set(struct mlxsw_sx_port *mlxsw_sx_port,
188*4882a593Smuzhiyun 				   u16 mtu)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
191*4882a593Smuzhiyun 	char pmtu_pl[MLXSW_REG_PMTU_LEN];
192*4882a593Smuzhiyun 	int max_mtu;
193*4882a593Smuzhiyun 	int err;
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sx_port->local_port, 0);
196*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(pmtu), pmtu_pl);
197*4882a593Smuzhiyun 	if (err)
198*4882a593Smuzhiyun 		return err;
199*4882a593Smuzhiyun 	max_mtu = mlxsw_reg_pmtu_max_mtu_get(pmtu_pl);
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	if (mtu > max_mtu)
202*4882a593Smuzhiyun 		return -EINVAL;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sx_port->local_port, mtu);
205*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(pmtu), pmtu_pl);
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun 
mlxsw_sx_port_mtu_eth_set(struct mlxsw_sx_port * mlxsw_sx_port,u16 mtu)208*4882a593Smuzhiyun static int mlxsw_sx_port_mtu_eth_set(struct mlxsw_sx_port *mlxsw_sx_port,
209*4882a593Smuzhiyun 				     u16 mtu)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun 	mtu += MLXSW_TXHDR_LEN + ETH_HLEN;
212*4882a593Smuzhiyun 	return __mlxsw_sx_port_mtu_set(mlxsw_sx_port, mtu);
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun 
mlxsw_sx_port_mtu_ib_set(struct mlxsw_sx_port * mlxsw_sx_port,u16 mtu)215*4882a593Smuzhiyun static int mlxsw_sx_port_mtu_ib_set(struct mlxsw_sx_port *mlxsw_sx_port,
216*4882a593Smuzhiyun 				    u16 mtu)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun 	return __mlxsw_sx_port_mtu_set(mlxsw_sx_port, mtu);
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun 
mlxsw_sx_port_ib_port_set(struct mlxsw_sx_port * mlxsw_sx_port,u8 ib_port)221*4882a593Smuzhiyun static int mlxsw_sx_port_ib_port_set(struct mlxsw_sx_port *mlxsw_sx_port,
222*4882a593Smuzhiyun 				     u8 ib_port)
223*4882a593Smuzhiyun {
224*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
225*4882a593Smuzhiyun 	char plib_pl[MLXSW_REG_PLIB_LEN] = {0};
226*4882a593Smuzhiyun 	int err;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	mlxsw_reg_plib_local_port_set(plib_pl, mlxsw_sx_port->local_port);
229*4882a593Smuzhiyun 	mlxsw_reg_plib_ib_port_set(plib_pl, ib_port);
230*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(plib), plib_pl);
231*4882a593Smuzhiyun 	return err;
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun 
mlxsw_sx_port_swid_set(struct mlxsw_sx_port * mlxsw_sx_port,u8 swid)234*4882a593Smuzhiyun static int mlxsw_sx_port_swid_set(struct mlxsw_sx_port *mlxsw_sx_port, u8 swid)
235*4882a593Smuzhiyun {
236*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
237*4882a593Smuzhiyun 	char pspa_pl[MLXSW_REG_PSPA_LEN];
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	mlxsw_reg_pspa_pack(pspa_pl, swid, mlxsw_sx_port->local_port);
240*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(pspa), pspa_pl);
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun static int
mlxsw_sx_port_system_port_mapping_set(struct mlxsw_sx_port * mlxsw_sx_port)244*4882a593Smuzhiyun mlxsw_sx_port_system_port_mapping_set(struct mlxsw_sx_port *mlxsw_sx_port)
245*4882a593Smuzhiyun {
246*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
247*4882a593Smuzhiyun 	char sspr_pl[MLXSW_REG_SSPR_LEN];
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	mlxsw_reg_sspr_pack(sspr_pl, mlxsw_sx_port->local_port);
250*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sspr), sspr_pl);
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun 
mlxsw_sx_port_module_info_get(struct mlxsw_sx * mlxsw_sx,u8 local_port,u8 * p_module,u8 * p_width)253*4882a593Smuzhiyun static int mlxsw_sx_port_module_info_get(struct mlxsw_sx *mlxsw_sx,
254*4882a593Smuzhiyun 					 u8 local_port, u8 *p_module,
255*4882a593Smuzhiyun 					 u8 *p_width)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun 	char pmlp_pl[MLXSW_REG_PMLP_LEN];
258*4882a593Smuzhiyun 	int err;
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
261*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(pmlp), pmlp_pl);
262*4882a593Smuzhiyun 	if (err)
263*4882a593Smuzhiyun 		return err;
264*4882a593Smuzhiyun 	*p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0);
265*4882a593Smuzhiyun 	*p_width = mlxsw_reg_pmlp_width_get(pmlp_pl);
266*4882a593Smuzhiyun 	return 0;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
mlxsw_sx_port_open(struct net_device * dev)269*4882a593Smuzhiyun static int mlxsw_sx_port_open(struct net_device *dev)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
272*4882a593Smuzhiyun 	int err;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, true);
275*4882a593Smuzhiyun 	if (err)
276*4882a593Smuzhiyun 		return err;
277*4882a593Smuzhiyun 	netif_start_queue(dev);
278*4882a593Smuzhiyun 	return 0;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun 
mlxsw_sx_port_stop(struct net_device * dev)281*4882a593Smuzhiyun static int mlxsw_sx_port_stop(struct net_device *dev)
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	netif_stop_queue(dev);
286*4882a593Smuzhiyun 	return mlxsw_sx_port_admin_status_set(mlxsw_sx_port, false);
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun 
mlxsw_sx_port_xmit(struct sk_buff * skb,struct net_device * dev)289*4882a593Smuzhiyun static netdev_tx_t mlxsw_sx_port_xmit(struct sk_buff *skb,
290*4882a593Smuzhiyun 				      struct net_device *dev)
291*4882a593Smuzhiyun {
292*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
293*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
294*4882a593Smuzhiyun 	struct mlxsw_sx_port_pcpu_stats *pcpu_stats;
295*4882a593Smuzhiyun 	const struct mlxsw_tx_info tx_info = {
296*4882a593Smuzhiyun 		.local_port = mlxsw_sx_port->local_port,
297*4882a593Smuzhiyun 		.is_emad = false,
298*4882a593Smuzhiyun 	};
299*4882a593Smuzhiyun 	u64 len;
300*4882a593Smuzhiyun 	int err;
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	if (skb_cow_head(skb, MLXSW_TXHDR_LEN)) {
303*4882a593Smuzhiyun 		this_cpu_inc(mlxsw_sx_port->pcpu_stats->tx_dropped);
304*4882a593Smuzhiyun 		dev_kfree_skb_any(skb);
305*4882a593Smuzhiyun 		return NETDEV_TX_OK;
306*4882a593Smuzhiyun 	}
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	memset(skb->cb, 0, sizeof(struct mlxsw_skb_cb));
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	if (mlxsw_core_skb_transmit_busy(mlxsw_sx->core, &tx_info))
311*4882a593Smuzhiyun 		return NETDEV_TX_BUSY;
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	mlxsw_sx_txhdr_construct(skb, &tx_info);
314*4882a593Smuzhiyun 	/* TX header is consumed by HW on the way so we shouldn't count its
315*4882a593Smuzhiyun 	 * bytes as being sent.
316*4882a593Smuzhiyun 	 */
317*4882a593Smuzhiyun 	len = skb->len - MLXSW_TXHDR_LEN;
318*4882a593Smuzhiyun 	/* Due to a race we might fail here because of a full queue. In that
319*4882a593Smuzhiyun 	 * unlikely case we simply drop the packet.
320*4882a593Smuzhiyun 	 */
321*4882a593Smuzhiyun 	err = mlxsw_core_skb_transmit(mlxsw_sx->core, skb, &tx_info);
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	if (!err) {
324*4882a593Smuzhiyun 		pcpu_stats = this_cpu_ptr(mlxsw_sx_port->pcpu_stats);
325*4882a593Smuzhiyun 		u64_stats_update_begin(&pcpu_stats->syncp);
326*4882a593Smuzhiyun 		pcpu_stats->tx_packets++;
327*4882a593Smuzhiyun 		pcpu_stats->tx_bytes += len;
328*4882a593Smuzhiyun 		u64_stats_update_end(&pcpu_stats->syncp);
329*4882a593Smuzhiyun 	} else {
330*4882a593Smuzhiyun 		this_cpu_inc(mlxsw_sx_port->pcpu_stats->tx_dropped);
331*4882a593Smuzhiyun 		dev_kfree_skb_any(skb);
332*4882a593Smuzhiyun 	}
333*4882a593Smuzhiyun 	return NETDEV_TX_OK;
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun 
mlxsw_sx_port_change_mtu(struct net_device * dev,int mtu)336*4882a593Smuzhiyun static int mlxsw_sx_port_change_mtu(struct net_device *dev, int mtu)
337*4882a593Smuzhiyun {
338*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
339*4882a593Smuzhiyun 	int err;
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	err = mlxsw_sx_port_mtu_eth_set(mlxsw_sx_port, mtu);
342*4882a593Smuzhiyun 	if (err)
343*4882a593Smuzhiyun 		return err;
344*4882a593Smuzhiyun 	dev->mtu = mtu;
345*4882a593Smuzhiyun 	return 0;
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun static void
mlxsw_sx_port_get_stats64(struct net_device * dev,struct rtnl_link_stats64 * stats)349*4882a593Smuzhiyun mlxsw_sx_port_get_stats64(struct net_device *dev,
350*4882a593Smuzhiyun 			  struct rtnl_link_stats64 *stats)
351*4882a593Smuzhiyun {
352*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
353*4882a593Smuzhiyun 	struct mlxsw_sx_port_pcpu_stats *p;
354*4882a593Smuzhiyun 	u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
355*4882a593Smuzhiyun 	u32 tx_dropped = 0;
356*4882a593Smuzhiyun 	unsigned int start;
357*4882a593Smuzhiyun 	int i;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	for_each_possible_cpu(i) {
360*4882a593Smuzhiyun 		p = per_cpu_ptr(mlxsw_sx_port->pcpu_stats, i);
361*4882a593Smuzhiyun 		do {
362*4882a593Smuzhiyun 			start = u64_stats_fetch_begin_irq(&p->syncp);
363*4882a593Smuzhiyun 			rx_packets	= p->rx_packets;
364*4882a593Smuzhiyun 			rx_bytes	= p->rx_bytes;
365*4882a593Smuzhiyun 			tx_packets	= p->tx_packets;
366*4882a593Smuzhiyun 			tx_bytes	= p->tx_bytes;
367*4882a593Smuzhiyun 		} while (u64_stats_fetch_retry_irq(&p->syncp, start));
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 		stats->rx_packets	+= rx_packets;
370*4882a593Smuzhiyun 		stats->rx_bytes		+= rx_bytes;
371*4882a593Smuzhiyun 		stats->tx_packets	+= tx_packets;
372*4882a593Smuzhiyun 		stats->tx_bytes		+= tx_bytes;
373*4882a593Smuzhiyun 		/* tx_dropped is u32, updated without syncp protection. */
374*4882a593Smuzhiyun 		tx_dropped	+= p->tx_dropped;
375*4882a593Smuzhiyun 	}
376*4882a593Smuzhiyun 	stats->tx_dropped	= tx_dropped;
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun static struct devlink_port *
mlxsw_sx_port_get_devlink_port(struct net_device * dev)380*4882a593Smuzhiyun mlxsw_sx_port_get_devlink_port(struct net_device *dev)
381*4882a593Smuzhiyun {
382*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
383*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	return mlxsw_core_port_devlink_port_get(mlxsw_sx->core,
386*4882a593Smuzhiyun 						mlxsw_sx_port->local_port);
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun static const struct net_device_ops mlxsw_sx_port_netdev_ops = {
390*4882a593Smuzhiyun 	.ndo_open		= mlxsw_sx_port_open,
391*4882a593Smuzhiyun 	.ndo_stop		= mlxsw_sx_port_stop,
392*4882a593Smuzhiyun 	.ndo_start_xmit		= mlxsw_sx_port_xmit,
393*4882a593Smuzhiyun 	.ndo_change_mtu		= mlxsw_sx_port_change_mtu,
394*4882a593Smuzhiyun 	.ndo_get_stats64	= mlxsw_sx_port_get_stats64,
395*4882a593Smuzhiyun 	.ndo_get_devlink_port	= mlxsw_sx_port_get_devlink_port,
396*4882a593Smuzhiyun };
397*4882a593Smuzhiyun 
mlxsw_sx_port_get_drvinfo(struct net_device * dev,struct ethtool_drvinfo * drvinfo)398*4882a593Smuzhiyun static void mlxsw_sx_port_get_drvinfo(struct net_device *dev,
399*4882a593Smuzhiyun 				      struct ethtool_drvinfo *drvinfo)
400*4882a593Smuzhiyun {
401*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
402*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	strlcpy(drvinfo->driver, mlxsw_sx_driver_name, sizeof(drvinfo->driver));
405*4882a593Smuzhiyun 	strlcpy(drvinfo->version, mlxsw_sx_driver_version,
406*4882a593Smuzhiyun 		sizeof(drvinfo->version));
407*4882a593Smuzhiyun 	snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
408*4882a593Smuzhiyun 		 "%d.%d.%d",
409*4882a593Smuzhiyun 		 mlxsw_sx->bus_info->fw_rev.major,
410*4882a593Smuzhiyun 		 mlxsw_sx->bus_info->fw_rev.minor,
411*4882a593Smuzhiyun 		 mlxsw_sx->bus_info->fw_rev.subminor);
412*4882a593Smuzhiyun 	strlcpy(drvinfo->bus_info, mlxsw_sx->bus_info->device_name,
413*4882a593Smuzhiyun 		sizeof(drvinfo->bus_info));
414*4882a593Smuzhiyun }
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun struct mlxsw_sx_port_hw_stats {
417*4882a593Smuzhiyun 	char str[ETH_GSTRING_LEN];
418*4882a593Smuzhiyun 	u64 (*getter)(const char *payload);
419*4882a593Smuzhiyun };
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun static const struct mlxsw_sx_port_hw_stats mlxsw_sx_port_hw_stats[] = {
422*4882a593Smuzhiyun 	{
423*4882a593Smuzhiyun 		.str = "a_frames_transmitted_ok",
424*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_frames_transmitted_ok_get,
425*4882a593Smuzhiyun 	},
426*4882a593Smuzhiyun 	{
427*4882a593Smuzhiyun 		.str = "a_frames_received_ok",
428*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_frames_received_ok_get,
429*4882a593Smuzhiyun 	},
430*4882a593Smuzhiyun 	{
431*4882a593Smuzhiyun 		.str = "a_frame_check_sequence_errors",
432*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get,
433*4882a593Smuzhiyun 	},
434*4882a593Smuzhiyun 	{
435*4882a593Smuzhiyun 		.str = "a_alignment_errors",
436*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_alignment_errors_get,
437*4882a593Smuzhiyun 	},
438*4882a593Smuzhiyun 	{
439*4882a593Smuzhiyun 		.str = "a_octets_transmitted_ok",
440*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_octets_transmitted_ok_get,
441*4882a593Smuzhiyun 	},
442*4882a593Smuzhiyun 	{
443*4882a593Smuzhiyun 		.str = "a_octets_received_ok",
444*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_octets_received_ok_get,
445*4882a593Smuzhiyun 	},
446*4882a593Smuzhiyun 	{
447*4882a593Smuzhiyun 		.str = "a_multicast_frames_xmitted_ok",
448*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_multicast_frames_xmitted_ok_get,
449*4882a593Smuzhiyun 	},
450*4882a593Smuzhiyun 	{
451*4882a593Smuzhiyun 		.str = "a_broadcast_frames_xmitted_ok",
452*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_broadcast_frames_xmitted_ok_get,
453*4882a593Smuzhiyun 	},
454*4882a593Smuzhiyun 	{
455*4882a593Smuzhiyun 		.str = "a_multicast_frames_received_ok",
456*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get,
457*4882a593Smuzhiyun 	},
458*4882a593Smuzhiyun 	{
459*4882a593Smuzhiyun 		.str = "a_broadcast_frames_received_ok",
460*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_broadcast_frames_received_ok_get,
461*4882a593Smuzhiyun 	},
462*4882a593Smuzhiyun 	{
463*4882a593Smuzhiyun 		.str = "a_in_range_length_errors",
464*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_in_range_length_errors_get,
465*4882a593Smuzhiyun 	},
466*4882a593Smuzhiyun 	{
467*4882a593Smuzhiyun 		.str = "a_out_of_range_length_field",
468*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_out_of_range_length_field_get,
469*4882a593Smuzhiyun 	},
470*4882a593Smuzhiyun 	{
471*4882a593Smuzhiyun 		.str = "a_frame_too_long_errors",
472*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_frame_too_long_errors_get,
473*4882a593Smuzhiyun 	},
474*4882a593Smuzhiyun 	{
475*4882a593Smuzhiyun 		.str = "a_symbol_error_during_carrier",
476*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_symbol_error_during_carrier_get,
477*4882a593Smuzhiyun 	},
478*4882a593Smuzhiyun 	{
479*4882a593Smuzhiyun 		.str = "a_mac_control_frames_transmitted",
480*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_mac_control_frames_transmitted_get,
481*4882a593Smuzhiyun 	},
482*4882a593Smuzhiyun 	{
483*4882a593Smuzhiyun 		.str = "a_mac_control_frames_received",
484*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_mac_control_frames_received_get,
485*4882a593Smuzhiyun 	},
486*4882a593Smuzhiyun 	{
487*4882a593Smuzhiyun 		.str = "a_unsupported_opcodes_received",
488*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_unsupported_opcodes_received_get,
489*4882a593Smuzhiyun 	},
490*4882a593Smuzhiyun 	{
491*4882a593Smuzhiyun 		.str = "a_pause_mac_ctrl_frames_received",
492*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_received_get,
493*4882a593Smuzhiyun 	},
494*4882a593Smuzhiyun 	{
495*4882a593Smuzhiyun 		.str = "a_pause_mac_ctrl_frames_xmitted",
496*4882a593Smuzhiyun 		.getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_transmitted_get,
497*4882a593Smuzhiyun 	},
498*4882a593Smuzhiyun };
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun #define MLXSW_SX_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sx_port_hw_stats)
501*4882a593Smuzhiyun 
mlxsw_sx_port_get_strings(struct net_device * dev,u32 stringset,u8 * data)502*4882a593Smuzhiyun static void mlxsw_sx_port_get_strings(struct net_device *dev,
503*4882a593Smuzhiyun 				      u32 stringset, u8 *data)
504*4882a593Smuzhiyun {
505*4882a593Smuzhiyun 	u8 *p = data;
506*4882a593Smuzhiyun 	int i;
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 	switch (stringset) {
509*4882a593Smuzhiyun 	case ETH_SS_STATS:
510*4882a593Smuzhiyun 		for (i = 0; i < MLXSW_SX_PORT_HW_STATS_LEN; i++) {
511*4882a593Smuzhiyun 			memcpy(p, mlxsw_sx_port_hw_stats[i].str,
512*4882a593Smuzhiyun 			       ETH_GSTRING_LEN);
513*4882a593Smuzhiyun 			p += ETH_GSTRING_LEN;
514*4882a593Smuzhiyun 		}
515*4882a593Smuzhiyun 		break;
516*4882a593Smuzhiyun 	}
517*4882a593Smuzhiyun }
518*4882a593Smuzhiyun 
mlxsw_sx_port_get_stats(struct net_device * dev,struct ethtool_stats * stats,u64 * data)519*4882a593Smuzhiyun static void mlxsw_sx_port_get_stats(struct net_device *dev,
520*4882a593Smuzhiyun 				    struct ethtool_stats *stats, u64 *data)
521*4882a593Smuzhiyun {
522*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
523*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
524*4882a593Smuzhiyun 	char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
525*4882a593Smuzhiyun 	int i;
526*4882a593Smuzhiyun 	int err;
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sx_port->local_port,
529*4882a593Smuzhiyun 			     MLXSW_REG_PPCNT_IEEE_8023_CNT, 0);
530*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ppcnt), ppcnt_pl);
531*4882a593Smuzhiyun 	for (i = 0; i < MLXSW_SX_PORT_HW_STATS_LEN; i++)
532*4882a593Smuzhiyun 		data[i] = !err ? mlxsw_sx_port_hw_stats[i].getter(ppcnt_pl) : 0;
533*4882a593Smuzhiyun }
534*4882a593Smuzhiyun 
mlxsw_sx_port_get_sset_count(struct net_device * dev,int sset)535*4882a593Smuzhiyun static int mlxsw_sx_port_get_sset_count(struct net_device *dev, int sset)
536*4882a593Smuzhiyun {
537*4882a593Smuzhiyun 	switch (sset) {
538*4882a593Smuzhiyun 	case ETH_SS_STATS:
539*4882a593Smuzhiyun 		return MLXSW_SX_PORT_HW_STATS_LEN;
540*4882a593Smuzhiyun 	default:
541*4882a593Smuzhiyun 		return -EOPNOTSUPP;
542*4882a593Smuzhiyun 	}
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun struct mlxsw_sx_port_link_mode {
546*4882a593Smuzhiyun 	u32 mask;
547*4882a593Smuzhiyun 	u32 supported;
548*4882a593Smuzhiyun 	u32 advertised;
549*4882a593Smuzhiyun 	u32 speed;
550*4882a593Smuzhiyun };
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun static const struct mlxsw_sx_port_link_mode mlxsw_sx_port_link_mode[] = {
553*4882a593Smuzhiyun 	{
554*4882a593Smuzhiyun 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_SGMII |
555*4882a593Smuzhiyun 				  MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX,
556*4882a593Smuzhiyun 		.supported	= SUPPORTED_1000baseKX_Full,
557*4882a593Smuzhiyun 		.advertised	= ADVERTISED_1000baseKX_Full,
558*4882a593Smuzhiyun 		.speed		= 1000,
559*4882a593Smuzhiyun 	},
560*4882a593Smuzhiyun 	{
561*4882a593Smuzhiyun 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CX4 |
562*4882a593Smuzhiyun 				  MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4,
563*4882a593Smuzhiyun 		.supported	= SUPPORTED_10000baseKX4_Full,
564*4882a593Smuzhiyun 		.advertised	= ADVERTISED_10000baseKX4_Full,
565*4882a593Smuzhiyun 		.speed		= 10000,
566*4882a593Smuzhiyun 	},
567*4882a593Smuzhiyun 	{
568*4882a593Smuzhiyun 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
569*4882a593Smuzhiyun 				  MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
570*4882a593Smuzhiyun 				  MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
571*4882a593Smuzhiyun 				  MLXSW_REG_PTYS_ETH_SPEED_10GBASE_ER_LR,
572*4882a593Smuzhiyun 		.supported	= SUPPORTED_10000baseKR_Full,
573*4882a593Smuzhiyun 		.advertised	= ADVERTISED_10000baseKR_Full,
574*4882a593Smuzhiyun 		.speed		= 10000,
575*4882a593Smuzhiyun 	},
576*4882a593Smuzhiyun 	{
577*4882a593Smuzhiyun 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4,
578*4882a593Smuzhiyun 		.supported	= SUPPORTED_40000baseCR4_Full,
579*4882a593Smuzhiyun 		.advertised	= ADVERTISED_40000baseCR4_Full,
580*4882a593Smuzhiyun 		.speed		= 40000,
581*4882a593Smuzhiyun 	},
582*4882a593Smuzhiyun 	{
583*4882a593Smuzhiyun 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4,
584*4882a593Smuzhiyun 		.supported	= SUPPORTED_40000baseKR4_Full,
585*4882a593Smuzhiyun 		.advertised	= ADVERTISED_40000baseKR4_Full,
586*4882a593Smuzhiyun 		.speed		= 40000,
587*4882a593Smuzhiyun 	},
588*4882a593Smuzhiyun 	{
589*4882a593Smuzhiyun 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4,
590*4882a593Smuzhiyun 		.supported	= SUPPORTED_40000baseSR4_Full,
591*4882a593Smuzhiyun 		.advertised	= ADVERTISED_40000baseSR4_Full,
592*4882a593Smuzhiyun 		.speed		= 40000,
593*4882a593Smuzhiyun 	},
594*4882a593Smuzhiyun 	{
595*4882a593Smuzhiyun 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_40GBASE_LR4_ER4,
596*4882a593Smuzhiyun 		.supported	= SUPPORTED_40000baseLR4_Full,
597*4882a593Smuzhiyun 		.advertised	= ADVERTISED_40000baseLR4_Full,
598*4882a593Smuzhiyun 		.speed		= 40000,
599*4882a593Smuzhiyun 	},
600*4882a593Smuzhiyun 	{
601*4882a593Smuzhiyun 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_25GBASE_CR |
602*4882a593Smuzhiyun 				  MLXSW_REG_PTYS_ETH_SPEED_25GBASE_KR |
603*4882a593Smuzhiyun 				  MLXSW_REG_PTYS_ETH_SPEED_25GBASE_SR,
604*4882a593Smuzhiyun 		.speed		= 25000,
605*4882a593Smuzhiyun 	},
606*4882a593Smuzhiyun 	{
607*4882a593Smuzhiyun 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR4 |
608*4882a593Smuzhiyun 				  MLXSW_REG_PTYS_ETH_SPEED_50GBASE_CR2 |
609*4882a593Smuzhiyun 				  MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR2,
610*4882a593Smuzhiyun 		.speed		= 50000,
611*4882a593Smuzhiyun 	},
612*4882a593Smuzhiyun 	{
613*4882a593Smuzhiyun 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4 |
614*4882a593Smuzhiyun 				  MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
615*4882a593Smuzhiyun 				  MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 |
616*4882a593Smuzhiyun 				  MLXSW_REG_PTYS_ETH_SPEED_100GBASE_LR4_ER4,
617*4882a593Smuzhiyun 		.speed		= 100000,
618*4882a593Smuzhiyun 	},
619*4882a593Smuzhiyun };
620*4882a593Smuzhiyun 
621*4882a593Smuzhiyun #define MLXSW_SX_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sx_port_link_mode)
622*4882a593Smuzhiyun #define MLXSW_SX_PORT_BASE_SPEED 10000 /* Mb/s */
623*4882a593Smuzhiyun 
mlxsw_sx_from_ptys_supported_port(u32 ptys_eth_proto)624*4882a593Smuzhiyun static u32 mlxsw_sx_from_ptys_supported_port(u32 ptys_eth_proto)
625*4882a593Smuzhiyun {
626*4882a593Smuzhiyun 	if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
627*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
628*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 |
629*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 |
630*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
631*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_SGMII))
632*4882a593Smuzhiyun 		return SUPPORTED_FIBRE;
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun 	if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
635*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 |
636*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 |
637*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 |
638*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX))
639*4882a593Smuzhiyun 		return SUPPORTED_Backplane;
640*4882a593Smuzhiyun 	return 0;
641*4882a593Smuzhiyun }
642*4882a593Smuzhiyun 
mlxsw_sx_from_ptys_supported_link(u32 ptys_eth_proto)643*4882a593Smuzhiyun static u32 mlxsw_sx_from_ptys_supported_link(u32 ptys_eth_proto)
644*4882a593Smuzhiyun {
645*4882a593Smuzhiyun 	u32 modes = 0;
646*4882a593Smuzhiyun 	int i;
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
649*4882a593Smuzhiyun 		if (ptys_eth_proto & mlxsw_sx_port_link_mode[i].mask)
650*4882a593Smuzhiyun 			modes |= mlxsw_sx_port_link_mode[i].supported;
651*4882a593Smuzhiyun 	}
652*4882a593Smuzhiyun 	return modes;
653*4882a593Smuzhiyun }
654*4882a593Smuzhiyun 
mlxsw_sx_from_ptys_advert_link(u32 ptys_eth_proto)655*4882a593Smuzhiyun static u32 mlxsw_sx_from_ptys_advert_link(u32 ptys_eth_proto)
656*4882a593Smuzhiyun {
657*4882a593Smuzhiyun 	u32 modes = 0;
658*4882a593Smuzhiyun 	int i;
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun 	for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
661*4882a593Smuzhiyun 		if (ptys_eth_proto & mlxsw_sx_port_link_mode[i].mask)
662*4882a593Smuzhiyun 			modes |= mlxsw_sx_port_link_mode[i].advertised;
663*4882a593Smuzhiyun 	}
664*4882a593Smuzhiyun 	return modes;
665*4882a593Smuzhiyun }
666*4882a593Smuzhiyun 
mlxsw_sx_from_ptys_speed_duplex(bool carrier_ok,u32 ptys_eth_proto,struct ethtool_link_ksettings * cmd)667*4882a593Smuzhiyun static void mlxsw_sx_from_ptys_speed_duplex(bool carrier_ok, u32 ptys_eth_proto,
668*4882a593Smuzhiyun 					    struct ethtool_link_ksettings *cmd)
669*4882a593Smuzhiyun {
670*4882a593Smuzhiyun 	u32 speed = SPEED_UNKNOWN;
671*4882a593Smuzhiyun 	u8 duplex = DUPLEX_UNKNOWN;
672*4882a593Smuzhiyun 	int i;
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 	if (!carrier_ok)
675*4882a593Smuzhiyun 		goto out;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
678*4882a593Smuzhiyun 		if (ptys_eth_proto & mlxsw_sx_port_link_mode[i].mask) {
679*4882a593Smuzhiyun 			speed = mlxsw_sx_port_link_mode[i].speed;
680*4882a593Smuzhiyun 			duplex = DUPLEX_FULL;
681*4882a593Smuzhiyun 			break;
682*4882a593Smuzhiyun 		}
683*4882a593Smuzhiyun 	}
684*4882a593Smuzhiyun out:
685*4882a593Smuzhiyun 	cmd->base.speed = speed;
686*4882a593Smuzhiyun 	cmd->base.duplex = duplex;
687*4882a593Smuzhiyun }
688*4882a593Smuzhiyun 
mlxsw_sx_port_connector_port(u32 ptys_eth_proto)689*4882a593Smuzhiyun static u8 mlxsw_sx_port_connector_port(u32 ptys_eth_proto)
690*4882a593Smuzhiyun {
691*4882a593Smuzhiyun 	if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
692*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 |
693*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
694*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_SGMII))
695*4882a593Smuzhiyun 		return PORT_FIBRE;
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
698*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 |
699*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4))
700*4882a593Smuzhiyun 		return PORT_DA;
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun 	if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
703*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 |
704*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 |
705*4882a593Smuzhiyun 			      MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4))
706*4882a593Smuzhiyun 		return PORT_NONE;
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun 	return PORT_OTHER;
709*4882a593Smuzhiyun }
710*4882a593Smuzhiyun 
711*4882a593Smuzhiyun static int
mlxsw_sx_port_get_link_ksettings(struct net_device * dev,struct ethtool_link_ksettings * cmd)712*4882a593Smuzhiyun mlxsw_sx_port_get_link_ksettings(struct net_device *dev,
713*4882a593Smuzhiyun 				 struct ethtool_link_ksettings *cmd)
714*4882a593Smuzhiyun {
715*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
716*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
717*4882a593Smuzhiyun 	char ptys_pl[MLXSW_REG_PTYS_LEN];
718*4882a593Smuzhiyun 	u32 eth_proto_cap;
719*4882a593Smuzhiyun 	u32 eth_proto_admin;
720*4882a593Smuzhiyun 	u32 eth_proto_oper;
721*4882a593Smuzhiyun 	u32 supported, advertising, lp_advertising;
722*4882a593Smuzhiyun 	int err;
723*4882a593Smuzhiyun 
724*4882a593Smuzhiyun 	mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port, 0, false);
725*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
726*4882a593Smuzhiyun 	if (err) {
727*4882a593Smuzhiyun 		netdev_err(dev, "Failed to get proto");
728*4882a593Smuzhiyun 		return err;
729*4882a593Smuzhiyun 	}
730*4882a593Smuzhiyun 	mlxsw_reg_ptys_eth_unpack(ptys_pl, &eth_proto_cap,
731*4882a593Smuzhiyun 				  &eth_proto_admin, &eth_proto_oper);
732*4882a593Smuzhiyun 
733*4882a593Smuzhiyun 	supported = mlxsw_sx_from_ptys_supported_port(eth_proto_cap) |
734*4882a593Smuzhiyun 			 mlxsw_sx_from_ptys_supported_link(eth_proto_cap) |
735*4882a593Smuzhiyun 			 SUPPORTED_Pause | SUPPORTED_Asym_Pause;
736*4882a593Smuzhiyun 	advertising = mlxsw_sx_from_ptys_advert_link(eth_proto_admin);
737*4882a593Smuzhiyun 	mlxsw_sx_from_ptys_speed_duplex(netif_carrier_ok(dev),
738*4882a593Smuzhiyun 					eth_proto_oper, cmd);
739*4882a593Smuzhiyun 
740*4882a593Smuzhiyun 	eth_proto_oper = eth_proto_oper ? eth_proto_oper : eth_proto_cap;
741*4882a593Smuzhiyun 	cmd->base.port = mlxsw_sx_port_connector_port(eth_proto_oper);
742*4882a593Smuzhiyun 	lp_advertising = mlxsw_sx_from_ptys_advert_link(eth_proto_oper);
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun 	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
745*4882a593Smuzhiyun 						supported);
746*4882a593Smuzhiyun 	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
747*4882a593Smuzhiyun 						advertising);
748*4882a593Smuzhiyun 	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
749*4882a593Smuzhiyun 						lp_advertising);
750*4882a593Smuzhiyun 
751*4882a593Smuzhiyun 	return 0;
752*4882a593Smuzhiyun }
753*4882a593Smuzhiyun 
mlxsw_sx_to_ptys_advert_link(u32 advertising)754*4882a593Smuzhiyun static u32 mlxsw_sx_to_ptys_advert_link(u32 advertising)
755*4882a593Smuzhiyun {
756*4882a593Smuzhiyun 	u32 ptys_proto = 0;
757*4882a593Smuzhiyun 	int i;
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun 	for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
760*4882a593Smuzhiyun 		if (advertising & mlxsw_sx_port_link_mode[i].advertised)
761*4882a593Smuzhiyun 			ptys_proto |= mlxsw_sx_port_link_mode[i].mask;
762*4882a593Smuzhiyun 	}
763*4882a593Smuzhiyun 	return ptys_proto;
764*4882a593Smuzhiyun }
765*4882a593Smuzhiyun 
mlxsw_sx_to_ptys_speed(u32 speed)766*4882a593Smuzhiyun static u32 mlxsw_sx_to_ptys_speed(u32 speed)
767*4882a593Smuzhiyun {
768*4882a593Smuzhiyun 	u32 ptys_proto = 0;
769*4882a593Smuzhiyun 	int i;
770*4882a593Smuzhiyun 
771*4882a593Smuzhiyun 	for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
772*4882a593Smuzhiyun 		if (speed == mlxsw_sx_port_link_mode[i].speed)
773*4882a593Smuzhiyun 			ptys_proto |= mlxsw_sx_port_link_mode[i].mask;
774*4882a593Smuzhiyun 	}
775*4882a593Smuzhiyun 	return ptys_proto;
776*4882a593Smuzhiyun }
777*4882a593Smuzhiyun 
mlxsw_sx_to_ptys_upper_speed(u32 upper_speed)778*4882a593Smuzhiyun static u32 mlxsw_sx_to_ptys_upper_speed(u32 upper_speed)
779*4882a593Smuzhiyun {
780*4882a593Smuzhiyun 	u32 ptys_proto = 0;
781*4882a593Smuzhiyun 	int i;
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	for (i = 0; i < MLXSW_SX_PORT_LINK_MODE_LEN; i++) {
784*4882a593Smuzhiyun 		if (mlxsw_sx_port_link_mode[i].speed <= upper_speed)
785*4882a593Smuzhiyun 			ptys_proto |= mlxsw_sx_port_link_mode[i].mask;
786*4882a593Smuzhiyun 	}
787*4882a593Smuzhiyun 	return ptys_proto;
788*4882a593Smuzhiyun }
789*4882a593Smuzhiyun 
790*4882a593Smuzhiyun static int
mlxsw_sx_port_set_link_ksettings(struct net_device * dev,const struct ethtool_link_ksettings * cmd)791*4882a593Smuzhiyun mlxsw_sx_port_set_link_ksettings(struct net_device *dev,
792*4882a593Smuzhiyun 				 const struct ethtool_link_ksettings *cmd)
793*4882a593Smuzhiyun {
794*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
795*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
796*4882a593Smuzhiyun 	char ptys_pl[MLXSW_REG_PTYS_LEN];
797*4882a593Smuzhiyun 	u32 speed;
798*4882a593Smuzhiyun 	u32 eth_proto_new;
799*4882a593Smuzhiyun 	u32 eth_proto_cap;
800*4882a593Smuzhiyun 	u32 eth_proto_admin;
801*4882a593Smuzhiyun 	u32 advertising;
802*4882a593Smuzhiyun 	bool is_up;
803*4882a593Smuzhiyun 	int err;
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 	speed = cmd->base.speed;
806*4882a593Smuzhiyun 
807*4882a593Smuzhiyun 	ethtool_convert_link_mode_to_legacy_u32(&advertising,
808*4882a593Smuzhiyun 						cmd->link_modes.advertising);
809*4882a593Smuzhiyun 
810*4882a593Smuzhiyun 	eth_proto_new = cmd->base.autoneg == AUTONEG_ENABLE ?
811*4882a593Smuzhiyun 		mlxsw_sx_to_ptys_advert_link(advertising) :
812*4882a593Smuzhiyun 		mlxsw_sx_to_ptys_speed(speed);
813*4882a593Smuzhiyun 
814*4882a593Smuzhiyun 	mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port, 0, false);
815*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
816*4882a593Smuzhiyun 	if (err) {
817*4882a593Smuzhiyun 		netdev_err(dev, "Failed to get proto");
818*4882a593Smuzhiyun 		return err;
819*4882a593Smuzhiyun 	}
820*4882a593Smuzhiyun 	mlxsw_reg_ptys_eth_unpack(ptys_pl, &eth_proto_cap, &eth_proto_admin,
821*4882a593Smuzhiyun 				  NULL);
822*4882a593Smuzhiyun 
823*4882a593Smuzhiyun 	eth_proto_new = eth_proto_new & eth_proto_cap;
824*4882a593Smuzhiyun 	if (!eth_proto_new) {
825*4882a593Smuzhiyun 		netdev_err(dev, "Not supported proto admin requested");
826*4882a593Smuzhiyun 		return -EINVAL;
827*4882a593Smuzhiyun 	}
828*4882a593Smuzhiyun 	if (eth_proto_new == eth_proto_admin)
829*4882a593Smuzhiyun 		return 0;
830*4882a593Smuzhiyun 
831*4882a593Smuzhiyun 	mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port,
832*4882a593Smuzhiyun 				eth_proto_new, true);
833*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
834*4882a593Smuzhiyun 	if (err) {
835*4882a593Smuzhiyun 		netdev_err(dev, "Failed to set proto admin");
836*4882a593Smuzhiyun 		return err;
837*4882a593Smuzhiyun 	}
838*4882a593Smuzhiyun 
839*4882a593Smuzhiyun 	err = mlxsw_sx_port_oper_status_get(mlxsw_sx_port, &is_up);
840*4882a593Smuzhiyun 	if (err) {
841*4882a593Smuzhiyun 		netdev_err(dev, "Failed to get oper status");
842*4882a593Smuzhiyun 		return err;
843*4882a593Smuzhiyun 	}
844*4882a593Smuzhiyun 	if (!is_up)
845*4882a593Smuzhiyun 		return 0;
846*4882a593Smuzhiyun 
847*4882a593Smuzhiyun 	err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, false);
848*4882a593Smuzhiyun 	if (err) {
849*4882a593Smuzhiyun 		netdev_err(dev, "Failed to set admin status");
850*4882a593Smuzhiyun 		return err;
851*4882a593Smuzhiyun 	}
852*4882a593Smuzhiyun 
853*4882a593Smuzhiyun 	err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, true);
854*4882a593Smuzhiyun 	if (err) {
855*4882a593Smuzhiyun 		netdev_err(dev, "Failed to set admin status");
856*4882a593Smuzhiyun 		return err;
857*4882a593Smuzhiyun 	}
858*4882a593Smuzhiyun 
859*4882a593Smuzhiyun 	return 0;
860*4882a593Smuzhiyun }
861*4882a593Smuzhiyun 
862*4882a593Smuzhiyun static const struct ethtool_ops mlxsw_sx_port_ethtool_ops = {
863*4882a593Smuzhiyun 	.get_drvinfo		= mlxsw_sx_port_get_drvinfo,
864*4882a593Smuzhiyun 	.get_link		= ethtool_op_get_link,
865*4882a593Smuzhiyun 	.get_strings		= mlxsw_sx_port_get_strings,
866*4882a593Smuzhiyun 	.get_ethtool_stats	= mlxsw_sx_port_get_stats,
867*4882a593Smuzhiyun 	.get_sset_count		= mlxsw_sx_port_get_sset_count,
868*4882a593Smuzhiyun 	.get_link_ksettings	= mlxsw_sx_port_get_link_ksettings,
869*4882a593Smuzhiyun 	.set_link_ksettings	= mlxsw_sx_port_set_link_ksettings,
870*4882a593Smuzhiyun };
871*4882a593Smuzhiyun 
mlxsw_sx_hw_id_get(struct mlxsw_sx * mlxsw_sx)872*4882a593Smuzhiyun static int mlxsw_sx_hw_id_get(struct mlxsw_sx *mlxsw_sx)
873*4882a593Smuzhiyun {
874*4882a593Smuzhiyun 	char spad_pl[MLXSW_REG_SPAD_LEN] = {0};
875*4882a593Smuzhiyun 	int err;
876*4882a593Smuzhiyun 
877*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(spad), spad_pl);
878*4882a593Smuzhiyun 	if (err)
879*4882a593Smuzhiyun 		return err;
880*4882a593Smuzhiyun 	mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_sx->hw_id);
881*4882a593Smuzhiyun 	return 0;
882*4882a593Smuzhiyun }
883*4882a593Smuzhiyun 
mlxsw_sx_port_dev_addr_get(struct mlxsw_sx_port * mlxsw_sx_port)884*4882a593Smuzhiyun static int mlxsw_sx_port_dev_addr_get(struct mlxsw_sx_port *mlxsw_sx_port)
885*4882a593Smuzhiyun {
886*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
887*4882a593Smuzhiyun 	struct net_device *dev = mlxsw_sx_port->dev;
888*4882a593Smuzhiyun 	char ppad_pl[MLXSW_REG_PPAD_LEN];
889*4882a593Smuzhiyun 	int err;
890*4882a593Smuzhiyun 
891*4882a593Smuzhiyun 	mlxsw_reg_ppad_pack(ppad_pl, false, 0);
892*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sx->core, MLXSW_REG(ppad), ppad_pl);
893*4882a593Smuzhiyun 	if (err)
894*4882a593Smuzhiyun 		return err;
895*4882a593Smuzhiyun 	mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, dev->dev_addr);
896*4882a593Smuzhiyun 	/* The last byte value in base mac address is guaranteed
897*4882a593Smuzhiyun 	 * to be such it does not overflow when adding local_port
898*4882a593Smuzhiyun 	 * value.
899*4882a593Smuzhiyun 	 */
900*4882a593Smuzhiyun 	dev->dev_addr[ETH_ALEN - 1] += mlxsw_sx_port->local_port;
901*4882a593Smuzhiyun 	return 0;
902*4882a593Smuzhiyun }
903*4882a593Smuzhiyun 
mlxsw_sx_port_stp_state_set(struct mlxsw_sx_port * mlxsw_sx_port,u16 vid,enum mlxsw_reg_spms_state state)904*4882a593Smuzhiyun static int mlxsw_sx_port_stp_state_set(struct mlxsw_sx_port *mlxsw_sx_port,
905*4882a593Smuzhiyun 				       u16 vid, enum mlxsw_reg_spms_state state)
906*4882a593Smuzhiyun {
907*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
908*4882a593Smuzhiyun 	char *spms_pl;
909*4882a593Smuzhiyun 	int err;
910*4882a593Smuzhiyun 
911*4882a593Smuzhiyun 	spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL);
912*4882a593Smuzhiyun 	if (!spms_pl)
913*4882a593Smuzhiyun 		return -ENOMEM;
914*4882a593Smuzhiyun 	mlxsw_reg_spms_pack(spms_pl, mlxsw_sx_port->local_port);
915*4882a593Smuzhiyun 	mlxsw_reg_spms_vid_pack(spms_pl, vid, state);
916*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(spms), spms_pl);
917*4882a593Smuzhiyun 	kfree(spms_pl);
918*4882a593Smuzhiyun 	return err;
919*4882a593Smuzhiyun }
920*4882a593Smuzhiyun 
mlxsw_sx_port_ib_speed_set(struct mlxsw_sx_port * mlxsw_sx_port,u16 speed,u16 width)921*4882a593Smuzhiyun static int mlxsw_sx_port_ib_speed_set(struct mlxsw_sx_port *mlxsw_sx_port,
922*4882a593Smuzhiyun 				      u16 speed, u16 width)
923*4882a593Smuzhiyun {
924*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
925*4882a593Smuzhiyun 	char ptys_pl[MLXSW_REG_PTYS_LEN];
926*4882a593Smuzhiyun 
927*4882a593Smuzhiyun 	mlxsw_reg_ptys_ib_pack(ptys_pl, mlxsw_sx_port->local_port, speed,
928*4882a593Smuzhiyun 			       width);
929*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
930*4882a593Smuzhiyun }
931*4882a593Smuzhiyun 
932*4882a593Smuzhiyun static int
mlxsw_sx_port_speed_by_width_set(struct mlxsw_sx_port * mlxsw_sx_port,u8 width)933*4882a593Smuzhiyun mlxsw_sx_port_speed_by_width_set(struct mlxsw_sx_port *mlxsw_sx_port, u8 width)
934*4882a593Smuzhiyun {
935*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
936*4882a593Smuzhiyun 	u32 upper_speed = MLXSW_SX_PORT_BASE_SPEED * width;
937*4882a593Smuzhiyun 	char ptys_pl[MLXSW_REG_PTYS_LEN];
938*4882a593Smuzhiyun 	u32 eth_proto_admin;
939*4882a593Smuzhiyun 
940*4882a593Smuzhiyun 	eth_proto_admin = mlxsw_sx_to_ptys_upper_speed(upper_speed);
941*4882a593Smuzhiyun 	mlxsw_reg_ptys_eth_pack(ptys_pl, mlxsw_sx_port->local_port,
942*4882a593Smuzhiyun 				eth_proto_admin, true);
943*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(ptys), ptys_pl);
944*4882a593Smuzhiyun }
945*4882a593Smuzhiyun 
946*4882a593Smuzhiyun static int
mlxsw_sx_port_mac_learning_mode_set(struct mlxsw_sx_port * mlxsw_sx_port,enum mlxsw_reg_spmlr_learn_mode mode)947*4882a593Smuzhiyun mlxsw_sx_port_mac_learning_mode_set(struct mlxsw_sx_port *mlxsw_sx_port,
948*4882a593Smuzhiyun 				    enum mlxsw_reg_spmlr_learn_mode mode)
949*4882a593Smuzhiyun {
950*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx;
951*4882a593Smuzhiyun 	char spmlr_pl[MLXSW_REG_SPMLR_LEN];
952*4882a593Smuzhiyun 
953*4882a593Smuzhiyun 	mlxsw_reg_spmlr_pack(spmlr_pl, mlxsw_sx_port->local_port, mode);
954*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(spmlr), spmlr_pl);
955*4882a593Smuzhiyun }
956*4882a593Smuzhiyun 
__mlxsw_sx_port_eth_create(struct mlxsw_sx * mlxsw_sx,u8 local_port,u8 module,u8 width)957*4882a593Smuzhiyun static int __mlxsw_sx_port_eth_create(struct mlxsw_sx *mlxsw_sx, u8 local_port,
958*4882a593Smuzhiyun 				      u8 module, u8 width)
959*4882a593Smuzhiyun {
960*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port;
961*4882a593Smuzhiyun 	struct net_device *dev;
962*4882a593Smuzhiyun 	int err;
963*4882a593Smuzhiyun 
964*4882a593Smuzhiyun 	dev = alloc_etherdev(sizeof(struct mlxsw_sx_port));
965*4882a593Smuzhiyun 	if (!dev)
966*4882a593Smuzhiyun 		return -ENOMEM;
967*4882a593Smuzhiyun 	SET_NETDEV_DEV(dev, mlxsw_sx->bus_info->dev);
968*4882a593Smuzhiyun 	dev_net_set(dev, mlxsw_core_net(mlxsw_sx->core));
969*4882a593Smuzhiyun 	mlxsw_sx_port = netdev_priv(dev);
970*4882a593Smuzhiyun 	mlxsw_sx_port->dev = dev;
971*4882a593Smuzhiyun 	mlxsw_sx_port->mlxsw_sx = mlxsw_sx;
972*4882a593Smuzhiyun 	mlxsw_sx_port->local_port = local_port;
973*4882a593Smuzhiyun 	mlxsw_sx_port->mapping.module = module;
974*4882a593Smuzhiyun 
975*4882a593Smuzhiyun 	mlxsw_sx_port->pcpu_stats =
976*4882a593Smuzhiyun 		netdev_alloc_pcpu_stats(struct mlxsw_sx_port_pcpu_stats);
977*4882a593Smuzhiyun 	if (!mlxsw_sx_port->pcpu_stats) {
978*4882a593Smuzhiyun 		err = -ENOMEM;
979*4882a593Smuzhiyun 		goto err_alloc_stats;
980*4882a593Smuzhiyun 	}
981*4882a593Smuzhiyun 
982*4882a593Smuzhiyun 	dev->netdev_ops = &mlxsw_sx_port_netdev_ops;
983*4882a593Smuzhiyun 	dev->ethtool_ops = &mlxsw_sx_port_ethtool_ops;
984*4882a593Smuzhiyun 
985*4882a593Smuzhiyun 	err = mlxsw_sx_port_dev_addr_get(mlxsw_sx_port);
986*4882a593Smuzhiyun 	if (err) {
987*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Unable to get port mac address\n",
988*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
989*4882a593Smuzhiyun 		goto err_dev_addr_get;
990*4882a593Smuzhiyun 	}
991*4882a593Smuzhiyun 
992*4882a593Smuzhiyun 	netif_carrier_off(dev);
993*4882a593Smuzhiyun 
994*4882a593Smuzhiyun 	dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_LLTX | NETIF_F_SG |
995*4882a593Smuzhiyun 			 NETIF_F_VLAN_CHALLENGED;
996*4882a593Smuzhiyun 
997*4882a593Smuzhiyun 	dev->min_mtu = 0;
998*4882a593Smuzhiyun 	dev->max_mtu = ETH_MAX_MTU;
999*4882a593Smuzhiyun 
1000*4882a593Smuzhiyun 	/* Each packet needs to have a Tx header (metadata) on top all other
1001*4882a593Smuzhiyun 	 * headers.
1002*4882a593Smuzhiyun 	 */
1003*4882a593Smuzhiyun 	dev->needed_headroom = MLXSW_TXHDR_LEN;
1004*4882a593Smuzhiyun 
1005*4882a593Smuzhiyun 	err = mlxsw_sx_port_system_port_mapping_set(mlxsw_sx_port);
1006*4882a593Smuzhiyun 	if (err) {
1007*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set system port mapping\n",
1008*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
1009*4882a593Smuzhiyun 		goto err_port_system_port_mapping_set;
1010*4882a593Smuzhiyun 	}
1011*4882a593Smuzhiyun 
1012*4882a593Smuzhiyun 	err = mlxsw_sx_port_swid_set(mlxsw_sx_port, 0);
1013*4882a593Smuzhiyun 	if (err) {
1014*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set SWID\n",
1015*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
1016*4882a593Smuzhiyun 		goto err_port_swid_set;
1017*4882a593Smuzhiyun 	}
1018*4882a593Smuzhiyun 
1019*4882a593Smuzhiyun 	err = mlxsw_sx_port_speed_by_width_set(mlxsw_sx_port, width);
1020*4882a593Smuzhiyun 	if (err) {
1021*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set speed\n",
1022*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
1023*4882a593Smuzhiyun 		goto err_port_speed_set;
1024*4882a593Smuzhiyun 	}
1025*4882a593Smuzhiyun 
1026*4882a593Smuzhiyun 	err = mlxsw_sx_port_mtu_eth_set(mlxsw_sx_port, ETH_DATA_LEN);
1027*4882a593Smuzhiyun 	if (err) {
1028*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set MTU\n",
1029*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
1030*4882a593Smuzhiyun 		goto err_port_mtu_set;
1031*4882a593Smuzhiyun 	}
1032*4882a593Smuzhiyun 
1033*4882a593Smuzhiyun 	err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, false);
1034*4882a593Smuzhiyun 	if (err)
1035*4882a593Smuzhiyun 		goto err_port_admin_status_set;
1036*4882a593Smuzhiyun 
1037*4882a593Smuzhiyun 	err = mlxsw_sx_port_stp_state_set(mlxsw_sx_port,
1038*4882a593Smuzhiyun 					  MLXSW_PORT_DEFAULT_VID,
1039*4882a593Smuzhiyun 					  MLXSW_REG_SPMS_STATE_FORWARDING);
1040*4882a593Smuzhiyun 	if (err) {
1041*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set STP state\n",
1042*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
1043*4882a593Smuzhiyun 		goto err_port_stp_state_set;
1044*4882a593Smuzhiyun 	}
1045*4882a593Smuzhiyun 
1046*4882a593Smuzhiyun 	err = mlxsw_sx_port_mac_learning_mode_set(mlxsw_sx_port,
1047*4882a593Smuzhiyun 						  MLXSW_REG_SPMLR_LEARN_MODE_DISABLE);
1048*4882a593Smuzhiyun 	if (err) {
1049*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set MAC learning mode\n",
1050*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
1051*4882a593Smuzhiyun 		goto err_port_mac_learning_mode_set;
1052*4882a593Smuzhiyun 	}
1053*4882a593Smuzhiyun 
1054*4882a593Smuzhiyun 	err = register_netdev(dev);
1055*4882a593Smuzhiyun 	if (err) {
1056*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to register netdev\n",
1057*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
1058*4882a593Smuzhiyun 		goto err_register_netdev;
1059*4882a593Smuzhiyun 	}
1060*4882a593Smuzhiyun 
1061*4882a593Smuzhiyun 	mlxsw_core_port_eth_set(mlxsw_sx->core, mlxsw_sx_port->local_port,
1062*4882a593Smuzhiyun 				mlxsw_sx_port, dev);
1063*4882a593Smuzhiyun 	mlxsw_sx->ports[local_port] = mlxsw_sx_port;
1064*4882a593Smuzhiyun 	return 0;
1065*4882a593Smuzhiyun 
1066*4882a593Smuzhiyun err_register_netdev:
1067*4882a593Smuzhiyun err_port_mac_learning_mode_set:
1068*4882a593Smuzhiyun err_port_stp_state_set:
1069*4882a593Smuzhiyun err_port_admin_status_set:
1070*4882a593Smuzhiyun err_port_mtu_set:
1071*4882a593Smuzhiyun err_port_speed_set:
1072*4882a593Smuzhiyun 	mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT);
1073*4882a593Smuzhiyun err_port_swid_set:
1074*4882a593Smuzhiyun err_port_system_port_mapping_set:
1075*4882a593Smuzhiyun err_dev_addr_get:
1076*4882a593Smuzhiyun 	free_percpu(mlxsw_sx_port->pcpu_stats);
1077*4882a593Smuzhiyun err_alloc_stats:
1078*4882a593Smuzhiyun 	free_netdev(dev);
1079*4882a593Smuzhiyun 	return err;
1080*4882a593Smuzhiyun }
1081*4882a593Smuzhiyun 
mlxsw_sx_port_eth_create(struct mlxsw_sx * mlxsw_sx,u8 local_port,u8 module,u8 width)1082*4882a593Smuzhiyun static int mlxsw_sx_port_eth_create(struct mlxsw_sx *mlxsw_sx, u8 local_port,
1083*4882a593Smuzhiyun 				    u8 module, u8 width)
1084*4882a593Smuzhiyun {
1085*4882a593Smuzhiyun 	int err;
1086*4882a593Smuzhiyun 
1087*4882a593Smuzhiyun 	err = mlxsw_core_port_init(mlxsw_sx->core, local_port,
1088*4882a593Smuzhiyun 				   module + 1, false, 0, false, 0,
1089*4882a593Smuzhiyun 				   mlxsw_sx->hw_id, sizeof(mlxsw_sx->hw_id));
1090*4882a593Smuzhiyun 	if (err) {
1091*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to init core port\n",
1092*4882a593Smuzhiyun 			local_port);
1093*4882a593Smuzhiyun 		return err;
1094*4882a593Smuzhiyun 	}
1095*4882a593Smuzhiyun 	err = __mlxsw_sx_port_eth_create(mlxsw_sx, local_port, module, width);
1096*4882a593Smuzhiyun 	if (err)
1097*4882a593Smuzhiyun 		goto err_port_create;
1098*4882a593Smuzhiyun 
1099*4882a593Smuzhiyun 	return 0;
1100*4882a593Smuzhiyun 
1101*4882a593Smuzhiyun err_port_create:
1102*4882a593Smuzhiyun 	mlxsw_core_port_fini(mlxsw_sx->core, local_port);
1103*4882a593Smuzhiyun 	return err;
1104*4882a593Smuzhiyun }
1105*4882a593Smuzhiyun 
__mlxsw_sx_port_eth_remove(struct mlxsw_sx * mlxsw_sx,u8 local_port)1106*4882a593Smuzhiyun static void __mlxsw_sx_port_eth_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port)
1107*4882a593Smuzhiyun {
1108*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port = mlxsw_sx->ports[local_port];
1109*4882a593Smuzhiyun 
1110*4882a593Smuzhiyun 	mlxsw_core_port_clear(mlxsw_sx->core, local_port, mlxsw_sx);
1111*4882a593Smuzhiyun 	unregister_netdev(mlxsw_sx_port->dev); /* This calls ndo_stop */
1112*4882a593Smuzhiyun 	mlxsw_sx->ports[local_port] = NULL;
1113*4882a593Smuzhiyun 	mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT);
1114*4882a593Smuzhiyun 	free_percpu(mlxsw_sx_port->pcpu_stats);
1115*4882a593Smuzhiyun 	free_netdev(mlxsw_sx_port->dev);
1116*4882a593Smuzhiyun }
1117*4882a593Smuzhiyun 
mlxsw_sx_port_created(struct mlxsw_sx * mlxsw_sx,u8 local_port)1118*4882a593Smuzhiyun static bool mlxsw_sx_port_created(struct mlxsw_sx *mlxsw_sx, u8 local_port)
1119*4882a593Smuzhiyun {
1120*4882a593Smuzhiyun 	return mlxsw_sx->ports[local_port] != NULL;
1121*4882a593Smuzhiyun }
1122*4882a593Smuzhiyun 
__mlxsw_sx_port_ib_create(struct mlxsw_sx * mlxsw_sx,u8 local_port,u8 module,u8 width)1123*4882a593Smuzhiyun static int __mlxsw_sx_port_ib_create(struct mlxsw_sx *mlxsw_sx, u8 local_port,
1124*4882a593Smuzhiyun 				     u8 module, u8 width)
1125*4882a593Smuzhiyun {
1126*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port;
1127*4882a593Smuzhiyun 	int err;
1128*4882a593Smuzhiyun 
1129*4882a593Smuzhiyun 	mlxsw_sx_port = kzalloc(sizeof(*mlxsw_sx_port), GFP_KERNEL);
1130*4882a593Smuzhiyun 	if (!mlxsw_sx_port)
1131*4882a593Smuzhiyun 		return -ENOMEM;
1132*4882a593Smuzhiyun 	mlxsw_sx_port->mlxsw_sx = mlxsw_sx;
1133*4882a593Smuzhiyun 	mlxsw_sx_port->local_port = local_port;
1134*4882a593Smuzhiyun 	mlxsw_sx_port->mapping.module = module;
1135*4882a593Smuzhiyun 
1136*4882a593Smuzhiyun 	err = mlxsw_sx_port_system_port_mapping_set(mlxsw_sx_port);
1137*4882a593Smuzhiyun 	if (err) {
1138*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set system port mapping\n",
1139*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
1140*4882a593Smuzhiyun 		goto err_port_system_port_mapping_set;
1141*4882a593Smuzhiyun 	}
1142*4882a593Smuzhiyun 
1143*4882a593Smuzhiyun 	/* Adding port to Infiniband swid (1) */
1144*4882a593Smuzhiyun 	err = mlxsw_sx_port_swid_set(mlxsw_sx_port, 1);
1145*4882a593Smuzhiyun 	if (err) {
1146*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set SWID\n",
1147*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
1148*4882a593Smuzhiyun 		goto err_port_swid_set;
1149*4882a593Smuzhiyun 	}
1150*4882a593Smuzhiyun 
1151*4882a593Smuzhiyun 	/* Expose the IB port number as it's front panel name */
1152*4882a593Smuzhiyun 	err = mlxsw_sx_port_ib_port_set(mlxsw_sx_port, module + 1);
1153*4882a593Smuzhiyun 	if (err) {
1154*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set IB port\n",
1155*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
1156*4882a593Smuzhiyun 		goto err_port_ib_set;
1157*4882a593Smuzhiyun 	}
1158*4882a593Smuzhiyun 
1159*4882a593Smuzhiyun 	/* Supports all speeds from SDR to FDR (bitmask) and support bus width
1160*4882a593Smuzhiyun 	 * of 1x, 2x and 4x (3 bits bitmask)
1161*4882a593Smuzhiyun 	 */
1162*4882a593Smuzhiyun 	err = mlxsw_sx_port_ib_speed_set(mlxsw_sx_port,
1163*4882a593Smuzhiyun 					 MLXSW_REG_PTYS_IB_SPEED_EDR - 1,
1164*4882a593Smuzhiyun 					 BIT(3) - 1);
1165*4882a593Smuzhiyun 	if (err) {
1166*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set speed\n",
1167*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
1168*4882a593Smuzhiyun 		goto err_port_speed_set;
1169*4882a593Smuzhiyun 	}
1170*4882a593Smuzhiyun 
1171*4882a593Smuzhiyun 	/* Change to the maximum MTU the device supports, the SMA will take
1172*4882a593Smuzhiyun 	 * care of the active MTU
1173*4882a593Smuzhiyun 	 */
1174*4882a593Smuzhiyun 	err = mlxsw_sx_port_mtu_ib_set(mlxsw_sx_port, MLXSW_IB_DEFAULT_MTU);
1175*4882a593Smuzhiyun 	if (err) {
1176*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to set MTU\n",
1177*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
1178*4882a593Smuzhiyun 		goto err_port_mtu_set;
1179*4882a593Smuzhiyun 	}
1180*4882a593Smuzhiyun 
1181*4882a593Smuzhiyun 	err = mlxsw_sx_port_admin_status_set(mlxsw_sx_port, true);
1182*4882a593Smuzhiyun 	if (err) {
1183*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to change admin state to UP\n",
1184*4882a593Smuzhiyun 			mlxsw_sx_port->local_port);
1185*4882a593Smuzhiyun 		goto err_port_admin_set;
1186*4882a593Smuzhiyun 	}
1187*4882a593Smuzhiyun 
1188*4882a593Smuzhiyun 	mlxsw_core_port_ib_set(mlxsw_sx->core, mlxsw_sx_port->local_port,
1189*4882a593Smuzhiyun 			       mlxsw_sx_port);
1190*4882a593Smuzhiyun 	mlxsw_sx->ports[local_port] = mlxsw_sx_port;
1191*4882a593Smuzhiyun 	return 0;
1192*4882a593Smuzhiyun 
1193*4882a593Smuzhiyun err_port_admin_set:
1194*4882a593Smuzhiyun err_port_mtu_set:
1195*4882a593Smuzhiyun err_port_speed_set:
1196*4882a593Smuzhiyun err_port_ib_set:
1197*4882a593Smuzhiyun 	mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT);
1198*4882a593Smuzhiyun err_port_swid_set:
1199*4882a593Smuzhiyun err_port_system_port_mapping_set:
1200*4882a593Smuzhiyun 	kfree(mlxsw_sx_port);
1201*4882a593Smuzhiyun 	return err;
1202*4882a593Smuzhiyun }
1203*4882a593Smuzhiyun 
__mlxsw_sx_port_ib_remove(struct mlxsw_sx * mlxsw_sx,u8 local_port)1204*4882a593Smuzhiyun static void __mlxsw_sx_port_ib_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port)
1205*4882a593Smuzhiyun {
1206*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port = mlxsw_sx->ports[local_port];
1207*4882a593Smuzhiyun 
1208*4882a593Smuzhiyun 	mlxsw_core_port_clear(mlxsw_sx->core, local_port, mlxsw_sx);
1209*4882a593Smuzhiyun 	mlxsw_sx->ports[local_port] = NULL;
1210*4882a593Smuzhiyun 	mlxsw_sx_port_admin_status_set(mlxsw_sx_port, false);
1211*4882a593Smuzhiyun 	mlxsw_sx_port_swid_set(mlxsw_sx_port, MLXSW_PORT_SWID_DISABLED_PORT);
1212*4882a593Smuzhiyun 	kfree(mlxsw_sx_port);
1213*4882a593Smuzhiyun }
1214*4882a593Smuzhiyun 
__mlxsw_sx_port_remove(struct mlxsw_sx * mlxsw_sx,u8 local_port)1215*4882a593Smuzhiyun static void __mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port)
1216*4882a593Smuzhiyun {
1217*4882a593Smuzhiyun 	enum devlink_port_type port_type =
1218*4882a593Smuzhiyun 		mlxsw_core_port_type_get(mlxsw_sx->core, local_port);
1219*4882a593Smuzhiyun 
1220*4882a593Smuzhiyun 	if (port_type == DEVLINK_PORT_TYPE_ETH)
1221*4882a593Smuzhiyun 		__mlxsw_sx_port_eth_remove(mlxsw_sx, local_port);
1222*4882a593Smuzhiyun 	else if (port_type == DEVLINK_PORT_TYPE_IB)
1223*4882a593Smuzhiyun 		__mlxsw_sx_port_ib_remove(mlxsw_sx, local_port);
1224*4882a593Smuzhiyun }
1225*4882a593Smuzhiyun 
mlxsw_sx_port_remove(struct mlxsw_sx * mlxsw_sx,u8 local_port)1226*4882a593Smuzhiyun static void mlxsw_sx_port_remove(struct mlxsw_sx *mlxsw_sx, u8 local_port)
1227*4882a593Smuzhiyun {
1228*4882a593Smuzhiyun 	__mlxsw_sx_port_remove(mlxsw_sx, local_port);
1229*4882a593Smuzhiyun 	mlxsw_core_port_fini(mlxsw_sx->core, local_port);
1230*4882a593Smuzhiyun }
1231*4882a593Smuzhiyun 
mlxsw_sx_ports_remove(struct mlxsw_sx * mlxsw_sx)1232*4882a593Smuzhiyun static void mlxsw_sx_ports_remove(struct mlxsw_sx *mlxsw_sx)
1233*4882a593Smuzhiyun {
1234*4882a593Smuzhiyun 	int i;
1235*4882a593Smuzhiyun 
1236*4882a593Smuzhiyun 	for (i = 1; i < mlxsw_core_max_ports(mlxsw_sx->core); i++)
1237*4882a593Smuzhiyun 		if (mlxsw_sx_port_created(mlxsw_sx, i))
1238*4882a593Smuzhiyun 			mlxsw_sx_port_remove(mlxsw_sx, i);
1239*4882a593Smuzhiyun 	kfree(mlxsw_sx->ports);
1240*4882a593Smuzhiyun 	mlxsw_sx->ports = NULL;
1241*4882a593Smuzhiyun }
1242*4882a593Smuzhiyun 
mlxsw_sx_ports_create(struct mlxsw_sx * mlxsw_sx)1243*4882a593Smuzhiyun static int mlxsw_sx_ports_create(struct mlxsw_sx *mlxsw_sx)
1244*4882a593Smuzhiyun {
1245*4882a593Smuzhiyun 	unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sx->core);
1246*4882a593Smuzhiyun 	size_t alloc_size;
1247*4882a593Smuzhiyun 	u8 module, width;
1248*4882a593Smuzhiyun 	int i;
1249*4882a593Smuzhiyun 	int err;
1250*4882a593Smuzhiyun 
1251*4882a593Smuzhiyun 	alloc_size = sizeof(struct mlxsw_sx_port *) * max_ports;
1252*4882a593Smuzhiyun 	mlxsw_sx->ports = kzalloc(alloc_size, GFP_KERNEL);
1253*4882a593Smuzhiyun 	if (!mlxsw_sx->ports)
1254*4882a593Smuzhiyun 		return -ENOMEM;
1255*4882a593Smuzhiyun 
1256*4882a593Smuzhiyun 	for (i = 1; i < max_ports; i++) {
1257*4882a593Smuzhiyun 		err = mlxsw_sx_port_module_info_get(mlxsw_sx, i, &module,
1258*4882a593Smuzhiyun 						    &width);
1259*4882a593Smuzhiyun 		if (err)
1260*4882a593Smuzhiyun 			goto err_port_module_info_get;
1261*4882a593Smuzhiyun 		if (!width)
1262*4882a593Smuzhiyun 			continue;
1263*4882a593Smuzhiyun 		err = mlxsw_sx_port_eth_create(mlxsw_sx, i, module, width);
1264*4882a593Smuzhiyun 		if (err)
1265*4882a593Smuzhiyun 			goto err_port_create;
1266*4882a593Smuzhiyun 	}
1267*4882a593Smuzhiyun 	return 0;
1268*4882a593Smuzhiyun 
1269*4882a593Smuzhiyun err_port_create:
1270*4882a593Smuzhiyun err_port_module_info_get:
1271*4882a593Smuzhiyun 	for (i--; i >= 1; i--)
1272*4882a593Smuzhiyun 		if (mlxsw_sx_port_created(mlxsw_sx, i))
1273*4882a593Smuzhiyun 			mlxsw_sx_port_remove(mlxsw_sx, i);
1274*4882a593Smuzhiyun 	kfree(mlxsw_sx->ports);
1275*4882a593Smuzhiyun 	mlxsw_sx->ports = NULL;
1276*4882a593Smuzhiyun 	return err;
1277*4882a593Smuzhiyun }
1278*4882a593Smuzhiyun 
mlxsw_sx_pude_eth_event_func(struct mlxsw_sx_port * mlxsw_sx_port,enum mlxsw_reg_pude_oper_status status)1279*4882a593Smuzhiyun static void mlxsw_sx_pude_eth_event_func(struct mlxsw_sx_port *mlxsw_sx_port,
1280*4882a593Smuzhiyun 					 enum mlxsw_reg_pude_oper_status status)
1281*4882a593Smuzhiyun {
1282*4882a593Smuzhiyun 	if (status == MLXSW_PORT_OPER_STATUS_UP) {
1283*4882a593Smuzhiyun 		netdev_info(mlxsw_sx_port->dev, "link up\n");
1284*4882a593Smuzhiyun 		netif_carrier_on(mlxsw_sx_port->dev);
1285*4882a593Smuzhiyun 	} else {
1286*4882a593Smuzhiyun 		netdev_info(mlxsw_sx_port->dev, "link down\n");
1287*4882a593Smuzhiyun 		netif_carrier_off(mlxsw_sx_port->dev);
1288*4882a593Smuzhiyun 	}
1289*4882a593Smuzhiyun }
1290*4882a593Smuzhiyun 
mlxsw_sx_pude_ib_event_func(struct mlxsw_sx_port * mlxsw_sx_port,enum mlxsw_reg_pude_oper_status status)1291*4882a593Smuzhiyun static void mlxsw_sx_pude_ib_event_func(struct mlxsw_sx_port *mlxsw_sx_port,
1292*4882a593Smuzhiyun 					enum mlxsw_reg_pude_oper_status status)
1293*4882a593Smuzhiyun {
1294*4882a593Smuzhiyun 	if (status == MLXSW_PORT_OPER_STATUS_UP)
1295*4882a593Smuzhiyun 		pr_info("ib link for port %d - up\n",
1296*4882a593Smuzhiyun 			mlxsw_sx_port->mapping.module + 1);
1297*4882a593Smuzhiyun 	else
1298*4882a593Smuzhiyun 		pr_info("ib link for port %d - down\n",
1299*4882a593Smuzhiyun 			mlxsw_sx_port->mapping.module + 1);
1300*4882a593Smuzhiyun }
1301*4882a593Smuzhiyun 
mlxsw_sx_pude_event_func(const struct mlxsw_reg_info * reg,char * pude_pl,void * priv)1302*4882a593Smuzhiyun static void mlxsw_sx_pude_event_func(const struct mlxsw_reg_info *reg,
1303*4882a593Smuzhiyun 				     char *pude_pl, void *priv)
1304*4882a593Smuzhiyun {
1305*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = priv;
1306*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port;
1307*4882a593Smuzhiyun 	enum mlxsw_reg_pude_oper_status status;
1308*4882a593Smuzhiyun 	enum devlink_port_type port_type;
1309*4882a593Smuzhiyun 	u8 local_port;
1310*4882a593Smuzhiyun 
1311*4882a593Smuzhiyun 	local_port = mlxsw_reg_pude_local_port_get(pude_pl);
1312*4882a593Smuzhiyun 	mlxsw_sx_port = mlxsw_sx->ports[local_port];
1313*4882a593Smuzhiyun 	if (!mlxsw_sx_port) {
1314*4882a593Smuzhiyun 		dev_warn(mlxsw_sx->bus_info->dev, "Port %d: Link event received for non-existent port\n",
1315*4882a593Smuzhiyun 			 local_port);
1316*4882a593Smuzhiyun 		return;
1317*4882a593Smuzhiyun 	}
1318*4882a593Smuzhiyun 
1319*4882a593Smuzhiyun 	status = mlxsw_reg_pude_oper_status_get(pude_pl);
1320*4882a593Smuzhiyun 	port_type = mlxsw_core_port_type_get(mlxsw_sx->core, local_port);
1321*4882a593Smuzhiyun 	if (port_type == DEVLINK_PORT_TYPE_ETH)
1322*4882a593Smuzhiyun 		mlxsw_sx_pude_eth_event_func(mlxsw_sx_port, status);
1323*4882a593Smuzhiyun 	else if (port_type == DEVLINK_PORT_TYPE_IB)
1324*4882a593Smuzhiyun 		mlxsw_sx_pude_ib_event_func(mlxsw_sx_port, status);
1325*4882a593Smuzhiyun }
1326*4882a593Smuzhiyun 
mlxsw_sx_rx_listener_func(struct sk_buff * skb,u8 local_port,void * priv)1327*4882a593Smuzhiyun static void mlxsw_sx_rx_listener_func(struct sk_buff *skb, u8 local_port,
1328*4882a593Smuzhiyun 				      void *priv)
1329*4882a593Smuzhiyun {
1330*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = priv;
1331*4882a593Smuzhiyun 	struct mlxsw_sx_port *mlxsw_sx_port = mlxsw_sx->ports[local_port];
1332*4882a593Smuzhiyun 	struct mlxsw_sx_port_pcpu_stats *pcpu_stats;
1333*4882a593Smuzhiyun 
1334*4882a593Smuzhiyun 	if (unlikely(!mlxsw_sx_port)) {
1335*4882a593Smuzhiyun 		dev_warn_ratelimited(mlxsw_sx->bus_info->dev, "Port %d: skb received for non-existent port\n",
1336*4882a593Smuzhiyun 				     local_port);
1337*4882a593Smuzhiyun 		return;
1338*4882a593Smuzhiyun 	}
1339*4882a593Smuzhiyun 
1340*4882a593Smuzhiyun 	skb->dev = mlxsw_sx_port->dev;
1341*4882a593Smuzhiyun 
1342*4882a593Smuzhiyun 	pcpu_stats = this_cpu_ptr(mlxsw_sx_port->pcpu_stats);
1343*4882a593Smuzhiyun 	u64_stats_update_begin(&pcpu_stats->syncp);
1344*4882a593Smuzhiyun 	pcpu_stats->rx_packets++;
1345*4882a593Smuzhiyun 	pcpu_stats->rx_bytes += skb->len;
1346*4882a593Smuzhiyun 	u64_stats_update_end(&pcpu_stats->syncp);
1347*4882a593Smuzhiyun 
1348*4882a593Smuzhiyun 	skb->protocol = eth_type_trans(skb, skb->dev);
1349*4882a593Smuzhiyun 	netif_receive_skb(skb);
1350*4882a593Smuzhiyun }
1351*4882a593Smuzhiyun 
mlxsw_sx_port_type_set(struct mlxsw_core * mlxsw_core,u8 local_port,enum devlink_port_type new_type)1352*4882a593Smuzhiyun static int mlxsw_sx_port_type_set(struct mlxsw_core *mlxsw_core, u8 local_port,
1353*4882a593Smuzhiyun 				  enum devlink_port_type new_type)
1354*4882a593Smuzhiyun {
1355*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_core_driver_priv(mlxsw_core);
1356*4882a593Smuzhiyun 	u8 module, width;
1357*4882a593Smuzhiyun 	int err;
1358*4882a593Smuzhiyun 
1359*4882a593Smuzhiyun 	if (!mlxsw_sx->ports || !mlxsw_sx->ports[local_port]) {
1360*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Port number \"%d\" does not exist\n",
1361*4882a593Smuzhiyun 			local_port);
1362*4882a593Smuzhiyun 		return -EINVAL;
1363*4882a593Smuzhiyun 	}
1364*4882a593Smuzhiyun 
1365*4882a593Smuzhiyun 	if (new_type == DEVLINK_PORT_TYPE_AUTO)
1366*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1367*4882a593Smuzhiyun 
1368*4882a593Smuzhiyun 	__mlxsw_sx_port_remove(mlxsw_sx, local_port);
1369*4882a593Smuzhiyun 	err = mlxsw_sx_port_module_info_get(mlxsw_sx, local_port, &module,
1370*4882a593Smuzhiyun 					    &width);
1371*4882a593Smuzhiyun 	if (err)
1372*4882a593Smuzhiyun 		goto err_port_module_info_get;
1373*4882a593Smuzhiyun 
1374*4882a593Smuzhiyun 	if (new_type == DEVLINK_PORT_TYPE_ETH)
1375*4882a593Smuzhiyun 		err = __mlxsw_sx_port_eth_create(mlxsw_sx, local_port, module,
1376*4882a593Smuzhiyun 						 width);
1377*4882a593Smuzhiyun 	else if (new_type == DEVLINK_PORT_TYPE_IB)
1378*4882a593Smuzhiyun 		err = __mlxsw_sx_port_ib_create(mlxsw_sx, local_port, module,
1379*4882a593Smuzhiyun 						width);
1380*4882a593Smuzhiyun 
1381*4882a593Smuzhiyun err_port_module_info_get:
1382*4882a593Smuzhiyun 	return err;
1383*4882a593Smuzhiyun }
1384*4882a593Smuzhiyun 
1385*4882a593Smuzhiyun enum {
1386*4882a593Smuzhiyun 	MLXSW_REG_HTGT_TRAP_GROUP_SX2_RX = 1,
1387*4882a593Smuzhiyun 	MLXSW_REG_HTGT_TRAP_GROUP_SX2_CTRL = 2,
1388*4882a593Smuzhiyun };
1389*4882a593Smuzhiyun 
1390*4882a593Smuzhiyun #define MLXSW_SX_RXL(_trap_id) \
1391*4882a593Smuzhiyun 	MLXSW_RXL(mlxsw_sx_rx_listener_func, _trap_id, TRAP_TO_CPU,	\
1392*4882a593Smuzhiyun 		  false, SX2_RX, FORWARD)
1393*4882a593Smuzhiyun 
1394*4882a593Smuzhiyun static const struct mlxsw_listener mlxsw_sx_listener[] = {
1395*4882a593Smuzhiyun 	MLXSW_EVENTL(mlxsw_sx_pude_event_func, PUDE, EMAD),
1396*4882a593Smuzhiyun 	MLXSW_SX_RXL(FDB_MC),
1397*4882a593Smuzhiyun 	MLXSW_SX_RXL(STP),
1398*4882a593Smuzhiyun 	MLXSW_SX_RXL(LACP),
1399*4882a593Smuzhiyun 	MLXSW_SX_RXL(EAPOL),
1400*4882a593Smuzhiyun 	MLXSW_SX_RXL(LLDP),
1401*4882a593Smuzhiyun 	MLXSW_SX_RXL(MMRP),
1402*4882a593Smuzhiyun 	MLXSW_SX_RXL(MVRP),
1403*4882a593Smuzhiyun 	MLXSW_SX_RXL(RPVST),
1404*4882a593Smuzhiyun 	MLXSW_SX_RXL(DHCP),
1405*4882a593Smuzhiyun 	MLXSW_SX_RXL(IGMP_QUERY),
1406*4882a593Smuzhiyun 	MLXSW_SX_RXL(IGMP_V1_REPORT),
1407*4882a593Smuzhiyun 	MLXSW_SX_RXL(IGMP_V2_REPORT),
1408*4882a593Smuzhiyun 	MLXSW_SX_RXL(IGMP_V2_LEAVE),
1409*4882a593Smuzhiyun 	MLXSW_SX_RXL(IGMP_V3_REPORT),
1410*4882a593Smuzhiyun };
1411*4882a593Smuzhiyun 
mlxsw_sx_traps_init(struct mlxsw_sx * mlxsw_sx)1412*4882a593Smuzhiyun static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx)
1413*4882a593Smuzhiyun {
1414*4882a593Smuzhiyun 	char htgt_pl[MLXSW_REG_HTGT_LEN];
1415*4882a593Smuzhiyun 	int i;
1416*4882a593Smuzhiyun 	int err;
1417*4882a593Smuzhiyun 
1418*4882a593Smuzhiyun 	mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_SX2_RX,
1419*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_INVALID_POLICER,
1420*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_PRIORITY,
1421*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_TC);
1422*4882a593Smuzhiyun 	mlxsw_reg_htgt_local_path_rdq_set(htgt_pl,
1423*4882a593Smuzhiyun 					  MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SX2_RX);
1424*4882a593Smuzhiyun 
1425*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(htgt), htgt_pl);
1426*4882a593Smuzhiyun 	if (err)
1427*4882a593Smuzhiyun 		return err;
1428*4882a593Smuzhiyun 
1429*4882a593Smuzhiyun 	mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_SX2_CTRL,
1430*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_INVALID_POLICER,
1431*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_PRIORITY,
1432*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_TC);
1433*4882a593Smuzhiyun 	mlxsw_reg_htgt_local_path_rdq_set(htgt_pl,
1434*4882a593Smuzhiyun 					MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SX2_CTRL);
1435*4882a593Smuzhiyun 
1436*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(htgt), htgt_pl);
1437*4882a593Smuzhiyun 	if (err)
1438*4882a593Smuzhiyun 		return err;
1439*4882a593Smuzhiyun 
1440*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(mlxsw_sx_listener); i++) {
1441*4882a593Smuzhiyun 		err = mlxsw_core_trap_register(mlxsw_sx->core,
1442*4882a593Smuzhiyun 					       &mlxsw_sx_listener[i],
1443*4882a593Smuzhiyun 					       mlxsw_sx);
1444*4882a593Smuzhiyun 		if (err)
1445*4882a593Smuzhiyun 			goto err_listener_register;
1446*4882a593Smuzhiyun 
1447*4882a593Smuzhiyun 	}
1448*4882a593Smuzhiyun 	return 0;
1449*4882a593Smuzhiyun 
1450*4882a593Smuzhiyun err_listener_register:
1451*4882a593Smuzhiyun 	for (i--; i >= 0; i--) {
1452*4882a593Smuzhiyun 		mlxsw_core_trap_unregister(mlxsw_sx->core,
1453*4882a593Smuzhiyun 					   &mlxsw_sx_listener[i],
1454*4882a593Smuzhiyun 					   mlxsw_sx);
1455*4882a593Smuzhiyun 	}
1456*4882a593Smuzhiyun 	return err;
1457*4882a593Smuzhiyun }
1458*4882a593Smuzhiyun 
mlxsw_sx_traps_fini(struct mlxsw_sx * mlxsw_sx)1459*4882a593Smuzhiyun static void mlxsw_sx_traps_fini(struct mlxsw_sx *mlxsw_sx)
1460*4882a593Smuzhiyun {
1461*4882a593Smuzhiyun 	int i;
1462*4882a593Smuzhiyun 
1463*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(mlxsw_sx_listener); i++) {
1464*4882a593Smuzhiyun 		mlxsw_core_trap_unregister(mlxsw_sx->core,
1465*4882a593Smuzhiyun 					   &mlxsw_sx_listener[i],
1466*4882a593Smuzhiyun 					   mlxsw_sx);
1467*4882a593Smuzhiyun 	}
1468*4882a593Smuzhiyun }
1469*4882a593Smuzhiyun 
mlxsw_sx_flood_init(struct mlxsw_sx * mlxsw_sx)1470*4882a593Smuzhiyun static int mlxsw_sx_flood_init(struct mlxsw_sx *mlxsw_sx)
1471*4882a593Smuzhiyun {
1472*4882a593Smuzhiyun 	char sfgc_pl[MLXSW_REG_SFGC_LEN];
1473*4882a593Smuzhiyun 	char sgcr_pl[MLXSW_REG_SGCR_LEN];
1474*4882a593Smuzhiyun 	char *sftr_pl;
1475*4882a593Smuzhiyun 	int err;
1476*4882a593Smuzhiyun 
1477*4882a593Smuzhiyun 	/* Configure a flooding table, which includes only CPU port. */
1478*4882a593Smuzhiyun 	sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
1479*4882a593Smuzhiyun 	if (!sftr_pl)
1480*4882a593Smuzhiyun 		return -ENOMEM;
1481*4882a593Smuzhiyun 	mlxsw_reg_sftr_pack(sftr_pl, 0, 0, MLXSW_REG_SFGC_TABLE_TYPE_SINGLE, 0,
1482*4882a593Smuzhiyun 			    MLXSW_PORT_CPU_PORT, true);
1483*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sftr), sftr_pl);
1484*4882a593Smuzhiyun 	kfree(sftr_pl);
1485*4882a593Smuzhiyun 	if (err)
1486*4882a593Smuzhiyun 		return err;
1487*4882a593Smuzhiyun 
1488*4882a593Smuzhiyun 	/* Flood different packet types using the flooding table. */
1489*4882a593Smuzhiyun 	mlxsw_reg_sfgc_pack(sfgc_pl,
1490*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST,
1491*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1492*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1493*4882a593Smuzhiyun 			    0);
1494*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1495*4882a593Smuzhiyun 	if (err)
1496*4882a593Smuzhiyun 		return err;
1497*4882a593Smuzhiyun 
1498*4882a593Smuzhiyun 	mlxsw_reg_sfgc_pack(sfgc_pl,
1499*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_TYPE_BROADCAST,
1500*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1501*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1502*4882a593Smuzhiyun 			    0);
1503*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1504*4882a593Smuzhiyun 	if (err)
1505*4882a593Smuzhiyun 		return err;
1506*4882a593Smuzhiyun 
1507*4882a593Smuzhiyun 	mlxsw_reg_sfgc_pack(sfgc_pl,
1508*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP,
1509*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1510*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1511*4882a593Smuzhiyun 			    0);
1512*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1513*4882a593Smuzhiyun 	if (err)
1514*4882a593Smuzhiyun 		return err;
1515*4882a593Smuzhiyun 
1516*4882a593Smuzhiyun 	mlxsw_reg_sfgc_pack(sfgc_pl,
1517*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6,
1518*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1519*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1520*4882a593Smuzhiyun 			    0);
1521*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1522*4882a593Smuzhiyun 	if (err)
1523*4882a593Smuzhiyun 		return err;
1524*4882a593Smuzhiyun 
1525*4882a593Smuzhiyun 	mlxsw_reg_sfgc_pack(sfgc_pl,
1526*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4,
1527*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID,
1528*4882a593Smuzhiyun 			    MLXSW_REG_SFGC_TABLE_TYPE_SINGLE,
1529*4882a593Smuzhiyun 			    0);
1530*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sfgc), sfgc_pl);
1531*4882a593Smuzhiyun 	if (err)
1532*4882a593Smuzhiyun 		return err;
1533*4882a593Smuzhiyun 
1534*4882a593Smuzhiyun 	mlxsw_reg_sgcr_pack(sgcr_pl, true);
1535*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sgcr), sgcr_pl);
1536*4882a593Smuzhiyun }
1537*4882a593Smuzhiyun 
mlxsw_sx_basic_trap_groups_set(struct mlxsw_core * mlxsw_core)1538*4882a593Smuzhiyun static int mlxsw_sx_basic_trap_groups_set(struct mlxsw_core *mlxsw_core)
1539*4882a593Smuzhiyun {
1540*4882a593Smuzhiyun 	char htgt_pl[MLXSW_REG_HTGT_LEN];
1541*4882a593Smuzhiyun 
1542*4882a593Smuzhiyun 	mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD,
1543*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_INVALID_POLICER,
1544*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_PRIORITY,
1545*4882a593Smuzhiyun 			    MLXSW_REG_HTGT_DEFAULT_TC);
1546*4882a593Smuzhiyun 	mlxsw_reg_htgt_swid_set(htgt_pl, MLXSW_PORT_SWID_ALL_SWIDS);
1547*4882a593Smuzhiyun 	mlxsw_reg_htgt_local_path_rdq_set(htgt_pl,
1548*4882a593Smuzhiyun 					MLXSW_REG_HTGT_LOCAL_PATH_RDQ_SX2_EMAD);
1549*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl);
1550*4882a593Smuzhiyun }
1551*4882a593Smuzhiyun 
mlxsw_sx_init(struct mlxsw_core * mlxsw_core,const struct mlxsw_bus_info * mlxsw_bus_info,struct netlink_ext_ack * extack)1552*4882a593Smuzhiyun static int mlxsw_sx_init(struct mlxsw_core *mlxsw_core,
1553*4882a593Smuzhiyun 			 const struct mlxsw_bus_info *mlxsw_bus_info,
1554*4882a593Smuzhiyun 			 struct netlink_ext_ack *extack)
1555*4882a593Smuzhiyun {
1556*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_core_driver_priv(mlxsw_core);
1557*4882a593Smuzhiyun 	int err;
1558*4882a593Smuzhiyun 
1559*4882a593Smuzhiyun 	mlxsw_sx->core = mlxsw_core;
1560*4882a593Smuzhiyun 	mlxsw_sx->bus_info = mlxsw_bus_info;
1561*4882a593Smuzhiyun 
1562*4882a593Smuzhiyun 	err = mlxsw_sx_hw_id_get(mlxsw_sx);
1563*4882a593Smuzhiyun 	if (err) {
1564*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Failed to get switch HW ID\n");
1565*4882a593Smuzhiyun 		return err;
1566*4882a593Smuzhiyun 	}
1567*4882a593Smuzhiyun 
1568*4882a593Smuzhiyun 	err = mlxsw_sx_ports_create(mlxsw_sx);
1569*4882a593Smuzhiyun 	if (err) {
1570*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Failed to create ports\n");
1571*4882a593Smuzhiyun 		return err;
1572*4882a593Smuzhiyun 	}
1573*4882a593Smuzhiyun 
1574*4882a593Smuzhiyun 	err = mlxsw_sx_traps_init(mlxsw_sx);
1575*4882a593Smuzhiyun 	if (err) {
1576*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Failed to set traps\n");
1577*4882a593Smuzhiyun 		goto err_listener_register;
1578*4882a593Smuzhiyun 	}
1579*4882a593Smuzhiyun 
1580*4882a593Smuzhiyun 	err = mlxsw_sx_flood_init(mlxsw_sx);
1581*4882a593Smuzhiyun 	if (err) {
1582*4882a593Smuzhiyun 		dev_err(mlxsw_sx->bus_info->dev, "Failed to initialize flood tables\n");
1583*4882a593Smuzhiyun 		goto err_flood_init;
1584*4882a593Smuzhiyun 	}
1585*4882a593Smuzhiyun 
1586*4882a593Smuzhiyun 	return 0;
1587*4882a593Smuzhiyun 
1588*4882a593Smuzhiyun err_flood_init:
1589*4882a593Smuzhiyun 	mlxsw_sx_traps_fini(mlxsw_sx);
1590*4882a593Smuzhiyun err_listener_register:
1591*4882a593Smuzhiyun 	mlxsw_sx_ports_remove(mlxsw_sx);
1592*4882a593Smuzhiyun 	return err;
1593*4882a593Smuzhiyun }
1594*4882a593Smuzhiyun 
mlxsw_sx_fini(struct mlxsw_core * mlxsw_core)1595*4882a593Smuzhiyun static void mlxsw_sx_fini(struct mlxsw_core *mlxsw_core)
1596*4882a593Smuzhiyun {
1597*4882a593Smuzhiyun 	struct mlxsw_sx *mlxsw_sx = mlxsw_core_driver_priv(mlxsw_core);
1598*4882a593Smuzhiyun 
1599*4882a593Smuzhiyun 	mlxsw_sx_traps_fini(mlxsw_sx);
1600*4882a593Smuzhiyun 	mlxsw_sx_ports_remove(mlxsw_sx);
1601*4882a593Smuzhiyun }
1602*4882a593Smuzhiyun 
1603*4882a593Smuzhiyun static const struct mlxsw_config_profile mlxsw_sx_config_profile = {
1604*4882a593Smuzhiyun 	.used_max_vepa_channels		= 1,
1605*4882a593Smuzhiyun 	.max_vepa_channels		= 0,
1606*4882a593Smuzhiyun 	.used_max_mid			= 1,
1607*4882a593Smuzhiyun 	.max_mid			= 7000,
1608*4882a593Smuzhiyun 	.used_max_pgt			= 1,
1609*4882a593Smuzhiyun 	.max_pgt			= 0,
1610*4882a593Smuzhiyun 	.used_max_system_port		= 1,
1611*4882a593Smuzhiyun 	.max_system_port		= 48000,
1612*4882a593Smuzhiyun 	.used_max_vlan_groups		= 1,
1613*4882a593Smuzhiyun 	.max_vlan_groups		= 127,
1614*4882a593Smuzhiyun 	.used_max_regions		= 1,
1615*4882a593Smuzhiyun 	.max_regions			= 400,
1616*4882a593Smuzhiyun 	.used_flood_tables		= 1,
1617*4882a593Smuzhiyun 	.max_flood_tables		= 2,
1618*4882a593Smuzhiyun 	.max_vid_flood_tables		= 1,
1619*4882a593Smuzhiyun 	.used_flood_mode		= 1,
1620*4882a593Smuzhiyun 	.flood_mode			= 3,
1621*4882a593Smuzhiyun 	.used_max_ib_mc			= 1,
1622*4882a593Smuzhiyun 	.max_ib_mc			= 6,
1623*4882a593Smuzhiyun 	.used_max_pkey			= 1,
1624*4882a593Smuzhiyun 	.max_pkey			= 0,
1625*4882a593Smuzhiyun 	.swid_config			= {
1626*4882a593Smuzhiyun 		{
1627*4882a593Smuzhiyun 			.used_type	= 1,
1628*4882a593Smuzhiyun 			.type		= MLXSW_PORT_SWID_TYPE_ETH,
1629*4882a593Smuzhiyun 		},
1630*4882a593Smuzhiyun 		{
1631*4882a593Smuzhiyun 			.used_type	= 1,
1632*4882a593Smuzhiyun 			.type		= MLXSW_PORT_SWID_TYPE_IB,
1633*4882a593Smuzhiyun 		}
1634*4882a593Smuzhiyun 	},
1635*4882a593Smuzhiyun };
1636*4882a593Smuzhiyun 
1637*4882a593Smuzhiyun static struct mlxsw_driver mlxsw_sx_driver = {
1638*4882a593Smuzhiyun 	.kind			= mlxsw_sx_driver_name,
1639*4882a593Smuzhiyun 	.priv_size		= sizeof(struct mlxsw_sx),
1640*4882a593Smuzhiyun 	.init			= mlxsw_sx_init,
1641*4882a593Smuzhiyun 	.fini			= mlxsw_sx_fini,
1642*4882a593Smuzhiyun 	.basic_trap_groups_set	= mlxsw_sx_basic_trap_groups_set,
1643*4882a593Smuzhiyun 	.txhdr_construct	= mlxsw_sx_txhdr_construct,
1644*4882a593Smuzhiyun 	.txhdr_len		= MLXSW_TXHDR_LEN,
1645*4882a593Smuzhiyun 	.profile		= &mlxsw_sx_config_profile,
1646*4882a593Smuzhiyun 	.port_type_set		= mlxsw_sx_port_type_set,
1647*4882a593Smuzhiyun };
1648*4882a593Smuzhiyun 
1649*4882a593Smuzhiyun static const struct pci_device_id mlxsw_sx_pci_id_table[] = {
1650*4882a593Smuzhiyun 	{PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SWITCHX2), 0},
1651*4882a593Smuzhiyun 	{0, },
1652*4882a593Smuzhiyun };
1653*4882a593Smuzhiyun 
1654*4882a593Smuzhiyun static struct pci_driver mlxsw_sx_pci_driver = {
1655*4882a593Smuzhiyun 	.name = mlxsw_sx_driver_name,
1656*4882a593Smuzhiyun 	.id_table = mlxsw_sx_pci_id_table,
1657*4882a593Smuzhiyun };
1658*4882a593Smuzhiyun 
mlxsw_sx_module_init(void)1659*4882a593Smuzhiyun static int __init mlxsw_sx_module_init(void)
1660*4882a593Smuzhiyun {
1661*4882a593Smuzhiyun 	int err;
1662*4882a593Smuzhiyun 
1663*4882a593Smuzhiyun 	err = mlxsw_core_driver_register(&mlxsw_sx_driver);
1664*4882a593Smuzhiyun 	if (err)
1665*4882a593Smuzhiyun 		return err;
1666*4882a593Smuzhiyun 
1667*4882a593Smuzhiyun 	err = mlxsw_pci_driver_register(&mlxsw_sx_pci_driver);
1668*4882a593Smuzhiyun 	if (err)
1669*4882a593Smuzhiyun 		goto err_pci_driver_register;
1670*4882a593Smuzhiyun 
1671*4882a593Smuzhiyun 	return 0;
1672*4882a593Smuzhiyun 
1673*4882a593Smuzhiyun err_pci_driver_register:
1674*4882a593Smuzhiyun 	mlxsw_core_driver_unregister(&mlxsw_sx_driver);
1675*4882a593Smuzhiyun 	return err;
1676*4882a593Smuzhiyun }
1677*4882a593Smuzhiyun 
mlxsw_sx_module_exit(void)1678*4882a593Smuzhiyun static void __exit mlxsw_sx_module_exit(void)
1679*4882a593Smuzhiyun {
1680*4882a593Smuzhiyun 	mlxsw_pci_driver_unregister(&mlxsw_sx_pci_driver);
1681*4882a593Smuzhiyun 	mlxsw_core_driver_unregister(&mlxsw_sx_driver);
1682*4882a593Smuzhiyun }
1683*4882a593Smuzhiyun 
1684*4882a593Smuzhiyun module_init(mlxsw_sx_module_init);
1685*4882a593Smuzhiyun module_exit(mlxsw_sx_module_exit);
1686*4882a593Smuzhiyun 
1687*4882a593Smuzhiyun MODULE_LICENSE("Dual BSD/GPL");
1688*4882a593Smuzhiyun MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
1689*4882a593Smuzhiyun MODULE_DESCRIPTION("Mellanox SwitchX-2 driver");
1690*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, mlxsw_sx_pci_id_table);
1691