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
9 static const char mlxsw_sp_driver_version[] = "1.0";
10
mlxsw_sp_port_get_drvinfo(struct net_device * dev,struct ethtool_drvinfo * drvinfo)11 static 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
30 struct 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
36 static const struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping
37 mlxsw_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
101 static void
mlxsw_sp_port_set_link_ext_state(struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping link_ext_state_mapping,struct ethtool_link_ext_state_info * link_ext_state_info)102 mlxsw_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
134 static int
mlxsw_sp_port_get_link_ext_state(struct net_device * dev,struct ethtool_link_ext_state_info * link_ext_state_info)135 mlxsw_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
mlxsw_sp_port_get_pauseparam(struct net_device * dev,struct ethtool_pauseparam * pause)174 static 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
mlxsw_sp_port_pause_set(struct mlxsw_sp_port * mlxsw_sp_port,struct ethtool_pauseparam * pause)183 static 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
mlxsw_sp_port_set_pauseparam(struct net_device * dev,struct ethtool_pauseparam * pause)201 static 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
252 err_port_pause_configure:
253 mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom);
254 return err;
255 }
256
257 struct mlxsw_sp_port_hw_stats {
258 char str[ETH_GSTRING_LEN];
259 u64 (*getter)(const char *payload);
260 bool cells_bytes;
261 };
262
263 static 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
344 static 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
362 static 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
420 static 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
442 static 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
451 static 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
505 static 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
542 static 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
556 struct mlxsw_sp_port_stats {
557 char str[ETH_GSTRING_LEN];
558 u64 (*getter)(struct mlxsw_sp_port *mlxsw_sp_port);
559 };
560
561 static u64
mlxsw_sp_port_get_transceiver_overheat_stats(struct mlxsw_sp_port * mlxsw_sp_port)562 mlxsw_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
578 static 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
mlxsw_sp_port_get_prio_strings(u8 ** p,int prio)599 static 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
mlxsw_sp_port_get_tc_strings(u8 ** p,int tc)610 static 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
mlxsw_sp_port_get_strings(struct net_device * dev,u32 stringset,u8 * data)621 static 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
mlxsw_sp_port_set_phys_id(struct net_device * dev,enum ethtool_phys_id_state state)683 static 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
706 static int
mlxsw_sp_get_hw_stats_by_group(struct mlxsw_sp_port_hw_stats ** p_hw_stats,int * p_len,enum mlxsw_reg_ppcnt_grp grp)707 mlxsw_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
__mlxsw_sp_port_get_stats(struct net_device * dev,enum mlxsw_reg_ppcnt_grp grp,int prio,u64 * data,int data_index)750 static 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
__mlxsw_sp_port_get_env_stats(struct net_device * dev,u64 * data,int data_index,struct mlxsw_sp_port_stats * port_stats,int len)774 static 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
mlxsw_sp_port_get_stats(struct net_device * dev,struct ethtool_stats * stats,u64 * data)785 static 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
mlxsw_sp_port_get_sset_count(struct net_device * dev,int sset)846 static 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
859 static void
mlxsw_sp_port_get_link_supported(struct mlxsw_sp * mlxsw_sp,u32 eth_proto_cap,u8 width,struct ethtool_link_ksettings * cmd)860 mlxsw_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
876 static void
mlxsw_sp_port_get_link_advertise(struct mlxsw_sp * mlxsw_sp,u32 eth_proto_admin,bool autoneg,u8 width,struct ethtool_link_ksettings * cmd)877 mlxsw_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
893 static u8
mlxsw_sp_port_connector_port(enum mlxsw_reg_ptys_connector_type connector_type)894 mlxsw_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
mlxsw_sp_port_ptys_query(struct mlxsw_sp_port * mlxsw_sp_port,u32 * p_eth_proto_cap,u32 * p_eth_proto_admin,u32 * p_eth_proto_oper,u8 * p_connector_type)921 static 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
mlxsw_sp_port_get_link_ksettings(struct net_device * dev,struct ethtool_link_ksettings * cmd)944 static 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, ð_proto_cap, ð_proto_admin,
956 ð_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
977 static int
mlxsw_sp_port_set_link_ksettings(struct net_device * dev,const struct ethtool_link_ksettings * cmd)978 mlxsw_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, ð_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
mlxsw_sp_get_module_info(struct net_device * netdev,struct ethtool_modinfo * modinfo)1028 static 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
mlxsw_sp_get_module_eeprom(struct net_device * netdev,struct ethtool_eeprom * ee,u8 * data)1042 static 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
1056 static int
mlxsw_sp_get_ts_info(struct net_device * netdev,struct ethtool_ts_info * info)1057 mlxsw_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
1065 const 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
1082 struct mlxsw_sp1_port_link_mode {
1083 enum ethtool_link_mode_bit_indices mask_ethtool;
1084 u32 mask;
1085 u32 speed;
1086 };
1087
1088 static 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
1183 static void
mlxsw_sp1_from_ptys_supported_port(struct mlxsw_sp * mlxsw_sp,u32 ptys_eth_proto,struct ethtool_link_ksettings * cmd)1184 mlxsw_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
1204 static void
mlxsw_sp1_from_ptys_link(struct mlxsw_sp * mlxsw_sp,u32 ptys_eth_proto,u8 width,unsigned long * mode)1205 mlxsw_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
1217 static u32
mlxsw_sp1_from_ptys_speed(struct mlxsw_sp * mlxsw_sp,u32 ptys_eth_proto)1218 mlxsw_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
1230 static void
mlxsw_sp1_from_ptys_speed_duplex(struct mlxsw_sp * mlxsw_sp,bool carrier_ok,u32 ptys_eth_proto,struct ethtool_link_ksettings * cmd)1231 mlxsw_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
mlxsw_sp1_ptys_max_speed(struct mlxsw_sp_port * mlxsw_sp_port,u32 * p_max_speed)1246 static 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, ð_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
1267 static u32
mlxsw_sp1_to_ptys_advert_link(struct mlxsw_sp * mlxsw_sp,u8 width,const struct ethtool_link_ksettings * cmd)1268 mlxsw_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
mlxsw_sp1_to_ptys_speed(struct mlxsw_sp * mlxsw_sp,u8 width,u32 speed)1282 static 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
1295 static void
mlxsw_sp1_reg_ptys_eth_pack(struct mlxsw_sp * mlxsw_sp,char * payload,u8 local_port,u32 proto_admin,bool autoneg)1296 mlxsw_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
1302 static void
mlxsw_sp1_reg_ptys_eth_unpack(struct mlxsw_sp * mlxsw_sp,char * payload,u32 * p_eth_proto_cap,u32 * p_eth_proto_admin,u32 * p_eth_proto_oper)1303 mlxsw_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
mlxsw_sp1_ptys_proto_cap_masked_get(u32 eth_proto_cap)1311 static 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
1325 const 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
1338 static const enum ethtool_link_mode_bit_indices
1339 mlxsw_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
1346 static const enum ethtool_link_mode_bit_indices
1347 mlxsw_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
1355 static const enum ethtool_link_mode_bit_indices
1356 mlxsw_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
1363 static const enum ethtool_link_mode_bit_indices
1364 mlxsw_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
1377 static const enum ethtool_link_mode_bit_indices
1378 mlxsw_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
1388 static const enum ethtool_link_mode_bit_indices
1389 mlxsw_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
1398 static const enum ethtool_link_mode_bit_indices
1399 mlxsw_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
1408 static const enum ethtool_link_mode_bit_indices
1409 mlxsw_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
1420 static const enum ethtool_link_mode_bit_indices
1421 mlxsw_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
1431 static const enum ethtool_link_mode_bit_indices
1432 mlxsw_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
1443 static const enum ethtool_link_mode_bit_indices
1444 mlxsw_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
1455 static const enum ethtool_link_mode_bit_indices
1456 mlxsw_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
mlxsw_sp_port_mask_width_get(u8 width)1472 static 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
1489 struct 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
1497 static 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
1606 static void
mlxsw_sp2_from_ptys_supported_port(struct mlxsw_sp * mlxsw_sp,u32 ptys_eth_proto,struct ethtool_link_ksettings * cmd)1607 mlxsw_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
1615 static void
mlxsw_sp2_set_bit_ethtool(const struct mlxsw_sp2_port_link_mode * link_mode,unsigned long * mode)1616 mlxsw_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
1625 static void
mlxsw_sp2_from_ptys_link(struct mlxsw_sp * mlxsw_sp,u32 ptys_eth_proto,u8 width,unsigned long * mode)1626 mlxsw_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
1640 static u32
mlxsw_sp2_from_ptys_speed(struct mlxsw_sp * mlxsw_sp,u32 ptys_eth_proto)1641 mlxsw_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
1653 static void
mlxsw_sp2_from_ptys_speed_duplex(struct mlxsw_sp * mlxsw_sp,bool carrier_ok,u32 ptys_eth_proto,struct ethtool_link_ksettings * cmd)1654 mlxsw_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
mlxsw_sp2_ptys_max_speed(struct mlxsw_sp_port * mlxsw_sp_port,u32 * p_max_speed)1669 static 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, ð_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
1690 static bool
mlxsw_sp2_test_bit_ethtool(const struct mlxsw_sp2_port_link_mode * link_mode,const unsigned long * mode)1691 mlxsw_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
1705 static u32
mlxsw_sp2_to_ptys_advert_link(struct mlxsw_sp * mlxsw_sp,u8 width,const struct ethtool_link_ksettings * cmd)1706 mlxsw_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
mlxsw_sp2_to_ptys_speed(struct mlxsw_sp * mlxsw_sp,u8 width,u32 speed)1722 static 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
1737 static void
mlxsw_sp2_reg_ptys_eth_pack(struct mlxsw_sp * mlxsw_sp,char * payload,u8 local_port,u32 proto_admin,bool autoneg)1738 mlxsw_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
1745 static void
mlxsw_sp2_reg_ptys_eth_unpack(struct mlxsw_sp * mlxsw_sp,char * payload,u32 * p_eth_proto_cap,u32 * p_eth_proto_admin,u32 * p_eth_proto_oper)1746 mlxsw_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
mlxsw_sp2_ptys_proto_cap_masked_get(u32 eth_proto_cap)1754 static 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
1768 const 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