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