1 /*
2  * Copyright (c) 2018, Mellanox Technologies. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  */
32 
33 #include "port.h"
34 
35 /* speed in units of 1Mb */
36 static const u32 mlx5e_link_speed[MLX5E_LINK_MODES_NUMBER] = {
37 	[MLX5E_1000BASE_CX_SGMII] = 1000,
38 	[MLX5E_1000BASE_KX]       = 1000,
39 	[MLX5E_10GBASE_CX4]       = 10000,
40 	[MLX5E_10GBASE_KX4]       = 10000,
41 	[MLX5E_10GBASE_KR]        = 10000,
42 	[MLX5E_20GBASE_KR2]       = 20000,
43 	[MLX5E_40GBASE_CR4]       = 40000,
44 	[MLX5E_40GBASE_KR4]       = 40000,
45 	[MLX5E_56GBASE_R4]        = 56000,
46 	[MLX5E_10GBASE_CR]        = 10000,
47 	[MLX5E_10GBASE_SR]        = 10000,
48 	[MLX5E_10GBASE_ER]        = 10000,
49 	[MLX5E_40GBASE_SR4]       = 40000,
50 	[MLX5E_40GBASE_LR4]       = 40000,
51 	[MLX5E_50GBASE_SR2]       = 50000,
52 	[MLX5E_100GBASE_CR4]      = 100000,
53 	[MLX5E_100GBASE_SR4]      = 100000,
54 	[MLX5E_100GBASE_KR4]      = 100000,
55 	[MLX5E_100GBASE_LR4]      = 100000,
56 	[MLX5E_100BASE_TX]        = 100,
57 	[MLX5E_1000BASE_T]        = 1000,
58 	[MLX5E_10GBASE_T]         = 10000,
59 	[MLX5E_25GBASE_CR]        = 25000,
60 	[MLX5E_25GBASE_KR]        = 25000,
61 	[MLX5E_25GBASE_SR]        = 25000,
62 	[MLX5E_50GBASE_CR2]       = 50000,
63 	[MLX5E_50GBASE_KR2]       = 50000,
64 };
65 
66 static const u32 mlx5e_ext_link_speed[MLX5E_EXT_LINK_MODES_NUMBER] = {
67 	[MLX5E_SGMII_100M]			= 100,
68 	[MLX5E_1000BASE_X_SGMII]		= 1000,
69 	[MLX5E_5GBASE_R]			= 5000,
70 	[MLX5E_10GBASE_XFI_XAUI_1]		= 10000,
71 	[MLX5E_40GBASE_XLAUI_4_XLPPI_4]		= 40000,
72 	[MLX5E_25GAUI_1_25GBASE_CR_KR]		= 25000,
73 	[MLX5E_50GAUI_2_LAUI_2_50GBASE_CR2_KR2]	= 50000,
74 	[MLX5E_50GAUI_1_LAUI_1_50GBASE_CR_KR]	= 50000,
75 	[MLX5E_CAUI_4_100GBASE_CR4_KR4]		= 100000,
76 	[MLX5E_100GAUI_2_100GBASE_CR2_KR2]	= 100000,
77 	[MLX5E_200GAUI_4_200GBASE_CR4_KR4]	= 200000,
78 	[MLX5E_400GAUI_8]			= 400000,
79 	[MLX5E_100GAUI_1_100GBASE_CR_KR]	= 100000,
80 	[MLX5E_200GAUI_2_200GBASE_CR2_KR2]	= 200000,
81 	[MLX5E_400GAUI_4_400GBASE_CR4_KR4]	= 400000,
82 };
83 
84 bool mlx5e_ptys_ext_supported(struct mlx5_core_dev *mdev)
85 {
86 	struct mlx5e_port_eth_proto eproto;
87 	int err;
88 
89 	if (MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet))
90 		return true;
91 
92 	err = mlx5_port_query_eth_proto(mdev, 1, true, &eproto);
93 	if (err)
94 		return false;
95 
96 	return !!eproto.cap;
97 }
98 
99 static void mlx5e_port_get_speed_arr(struct mlx5_core_dev *mdev,
100 				     const u32 **arr, u32 *size,
101 				     bool force_legacy)
102 {
103 	bool ext = force_legacy ? false : mlx5e_ptys_ext_supported(mdev);
104 
105 	*size = ext ? ARRAY_SIZE(mlx5e_ext_link_speed) :
106 		      ARRAY_SIZE(mlx5e_link_speed);
107 	*arr  = ext ? mlx5e_ext_link_speed : mlx5e_link_speed;
108 }
109 
110 int mlx5_port_query_eth_proto(struct mlx5_core_dev *dev, u8 port, bool ext,
111 			      struct mlx5e_port_eth_proto *eproto)
112 {
113 	u32 out[MLX5_ST_SZ_DW(ptys_reg)];
114 	int err;
115 
116 	if (!eproto)
117 		return -EINVAL;
118 
119 	err = mlx5_query_port_ptys(dev, out, sizeof(out), MLX5_PTYS_EN, port);
120 	if (err)
121 		return err;
122 
123 	eproto->cap   = MLX5_GET_ETH_PROTO(ptys_reg, out, ext,
124 					   eth_proto_capability);
125 	eproto->admin = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, eth_proto_admin);
126 	eproto->oper  = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, eth_proto_oper);
127 	return 0;
128 }
129 
130 void mlx5_port_query_eth_autoneg(struct mlx5_core_dev *dev, u8 *an_status,
131 				 u8 *an_disable_cap, u8 *an_disable_admin)
132 {
133 	u32 out[MLX5_ST_SZ_DW(ptys_reg)];
134 
135 	*an_status = 0;
136 	*an_disable_cap = 0;
137 	*an_disable_admin = 0;
138 
139 	if (mlx5_query_port_ptys(dev, out, sizeof(out), MLX5_PTYS_EN, 1))
140 		return;
141 
142 	*an_status = MLX5_GET(ptys_reg, out, an_status);
143 	*an_disable_cap = MLX5_GET(ptys_reg, out, an_disable_cap);
144 	*an_disable_admin = MLX5_GET(ptys_reg, out, an_disable_admin);
145 }
146 
147 int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable,
148 			   u32 proto_admin, bool ext)
149 {
150 	u32 out[MLX5_ST_SZ_DW(ptys_reg)];
151 	u32 in[MLX5_ST_SZ_DW(ptys_reg)];
152 	u8 an_disable_admin;
153 	u8 an_disable_cap;
154 	u8 an_status;
155 
156 	mlx5_port_query_eth_autoneg(dev, &an_status, &an_disable_cap,
157 				    &an_disable_admin);
158 	if (!an_disable_cap && an_disable)
159 		return -EPERM;
160 
161 	memset(in, 0, sizeof(in));
162 
163 	MLX5_SET(ptys_reg, in, local_port, 1);
164 	MLX5_SET(ptys_reg, in, an_disable_admin, an_disable);
165 	MLX5_SET(ptys_reg, in, proto_mask, MLX5_PTYS_EN);
166 	if (ext)
167 		MLX5_SET(ptys_reg, in, ext_eth_proto_admin, proto_admin);
168 	else
169 		MLX5_SET(ptys_reg, in, eth_proto_admin, proto_admin);
170 
171 	return mlx5_core_access_reg(dev, in, sizeof(in), out,
172 			    sizeof(out), MLX5_REG_PTYS, 0, 1);
173 }
174 
175 u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper,
176 			  bool force_legacy)
177 {
178 	unsigned long temp = eth_proto_oper;
179 	const u32 *table;
180 	u32 speed = 0;
181 	u32 max_size;
182 	int i;
183 
184 	mlx5e_port_get_speed_arr(mdev, &table, &max_size, force_legacy);
185 	i = find_first_bit(&temp, max_size);
186 	if (i < max_size)
187 		speed = table[i];
188 	return speed;
189 }
190 
191 int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
192 {
193 	struct mlx5e_port_eth_proto eproto;
194 	bool force_legacy = false;
195 	bool ext;
196 	int err;
197 
198 	ext = mlx5e_ptys_ext_supported(mdev);
199 	err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto);
200 	if (err)
201 		goto out;
202 	if (ext && !eproto.admin) {
203 		force_legacy = true;
204 		err = mlx5_port_query_eth_proto(mdev, 1, false, &eproto);
205 		if (err)
206 			goto out;
207 	}
208 	*speed = mlx5e_port_ptys2speed(mdev, eproto.oper, force_legacy);
209 	if (!(*speed))
210 		err = -EINVAL;
211 
212 out:
213 	return err;
214 }
215 
216 int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
217 {
218 	struct mlx5e_port_eth_proto eproto;
219 	u32 max_speed = 0;
220 	const u32 *table;
221 	u32 max_size;
222 	bool ext;
223 	int err;
224 	int i;
225 
226 	ext = mlx5e_ptys_ext_supported(mdev);
227 	err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto);
228 	if (err)
229 		return err;
230 
231 	mlx5e_port_get_speed_arr(mdev, &table, &max_size, false);
232 	for (i = 0; i < max_size; ++i)
233 		if (eproto.cap & MLX5E_PROT_MASK(i))
234 			max_speed = max(max_speed, table[i]);
235 
236 	*speed = max_speed;
237 	return 0;
238 }
239 
240 u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed,
241 			       bool force_legacy)
242 {
243 	u32 link_modes = 0;
244 	const u32 *table;
245 	u32 max_size;
246 	int i;
247 
248 	mlx5e_port_get_speed_arr(mdev, &table, &max_size, force_legacy);
249 	for (i = 0; i < max_size; ++i) {
250 		if (table[i] == speed)
251 			link_modes |= MLX5E_PROT_MASK(i);
252 	}
253 	return link_modes;
254 }
255 
256 int mlx5e_port_query_pbmc(struct mlx5_core_dev *mdev, void *out)
257 {
258 	int sz = MLX5_ST_SZ_BYTES(pbmc_reg);
259 	void *in;
260 	int err;
261 
262 	in = kzalloc(sz, GFP_KERNEL);
263 	if (!in)
264 		return -ENOMEM;
265 
266 	MLX5_SET(pbmc_reg, in, local_port, 1);
267 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PBMC, 0, 0);
268 
269 	kfree(in);
270 	return err;
271 }
272 
273 int mlx5e_port_set_pbmc(struct mlx5_core_dev *mdev, void *in)
274 {
275 	int sz = MLX5_ST_SZ_BYTES(pbmc_reg);
276 	void *out;
277 	int err;
278 
279 	out = kzalloc(sz, GFP_KERNEL);
280 	if (!out)
281 		return -ENOMEM;
282 
283 	MLX5_SET(pbmc_reg, in, local_port, 1);
284 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PBMC, 0, 1);
285 
286 	kfree(out);
287 	return err;
288 }
289 
290 /* buffer[i]: buffer that priority i mapped to */
291 int mlx5e_port_query_priority2buffer(struct mlx5_core_dev *mdev, u8 *buffer)
292 {
293 	int sz = MLX5_ST_SZ_BYTES(pptb_reg);
294 	u32 prio_x_buff;
295 	void *out;
296 	void *in;
297 	int prio;
298 	int err;
299 
300 	in = kzalloc(sz, GFP_KERNEL);
301 	out = kzalloc(sz, GFP_KERNEL);
302 	if (!in || !out) {
303 		err = -ENOMEM;
304 		goto out;
305 	}
306 
307 	MLX5_SET(pptb_reg, in, local_port, 1);
308 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 0);
309 	if (err)
310 		goto out;
311 
312 	prio_x_buff = MLX5_GET(pptb_reg, out, prio_x_buff);
313 	for (prio = 0; prio < 8; prio++) {
314 		buffer[prio] = (u8)(prio_x_buff >> (4 * prio)) & 0xF;
315 		mlx5_core_dbg(mdev, "prio %d, buffer %d\n", prio, buffer[prio]);
316 	}
317 out:
318 	kfree(in);
319 	kfree(out);
320 	return err;
321 }
322 
323 int mlx5e_port_set_priority2buffer(struct mlx5_core_dev *mdev, u8 *buffer)
324 {
325 	int sz = MLX5_ST_SZ_BYTES(pptb_reg);
326 	u32 prio_x_buff;
327 	void *out;
328 	void *in;
329 	int prio;
330 	int err;
331 
332 	in = kzalloc(sz, GFP_KERNEL);
333 	out = kzalloc(sz, GFP_KERNEL);
334 	if (!in || !out) {
335 		err = -ENOMEM;
336 		goto out;
337 	}
338 
339 	/* First query the pptb register */
340 	MLX5_SET(pptb_reg, in, local_port, 1);
341 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 0);
342 	if (err)
343 		goto out;
344 
345 	memcpy(in, out, sz);
346 	MLX5_SET(pptb_reg, in, local_port, 1);
347 
348 	/* Update the pm and prio_x_buff */
349 	MLX5_SET(pptb_reg, in, pm, 0xFF);
350 
351 	prio_x_buff = 0;
352 	for (prio = 0; prio < 8; prio++)
353 		prio_x_buff |= (buffer[prio] << (4 * prio));
354 	MLX5_SET(pptb_reg, in, prio_x_buff, prio_x_buff);
355 
356 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 1);
357 
358 out:
359 	kfree(in);
360 	kfree(out);
361 	return err;
362 }
363 
364 enum mlx5e_fec_supported_link_mode {
365 	MLX5E_FEC_SUPPORTED_LINK_MODES_10G_40G,
366 	MLX5E_FEC_SUPPORTED_LINK_MODES_25G,
367 	MLX5E_FEC_SUPPORTED_LINK_MODES_50G,
368 	MLX5E_FEC_SUPPORTED_LINK_MODES_56G,
369 	MLX5E_FEC_SUPPORTED_LINK_MODES_100G,
370 	MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X,
371 	MLX5E_FEC_SUPPORTED_LINK_MODE_100G_2X,
372 	MLX5E_FEC_SUPPORTED_LINK_MODE_200G_4X,
373 	MLX5E_FEC_SUPPORTED_LINK_MODE_400G_8X,
374 	MLX5E_MAX_FEC_SUPPORTED_LINK_MODE,
375 };
376 
377 #define MLX5E_FEC_FIRST_50G_PER_LANE_MODE MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X
378 
379 #define MLX5E_FEC_OVERRIDE_ADMIN_POLICY(buf, policy, write, link)			\
380 	do {										\
381 		u16 *_policy = &(policy);						\
382 		u32 *_buf = buf;							\
383 											\
384 		if (write)								\
385 			MLX5_SET(pplm_reg, _buf, fec_override_admin_##link, *_policy);	\
386 		else									\
387 			*_policy = MLX5_GET(pplm_reg, _buf, fec_override_admin_##link);	\
388 	} while (0)
389 
390 /* get/set FEC admin field for a given speed */
391 static int mlx5e_fec_admin_field(u32 *pplm, u16 *fec_policy, bool write,
392 				 enum mlx5e_fec_supported_link_mode link_mode)
393 {
394 	switch (link_mode) {
395 	case MLX5E_FEC_SUPPORTED_LINK_MODES_10G_40G:
396 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 10g_40g);
397 		break;
398 	case MLX5E_FEC_SUPPORTED_LINK_MODES_25G:
399 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 25g);
400 		break;
401 	case MLX5E_FEC_SUPPORTED_LINK_MODES_50G:
402 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 50g);
403 		break;
404 	case MLX5E_FEC_SUPPORTED_LINK_MODES_56G:
405 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 56g);
406 		break;
407 	case MLX5E_FEC_SUPPORTED_LINK_MODES_100G:
408 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 100g);
409 		break;
410 	case MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X:
411 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 50g_1x);
412 		break;
413 	case MLX5E_FEC_SUPPORTED_LINK_MODE_100G_2X:
414 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 100g_2x);
415 		break;
416 	case MLX5E_FEC_SUPPORTED_LINK_MODE_200G_4X:
417 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 200g_4x);
418 		break;
419 	case MLX5E_FEC_SUPPORTED_LINK_MODE_400G_8X:
420 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 400g_8x);
421 		break;
422 	default:
423 		return -EINVAL;
424 	}
425 	return 0;
426 }
427 
428 #define MLX5E_GET_FEC_OVERRIDE_CAP(buf, link)  \
429 	MLX5_GET(pplm_reg, buf, fec_override_cap_##link)
430 
431 /* returns FEC capabilities for a given speed */
432 static int mlx5e_get_fec_cap_field(u32 *pplm, u16 *fec_cap,
433 				   enum mlx5e_fec_supported_link_mode link_mode)
434 {
435 	switch (link_mode) {
436 	case MLX5E_FEC_SUPPORTED_LINK_MODES_10G_40G:
437 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 10g_40g);
438 		break;
439 	case MLX5E_FEC_SUPPORTED_LINK_MODES_25G:
440 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 25g);
441 		break;
442 	case MLX5E_FEC_SUPPORTED_LINK_MODES_50G:
443 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 50g);
444 		break;
445 	case MLX5E_FEC_SUPPORTED_LINK_MODES_56G:
446 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 56g);
447 		break;
448 	case MLX5E_FEC_SUPPORTED_LINK_MODES_100G:
449 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 100g);
450 		break;
451 	case MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X:
452 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 50g_1x);
453 		break;
454 	case MLX5E_FEC_SUPPORTED_LINK_MODE_100G_2X:
455 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 100g_2x);
456 		break;
457 	case MLX5E_FEC_SUPPORTED_LINK_MODE_200G_4X:
458 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 200g_4x);
459 		break;
460 	case MLX5E_FEC_SUPPORTED_LINK_MODE_400G_8X:
461 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 400g_8x);
462 		break;
463 	default:
464 		return -EINVAL;
465 	}
466 	return 0;
467 }
468 
469 bool mlx5e_fec_in_caps(struct mlx5_core_dev *dev, int fec_policy)
470 {
471 	bool fec_50g_per_lane = MLX5_CAP_PCAM_FEATURE(dev, fec_50G_per_lane_in_pplm);
472 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
473 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
474 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
475 	int err;
476 	int i;
477 
478 	if (!MLX5_CAP_GEN(dev, pcam_reg) || !MLX5_CAP_PCAM_REG(dev, pplm))
479 		return false;
480 
481 	MLX5_SET(pplm_reg, in, local_port, 1);
482 	err =  mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
483 	if (err)
484 		return false;
485 
486 	for (i = 0; i < MLX5E_MAX_FEC_SUPPORTED_LINK_MODE; i++) {
487 		u16 fec_caps;
488 
489 		if (i >= MLX5E_FEC_FIRST_50G_PER_LANE_MODE && !fec_50g_per_lane)
490 			break;
491 
492 		mlx5e_get_fec_cap_field(out, &fec_caps, i);
493 		if (fec_caps & fec_policy)
494 			return true;
495 	}
496 	return false;
497 }
498 
499 int mlx5e_get_fec_mode(struct mlx5_core_dev *dev, u32 *fec_mode_active,
500 		       u16 *fec_configured_mode)
501 {
502 	bool fec_50g_per_lane = MLX5_CAP_PCAM_FEATURE(dev, fec_50G_per_lane_in_pplm);
503 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
504 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
505 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
506 	int err;
507 	int i;
508 
509 	if (!MLX5_CAP_GEN(dev, pcam_reg))
510 		return -EOPNOTSUPP;
511 
512 	if (!MLX5_CAP_PCAM_REG(dev, pplm))
513 		return -EOPNOTSUPP;
514 
515 	MLX5_SET(pplm_reg, in, local_port, 1);
516 	err =  mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
517 	if (err)
518 		return err;
519 
520 	*fec_mode_active = MLX5_GET(pplm_reg, out, fec_mode_active);
521 
522 	if (!fec_configured_mode)
523 		goto out;
524 
525 	*fec_configured_mode = 0;
526 	for (i = 0; i < MLX5E_MAX_FEC_SUPPORTED_LINK_MODE; i++) {
527 		if (i >= MLX5E_FEC_FIRST_50G_PER_LANE_MODE && !fec_50g_per_lane)
528 			break;
529 
530 		mlx5e_fec_admin_field(out, fec_configured_mode, 0, i);
531 		if (*fec_configured_mode != 0)
532 			goto out;
533 	}
534 out:
535 	return 0;
536 }
537 
538 int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u16 fec_policy)
539 {
540 	bool fec_50g_per_lane = MLX5_CAP_PCAM_FEATURE(dev, fec_50G_per_lane_in_pplm);
541 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
542 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
543 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
544 	u16 fec_policy_auto = 0;
545 	int err;
546 	int i;
547 
548 	if (!MLX5_CAP_GEN(dev, pcam_reg))
549 		return -EOPNOTSUPP;
550 
551 	if (!MLX5_CAP_PCAM_REG(dev, pplm))
552 		return -EOPNOTSUPP;
553 
554 	if (fec_policy >= (1 << MLX5E_FEC_LLRS_272_257_1) && !fec_50g_per_lane)
555 		return -EOPNOTSUPP;
556 
557 	if (fec_policy && !mlx5e_fec_in_caps(dev, fec_policy))
558 		return -EOPNOTSUPP;
559 
560 	MLX5_SET(pplm_reg, in, local_port, 1);
561 	err = mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
562 	if (err)
563 		return err;
564 
565 	MLX5_SET(pplm_reg, out, local_port, 1);
566 
567 	for (i = 0; i < MLX5E_MAX_FEC_SUPPORTED_LINK_MODE; i++) {
568 		u16 conf_fec = fec_policy;
569 		u16 fec_caps = 0;
570 
571 		if (i >= MLX5E_FEC_FIRST_50G_PER_LANE_MODE && !fec_50g_per_lane)
572 			break;
573 
574 		/* RS fec in ethtool is mapped to MLX5E_FEC_RS_528_514
575 		 * to link modes up to 25G per lane and to
576 		 * MLX5E_FEC_RS_544_514 in the new link modes based on
577 		 * 50 G per lane
578 		 */
579 		if (conf_fec == (1 << MLX5E_FEC_RS_528_514) &&
580 		    i >= MLX5E_FEC_FIRST_50G_PER_LANE_MODE)
581 			conf_fec = (1 << MLX5E_FEC_RS_544_514);
582 
583 		mlx5e_get_fec_cap_field(out, &fec_caps, i);
584 
585 		/* policy supported for link speed */
586 		if (fec_caps & conf_fec)
587 			mlx5e_fec_admin_field(out, &conf_fec, 1, i);
588 		else
589 			/* set FEC to auto*/
590 			mlx5e_fec_admin_field(out, &fec_policy_auto, 1, i);
591 	}
592 
593 	return mlx5_core_access_reg(dev, out, sz, out, sz, MLX5_REG_PPLM, 0, 1);
594 }
595