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	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(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
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
359static 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
383static 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
411static 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
428static 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, &state->lmode_bmap,
438					    &state->remote_fc.pause,
439					    &state->remote_fc.asym_pause);
440	if (err)
441		netdev_warn(port->dev, "Remote link caps get failed %d",
442			    port->caps.transceiver);
443
444	bitmap = state->lmode_bmap;
445
446	prestera_modes_to_eth(ecmd->link_modes.lp_advertising,
447			      bitmap, 0, PRESTERA_PORT_TYPE_NONE);
448
449	if (!bitmap_empty(ecmd->link_modes.lp_advertising,
450			  __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
469static 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,
482						    &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
497static 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					  &state->mdix, NULL, NULL, NULL)) {
504		netdev_warn(port->dev, "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
512static int
513prestera_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(ecmd->link_modes.supported,
540			      port->caps.supp_link_modes,
541			      port->caps.supp_fec,
542			      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(ecmd->link_modes.advertising,
554					      port->adver_link_modes,
555					      port->adver_fec,
556					      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
570static 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, port->cfg_phy.admin,
578						     port->autoneg,
579						     port->cfg_phy.mode,
580						     port->adver_link_modes,
581						     port->cfg_phy.mdix);
582	}
583	return 0;
584
585}
586
587static 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, port->cfg_phy.admin,
618					    false, new_mode, 0,
619					    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
631static int
632prestera_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, ecmd->base.speed, duplex,
642					   port->caps.type);
643}
644
645static int
646prestera_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(ecmd->link_modes.advertising, &adver_modes,
668				&adver_fec, port->caps.type);
669
670	if (ecmd->base.autoneg == AUTONEG_ENABLE)
671		err = prestera_port_autoneg_set(port, adver_modes);
672	else
673		err = prestera_port_speed_duplex_set(ecmd, port);
674
675	return err;
676}
677
678static 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, &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
707static 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, "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, "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_mac);
735
736	if (fec == cfg_mac.fec)
737		return 0;
738
739	if (fec == PRESTERA_PORT_FEC_MAX) {
740		netdev_err(dev, "Unsupported FEC requested");
741		return -EINVAL;
742	}
743
744	cfg_mac.fec = fec;
745
746	return prestera_port_cfg_mac_write(port, &cfg_mac);
747}
748
749static 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
759static 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
768static 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
779static 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
791const 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