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