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 
mlx5_port_query_eth_autoneg(struct mlx5_core_dev * dev,u8 * an_status,u8 * an_disable_cap,u8 * an_disable_admin)35 void mlx5_port_query_eth_autoneg(struct mlx5_core_dev *dev, u8 *an_status,
36 				 u8 *an_disable_cap, u8 *an_disable_admin)
37 {
38 	u32 out[MLX5_ST_SZ_DW(ptys_reg)];
39 
40 	*an_status = 0;
41 	*an_disable_cap = 0;
42 	*an_disable_admin = 0;
43 
44 	if (mlx5_query_port_ptys(dev, out, sizeof(out), MLX5_PTYS_EN, 1, 0))
45 		return;
46 
47 	*an_status = MLX5_GET(ptys_reg, out, an_status);
48 	*an_disable_cap = MLX5_GET(ptys_reg, out, an_disable_cap);
49 	*an_disable_admin = MLX5_GET(ptys_reg, out, an_disable_admin);
50 }
51 
mlx5_port_set_eth_ptys(struct mlx5_core_dev * dev,bool an_disable,u32 proto_admin,bool ext)52 int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable,
53 			   u32 proto_admin, bool ext)
54 {
55 	u32 out[MLX5_ST_SZ_DW(ptys_reg)];
56 	u32 in[MLX5_ST_SZ_DW(ptys_reg)];
57 	u8 an_disable_admin;
58 	u8 an_disable_cap;
59 	u8 an_status;
60 
61 	mlx5_port_query_eth_autoneg(dev, &an_status, &an_disable_cap,
62 				    &an_disable_admin);
63 	if (!an_disable_cap && an_disable)
64 		return -EPERM;
65 
66 	memset(in, 0, sizeof(in));
67 
68 	MLX5_SET(ptys_reg, in, local_port, 1);
69 	MLX5_SET(ptys_reg, in, an_disable_admin, an_disable);
70 	MLX5_SET(ptys_reg, in, proto_mask, MLX5_PTYS_EN);
71 	if (ext)
72 		MLX5_SET(ptys_reg, in, ext_eth_proto_admin, proto_admin);
73 	else
74 		MLX5_SET(ptys_reg, in, eth_proto_admin, proto_admin);
75 
76 	return mlx5_core_access_reg(dev, in, sizeof(in), out,
77 			    sizeof(out), MLX5_REG_PTYS, 0, 1);
78 }
79 
mlx5e_port_linkspeed(struct mlx5_core_dev * mdev,u32 * speed)80 int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
81 {
82 	struct mlx5_port_eth_proto eproto;
83 	const struct mlx5_link_info *info;
84 	bool force_legacy = false;
85 	bool ext;
86 	int err;
87 
88 	ext = mlx5_ptys_ext_supported(mdev);
89 	err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto);
90 	if (err)
91 		goto out;
92 	if (ext && !eproto.admin) {
93 		force_legacy = true;
94 		err = mlx5_port_query_eth_proto(mdev, 1, false, &eproto);
95 		if (err)
96 			goto out;
97 	}
98 	info = mlx5_port_ptys2info(mdev, eproto.oper, force_legacy);
99 	if (!info) {
100 		*speed = SPEED_UNKNOWN;
101 		err = -EINVAL;
102 		goto out;
103 	}
104 	*speed = info->speed;
105 
106 out:
107 	return err;
108 }
109 
mlx5e_port_query_pbmc(struct mlx5_core_dev * mdev,void * out)110 int mlx5e_port_query_pbmc(struct mlx5_core_dev *mdev, void *out)
111 {
112 	int sz = MLX5_ST_SZ_BYTES(pbmc_reg);
113 	void *in;
114 	int err;
115 
116 	in = kzalloc(sz, GFP_KERNEL);
117 	if (!in)
118 		return -ENOMEM;
119 
120 	MLX5_SET(pbmc_reg, in, local_port, 1);
121 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PBMC, 0, 0);
122 
123 	kfree(in);
124 	return err;
125 }
126 
mlx5e_port_set_pbmc(struct mlx5_core_dev * mdev,void * in)127 int mlx5e_port_set_pbmc(struct mlx5_core_dev *mdev, void *in)
128 {
129 	int sz = MLX5_ST_SZ_BYTES(pbmc_reg);
130 	void *out;
131 	int err;
132 
133 	out = kzalloc(sz, GFP_KERNEL);
134 	if (!out)
135 		return -ENOMEM;
136 
137 	MLX5_SET(pbmc_reg, in, local_port, 1);
138 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PBMC, 0, 1);
139 
140 	kfree(out);
141 	return err;
142 }
143 
mlx5e_port_query_sbpr(struct mlx5_core_dev * mdev,u32 desc,u8 dir,u8 pool_idx,void * out,int size_out)144 int mlx5e_port_query_sbpr(struct mlx5_core_dev *mdev, u32 desc, u8 dir,
145 			  u8 pool_idx, void *out, int size_out)
146 {
147 	u32 in[MLX5_ST_SZ_DW(sbpr_reg)] = {};
148 
149 	MLX5_SET(sbpr_reg, in, desc, desc);
150 	MLX5_SET(sbpr_reg, in, dir, dir);
151 	MLX5_SET(sbpr_reg, in, pool, pool_idx);
152 
153 	return mlx5_core_access_reg(mdev, in, sizeof(in), out, size_out, MLX5_REG_SBPR, 0, 0);
154 }
155 
mlx5e_port_set_sbpr(struct mlx5_core_dev * mdev,u32 desc,u8 dir,u8 pool_idx,u32 infi_size,u32 size)156 int mlx5e_port_set_sbpr(struct mlx5_core_dev *mdev, u32 desc, u8 dir,
157 			u8 pool_idx, u32 infi_size, u32 size)
158 {
159 	u32 out[MLX5_ST_SZ_DW(sbpr_reg)] = {};
160 	u32 in[MLX5_ST_SZ_DW(sbpr_reg)] = {};
161 
162 	MLX5_SET(sbpr_reg, in, desc, desc);
163 	MLX5_SET(sbpr_reg, in, dir, dir);
164 	MLX5_SET(sbpr_reg, in, pool, pool_idx);
165 	MLX5_SET(sbpr_reg, in, infi_size, infi_size);
166 	MLX5_SET(sbpr_reg, in, size, size);
167 	MLX5_SET(sbpr_reg, in, mode, 1);
168 
169 	return mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out), MLX5_REG_SBPR, 0, 1);
170 }
171 
mlx5e_port_query_sbcm(struct mlx5_core_dev * mdev,u32 desc,u8 pg_buff_idx,u8 dir,void * out,int size_out)172 static int mlx5e_port_query_sbcm(struct mlx5_core_dev *mdev, u32 desc,
173 				 u8 pg_buff_idx, u8 dir, void *out,
174 				 int size_out)
175 {
176 	u32 in[MLX5_ST_SZ_DW(sbcm_reg)] = {};
177 
178 	MLX5_SET(sbcm_reg, in, desc, desc);
179 	MLX5_SET(sbcm_reg, in, local_port, 1);
180 	MLX5_SET(sbcm_reg, in, pg_buff, pg_buff_idx);
181 	MLX5_SET(sbcm_reg, in, dir, dir);
182 
183 	return mlx5_core_access_reg(mdev, in, sizeof(in), out, size_out, MLX5_REG_SBCM, 0, 0);
184 }
185 
mlx5e_port_set_sbcm(struct mlx5_core_dev * mdev,u32 desc,u8 pg_buff_idx,u8 dir,u8 infi_size,u32 max_buff,u8 pool_idx)186 int mlx5e_port_set_sbcm(struct mlx5_core_dev *mdev, u32 desc, u8 pg_buff_idx,
187 			u8 dir, u8 infi_size, u32 max_buff, u8 pool_idx)
188 {
189 	u32 out[MLX5_ST_SZ_DW(sbcm_reg)] = {};
190 	u32 in[MLX5_ST_SZ_DW(sbcm_reg)] = {};
191 	u32 min_buff;
192 	int err;
193 	u8 exc;
194 
195 	err = mlx5e_port_query_sbcm(mdev, desc, pg_buff_idx, dir, out,
196 				    sizeof(out));
197 	if (err)
198 		return err;
199 
200 	exc = MLX5_GET(sbcm_reg, out, exc);
201 	min_buff = MLX5_GET(sbcm_reg, out, min_buff);
202 
203 	MLX5_SET(sbcm_reg, in, desc, desc);
204 	MLX5_SET(sbcm_reg, in, local_port, 1);
205 	MLX5_SET(sbcm_reg, in, pg_buff, pg_buff_idx);
206 	MLX5_SET(sbcm_reg, in, dir, dir);
207 	MLX5_SET(sbcm_reg, in, exc, exc);
208 	MLX5_SET(sbcm_reg, in, min_buff, min_buff);
209 	MLX5_SET(sbcm_reg, in, infi_max, infi_size);
210 	MLX5_SET(sbcm_reg, in, max_buff, max_buff);
211 	MLX5_SET(sbcm_reg, in, pool, pool_idx);
212 
213 	return mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out), MLX5_REG_SBCM, 0, 1);
214 }
215 
216 /* buffer[i]: buffer that priority i mapped to */
mlx5e_port_query_priority2buffer(struct mlx5_core_dev * mdev,u8 * buffer)217 int mlx5e_port_query_priority2buffer(struct mlx5_core_dev *mdev, u8 *buffer)
218 {
219 	int sz = MLX5_ST_SZ_BYTES(pptb_reg);
220 	u32 prio_x_buff;
221 	void *out;
222 	void *in;
223 	int prio;
224 	int err;
225 
226 	in = kzalloc(sz, GFP_KERNEL);
227 	out = kzalloc(sz, GFP_KERNEL);
228 	if (!in || !out) {
229 		err = -ENOMEM;
230 		goto out;
231 	}
232 
233 	MLX5_SET(pptb_reg, in, local_port, 1);
234 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 0);
235 	if (err)
236 		goto out;
237 
238 	prio_x_buff = MLX5_GET(pptb_reg, out, prio_x_buff);
239 	for (prio = 0; prio < 8; prio++) {
240 		buffer[prio] = (u8)(prio_x_buff >> (4 * prio)) & 0xF;
241 		mlx5_core_dbg(mdev, "prio %d, buffer %d\n", prio, buffer[prio]);
242 	}
243 out:
244 	kfree(in);
245 	kfree(out);
246 	return err;
247 }
248 
mlx5e_port_set_priority2buffer(struct mlx5_core_dev * mdev,u8 * buffer)249 int mlx5e_port_set_priority2buffer(struct mlx5_core_dev *mdev, u8 *buffer)
250 {
251 	int sz = MLX5_ST_SZ_BYTES(pptb_reg);
252 	u32 prio_x_buff;
253 	void *out;
254 	void *in;
255 	int prio;
256 	int err;
257 
258 	in = kzalloc(sz, GFP_KERNEL);
259 	out = kzalloc(sz, GFP_KERNEL);
260 	if (!in || !out) {
261 		err = -ENOMEM;
262 		goto out;
263 	}
264 
265 	/* First query the pptb register */
266 	MLX5_SET(pptb_reg, in, local_port, 1);
267 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 0);
268 	if (err)
269 		goto out;
270 
271 	memcpy(in, out, sz);
272 	MLX5_SET(pptb_reg, in, local_port, 1);
273 
274 	/* Update the pm and prio_x_buff */
275 	MLX5_SET(pptb_reg, in, pm, 0xFF);
276 
277 	prio_x_buff = 0;
278 	for (prio = 0; prio < 8; prio++)
279 		prio_x_buff |= (buffer[prio] << (4 * prio));
280 	MLX5_SET(pptb_reg, in, prio_x_buff, prio_x_buff);
281 
282 	err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPTB, 0, 1);
283 
284 out:
285 	kfree(in);
286 	kfree(out);
287 	return err;
288 }
289 
290 enum mlx5e_fec_supported_link_mode {
291 	MLX5E_FEC_SUPPORTED_LINK_MODES_10G_40G,
292 	MLX5E_FEC_SUPPORTED_LINK_MODES_25G,
293 	MLX5E_FEC_SUPPORTED_LINK_MODES_50G,
294 	MLX5E_FEC_SUPPORTED_LINK_MODES_56G,
295 	MLX5E_FEC_SUPPORTED_LINK_MODES_100G,
296 	MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X,
297 	MLX5E_FEC_SUPPORTED_LINK_MODE_100G_2X,
298 	MLX5E_FEC_SUPPORTED_LINK_MODE_200G_4X,
299 	MLX5E_FEC_SUPPORTED_LINK_MODE_400G_8X,
300 	MLX5E_FEC_SUPPORTED_LINK_MODE_100G_1X,
301 	MLX5E_FEC_SUPPORTED_LINK_MODE_200G_2X,
302 	MLX5E_FEC_SUPPORTED_LINK_MODE_400G_4X,
303 	MLX5E_FEC_SUPPORTED_LINK_MODE_800G_8X,
304 	MLX5E_FEC_SUPPORTED_LINK_MODE_200G_1X,
305 	MLX5E_FEC_SUPPORTED_LINK_MODE_400G_2X,
306 	MLX5E_FEC_SUPPORTED_LINK_MODE_800G_4X,
307 	MLX5E_FEC_SUPPORTED_LINK_MODE_1600G_8X,
308 	MLX5E_MAX_FEC_SUPPORTED_LINK_MODE,
309 };
310 
311 #define MLX5E_FEC_FIRST_50G_PER_LANE_MODE MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X
312 #define MLX5E_FEC_FIRST_100G_PER_LANE_MODE MLX5E_FEC_SUPPORTED_LINK_MODE_100G_1X
313 #define MLX5E_FEC_FIRST_200G_PER_LANE_MODE MLX5E_FEC_SUPPORTED_LINK_MODE_200G_1X
314 
315 #define MLX5E_FEC_OVERRIDE_ADMIN_POLICY(buf, policy, write, link)			\
316 	do {										\
317 		u16 *_policy = &(policy);						\
318 		u32 *_buf = buf;							\
319 											\
320 		if (write)								\
321 			MLX5_SET(pplm_reg, _buf, fec_override_admin_##link, *_policy);	\
322 		else									\
323 			*_policy = MLX5_GET(pplm_reg, _buf, fec_override_admin_##link);	\
324 	} while (0)
325 
326 /* Returns true if FEC can be set for a given link mode. */
mlx5e_is_fec_supported_link_mode(struct mlx5_core_dev * dev,enum mlx5e_fec_supported_link_mode link_mode)327 static bool mlx5e_is_fec_supported_link_mode(struct mlx5_core_dev *dev,
328 					     enum mlx5e_fec_supported_link_mode link_mode)
329 {
330 	return link_mode < MLX5E_FEC_FIRST_50G_PER_LANE_MODE ||
331 	       (link_mode < MLX5E_FEC_FIRST_100G_PER_LANE_MODE &&
332 		MLX5_CAP_PCAM_FEATURE(dev, fec_50G_per_lane_in_pplm)) ||
333 	       (link_mode < MLX5E_FEC_FIRST_200G_PER_LANE_MODE &&
334 		MLX5_CAP_PCAM_FEATURE(dev, fec_100G_per_lane_in_pplm)) ||
335 	       (link_mode >= MLX5E_FEC_FIRST_200G_PER_LANE_MODE &&
336 		MLX5_CAP_PCAM_FEATURE(dev, fec_200G_per_lane_in_pplm));
337 }
338 
339 /* get/set FEC admin field for a given speed */
mlx5e_fec_admin_field(u32 * pplm,u16 * fec_policy,bool write,enum mlx5e_fec_supported_link_mode link_mode)340 static int mlx5e_fec_admin_field(u32 *pplm, u16 *fec_policy, bool write,
341 				 enum mlx5e_fec_supported_link_mode link_mode)
342 {
343 	switch (link_mode) {
344 	case MLX5E_FEC_SUPPORTED_LINK_MODES_10G_40G:
345 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 10g_40g);
346 		break;
347 	case MLX5E_FEC_SUPPORTED_LINK_MODES_25G:
348 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 25g);
349 		break;
350 	case MLX5E_FEC_SUPPORTED_LINK_MODES_50G:
351 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 50g);
352 		break;
353 	case MLX5E_FEC_SUPPORTED_LINK_MODES_56G:
354 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 56g);
355 		break;
356 	case MLX5E_FEC_SUPPORTED_LINK_MODES_100G:
357 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 100g);
358 		break;
359 	case MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X:
360 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 50g_1x);
361 		break;
362 	case MLX5E_FEC_SUPPORTED_LINK_MODE_100G_2X:
363 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 100g_2x);
364 		break;
365 	case MLX5E_FEC_SUPPORTED_LINK_MODE_200G_4X:
366 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 200g_4x);
367 		break;
368 	case MLX5E_FEC_SUPPORTED_LINK_MODE_400G_8X:
369 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 400g_8x);
370 		break;
371 	case MLX5E_FEC_SUPPORTED_LINK_MODE_100G_1X:
372 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 100g_1x);
373 		break;
374 	case MLX5E_FEC_SUPPORTED_LINK_MODE_200G_2X:
375 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 200g_2x);
376 		break;
377 	case MLX5E_FEC_SUPPORTED_LINK_MODE_400G_4X:
378 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 400g_4x);
379 		break;
380 	case MLX5E_FEC_SUPPORTED_LINK_MODE_800G_8X:
381 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 800g_8x);
382 		break;
383 	case MLX5E_FEC_SUPPORTED_LINK_MODE_200G_1X:
384 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 200g_1x);
385 		break;
386 	case MLX5E_FEC_SUPPORTED_LINK_MODE_400G_2X:
387 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 400g_2x);
388 		break;
389 	case MLX5E_FEC_SUPPORTED_LINK_MODE_800G_4X:
390 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 800g_4x);
391 		break;
392 	case MLX5E_FEC_SUPPORTED_LINK_MODE_1600G_8X:
393 		MLX5E_FEC_OVERRIDE_ADMIN_POLICY(pplm, *fec_policy, write, 1600g_8x);
394 		break;
395 	default:
396 		return -EINVAL;
397 	}
398 	return 0;
399 }
400 
401 #define MLX5E_GET_FEC_OVERRIDE_CAP(buf, link)  \
402 	MLX5_GET(pplm_reg, buf, fec_override_cap_##link)
403 
404 /* returns FEC capabilities for a given speed */
mlx5e_get_fec_cap_field(u32 * pplm,u16 * fec_cap,enum mlx5e_fec_supported_link_mode link_mode)405 static int mlx5e_get_fec_cap_field(u32 *pplm, u16 *fec_cap,
406 				   enum mlx5e_fec_supported_link_mode link_mode)
407 {
408 	switch (link_mode) {
409 	case MLX5E_FEC_SUPPORTED_LINK_MODES_10G_40G:
410 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 10g_40g);
411 		break;
412 	case MLX5E_FEC_SUPPORTED_LINK_MODES_25G:
413 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 25g);
414 		break;
415 	case MLX5E_FEC_SUPPORTED_LINK_MODES_50G:
416 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 50g);
417 		break;
418 	case MLX5E_FEC_SUPPORTED_LINK_MODES_56G:
419 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 56g);
420 		break;
421 	case MLX5E_FEC_SUPPORTED_LINK_MODES_100G:
422 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 100g);
423 		break;
424 	case MLX5E_FEC_SUPPORTED_LINK_MODE_50G_1X:
425 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 50g_1x);
426 		break;
427 	case MLX5E_FEC_SUPPORTED_LINK_MODE_100G_2X:
428 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 100g_2x);
429 		break;
430 	case MLX5E_FEC_SUPPORTED_LINK_MODE_200G_4X:
431 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 200g_4x);
432 		break;
433 	case MLX5E_FEC_SUPPORTED_LINK_MODE_400G_8X:
434 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 400g_8x);
435 		break;
436 	case MLX5E_FEC_SUPPORTED_LINK_MODE_100G_1X:
437 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 100g_1x);
438 		break;
439 	case MLX5E_FEC_SUPPORTED_LINK_MODE_200G_2X:
440 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 200g_2x);
441 		break;
442 	case MLX5E_FEC_SUPPORTED_LINK_MODE_400G_4X:
443 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 400g_4x);
444 		break;
445 	case MLX5E_FEC_SUPPORTED_LINK_MODE_800G_8X:
446 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 800g_8x);
447 		break;
448 	case MLX5E_FEC_SUPPORTED_LINK_MODE_200G_1X:
449 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 200g_1x);
450 		break;
451 	case MLX5E_FEC_SUPPORTED_LINK_MODE_400G_2X:
452 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 400g_2x);
453 		break;
454 	case MLX5E_FEC_SUPPORTED_LINK_MODE_800G_4X:
455 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 800g_4x);
456 		break;
457 	case MLX5E_FEC_SUPPORTED_LINK_MODE_1600G_8X:
458 		*fec_cap = MLX5E_GET_FEC_OVERRIDE_CAP(pplm, 1600g_8x);
459 		break;
460 	default:
461 		return -EINVAL;
462 	}
463 	return 0;
464 }
465 
mlx5e_fec_in_caps(struct mlx5_core_dev * dev,int fec_policy)466 bool mlx5e_fec_in_caps(struct mlx5_core_dev *dev, int fec_policy)
467 {
468 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
469 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
470 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
471 	int err;
472 	int i;
473 
474 	if (!MLX5_CAP_GEN(dev, pcam_reg) || !MLX5_CAP_PCAM_REG(dev, pplm))
475 		return false;
476 
477 	MLX5_SET(pplm_reg, in, local_port, 1);
478 	err =  mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
479 	if (err)
480 		return false;
481 
482 	for (i = 0; i < MLX5E_MAX_FEC_SUPPORTED_LINK_MODE; i++) {
483 		u16 fec_caps;
484 
485 		if (!mlx5e_is_fec_supported_link_mode(dev, i))
486 			break;
487 
488 		mlx5e_get_fec_cap_field(out, &fec_caps, i);
489 		if (fec_caps & fec_policy)
490 			return true;
491 	}
492 	return false;
493 }
494 
mlx5e_get_fec_mode(struct mlx5_core_dev * dev,u32 * fec_mode_active,u16 * fec_configured_mode)495 int mlx5e_get_fec_mode(struct mlx5_core_dev *dev, u32 *fec_mode_active,
496 		       u16 *fec_configured_mode)
497 {
498 	u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {};
499 	u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {};
500 	int sz = MLX5_ST_SZ_BYTES(pplm_reg);
501 	int err;
502 	int i;
503 
504 	if (!MLX5_CAP_GEN(dev, pcam_reg))
505 		return -EOPNOTSUPP;
506 
507 	if (!MLX5_CAP_PCAM_REG(dev, pplm))
508 		return -EOPNOTSUPP;
509 
510 	MLX5_SET(pplm_reg, in, local_port, 1);
511 	err =  mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0);
512 	if (err)
513 		return err;
514 
515 	*fec_mode_active = MLX5_GET(pplm_reg, out, fec_mode_active);
516 
517 	if (!fec_configured_mode)
518 		goto out;
519 
520 	*fec_configured_mode = 0;
521 	for (i = 0; i < MLX5E_MAX_FEC_SUPPORTED_LINK_MODE; i++) {
522 		if (!mlx5e_is_fec_supported_link_mode(dev, i))
523 			break;
524 
525 		mlx5e_fec_admin_field(out, fec_configured_mode, 0, i);
526 		if (*fec_configured_mode != 0)
527 			goto out;
528 	}
529 out:
530 	return 0;
531 }
532 
mlx5e_remap_fec_conf_mode(enum mlx5e_fec_supported_link_mode link_mode,u16 conf_fec)533 static u16 mlx5e_remap_fec_conf_mode(enum mlx5e_fec_supported_link_mode link_mode,
534 				     u16 conf_fec)
535 {
536 	/* RS fec in ethtool is originally mapped to MLX5E_FEC_RS_528_514.
537 	 * For link modes up to 25G per lane, the value is kept.
538 	 * For 50G or 100G per lane, it's remapped to MLX5E_FEC_RS_544_514.
539 	 * For 200G per lane, remapped to MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD.
540 	 */
541 	if (conf_fec != BIT(MLX5E_FEC_RS_528_514))
542 		return conf_fec;
543 
544 	if (link_mode >= MLX5E_FEC_FIRST_200G_PER_LANE_MODE)
545 		return BIT(MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD);
546 
547 	if (link_mode >= MLX5E_FEC_FIRST_50G_PER_LANE_MODE)
548 		return BIT(MLX5E_FEC_RS_544_514);
549 
550 	return conf_fec;
551 }
552 
mlx5e_set_fec_mode(struct mlx5_core_dev * dev,u16 fec_policy)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 (!mlx5e_is_fec_supported_link_mode(dev, i))
587 			break;
588 
589 		conf_fec = mlx5e_remap_fec_conf_mode(i, conf_fec);
590 
591 		mlx5e_get_fec_cap_field(out, &fec_caps, i);
592 
593 		/* policy supported for link speed */
594 		if (fec_caps & conf_fec)
595 			mlx5e_fec_admin_field(out, &conf_fec, 1, i);
596 		else
597 			/* set FEC to auto*/
598 			mlx5e_fec_admin_field(out, &fec_policy_auto, 1, i);
599 	}
600 
601 	return mlx5_core_access_reg(dev, out, sz, out, sz, MLX5_REG_PPLM, 0, 1);
602 }
603