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 int mlx5e_port_query_sbpr(struct mlx5_core_dev *mdev, u32 desc, u8 dir,
291 			  u8 pool_idx, void *out, int size_out)
292 {
293 	u32 in[MLX5_ST_SZ_DW(sbpr_reg)] = {};
294 
295 	MLX5_SET(sbpr_reg, in, desc, desc);
296 	MLX5_SET(sbpr_reg, in, dir, dir);
297 	MLX5_SET(sbpr_reg, in, pool, pool_idx);
298 
299 	return mlx5_core_access_reg(mdev, in, sizeof(in), out, size_out, MLX5_REG_SBPR, 0, 0);
300 }
301 
302 int mlx5e_port_set_sbpr(struct mlx5_core_dev *mdev, u32 desc, u8 dir,
303 			u8 pool_idx, u32 infi_size, u32 size)
304 {
305 	u32 out[MLX5_ST_SZ_DW(sbpr_reg)] = {};
306 	u32 in[MLX5_ST_SZ_DW(sbpr_reg)] = {};
307 
308 	MLX5_SET(sbpr_reg, in, desc, desc);
309 	MLX5_SET(sbpr_reg, in, dir, dir);
310 	MLX5_SET(sbpr_reg, in, pool, pool_idx);
311 	MLX5_SET(sbpr_reg, in, infi_size, infi_size);
312 	MLX5_SET(sbpr_reg, in, size, size);
313 	MLX5_SET(sbpr_reg, in, mode, 1);
314 
315 	return mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out), MLX5_REG_SBPR, 0, 1);
316 }
317 
318 static int mlx5e_port_query_sbcm(struct mlx5_core_dev *mdev, u32 desc,
319 				 u8 pg_buff_idx, u8 dir, void *out,
320 				 int size_out)
321 {
322 	u32 in[MLX5_ST_SZ_DW(sbcm_reg)] = {};
323 
324 	MLX5_SET(sbcm_reg, in, desc, desc);
325 	MLX5_SET(sbcm_reg, in, local_port, 1);
326 	MLX5_SET(sbcm_reg, in, pg_buff, pg_buff_idx);
327 	MLX5_SET(sbcm_reg, in, dir, dir);
328 
329 	return mlx5_core_access_reg(mdev, in, sizeof(in), out, size_out, MLX5_REG_SBCM, 0, 0);
330 }
331 
332 int mlx5e_port_set_sbcm(struct mlx5_core_dev *mdev, u32 desc, u8 pg_buff_idx,
333 			u8 dir, u8 infi_size, u32 max_buff, u8 pool_idx)
334 {
335 	u32 out[MLX5_ST_SZ_DW(sbcm_reg)] = {};
336 	u32 in[MLX5_ST_SZ_DW(sbcm_reg)] = {};
337 	u32 min_buff;
338 	int err;
339 	u8 exc;
340 
341 	err = mlx5e_port_query_sbcm(mdev, desc, pg_buff_idx, dir, out,
342 				    sizeof(out));
343 	if (err)
344 		return err;
345 
346 	exc = MLX5_GET(sbcm_reg, out, exc);
347 	min_buff = MLX5_GET(sbcm_reg, out, min_buff);
348 
349 	MLX5_SET(sbcm_reg, in, desc, desc);
350 	MLX5_SET(sbcm_reg, in, local_port, 1);
351 	MLX5_SET(sbcm_reg, in, pg_buff, pg_buff_idx);
352 	MLX5_SET(sbcm_reg, in, dir, dir);
353 	MLX5_SET(sbcm_reg, in, exc, exc);
354 	MLX5_SET(sbcm_reg, in, min_buff, min_buff);
355 	MLX5_SET(sbcm_reg, in, infi_max, infi_size);
356 	MLX5_SET(sbcm_reg, in, max_buff, max_buff);
357 	MLX5_SET(sbcm_reg, in, pool, pool_idx);
358 
359 	return mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out), MLX5_REG_SBCM, 0, 1);
360 }
361 
362 /* buffer[i]: buffer that priority i mapped to */
363 int mlx5e_port_query_priority2buffer(struct mlx5_core_dev *mdev, u8 *buffer)
364 {
365 	int sz = MLX5_ST_SZ_BYTES(pptb_reg);
366 	u32 prio_x_buff;
367 	void *out;
368 	void *in;
369 	int prio;
370 	int err;
371 
372 	in = kzalloc(sz, GFP_KERNEL);
373 	out = kzalloc(sz, GFP_KERNEL);
374 	if (!in || !out) {
375 		err = -ENOMEM;
376 		goto out;
377 	}
378 
379 	MLX5_SET(pptb_reg, in, local_port, 1);
380 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 0);
381 	if (err)
382 		goto out;
383 
384 	prio_x_buff = MLX5_GET(pptb_reg, out, prio_x_buff);
385 	for (prio = 0; prio < 8; prio++) {
386 		buffer[prio] = (u8)(prio_x_buff >> (4 * prio)) & 0xF;
387 		mlx5_core_dbg(mdev, "prio %d, buffer %d\n", prio, buffer[prio]);
388 	}
389 out:
390 	kfree(in);
391 	kfree(out);
392 	return err;
393 }
394 
395 int mlx5e_port_set_priority2buffer(struct mlx5_core_dev *mdev, u8 *buffer)
396 {
397 	int sz = MLX5_ST_SZ_BYTES(pptb_reg);
398 	u32 prio_x_buff;
399 	void *out;
400 	void *in;
401 	int prio;
402 	int err;
403 
404 	in = kzalloc(sz, GFP_KERNEL);
405 	out = kzalloc(sz, GFP_KERNEL);
406 	if (!in || !out) {
407 		err = -ENOMEM;
408 		goto out;
409 	}
410 
411 	/* First query the pptb register */
412 	MLX5_SET(pptb_reg, in, local_port, 1);
413 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 0);
414 	if (err)
415 		goto out;
416 
417 	memcpy(in, out, sz);
418 	MLX5_SET(pptb_reg, in, local_port, 1);
419 
420 	/* Update the pm and prio_x_buff */
421 	MLX5_SET(pptb_reg, in, pm, 0xFF);
422 
423 	prio_x_buff = 0;
424 	for (prio = 0; prio < 8; prio++)
425 		prio_x_buff |= (buffer[prio] << (4 * prio));
426 	MLX5_SET(pptb_reg, in, prio_x_buff, prio_x_buff);
427 
428 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 1);
429 
430 out:
431 	kfree(in);
432 	kfree(out);
433 	return err;
434 }
435 
436 enum mlx5e_fec_supported_link_mode {
437 	MLX5E_FEC_SUPPORTED_LINK_MODES_10G_40G,
438 	MLX5E_FEC_SUPPORTED_LINK_MODES_25G,
439 	MLX5E_FEC_SUPPORTED_LINK_MODES_50G,
440 	MLX5E_FEC_SUPPORTED_LINK_MODES_56G,
441 	MLX5E_FEC_SUPPORTED_LINK_MODES_100G,
442 	MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X,
443 	MLX5E_FEC_SUPPORTED_LINK_MODE_100G_2X,
444 	MLX5E_FEC_SUPPORTED_LINK_MODE_200G_4X,
445 	MLX5E_FEC_SUPPORTED_LINK_MODE_400G_8X,
446 	MLX5E_MAX_FEC_SUPPORTED_LINK_MODE,
447 };
448 
449 #define MLX5E_FEC_FIRST_50G_PER_LANE_MODE MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X
450 
451 #define MLX5E_FEC_OVERRIDE_ADMIN_POLICY(buf, policy, write, link)			\
452 	do {										\
453 		u16 *_policy = &(policy);						\
454 		u32 *_buf = buf;							\
455 											\
456 		if (write)								\
457 			MLX5_SET(pplm_reg, _buf, fec_override_admin_##link, *_policy);	\
458 		else									\
459 			*_policy = MLX5_GET(pplm_reg, _buf, fec_override_admin_##link);	\
460 	} while (0)
461 
462 /* get/set FEC admin field for a given speed */
463 static int mlx5e_fec_admin_field(u32 *pplm, u16 *fec_policy, bool write,
464 				 enum mlx5e_fec_supported_link_mode link_mode)
465 {
466 	switch (link_mode) {
467 	case MLX5E_FEC_SUPPORTED_LINK_MODES_10G_40G:
468 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 10g_40g);
469 		break;
470 	case MLX5E_FEC_SUPPORTED_LINK_MODES_25G:
471 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 25g);
472 		break;
473 	case MLX5E_FEC_SUPPORTED_LINK_MODES_50G:
474 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 50g);
475 		break;
476 	case MLX5E_FEC_SUPPORTED_LINK_MODES_56G:
477 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 56g);
478 		break;
479 	case MLX5E_FEC_SUPPORTED_LINK_MODES_100G:
480 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 100g);
481 		break;
482 	case MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X:
483 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 50g_1x);
484 		break;
485 	case MLX5E_FEC_SUPPORTED_LINK_MODE_100G_2X:
486 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 100g_2x);
487 		break;
488 	case MLX5E_FEC_SUPPORTED_LINK_MODE_200G_4X:
489 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 200g_4x);
490 		break;
491 	case MLX5E_FEC_SUPPORTED_LINK_MODE_400G_8X:
492 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 400g_8x);
493 		break;
494 	default:
495 		return -EINVAL;
496 	}
497 	return 0;
498 }
499 
500 #define MLX5E_GET_FEC_OVERRIDE_CAP(buf, link)  \
501 	MLX5_GET(pplm_reg, buf, fec_override_cap_##link)
502 
503 /* returns FEC capabilities for a given speed */
504 static int mlx5e_get_fec_cap_field(u32 *pplm, u16 *fec_cap,
505 				   enum mlx5e_fec_supported_link_mode link_mode)
506 {
507 	switch (link_mode) {
508 	case MLX5E_FEC_SUPPORTED_LINK_MODES_10G_40G:
509 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 10g_40g);
510 		break;
511 	case MLX5E_FEC_SUPPORTED_LINK_MODES_25G:
512 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 25g);
513 		break;
514 	case MLX5E_FEC_SUPPORTED_LINK_MODES_50G:
515 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 50g);
516 		break;
517 	case MLX5E_FEC_SUPPORTED_LINK_MODES_56G:
518 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 56g);
519 		break;
520 	case MLX5E_FEC_SUPPORTED_LINK_MODES_100G:
521 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 100g);
522 		break;
523 	case MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X:
524 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 50g_1x);
525 		break;
526 	case MLX5E_FEC_SUPPORTED_LINK_MODE_100G_2X:
527 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 100g_2x);
528 		break;
529 	case MLX5E_FEC_SUPPORTED_LINK_MODE_200G_4X:
530 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 200g_4x);
531 		break;
532 	case MLX5E_FEC_SUPPORTED_LINK_MODE_400G_8X:
533 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 400g_8x);
534 		break;
535 	default:
536 		return -EINVAL;
537 	}
538 	return 0;
539 }
540 
541 bool mlx5e_fec_in_caps(struct mlx5_core_dev *dev, int fec_policy)
542 {
543 	bool fec_50g_per_lane = MLX5_CAP_PCAM_FEATURE(dev, fec_50G_per_lane_in_pplm);
544 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
545 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
546 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
547 	int err;
548 	int i;
549 
550 	if (!MLX5_CAP_GEN(dev, pcam_reg) || !MLX5_CAP_PCAM_REG(dev, pplm))
551 		return false;
552 
553 	MLX5_SET(pplm_reg, in, local_port, 1);
554 	err =  mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
555 	if (err)
556 		return false;
557 
558 	for (i = 0; i < MLX5E_MAX_FEC_SUPPORTED_LINK_MODE; i++) {
559 		u16 fec_caps;
560 
561 		if (i >= MLX5E_FEC_FIRST_50G_PER_LANE_MODE && !fec_50g_per_lane)
562 			break;
563 
564 		mlx5e_get_fec_cap_field(out, &fec_caps, i);
565 		if (fec_caps & fec_policy)
566 			return true;
567 	}
568 	return false;
569 }
570 
571 int mlx5e_get_fec_mode(struct mlx5_core_dev *dev, u32 *fec_mode_active,
572 		       u16 *fec_configured_mode)
573 {
574 	bool fec_50g_per_lane = MLX5_CAP_PCAM_FEATURE(dev, fec_50G_per_lane_in_pplm);
575 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
576 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
577 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
578 	int err;
579 	int i;
580 
581 	if (!MLX5_CAP_GEN(dev, pcam_reg))
582 		return -EOPNOTSUPP;
583 
584 	if (!MLX5_CAP_PCAM_REG(dev, pplm))
585 		return -EOPNOTSUPP;
586 
587 	MLX5_SET(pplm_reg, in, local_port, 1);
588 	err =  mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
589 	if (err)
590 		return err;
591 
592 	*fec_mode_active = MLX5_GET(pplm_reg, out, fec_mode_active);
593 
594 	if (!fec_configured_mode)
595 		goto out;
596 
597 	*fec_configured_mode = 0;
598 	for (i = 0; i < MLX5E_MAX_FEC_SUPPORTED_LINK_MODE; i++) {
599 		if (i >= MLX5E_FEC_FIRST_50G_PER_LANE_MODE && !fec_50g_per_lane)
600 			break;
601 
602 		mlx5e_fec_admin_field(out, fec_configured_mode, 0, i);
603 		if (*fec_configured_mode != 0)
604 			goto out;
605 	}
606 out:
607 	return 0;
608 }
609 
610 int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u16 fec_policy)
611 {
612 	bool fec_50g_per_lane = MLX5_CAP_PCAM_FEATURE(dev, fec_50G_per_lane_in_pplm);
613 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
614 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
615 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
616 	u16 fec_policy_auto = 0;
617 	int err;
618 	int i;
619 
620 	if (!MLX5_CAP_GEN(dev, pcam_reg))
621 		return -EOPNOTSUPP;
622 
623 	if (!MLX5_CAP_PCAM_REG(dev, pplm))
624 		return -EOPNOTSUPP;
625 
626 	if (fec_policy >= (1 << MLX5E_FEC_LLRS_272_257_1) && !fec_50g_per_lane)
627 		return -EOPNOTSUPP;
628 
629 	if (fec_policy && !mlx5e_fec_in_caps(dev, fec_policy))
630 		return -EOPNOTSUPP;
631 
632 	MLX5_SET(pplm_reg, in, local_port, 1);
633 	err = mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
634 	if (err)
635 		return err;
636 
637 	MLX5_SET(pplm_reg, out, local_port, 1);
638 
639 	for (i = 0; i < MLX5E_MAX_FEC_SUPPORTED_LINK_MODE; i++) {
640 		u16 conf_fec = fec_policy;
641 		u16 fec_caps = 0;
642 
643 		if (i >= MLX5E_FEC_FIRST_50G_PER_LANE_MODE && !fec_50g_per_lane)
644 			break;
645 
646 		/* RS fec in ethtool is mapped to MLX5E_FEC_RS_528_514
647 		 * to link modes up to 25G per lane and to
648 		 * MLX5E_FEC_RS_544_514 in the new link modes based on
649 		 * 50 G per lane
650 		 */
651 		if (conf_fec == (1 << MLX5E_FEC_RS_528_514) &&
652 		    i >= MLX5E_FEC_FIRST_50G_PER_LANE_MODE)
653 			conf_fec = (1 << MLX5E_FEC_RS_544_514);
654 
655 		mlx5e_get_fec_cap_field(out, &fec_caps, i);
656 
657 		/* policy supported for link speed */
658 		if (fec_caps & conf_fec)
659 			mlx5e_fec_admin_field(out, &conf_fec, 1, i);
660 		else
661 			/* set FEC to auto*/
662 			mlx5e_fec_admin_field(out, &fec_policy_auto, 1, i);
663 	}
664 
665 	return mlx5_core_access_reg(dev, out, sz, out, sz, MLX5_REG_PPLM, 0, 1);
666 }
667