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_200GAUI_4_200GBASE_CR4_KR4]	= 200000,
77 	[MLX5E_400GAUI_8]			= 400000,
78 };
79 
mlx5e_port_get_speed_arr(struct mlx5_core_dev * mdev,const u32 ** arr,u32 * size,bool force_legacy)80 static void mlx5e_port_get_speed_arr(struct mlx5_core_dev *mdev,
81 				     const u32 **arr, u32 *size,
82 				     bool force_legacy)
83 {
84 	bool ext = force_legacy ? false : MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet);
85 
86 	*size = ext ? ARRAY_SIZE(mlx5e_ext_link_speed) :
87 		      ARRAY_SIZE(mlx5e_link_speed);
88 	*arr  = ext ? mlx5e_ext_link_speed : mlx5e_link_speed;
89 }
90 
mlx5_port_query_eth_proto(struct mlx5_core_dev * dev,u8 port,bool ext,struct mlx5e_port_eth_proto * eproto)91 int mlx5_port_query_eth_proto(struct mlx5_core_dev *dev, u8 port, bool ext,
92 			      struct mlx5e_port_eth_proto *eproto)
93 {
94 	u32 out[MLX5_ST_SZ_DW(ptys_reg)];
95 	int err;
96 
97 	if (!eproto)
98 		return -EINVAL;
99 
100 	err = mlx5_query_port_ptys(dev, out, sizeof(out), MLX5_PTYS_EN, port);
101 	if (err)
102 		return err;
103 
104 	eproto->cap   = MLX5_GET_ETH_PROTO(ptys_reg, out, ext,
105 					   eth_proto_capability);
106 	eproto->admin = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, eth_proto_admin);
107 	eproto->oper  = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, eth_proto_oper);
108 	return 0;
109 }
110 
mlx5_port_query_eth_autoneg(struct mlx5_core_dev * dev,u8 * an_status,u8 * an_disable_cap,u8 * an_disable_admin)111 void mlx5_port_query_eth_autoneg(struct mlx5_core_dev *dev, u8 *an_status,
112 				 u8 *an_disable_cap, u8 *an_disable_admin)
113 {
114 	u32 out[MLX5_ST_SZ_DW(ptys_reg)];
115 
116 	*an_status = 0;
117 	*an_disable_cap = 0;
118 	*an_disable_admin = 0;
119 
120 	if (mlx5_query_port_ptys(dev, out, sizeof(out), MLX5_PTYS_EN, 1))
121 		return;
122 
123 	*an_status = MLX5_GET(ptys_reg, out, an_status);
124 	*an_disable_cap = MLX5_GET(ptys_reg, out, an_disable_cap);
125 	*an_disable_admin = MLX5_GET(ptys_reg, out, an_disable_admin);
126 }
127 
mlx5_port_set_eth_ptys(struct mlx5_core_dev * dev,bool an_disable,u32 proto_admin,bool ext)128 int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable,
129 			   u32 proto_admin, bool ext)
130 {
131 	u32 out[MLX5_ST_SZ_DW(ptys_reg)];
132 	u32 in[MLX5_ST_SZ_DW(ptys_reg)];
133 	u8 an_disable_admin;
134 	u8 an_disable_cap;
135 	u8 an_status;
136 
137 	mlx5_port_query_eth_autoneg(dev, &an_status, &an_disable_cap,
138 				    &an_disable_admin);
139 	if (!an_disable_cap && an_disable)
140 		return -EPERM;
141 
142 	memset(in, 0, sizeof(in));
143 
144 	MLX5_SET(ptys_reg, in, local_port, 1);
145 	MLX5_SET(ptys_reg, in, an_disable_admin, an_disable);
146 	MLX5_SET(ptys_reg, in, proto_mask, MLX5_PTYS_EN);
147 	if (ext)
148 		MLX5_SET(ptys_reg, in, ext_eth_proto_admin, proto_admin);
149 	else
150 		MLX5_SET(ptys_reg, in, eth_proto_admin, proto_admin);
151 
152 	return mlx5_core_access_reg(dev, in, sizeof(in), out,
153 			    sizeof(out), MLX5_REG_PTYS, 0, 1);
154 }
155 
mlx5e_port_ptys2speed(struct mlx5_core_dev * mdev,u32 eth_proto_oper,bool force_legacy)156 u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper,
157 			  bool force_legacy)
158 {
159 	unsigned long temp = eth_proto_oper;
160 	const u32 *table;
161 	u32 speed = 0;
162 	u32 max_size;
163 	int i;
164 
165 	mlx5e_port_get_speed_arr(mdev, &table, &max_size, force_legacy);
166 	i = find_first_bit(&temp, max_size);
167 	if (i < max_size)
168 		speed = table[i];
169 	return speed;
170 }
171 
mlx5e_port_linkspeed(struct mlx5_core_dev * mdev,u32 * speed)172 int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
173 {
174 	struct mlx5e_port_eth_proto eproto;
175 	bool force_legacy = false;
176 	bool ext;
177 	int err;
178 
179 	ext = MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet);
180 	err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto);
181 	if (err)
182 		goto out;
183 	if (ext && !eproto.admin) {
184 		force_legacy = true;
185 		err = mlx5_port_query_eth_proto(mdev, 1, false, &eproto);
186 		if (err)
187 			goto out;
188 	}
189 	*speed = mlx5e_port_ptys2speed(mdev, eproto.oper, force_legacy);
190 	if (!(*speed))
191 		err = -EINVAL;
192 
193 out:
194 	return err;
195 }
196 
mlx5e_port_max_linkspeed(struct mlx5_core_dev * mdev,u32 * speed)197 int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
198 {
199 	struct mlx5e_port_eth_proto eproto;
200 	u32 max_speed = 0;
201 	const u32 *table;
202 	u32 max_size;
203 	bool ext;
204 	int err;
205 	int i;
206 
207 	ext = MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet);
208 	err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto);
209 	if (err)
210 		return err;
211 
212 	mlx5e_port_get_speed_arr(mdev, &table, &max_size, false);
213 	for (i = 0; i < max_size; ++i)
214 		if (eproto.cap & MLX5E_PROT_MASK(i))
215 			max_speed = max(max_speed, table[i]);
216 
217 	*speed = max_speed;
218 	return 0;
219 }
220 
mlx5e_port_speed2linkmodes(struct mlx5_core_dev * mdev,u32 speed,bool force_legacy)221 u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed,
222 			       bool force_legacy)
223 {
224 	u32 link_modes = 0;
225 	const u32 *table;
226 	u32 max_size;
227 	int i;
228 
229 	mlx5e_port_get_speed_arr(mdev, &table, &max_size, force_legacy);
230 	for (i = 0; i < max_size; ++i) {
231 		if (table[i] == speed)
232 			link_modes |= MLX5E_PROT_MASK(i);
233 	}
234 	return link_modes;
235 }
236 
mlx5e_port_query_pbmc(struct mlx5_core_dev * mdev,void * out)237 int mlx5e_port_query_pbmc(struct mlx5_core_dev *mdev, void *out)
238 {
239 	int sz = MLX5_ST_SZ_BYTES(pbmc_reg);
240 	void *in;
241 	int err;
242 
243 	in = kzalloc(sz, GFP_KERNEL);
244 	if (!in)
245 		return -ENOMEM;
246 
247 	MLX5_SET(pbmc_reg, in, local_port, 1);
248 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PBMC, 0, 0);
249 
250 	kfree(in);
251 	return err;
252 }
253 
mlx5e_port_set_pbmc(struct mlx5_core_dev * mdev,void * in)254 int mlx5e_port_set_pbmc(struct mlx5_core_dev *mdev, void *in)
255 {
256 	int sz = MLX5_ST_SZ_BYTES(pbmc_reg);
257 	void *out;
258 	int err;
259 
260 	out = kzalloc(sz, GFP_KERNEL);
261 	if (!out)
262 		return -ENOMEM;
263 
264 	MLX5_SET(pbmc_reg, in, local_port, 1);
265 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PBMC, 0, 1);
266 
267 	kfree(out);
268 	return err;
269 }
270 
271 /* buffer[i]: buffer that priority i mapped to */
mlx5e_port_query_priority2buffer(struct mlx5_core_dev * mdev,u8 * buffer)272 int mlx5e_port_query_priority2buffer(struct mlx5_core_dev *mdev, u8 *buffer)
273 {
274 	int sz = MLX5_ST_SZ_BYTES(pptb_reg);
275 	u32 prio_x_buff;
276 	void *out;
277 	void *in;
278 	int prio;
279 	int err;
280 
281 	in = kzalloc(sz, GFP_KERNEL);
282 	out = kzalloc(sz, GFP_KERNEL);
283 	if (!in || !out) {
284 		err = -ENOMEM;
285 		goto out;
286 	}
287 
288 	MLX5_SET(pptb_reg, in, local_port, 1);
289 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 0);
290 	if (err)
291 		goto out;
292 
293 	prio_x_buff = MLX5_GET(pptb_reg, out, prio_x_buff);
294 	for (prio = 0; prio < 8; prio++) {
295 		buffer[prio] = (u8)(prio_x_buff >> (4 * prio)) & 0xF;
296 		mlx5_core_dbg(mdev, "prio %d, buffer %d\n", prio, buffer[prio]);
297 	}
298 out:
299 	kfree(in);
300 	kfree(out);
301 	return err;
302 }
303 
mlx5e_port_set_priority2buffer(struct mlx5_core_dev * mdev,u8 * buffer)304 int mlx5e_port_set_priority2buffer(struct mlx5_core_dev *mdev, u8 *buffer)
305 {
306 	int sz = MLX5_ST_SZ_BYTES(pptb_reg);
307 	u32 prio_x_buff;
308 	void *out;
309 	void *in;
310 	int prio;
311 	int err;
312 
313 	in = kzalloc(sz, GFP_KERNEL);
314 	out = kzalloc(sz, GFP_KERNEL);
315 	if (!in || !out) {
316 		err = -ENOMEM;
317 		goto out;
318 	}
319 
320 	/* First query the pptb register */
321 	MLX5_SET(pptb_reg, in, local_port, 1);
322 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 0);
323 	if (err)
324 		goto out;
325 
326 	memcpy(in, out, sz);
327 	MLX5_SET(pptb_reg, in, local_port, 1);
328 
329 	/* Update the pm and prio_x_buff */
330 	MLX5_SET(pptb_reg, in, pm, 0xFF);
331 
332 	prio_x_buff = 0;
333 	for (prio = 0; prio < 8; prio++)
334 		prio_x_buff |= (buffer[prio] << (4 * prio));
335 	MLX5_SET(pptb_reg, in, prio_x_buff, prio_x_buff);
336 
337 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 1);
338 
339 out:
340 	kfree(in);
341 	kfree(out);
342 	return err;
343 }
344 
345 static u32 fec_supported_speeds[] = {
346 	10000,
347 	40000,
348 	25000,
349 	50000,
350 	56000,
351 	100000
352 };
353 
354 #define MLX5E_FEC_SUPPORTED_SPEEDS ARRAY_SIZE(fec_supported_speeds)
355 
356 /* get/set FEC admin field for a given speed */
mlx5e_fec_admin_field(u32 * pplm,u8 * fec_policy,bool write,u32 speed)357 static int mlx5e_fec_admin_field(u32 *pplm,
358 				 u8 *fec_policy,
359 				 bool write,
360 				 u32 speed)
361 {
362 	switch (speed) {
363 	case 10000:
364 	case 40000:
365 		if (!write)
366 			*fec_policy = MLX5_GET(pplm_reg, pplm,
367 					       fec_override_admin_10g_40g);
368 		else
369 			MLX5_SET(pplm_reg, pplm,
370 				 fec_override_admin_10g_40g, *fec_policy);
371 		break;
372 	case 25000:
373 		if (!write)
374 			*fec_policy = MLX5_GET(pplm_reg, pplm,
375 					       fec_override_admin_25g);
376 		else
377 			MLX5_SET(pplm_reg, pplm,
378 				 fec_override_admin_25g, *fec_policy);
379 		break;
380 	case 50000:
381 		if (!write)
382 			*fec_policy = MLX5_GET(pplm_reg, pplm,
383 					       fec_override_admin_50g);
384 		else
385 			MLX5_SET(pplm_reg, pplm,
386 				 fec_override_admin_50g, *fec_policy);
387 		break;
388 	case 56000:
389 		if (!write)
390 			*fec_policy = MLX5_GET(pplm_reg, pplm,
391 					       fec_override_admin_56g);
392 		else
393 			MLX5_SET(pplm_reg, pplm,
394 				 fec_override_admin_56g, *fec_policy);
395 		break;
396 	case 100000:
397 		if (!write)
398 			*fec_policy = MLX5_GET(pplm_reg, pplm,
399 					       fec_override_admin_100g);
400 		else
401 			MLX5_SET(pplm_reg, pplm,
402 				 fec_override_admin_100g, *fec_policy);
403 		break;
404 	default:
405 		return -EINVAL;
406 	}
407 	return 0;
408 }
409 
410 /* returns FEC capabilities for a given speed */
mlx5e_get_fec_cap_field(u32 * pplm,u8 * fec_cap,u32 speed)411 static int mlx5e_get_fec_cap_field(u32 *pplm,
412 				   u8 *fec_cap,
413 				   u32 speed)
414 {
415 	switch (speed) {
416 	case 10000:
417 	case 40000:
418 		*fec_cap = MLX5_GET(pplm_reg, pplm,
419 				    fec_override_cap_10g_40g);
420 		break;
421 	case 25000:
422 		*fec_cap = MLX5_GET(pplm_reg, pplm,
423 				    fec_override_cap_25g);
424 		break;
425 	case 50000:
426 		*fec_cap = MLX5_GET(pplm_reg, pplm,
427 				    fec_override_cap_50g);
428 		break;
429 	case 56000:
430 		*fec_cap = MLX5_GET(pplm_reg, pplm,
431 				    fec_override_cap_56g);
432 		break;
433 	case 100000:
434 		*fec_cap = MLX5_GET(pplm_reg, pplm,
435 				    fec_override_cap_100g);
436 		break;
437 	default:
438 		return -EINVAL;
439 	}
440 	return 0;
441 }
442 
mlx5e_get_fec_caps(struct mlx5_core_dev * dev,u8 * fec_caps)443 int mlx5e_get_fec_caps(struct mlx5_core_dev *dev, u8 *fec_caps)
444 {
445 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
446 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
447 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
448 	u32 current_fec_speed;
449 	int err;
450 
451 	if (!MLX5_CAP_GEN(dev, pcam_reg))
452 		return -EOPNOTSUPP;
453 
454 	if (!MLX5_CAP_PCAM_REG(dev, pplm))
455 		return -EOPNOTSUPP;
456 
457 	MLX5_SET(pplm_reg, in, local_port, 1);
458 	err =  mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
459 	if (err)
460 		return err;
461 
462 	err = mlx5e_port_linkspeed(dev, &current_fec_speed);
463 	if (err)
464 		return err;
465 
466 	return mlx5e_get_fec_cap_field(out, fec_caps, current_fec_speed);
467 }
468 
mlx5e_get_fec_mode(struct mlx5_core_dev * dev,u32 * fec_mode_active,u8 * fec_configured_mode)469 int mlx5e_get_fec_mode(struct mlx5_core_dev *dev, u32 *fec_mode_active,
470 		       u8 *fec_configured_mode)
471 {
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 	u32 link_speed;
476 	int err;
477 
478 	if (!MLX5_CAP_GEN(dev, pcam_reg))
479 		return -EOPNOTSUPP;
480 
481 	if (!MLX5_CAP_PCAM_REG(dev, pplm))
482 		return -EOPNOTSUPP;
483 
484 	MLX5_SET(pplm_reg, in, local_port, 1);
485 	err =  mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
486 	if (err)
487 		return err;
488 
489 	*fec_mode_active = MLX5_GET(pplm_reg, out, fec_mode_active);
490 
491 	if (!fec_configured_mode)
492 		return 0;
493 
494 	err = mlx5e_port_linkspeed(dev, &link_speed);
495 	if (err)
496 		return err;
497 
498 	return mlx5e_fec_admin_field(out, fec_configured_mode, 0, link_speed);
499 }
500 
mlx5e_set_fec_mode(struct mlx5_core_dev * dev,u8 fec_policy)501 int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u8 fec_policy)
502 {
503 	u8 fec_policy_nofec = BIT(MLX5E_FEC_NOFEC);
504 	bool fec_mode_not_supp_in_speed = false;
505 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
506 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
507 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
508 	u8 fec_policy_auto = 0;
509 	u8 fec_caps = 0;
510 	int err;
511 	int i;
512 
513 	if (!MLX5_CAP_GEN(dev, pcam_reg))
514 		return -EOPNOTSUPP;
515 
516 	if (!MLX5_CAP_PCAM_REG(dev, pplm))
517 		return -EOPNOTSUPP;
518 
519 	MLX5_SET(pplm_reg, in, local_port, 1);
520 	err = mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
521 	if (err)
522 		return err;
523 
524 	MLX5_SET(pplm_reg, out, local_port, 1);
525 
526 	for (i = 0; i < MLX5E_FEC_SUPPORTED_SPEEDS; i++) {
527 		mlx5e_get_fec_cap_field(out, &fec_caps, fec_supported_speeds[i]);
528 		/* policy supported for link speed, or policy is auto */
529 		if (fec_caps & fec_policy || fec_policy == fec_policy_auto) {
530 			mlx5e_fec_admin_field(out, &fec_policy, 1,
531 					      fec_supported_speeds[i]);
532 		} else {
533 			/* turn off FEC if supported. Else, leave it the same */
534 			if (fec_caps & fec_policy_nofec)
535 				mlx5e_fec_admin_field(out, &fec_policy_nofec, 1,
536 						      fec_supported_speeds[i]);
537 			fec_mode_not_supp_in_speed = true;
538 		}
539 	}
540 
541 	if (fec_mode_not_supp_in_speed)
542 		mlx5_core_dbg(dev,
543 			      "FEC policy 0x%x is not supported for some speeds",
544 			      fec_policy);
545 
546 	return mlx5_core_access_reg(dev, out, sz, out, sz, MLX5_REG_PPLM, 0, 1);
547 }
548