xref: /linux/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
3 
4 #include <linux/kernel.h>
5 #include <linux/pci.h>
6 #include <linux/device.h>
7 #include <linux/module.h>
8 #include <linux/types.h>
9 #include <linux/errno.h>
10 #include <linux/etherdevice.h>
11 #include <linux/netdevice.h>
12 #include <linux/ethtool.h>
13 
14 #include "hinic3_lld.h"
15 #include "hinic3_hw_comm.h"
16 #include "hinic3_nic_dev.h"
17 #include "hinic3_nic_cfg.h"
18 
19 #define HINIC3_MGMT_VERSION_MAX_LEN     32
20 
21 static void hinic3_get_drvinfo(struct net_device *netdev,
22 			       struct ethtool_drvinfo *info)
23 {
24 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
25 	u8 mgmt_ver[HINIC3_MGMT_VERSION_MAX_LEN];
26 	struct pci_dev *pdev = nic_dev->pdev;
27 	int err;
28 
29 	strscpy(info->driver, HINIC3_NIC_DRV_NAME, sizeof(info->driver));
30 	strscpy(info->bus_info, pci_name(pdev), sizeof(info->bus_info));
31 
32 	err = hinic3_get_mgmt_version(nic_dev->hwdev, mgmt_ver,
33 				      HINIC3_MGMT_VERSION_MAX_LEN);
34 	if (err) {
35 		netdev_err(netdev, "Failed to get fw version\n");
36 		return;
37 	}
38 
39 	snprintf(info->fw_version, sizeof(info->fw_version), "%s", mgmt_ver);
40 }
41 
42 static u32 hinic3_get_msglevel(struct net_device *netdev)
43 {
44 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
45 
46 	return nic_dev->msg_enable;
47 }
48 
49 static void hinic3_set_msglevel(struct net_device *netdev, u32 data)
50 {
51 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
52 
53 	nic_dev->msg_enable = data;
54 
55 	netdev_dbg(netdev, "Set message level: 0x%x\n", data);
56 }
57 
58 static const u32 hinic3_link_mode_ge[] = {
59 	ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
60 	ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
61 	ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
62 };
63 
64 static const u32 hinic3_link_mode_10ge_base_r[] = {
65 	ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
66 	ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
67 	ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
68 	ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
69 	ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
70 	ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
71 };
72 
73 static const u32 hinic3_link_mode_25ge_base_r[] = {
74 	ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
75 	ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
76 	ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
77 };
78 
79 static const u32 hinic3_link_mode_40ge_base_r4[] = {
80 	ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
81 	ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
82 	ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
83 	ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
84 };
85 
86 static const u32 hinic3_link_mode_50ge_base_r[] = {
87 	ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
88 	ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
89 	ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
90 };
91 
92 static const u32 hinic3_link_mode_50ge_base_r2[] = {
93 	ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
94 	ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
95 	ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
96 };
97 
98 static const u32 hinic3_link_mode_100ge_base_r[] = {
99 	ETHTOOL_LINK_MODE_100000baseKR_Full_BIT,
100 	ETHTOOL_LINK_MODE_100000baseSR_Full_BIT,
101 	ETHTOOL_LINK_MODE_100000baseCR_Full_BIT,
102 };
103 
104 static const u32 hinic3_link_mode_100ge_base_r2[] = {
105 	ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
106 	ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
107 	ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
108 };
109 
110 static const u32 hinic3_link_mode_100ge_base_r4[] = {
111 	ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
112 	ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
113 	ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
114 	ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
115 };
116 
117 static const u32 hinic3_link_mode_200ge_base_r2[] = {
118 	ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT,
119 	ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT,
120 	ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT,
121 };
122 
123 static const u32 hinic3_link_mode_200ge_base_r4[] = {
124 	ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
125 	ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
126 	ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
127 };
128 
129 struct hw2ethtool_link_mode {
130 	const u32 *link_mode_bit_arr;
131 	u32       arr_size;
132 	u32       speed;
133 };
134 
135 static const struct hw2ethtool_link_mode
136 	hw2ethtool_link_mode_table[LINK_MODE_MAX_NUMBERS] = {
137 	[LINK_MODE_GE] = {
138 		.link_mode_bit_arr = hinic3_link_mode_ge,
139 		.arr_size          = ARRAY_SIZE(hinic3_link_mode_ge),
140 		.speed             = SPEED_1000,
141 	},
142 	[LINK_MODE_10GE_BASE_R] = {
143 		.link_mode_bit_arr = hinic3_link_mode_10ge_base_r,
144 		.arr_size          = ARRAY_SIZE(hinic3_link_mode_10ge_base_r),
145 		.speed             = SPEED_10000,
146 	},
147 	[LINK_MODE_25GE_BASE_R] = {
148 		.link_mode_bit_arr = hinic3_link_mode_25ge_base_r,
149 		.arr_size          = ARRAY_SIZE(hinic3_link_mode_25ge_base_r),
150 		.speed             = SPEED_25000,
151 	},
152 	[LINK_MODE_40GE_BASE_R4] = {
153 		.link_mode_bit_arr = hinic3_link_mode_40ge_base_r4,
154 		.arr_size          = ARRAY_SIZE(hinic3_link_mode_40ge_base_r4),
155 		.speed             = SPEED_40000,
156 	},
157 	[LINK_MODE_50GE_BASE_R] = {
158 		.link_mode_bit_arr = hinic3_link_mode_50ge_base_r,
159 		.arr_size          = ARRAY_SIZE(hinic3_link_mode_50ge_base_r),
160 		.speed             = SPEED_50000,
161 	},
162 	[LINK_MODE_50GE_BASE_R2] = {
163 		.link_mode_bit_arr = hinic3_link_mode_50ge_base_r2,
164 		.arr_size          = ARRAY_SIZE(hinic3_link_mode_50ge_base_r2),
165 		.speed             = SPEED_50000,
166 	},
167 	[LINK_MODE_100GE_BASE_R] = {
168 		.link_mode_bit_arr = hinic3_link_mode_100ge_base_r,
169 		.arr_size          = ARRAY_SIZE(hinic3_link_mode_100ge_base_r),
170 		.speed             = SPEED_100000,
171 	},
172 	[LINK_MODE_100GE_BASE_R2] = {
173 		.link_mode_bit_arr = hinic3_link_mode_100ge_base_r2,
174 		.arr_size          = ARRAY_SIZE(hinic3_link_mode_100ge_base_r2),
175 		.speed             = SPEED_100000,
176 	},
177 	[LINK_MODE_100GE_BASE_R4] = {
178 		.link_mode_bit_arr = hinic3_link_mode_100ge_base_r4,
179 		.arr_size          = ARRAY_SIZE(hinic3_link_mode_100ge_base_r4),
180 		.speed             = SPEED_100000,
181 	},
182 	[LINK_MODE_200GE_BASE_R2] = {
183 		.link_mode_bit_arr = hinic3_link_mode_200ge_base_r2,
184 		.arr_size          = ARRAY_SIZE(hinic3_link_mode_200ge_base_r2),
185 		.speed             = SPEED_200000,
186 	},
187 	[LINK_MODE_200GE_BASE_R4] = {
188 		.link_mode_bit_arr = hinic3_link_mode_200ge_base_r4,
189 		.arr_size          = ARRAY_SIZE(hinic3_link_mode_200ge_base_r4),
190 		.speed             = SPEED_200000,
191 	},
192 };
193 
194 #define GET_SUPPORTED_MODE     0
195 #define GET_ADVERTISED_MODE    1
196 
197 struct hinic3_link_settings {
198 	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
199 	__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
200 
201 	u32 speed;
202 	u8  duplex;
203 	u8  port;
204 	u8  autoneg;
205 };
206 
207 #define HINIC3_ADD_SUPPORTED_LINK_MODE(ecmd, mode) \
208 	set_bit(ETHTOOL_LINK_##mode##_BIT, (ecmd)->supported)
209 #define HINIC3_ADD_ADVERTISED_LINK_MODE(ecmd, mode) \
210 	set_bit(ETHTOOL_LINK_##mode##_BIT, (ecmd)->advertising)
211 
212 static void hinic3_add_speed_link_mode(unsigned long *bitmap, u32 mode)
213 {
214 	u32 i;
215 
216 	for (i = 0; i < hw2ethtool_link_mode_table[mode].arr_size; i++) {
217 		if (hw2ethtool_link_mode_table[mode].link_mode_bit_arr[i] >=
218 		    __ETHTOOL_LINK_MODE_MASK_NBITS)
219 			continue;
220 
221 		set_bit(hw2ethtool_link_mode_table[mode].link_mode_bit_arr[i],
222 			bitmap);
223 	}
224 }
225 
226 /* Related to enum mag_cmd_port_speed */
227 static const u32 hw_to_ethtool_speed[] = {
228 	(u32)SPEED_UNKNOWN, SPEED_10,    SPEED_100,   SPEED_1000,   SPEED_10000,
229 	SPEED_25000,        SPEED_40000, SPEED_50000, SPEED_100000, SPEED_200000
230 };
231 
232 static void
233 hinic3_add_ethtool_link_mode(struct hinic3_link_settings *link_settings,
234 			     u32 hw_link_mode, u32 name)
235 {
236 	unsigned long *advertising_mask = link_settings->advertising;
237 	unsigned long *supported_mask = link_settings->supported;
238 	u32 link_mode;
239 
240 	for (link_mode = 0; link_mode < LINK_MODE_MAX_NUMBERS; link_mode++) {
241 		if (hw_link_mode & BIT(link_mode)) {
242 			if (name == GET_SUPPORTED_MODE)
243 				hinic3_add_speed_link_mode(supported_mask,
244 							   link_mode);
245 			else
246 				hinic3_add_speed_link_mode(advertising_mask,
247 							   link_mode);
248 		}
249 	}
250 }
251 
252 static void
253 hinic3_link_speed_set(struct net_device *netdev,
254 		      struct hinic3_link_settings *link_settings,
255 		      struct hinic3_nic_port_info *port_info)
256 {
257 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
258 	bool link_status_up;
259 	int err;
260 
261 	if (port_info->supported_mode != LINK_MODE_UNKNOWN)
262 		hinic3_add_ethtool_link_mode(link_settings,
263 					     port_info->supported_mode,
264 					     GET_SUPPORTED_MODE);
265 	if (port_info->advertised_mode != LINK_MODE_UNKNOWN)
266 		hinic3_add_ethtool_link_mode(link_settings,
267 					     port_info->advertised_mode,
268 					     GET_ADVERTISED_MODE);
269 
270 	err = hinic3_get_link_status(nic_dev->hwdev, &link_status_up);
271 	if (!err && link_status_up) {
272 		link_settings->speed =
273 			port_info->speed < ARRAY_SIZE(hw_to_ethtool_speed) ?
274 			hw_to_ethtool_speed[port_info->speed] :
275 			(u32)SPEED_UNKNOWN;
276 
277 		link_settings->duplex = port_info->duplex;
278 	} else {
279 		link_settings->speed = (u32)SPEED_UNKNOWN;
280 		link_settings->duplex = DUPLEX_UNKNOWN;
281 	}
282 }
283 
284 static void
285 hinic3_link_port_type_set(struct hinic3_link_settings *link_settings,
286 			  u8 port_type)
287 {
288 	switch (port_type) {
289 	case MAG_CMD_WIRE_TYPE_ELECTRIC:
290 		HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_TP);
291 		HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_TP);
292 		link_settings->port = PORT_TP;
293 		break;
294 
295 	case MAG_CMD_WIRE_TYPE_AOC:
296 	case MAG_CMD_WIRE_TYPE_MM:
297 	case MAG_CMD_WIRE_TYPE_SM:
298 		HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_FIBRE);
299 		HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_FIBRE);
300 		link_settings->port = PORT_FIBRE;
301 		break;
302 
303 	case MAG_CMD_WIRE_TYPE_COPPER:
304 		HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_FIBRE);
305 		HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_FIBRE);
306 		link_settings->port = PORT_DA;
307 		break;
308 
309 	case MAG_CMD_WIRE_TYPE_BACKPLANE:
310 		HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_Backplane);
311 		HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_Backplane);
312 		link_settings->port = PORT_NONE;
313 		break;
314 
315 	default:
316 		link_settings->port = PORT_OTHER;
317 		break;
318 	}
319 }
320 
321 static int
322 hinic3_get_link_pause_settings(struct net_device *netdev,
323 			       struct hinic3_link_settings *link_settings)
324 {
325 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
326 	struct hinic3_nic_pause_config nic_pause = {};
327 	int err;
328 
329 	err = hinic3_get_pause_info(nic_dev, &nic_pause);
330 	if (err) {
331 		netdev_err(netdev, "Failed to get pause param from hw\n");
332 		return err;
333 	}
334 
335 	HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_Pause);
336 	if (nic_pause.rx_pause && nic_pause.tx_pause) {
337 		HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_Pause);
338 	} else if (nic_pause.tx_pause) {
339 		HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings,
340 						MODE_Asym_Pause);
341 	} else if (nic_pause.rx_pause) {
342 		HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_Pause);
343 		HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings,
344 						MODE_Asym_Pause);
345 	}
346 
347 	return 0;
348 }
349 
350 static int
351 hinic3_get_link_settings(struct net_device *netdev,
352 			 struct hinic3_link_settings *link_settings)
353 {
354 	struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
355 	struct hinic3_nic_port_info port_info = {};
356 	int err;
357 
358 	err = hinic3_get_port_info(nic_dev->hwdev, &port_info);
359 	if (err) {
360 		netdev_err(netdev, "Failed to get port info\n");
361 		return err;
362 	}
363 
364 	hinic3_link_speed_set(netdev, link_settings, &port_info);
365 
366 	hinic3_link_port_type_set(link_settings, port_info.port_type);
367 
368 	link_settings->autoneg = port_info.autoneg_state == PORT_CFG_AN_ON ?
369 				 AUTONEG_ENABLE : AUTONEG_DISABLE;
370 	if (port_info.autoneg_cap)
371 		HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_Autoneg);
372 	if (port_info.autoneg_state == PORT_CFG_AN_ON)
373 		HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_Autoneg);
374 
375 	if (!HINIC3_IS_VF(nic_dev->hwdev)) {
376 		err = hinic3_get_link_pause_settings(netdev, link_settings);
377 		if (err)
378 			return err;
379 	}
380 
381 	return 0;
382 }
383 
384 static int
385 hinic3_get_link_ksettings(struct net_device *netdev,
386 			  struct ethtool_link_ksettings *link_settings)
387 {
388 	struct ethtool_link_settings *base = &link_settings->base;
389 	struct hinic3_link_settings settings = {};
390 	int err;
391 
392 	ethtool_link_ksettings_zero_link_mode(link_settings, supported);
393 	ethtool_link_ksettings_zero_link_mode(link_settings, advertising);
394 
395 	err = hinic3_get_link_settings(netdev, &settings);
396 	if (err)
397 		return err;
398 
399 	bitmap_copy(link_settings->link_modes.supported, settings.supported,
400 		    __ETHTOOL_LINK_MODE_MASK_NBITS);
401 	bitmap_copy(link_settings->link_modes.advertising, settings.advertising,
402 		    __ETHTOOL_LINK_MODE_MASK_NBITS);
403 
404 	base->autoneg = settings.autoneg;
405 	base->speed = settings.speed;
406 	base->duplex = settings.duplex;
407 	base->port = settings.port;
408 
409 	return 0;
410 }
411 
412 static const struct ethtool_ops hinic3_ethtool_ops = {
413 	.supported_coalesce_params      = ETHTOOL_COALESCE_USECS |
414 					  ETHTOOL_COALESCE_PKT_RATE_RX_USECS,
415 	.get_link_ksettings             = hinic3_get_link_ksettings,
416 	.get_drvinfo                    = hinic3_get_drvinfo,
417 	.get_msglevel                   = hinic3_get_msglevel,
418 	.set_msglevel                   = hinic3_set_msglevel,
419 	.get_link                       = ethtool_op_get_link,
420 };
421 
422 void hinic3_set_ethtool_ops(struct net_device *netdev)
423 {
424 	netdev->ethtool_ops = &hinic3_ethtool_ops;
425 }
426