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