1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2020 Mellanox Technologies. All rights reserved */
3 
4 #include "reg.h"
5 #include "spectrum.h"
6 #include "core_env.h"
7 
8 static const char mlxsw_sp_driver_version[] = "1.0";
9 
10 static void mlxsw_sp_port_get_drvinfo(struct net_device *dev,
11 				      struct ethtool_drvinfo *drvinfo)
12 {
13 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
14 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
15 
16 	strlcpy(drvinfo->driver, mlxsw_sp->bus_info->device_kind,
17 		sizeof(drvinfo->driver));
18 	strlcpy(drvinfo->version, mlxsw_sp_driver_version,
19 		sizeof(drvinfo->version));
20 	snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
21 		 "%d.%d.%d",
22 		 mlxsw_sp->bus_info->fw_rev.major,
23 		 mlxsw_sp->bus_info->fw_rev.minor,
24 		 mlxsw_sp->bus_info->fw_rev.subminor);
25 	strlcpy(drvinfo->bus_info, mlxsw_sp->bus_info->device_name,
26 		sizeof(drvinfo->bus_info));
27 }
28 
29 struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping {
30 	u32 status_opcode;
31 	enum ethtool_link_ext_state link_ext_state;
32 	u8 link_ext_substate;
33 };
34 
35 static const struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping
36 mlxsw_sp_link_ext_state_opcode_map[] = {
37 	{2, ETHTOOL_LINK_EXT_STATE_AUTONEG,
38 		ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED},
39 	{3, ETHTOOL_LINK_EXT_STATE_AUTONEG,
40 		ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED},
41 	{4, ETHTOOL_LINK_EXT_STATE_AUTONEG,
42 		ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED},
43 	{36, ETHTOOL_LINK_EXT_STATE_AUTONEG,
44 		ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE},
45 	{38, ETHTOOL_LINK_EXT_STATE_AUTONEG,
46 		ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE},
47 	{39, ETHTOOL_LINK_EXT_STATE_AUTONEG,
48 		ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD},
49 
50 	{5, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE,
51 		ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED},
52 	{6, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE,
53 		ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT},
54 	{7, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE,
55 		ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY},
56 	{8, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE, 0},
57 	{14, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE,
58 		ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT},
59 
60 	{9, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
61 		ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK},
62 	{10, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
63 		ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK},
64 	{11, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
65 		ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS},
66 	{12, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
67 		ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED},
68 	{13, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
69 		ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED},
70 
71 	{15, ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY, 0},
72 	{17, ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY,
73 		ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS},
74 	{42, ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY,
75 		ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE},
76 
77 	{1024, ETHTOOL_LINK_EXT_STATE_NO_CABLE, 0},
78 
79 	{16, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
80 		ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE},
81 	{20, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
82 		ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE},
83 	{29, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
84 		ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE},
85 	{1025, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
86 		ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE},
87 	{1029, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
88 		ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE},
89 	{1031, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE, 0},
90 
91 	{1027, ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE, 0},
92 
93 	{23, ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE, 0},
94 
95 	{1032, ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED, 0},
96 
97 	{1030, ETHTOOL_LINK_EXT_STATE_OVERHEAT, 0},
98 };
99 
100 static void
101 mlxsw_sp_port_set_link_ext_state(struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping
102 				 link_ext_state_mapping,
103 				 struct ethtool_link_ext_state_info *link_ext_state_info)
104 {
105 	switch (link_ext_state_mapping.link_ext_state) {
106 	case ETHTOOL_LINK_EXT_STATE_AUTONEG:
107 		link_ext_state_info->autoneg =
108 			link_ext_state_mapping.link_ext_substate;
109 		break;
110 	case ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE:
111 		link_ext_state_info->link_training =
112 			link_ext_state_mapping.link_ext_substate;
113 		break;
114 	case ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH:
115 		link_ext_state_info->link_logical_mismatch =
116 			link_ext_state_mapping.link_ext_substate;
117 		break;
118 	case ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY:
119 		link_ext_state_info->bad_signal_integrity =
120 			link_ext_state_mapping.link_ext_substate;
121 		break;
122 	case ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE:
123 		link_ext_state_info->cable_issue =
124 			link_ext_state_mapping.link_ext_substate;
125 		break;
126 	default:
127 		break;
128 	}
129 
130 	link_ext_state_info->link_ext_state = link_ext_state_mapping.link_ext_state;
131 }
132 
133 static int
134 mlxsw_sp_port_get_link_ext_state(struct net_device *dev,
135 				 struct ethtool_link_ext_state_info *link_ext_state_info)
136 {
137 	struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping link_ext_state_mapping;
138 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
139 	char pddr_pl[MLXSW_REG_PDDR_LEN];
140 	int opcode, err, i;
141 	u32 status_opcode;
142 
143 	if (netif_carrier_ok(dev))
144 		return -ENODATA;
145 
146 	mlxsw_reg_pddr_pack(pddr_pl, mlxsw_sp_port->local_port,
147 			    MLXSW_REG_PDDR_PAGE_SELECT_TROUBLESHOOTING_INFO);
148 
149 	opcode = MLXSW_REG_PDDR_TRBLSH_GROUP_OPCODE_MONITOR;
150 	mlxsw_reg_pddr_trblsh_group_opcode_set(pddr_pl, opcode);
151 
152 	err = mlxsw_reg_query(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pddr),
153 			      pddr_pl);
154 	if (err)
155 		return err;
156 
157 	status_opcode = mlxsw_reg_pddr_trblsh_status_opcode_get(pddr_pl);
158 	if (!status_opcode)
159 		return -ENODATA;
160 
161 	for (i = 0; i < ARRAY_SIZE(mlxsw_sp_link_ext_state_opcode_map); i++) {
162 		link_ext_state_mapping = mlxsw_sp_link_ext_state_opcode_map[i];
163 		if (link_ext_state_mapping.status_opcode == status_opcode) {
164 			mlxsw_sp_port_set_link_ext_state(link_ext_state_mapping,
165 							 link_ext_state_info);
166 			return 0;
167 		}
168 	}
169 
170 	return -ENODATA;
171 }
172 
173 static void mlxsw_sp_port_get_pauseparam(struct net_device *dev,
174 					 struct ethtool_pauseparam *pause)
175 {
176 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
177 
178 	pause->rx_pause = mlxsw_sp_port->link.rx_pause;
179 	pause->tx_pause = mlxsw_sp_port->link.tx_pause;
180 }
181 
182 static int mlxsw_sp_port_pause_set(struct mlxsw_sp_port *mlxsw_sp_port,
183 				   struct ethtool_pauseparam *pause)
184 {
185 	char pfcc_pl[MLXSW_REG_PFCC_LEN];
186 
187 	mlxsw_reg_pfcc_pack(pfcc_pl, mlxsw_sp_port->local_port);
188 	mlxsw_reg_pfcc_pprx_set(pfcc_pl, pause->rx_pause);
189 	mlxsw_reg_pfcc_pptx_set(pfcc_pl, pause->tx_pause);
190 
191 	return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pfcc),
192 			       pfcc_pl);
193 }
194 
195 static int mlxsw_sp_port_set_pauseparam(struct net_device *dev,
196 					struct ethtool_pauseparam *pause)
197 {
198 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
199 	bool pause_en = pause->tx_pause || pause->rx_pause;
200 	int err;
201 
202 	if (mlxsw_sp_port->dcb.pfc && mlxsw_sp_port->dcb.pfc->pfc_en) {
203 		netdev_err(dev, "PFC already enabled on port\n");
204 		return -EINVAL;
205 	}
206 
207 	if (pause->autoneg) {
208 		netdev_err(dev, "PAUSE frames autonegotiation isn't supported\n");
209 		return -EINVAL;
210 	}
211 
212 	err = mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, pause_en);
213 	if (err) {
214 		netdev_err(dev, "Failed to configure port's headroom\n");
215 		return err;
216 	}
217 
218 	err = mlxsw_sp_port_pause_set(mlxsw_sp_port, pause);
219 	if (err) {
220 		netdev_err(dev, "Failed to set PAUSE parameters\n");
221 		goto err_port_pause_configure;
222 	}
223 
224 	mlxsw_sp_port->link.rx_pause = pause->rx_pause;
225 	mlxsw_sp_port->link.tx_pause = pause->tx_pause;
226 
227 	return 0;
228 
229 err_port_pause_configure:
230 	pause_en = mlxsw_sp_port_is_pause_en(mlxsw_sp_port);
231 	mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, pause_en);
232 	return err;
233 }
234 
235 struct mlxsw_sp_port_hw_stats {
236 	char str[ETH_GSTRING_LEN];
237 	u64 (*getter)(const char *payload);
238 	bool cells_bytes;
239 };
240 
241 static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = {
242 	{
243 		.str = "a_frames_transmitted_ok",
244 		.getter = mlxsw_reg_ppcnt_a_frames_transmitted_ok_get,
245 	},
246 	{
247 		.str = "a_frames_received_ok",
248 		.getter = mlxsw_reg_ppcnt_a_frames_received_ok_get,
249 	},
250 	{
251 		.str = "a_frame_check_sequence_errors",
252 		.getter = mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get,
253 	},
254 	{
255 		.str = "a_alignment_errors",
256 		.getter = mlxsw_reg_ppcnt_a_alignment_errors_get,
257 	},
258 	{
259 		.str = "a_octets_transmitted_ok",
260 		.getter = mlxsw_reg_ppcnt_a_octets_transmitted_ok_get,
261 	},
262 	{
263 		.str = "a_octets_received_ok",
264 		.getter = mlxsw_reg_ppcnt_a_octets_received_ok_get,
265 	},
266 	{
267 		.str = "a_multicast_frames_xmitted_ok",
268 		.getter = mlxsw_reg_ppcnt_a_multicast_frames_xmitted_ok_get,
269 	},
270 	{
271 		.str = "a_broadcast_frames_xmitted_ok",
272 		.getter = mlxsw_reg_ppcnt_a_broadcast_frames_xmitted_ok_get,
273 	},
274 	{
275 		.str = "a_multicast_frames_received_ok",
276 		.getter = mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get,
277 	},
278 	{
279 		.str = "a_broadcast_frames_received_ok",
280 		.getter = mlxsw_reg_ppcnt_a_broadcast_frames_received_ok_get,
281 	},
282 	{
283 		.str = "a_in_range_length_errors",
284 		.getter = mlxsw_reg_ppcnt_a_in_range_length_errors_get,
285 	},
286 	{
287 		.str = "a_out_of_range_length_field",
288 		.getter = mlxsw_reg_ppcnt_a_out_of_range_length_field_get,
289 	},
290 	{
291 		.str = "a_frame_too_long_errors",
292 		.getter = mlxsw_reg_ppcnt_a_frame_too_long_errors_get,
293 	},
294 	{
295 		.str = "a_symbol_error_during_carrier",
296 		.getter = mlxsw_reg_ppcnt_a_symbol_error_during_carrier_get,
297 	},
298 	{
299 		.str = "a_mac_control_frames_transmitted",
300 		.getter = mlxsw_reg_ppcnt_a_mac_control_frames_transmitted_get,
301 	},
302 	{
303 		.str = "a_mac_control_frames_received",
304 		.getter = mlxsw_reg_ppcnt_a_mac_control_frames_received_get,
305 	},
306 	{
307 		.str = "a_unsupported_opcodes_received",
308 		.getter = mlxsw_reg_ppcnt_a_unsupported_opcodes_received_get,
309 	},
310 	{
311 		.str = "a_pause_mac_ctrl_frames_received",
312 		.getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_received_get,
313 	},
314 	{
315 		.str = "a_pause_mac_ctrl_frames_xmitted",
316 		.getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_transmitted_get,
317 	},
318 };
319 
320 #define MLXSW_SP_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_stats)
321 
322 static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2863_stats[] = {
323 	{
324 		.str = "if_in_discards",
325 		.getter = mlxsw_reg_ppcnt_if_in_discards_get,
326 	},
327 	{
328 		.str = "if_out_discards",
329 		.getter = mlxsw_reg_ppcnt_if_out_discards_get,
330 	},
331 	{
332 		.str = "if_out_errors",
333 		.getter = mlxsw_reg_ppcnt_if_out_errors_get,
334 	},
335 };
336 
337 #define MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN \
338 	ARRAY_SIZE(mlxsw_sp_port_hw_rfc_2863_stats)
339 
340 static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2819_stats[] = {
341 	{
342 		.str = "ether_stats_undersize_pkts",
343 		.getter = mlxsw_reg_ppcnt_ether_stats_undersize_pkts_get,
344 	},
345 	{
346 		.str = "ether_stats_oversize_pkts",
347 		.getter = mlxsw_reg_ppcnt_ether_stats_oversize_pkts_get,
348 	},
349 	{
350 		.str = "ether_stats_fragments",
351 		.getter = mlxsw_reg_ppcnt_ether_stats_fragments_get,
352 	},
353 	{
354 		.str = "ether_pkts64octets",
355 		.getter = mlxsw_reg_ppcnt_ether_stats_pkts64octets_get,
356 	},
357 	{
358 		.str = "ether_pkts65to127octets",
359 		.getter = mlxsw_reg_ppcnt_ether_stats_pkts65to127octets_get,
360 	},
361 	{
362 		.str = "ether_pkts128to255octets",
363 		.getter = mlxsw_reg_ppcnt_ether_stats_pkts128to255octets_get,
364 	},
365 	{
366 		.str = "ether_pkts256to511octets",
367 		.getter = mlxsw_reg_ppcnt_ether_stats_pkts256to511octets_get,
368 	},
369 	{
370 		.str = "ether_pkts512to1023octets",
371 		.getter = mlxsw_reg_ppcnt_ether_stats_pkts512to1023octets_get,
372 	},
373 	{
374 		.str = "ether_pkts1024to1518octets",
375 		.getter = mlxsw_reg_ppcnt_ether_stats_pkts1024to1518octets_get,
376 	},
377 	{
378 		.str = "ether_pkts1519to2047octets",
379 		.getter = mlxsw_reg_ppcnt_ether_stats_pkts1519to2047octets_get,
380 	},
381 	{
382 		.str = "ether_pkts2048to4095octets",
383 		.getter = mlxsw_reg_ppcnt_ether_stats_pkts2048to4095octets_get,
384 	},
385 	{
386 		.str = "ether_pkts4096to8191octets",
387 		.getter = mlxsw_reg_ppcnt_ether_stats_pkts4096to8191octets_get,
388 	},
389 	{
390 		.str = "ether_pkts8192to10239octets",
391 		.getter = mlxsw_reg_ppcnt_ether_stats_pkts8192to10239octets_get,
392 	},
393 };
394 
395 #define MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN \
396 	ARRAY_SIZE(mlxsw_sp_port_hw_rfc_2819_stats)
397 
398 static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_3635_stats[] = {
399 	{
400 		.str = "dot3stats_fcs_errors",
401 		.getter = mlxsw_reg_ppcnt_dot3stats_fcs_errors_get,
402 	},
403 	{
404 		.str = "dot3stats_symbol_errors",
405 		.getter = mlxsw_reg_ppcnt_dot3stats_symbol_errors_get,
406 	},
407 	{
408 		.str = "dot3control_in_unknown_opcodes",
409 		.getter = mlxsw_reg_ppcnt_dot3control_in_unknown_opcodes_get,
410 	},
411 	{
412 		.str = "dot3in_pause_frames",
413 		.getter = mlxsw_reg_ppcnt_dot3in_pause_frames_get,
414 	},
415 };
416 
417 #define MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN \
418 	ARRAY_SIZE(mlxsw_sp_port_hw_rfc_3635_stats)
419 
420 static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_ext_stats[] = {
421 	{
422 		.str = "ecn_marked",
423 		.getter = mlxsw_reg_ppcnt_ecn_marked_get,
424 	},
425 };
426 
427 #define MLXSW_SP_PORT_HW_EXT_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_ext_stats)
428 
429 static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_discard_stats[] = {
430 	{
431 		.str = "discard_ingress_general",
432 		.getter = mlxsw_reg_ppcnt_ingress_general_get,
433 	},
434 	{
435 		.str = "discard_ingress_policy_engine",
436 		.getter = mlxsw_reg_ppcnt_ingress_policy_engine_get,
437 	},
438 	{
439 		.str = "discard_ingress_vlan_membership",
440 		.getter = mlxsw_reg_ppcnt_ingress_vlan_membership_get,
441 	},
442 	{
443 		.str = "discard_ingress_tag_frame_type",
444 		.getter = mlxsw_reg_ppcnt_ingress_tag_frame_type_get,
445 	},
446 	{
447 		.str = "discard_egress_vlan_membership",
448 		.getter = mlxsw_reg_ppcnt_egress_vlan_membership_get,
449 	},
450 	{
451 		.str = "discard_loopback_filter",
452 		.getter = mlxsw_reg_ppcnt_loopback_filter_get,
453 	},
454 	{
455 		.str = "discard_egress_general",
456 		.getter = mlxsw_reg_ppcnt_egress_general_get,
457 	},
458 	{
459 		.str = "discard_egress_hoq",
460 		.getter = mlxsw_reg_ppcnt_egress_hoq_get,
461 	},
462 	{
463 		.str = "discard_egress_policy_engine",
464 		.getter = mlxsw_reg_ppcnt_egress_policy_engine_get,
465 	},
466 	{
467 		.str = "discard_ingress_tx_link_down",
468 		.getter = mlxsw_reg_ppcnt_ingress_tx_link_down_get,
469 	},
470 	{
471 		.str = "discard_egress_stp_filter",
472 		.getter = mlxsw_reg_ppcnt_egress_stp_filter_get,
473 	},
474 	{
475 		.str = "discard_egress_sll",
476 		.getter = mlxsw_reg_ppcnt_egress_sll_get,
477 	},
478 };
479 
480 #define MLXSW_SP_PORT_HW_DISCARD_STATS_LEN \
481 	ARRAY_SIZE(mlxsw_sp_port_hw_discard_stats)
482 
483 static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_prio_stats[] = {
484 	{
485 		.str = "rx_octets_prio",
486 		.getter = mlxsw_reg_ppcnt_rx_octets_get,
487 	},
488 	{
489 		.str = "rx_frames_prio",
490 		.getter = mlxsw_reg_ppcnt_rx_frames_get,
491 	},
492 	{
493 		.str = "tx_octets_prio",
494 		.getter = mlxsw_reg_ppcnt_tx_octets_get,
495 	},
496 	{
497 		.str = "tx_frames_prio",
498 		.getter = mlxsw_reg_ppcnt_tx_frames_get,
499 	},
500 	{
501 		.str = "rx_pause_prio",
502 		.getter = mlxsw_reg_ppcnt_rx_pause_get,
503 	},
504 	{
505 		.str = "rx_pause_duration_prio",
506 		.getter = mlxsw_reg_ppcnt_rx_pause_duration_get,
507 	},
508 	{
509 		.str = "tx_pause_prio",
510 		.getter = mlxsw_reg_ppcnt_tx_pause_get,
511 	},
512 	{
513 		.str = "tx_pause_duration_prio",
514 		.getter = mlxsw_reg_ppcnt_tx_pause_duration_get,
515 	},
516 };
517 
518 #define MLXSW_SP_PORT_HW_PRIO_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_prio_stats)
519 
520 static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_tc_stats[] = {
521 	{
522 		.str = "tc_transmit_queue_tc",
523 		.getter = mlxsw_reg_ppcnt_tc_transmit_queue_get,
524 		.cells_bytes = true,
525 	},
526 	{
527 		.str = "tc_no_buffer_discard_uc_tc",
528 		.getter = mlxsw_reg_ppcnt_tc_no_buffer_discard_uc_get,
529 	},
530 };
531 
532 #define MLXSW_SP_PORT_HW_TC_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_tc_stats)
533 
534 #define MLXSW_SP_PORT_ETHTOOL_STATS_LEN (MLXSW_SP_PORT_HW_STATS_LEN + \
535 					 MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN + \
536 					 MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN + \
537 					 MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN + \
538 					 MLXSW_SP_PORT_HW_EXT_STATS_LEN + \
539 					 MLXSW_SP_PORT_HW_DISCARD_STATS_LEN + \
540 					 (MLXSW_SP_PORT_HW_PRIO_STATS_LEN * \
541 					  IEEE_8021QAZ_MAX_TCS) + \
542 					 (MLXSW_SP_PORT_HW_TC_STATS_LEN * \
543 					  TC_MAX_QUEUE))
544 
545 static void mlxsw_sp_port_get_prio_strings(u8 **p, int prio)
546 {
547 	int i;
548 
549 	for (i = 0; i < MLXSW_SP_PORT_HW_PRIO_STATS_LEN; i++) {
550 		snprintf(*p, ETH_GSTRING_LEN, "%.29s_%.1d",
551 			 mlxsw_sp_port_hw_prio_stats[i].str, prio);
552 		*p += ETH_GSTRING_LEN;
553 	}
554 }
555 
556 static void mlxsw_sp_port_get_tc_strings(u8 **p, int tc)
557 {
558 	int i;
559 
560 	for (i = 0; i < MLXSW_SP_PORT_HW_TC_STATS_LEN; i++) {
561 		snprintf(*p, ETH_GSTRING_LEN, "%.29s_%.1d",
562 			 mlxsw_sp_port_hw_tc_stats[i].str, tc);
563 		*p += ETH_GSTRING_LEN;
564 	}
565 }
566 
567 static void mlxsw_sp_port_get_strings(struct net_device *dev,
568 				      u32 stringset, u8 *data)
569 {
570 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
571 	u8 *p = data;
572 	int i;
573 
574 	switch (stringset) {
575 	case ETH_SS_STATS:
576 		for (i = 0; i < MLXSW_SP_PORT_HW_STATS_LEN; i++) {
577 			memcpy(p, mlxsw_sp_port_hw_stats[i].str,
578 			       ETH_GSTRING_LEN);
579 			p += ETH_GSTRING_LEN;
580 		}
581 
582 		for (i = 0; i < MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN; i++) {
583 			memcpy(p, mlxsw_sp_port_hw_rfc_2863_stats[i].str,
584 			       ETH_GSTRING_LEN);
585 			p += ETH_GSTRING_LEN;
586 		}
587 
588 		for (i = 0; i < MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN; i++) {
589 			memcpy(p, mlxsw_sp_port_hw_rfc_2819_stats[i].str,
590 			       ETH_GSTRING_LEN);
591 			p += ETH_GSTRING_LEN;
592 		}
593 
594 		for (i = 0; i < MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN; i++) {
595 			memcpy(p, mlxsw_sp_port_hw_rfc_3635_stats[i].str,
596 			       ETH_GSTRING_LEN);
597 			p += ETH_GSTRING_LEN;
598 		}
599 
600 		for (i = 0; i < MLXSW_SP_PORT_HW_EXT_STATS_LEN; i++) {
601 			memcpy(p, mlxsw_sp_port_hw_ext_stats[i].str,
602 			       ETH_GSTRING_LEN);
603 			p += ETH_GSTRING_LEN;
604 		}
605 
606 		for (i = 0; i < MLXSW_SP_PORT_HW_DISCARD_STATS_LEN; i++) {
607 			memcpy(p, mlxsw_sp_port_hw_discard_stats[i].str,
608 			       ETH_GSTRING_LEN);
609 			p += ETH_GSTRING_LEN;
610 		}
611 
612 		for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
613 			mlxsw_sp_port_get_prio_strings(&p, i);
614 
615 		for (i = 0; i < TC_MAX_QUEUE; i++)
616 			mlxsw_sp_port_get_tc_strings(&p, i);
617 
618 		mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats_strings(&p);
619 		break;
620 	}
621 }
622 
623 static int mlxsw_sp_port_set_phys_id(struct net_device *dev,
624 				     enum ethtool_phys_id_state state)
625 {
626 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
627 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
628 	char mlcr_pl[MLXSW_REG_MLCR_LEN];
629 	bool active;
630 
631 	switch (state) {
632 	case ETHTOOL_ID_ACTIVE:
633 		active = true;
634 		break;
635 	case ETHTOOL_ID_INACTIVE:
636 		active = false;
637 		break;
638 	default:
639 		return -EOPNOTSUPP;
640 	}
641 
642 	mlxsw_reg_mlcr_pack(mlcr_pl, mlxsw_sp_port->local_port, active);
643 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mlcr), mlcr_pl);
644 }
645 
646 static int
647 mlxsw_sp_get_hw_stats_by_group(struct mlxsw_sp_port_hw_stats **p_hw_stats,
648 			       int *p_len, enum mlxsw_reg_ppcnt_grp grp)
649 {
650 	switch (grp) {
651 	case MLXSW_REG_PPCNT_IEEE_8023_CNT:
652 		*p_hw_stats = mlxsw_sp_port_hw_stats;
653 		*p_len = MLXSW_SP_PORT_HW_STATS_LEN;
654 		break;
655 	case MLXSW_REG_PPCNT_RFC_2863_CNT:
656 		*p_hw_stats = mlxsw_sp_port_hw_rfc_2863_stats;
657 		*p_len = MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN;
658 		break;
659 	case MLXSW_REG_PPCNT_RFC_2819_CNT:
660 		*p_hw_stats = mlxsw_sp_port_hw_rfc_2819_stats;
661 		*p_len = MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN;
662 		break;
663 	case MLXSW_REG_PPCNT_RFC_3635_CNT:
664 		*p_hw_stats = mlxsw_sp_port_hw_rfc_3635_stats;
665 		*p_len = MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN;
666 		break;
667 	case MLXSW_REG_PPCNT_EXT_CNT:
668 		*p_hw_stats = mlxsw_sp_port_hw_ext_stats;
669 		*p_len = MLXSW_SP_PORT_HW_EXT_STATS_LEN;
670 		break;
671 	case MLXSW_REG_PPCNT_DISCARD_CNT:
672 		*p_hw_stats = mlxsw_sp_port_hw_discard_stats;
673 		*p_len = MLXSW_SP_PORT_HW_DISCARD_STATS_LEN;
674 		break;
675 	case MLXSW_REG_PPCNT_PRIO_CNT:
676 		*p_hw_stats = mlxsw_sp_port_hw_prio_stats;
677 		*p_len = MLXSW_SP_PORT_HW_PRIO_STATS_LEN;
678 		break;
679 	case MLXSW_REG_PPCNT_TC_CNT:
680 		*p_hw_stats = mlxsw_sp_port_hw_tc_stats;
681 		*p_len = MLXSW_SP_PORT_HW_TC_STATS_LEN;
682 		break;
683 	default:
684 		WARN_ON(1);
685 		return -EOPNOTSUPP;
686 	}
687 	return 0;
688 }
689 
690 static void __mlxsw_sp_port_get_stats(struct net_device *dev,
691 				      enum mlxsw_reg_ppcnt_grp grp, int prio,
692 				      u64 *data, int data_index)
693 {
694 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
695 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
696 	struct mlxsw_sp_port_hw_stats *hw_stats;
697 	char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
698 	int i, len;
699 	int err;
700 
701 	err = mlxsw_sp_get_hw_stats_by_group(&hw_stats, &len, grp);
702 	if (err)
703 		return;
704 	mlxsw_sp_port_get_stats_raw(dev, grp, prio, ppcnt_pl);
705 	for (i = 0; i < len; i++) {
706 		data[data_index + i] = hw_stats[i].getter(ppcnt_pl);
707 		if (!hw_stats[i].cells_bytes)
708 			continue;
709 		data[data_index + i] = mlxsw_sp_cells_bytes(mlxsw_sp,
710 							    data[data_index + i]);
711 	}
712 }
713 
714 static void mlxsw_sp_port_get_stats(struct net_device *dev,
715 				    struct ethtool_stats *stats, u64 *data)
716 {
717 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
718 	int i, data_index = 0;
719 
720 	/* IEEE 802.3 Counters */
721 	__mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT, 0,
722 				  data, data_index);
723 	data_index = MLXSW_SP_PORT_HW_STATS_LEN;
724 
725 	/* RFC 2863 Counters */
726 	__mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_2863_CNT, 0,
727 				  data, data_index);
728 	data_index += MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN;
729 
730 	/* RFC 2819 Counters */
731 	__mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_2819_CNT, 0,
732 				  data, data_index);
733 	data_index += MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN;
734 
735 	/* RFC 3635 Counters */
736 	__mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_3635_CNT, 0,
737 				  data, data_index);
738 	data_index += MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN;
739 
740 	/* Extended Counters */
741 	__mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_EXT_CNT, 0,
742 				  data, data_index);
743 	data_index += MLXSW_SP_PORT_HW_EXT_STATS_LEN;
744 
745 	/* Discard Counters */
746 	__mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_DISCARD_CNT, 0,
747 				  data, data_index);
748 	data_index += MLXSW_SP_PORT_HW_DISCARD_STATS_LEN;
749 
750 	/* Per-Priority Counters */
751 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
752 		__mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_PRIO_CNT, i,
753 					  data, data_index);
754 		data_index += MLXSW_SP_PORT_HW_PRIO_STATS_LEN;
755 	}
756 
757 	/* Per-TC Counters */
758 	for (i = 0; i < TC_MAX_QUEUE; i++) {
759 		__mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_TC_CNT, i,
760 					  data, data_index);
761 		data_index += MLXSW_SP_PORT_HW_TC_STATS_LEN;
762 	}
763 
764 	/* PTP counters */
765 	mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats(mlxsw_sp_port,
766 						    data, data_index);
767 	data_index += mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats_count();
768 }
769 
770 static int mlxsw_sp_port_get_sset_count(struct net_device *dev, int sset)
771 {
772 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
773 
774 	switch (sset) {
775 	case ETH_SS_STATS:
776 		return MLXSW_SP_PORT_ETHTOOL_STATS_LEN +
777 			mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats_count();
778 	default:
779 		return -EOPNOTSUPP;
780 	}
781 }
782 
783 static void
784 mlxsw_sp_port_get_link_supported(struct mlxsw_sp *mlxsw_sp, u32 eth_proto_cap,
785 				 u8 width, struct ethtool_link_ksettings *cmd)
786 {
787 	const struct mlxsw_sp_port_type_speed_ops *ops;
788 
789 	ops = mlxsw_sp->port_type_speed_ops;
790 
791 	ethtool_link_ksettings_add_link_mode(cmd, supported, Asym_Pause);
792 	ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
793 	ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);
794 
795 	ops->from_ptys_supported_port(mlxsw_sp, eth_proto_cap, cmd);
796 	ops->from_ptys_link(mlxsw_sp, eth_proto_cap, width,
797 			    cmd->link_modes.supported);
798 }
799 
800 static void
801 mlxsw_sp_port_get_link_advertise(struct mlxsw_sp *mlxsw_sp,
802 				 u32 eth_proto_admin, bool autoneg, u8 width,
803 				 struct ethtool_link_ksettings *cmd)
804 {
805 	const struct mlxsw_sp_port_type_speed_ops *ops;
806 
807 	ops = mlxsw_sp->port_type_speed_ops;
808 
809 	if (!autoneg)
810 		return;
811 
812 	ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
813 	ops->from_ptys_link(mlxsw_sp, eth_proto_admin, width,
814 			    cmd->link_modes.advertising);
815 }
816 
817 static u8
818 mlxsw_sp_port_connector_port(enum mlxsw_reg_ptys_connector_type connector_type)
819 {
820 	switch (connector_type) {
821 	case MLXSW_REG_PTYS_CONNECTOR_TYPE_UNKNOWN_OR_NO_CONNECTOR:
822 		return PORT_OTHER;
823 	case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_NONE:
824 		return PORT_NONE;
825 	case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_TP:
826 		return PORT_TP;
827 	case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_AUI:
828 		return PORT_AUI;
829 	case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_BNC:
830 		return PORT_BNC;
831 	case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_MII:
832 		return PORT_MII;
833 	case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_FIBRE:
834 		return PORT_FIBRE;
835 	case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_DA:
836 		return PORT_DA;
837 	case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_OTHER:
838 		return PORT_OTHER;
839 	default:
840 		WARN_ON_ONCE(1);
841 		return PORT_OTHER;
842 	}
843 }
844 
845 static int mlxsw_sp_port_get_link_ksettings(struct net_device *dev,
846 					    struct ethtool_link_ksettings *cmd)
847 {
848 	u32 eth_proto_cap, eth_proto_admin, eth_proto_oper;
849 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
850 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
851 	const struct mlxsw_sp_port_type_speed_ops *ops;
852 	char ptys_pl[MLXSW_REG_PTYS_LEN];
853 	u8 connector_type;
854 	bool autoneg;
855 	int err;
856 
857 	ops = mlxsw_sp->port_type_speed_ops;
858 
859 	autoneg = mlxsw_sp_port->link.autoneg;
860 	ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
861 			       0, false);
862 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
863 	if (err)
864 		return err;
865 	ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, &eth_proto_cap,
866 				 &eth_proto_admin, &eth_proto_oper);
867 
868 	mlxsw_sp_port_get_link_supported(mlxsw_sp, eth_proto_cap,
869 					 mlxsw_sp_port->mapping.width, cmd);
870 
871 	mlxsw_sp_port_get_link_advertise(mlxsw_sp, eth_proto_admin, autoneg,
872 					 mlxsw_sp_port->mapping.width, cmd);
873 
874 	cmd->base.autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
875 	connector_type = mlxsw_reg_ptys_connector_type_get(ptys_pl);
876 	cmd->base.port = mlxsw_sp_port_connector_port(connector_type);
877 	ops->from_ptys_speed_duplex(mlxsw_sp, netif_carrier_ok(dev),
878 				    eth_proto_oper, cmd);
879 
880 	return 0;
881 }
882 
883 static int
884 mlxsw_sp_port_set_link_ksettings(struct net_device *dev,
885 				 const struct ethtool_link_ksettings *cmd)
886 {
887 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
888 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
889 	const struct mlxsw_sp_port_type_speed_ops *ops;
890 	char ptys_pl[MLXSW_REG_PTYS_LEN];
891 	u32 eth_proto_cap, eth_proto_new;
892 	bool autoneg;
893 	int err;
894 
895 	ops = mlxsw_sp->port_type_speed_ops;
896 
897 	ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
898 			       0, false);
899 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
900 	if (err)
901 		return err;
902 	ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, &eth_proto_cap, NULL, NULL);
903 
904 	autoneg = cmd->base.autoneg == AUTONEG_ENABLE;
905 	eth_proto_new = autoneg ?
906 		ops->to_ptys_advert_link(mlxsw_sp, mlxsw_sp_port->mapping.width,
907 					 cmd) :
908 		ops->to_ptys_speed(mlxsw_sp, mlxsw_sp_port->mapping.width,
909 				   cmd->base.speed);
910 
911 	eth_proto_new = eth_proto_new & eth_proto_cap;
912 	if (!eth_proto_new) {
913 		netdev_err(dev, "No supported speed requested\n");
914 		return -EINVAL;
915 	}
916 
917 	ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
918 			       eth_proto_new, autoneg);
919 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
920 	if (err)
921 		return err;
922 
923 	mlxsw_sp_port->link.autoneg = autoneg;
924 
925 	if (!netif_running(dev))
926 		return 0;
927 
928 	mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
929 	mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true);
930 
931 	return 0;
932 }
933 
934 static int mlxsw_sp_get_module_info(struct net_device *netdev,
935 				    struct ethtool_modinfo *modinfo)
936 {
937 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
938 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
939 	int err;
940 
941 	err = mlxsw_env_get_module_info(mlxsw_sp->core,
942 					mlxsw_sp_port->mapping.module,
943 					modinfo);
944 
945 	return err;
946 }
947 
948 static int mlxsw_sp_get_module_eeprom(struct net_device *netdev,
949 				      struct ethtool_eeprom *ee, u8 *data)
950 {
951 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
952 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
953 	int err;
954 
955 	err = mlxsw_env_get_module_eeprom(netdev, mlxsw_sp->core,
956 					  mlxsw_sp_port->mapping.module, ee,
957 					  data);
958 
959 	return err;
960 }
961 
962 static int
963 mlxsw_sp_get_ts_info(struct net_device *netdev, struct ethtool_ts_info *info)
964 {
965 	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
966 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
967 
968 	return mlxsw_sp->ptp_ops->get_ts_info(mlxsw_sp, info);
969 }
970 
971 const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
972 	.get_drvinfo		= mlxsw_sp_port_get_drvinfo,
973 	.get_link		= ethtool_op_get_link,
974 	.get_link_ext_state	= mlxsw_sp_port_get_link_ext_state,
975 	.get_pauseparam		= mlxsw_sp_port_get_pauseparam,
976 	.set_pauseparam		= mlxsw_sp_port_set_pauseparam,
977 	.get_strings		= mlxsw_sp_port_get_strings,
978 	.set_phys_id		= mlxsw_sp_port_set_phys_id,
979 	.get_ethtool_stats	= mlxsw_sp_port_get_stats,
980 	.get_sset_count		= mlxsw_sp_port_get_sset_count,
981 	.get_link_ksettings	= mlxsw_sp_port_get_link_ksettings,
982 	.set_link_ksettings	= mlxsw_sp_port_set_link_ksettings,
983 	.get_module_info	= mlxsw_sp_get_module_info,
984 	.get_module_eeprom	= mlxsw_sp_get_module_eeprom,
985 	.get_ts_info		= mlxsw_sp_get_ts_info,
986 };
987 
988 struct mlxsw_sp1_port_link_mode {
989 	enum ethtool_link_mode_bit_indices mask_ethtool;
990 	u32 mask;
991 	u32 speed;
992 };
993 
994 static const struct mlxsw_sp1_port_link_mode mlxsw_sp1_port_link_mode[] = {
995 	{
996 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_100BASE_T,
997 		.mask_ethtool	= ETHTOOL_LINK_MODE_100baseT_Full_BIT,
998 		.speed		= SPEED_100,
999 	},
1000 	{
1001 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_SGMII |
1002 				  MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX,
1003 		.mask_ethtool	= ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
1004 		.speed		= SPEED_1000,
1005 	},
1006 	{
1007 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_10GBASE_T,
1008 		.mask_ethtool	= ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
1009 		.speed		= SPEED_10000,
1010 	},
1011 	{
1012 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CX4 |
1013 				  MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4,
1014 		.mask_ethtool	= ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
1015 		.speed		= SPEED_10000,
1016 	},
1017 	{
1018 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
1019 				  MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
1020 				  MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
1021 				  MLXSW_REG_PTYS_ETH_SPEED_10GBASE_ER_LR,
1022 		.mask_ethtool	= ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
1023 		.speed		= SPEED_10000,
1024 	},
1025 	{
1026 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_20GBASE_KR2,
1027 		.mask_ethtool	= ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
1028 		.speed		= SPEED_20000,
1029 	},
1030 	{
1031 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4,
1032 		.mask_ethtool	= ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
1033 		.speed		= SPEED_40000,
1034 	},
1035 	{
1036 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4,
1037 		.mask_ethtool	= ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
1038 		.speed		= SPEED_40000,
1039 	},
1040 	{
1041 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4,
1042 		.mask_ethtool	= ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
1043 		.speed		= SPEED_40000,
1044 	},
1045 	{
1046 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_40GBASE_LR4_ER4,
1047 		.mask_ethtool	= ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
1048 		.speed		= SPEED_40000,
1049 	},
1050 	{
1051 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_25GBASE_CR,
1052 		.mask_ethtool	= ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
1053 		.speed		= SPEED_25000,
1054 	},
1055 	{
1056 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_25GBASE_KR,
1057 		.mask_ethtool	= ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
1058 		.speed		= SPEED_25000,
1059 	},
1060 	{
1061 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_25GBASE_SR,
1062 		.mask_ethtool	= ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
1063 		.speed		= SPEED_25000,
1064 	},
1065 	{
1066 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_50GBASE_CR2,
1067 		.mask_ethtool	= ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
1068 		.speed		= SPEED_50000,
1069 	},
1070 	{
1071 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR2,
1072 		.mask_ethtool	= ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
1073 		.speed		= SPEED_50000,
1074 	},
1075 	{
1076 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_50GBASE_SR2,
1077 		.mask_ethtool	= ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
1078 		.speed		= SPEED_50000,
1079 	},
1080 	{
1081 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4,
1082 		.mask_ethtool	= ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
1083 		.speed		= SPEED_100000,
1084 	},
1085 	{
1086 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4,
1087 		.mask_ethtool	= ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
1088 		.speed		= SPEED_100000,
1089 	},
1090 	{
1091 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4,
1092 		.mask_ethtool	= ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
1093 		.speed		= SPEED_100000,
1094 	},
1095 	{
1096 		.mask		= MLXSW_REG_PTYS_ETH_SPEED_100GBASE_LR4_ER4,
1097 		.mask_ethtool	= ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
1098 		.speed		= SPEED_100000,
1099 	},
1100 };
1101 
1102 #define MLXSW_SP1_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sp1_port_link_mode)
1103 
1104 static void
1105 mlxsw_sp1_from_ptys_supported_port(struct mlxsw_sp *mlxsw_sp,
1106 				   u32 ptys_eth_proto,
1107 				   struct ethtool_link_ksettings *cmd)
1108 {
1109 	if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
1110 			      MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
1111 			      MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 |
1112 			      MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 |
1113 			      MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 |
1114 			      MLXSW_REG_PTYS_ETH_SPEED_SGMII))
1115 		ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
1116 
1117 	if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
1118 			      MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 |
1119 			      MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 |
1120 			      MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 |
1121 			      MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX))
1122 		ethtool_link_ksettings_add_link_mode(cmd, supported, Backplane);
1123 }
1124 
1125 static void
1126 mlxsw_sp1_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
1127 			 u8 width, unsigned long *mode)
1128 {
1129 	int i;
1130 
1131 	for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
1132 		if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask)
1133 			__set_bit(mlxsw_sp1_port_link_mode[i].mask_ethtool,
1134 				  mode);
1135 	}
1136 }
1137 
1138 static u32
1139 mlxsw_sp1_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto)
1140 {
1141 	int i;
1142 
1143 	for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
1144 		if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask)
1145 			return mlxsw_sp1_port_link_mode[i].speed;
1146 	}
1147 
1148 	return SPEED_UNKNOWN;
1149 }
1150 
1151 static void
1152 mlxsw_sp1_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
1153 				 u32 ptys_eth_proto,
1154 				 struct ethtool_link_ksettings *cmd)
1155 {
1156 	cmd->base.speed = SPEED_UNKNOWN;
1157 	cmd->base.duplex = DUPLEX_UNKNOWN;
1158 
1159 	if (!carrier_ok)
1160 		return;
1161 
1162 	cmd->base.speed = mlxsw_sp1_from_ptys_speed(mlxsw_sp, ptys_eth_proto);
1163 	if (cmd->base.speed != SPEED_UNKNOWN)
1164 		cmd->base.duplex = DUPLEX_FULL;
1165 }
1166 
1167 static u32
1168 mlxsw_sp1_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp, u8 width,
1169 			      const struct ethtool_link_ksettings *cmd)
1170 {
1171 	u32 ptys_proto = 0;
1172 	int i;
1173 
1174 	for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
1175 		if (test_bit(mlxsw_sp1_port_link_mode[i].mask_ethtool,
1176 			     cmd->link_modes.advertising))
1177 			ptys_proto |= mlxsw_sp1_port_link_mode[i].mask;
1178 	}
1179 	return ptys_proto;
1180 }
1181 
1182 static u32 mlxsw_sp1_to_ptys_speed(struct mlxsw_sp *mlxsw_sp, u8 width,
1183 				   u32 speed)
1184 {
1185 	u32 ptys_proto = 0;
1186 	int i;
1187 
1188 	for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
1189 		if (speed == mlxsw_sp1_port_link_mode[i].speed)
1190 			ptys_proto |= mlxsw_sp1_port_link_mode[i].mask;
1191 	}
1192 	return ptys_proto;
1193 }
1194 
1195 static void
1196 mlxsw_sp1_reg_ptys_eth_pack(struct mlxsw_sp *mlxsw_sp, char *payload,
1197 			    u8 local_port, u32 proto_admin, bool autoneg)
1198 {
1199 	mlxsw_reg_ptys_eth_pack(payload, local_port, proto_admin, autoneg);
1200 }
1201 
1202 static void
1203 mlxsw_sp1_reg_ptys_eth_unpack(struct mlxsw_sp *mlxsw_sp, char *payload,
1204 			      u32 *p_eth_proto_cap, u32 *p_eth_proto_admin,
1205 			      u32 *p_eth_proto_oper)
1206 {
1207 	mlxsw_reg_ptys_eth_unpack(payload, p_eth_proto_cap, p_eth_proto_admin,
1208 				  p_eth_proto_oper);
1209 }
1210 
1211 const struct mlxsw_sp_port_type_speed_ops mlxsw_sp1_port_type_speed_ops = {
1212 	.from_ptys_supported_port	= mlxsw_sp1_from_ptys_supported_port,
1213 	.from_ptys_link			= mlxsw_sp1_from_ptys_link,
1214 	.from_ptys_speed		= mlxsw_sp1_from_ptys_speed,
1215 	.from_ptys_speed_duplex		= mlxsw_sp1_from_ptys_speed_duplex,
1216 	.to_ptys_advert_link		= mlxsw_sp1_to_ptys_advert_link,
1217 	.to_ptys_speed			= mlxsw_sp1_to_ptys_speed,
1218 	.reg_ptys_eth_pack		= mlxsw_sp1_reg_ptys_eth_pack,
1219 	.reg_ptys_eth_unpack		= mlxsw_sp1_reg_ptys_eth_unpack,
1220 };
1221 
1222 static const enum ethtool_link_mode_bit_indices
1223 mlxsw_sp2_mask_ethtool_sgmii_100m[] = {
1224 	ETHTOOL_LINK_MODE_100baseT_Full_BIT,
1225 };
1226 
1227 #define MLXSW_SP2_MASK_ETHTOOL_SGMII_100M_LEN \
1228 	ARRAY_SIZE(mlxsw_sp2_mask_ethtool_sgmii_100m)
1229 
1230 static const enum ethtool_link_mode_bit_indices
1231 mlxsw_sp2_mask_ethtool_1000base_x_sgmii[] = {
1232 	ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
1233 	ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
1234 };
1235 
1236 #define MLXSW_SP2_MASK_ETHTOOL_1000BASE_X_SGMII_LEN \
1237 	ARRAY_SIZE(mlxsw_sp2_mask_ethtool_1000base_x_sgmii)
1238 
1239 static const enum ethtool_link_mode_bit_indices
1240 mlxsw_sp2_mask_ethtool_2_5gbase_x_2_5gmii[] = {
1241 	ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
1242 };
1243 
1244 #define MLXSW_SP2_MASK_ETHTOOL_2_5GBASE_X_2_5GMII_LEN \
1245 	ARRAY_SIZE(mlxsw_sp2_mask_ethtool_2_5gbase_x_2_5gmii)
1246 
1247 static const enum ethtool_link_mode_bit_indices
1248 mlxsw_sp2_mask_ethtool_5gbase_r[] = {
1249 	ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
1250 };
1251 
1252 #define MLXSW_SP2_MASK_ETHTOOL_5GBASE_R_LEN \
1253 	ARRAY_SIZE(mlxsw_sp2_mask_ethtool_5gbase_r)
1254 
1255 static const enum ethtool_link_mode_bit_indices
1256 mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g[] = {
1257 	ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
1258 	ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
1259 	ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
1260 	ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
1261 	ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
1262 	ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
1263 	ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
1264 };
1265 
1266 #define MLXSW_SP2_MASK_ETHTOOL_XFI_XAUI_1_10G_LEN \
1267 	ARRAY_SIZE(mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g)
1268 
1269 static const enum ethtool_link_mode_bit_indices
1270 mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g[] = {
1271 	ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
1272 	ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
1273 	ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
1274 	ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
1275 };
1276 
1277 #define MLXSW_SP2_MASK_ETHTOOL_XLAUI_4_XLPPI_4_40G_LEN \
1278 	ARRAY_SIZE(mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g)
1279 
1280 static const enum ethtool_link_mode_bit_indices
1281 mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr[] = {
1282 	ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
1283 	ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
1284 	ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
1285 };
1286 
1287 #define MLXSW_SP2_MASK_ETHTOOL_25GAUI_1_25GBASE_CR_KR_LEN \
1288 	ARRAY_SIZE(mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr)
1289 
1290 static const enum ethtool_link_mode_bit_indices
1291 mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2[] = {
1292 	ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
1293 	ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
1294 	ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
1295 };
1296 
1297 #define MLXSW_SP2_MASK_ETHTOOL_50GAUI_2_LAUI_2_50GBASE_CR2_KR2_LEN \
1298 	ARRAY_SIZE(mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2)
1299 
1300 static const enum ethtool_link_mode_bit_indices
1301 mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr[] = {
1302 	ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
1303 	ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
1304 	ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
1305 	ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
1306 	ETHTOOL_LINK_MODE_50000baseDR_Full_BIT,
1307 };
1308 
1309 #define MLXSW_SP2_MASK_ETHTOOL_50GAUI_1_LAUI_1_50GBASE_CR_KR_LEN \
1310 	ARRAY_SIZE(mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr)
1311 
1312 static const enum ethtool_link_mode_bit_indices
1313 mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4[] = {
1314 	ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
1315 	ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
1316 	ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
1317 	ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
1318 };
1319 
1320 #define MLXSW_SP2_MASK_ETHTOOL_CAUI_4_100GBASE_CR4_KR4_LEN \
1321 	ARRAY_SIZE(mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4)
1322 
1323 static const enum ethtool_link_mode_bit_indices
1324 mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2[] = {
1325 	ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
1326 	ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
1327 	ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
1328 	ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
1329 	ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT,
1330 };
1331 
1332 #define MLXSW_SP2_MASK_ETHTOOL_100GAUI_2_100GBASE_CR2_KR2_LEN \
1333 	ARRAY_SIZE(mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2)
1334 
1335 static const enum ethtool_link_mode_bit_indices
1336 mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4[] = {
1337 	ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
1338 	ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
1339 	ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
1340 	ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT,
1341 	ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
1342 };
1343 
1344 #define MLXSW_SP2_MASK_ETHTOOL_200GAUI_4_200GBASE_CR4_KR4_LEN \
1345 	ARRAY_SIZE(mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4)
1346 
1347 static const enum ethtool_link_mode_bit_indices
1348 mlxsw_sp2_mask_ethtool_400gaui_8[] = {
1349 	ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT,
1350 	ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT,
1351 	ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
1352 	ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT,
1353 	ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT,
1354 };
1355 
1356 #define MLXSW_SP2_MASK_ETHTOOL_400GAUI_8_LEN \
1357 	ARRAY_SIZE(mlxsw_sp2_mask_ethtool_400gaui_8)
1358 
1359 #define MLXSW_SP_PORT_MASK_WIDTH_1X	BIT(0)
1360 #define MLXSW_SP_PORT_MASK_WIDTH_2X	BIT(1)
1361 #define MLXSW_SP_PORT_MASK_WIDTH_4X	BIT(2)
1362 #define MLXSW_SP_PORT_MASK_WIDTH_8X	BIT(3)
1363 
1364 static u8 mlxsw_sp_port_mask_width_get(u8 width)
1365 {
1366 	switch (width) {
1367 	case 1:
1368 		return MLXSW_SP_PORT_MASK_WIDTH_1X;
1369 	case 2:
1370 		return MLXSW_SP_PORT_MASK_WIDTH_2X;
1371 	case 4:
1372 		return MLXSW_SP_PORT_MASK_WIDTH_4X;
1373 	case 8:
1374 		return MLXSW_SP_PORT_MASK_WIDTH_8X;
1375 	default:
1376 		WARN_ON_ONCE(1);
1377 		return 0;
1378 	}
1379 }
1380 
1381 struct mlxsw_sp2_port_link_mode {
1382 	const enum ethtool_link_mode_bit_indices *mask_ethtool;
1383 	int m_ethtool_len;
1384 	u32 mask;
1385 	u32 speed;
1386 	u8 mask_width;
1387 };
1388 
1389 static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = {
1390 	{
1391 		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_SGMII_100M,
1392 		.mask_ethtool	= mlxsw_sp2_mask_ethtool_sgmii_100m,
1393 		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_SGMII_100M_LEN,
1394 		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
1395 				  MLXSW_SP_PORT_MASK_WIDTH_2X |
1396 				  MLXSW_SP_PORT_MASK_WIDTH_4X |
1397 				  MLXSW_SP_PORT_MASK_WIDTH_8X,
1398 		.speed		= SPEED_100,
1399 	},
1400 	{
1401 		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_1000BASE_X_SGMII,
1402 		.mask_ethtool	= mlxsw_sp2_mask_ethtool_1000base_x_sgmii,
1403 		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_1000BASE_X_SGMII_LEN,
1404 		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
1405 				  MLXSW_SP_PORT_MASK_WIDTH_2X |
1406 				  MLXSW_SP_PORT_MASK_WIDTH_4X |
1407 				  MLXSW_SP_PORT_MASK_WIDTH_8X,
1408 		.speed		= SPEED_1000,
1409 	},
1410 	{
1411 		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_2_5GBASE_X_2_5GMII,
1412 		.mask_ethtool	= mlxsw_sp2_mask_ethtool_2_5gbase_x_2_5gmii,
1413 		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_2_5GBASE_X_2_5GMII_LEN,
1414 		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
1415 				  MLXSW_SP_PORT_MASK_WIDTH_2X |
1416 				  MLXSW_SP_PORT_MASK_WIDTH_4X |
1417 				  MLXSW_SP_PORT_MASK_WIDTH_8X,
1418 		.speed		= SPEED_2500,
1419 	},
1420 	{
1421 		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_5GBASE_R,
1422 		.mask_ethtool	= mlxsw_sp2_mask_ethtool_5gbase_r,
1423 		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_5GBASE_R_LEN,
1424 		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
1425 				  MLXSW_SP_PORT_MASK_WIDTH_2X |
1426 				  MLXSW_SP_PORT_MASK_WIDTH_4X |
1427 				  MLXSW_SP_PORT_MASK_WIDTH_8X,
1428 		.speed		= SPEED_5000,
1429 	},
1430 	{
1431 		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_XFI_XAUI_1_10G,
1432 		.mask_ethtool	= mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g,
1433 		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_XFI_XAUI_1_10G_LEN,
1434 		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
1435 				  MLXSW_SP_PORT_MASK_WIDTH_2X |
1436 				  MLXSW_SP_PORT_MASK_WIDTH_4X |
1437 				  MLXSW_SP_PORT_MASK_WIDTH_8X,
1438 		.speed		= SPEED_10000,
1439 	},
1440 	{
1441 		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_XLAUI_4_XLPPI_4_40G,
1442 		.mask_ethtool	= mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g,
1443 		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_XLAUI_4_XLPPI_4_40G_LEN,
1444 		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_4X |
1445 				  MLXSW_SP_PORT_MASK_WIDTH_8X,
1446 		.speed		= SPEED_40000,
1447 	},
1448 	{
1449 		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_25GAUI_1_25GBASE_CR_KR,
1450 		.mask_ethtool	= mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr,
1451 		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_25GAUI_1_25GBASE_CR_KR_LEN,
1452 		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_1X |
1453 				  MLXSW_SP_PORT_MASK_WIDTH_2X |
1454 				  MLXSW_SP_PORT_MASK_WIDTH_4X |
1455 				  MLXSW_SP_PORT_MASK_WIDTH_8X,
1456 		.speed		= SPEED_25000,
1457 	},
1458 	{
1459 		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_50GAUI_2_LAUI_2_50GBASE_CR2_KR2,
1460 		.mask_ethtool	= mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2,
1461 		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_50GAUI_2_LAUI_2_50GBASE_CR2_KR2_LEN,
1462 		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_2X |
1463 				  MLXSW_SP_PORT_MASK_WIDTH_4X |
1464 				  MLXSW_SP_PORT_MASK_WIDTH_8X,
1465 		.speed		= SPEED_50000,
1466 	},
1467 	{
1468 		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_50GAUI_1_LAUI_1_50GBASE_CR_KR,
1469 		.mask_ethtool	= mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr,
1470 		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_50GAUI_1_LAUI_1_50GBASE_CR_KR_LEN,
1471 		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_1X,
1472 		.speed		= SPEED_50000,
1473 	},
1474 	{
1475 		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_CAUI_4_100GBASE_CR4_KR4,
1476 		.mask_ethtool	= mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4,
1477 		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_CAUI_4_100GBASE_CR4_KR4_LEN,
1478 		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_4X |
1479 				  MLXSW_SP_PORT_MASK_WIDTH_8X,
1480 		.speed		= SPEED_100000,
1481 	},
1482 	{
1483 		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_100GAUI_2_100GBASE_CR2_KR2,
1484 		.mask_ethtool	= mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2,
1485 		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_100GAUI_2_100GBASE_CR2_KR2_LEN,
1486 		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_2X,
1487 		.speed		= SPEED_100000,
1488 	},
1489 	{
1490 		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_200GAUI_4_200GBASE_CR4_KR4,
1491 		.mask_ethtool	= mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4,
1492 		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_200GAUI_4_200GBASE_CR4_KR4_LEN,
1493 		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_4X |
1494 				  MLXSW_SP_PORT_MASK_WIDTH_8X,
1495 		.speed		= SPEED_200000,
1496 	},
1497 	{
1498 		.mask		= MLXSW_REG_PTYS_EXT_ETH_SPEED_400GAUI_8,
1499 		.mask_ethtool	= mlxsw_sp2_mask_ethtool_400gaui_8,
1500 		.m_ethtool_len	= MLXSW_SP2_MASK_ETHTOOL_400GAUI_8_LEN,
1501 		.mask_width	= MLXSW_SP_PORT_MASK_WIDTH_8X,
1502 		.speed		= SPEED_400000,
1503 	},
1504 };
1505 
1506 #define MLXSW_SP2_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sp2_port_link_mode)
1507 
1508 static void
1509 mlxsw_sp2_from_ptys_supported_port(struct mlxsw_sp *mlxsw_sp,
1510 				   u32 ptys_eth_proto,
1511 				   struct ethtool_link_ksettings *cmd)
1512 {
1513 	ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
1514 	ethtool_link_ksettings_add_link_mode(cmd, supported, Backplane);
1515 }
1516 
1517 static void
1518 mlxsw_sp2_set_bit_ethtool(const struct mlxsw_sp2_port_link_mode *link_mode,
1519 			  unsigned long *mode)
1520 {
1521 	int i;
1522 
1523 	for (i = 0; i < link_mode->m_ethtool_len; i++)
1524 		__set_bit(link_mode->mask_ethtool[i], mode);
1525 }
1526 
1527 static void
1528 mlxsw_sp2_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
1529 			 u8 width, unsigned long *mode)
1530 {
1531 	u8 mask_width = mlxsw_sp_port_mask_width_get(width);
1532 	int i;
1533 
1534 	for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
1535 		if ((ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) &&
1536 		    (mask_width & mlxsw_sp2_port_link_mode[i].mask_width))
1537 			mlxsw_sp2_set_bit_ethtool(&mlxsw_sp2_port_link_mode[i],
1538 						  mode);
1539 	}
1540 }
1541 
1542 static u32
1543 mlxsw_sp2_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto)
1544 {
1545 	int i;
1546 
1547 	for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
1548 		if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask)
1549 			return mlxsw_sp2_port_link_mode[i].speed;
1550 	}
1551 
1552 	return SPEED_UNKNOWN;
1553 }
1554 
1555 static void
1556 mlxsw_sp2_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
1557 				 u32 ptys_eth_proto,
1558 				 struct ethtool_link_ksettings *cmd)
1559 {
1560 	cmd->base.speed = SPEED_UNKNOWN;
1561 	cmd->base.duplex = DUPLEX_UNKNOWN;
1562 
1563 	if (!carrier_ok)
1564 		return;
1565 
1566 	cmd->base.speed = mlxsw_sp2_from_ptys_speed(mlxsw_sp, ptys_eth_proto);
1567 	if (cmd->base.speed != SPEED_UNKNOWN)
1568 		cmd->base.duplex = DUPLEX_FULL;
1569 }
1570 
1571 static bool
1572 mlxsw_sp2_test_bit_ethtool(const struct mlxsw_sp2_port_link_mode *link_mode,
1573 			   const unsigned long *mode)
1574 {
1575 	int cnt = 0;
1576 	int i;
1577 
1578 	for (i = 0; i < link_mode->m_ethtool_len; i++) {
1579 		if (test_bit(link_mode->mask_ethtool[i], mode))
1580 			cnt++;
1581 	}
1582 
1583 	return cnt == link_mode->m_ethtool_len;
1584 }
1585 
1586 static u32
1587 mlxsw_sp2_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp, u8 width,
1588 			      const struct ethtool_link_ksettings *cmd)
1589 {
1590 	u8 mask_width = mlxsw_sp_port_mask_width_get(width);
1591 	u32 ptys_proto = 0;
1592 	int i;
1593 
1594 	for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
1595 		if ((mask_width & mlxsw_sp2_port_link_mode[i].mask_width) &&
1596 		    mlxsw_sp2_test_bit_ethtool(&mlxsw_sp2_port_link_mode[i],
1597 					       cmd->link_modes.advertising))
1598 			ptys_proto |= mlxsw_sp2_port_link_mode[i].mask;
1599 	}
1600 	return ptys_proto;
1601 }
1602 
1603 static u32 mlxsw_sp2_to_ptys_speed(struct mlxsw_sp *mlxsw_sp,
1604 				   u8 width, u32 speed)
1605 {
1606 	u8 mask_width = mlxsw_sp_port_mask_width_get(width);
1607 	u32 ptys_proto = 0;
1608 	int i;
1609 
1610 	for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
1611 		if ((speed == mlxsw_sp2_port_link_mode[i].speed) &&
1612 		    (mask_width & mlxsw_sp2_port_link_mode[i].mask_width))
1613 			ptys_proto |= mlxsw_sp2_port_link_mode[i].mask;
1614 	}
1615 	return ptys_proto;
1616 }
1617 
1618 static void
1619 mlxsw_sp2_reg_ptys_eth_pack(struct mlxsw_sp *mlxsw_sp, char *payload,
1620 			    u8 local_port, u32 proto_admin,
1621 			    bool autoneg)
1622 {
1623 	mlxsw_reg_ptys_ext_eth_pack(payload, local_port, proto_admin, autoneg);
1624 }
1625 
1626 static void
1627 mlxsw_sp2_reg_ptys_eth_unpack(struct mlxsw_sp *mlxsw_sp, char *payload,
1628 			      u32 *p_eth_proto_cap, u32 *p_eth_proto_admin,
1629 			      u32 *p_eth_proto_oper)
1630 {
1631 	mlxsw_reg_ptys_ext_eth_unpack(payload, p_eth_proto_cap,
1632 				      p_eth_proto_admin, p_eth_proto_oper);
1633 }
1634 
1635 const struct mlxsw_sp_port_type_speed_ops mlxsw_sp2_port_type_speed_ops = {
1636 	.from_ptys_supported_port	= mlxsw_sp2_from_ptys_supported_port,
1637 	.from_ptys_link			= mlxsw_sp2_from_ptys_link,
1638 	.from_ptys_speed		= mlxsw_sp2_from_ptys_speed,
1639 	.from_ptys_speed_duplex		= mlxsw_sp2_from_ptys_speed_duplex,
1640 	.to_ptys_advert_link		= mlxsw_sp2_to_ptys_advert_link,
1641 	.to_ptys_speed			= mlxsw_sp2_to_ptys_speed,
1642 	.reg_ptys_eth_pack		= mlxsw_sp2_reg_ptys_eth_pack,
1643 	.reg_ptys_eth_unpack		= mlxsw_sp2_reg_ptys_eth_unpack,
1644 };
1645