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 | |
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 | strscpy(drvinfo->driver, driver_kind, sizeof(drvinfo->driver)); |
304 | strscpy(drvinfo->bus_info, dev_name(prestera_dev(sw)), |
305 | sizeof(drvinfo->bus_info)); |
306 | snprintf(buf: drvinfo->fw_version, size: sizeof(drvinfo->fw_version), |
307 | fmt: "%d.%d.%d" , |
308 | sw->dev->fw_rev.maj, |
309 | sw->dev->fw_rev.min, |
310 | sw->dev->fw_rev.sub); |
311 | } |
312 | |
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 | |
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 | |
327 | for (type = 0; type < PRESTERA_PORT_TYPE_MAX; type++) { |
328 | if (port_types[type].eth_type == ecmd->base.port && |
329 | test_bit(port_types[type].eth_mode, |
330 | ecmd->link_modes.supported)) { |
331 | break; |
332 | } |
333 | } |
334 | |
335 | if (type == port->caps.type) |
336 | return 0; |
337 | if (type != port->caps.type && ecmd->base.autoneg == AUTONEG_ENABLE) |
338 | return -EINVAL; |
339 | if (type == PRESTERA_PORT_TYPE_MAX) |
340 | return -EOPNOTSUPP; |
341 | |
342 | for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { |
343 | if ((port_link_modes[mode].pr_mask & |
344 | port->caps.supp_link_modes) && |
345 | type == port_link_modes[mode].port_type) { |
346 | new_mode = mode; |
347 | } |
348 | } |
349 | |
350 | if (new_mode >= PRESTERA_LINK_MODE_MAX) |
351 | return -EINVAL; |
352 | |
353 | port->caps.type = type; |
354 | port->autoneg = false; |
355 | |
356 | return 0; |
357 | } |
358 | |
359 | static void prestera_modes_to_eth(unsigned long *eth_modes, u64 link_modes, |
360 | u8 fec, u8 type) |
361 | { |
362 | u32 mode; |
363 | |
364 | for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { |
365 | if ((port_link_modes[mode].pr_mask & link_modes) == 0) |
366 | continue; |
367 | |
368 | if (type != PRESTERA_PORT_TYPE_NONE && |
369 | port_link_modes[mode].port_type != type) |
370 | continue; |
371 | |
372 | __set_bit(port_link_modes[mode].eth_mode, eth_modes); |
373 | } |
374 | |
375 | for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) { |
376 | if ((port_fec_caps[mode].pr_fec & fec) == 0) |
377 | continue; |
378 | |
379 | __set_bit(port_fec_caps[mode].eth_mode, eth_modes); |
380 | } |
381 | } |
382 | |
383 | static void prestera_modes_from_eth(const unsigned long *eth_modes, |
384 | u64 *link_modes, u8 *fec, u8 type) |
385 | { |
386 | u64 adver_modes = 0; |
387 | u32 fec_modes = 0; |
388 | u32 mode; |
389 | |
390 | for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { |
391 | if (!test_bit(port_link_modes[mode].eth_mode, eth_modes)) |
392 | continue; |
393 | |
394 | if (port_link_modes[mode].port_type != type) |
395 | continue; |
396 | |
397 | adver_modes |= port_link_modes[mode].pr_mask; |
398 | } |
399 | |
400 | for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) { |
401 | if (!test_bit(port_fec_caps[mode].eth_mode, eth_modes)) |
402 | continue; |
403 | |
404 | fec_modes |= port_fec_caps[mode].pr_fec; |
405 | } |
406 | |
407 | *link_modes = adver_modes; |
408 | *fec = fec_modes; |
409 | } |
410 | |
411 | static void prestera_port_supp_types_get(struct ethtool_link_ksettings *ecmd, |
412 | struct prestera_port *port) |
413 | { |
414 | u32 mode; |
415 | u8 ptype; |
416 | |
417 | for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { |
418 | if ((port_link_modes[mode].pr_mask & |
419 | port->caps.supp_link_modes) == 0) |
420 | continue; |
421 | |
422 | ptype = port_link_modes[mode].port_type; |
423 | __set_bit(port_types[ptype].eth_mode, |
424 | ecmd->link_modes.supported); |
425 | } |
426 | } |
427 | |
428 | static void prestera_port_remote_cap_get(struct ethtool_link_ksettings *ecmd, |
429 | struct prestera_port *port) |
430 | { |
431 | struct prestera_port_phy_state *state = &port->state_phy; |
432 | bool asym_pause; |
433 | bool pause; |
434 | u64 bitmap; |
435 | int err; |
436 | |
437 | err = prestera_hw_port_phy_mode_get(port, NULL, lmode_bmap: &state->lmode_bmap, |
438 | fc_pause: &state->remote_fc.pause, |
439 | fc_asym: &state->remote_fc.asym_pause); |
440 | if (err) |
441 | netdev_warn(dev: port->dev, format: "Remote link caps get failed %d" , |
442 | port->caps.transceiver); |
443 | |
444 | bitmap = state->lmode_bmap; |
445 | |
446 | prestera_modes_to_eth(eth_modes: ecmd->link_modes.lp_advertising, |
447 | link_modes: bitmap, fec: 0, type: PRESTERA_PORT_TYPE_NONE); |
448 | |
449 | if (!bitmap_empty(src: ecmd->link_modes.lp_advertising, |
450 | nbits: __ETHTOOL_LINK_MODE_MASK_NBITS)) { |
451 | ethtool_link_ksettings_add_link_mode(ecmd, |
452 | lp_advertising, |
453 | Autoneg); |
454 | } |
455 | |
456 | pause = state->remote_fc.pause; |
457 | asym_pause = state->remote_fc.asym_pause; |
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 | |
469 | static void prestera_port_link_mode_get(struct ethtool_link_ksettings *ecmd, |
470 | struct prestera_port *port) |
471 | { |
472 | struct prestera_port_mac_state *state = &port->state_mac; |
473 | u32 speed; |
474 | u8 duplex; |
475 | int err; |
476 | |
477 | if (!port->state_mac.oper) |
478 | return; |
479 | |
480 | if (state->speed == SPEED_UNKNOWN || state->duplex == DUPLEX_UNKNOWN) { |
481 | err = prestera_hw_port_mac_mode_get(port, NULL, speed: &speed, |
482 | duplex: &duplex, NULL); |
483 | if (err) { |
484 | state->speed = SPEED_UNKNOWN; |
485 | state->duplex = DUPLEX_UNKNOWN; |
486 | } else { |
487 | state->speed = speed; |
488 | state->duplex = duplex == PRESTERA_PORT_DUPLEX_FULL ? |
489 | DUPLEX_FULL : DUPLEX_HALF; |
490 | } |
491 | } |
492 | |
493 | ecmd->base.speed = port->state_mac.speed; |
494 | ecmd->base.duplex = port->state_mac.duplex; |
495 | } |
496 | |
497 | static void prestera_port_mdix_get(struct ethtool_link_ksettings *ecmd, |
498 | struct prestera_port *port) |
499 | { |
500 | struct prestera_port_phy_state *state = &port->state_phy; |
501 | |
502 | if (prestera_hw_port_phy_mode_get(port, |
503 | mdix: &state->mdix, NULL, NULL, NULL)) { |
504 | netdev_warn(dev: port->dev, format: "MDIX params get failed" ); |
505 | state->mdix = ETH_TP_MDI_INVALID; |
506 | } |
507 | |
508 | ecmd->base.eth_tp_mdix = port->state_phy.mdix; |
509 | ecmd->base.eth_tp_mdix_ctrl = port->cfg_phy.mdix; |
510 | } |
511 | |
512 | static int |
513 | prestera_ethtool_get_link_ksettings(struct net_device *dev, |
514 | struct ethtool_link_ksettings *ecmd) |
515 | { |
516 | struct prestera_port *port = netdev_priv(dev); |
517 | |
518 | ethtool_link_ksettings_zero_link_mode(ecmd, supported); |
519 | ethtool_link_ksettings_zero_link_mode(ecmd, advertising); |
520 | ethtool_link_ksettings_zero_link_mode(ecmd, lp_advertising); |
521 | ecmd->base.speed = SPEED_UNKNOWN; |
522 | ecmd->base.duplex = DUPLEX_UNKNOWN; |
523 | |
524 | if (port->phy_link) |
525 | return phylink_ethtool_ksettings_get(port->phy_link, ecmd); |
526 | |
527 | ecmd->base.autoneg = port->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; |
528 | |
529 | if (port->caps.type == PRESTERA_PORT_TYPE_TP) { |
530 | ethtool_link_ksettings_add_link_mode(ecmd, supported, Autoneg); |
531 | |
532 | if (netif_running(dev) && |
533 | (port->autoneg || |
534 | port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER)) |
535 | ethtool_link_ksettings_add_link_mode(ecmd, advertising, |
536 | Autoneg); |
537 | } |
538 | |
539 | prestera_modes_to_eth(eth_modes: ecmd->link_modes.supported, |
540 | link_modes: port->caps.supp_link_modes, |
541 | fec: port->caps.supp_fec, |
542 | type: port->caps.type); |
543 | |
544 | prestera_port_supp_types_get(ecmd, port); |
545 | |
546 | if (netif_carrier_ok(dev)) |
547 | prestera_port_link_mode_get(ecmd, port); |
548 | |
549 | ecmd->base.port = prestera_port_type_get(port); |
550 | |
551 | if (port->autoneg) { |
552 | if (netif_running(dev)) |
553 | prestera_modes_to_eth(eth_modes: ecmd->link_modes.advertising, |
554 | link_modes: port->adver_link_modes, |
555 | fec: port->adver_fec, |
556 | type: port->caps.type); |
557 | |
558 | if (netif_carrier_ok(dev) && |
559 | port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) |
560 | prestera_port_remote_cap_get(ecmd, port); |
561 | } |
562 | |
563 | if (port->caps.type == PRESTERA_PORT_TYPE_TP && |
564 | port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) |
565 | prestera_port_mdix_get(ecmd, port); |
566 | |
567 | return 0; |
568 | } |
569 | |
570 | static int prestera_port_mdix_set(const struct ethtool_link_ksettings *ecmd, |
571 | struct prestera_port *port) |
572 | { |
573 | if (ecmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_INVALID && |
574 | port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER && |
575 | port->caps.type == PRESTERA_PORT_TYPE_TP) { |
576 | port->cfg_phy.mdix = ecmd->base.eth_tp_mdix_ctrl; |
577 | return prestera_hw_port_phy_mode_set(port, admin: port->cfg_phy.admin, |
578 | adv: port->autoneg, |
579 | mode: port->cfg_phy.mode, |
580 | modes: port->adver_link_modes, |
581 | mdix: port->cfg_phy.mdix); |
582 | } |
583 | return 0; |
584 | |
585 | } |
586 | |
587 | static int prestera_port_link_mode_set(struct prestera_port *port, |
588 | u32 speed, u8 duplex, u8 type) |
589 | { |
590 | u32 new_mode = PRESTERA_LINK_MODE_MAX; |
591 | u32 mode; |
592 | int err; |
593 | |
594 | for (mode = 0; mode < PRESTERA_LINK_MODE_MAX; mode++) { |
595 | if (speed != SPEED_UNKNOWN && |
596 | speed != port_link_modes[mode].speed) |
597 | continue; |
598 | |
599 | if (duplex != DUPLEX_UNKNOWN && |
600 | duplex != port_link_modes[mode].duplex) |
601 | continue; |
602 | |
603 | if (!(port_link_modes[mode].pr_mask & |
604 | port->caps.supp_link_modes)) |
605 | continue; |
606 | |
607 | if (type != port_link_modes[mode].port_type) |
608 | continue; |
609 | |
610 | new_mode = mode; |
611 | break; |
612 | } |
613 | |
614 | if (new_mode == PRESTERA_LINK_MODE_MAX) |
615 | return -EOPNOTSUPP; |
616 | |
617 | err = prestera_hw_port_phy_mode_set(port, admin: port->cfg_phy.admin, |
618 | adv: false, mode: new_mode, modes: 0, |
619 | mdix: port->cfg_phy.mdix); |
620 | if (err) |
621 | return err; |
622 | |
623 | port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF); |
624 | port->adver_link_modes = 0; |
625 | port->cfg_phy.mode = new_mode; |
626 | port->autoneg = false; |
627 | |
628 | return 0; |
629 | } |
630 | |
631 | static int |
632 | prestera_port_speed_duplex_set(const struct ethtool_link_ksettings *ecmd, |
633 | struct prestera_port *port) |
634 | { |
635 | u8 duplex = DUPLEX_UNKNOWN; |
636 | |
637 | if (ecmd->base.duplex != DUPLEX_UNKNOWN) |
638 | duplex = ecmd->base.duplex == DUPLEX_FULL ? |
639 | PRESTERA_PORT_DUPLEX_FULL : PRESTERA_PORT_DUPLEX_HALF; |
640 | |
641 | return prestera_port_link_mode_set(port, speed: ecmd->base.speed, duplex, |
642 | type: port->caps.type); |
643 | } |
644 | |
645 | static int |
646 | prestera_ethtool_set_link_ksettings(struct net_device *dev, |
647 | const struct ethtool_link_ksettings *ecmd) |
648 | { |
649 | struct prestera_port *port = netdev_priv(dev); |
650 | u64 adver_modes; |
651 | u8 adver_fec; |
652 | int err; |
653 | |
654 | if (port->phy_link) |
655 | return phylink_ethtool_ksettings_set(port->phy_link, ecmd); |
656 | |
657 | err = prestera_port_type_set(ecmd, port); |
658 | if (err) |
659 | return err; |
660 | |
661 | if (port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER) { |
662 | err = prestera_port_mdix_set(ecmd, port); |
663 | if (err) |
664 | return err; |
665 | } |
666 | |
667 | prestera_modes_from_eth(eth_modes: ecmd->link_modes.advertising, link_modes: &adver_modes, |
668 | fec: &adver_fec, type: port->caps.type); |
669 | |
670 | if (ecmd->base.autoneg == AUTONEG_ENABLE) |
671 | err = prestera_port_autoneg_set(port, link_modes: adver_modes); |
672 | else |
673 | err = prestera_port_speed_duplex_set(ecmd, port); |
674 | |
675 | return err; |
676 | } |
677 | |
678 | static int prestera_ethtool_get_fecparam(struct net_device *dev, |
679 | struct ethtool_fecparam *fecparam) |
680 | { |
681 | struct prestera_port *port = netdev_priv(dev); |
682 | u8 active; |
683 | u32 mode; |
684 | int err; |
685 | |
686 | err = prestera_hw_port_mac_mode_get(port, NULL, NULL, NULL, fec: &active); |
687 | if (err) |
688 | return err; |
689 | |
690 | fecparam->fec = 0; |
691 | |
692 | for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) { |
693 | if ((port_fec_caps[mode].pr_fec & port->caps.supp_fec) == 0) |
694 | continue; |
695 | |
696 | fecparam->fec |= port_fec_caps[mode].eth_fec; |
697 | } |
698 | |
699 | if (active < PRESTERA_PORT_FEC_MAX) |
700 | fecparam->active_fec = port_fec_caps[active].eth_fec; |
701 | else |
702 | fecparam->active_fec = ETHTOOL_FEC_AUTO; |
703 | |
704 | return 0; |
705 | } |
706 | |
707 | static int prestera_ethtool_set_fecparam(struct net_device *dev, |
708 | struct ethtool_fecparam *fecparam) |
709 | { |
710 | struct prestera_port *port = netdev_priv(dev); |
711 | struct prestera_port_mac_config cfg_mac; |
712 | u32 mode; |
713 | u8 fec; |
714 | |
715 | if (port->autoneg) { |
716 | netdev_err(dev, format: "FEC set is not allowed while autoneg is on\n" ); |
717 | return -EINVAL; |
718 | } |
719 | |
720 | if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) { |
721 | netdev_err(dev, format: "FEC set is not allowed on non-SFP ports\n" ); |
722 | return -EINVAL; |
723 | } |
724 | |
725 | fec = PRESTERA_PORT_FEC_MAX; |
726 | for (mode = 0; mode < PRESTERA_PORT_FEC_MAX; mode++) { |
727 | if ((port_fec_caps[mode].eth_fec & fecparam->fec) && |
728 | (port_fec_caps[mode].pr_fec & port->caps.supp_fec)) { |
729 | fec = mode; |
730 | break; |
731 | } |
732 | } |
733 | |
734 | prestera_port_cfg_mac_read(port, cfg: &cfg_mac); |
735 | |
736 | if (fec == cfg_mac.fec) |
737 | return 0; |
738 | |
739 | if (fec == PRESTERA_PORT_FEC_MAX) { |
740 | netdev_err(dev, format: "Unsupported FEC requested" ); |
741 | return -EINVAL; |
742 | } |
743 | |
744 | cfg_mac.fec = fec; |
745 | |
746 | return prestera_port_cfg_mac_write(port, cfg: &cfg_mac); |
747 | } |
748 | |
749 | static int prestera_ethtool_get_sset_count(struct net_device *dev, int sset) |
750 | { |
751 | switch (sset) { |
752 | case ETH_SS_STATS: |
753 | return PRESTERA_STATS_CNT; |
754 | default: |
755 | return -EOPNOTSUPP; |
756 | } |
757 | } |
758 | |
759 | static void prestera_ethtool_get_strings(struct net_device *dev, |
760 | u32 stringset, u8 *data) |
761 | { |
762 | if (stringset != ETH_SS_STATS) |
763 | return; |
764 | |
765 | memcpy(data, prestera_cnt_name, sizeof(prestera_cnt_name)); |
766 | } |
767 | |
768 | static void prestera_ethtool_get_stats(struct net_device *dev, |
769 | struct ethtool_stats *stats, u64 *data) |
770 | { |
771 | struct prestera_port *port = netdev_priv(dev); |
772 | struct prestera_port_stats *port_stats; |
773 | |
774 | port_stats = &port->cached_hw_stats.stats; |
775 | |
776 | memcpy(data, port_stats, sizeof(*port_stats)); |
777 | } |
778 | |
779 | static int prestera_ethtool_nway_reset(struct net_device *dev) |
780 | { |
781 | struct prestera_port *port = netdev_priv(dev); |
782 | |
783 | if (netif_running(dev) && |
784 | port->caps.transceiver == PRESTERA_PORT_TCVR_COPPER && |
785 | port->caps.type == PRESTERA_PORT_TYPE_TP) |
786 | return prestera_hw_port_autoneg_restart(port); |
787 | |
788 | return -EINVAL; |
789 | } |
790 | |
791 | const struct ethtool_ops prestera_ethtool_ops = { |
792 | .get_drvinfo = prestera_ethtool_get_drvinfo, |
793 | .get_link_ksettings = prestera_ethtool_get_link_ksettings, |
794 | .set_link_ksettings = prestera_ethtool_set_link_ksettings, |
795 | .get_fecparam = prestera_ethtool_get_fecparam, |
796 | .set_fecparam = prestera_ethtool_set_fecparam, |
797 | .get_sset_count = prestera_ethtool_get_sset_count, |
798 | .get_strings = prestera_ethtool_get_strings, |
799 | .get_ethtool_stats = prestera_ethtool_get_stats, |
800 | .get_link = ethtool_op_get_link, |
801 | .nway_reset = prestera_ethtool_nway_reset |
802 | }; |
803 | |