1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
3
4 #include <linux/ethtool.h>
5 #include <linux/kernel.h>
6 #include <linux/netdevice.h>
7
8 #include "prestera_ethtool.h"
9 #include "prestera.h"
10 #include "prestera_hw.h"
11
12 #define PRESTERA_STATS_CNT \
13 (sizeof(struct prestera_port_stats) / sizeof(u64))
14 #define PRESTERA_STATS_IDX(name) \
15 (offsetof(struct prestera_port_stats, name) / sizeof(u64))
16 #define PRESTERA_STATS_FIELD(name) \
17 [PRESTERA_STATS_IDX(name)] = __stringify(name)
18
19 static const char driver_kind[] = "prestera";
20
21 static const struct prestera_link_mode {
22 enum ethtool_link_mode_bit_indices eth_mode;
23 u32 speed;
24 u64 pr_mask;
25 u8 duplex;
26 u8 port_type;
27 } port_link_modes[PRESTERA_LINK_MODE_MAX] = {
28 [PRESTERA_LINK_MODE_10baseT_Half] = {
29 .eth_mode = ETHTOOL_LINK_MODE_10baseT_Half_BIT,
30 .speed = 10,
31 .pr_mask = 1 << PRESTERA_LINK_MODE_10baseT_Half,
32 .duplex = PRESTERA_PORT_DUPLEX_HALF,
33 .port_type = PRESTERA_PORT_TYPE_TP,
34 },
35 [PRESTERA_LINK_MODE_10baseT_Full] = {
36 .eth_mode = ETHTOOL_LINK_MODE_10baseT_Full_BIT,
37 .speed = 10,
38 .pr_mask = 1 << PRESTERA_LINK_MODE_10baseT_Full,
39 .duplex = PRESTERA_PORT_DUPLEX_FULL,
40 .port_type = PRESTERA_PORT_TYPE_TP,
41 },
42 [PRESTERA_LINK_MODE_100baseT_Half] = {
43 .eth_mode = ETHTOOL_LINK_MODE_100baseT_Half_BIT,
44 .speed = 100,
45 .pr_mask = 1 << PRESTERA_LINK_MODE_100baseT_Half,
46 .duplex = PRESTERA_PORT_DUPLEX_HALF,
47 .port_type = PRESTERA_PORT_TYPE_TP,
48 },
49 [PRESTERA_LINK_MODE_100baseT_Full] = {
50 .eth_mode = ETHTOOL_LINK_MODE_100baseT_Full_BIT,
51 .speed = 100,
52 .pr_mask = 1 << PRESTERA_LINK_MODE_100baseT_Full,
53 .duplex = PRESTERA_PORT_DUPLEX_FULL,
54 .port_type = PRESTERA_PORT_TYPE_TP,
55 },
56 [PRESTERA_LINK_MODE_1000baseT_Half] = {
57 .eth_mode = ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
58 .speed = 1000,
59 .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseT_Half,
60 .duplex = PRESTERA_PORT_DUPLEX_HALF,
61 .port_type = PRESTERA_PORT_TYPE_TP,
62 },
63 [PRESTERA_LINK_MODE_1000baseT_Full] = {
64 .eth_mode = ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
65 .speed = 1000,
66 .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseT_Full,
67 .duplex = PRESTERA_PORT_DUPLEX_FULL,
68 .port_type = PRESTERA_PORT_TYPE_TP,
69 },
70 [PRESTERA_LINK_MODE_1000baseX_Full] = {
71 .eth_mode = ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
72 .speed = 1000,
73 .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseX_Full,
74 .duplex = PRESTERA_PORT_DUPLEX_FULL,
75 .port_type = PRESTERA_PORT_TYPE_FIBRE,
76 },
77 [PRESTERA_LINK_MODE_1000baseKX_Full] = {
78 .eth_mode = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
79 .speed = 1000,
80 .pr_mask = 1 << PRESTERA_LINK_MODE_1000baseKX_Full,
81 .duplex = PRESTERA_PORT_DUPLEX_FULL,
82 .port_type = PRESTERA_PORT_TYPE_TP,
83 },
84 [PRESTERA_LINK_MODE_2500baseX_Full] = {
85 .eth_mode = ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
86 .speed = 2500,
87 .pr_mask = 1 << PRESTERA_LINK_MODE_2500baseX_Full,
88 .duplex = PRESTERA_PORT_DUPLEX_FULL,
89 },
90 [PRESTERA_LINK_MODE_10GbaseKR_Full] = {
91 .eth_mode = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
92 .speed = 10000,
93 .pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseKR_Full,
94 .duplex = PRESTERA_PORT_DUPLEX_FULL,
95 .port_type = PRESTERA_PORT_TYPE_TP,
96 },
97 [PRESTERA_LINK_MODE_10GbaseSR_Full] = {
98 .eth_mode = ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
99 .speed = 10000,
100 .pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseSR_Full,
101 .duplex = PRESTERA_PORT_DUPLEX_FULL,
102 .port_type = PRESTERA_PORT_TYPE_FIBRE,
103 },
104 [PRESTERA_LINK_MODE_10GbaseLR_Full] = {
105 .eth_mode = ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
106 .speed = 10000,
107 .pr_mask = 1 << PRESTERA_LINK_MODE_10GbaseLR_Full,
108 .duplex = PRESTERA_PORT_DUPLEX_FULL,
109 .port_type = PRESTERA_PORT_TYPE_FIBRE,
110 },
111 [PRESTERA_LINK_MODE_20GbaseKR2_Full] = {
112 .eth_mode = ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
113 .speed = 20000,
114 .pr_mask = 1 << PRESTERA_LINK_MODE_20GbaseKR2_Full,
115 .duplex = PRESTERA_PORT_DUPLEX_FULL,
116 .port_type = PRESTERA_PORT_TYPE_TP,
117 },
118 [PRESTERA_LINK_MODE_25GbaseCR_Full] = {
119 .eth_mode = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
120 .speed = 25000,
121 .pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseCR_Full,
122 .duplex = PRESTERA_PORT_DUPLEX_FULL,
123 .port_type = PRESTERA_PORT_TYPE_DA,
124 },
125 [PRESTERA_LINK_MODE_25GbaseKR_Full] = {
126 .eth_mode = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
127 .speed = 25000,
128 .pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseKR_Full,
129 .duplex = PRESTERA_PORT_DUPLEX_FULL,
130 .port_type = PRESTERA_PORT_TYPE_TP,
131 },
132 [PRESTERA_LINK_MODE_25GbaseSR_Full] = {
133 .eth_mode = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
134 .speed = 25000,
135 .pr_mask = 1 << PRESTERA_LINK_MODE_25GbaseSR_Full,
136 .duplex = PRESTERA_PORT_DUPLEX_FULL,
137 .port_type = PRESTERA_PORT_TYPE_FIBRE,
138 },
139 [PRESTERA_LINK_MODE_40GbaseKR4_Full] = {
140 .eth_mode = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
141 .speed = 40000,
142 .pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseKR4_Full,
143 .duplex = PRESTERA_PORT_DUPLEX_FULL,
144 .port_type = PRESTERA_PORT_TYPE_TP,
145 },
146 [PRESTERA_LINK_MODE_40GbaseCR4_Full] = {
147 .eth_mode = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
148 .speed = 40000,
149 .pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseCR4_Full,
150 .duplex = PRESTERA_PORT_DUPLEX_FULL,
151 .port_type = PRESTERA_PORT_TYPE_DA,
152 },
153 [PRESTERA_LINK_MODE_40GbaseSR4_Full] = {
154 .eth_mode = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
155 .speed = 40000,
156 .pr_mask = 1 << PRESTERA_LINK_MODE_40GbaseSR4_Full,
157 .duplex = PRESTERA_PORT_DUPLEX_FULL,
158 .port_type = PRESTERA_PORT_TYPE_FIBRE,
159 },
160 [PRESTERA_LINK_MODE_50GbaseCR2_Full] = {
161 .eth_mode = ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
162 .speed = 50000,
163 .pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseCR2_Full,
164 .duplex = PRESTERA_PORT_DUPLEX_FULL,
165 .port_type = PRESTERA_PORT_TYPE_DA,
166 },
167 [PRESTERA_LINK_MODE_50GbaseKR2_Full] = {
168 .eth_mode = ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
169 .speed = 50000,
170 .pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseKR2_Full,
171 .duplex = PRESTERA_PORT_DUPLEX_FULL,
172 .port_type = PRESTERA_PORT_TYPE_TP,
173 },
174 [PRESTERA_LINK_MODE_50GbaseSR2_Full] = {
175 .eth_mode = ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
176 .speed = 50000,
177 .pr_mask = 1 << PRESTERA_LINK_MODE_50GbaseSR2_Full,
178 .duplex = PRESTERA_PORT_DUPLEX_FULL,
179 .port_type = PRESTERA_PORT_TYPE_FIBRE,
180 },
181 [PRESTERA_LINK_MODE_100GbaseKR4_Full] = {
182 .eth_mode = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
183 .speed = 100000,
184 .pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseKR4_Full,
185 .duplex = PRESTERA_PORT_DUPLEX_FULL,
186 .port_type = PRESTERA_PORT_TYPE_TP,
187 },
188 [PRESTERA_LINK_MODE_100GbaseSR4_Full] = {
189 .eth_mode = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
190 .speed = 100000,
191 .pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseSR4_Full,
192 .duplex = PRESTERA_PORT_DUPLEX_FULL,
193 .port_type = PRESTERA_PORT_TYPE_FIBRE,
194 },
195 [PRESTERA_LINK_MODE_100GbaseCR4_Full] = {
196 .eth_mode = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
197 .speed = 100000,
198 .pr_mask = 1 << PRESTERA_LINK_MODE_100GbaseCR4_Full,
199 .duplex = PRESTERA_PORT_DUPLEX_FULL,
200 .port_type = PRESTERA_PORT_TYPE_DA,
201 }
202 };
203
204 static const struct prestera_fec {
205 u32 eth_fec;
206 enum ethtool_link_mode_bit_indices eth_mode;
207 u8 pr_fec;
208 } port_fec_caps[PRESTERA_PORT_FEC_MAX] = {
209 [PRESTERA_PORT_FEC_OFF] = {
210 .eth_fec = ETHTOOL_FEC_OFF,
211 .eth_mode = ETHTOOL_LINK_MODE_FEC_NONE_BIT,
212 .pr_fec = 1 << PRESTERA_PORT_FEC_OFF,
213 },
214 [PRESTERA_PORT_FEC_BASER] = {
215 .eth_fec = ETHTOOL_FEC_BASER,
216 .eth_mode = ETHTOOL_LINK_MODE_FEC_BASER_BIT,
217 .pr_fec = 1 << PRESTERA_PORT_FEC_BASER,
218 },
219 [PRESTERA_PORT_FEC_RS] = {
220 .eth_fec = ETHTOOL_FEC_RS,
221 .eth_mode = ETHTOOL_LINK_MODE_FEC_RS_BIT,
222 .pr_fec = 1 << PRESTERA_PORT_FEC_RS,
223 }
224 };
225
226 static const struct prestera_port_type {
227 enum ethtool_link_mode_bit_indices eth_mode;
228 u8 eth_type;
229 } port_types[PRESTERA_PORT_TYPE_MAX] = {
230 [PRESTERA_PORT_TYPE_NONE] = {
231 .eth_mode = __ETHTOOL_LINK_MODE_MASK_NBITS,
232 .eth_type = PORT_NONE,
233 },
234 [PRESTERA_PORT_TYPE_TP] = {
235 .eth_mode = ETHTOOL_LINK_MODE_TP_BIT,
236 .eth_type = PORT_TP,
237 },
238 [PRESTERA_PORT_TYPE_AUI] = {
239 .eth_mode = ETHTOOL_LINK_MODE_AUI_BIT,
240 .eth_type = PORT_AUI,
241 },
242 [PRESTERA_PORT_TYPE_MII] = {
243 .eth_mode = ETHTOOL_LINK_MODE_MII_BIT,
244 .eth_type = PORT_MII,
245 },
246 [PRESTERA_PORT_TYPE_FIBRE] = {
247 .eth_mode = ETHTOOL_LINK_MODE_FIBRE_BIT,
248 .eth_type = PORT_FIBRE,
249 },
250 [PRESTERA_PORT_TYPE_BNC] = {
251 .eth_mode = ETHTOOL_LINK_MODE_BNC_BIT,
252 .eth_type = PORT_BNC,
253 },
254 [PRESTERA_PORT_TYPE_DA] = {
255 .eth_mode = ETHTOOL_LINK_MODE_TP_BIT,
256 .eth_type = PORT_TP,
257 },
258 [PRESTERA_PORT_TYPE_OTHER] = {
259 .eth_mode = __ETHTOOL_LINK_MODE_MASK_NBITS,
260 .eth_type = PORT_OTHER,
261 }
262 };
263
264 static const char prestera_cnt_name[PRESTERA_STATS_CNT][ETH_GSTRING_LEN] = {
265 PRESTERA_STATS_FIELD(good_octets_received),
266 PRESTERA_STATS_FIELD(bad_octets_received),
267 PRESTERA_STATS_FIELD(mac_trans_error),
268 PRESTERA_STATS_FIELD(broadcast_frames_received),
269 PRESTERA_STATS_FIELD(multicast_frames_received),
270 PRESTERA_STATS_FIELD(frames_64_octets),
271 PRESTERA_STATS_FIELD(frames_65_to_127_octets),
272 PRESTERA_STATS_FIELD(frames_128_to_255_octets),
273 PRESTERA_STATS_FIELD(frames_256_to_511_octets),
274 PRESTERA_STATS_FIELD(frames_512_to_1023_octets),
275 PRESTERA_STATS_FIELD(frames_1024_to_max_octets),
276 PRESTERA_STATS_FIELD(excessive_collision),
277 PRESTERA_STATS_FIELD(multicast_frames_sent),
278 PRESTERA_STATS_FIELD(broadcast_frames_sent),
279 PRESTERA_STATS_FIELD(fc_sent),
280 PRESTERA_STATS_FIELD(fc_received),
281 PRESTERA_STATS_FIELD(buffer_overrun),
282 PRESTERA_STATS_FIELD(undersize),
283 PRESTERA_STATS_FIELD(fragments),
284 PRESTERA_STATS_FIELD(oversize),
285 PRESTERA_STATS_FIELD(jabber),
286 PRESTERA_STATS_FIELD(rx_error_frame_received),
287 PRESTERA_STATS_FIELD(bad_crc),
288 PRESTERA_STATS_FIELD(collisions),
289 PRESTERA_STATS_FIELD(late_collision),
290 PRESTERA_STATS_FIELD(unicast_frames_received),
291 PRESTERA_STATS_FIELD(unicast_frames_sent),
292 PRESTERA_STATS_FIELD(sent_multiple),
293 PRESTERA_STATS_FIELD(sent_deferred),
294 PRESTERA_STATS_FIELD(good_octets_sent),
295 };
296
prestera_ethtool_get_drvinfo(struct net_device * dev,struct ethtool_drvinfo * drvinfo)297 static void prestera_ethtool_get_drvinfo(struct net_device *dev,
298 struct ethtool_drvinfo *drvinfo)
299 {
300 struct prestera_port *port = netdev_priv(dev);
301 struct prestera_switch *sw = port->sw;
302
303 strlcpy(drvinfo->driver, driver_kind, sizeof(drvinfo->driver));
304 strlcpy(drvinfo->bus_info, dev_name(prestera_dev(sw)),
305 sizeof(drvinfo->bus_info));
306 snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
307 "%d.%d.%d",
308 sw->dev->fw_rev.maj,
309 sw->dev->fw_rev.min,
310 sw->dev->fw_rev.sub);
311 }
312
prestera_port_type_get(struct prestera_port * port)313 static u8 prestera_port_type_get(struct prestera_port *port)
314 {
315 if (port->caps.type < PRESTERA_PORT_TYPE_MAX)
316 return port_types[port->caps.type].eth_type;
317
318 return PORT_OTHER;
319 }
320
prestera_port_type_set(const struct ethtool_link_ksettings * ecmd,struct prestera_port * port)321 static int prestera_port_type_set(const struct ethtool_link_ksettings *ecmd,
322 struct prestera_port *port)
323 {
324 u32 new_mode = PRESTERA_LINK_MODE_MAX;
325 u32 type, mode;
326 int err;
327
328 for (type = 0; type < PRESTERA_PORT_TYPE_MAX; type++) {
329 if (port_types[type].eth_type == ecmd->base.port &&
330 test_bit(port_types[type].eth_mode,
331 ecmd->link_modes.supported)) {
332 break;
333 }
334 }
335
336 if (type == port->caps.type)
337 return 0;
338 if (type != port->caps.type && ecmd->base.autoneg == AUTONEG_ENABLE)
339 return -EINVAL;
340 if (type == PRESTERA_PORT_TYPE_MAX)
341 return -EOPNOTSUPP;
342
343 for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
344 if ((port_link_modes[mode].pr_mask &
345 port->caps.supp_link_modes) &&
346 type == port_link_modes[mode].port_type) {
347 new_mode = mode;
348 }
349 }
350
351 if (new_mode < PRESTERA_LINK_MODE_MAX)
352 err = prestera_hw_port_link_mode_set(port, new_mode);
353 else
354 err = -EINVAL;
355
356 if (err)
357 return err;
358
359 port->caps.type = type;
360 port->autoneg = false;
361
362 return 0;
363 }
364
prestera_modes_to_eth(unsigned long * eth_modes,u64 link_modes,u8 fec,u8 type)365 static void prestera_modes_to_eth(unsigned long *eth_modes, u64 link_modes,
366 u8 fec, u8 type)
367 {
368 u32 mode;
369
370 for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
371 if ((port_link_modes[mode].pr_mask & link_modes) == 0)
372 continue;
373
374 if (type != PRESTERA_PORT_TYPE_NONE &&
375 port_link_modes[mode].port_type != type)
376 continue;
377
378 __set_bit(port_link_modes[mode].eth_mode, eth_modes);
379 }
380
381 for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
382 if ((port_fec_caps[mode].pr_fec & fec) == 0)
383 continue;
384
385 __set_bit(port_fec_caps[mode].eth_mode, eth_modes);
386 }
387 }
388
prestera_modes_from_eth(const unsigned long * eth_modes,u64 * link_modes,u8 * fec,u8 type)389 static void prestera_modes_from_eth(const unsigned long *eth_modes,
390 u64 *link_modes, u8 *fec, u8 type)
391 {
392 u64 adver_modes = 0;
393 u32 fec_modes = 0;
394 u32 mode;
395
396 for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
397 if (!test_bit(port_link_modes[mode].eth_mode, eth_modes))
398 continue;
399
400 if (port_link_modes[mode].port_type != type)
401 continue;
402
403 adver_modes |= port_link_modes[mode].pr_mask;
404 }
405
406 for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
407 if (!test_bit(port_fec_caps[mode].eth_mode, eth_modes))
408 continue;
409
410 fec_modes |= port_fec_caps[mode].pr_fec;
411 }
412
413 *link_modes = adver_modes;
414 *fec = fec_modes;
415 }
416
prestera_port_supp_types_get(struct ethtool_link_ksettings * ecmd,struct prestera_port * port)417 static void prestera_port_supp_types_get(struct ethtool_link_ksettings *ecmd,
418 struct prestera_port *port)
419 {
420 u32 mode;
421 u8 ptype;
422
423 for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
424 if ((port_link_modes[mode].pr_mask &
425 port->caps.supp_link_modes) == 0)
426 continue;
427
428 ptype = port_link_modes[mode].port_type;
429 __set_bit(port_types[ptype].eth_mode,
430 ecmd->link_modes.supported);
431 }
432 }
433
prestera_port_remote_cap_get(struct ethtool_link_ksettings * ecmd,struct prestera_port * port)434 static void prestera_port_remote_cap_get(struct ethtool_link_ksettings *ecmd,
435 struct prestera_port *port)
436 {
437 bool asym_pause;
438 bool pause;
439 u64 bitmap;
440 int err;
441
442 err = prestera_hw_port_remote_cap_get(port, &bitmap);
443 if (!err) {
444 prestera_modes_to_eth(ecmd->link_modes.lp_advertising,
445 bitmap, 0, PRESTERA_PORT_TYPE_NONE);
446
447 if (!bitmap_empty(ecmd->link_modes.lp_advertising,
448 __ETHTOOL_LINK_MODE_MASK_NBITS)) {
449 ethtool_link_ksettings_add_link_mode(ecmd,
450 lp_advertising,
451 Autoneg);
452 }
453 }
454
455 err = prestera_hw_port_remote_fc_get(port, &pause, &asym_pause);
456 if (err)
457 return;
458
459 if (pause)
460 ethtool_link_ksettings_add_link_mode(ecmd,
461 lp_advertising,
462 Pause);
463 if (asym_pause)
464 ethtool_link_ksettings_add_link_mode(ecmd,
465 lp_advertising,
466 Asym_Pause);
467 }
468
prestera_port_speed_get(struct ethtool_link_ksettings * ecmd,struct prestera_port * port)469 static void prestera_port_speed_get(struct ethtool_link_ksettings *ecmd,
470 struct prestera_port *port)
471 {
472 u32 speed;
473 int err;
474
475 err = prestera_hw_port_speed_get(port, &speed);
476 ecmd->base.speed = err ? SPEED_UNKNOWN : speed;
477 }
478
prestera_port_duplex_get(struct ethtool_link_ksettings * ecmd,struct prestera_port * port)479 static void prestera_port_duplex_get(struct ethtool_link_ksettings *ecmd,
480 struct prestera_port *port)
481 {
482 u8 duplex;
483 int err;
484
485 err = prestera_hw_port_duplex_get(port, &duplex);
486 if (err) {
487 ecmd->base.duplex = DUPLEX_UNKNOWN;
488 return;
489 }
490
491 ecmd->base.duplex = duplex == PRESTERA_PORT_DUPLEX_FULL ?
492 DUPLEX_FULL : DUPLEX_HALF;
493 }
494
495 static int
prestera_ethtool_get_link_ksettings(struct net_device * dev,struct ethtool_link_ksettings * ecmd)496 prestera_ethtool_get_link_ksettings(struct net_device *dev,
497 struct ethtool_link_ksettings *ecmd)
498 {
499 struct prestera_port *port = netdev_priv(dev);
500
501 ethtool_link_ksettings_zero_link_mode(ecmd, supported);
502 ethtool_link_ksettings_zero_link_mode(ecmd, advertising);
503 ethtool_link_ksettings_zero_link_mode(ecmd, lp_advertising);
504
505 ecmd->base.autoneg = port->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
506
507 if (port->caps.type == PRESTERA_PORT_TYPE_TP) {
508 ethtool_link_ksettings_add_link_mode(ecmd, supported, Autoneg);
509
510 if (netif_running(dev) &&
511 (port->autoneg ||
512 port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER))
513 ethtool_link_ksettings_add_link_mode(ecmd, advertising,
514 Autoneg);
515 }
516
517 prestera_modes_to_eth(ecmd->link_modes.supported,
518 port->caps.supp_link_modes,
519 port->caps.supp_fec,
520 port->caps.type);
521
522 prestera_port_supp_types_get(ecmd, port);
523
524 if (netif_carrier_ok(dev)) {
525 prestera_port_speed_get(ecmd, port);
526 prestera_port_duplex_get(ecmd, port);
527 } else {
528 ecmd->base.speed = SPEED_UNKNOWN;
529 ecmd->base.duplex = DUPLEX_UNKNOWN;
530 }
531
532 ecmd->base.port = prestera_port_type_get(port);
533
534 if (port->autoneg) {
535 if (netif_running(dev))
536 prestera_modes_to_eth(ecmd->link_modes.advertising,
537 port->adver_link_modes,
538 port->adver_fec,
539 port->caps.type);
540
541 if (netif_carrier_ok(dev) &&
542 port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)
543 prestera_port_remote_cap_get(ecmd, port);
544 }
545
546 if (port->caps.type == PRESTERA_PORT_TYPE_TP &&
547 port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)
548 prestera_hw_port_mdix_get(port, &ecmd->base.eth_tp_mdix,
549 &ecmd->base.eth_tp_mdix_ctrl);
550
551 return 0;
552 }
553
prestera_port_mdix_set(const struct ethtool_link_ksettings * ecmd,struct prestera_port * port)554 static int prestera_port_mdix_set(const struct ethtool_link_ksettings *ecmd,
555 struct prestera_port *port)
556 {
557 if (ecmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_INVALID &&
558 port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER &&
559 port->caps.type == PRESTERA_PORT_TYPE_TP)
560 return prestera_hw_port_mdix_set(port,
561 ecmd->base.eth_tp_mdix_ctrl);
562
563 return 0;
564 }
565
prestera_port_link_mode_set(struct prestera_port * port,u32 speed,u8 duplex,u8 type)566 static int prestera_port_link_mode_set(struct prestera_port *port,
567 u32 speed, u8 duplex, u8 type)
568 {
569 u32 new_mode = PRESTERA_LINK_MODE_MAX;
570 u32 mode;
571
572 for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) {
573 if (speed != port_link_modes[mode].speed)
574 continue;
575
576 if (duplex != port_link_modes[mode].duplex)
577 continue;
578
579 if (!(port_link_modes[mode].pr_mask &
580 port->caps.supp_link_modes))
581 continue;
582
583 if (type != port_link_modes[mode].port_type)
584 continue;
585
586 new_mode = mode;
587 break;
588 }
589
590 if (new_mode == PRESTERA_LINK_MODE_MAX)
591 return -EOPNOTSUPP;
592
593 return prestera_hw_port_link_mode_set(port, new_mode);
594 }
595
596 static int
prestera_port_speed_duplex_set(const struct ethtool_link_ksettings * ecmd,struct prestera_port * port)597 prestera_port_speed_duplex_set(const struct ethtool_link_ksettings *ecmd,
598 struct prestera_port *port)
599 {
600 u32 curr_mode;
601 u8 duplex;
602 u32 speed;
603 int err;
604
605 err = prestera_hw_port_link_mode_get(port, &curr_mode);
606 if (err)
607 return err;
608 if (curr_mode >= PRESTERA_LINK_MODE_MAX)
609 return -EINVAL;
610
611 if (ecmd->base.duplex != DUPLEX_UNKNOWN)
612 duplex = ecmd->base.duplex == DUPLEX_FULL ?
613 PRESTERA_PORT_DUPLEX_FULL : PRESTERA_PORT_DUPLEX_HALF;
614 else
615 duplex = port_link_modes[curr_mode].duplex;
616
617 if (ecmd->base.speed != SPEED_UNKNOWN)
618 speed = ecmd->base.speed;
619 else
620 speed = port_link_modes[curr_mode].speed;
621
622 return prestera_port_link_mode_set(port, speed, duplex,
623 port->caps.type);
624 }
625
626 static int
prestera_ethtool_set_link_ksettings(struct net_device * dev,const struct ethtool_link_ksettings * ecmd)627 prestera_ethtool_set_link_ksettings(struct net_device *dev,
628 const struct ethtool_link_ksettings *ecmd)
629 {
630 struct prestera_port *port = netdev_priv(dev);
631 u64 adver_modes;
632 u8 adver_fec;
633 int err;
634
635 err = prestera_port_type_set(ecmd, port);
636 if (err)
637 return err;
638
639 if (port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) {
640 err = prestera_port_mdix_set(ecmd, port);
641 if (err)
642 return err;
643 }
644
645 prestera_modes_from_eth(ecmd->link_modes.advertising, &adver_modes,
646 &adver_fec, port->caps.type);
647
648 err = prestera_port_autoneg_set(port,
649 ecmd->base.autoneg == AUTONEG_ENABLE,
650 adver_modes, adver_fec);
651 if (err)
652 return err;
653
654 if (ecmd->base.autoneg == AUTONEG_DISABLE) {
655 err = prestera_port_speed_duplex_set(ecmd, port);
656 if (err)
657 return err;
658 }
659
660 return 0;
661 }
662
prestera_ethtool_get_fecparam(struct net_device * dev,struct ethtool_fecparam * fecparam)663 static int prestera_ethtool_get_fecparam(struct net_device *dev,
664 struct ethtool_fecparam *fecparam)
665 {
666 struct prestera_port *port = netdev_priv(dev);
667 u8 active;
668 u32 mode;
669 int err;
670
671 err = prestera_hw_port_fec_get(port, &active);
672 if (err)
673 return err;
674
675 fecparam->fec = 0;
676
677 for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
678 if ((port_fec_caps[mode].pr_fec & port->caps.supp_fec) == 0)
679 continue;
680
681 fecparam->fec |= port_fec_caps[mode].eth_fec;
682 }
683
684 if (active < PRESTERA_PORT_FEC_MAX)
685 fecparam->active_fec = port_fec_caps[active].eth_fec;
686 else
687 fecparam->active_fec = ETHTOOL_FEC_AUTO;
688
689 return 0;
690 }
691
prestera_ethtool_set_fecparam(struct net_device * dev,struct ethtool_fecparam * fecparam)692 static int prestera_ethtool_set_fecparam(struct net_device *dev,
693 struct ethtool_fecparam *fecparam)
694 {
695 struct prestera_port *port = netdev_priv(dev);
696 u8 fec, active;
697 u32 mode;
698 int err;
699
700 if (port->autoneg) {
701 netdev_err(dev, "FEC set is not allowed while autoneg is on\n");
702 return -EINVAL;
703 }
704
705 err = prestera_hw_port_fec_get(port, &active);
706 if (err)
707 return err;
708
709 fec = PRESTERA_PORT_FEC_MAX;
710 for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) {
711 if ((port_fec_caps[mode].eth_fec & fecparam->fec) &&
712 (port_fec_caps[mode].pr_fec & port->caps.supp_fec)) {
713 fec = mode;
714 break;
715 }
716 }
717
718 if (fec == active)
719 return 0;
720
721 if (fec == PRESTERA_PORT_FEC_MAX)
722 return -EOPNOTSUPP;
723
724 return prestera_hw_port_fec_set(port, fec);
725 }
726
prestera_ethtool_get_sset_count(struct net_device * dev,int sset)727 static int prestera_ethtool_get_sset_count(struct net_device *dev, int sset)
728 {
729 switch (sset) {
730 case ETH_SS_STATS:
731 return PRESTERA_STATS_CNT;
732 default:
733 return -EOPNOTSUPP;
734 }
735 }
736
prestera_ethtool_get_strings(struct net_device * dev,u32 stringset,u8 * data)737 static void prestera_ethtool_get_strings(struct net_device *dev,
738 u32 stringset, u8 *data)
739 {
740 if (stringset != ETH_SS_STATS)
741 return;
742
743 memcpy(data, prestera_cnt_name, sizeof(prestera_cnt_name));
744 }
745
prestera_ethtool_get_stats(struct net_device * dev,struct ethtool_stats * stats,u64 * data)746 static void prestera_ethtool_get_stats(struct net_device *dev,
747 struct ethtool_stats *stats, u64 *data)
748 {
749 struct prestera_port *port = netdev_priv(dev);
750 struct prestera_port_stats *port_stats;
751
752 port_stats = &port->cached_hw_stats.stats;
753
754 memcpy(data, port_stats, sizeof(*port_stats));
755 }
756
prestera_ethtool_nway_reset(struct net_device * dev)757 static int prestera_ethtool_nway_reset(struct net_device *dev)
758 {
759 struct prestera_port *port = netdev_priv(dev);
760
761 if (netif_running(dev) &&
762 port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER &&
763 port->caps.type == PRESTERA_PORT_TYPE_TP)
764 return prestera_hw_port_autoneg_restart(port);
765
766 return -EINVAL;
767 }
768
769 const struct ethtool_ops prestera_ethtool_ops = {
770 .get_drvinfo = prestera_ethtool_get_drvinfo,
771 .get_link_ksettings = prestera_ethtool_get_link_ksettings,
772 .set_link_ksettings = prestera_ethtool_set_link_ksettings,
773 .get_fecparam = prestera_ethtool_get_fecparam,
774 .set_fecparam = prestera_ethtool_set_fecparam,
775 .get_sset_count = prestera_ethtool_get_sset_count,
776 .get_strings = prestera_ethtool_get_strings,
777 .get_ethtool_stats = prestera_ethtool_get_stats,
778 .get_link = ethtool_op_get_link,
779 .nway_reset = prestera_ethtool_nway_reset
780 };
781