This source file includes following definitions.
- bnxt_fw_reporter_diagnose
- bnxt_fw_reset_recover
- bnxt_fw_fatal_recover
- bnxt_dl_fw_reporters_create
- bnxt_dl_fw_reporters_destroy
- bnxt_devlink_health_report
- bnxt_copy_to_nvm_data
- bnxt_copy_from_nvm_data
- bnxt_hwrm_nvm_req
- bnxt_dl_nvm_param_get
- bnxt_dl_nvm_param_set
- bnxt_dl_msix_validate
- bnxt_dl_register
- bnxt_dl_unregister
1
2
3
4
5
6
7
8
9
10 #include <linux/pci.h>
11 #include <linux/netdevice.h>
12 #include <net/devlink.h>
13 #include "bnxt_hsi.h"
14 #include "bnxt.h"
15 #include "bnxt_vfr.h"
16 #include "bnxt_devlink.h"
17
18 static int bnxt_fw_reporter_diagnose(struct devlink_health_reporter *reporter,
19 struct devlink_fmsg *fmsg)
20 {
21 struct bnxt *bp = devlink_health_reporter_priv(reporter);
22 u32 val, health_status;
23 int rc;
24
25 if (test_bit(BNXT_STATE_IN_FW_RESET, &bp->state))
26 return 0;
27
28 val = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG);
29 health_status = val & 0xffff;
30
31 if (health_status < BNXT_FW_STATUS_HEALTHY) {
32 rc = devlink_fmsg_string_pair_put(fmsg, "Description",
33 "Not yet completed initialization");
34 if (rc)
35 return rc;
36 } else if (health_status > BNXT_FW_STATUS_HEALTHY) {
37 rc = devlink_fmsg_string_pair_put(fmsg, "Description",
38 "Encountered fatal error and cannot recover");
39 if (rc)
40 return rc;
41 }
42
43 if (val >> 16) {
44 rc = devlink_fmsg_u32_pair_put(fmsg, "Error code", val >> 16);
45 if (rc)
46 return rc;
47 }
48
49 val = bnxt_fw_health_readl(bp, BNXT_FW_RESET_CNT_REG);
50 rc = devlink_fmsg_u32_pair_put(fmsg, "Reset count", val);
51 if (rc)
52 return rc;
53
54 return 0;
55 }
56
57 static const struct devlink_health_reporter_ops bnxt_dl_fw_reporter_ops = {
58 .name = "fw",
59 .diagnose = bnxt_fw_reporter_diagnose,
60 };
61
62 static int bnxt_fw_reset_recover(struct devlink_health_reporter *reporter,
63 void *priv_ctx)
64 {
65 struct bnxt *bp = devlink_health_reporter_priv(reporter);
66
67 if (!priv_ctx)
68 return -EOPNOTSUPP;
69
70 bnxt_fw_reset(bp);
71 return 0;
72 }
73
74 static const
75 struct devlink_health_reporter_ops bnxt_dl_fw_reset_reporter_ops = {
76 .name = "fw_reset",
77 .recover = bnxt_fw_reset_recover,
78 };
79
80 static int bnxt_fw_fatal_recover(struct devlink_health_reporter *reporter,
81 void *priv_ctx)
82 {
83 struct bnxt *bp = devlink_health_reporter_priv(reporter);
84 struct bnxt_fw_reporter_ctx *fw_reporter_ctx = priv_ctx;
85 unsigned long event;
86
87 if (!priv_ctx)
88 return -EOPNOTSUPP;
89
90 event = fw_reporter_ctx->sp_event;
91 if (event == BNXT_FW_RESET_NOTIFY_SP_EVENT)
92 bnxt_fw_reset(bp);
93 else if (event == BNXT_FW_EXCEPTION_SP_EVENT)
94 bnxt_fw_exception(bp);
95
96 return 0;
97 }
98
99 static const
100 struct devlink_health_reporter_ops bnxt_dl_fw_fatal_reporter_ops = {
101 .name = "fw_fatal",
102 .recover = bnxt_fw_fatal_recover,
103 };
104
105 void bnxt_dl_fw_reporters_create(struct bnxt *bp)
106 {
107 struct bnxt_fw_health *health = bp->fw_health;
108
109 if (!bp->dl || !health)
110 return;
111
112 if (!(bp->fw_cap & BNXT_FW_CAP_HOT_RESET) || health->fw_reset_reporter)
113 goto err_recovery;
114
115 health->fw_reset_reporter =
116 devlink_health_reporter_create(bp->dl,
117 &bnxt_dl_fw_reset_reporter_ops,
118 0, true, bp);
119 if (IS_ERR(health->fw_reset_reporter)) {
120 netdev_warn(bp->dev, "Failed to create FW fatal health reporter, rc = %ld\n",
121 PTR_ERR(health->fw_reset_reporter));
122 health->fw_reset_reporter = NULL;
123 bp->fw_cap &= ~BNXT_FW_CAP_HOT_RESET;
124 }
125
126 err_recovery:
127 if (!(bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY))
128 return;
129
130 if (!health->fw_reporter) {
131 health->fw_reporter =
132 devlink_health_reporter_create(bp->dl,
133 &bnxt_dl_fw_reporter_ops,
134 0, false, bp);
135 if (IS_ERR(health->fw_reporter)) {
136 netdev_warn(bp->dev, "Failed to create FW health reporter, rc = %ld\n",
137 PTR_ERR(health->fw_reporter));
138 health->fw_reporter = NULL;
139 bp->fw_cap &= ~BNXT_FW_CAP_ERROR_RECOVERY;
140 return;
141 }
142 }
143
144 if (health->fw_fatal_reporter)
145 return;
146
147 health->fw_fatal_reporter =
148 devlink_health_reporter_create(bp->dl,
149 &bnxt_dl_fw_fatal_reporter_ops,
150 0, true, bp);
151 if (IS_ERR(health->fw_fatal_reporter)) {
152 netdev_warn(bp->dev, "Failed to create FW fatal health reporter, rc = %ld\n",
153 PTR_ERR(health->fw_fatal_reporter));
154 health->fw_fatal_reporter = NULL;
155 bp->fw_cap &= ~BNXT_FW_CAP_ERROR_RECOVERY;
156 }
157 }
158
159 void bnxt_dl_fw_reporters_destroy(struct bnxt *bp, bool all)
160 {
161 struct bnxt_fw_health *health = bp->fw_health;
162
163 if (!bp->dl || !health)
164 return;
165
166 if ((all || !(bp->fw_cap & BNXT_FW_CAP_HOT_RESET)) &&
167 health->fw_reset_reporter) {
168 devlink_health_reporter_destroy(health->fw_reset_reporter);
169 health->fw_reset_reporter = NULL;
170 }
171
172 if ((bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY) && !all)
173 return;
174
175 if (health->fw_reporter) {
176 devlink_health_reporter_destroy(health->fw_reporter);
177 health->fw_reporter = NULL;
178 }
179
180 if (health->fw_fatal_reporter) {
181 devlink_health_reporter_destroy(health->fw_fatal_reporter);
182 health->fw_fatal_reporter = NULL;
183 }
184 }
185
186 void bnxt_devlink_health_report(struct bnxt *bp, unsigned long event)
187 {
188 struct bnxt_fw_health *fw_health = bp->fw_health;
189 struct bnxt_fw_reporter_ctx fw_reporter_ctx;
190
191 fw_reporter_ctx.sp_event = event;
192 switch (event) {
193 case BNXT_FW_RESET_NOTIFY_SP_EVENT:
194 if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state)) {
195 if (!fw_health->fw_fatal_reporter)
196 return;
197
198 devlink_health_report(fw_health->fw_fatal_reporter,
199 "FW fatal async event received",
200 &fw_reporter_ctx);
201 return;
202 }
203 if (!fw_health->fw_reset_reporter)
204 return;
205
206 devlink_health_report(fw_health->fw_reset_reporter,
207 "FW non-fatal reset event received",
208 &fw_reporter_ctx);
209 return;
210
211 case BNXT_FW_EXCEPTION_SP_EVENT:
212 if (!fw_health->fw_fatal_reporter)
213 return;
214
215 devlink_health_report(fw_health->fw_fatal_reporter,
216 "FW fatal error reported",
217 &fw_reporter_ctx);
218 return;
219 }
220 }
221
222 static const struct devlink_ops bnxt_dl_ops = {
223 #ifdef CONFIG_BNXT_SRIOV
224 .eswitch_mode_set = bnxt_dl_eswitch_mode_set,
225 .eswitch_mode_get = bnxt_dl_eswitch_mode_get,
226 #endif
227 };
228
229 static const struct devlink_ops bnxt_vf_dl_ops;
230
231 enum bnxt_dl_param_id {
232 BNXT_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
233 BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK,
234 };
235
236 static const struct bnxt_dl_nvm_param nvm_params[] = {
237 {DEVLINK_PARAM_GENERIC_ID_ENABLE_SRIOV, NVM_OFF_ENABLE_SRIOV,
238 BNXT_NVM_SHARED_CFG, 1, 1},
239 {DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI, NVM_OFF_IGNORE_ARI,
240 BNXT_NVM_SHARED_CFG, 1, 1},
241 {DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX,
242 NVM_OFF_MSIX_VEC_PER_PF_MAX, BNXT_NVM_SHARED_CFG, 10, 4},
243 {DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN,
244 NVM_OFF_MSIX_VEC_PER_PF_MIN, BNXT_NVM_SHARED_CFG, 7, 4},
245 {BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK, NVM_OFF_DIS_GRE_VER_CHECK,
246 BNXT_NVM_SHARED_CFG, 1, 1},
247 };
248
249 union bnxt_nvm_data {
250 u8 val8;
251 __le32 val32;
252 };
253
254 static void bnxt_copy_to_nvm_data(union bnxt_nvm_data *dst,
255 union devlink_param_value *src,
256 int nvm_num_bits, int dl_num_bytes)
257 {
258 u32 val32 = 0;
259
260 if (nvm_num_bits == 1) {
261 dst->val8 = src->vbool;
262 return;
263 }
264 if (dl_num_bytes == 4)
265 val32 = src->vu32;
266 else if (dl_num_bytes == 2)
267 val32 = (u32)src->vu16;
268 else if (dl_num_bytes == 1)
269 val32 = (u32)src->vu8;
270 dst->val32 = cpu_to_le32(val32);
271 }
272
273 static void bnxt_copy_from_nvm_data(union devlink_param_value *dst,
274 union bnxt_nvm_data *src,
275 int nvm_num_bits, int dl_num_bytes)
276 {
277 u32 val32;
278
279 if (nvm_num_bits == 1) {
280 dst->vbool = src->val8;
281 return;
282 }
283 val32 = le32_to_cpu(src->val32);
284 if (dl_num_bytes == 4)
285 dst->vu32 = val32;
286 else if (dl_num_bytes == 2)
287 dst->vu16 = (u16)val32;
288 else if (dl_num_bytes == 1)
289 dst->vu8 = (u8)val32;
290 }
291
292 static int bnxt_hwrm_nvm_req(struct bnxt *bp, u32 param_id, void *msg,
293 int msg_len, union devlink_param_value *val)
294 {
295 struct hwrm_nvm_get_variable_input *req = msg;
296 struct bnxt_dl_nvm_param nvm_param;
297 union bnxt_nvm_data *data;
298 dma_addr_t data_dma_addr;
299 int idx = 0, rc, i;
300
301
302 if (BNXT_VF(bp))
303 return -EPERM;
304
305 for (i = 0; i < ARRAY_SIZE(nvm_params); i++) {
306 if (nvm_params[i].id == param_id) {
307 nvm_param = nvm_params[i];
308 break;
309 }
310 }
311
312 if (i == ARRAY_SIZE(nvm_params))
313 return -EOPNOTSUPP;
314
315 if (nvm_param.dir_type == BNXT_NVM_PORT_CFG)
316 idx = bp->pf.port_id;
317 else if (nvm_param.dir_type == BNXT_NVM_FUNC_CFG)
318 idx = bp->pf.fw_fid - BNXT_FIRST_PF_FID;
319
320 data = dma_alloc_coherent(&bp->pdev->dev, sizeof(*data),
321 &data_dma_addr, GFP_KERNEL);
322 if (!data)
323 return -ENOMEM;
324
325 req->dest_data_addr = cpu_to_le64(data_dma_addr);
326 req->data_len = cpu_to_le16(nvm_param.nvm_num_bits);
327 req->option_num = cpu_to_le16(nvm_param.offset);
328 req->index_0 = cpu_to_le16(idx);
329 if (idx)
330 req->dimensions = cpu_to_le16(1);
331
332 if (req->req_type == cpu_to_le16(HWRM_NVM_SET_VARIABLE)) {
333 bnxt_copy_to_nvm_data(data, val, nvm_param.nvm_num_bits,
334 nvm_param.dl_num_bytes);
335 rc = hwrm_send_message(bp, msg, msg_len, HWRM_CMD_TIMEOUT);
336 } else {
337 rc = hwrm_send_message_silent(bp, msg, msg_len,
338 HWRM_CMD_TIMEOUT);
339 if (!rc) {
340 bnxt_copy_from_nvm_data(val, data,
341 nvm_param.nvm_num_bits,
342 nvm_param.dl_num_bytes);
343 } else {
344 struct hwrm_err_output *resp = bp->hwrm_cmd_resp_addr;
345
346 if (resp->cmd_err ==
347 NVM_GET_VARIABLE_CMD_ERR_CODE_VAR_NOT_EXIST)
348 rc = -EOPNOTSUPP;
349 }
350 }
351 dma_free_coherent(&bp->pdev->dev, sizeof(*data), data, data_dma_addr);
352 if (rc == -EACCES)
353 netdev_err(bp->dev, "PF does not have admin privileges to modify NVM config\n");
354 return rc;
355 }
356
357 static int bnxt_dl_nvm_param_get(struct devlink *dl, u32 id,
358 struct devlink_param_gset_ctx *ctx)
359 {
360 struct hwrm_nvm_get_variable_input req = {0};
361 struct bnxt *bp = bnxt_get_bp_from_dl(dl);
362 int rc;
363
364 bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_NVM_GET_VARIABLE, -1, -1);
365 rc = bnxt_hwrm_nvm_req(bp, id, &req, sizeof(req), &ctx->val);
366 if (!rc)
367 if (id == BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK)
368 ctx->val.vbool = !ctx->val.vbool;
369
370 return rc;
371 }
372
373 static int bnxt_dl_nvm_param_set(struct devlink *dl, u32 id,
374 struct devlink_param_gset_ctx *ctx)
375 {
376 struct hwrm_nvm_set_variable_input req = {0};
377 struct bnxt *bp = bnxt_get_bp_from_dl(dl);
378
379 bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_NVM_SET_VARIABLE, -1, -1);
380
381 if (id == BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK)
382 ctx->val.vbool = !ctx->val.vbool;
383
384 return bnxt_hwrm_nvm_req(bp, id, &req, sizeof(req), &ctx->val);
385 }
386
387 static int bnxt_dl_msix_validate(struct devlink *dl, u32 id,
388 union devlink_param_value val,
389 struct netlink_ext_ack *extack)
390 {
391 int max_val = -1;
392
393 if (id == DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX)
394 max_val = BNXT_MSIX_VEC_MAX;
395
396 if (id == DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN)
397 max_val = BNXT_MSIX_VEC_MIN_MAX;
398
399 if (val.vu32 > max_val) {
400 NL_SET_ERR_MSG_MOD(extack, "MSIX value is exceeding the range");
401 return -EINVAL;
402 }
403
404 return 0;
405 }
406
407 static const struct devlink_param bnxt_dl_params[] = {
408 DEVLINK_PARAM_GENERIC(ENABLE_SRIOV,
409 BIT(DEVLINK_PARAM_CMODE_PERMANENT),
410 bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
411 NULL),
412 DEVLINK_PARAM_GENERIC(IGNORE_ARI,
413 BIT(DEVLINK_PARAM_CMODE_PERMANENT),
414 bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
415 NULL),
416 DEVLINK_PARAM_GENERIC(MSIX_VEC_PER_PF_MAX,
417 BIT(DEVLINK_PARAM_CMODE_PERMANENT),
418 bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
419 bnxt_dl_msix_validate),
420 DEVLINK_PARAM_GENERIC(MSIX_VEC_PER_PF_MIN,
421 BIT(DEVLINK_PARAM_CMODE_PERMANENT),
422 bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
423 bnxt_dl_msix_validate),
424 DEVLINK_PARAM_DRIVER(BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK,
425 "gre_ver_check", DEVLINK_PARAM_TYPE_BOOL,
426 BIT(DEVLINK_PARAM_CMODE_PERMANENT),
427 bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
428 NULL),
429 };
430
431 static const struct devlink_param bnxt_dl_port_params[] = {
432 };
433
434 int bnxt_dl_register(struct bnxt *bp)
435 {
436 struct devlink *dl;
437 int rc;
438
439 if (bp->hwrm_spec_code < 0x10600) {
440 netdev_warn(bp->dev, "Firmware does not support NVM params");
441 return -ENOTSUPP;
442 }
443
444 if (BNXT_PF(bp))
445 dl = devlink_alloc(&bnxt_dl_ops, sizeof(struct bnxt_dl));
446 else
447 dl = devlink_alloc(&bnxt_vf_dl_ops, sizeof(struct bnxt_dl));
448 if (!dl) {
449 netdev_warn(bp->dev, "devlink_alloc failed");
450 return -ENOMEM;
451 }
452
453 bnxt_link_bp_to_dl(bp, dl);
454
455
456 if (pci_find_ext_capability(bp->pdev, PCI_EXT_CAP_ID_SRIOV) &&
457 bp->hwrm_spec_code > 0x10803)
458 bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY;
459
460 rc = devlink_register(dl, &bp->pdev->dev);
461 if (rc) {
462 netdev_warn(bp->dev, "devlink_register failed. rc=%d", rc);
463 goto err_dl_free;
464 }
465
466 if (!BNXT_PF(bp))
467 return 0;
468
469 rc = devlink_params_register(dl, bnxt_dl_params,
470 ARRAY_SIZE(bnxt_dl_params));
471 if (rc) {
472 netdev_warn(bp->dev, "devlink_params_register failed. rc=%d",
473 rc);
474 goto err_dl_unreg;
475 }
476
477 devlink_port_attrs_set(&bp->dl_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
478 bp->pf.port_id, false, 0,
479 bp->switch_id, sizeof(bp->switch_id));
480 rc = devlink_port_register(dl, &bp->dl_port, bp->pf.port_id);
481 if (rc) {
482 netdev_err(bp->dev, "devlink_port_register failed");
483 goto err_dl_param_unreg;
484 }
485
486 rc = devlink_port_params_register(&bp->dl_port, bnxt_dl_port_params,
487 ARRAY_SIZE(bnxt_dl_port_params));
488 if (rc) {
489 netdev_err(bp->dev, "devlink_port_params_register failed");
490 goto err_dl_port_unreg;
491 }
492
493 devlink_params_publish(dl);
494
495 return 0;
496
497 err_dl_port_unreg:
498 devlink_port_unregister(&bp->dl_port);
499 err_dl_param_unreg:
500 devlink_params_unregister(dl, bnxt_dl_params,
501 ARRAY_SIZE(bnxt_dl_params));
502 err_dl_unreg:
503 devlink_unregister(dl);
504 err_dl_free:
505 bnxt_link_bp_to_dl(bp, NULL);
506 devlink_free(dl);
507 return rc;
508 }
509
510 void bnxt_dl_unregister(struct bnxt *bp)
511 {
512 struct devlink *dl = bp->dl;
513
514 if (!dl)
515 return;
516
517 if (BNXT_PF(bp)) {
518 devlink_port_params_unregister(&bp->dl_port,
519 bnxt_dl_port_params,
520 ARRAY_SIZE(bnxt_dl_port_params));
521 devlink_port_unregister(&bp->dl_port);
522 devlink_params_unregister(dl, bnxt_dl_params,
523 ARRAY_SIZE(bnxt_dl_params));
524 }
525 devlink_unregister(dl);
526 devlink_free(dl);
527 }