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
19static const char driver_kind[] = "prestera";
20
21static 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
204static 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
226static 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
264static 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
297static 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
313static 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
321static 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
365static 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
389static 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
417static 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
434static 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
469static 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
479static 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
495static int
496prestera_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
554static 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
566static 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
596static int
597prestera_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
626static int
627prestera_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
663static 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
692static 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
727static 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
737static 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
746static 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
757static 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
769const 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