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