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 #define MLX5E_FEC_OVERRIDE_ADMIN_50G_POLICY(buf, policy, write, link)			\
391 	do {										\
392 		unsigned long policy_long;						\
393 		u16 *__policy = &(policy);						\
394 		bool _write = (write);							\
395 											\
396 		policy_long = *__policy;						\
397 		if (_write && *__policy)						\
398 			*__policy = find_first_bit(&policy_long,			\
399 						   sizeof(policy_long) * BITS_PER_BYTE);\
400 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(buf, *__policy, _write, link);		\
401 		if (!_write && *__policy)						\
402 			*__policy = 1 << *__policy;					\
403 	} while (0)
404 
405 /* get/set FEC admin field for a given speed */
406 static int mlx5e_fec_admin_field(u32 *pplm, u16 *fec_policy, bool write,
407 				 enum mlx5e_fec_supported_link_mode link_mode)
408 {
409 	switch (link_mode) {
410 	case MLX5E_FEC_SUPPORTED_LINK_MODES_10G_40G:
411 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 10g_40g);
412 		break;
413 	case MLX5E_FEC_SUPPORTED_LINK_MODES_25G:
414 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 25g);
415 		break;
416 	case MLX5E_FEC_SUPPORTED_LINK_MODES_50G:
417 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 50g);
418 		break;
419 	case MLX5E_FEC_SUPPORTED_LINK_MODES_56G:
420 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 56g);
421 		break;
422 	case MLX5E_FEC_SUPPORTED_LINK_MODES_100G:
423 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 100g);
424 		break;
425 	case MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X:
426 		MLX5E_FEC_OVERRIDE_ADMIN_50G_POLICY(pplm, *fec_policy, write, 50g_1x);
427 		break;
428 	case MLX5E_FEC_SUPPORTED_LINK_MODE_100G_2X:
429 		MLX5E_FEC_OVERRIDE_ADMIN_50G_POLICY(pplm, *fec_policy, write, 100g_2x);
430 		break;
431 	case MLX5E_FEC_SUPPORTED_LINK_MODE_200G_4X:
432 		MLX5E_FEC_OVERRIDE_ADMIN_50G_POLICY(pplm, *fec_policy, write, 200g_4x);
433 		break;
434 	case MLX5E_FEC_SUPPORTED_LINK_MODE_400G_8X:
435 		MLX5E_FEC_OVERRIDE_ADMIN_50G_POLICY(pplm, *fec_policy, write, 400g_8x);
436 		break;
437 	default:
438 		return -EINVAL;
439 	}
440 	return 0;
441 }
442 
443 #define MLX5E_GET_FEC_OVERRIDE_CAP(buf, link)  \
444 	MLX5_GET(pplm_reg, buf, fec_override_cap_##link)
445 
446 /* returns FEC capabilities for a given speed */
447 static int mlx5e_get_fec_cap_field(u32 *pplm, u16 *fec_cap,
448 				   enum mlx5e_fec_supported_link_mode link_mode)
449 {
450 	switch (link_mode) {
451 	case MLX5E_FEC_SUPPORTED_LINK_MODES_10G_40G:
452 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 10g_40g);
453 		break;
454 	case MLX5E_FEC_SUPPORTED_LINK_MODES_25G:
455 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 25g);
456 		break;
457 	case MLX5E_FEC_SUPPORTED_LINK_MODES_50G:
458 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 50g);
459 		break;
460 	case MLX5E_FEC_SUPPORTED_LINK_MODES_56G:
461 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 56g);
462 		break;
463 	case MLX5E_FEC_SUPPORTED_LINK_MODES_100G:
464 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 100g);
465 		break;
466 	case MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X:
467 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 50g_1x);
468 		break;
469 	case MLX5E_FEC_SUPPORTED_LINK_MODE_100G_2X:
470 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 100g_2x);
471 		break;
472 	case MLX5E_FEC_SUPPORTED_LINK_MODE_200G_4X:
473 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 200g_4x);
474 		break;
475 	case MLX5E_FEC_SUPPORTED_LINK_MODE_400G_8X:
476 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 400g_8x);
477 		break;
478 	default:
479 		return -EINVAL;
480 	}
481 	return 0;
482 }
483 
484 bool mlx5e_fec_in_caps(struct mlx5_core_dev *dev, int fec_policy)
485 {
486 	bool fec_50g_per_lane = MLX5_CAP_PCAM_FEATURE(dev, fec_50G_per_lane_in_pplm);
487 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
488 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
489 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
490 	int err;
491 	int i;
492 
493 	if (!MLX5_CAP_GEN(dev, pcam_reg) || !MLX5_CAP_PCAM_REG(dev, pplm))
494 		return false;
495 
496 	MLX5_SET(pplm_reg, in, local_port, 1);
497 	err =  mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
498 	if (err)
499 		return false;
500 
501 	for (i = 0; i < MLX5E_MAX_FEC_SUPPORTED_LINK_MODE; i++) {
502 		u16 fec_caps;
503 
504 		if (i >= MLX5E_FEC_FIRST_50G_PER_LANE_MODE && !fec_50g_per_lane)
505 			break;
506 
507 		mlx5e_get_fec_cap_field(out, &fec_caps, i);
508 		if (fec_caps & fec_policy)
509 			return true;
510 	}
511 	return false;
512 }
513 
514 int mlx5e_get_fec_mode(struct mlx5_core_dev *dev, u32 *fec_mode_active,
515 		       u16 *fec_configured_mode)
516 {
517 	bool fec_50g_per_lane = MLX5_CAP_PCAM_FEATURE(dev, fec_50G_per_lane_in_pplm);
518 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
519 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
520 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
521 	int err;
522 	int i;
523 
524 	if (!MLX5_CAP_GEN(dev, pcam_reg))
525 		return -EOPNOTSUPP;
526 
527 	if (!MLX5_CAP_PCAM_REG(dev, pplm))
528 		return -EOPNOTSUPP;
529 
530 	MLX5_SET(pplm_reg, in, local_port, 1);
531 	err =  mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
532 	if (err)
533 		return err;
534 
535 	*fec_mode_active = MLX5_GET(pplm_reg, out, fec_mode_active);
536 
537 	if (!fec_configured_mode)
538 		goto out;
539 
540 	*fec_configured_mode = 0;
541 	for (i = 0; i < MLX5E_MAX_FEC_SUPPORTED_LINK_MODE; i++) {
542 		if (i >= MLX5E_FEC_FIRST_50G_PER_LANE_MODE && !fec_50g_per_lane)
543 			break;
544 
545 		mlx5e_fec_admin_field(out, fec_configured_mode, 0, i);
546 		if (*fec_configured_mode != 0)
547 			goto out;
548 	}
549 out:
550 	return 0;
551 }
552 
553 int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u16 fec_policy)
554 {
555 	bool fec_50g_per_lane = MLX5_CAP_PCAM_FEATURE(dev, fec_50G_per_lane_in_pplm);
556 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
557 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
558 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
559 	u16 fec_policy_auto = 0;
560 	int err;
561 	int i;
562 
563 	if (!MLX5_CAP_GEN(dev, pcam_reg))
564 		return -EOPNOTSUPP;
565 
566 	if (!MLX5_CAP_PCAM_REG(dev, pplm))
567 		return -EOPNOTSUPP;
568 
569 	if (fec_policy >= (1 << MLX5E_FEC_LLRS_272_257_1) && !fec_50g_per_lane)
570 		return -EOPNOTSUPP;
571 
572 	if (fec_policy && !mlx5e_fec_in_caps(dev, fec_policy))
573 		return -EOPNOTSUPP;
574 
575 	MLX5_SET(pplm_reg, in, local_port, 1);
576 	err = mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
577 	if (err)
578 		return err;
579 
580 	MLX5_SET(pplm_reg, out, local_port, 1);
581 
582 	for (i = 0; i < MLX5E_MAX_FEC_SUPPORTED_LINK_MODE; i++) {
583 		u16 conf_fec = fec_policy;
584 		u16 fec_caps = 0;
585 
586 		if (i >= MLX5E_FEC_FIRST_50G_PER_LANE_MODE && !fec_50g_per_lane)
587 			break;
588 
589 		/* RS fec in ethtool is mapped to MLX5E_FEC_RS_528_514
590 		 * to link modes up to 25G per lane and to
591 		 * MLX5E_FEC_RS_544_514 in the new link modes based on
592 		 * 50 G per lane
593 		 */
594 		if (conf_fec == (1 << MLX5E_FEC_RS_528_514) &&
595 		    i >= MLX5E_FEC_FIRST_50G_PER_LANE_MODE)
596 			conf_fec = (1 << MLX5E_FEC_RS_544_514);
597 
598 		mlx5e_get_fec_cap_field(out, &fec_caps, i);
599 
600 		/* policy supported for link speed */
601 		if (fec_caps & conf_fec)
602 			mlx5e_fec_admin_field(out, &conf_fec, 1, i);
603 		else
604 			/* set FEC to auto*/
605 			mlx5e_fec_admin_field(out, &fec_policy_auto, 1, i);
606 	}
607 
608 	return mlx5_core_access_reg(dev, out, sz, out, sz, MLX5_REG_PPLM, 0, 1);
609 }
610