1 | /* Broadcom NetXtreme-C/E network driver. |
2 | * |
3 | * Copyright (c) 2017 Broadcom Limited |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation. |
8 | */ |
9 | |
10 | #include <linux/pci.h> |
11 | #include <linux/netdevice.h> |
12 | #include <linux/vmalloc.h> |
13 | #include <net/devlink.h> |
14 | #include "bnxt_hsi.h" |
15 | #include "bnxt.h" |
16 | #include "bnxt_hwrm.h" |
17 | #include "bnxt_vfr.h" |
18 | #include "bnxt_devlink.h" |
19 | #include "bnxt_ethtool.h" |
20 | #include "bnxt_ulp.h" |
21 | #include "bnxt_ptp.h" |
22 | #include "bnxt_coredump.h" |
23 | #include "bnxt_nvm_defs.h" |
24 | |
25 | static void __bnxt_fw_recover(struct bnxt *bp) |
26 | { |
27 | if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state) || |
28 | test_bit(BNXT_STATE_FW_NON_FATAL_COND, &bp->state)) |
29 | bnxt_fw_reset(bp); |
30 | else |
31 | bnxt_fw_exception(bp); |
32 | } |
33 | |
34 | static int |
35 | bnxt_dl_flash_update(struct devlink *dl, |
36 | struct devlink_flash_update_params *params, |
37 | struct netlink_ext_ack *extack) |
38 | { |
39 | struct bnxt *bp = bnxt_get_bp_from_dl(dl); |
40 | int rc; |
41 | |
42 | if (!BNXT_PF(bp)) { |
43 | NL_SET_ERR_MSG_MOD(extack, |
44 | "flash update not supported from a VF" ); |
45 | return -EPERM; |
46 | } |
47 | |
48 | devlink_flash_update_status_notify(devlink: dl, status_msg: "Preparing to flash" , NULL, done: 0, total: 0); |
49 | rc = bnxt_flash_package_from_fw_obj(dev: bp->dev, fw: params->fw, install_type: 0, extack); |
50 | if (!rc) |
51 | devlink_flash_update_status_notify(devlink: dl, status_msg: "Flashing done" , NULL, done: 0, total: 0); |
52 | else |
53 | devlink_flash_update_status_notify(devlink: dl, status_msg: "Flashing failed" , NULL, done: 0, total: 0); |
54 | return rc; |
55 | } |
56 | |
57 | static int bnxt_hwrm_remote_dev_reset_set(struct bnxt *bp, bool remote_reset) |
58 | { |
59 | struct hwrm_func_cfg_input *req; |
60 | int rc; |
61 | |
62 | if (~bp->fw_cap & BNXT_FW_CAP_HOT_RESET_IF) |
63 | return -EOPNOTSUPP; |
64 | |
65 | rc = bnxt_hwrm_func_cfg_short_req_init(bp, req: &req); |
66 | if (rc) |
67 | return rc; |
68 | |
69 | req->fid = cpu_to_le16(0xffff); |
70 | req->enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_HOT_RESET_IF_SUPPORT); |
71 | if (remote_reset) |
72 | req->flags = cpu_to_le32(FUNC_CFG_REQ_FLAGS_HOT_RESET_IF_EN_DIS); |
73 | |
74 | return hwrm_req_send(bp, req); |
75 | } |
76 | |
77 | static char *bnxt_health_severity_str(enum bnxt_health_severity severity) |
78 | { |
79 | switch (severity) { |
80 | case SEVERITY_NORMAL: return "normal" ; |
81 | case SEVERITY_WARNING: return "warning" ; |
82 | case SEVERITY_RECOVERABLE: return "recoverable" ; |
83 | case SEVERITY_FATAL: return "fatal" ; |
84 | default: return "unknown" ; |
85 | } |
86 | } |
87 | |
88 | static char *bnxt_health_remedy_str(enum bnxt_health_remedy remedy) |
89 | { |
90 | switch (remedy) { |
91 | case REMEDY_DEVLINK_RECOVER: return "devlink recover" ; |
92 | case REMEDY_POWER_CYCLE_DEVICE: return "device power cycle" ; |
93 | case REMEDY_POWER_CYCLE_HOST: return "host power cycle" ; |
94 | case REMEDY_FW_UPDATE: return "update firmware" ; |
95 | case REMEDY_HW_REPLACE: return "replace hardware" ; |
96 | default: return "unknown" ; |
97 | } |
98 | } |
99 | |
100 | static int bnxt_fw_diagnose(struct devlink_health_reporter *reporter, |
101 | struct devlink_fmsg *fmsg, |
102 | struct netlink_ext_ack *extack) |
103 | { |
104 | struct bnxt *bp = devlink_health_reporter_priv(reporter); |
105 | struct bnxt_fw_health *h = bp->fw_health; |
106 | u32 fw_status, fw_resets; |
107 | |
108 | if (test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)) { |
109 | devlink_fmsg_string_pair_put(fmsg, name: "Status" , value: "recovering" ); |
110 | return 0; |
111 | } |
112 | |
113 | if (!h->status_reliable) { |
114 | devlink_fmsg_string_pair_put(fmsg, name: "Status" , value: "unknown" ); |
115 | return 0; |
116 | } |
117 | |
118 | mutex_lock(&h->lock); |
119 | fw_status = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG); |
120 | if (BNXT_FW_IS_BOOTING(fw_status)) { |
121 | devlink_fmsg_string_pair_put(fmsg, name: "Status" , value: "initializing" ); |
122 | } else if (h->severity || fw_status != BNXT_FW_STATUS_HEALTHY) { |
123 | if (!h->severity) { |
124 | h->severity = SEVERITY_FATAL; |
125 | h->remedy = REMEDY_POWER_CYCLE_DEVICE; |
126 | h->diagnoses++; |
127 | devlink_health_report(reporter: h->fw_reporter, |
128 | msg: "FW error diagnosed" , priv_ctx: h); |
129 | } |
130 | devlink_fmsg_string_pair_put(fmsg, name: "Status" , value: "error" ); |
131 | devlink_fmsg_u32_pair_put(fmsg, name: "Syndrome" , value: fw_status); |
132 | } else { |
133 | devlink_fmsg_string_pair_put(fmsg, name: "Status" , value: "healthy" ); |
134 | } |
135 | |
136 | devlink_fmsg_string_pair_put(fmsg, name: "Severity" , |
137 | value: bnxt_health_severity_str(severity: h->severity)); |
138 | |
139 | if (h->severity) { |
140 | devlink_fmsg_string_pair_put(fmsg, name: "Remedy" , |
141 | value: bnxt_health_remedy_str(remedy: h->remedy)); |
142 | if (h->remedy == REMEDY_DEVLINK_RECOVER) |
143 | devlink_fmsg_string_pair_put(fmsg, name: "Impact" , |
144 | value: "traffic+ntuple_cfg" ); |
145 | } |
146 | |
147 | mutex_unlock(lock: &h->lock); |
148 | if (!h->resets_reliable) |
149 | return 0; |
150 | |
151 | fw_resets = bnxt_fw_health_readl(bp, BNXT_FW_RESET_CNT_REG); |
152 | devlink_fmsg_u32_pair_put(fmsg, name: "Resets" , value: fw_resets); |
153 | devlink_fmsg_u32_pair_put(fmsg, name: "Arrests" , value: h->arrests); |
154 | devlink_fmsg_u32_pair_put(fmsg, name: "Survivals" , value: h->survivals); |
155 | devlink_fmsg_u32_pair_put(fmsg, name: "Discoveries" , value: h->discoveries); |
156 | devlink_fmsg_u32_pair_put(fmsg, name: "Fatalities" , value: h->fatalities); |
157 | devlink_fmsg_u32_pair_put(fmsg, name: "Diagnoses" , value: h->diagnoses); |
158 | return 0; |
159 | } |
160 | |
161 | static int bnxt_fw_dump(struct devlink_health_reporter *reporter, |
162 | struct devlink_fmsg *fmsg, void *priv_ctx, |
163 | struct netlink_ext_ack *extack) |
164 | { |
165 | struct bnxt *bp = devlink_health_reporter_priv(reporter); |
166 | u32 dump_len; |
167 | void *data; |
168 | int rc; |
169 | |
170 | /* TODO: no firmware dump support in devlink_health_report() context */ |
171 | if (priv_ctx) |
172 | return -EOPNOTSUPP; |
173 | |
174 | dump_len = bnxt_get_coredump_length(bp, BNXT_DUMP_LIVE); |
175 | if (!dump_len) |
176 | return -EIO; |
177 | |
178 | data = vmalloc(size: dump_len); |
179 | if (!data) |
180 | return -ENOMEM; |
181 | |
182 | rc = bnxt_get_coredump(bp, BNXT_DUMP_LIVE, buf: data, dump_len: &dump_len); |
183 | if (!rc) { |
184 | devlink_fmsg_pair_nest_start(fmsg, name: "core" ); |
185 | devlink_fmsg_binary_pair_put(fmsg, name: "data" , value: data, value_len: dump_len); |
186 | devlink_fmsg_u32_pair_put(fmsg, name: "size" , value: dump_len); |
187 | devlink_fmsg_pair_nest_end(fmsg); |
188 | } |
189 | |
190 | vfree(addr: data); |
191 | return rc; |
192 | } |
193 | |
194 | static int bnxt_fw_recover(struct devlink_health_reporter *reporter, |
195 | void *priv_ctx, |
196 | struct netlink_ext_ack *extack) |
197 | { |
198 | struct bnxt *bp = devlink_health_reporter_priv(reporter); |
199 | |
200 | if (bp->fw_health->severity == SEVERITY_FATAL) |
201 | return -ENODEV; |
202 | |
203 | set_bit(BNXT_STATE_RECOVER, addr: &bp->state); |
204 | __bnxt_fw_recover(bp); |
205 | |
206 | return -EINPROGRESS; |
207 | } |
208 | |
209 | static const struct devlink_health_reporter_ops bnxt_dl_fw_reporter_ops = { |
210 | .name = "fw" , |
211 | .diagnose = bnxt_fw_diagnose, |
212 | .dump = bnxt_fw_dump, |
213 | .recover = bnxt_fw_recover, |
214 | }; |
215 | |
216 | static struct devlink_health_reporter * |
217 | __bnxt_dl_reporter_create(struct bnxt *bp, |
218 | const struct devlink_health_reporter_ops *ops) |
219 | { |
220 | struct devlink_health_reporter *reporter; |
221 | |
222 | reporter = devlink_health_reporter_create(devlink: bp->dl, ops, graceful_period: 0, priv: bp); |
223 | if (IS_ERR(ptr: reporter)) { |
224 | netdev_warn(dev: bp->dev, format: "Failed to create %s health reporter, rc = %ld\n" , |
225 | ops->name, PTR_ERR(ptr: reporter)); |
226 | return NULL; |
227 | } |
228 | |
229 | return reporter; |
230 | } |
231 | |
232 | void bnxt_dl_fw_reporters_create(struct bnxt *bp) |
233 | { |
234 | struct bnxt_fw_health *fw_health = bp->fw_health; |
235 | |
236 | if (fw_health && !fw_health->fw_reporter) |
237 | fw_health->fw_reporter = __bnxt_dl_reporter_create(bp, ops: &bnxt_dl_fw_reporter_ops); |
238 | } |
239 | |
240 | void bnxt_dl_fw_reporters_destroy(struct bnxt *bp) |
241 | { |
242 | struct bnxt_fw_health *fw_health = bp->fw_health; |
243 | |
244 | if (fw_health && fw_health->fw_reporter) { |
245 | devlink_health_reporter_destroy(reporter: fw_health->fw_reporter); |
246 | fw_health->fw_reporter = NULL; |
247 | } |
248 | } |
249 | |
250 | void bnxt_devlink_health_fw_report(struct bnxt *bp) |
251 | { |
252 | struct bnxt_fw_health *fw_health = bp->fw_health; |
253 | int rc; |
254 | |
255 | if (!fw_health) |
256 | return; |
257 | |
258 | if (!fw_health->fw_reporter) { |
259 | __bnxt_fw_recover(bp); |
260 | return; |
261 | } |
262 | |
263 | mutex_lock(&fw_health->lock); |
264 | fw_health->severity = SEVERITY_RECOVERABLE; |
265 | fw_health->remedy = REMEDY_DEVLINK_RECOVER; |
266 | mutex_unlock(lock: &fw_health->lock); |
267 | rc = devlink_health_report(reporter: fw_health->fw_reporter, msg: "FW error reported" , |
268 | priv_ctx: fw_health); |
269 | if (rc == -ECANCELED) |
270 | __bnxt_fw_recover(bp); |
271 | } |
272 | |
273 | void bnxt_dl_health_fw_status_update(struct bnxt *bp, bool healthy) |
274 | { |
275 | struct bnxt_fw_health *fw_health = bp->fw_health; |
276 | u8 state; |
277 | |
278 | mutex_lock(&fw_health->lock); |
279 | if (healthy) { |
280 | fw_health->severity = SEVERITY_NORMAL; |
281 | state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY; |
282 | } else { |
283 | fw_health->severity = SEVERITY_FATAL; |
284 | fw_health->remedy = REMEDY_POWER_CYCLE_DEVICE; |
285 | state = DEVLINK_HEALTH_REPORTER_STATE_ERROR; |
286 | } |
287 | mutex_unlock(lock: &fw_health->lock); |
288 | devlink_health_reporter_state_update(reporter: fw_health->fw_reporter, state); |
289 | } |
290 | |
291 | void bnxt_dl_health_fw_recovery_done(struct bnxt *bp) |
292 | { |
293 | struct bnxt_dl *dl = devlink_priv(devlink: bp->dl); |
294 | |
295 | devlink_health_reporter_recovery_done(reporter: bp->fw_health->fw_reporter); |
296 | bnxt_hwrm_remote_dev_reset_set(bp, remote_reset: dl->remote_reset); |
297 | } |
298 | |
299 | static int bnxt_dl_info_get(struct devlink *dl, struct devlink_info_req *req, |
300 | struct netlink_ext_ack *extack); |
301 | |
302 | static void |
303 | bnxt_dl_livepatch_report_err(struct bnxt *bp, struct netlink_ext_ack *extack, |
304 | struct hwrm_fw_livepatch_output *resp) |
305 | { |
306 | int err = ((struct hwrm_err_output *)resp)->cmd_err; |
307 | |
308 | switch (err) { |
309 | case FW_LIVEPATCH_CMD_ERR_CODE_INVALID_OPCODE: |
310 | netdev_err(dev: bp->dev, format: "Illegal live patch opcode" ); |
311 | NL_SET_ERR_MSG_MOD(extack, "Invalid opcode" ); |
312 | break; |
313 | case FW_LIVEPATCH_CMD_ERR_CODE_NOT_SUPPORTED: |
314 | NL_SET_ERR_MSG_MOD(extack, "Live patch operation not supported" ); |
315 | break; |
316 | case FW_LIVEPATCH_CMD_ERR_CODE_NOT_INSTALLED: |
317 | NL_SET_ERR_MSG_MOD(extack, "Live patch not found" ); |
318 | break; |
319 | case FW_LIVEPATCH_CMD_ERR_CODE_NOT_PATCHED: |
320 | NL_SET_ERR_MSG_MOD(extack, |
321 | "Live patch deactivation failed. Firmware not patched." ); |
322 | break; |
323 | case FW_LIVEPATCH_CMD_ERR_CODE_AUTH_FAIL: |
324 | NL_SET_ERR_MSG_MOD(extack, "Live patch not authenticated" ); |
325 | break; |
326 | case FW_LIVEPATCH_CMD_ERR_CODE_INVALID_HEADER: |
327 | NL_SET_ERR_MSG_MOD(extack, "Incompatible live patch" ); |
328 | break; |
329 | case FW_LIVEPATCH_CMD_ERR_CODE_INVALID_SIZE: |
330 | NL_SET_ERR_MSG_MOD(extack, "Live patch has invalid size" ); |
331 | break; |
332 | case FW_LIVEPATCH_CMD_ERR_CODE_ALREADY_PATCHED: |
333 | NL_SET_ERR_MSG_MOD(extack, "Live patch already applied" ); |
334 | break; |
335 | default: |
336 | netdev_err(dev: bp->dev, format: "Unexpected live patch error: %d\n" , err); |
337 | NL_SET_ERR_MSG_MOD(extack, "Failed to activate live patch" ); |
338 | break; |
339 | } |
340 | } |
341 | |
342 | /* Live patch status in NVM */ |
343 | #define BNXT_LIVEPATCH_NOT_INSTALLED 0 |
344 | #define BNXT_LIVEPATCH_INSTALLED FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_INSTALL |
345 | #define BNXT_LIVEPATCH_REMOVED FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_ACTIVE |
346 | #define BNXT_LIVEPATCH_MASK (FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_INSTALL | \ |
347 | FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_ACTIVE) |
348 | #define BNXT_LIVEPATCH_ACTIVATED BNXT_LIVEPATCH_MASK |
349 | |
350 | #define BNXT_LIVEPATCH_STATE(flags) ((flags) & BNXT_LIVEPATCH_MASK) |
351 | |
352 | static int |
353 | bnxt_dl_livepatch_activate(struct bnxt *bp, struct netlink_ext_ack *extack) |
354 | { |
355 | struct hwrm_fw_livepatch_query_output *query_resp; |
356 | struct hwrm_fw_livepatch_query_input *query_req; |
357 | struct hwrm_fw_livepatch_output *patch_resp; |
358 | struct hwrm_fw_livepatch_input *patch_req; |
359 | u16 flags, live_patch_state; |
360 | bool activated = false; |
361 | u32 installed = 0; |
362 | u8 target; |
363 | int rc; |
364 | |
365 | if (~bp->fw_cap & BNXT_FW_CAP_LIVEPATCH) { |
366 | NL_SET_ERR_MSG_MOD(extack, "Device does not support live patch" ); |
367 | return -EOPNOTSUPP; |
368 | } |
369 | |
370 | rc = hwrm_req_init(bp, query_req, HWRM_FW_LIVEPATCH_QUERY); |
371 | if (rc) |
372 | return rc; |
373 | query_resp = hwrm_req_hold(bp, req: query_req); |
374 | |
375 | rc = hwrm_req_init(bp, patch_req, HWRM_FW_LIVEPATCH); |
376 | if (rc) { |
377 | hwrm_req_drop(bp, req: query_req); |
378 | return rc; |
379 | } |
380 | patch_req->loadtype = FW_LIVEPATCH_REQ_LOADTYPE_NVM_INSTALL; |
381 | patch_resp = hwrm_req_hold(bp, req: patch_req); |
382 | |
383 | for (target = 1; target <= FW_LIVEPATCH_REQ_FW_TARGET_LAST; target++) { |
384 | query_req->fw_target = target; |
385 | rc = hwrm_req_send(bp, req: query_req); |
386 | if (rc) { |
387 | NL_SET_ERR_MSG_MOD(extack, "Failed to query packages" ); |
388 | break; |
389 | } |
390 | |
391 | flags = le16_to_cpu(query_resp->status_flags); |
392 | live_patch_state = BNXT_LIVEPATCH_STATE(flags); |
393 | |
394 | if (live_patch_state == BNXT_LIVEPATCH_NOT_INSTALLED) |
395 | continue; |
396 | |
397 | if (live_patch_state == BNXT_LIVEPATCH_ACTIVATED) { |
398 | activated = true; |
399 | continue; |
400 | } |
401 | |
402 | if (live_patch_state == BNXT_LIVEPATCH_INSTALLED) |
403 | patch_req->opcode = FW_LIVEPATCH_REQ_OPCODE_ACTIVATE; |
404 | else if (live_patch_state == BNXT_LIVEPATCH_REMOVED) |
405 | patch_req->opcode = FW_LIVEPATCH_REQ_OPCODE_DEACTIVATE; |
406 | |
407 | patch_req->fw_target = target; |
408 | rc = hwrm_req_send(bp, req: patch_req); |
409 | if (rc) { |
410 | bnxt_dl_livepatch_report_err(bp, extack, resp: patch_resp); |
411 | break; |
412 | } |
413 | installed++; |
414 | } |
415 | |
416 | if (!rc && !installed) { |
417 | if (activated) { |
418 | NL_SET_ERR_MSG_MOD(extack, "Live patch already activated" ); |
419 | rc = -EEXIST; |
420 | } else { |
421 | NL_SET_ERR_MSG_MOD(extack, "No live patches found" ); |
422 | rc = -ENOENT; |
423 | } |
424 | } |
425 | hwrm_req_drop(bp, req: query_req); |
426 | hwrm_req_drop(bp, req: patch_req); |
427 | return rc; |
428 | } |
429 | |
430 | static int bnxt_dl_reload_down(struct devlink *dl, bool netns_change, |
431 | enum devlink_reload_action action, |
432 | enum devlink_reload_limit limit, |
433 | struct netlink_ext_ack *extack) |
434 | { |
435 | struct bnxt *bp = bnxt_get_bp_from_dl(dl); |
436 | int rc = 0; |
437 | |
438 | switch (action) { |
439 | case DEVLINK_RELOAD_ACTION_DRIVER_REINIT: { |
440 | rtnl_lock(); |
441 | if (bnxt_sriov_cfg(bp)) { |
442 | NL_SET_ERR_MSG_MOD(extack, |
443 | "reload is unsupported while VFs are allocated or being configured" ); |
444 | rtnl_unlock(); |
445 | return -EOPNOTSUPP; |
446 | } |
447 | if (bp->dev->reg_state == NETREG_UNREGISTERED) { |
448 | rtnl_unlock(); |
449 | return -ENODEV; |
450 | } |
451 | bnxt_ulp_stop(bp); |
452 | if (netif_running(dev: bp->dev)) |
453 | bnxt_close_nic(bp, true, true); |
454 | bnxt_vf_reps_free(bp); |
455 | rc = bnxt_hwrm_func_drv_unrgtr(bp); |
456 | if (rc) { |
457 | NL_SET_ERR_MSG_MOD(extack, "Failed to deregister" ); |
458 | if (netif_running(dev: bp->dev)) |
459 | dev_close(dev: bp->dev); |
460 | rtnl_unlock(); |
461 | break; |
462 | } |
463 | bnxt_cancel_reservations(bp, fw_reset: false); |
464 | bnxt_free_ctx_mem(bp); |
465 | break; |
466 | } |
467 | case DEVLINK_RELOAD_ACTION_FW_ACTIVATE: { |
468 | if (limit == DEVLINK_RELOAD_LIMIT_NO_RESET) |
469 | return bnxt_dl_livepatch_activate(bp, extack); |
470 | if (~bp->fw_cap & BNXT_FW_CAP_HOT_RESET) { |
471 | NL_SET_ERR_MSG_MOD(extack, "Device not capable, requires reboot" ); |
472 | return -EOPNOTSUPP; |
473 | } |
474 | if (!bnxt_hwrm_reset_permitted(bp)) { |
475 | NL_SET_ERR_MSG_MOD(extack, |
476 | "Reset denied by firmware, it may be inhibited by remote driver" ); |
477 | return -EPERM; |
478 | } |
479 | rtnl_lock(); |
480 | if (bp->dev->reg_state == NETREG_UNREGISTERED) { |
481 | rtnl_unlock(); |
482 | return -ENODEV; |
483 | } |
484 | if (netif_running(dev: bp->dev)) |
485 | set_bit(BNXT_STATE_FW_ACTIVATE, addr: &bp->state); |
486 | rc = bnxt_hwrm_firmware_reset(dev: bp->dev, |
487 | FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIP, |
488 | FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP, |
489 | FW_RESET_REQ_FLAGS_RESET_GRACEFUL | |
490 | FW_RESET_REQ_FLAGS_FW_ACTIVATION); |
491 | if (rc) { |
492 | NL_SET_ERR_MSG_MOD(extack, "Failed to activate firmware" ); |
493 | clear_bit(BNXT_STATE_FW_ACTIVATE, addr: &bp->state); |
494 | rtnl_unlock(); |
495 | } |
496 | break; |
497 | } |
498 | default: |
499 | rc = -EOPNOTSUPP; |
500 | } |
501 | |
502 | return rc; |
503 | } |
504 | |
505 | static int bnxt_dl_reload_up(struct devlink *dl, enum devlink_reload_action action, |
506 | enum devlink_reload_limit limit, u32 *actions_performed, |
507 | struct netlink_ext_ack *extack) |
508 | { |
509 | struct bnxt *bp = bnxt_get_bp_from_dl(dl); |
510 | int rc = 0; |
511 | |
512 | *actions_performed = 0; |
513 | switch (action) { |
514 | case DEVLINK_RELOAD_ACTION_DRIVER_REINIT: { |
515 | bnxt_fw_init_one(bp); |
516 | bnxt_vf_reps_alloc(bp); |
517 | if (netif_running(dev: bp->dev)) |
518 | rc = bnxt_open_nic(bp, true, true); |
519 | bnxt_ulp_start(bp, err: rc); |
520 | if (!rc) { |
521 | bnxt_reenable_sriov(bp); |
522 | bnxt_ptp_reapply_pps(bp); |
523 | } |
524 | break; |
525 | } |
526 | case DEVLINK_RELOAD_ACTION_FW_ACTIVATE: { |
527 | unsigned long start = jiffies; |
528 | unsigned long timeout = start + BNXT_DFLT_FW_RST_MAX_DSECS * HZ / 10; |
529 | |
530 | if (limit == DEVLINK_RELOAD_LIMIT_NO_RESET) |
531 | break; |
532 | if (bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY) |
533 | timeout = start + bp->fw_health->normal_func_wait_dsecs * HZ / 10; |
534 | if (!netif_running(dev: bp->dev)) |
535 | NL_SET_ERR_MSG_MOD(extack, |
536 | "Device is closed, not waiting for reset notice that will never come" ); |
537 | rtnl_unlock(); |
538 | while (test_bit(BNXT_STATE_FW_ACTIVATE, &bp->state)) { |
539 | if (time_after(jiffies, timeout)) { |
540 | NL_SET_ERR_MSG_MOD(extack, "Activation incomplete" ); |
541 | rc = -ETIMEDOUT; |
542 | break; |
543 | } |
544 | if (test_bit(BNXT_STATE_ABORT_ERR, &bp->state)) { |
545 | NL_SET_ERR_MSG_MOD(extack, "Activation aborted" ); |
546 | rc = -ENODEV; |
547 | break; |
548 | } |
549 | msleep(msecs: 50); |
550 | } |
551 | rtnl_lock(); |
552 | if (!rc) |
553 | *actions_performed |= BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT); |
554 | clear_bit(BNXT_STATE_FW_ACTIVATE, addr: &bp->state); |
555 | break; |
556 | } |
557 | default: |
558 | return -EOPNOTSUPP; |
559 | } |
560 | |
561 | if (!rc) { |
562 | bnxt_print_device_info(bp); |
563 | if (netif_running(dev: bp->dev)) { |
564 | mutex_lock(&bp->link_lock); |
565 | bnxt_report_link(bp); |
566 | mutex_unlock(lock: &bp->link_lock); |
567 | } |
568 | *actions_performed |= BIT(action); |
569 | } else if (netif_running(dev: bp->dev)) { |
570 | dev_close(dev: bp->dev); |
571 | } |
572 | rtnl_unlock(); |
573 | return rc; |
574 | } |
575 | |
576 | static bool bnxt_nvm_test(struct bnxt *bp, struct netlink_ext_ack *extack) |
577 | { |
578 | bool rc = false; |
579 | u32 datalen; |
580 | u16 index; |
581 | u8 *buf; |
582 | |
583 | if (bnxt_find_nvram_item(dev: bp->dev, type: BNX_DIR_TYPE_VPD, |
584 | BNX_DIR_ORDINAL_FIRST, BNX_DIR_EXT_NONE, |
585 | index: &index, NULL, data_length: &datalen) || !datalen) { |
586 | NL_SET_ERR_MSG_MOD(extack, "nvm test vpd entry error" ); |
587 | return false; |
588 | } |
589 | |
590 | buf = kzalloc(size: datalen, GFP_KERNEL); |
591 | if (!buf) { |
592 | NL_SET_ERR_MSG_MOD(extack, "insufficient memory for nvm test" ); |
593 | return false; |
594 | } |
595 | |
596 | if (bnxt_get_nvram_item(dev: bp->dev, index, offset: 0, length: datalen, data: buf)) { |
597 | NL_SET_ERR_MSG_MOD(extack, "nvm test vpd read error" ); |
598 | goto done; |
599 | } |
600 | |
601 | if (bnxt_flash_nvram(dev: bp->dev, dir_type: BNX_DIR_TYPE_VPD, BNX_DIR_ORDINAL_FIRST, |
602 | BNX_DIR_EXT_NONE, dir_attr: 0, dir_item_len: 0, data: buf, data_len: datalen)) { |
603 | NL_SET_ERR_MSG_MOD(extack, "nvm test vpd write error" ); |
604 | goto done; |
605 | } |
606 | |
607 | rc = true; |
608 | |
609 | done: |
610 | kfree(objp: buf); |
611 | return rc; |
612 | } |
613 | |
614 | static bool bnxt_dl_selftest_check(struct devlink *dl, unsigned int id, |
615 | struct netlink_ext_ack *extack) |
616 | { |
617 | return id == DEVLINK_ATTR_SELFTEST_ID_FLASH; |
618 | } |
619 | |
620 | static enum devlink_selftest_status bnxt_dl_selftest_run(struct devlink *dl, |
621 | unsigned int id, |
622 | struct netlink_ext_ack *extack) |
623 | { |
624 | struct bnxt *bp = bnxt_get_bp_from_dl(dl); |
625 | |
626 | if (id == DEVLINK_ATTR_SELFTEST_ID_FLASH) |
627 | return bnxt_nvm_test(bp, extack) ? |
628 | DEVLINK_SELFTEST_STATUS_PASS : |
629 | DEVLINK_SELFTEST_STATUS_FAIL; |
630 | |
631 | return DEVLINK_SELFTEST_STATUS_SKIP; |
632 | } |
633 | |
634 | static const struct devlink_ops bnxt_dl_ops = { |
635 | #ifdef CONFIG_BNXT_SRIOV |
636 | .eswitch_mode_set = bnxt_dl_eswitch_mode_set, |
637 | .eswitch_mode_get = bnxt_dl_eswitch_mode_get, |
638 | #endif /* CONFIG_BNXT_SRIOV */ |
639 | .info_get = bnxt_dl_info_get, |
640 | .flash_update = bnxt_dl_flash_update, |
641 | .reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) | |
642 | BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE), |
643 | .reload_limits = BIT(DEVLINK_RELOAD_LIMIT_NO_RESET), |
644 | .reload_down = bnxt_dl_reload_down, |
645 | .reload_up = bnxt_dl_reload_up, |
646 | .selftest_check = bnxt_dl_selftest_check, |
647 | .selftest_run = bnxt_dl_selftest_run, |
648 | }; |
649 | |
650 | static const struct devlink_ops bnxt_vf_dl_ops; |
651 | |
652 | enum bnxt_dl_param_id { |
653 | BNXT_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, |
654 | BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK, |
655 | }; |
656 | |
657 | static const struct bnxt_dl_nvm_param nvm_params[] = { |
658 | {DEVLINK_PARAM_GENERIC_ID_ENABLE_SRIOV, NVM_OFF_ENABLE_SRIOV, |
659 | BNXT_NVM_SHARED_CFG, 1, 1}, |
660 | {DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI, NVM_OFF_IGNORE_ARI, |
661 | BNXT_NVM_SHARED_CFG, 1, 1}, |
662 | {DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX, |
663 | NVM_OFF_MSIX_VEC_PER_PF_MAX, BNXT_NVM_SHARED_CFG, 10, 4}, |
664 | {DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN, |
665 | NVM_OFF_MSIX_VEC_PER_PF_MIN, BNXT_NVM_SHARED_CFG, 7, 4}, |
666 | {BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK, NVM_OFF_DIS_GRE_VER_CHECK, |
667 | BNXT_NVM_SHARED_CFG, 1, 1}, |
668 | }; |
669 | |
670 | union bnxt_nvm_data { |
671 | u8 val8; |
672 | __le32 val32; |
673 | }; |
674 | |
675 | static void bnxt_copy_to_nvm_data(union bnxt_nvm_data *dst, |
676 | union devlink_param_value *src, |
677 | int nvm_num_bits, int dl_num_bytes) |
678 | { |
679 | u32 val32 = 0; |
680 | |
681 | if (nvm_num_bits == 1) { |
682 | dst->val8 = src->vbool; |
683 | return; |
684 | } |
685 | if (dl_num_bytes == 4) |
686 | val32 = src->vu32; |
687 | else if (dl_num_bytes == 2) |
688 | val32 = (u32)src->vu16; |
689 | else if (dl_num_bytes == 1) |
690 | val32 = (u32)src->vu8; |
691 | dst->val32 = cpu_to_le32(val32); |
692 | } |
693 | |
694 | static void bnxt_copy_from_nvm_data(union devlink_param_value *dst, |
695 | union bnxt_nvm_data *src, |
696 | int nvm_num_bits, int dl_num_bytes) |
697 | { |
698 | u32 val32; |
699 | |
700 | if (nvm_num_bits == 1) { |
701 | dst->vbool = src->val8; |
702 | return; |
703 | } |
704 | val32 = le32_to_cpu(src->val32); |
705 | if (dl_num_bytes == 4) |
706 | dst->vu32 = val32; |
707 | else if (dl_num_bytes == 2) |
708 | dst->vu16 = (u16)val32; |
709 | else if (dl_num_bytes == 1) |
710 | dst->vu8 = (u8)val32; |
711 | } |
712 | |
713 | static int bnxt_hwrm_get_nvm_cfg_ver(struct bnxt *bp, u32 *nvm_cfg_ver) |
714 | { |
715 | struct hwrm_nvm_get_variable_input *req; |
716 | u16 bytes = BNXT_NVM_CFG_VER_BYTES; |
717 | u16 bits = BNXT_NVM_CFG_VER_BITS; |
718 | union devlink_param_value ver; |
719 | union bnxt_nvm_data *data; |
720 | dma_addr_t data_dma_addr; |
721 | int rc, i = 2; |
722 | u16 dim = 1; |
723 | |
724 | rc = hwrm_req_init(bp, req, HWRM_NVM_GET_VARIABLE); |
725 | if (rc) |
726 | return rc; |
727 | |
728 | data = hwrm_req_dma_slice(bp, req, size: sizeof(*data), dma: &data_dma_addr); |
729 | if (!data) { |
730 | rc = -ENOMEM; |
731 | goto exit; |
732 | } |
733 | |
734 | /* earlier devices present as an array of raw bytes */ |
735 | if (!BNXT_CHIP_P5_PLUS(bp)) { |
736 | dim = 0; |
737 | i = 0; |
738 | bits *= 3; /* array of 3 version components */ |
739 | bytes *= 4; /* copy whole word */ |
740 | } |
741 | |
742 | hwrm_req_hold(bp, req); |
743 | req->dest_data_addr = cpu_to_le64(data_dma_addr); |
744 | req->data_len = cpu_to_le16(bits); |
745 | req->option_num = cpu_to_le16(NVM_OFF_NVM_CFG_VER); |
746 | req->dimensions = cpu_to_le16(dim); |
747 | |
748 | while (i >= 0) { |
749 | req->index_0 = cpu_to_le16(i--); |
750 | rc = hwrm_req_send_silent(bp, req); |
751 | if (rc) |
752 | goto exit; |
753 | bnxt_copy_from_nvm_data(dst: &ver, src: data, nvm_num_bits: bits, dl_num_bytes: bytes); |
754 | |
755 | if (BNXT_CHIP_P5_PLUS(bp)) { |
756 | *nvm_cfg_ver <<= 8; |
757 | *nvm_cfg_ver |= ver.vu8; |
758 | } else { |
759 | *nvm_cfg_ver = ver.vu32; |
760 | } |
761 | } |
762 | |
763 | exit: |
764 | hwrm_req_drop(bp, req); |
765 | return rc; |
766 | } |
767 | |
768 | static int bnxt_dl_info_put(struct bnxt *bp, struct devlink_info_req *req, |
769 | enum bnxt_dl_version_type type, const char *key, |
770 | char *buf) |
771 | { |
772 | if (!strlen(buf)) |
773 | return 0; |
774 | |
775 | if ((bp->flags & BNXT_FLAG_CHIP_P5_PLUS) && |
776 | (!strcmp(key, DEVLINK_INFO_VERSION_GENERIC_FW_NCSI) || |
777 | !strcmp(key, DEVLINK_INFO_VERSION_GENERIC_FW_ROCE))) |
778 | return 0; |
779 | |
780 | switch (type) { |
781 | case BNXT_VERSION_FIXED: |
782 | return devlink_info_version_fixed_put(req, version_name: key, version_value: buf); |
783 | case BNXT_VERSION_RUNNING: |
784 | return devlink_info_version_running_put(req, version_name: key, version_value: buf); |
785 | case BNXT_VERSION_STORED: |
786 | return devlink_info_version_stored_put(req, version_name: key, version_value: buf); |
787 | } |
788 | return 0; |
789 | } |
790 | |
791 | #define BNXT_FW_SRT_PATCH "fw.srt.patch" |
792 | #define BNXT_FW_CRT_PATCH "fw.crt.patch" |
793 | |
794 | static int bnxt_dl_livepatch_info_put(struct bnxt *bp, |
795 | struct devlink_info_req *req, |
796 | const char *key) |
797 | { |
798 | struct hwrm_fw_livepatch_query_input *query; |
799 | struct hwrm_fw_livepatch_query_output *resp; |
800 | u16 flags; |
801 | int rc; |
802 | |
803 | if (~bp->fw_cap & BNXT_FW_CAP_LIVEPATCH) |
804 | return 0; |
805 | |
806 | rc = hwrm_req_init(bp, query, HWRM_FW_LIVEPATCH_QUERY); |
807 | if (rc) |
808 | return rc; |
809 | |
810 | if (!strcmp(key, BNXT_FW_SRT_PATCH)) |
811 | query->fw_target = FW_LIVEPATCH_QUERY_REQ_FW_TARGET_SECURE_FW; |
812 | else if (!strcmp(key, BNXT_FW_CRT_PATCH)) |
813 | query->fw_target = FW_LIVEPATCH_QUERY_REQ_FW_TARGET_COMMON_FW; |
814 | else |
815 | goto exit; |
816 | |
817 | resp = hwrm_req_hold(bp, req: query); |
818 | rc = hwrm_req_send(bp, req: query); |
819 | if (rc) |
820 | goto exit; |
821 | |
822 | flags = le16_to_cpu(resp->status_flags); |
823 | if (flags & FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_ACTIVE) { |
824 | resp->active_ver[sizeof(resp->active_ver) - 1] = '\0'; |
825 | rc = devlink_info_version_running_put(req, version_name: key, version_value: resp->active_ver); |
826 | if (rc) |
827 | goto exit; |
828 | } |
829 | |
830 | if (flags & FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_INSTALL) { |
831 | resp->install_ver[sizeof(resp->install_ver) - 1] = '\0'; |
832 | rc = devlink_info_version_stored_put(req, version_name: key, version_value: resp->install_ver); |
833 | if (rc) |
834 | goto exit; |
835 | } |
836 | |
837 | exit: |
838 | hwrm_req_drop(bp, req: query); |
839 | return rc; |
840 | } |
841 | |
842 | #define HWRM_FW_VER_STR_LEN 16 |
843 | |
844 | static int bnxt_dl_info_get(struct devlink *dl, struct devlink_info_req *req, |
845 | struct netlink_ext_ack *extack) |
846 | { |
847 | struct hwrm_nvm_get_dev_info_output nvm_dev_info; |
848 | struct bnxt *bp = bnxt_get_bp_from_dl(dl); |
849 | struct hwrm_ver_get_output *ver_resp; |
850 | char mgmt_ver[FW_VER_STR_LEN]; |
851 | char roce_ver[FW_VER_STR_LEN]; |
852 | char ncsi_ver[FW_VER_STR_LEN]; |
853 | char buf[32]; |
854 | u32 ver = 0; |
855 | int rc; |
856 | |
857 | if (BNXT_PF(bp) && (bp->flags & BNXT_FLAG_DSN_VALID)) { |
858 | sprintf(buf, fmt: "%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X" , |
859 | bp->dsn[7], bp->dsn[6], bp->dsn[5], bp->dsn[4], |
860 | bp->dsn[3], bp->dsn[2], bp->dsn[1], bp->dsn[0]); |
861 | rc = devlink_info_serial_number_put(req, sn: buf); |
862 | if (rc) |
863 | return rc; |
864 | } |
865 | |
866 | if (strlen(bp->board_serialno)) { |
867 | rc = devlink_info_board_serial_number_put(req, bsn: bp->board_serialno); |
868 | if (rc) |
869 | return rc; |
870 | } |
871 | |
872 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_FIXED, |
873 | DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, |
874 | buf: bp->board_partno); |
875 | if (rc) |
876 | return rc; |
877 | |
878 | sprintf(buf, fmt: "%X" , bp->chip_num); |
879 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_FIXED, |
880 | DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, buf); |
881 | if (rc) |
882 | return rc; |
883 | |
884 | ver_resp = &bp->ver_resp; |
885 | sprintf(buf, fmt: "%c%d" , 'A' + ver_resp->chip_rev, ver_resp->chip_metal); |
886 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_FIXED, |
887 | DEVLINK_INFO_VERSION_GENERIC_ASIC_REV, buf); |
888 | if (rc) |
889 | return rc; |
890 | |
891 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_RUNNING, |
892 | DEVLINK_INFO_VERSION_GENERIC_FW_PSID, |
893 | buf: bp->nvm_cfg_ver); |
894 | if (rc) |
895 | return rc; |
896 | |
897 | buf[0] = 0; |
898 | strncat(p: buf, q: ver_resp->active_pkg_name, HWRM_FW_VER_STR_LEN); |
899 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_RUNNING, |
900 | DEVLINK_INFO_VERSION_GENERIC_FW, buf); |
901 | if (rc) |
902 | return rc; |
903 | |
904 | if (BNXT_PF(bp) && !bnxt_hwrm_get_nvm_cfg_ver(bp, nvm_cfg_ver: &ver)) { |
905 | sprintf(buf, fmt: "%d.%d.%d" , (ver >> 16) & 0xff, (ver >> 8) & 0xff, |
906 | ver & 0xff); |
907 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_STORED, |
908 | DEVLINK_INFO_VERSION_GENERIC_FW_PSID, |
909 | buf); |
910 | if (rc) |
911 | return rc; |
912 | } |
913 | |
914 | if (ver_resp->flags & VER_GET_RESP_FLAGS_EXT_VER_AVAIL) { |
915 | snprintf(buf: mgmt_ver, FW_VER_STR_LEN, fmt: "%d.%d.%d.%d" , |
916 | ver_resp->hwrm_fw_major, ver_resp->hwrm_fw_minor, |
917 | ver_resp->hwrm_fw_build, ver_resp->hwrm_fw_patch); |
918 | |
919 | snprintf(buf: ncsi_ver, FW_VER_STR_LEN, fmt: "%d.%d.%d.%d" , |
920 | ver_resp->mgmt_fw_major, ver_resp->mgmt_fw_minor, |
921 | ver_resp->mgmt_fw_build, ver_resp->mgmt_fw_patch); |
922 | |
923 | snprintf(buf: roce_ver, FW_VER_STR_LEN, fmt: "%d.%d.%d.%d" , |
924 | ver_resp->roce_fw_major, ver_resp->roce_fw_minor, |
925 | ver_resp->roce_fw_build, ver_resp->roce_fw_patch); |
926 | } else { |
927 | snprintf(buf: mgmt_ver, FW_VER_STR_LEN, fmt: "%d.%d.%d.%d" , |
928 | ver_resp->hwrm_fw_maj_8b, ver_resp->hwrm_fw_min_8b, |
929 | ver_resp->hwrm_fw_bld_8b, ver_resp->hwrm_fw_rsvd_8b); |
930 | |
931 | snprintf(buf: ncsi_ver, FW_VER_STR_LEN, fmt: "%d.%d.%d.%d" , |
932 | ver_resp->mgmt_fw_maj_8b, ver_resp->mgmt_fw_min_8b, |
933 | ver_resp->mgmt_fw_bld_8b, ver_resp->mgmt_fw_rsvd_8b); |
934 | |
935 | snprintf(buf: roce_ver, FW_VER_STR_LEN, fmt: "%d.%d.%d.%d" , |
936 | ver_resp->roce_fw_maj_8b, ver_resp->roce_fw_min_8b, |
937 | ver_resp->roce_fw_bld_8b, ver_resp->roce_fw_rsvd_8b); |
938 | } |
939 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_RUNNING, |
940 | DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf: mgmt_ver); |
941 | if (rc) |
942 | return rc; |
943 | |
944 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_RUNNING, |
945 | DEVLINK_INFO_VERSION_GENERIC_FW_MGMT_API, |
946 | buf: bp->hwrm_ver_supp); |
947 | if (rc) |
948 | return rc; |
949 | |
950 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_RUNNING, |
951 | DEVLINK_INFO_VERSION_GENERIC_FW_NCSI, buf: ncsi_ver); |
952 | if (rc) |
953 | return rc; |
954 | |
955 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_RUNNING, |
956 | DEVLINK_INFO_VERSION_GENERIC_FW_ROCE, buf: roce_ver); |
957 | if (rc) |
958 | return rc; |
959 | |
960 | rc = bnxt_hwrm_nvm_get_dev_info(bp, nvm_dev_info: &nvm_dev_info); |
961 | if (rc || |
962 | !(nvm_dev_info.flags & NVM_GET_DEV_INFO_RESP_FLAGS_FW_VER_VALID)) { |
963 | if (!bnxt_get_pkginfo(dev: bp->dev, ver: buf, size: sizeof(buf))) |
964 | return bnxt_dl_info_put(bp, req, type: BNXT_VERSION_STORED, |
965 | DEVLINK_INFO_VERSION_GENERIC_FW, |
966 | buf); |
967 | return 0; |
968 | } |
969 | |
970 | buf[0] = 0; |
971 | strncat(p: buf, q: nvm_dev_info.pkg_name, HWRM_FW_VER_STR_LEN); |
972 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_STORED, |
973 | DEVLINK_INFO_VERSION_GENERIC_FW, buf); |
974 | if (rc) |
975 | return rc; |
976 | |
977 | snprintf(buf: mgmt_ver, FW_VER_STR_LEN, fmt: "%d.%d.%d.%d" , |
978 | nvm_dev_info.hwrm_fw_major, nvm_dev_info.hwrm_fw_minor, |
979 | nvm_dev_info.hwrm_fw_build, nvm_dev_info.hwrm_fw_patch); |
980 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_STORED, |
981 | DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf: mgmt_ver); |
982 | if (rc) |
983 | return rc; |
984 | |
985 | snprintf(buf: ncsi_ver, FW_VER_STR_LEN, fmt: "%d.%d.%d.%d" , |
986 | nvm_dev_info.mgmt_fw_major, nvm_dev_info.mgmt_fw_minor, |
987 | nvm_dev_info.mgmt_fw_build, nvm_dev_info.mgmt_fw_patch); |
988 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_STORED, |
989 | DEVLINK_INFO_VERSION_GENERIC_FW_NCSI, buf: ncsi_ver); |
990 | if (rc) |
991 | return rc; |
992 | |
993 | snprintf(buf: roce_ver, FW_VER_STR_LEN, fmt: "%d.%d.%d.%d" , |
994 | nvm_dev_info.roce_fw_major, nvm_dev_info.roce_fw_minor, |
995 | nvm_dev_info.roce_fw_build, nvm_dev_info.roce_fw_patch); |
996 | rc = bnxt_dl_info_put(bp, req, type: BNXT_VERSION_STORED, |
997 | DEVLINK_INFO_VERSION_GENERIC_FW_ROCE, buf: roce_ver); |
998 | if (rc) |
999 | return rc; |
1000 | |
1001 | if (BNXT_CHIP_P5_PLUS(bp)) { |
1002 | rc = bnxt_dl_livepatch_info_put(bp, req, BNXT_FW_SRT_PATCH); |
1003 | if (rc) |
1004 | return rc; |
1005 | } |
1006 | return bnxt_dl_livepatch_info_put(bp, req, BNXT_FW_CRT_PATCH); |
1007 | |
1008 | } |
1009 | |
1010 | static int bnxt_hwrm_nvm_req(struct bnxt *bp, u32 param_id, void *msg, |
1011 | union devlink_param_value *val) |
1012 | { |
1013 | struct hwrm_nvm_get_variable_input *req = msg; |
1014 | struct bnxt_dl_nvm_param nvm_param; |
1015 | struct hwrm_err_output *resp; |
1016 | union bnxt_nvm_data *data; |
1017 | dma_addr_t data_dma_addr; |
1018 | int idx = 0, rc, i; |
1019 | |
1020 | /* Get/Set NVM CFG parameter is supported only on PFs */ |
1021 | if (BNXT_VF(bp)) { |
1022 | hwrm_req_drop(bp, req); |
1023 | return -EPERM; |
1024 | } |
1025 | |
1026 | for (i = 0; i < ARRAY_SIZE(nvm_params); i++) { |
1027 | if (nvm_params[i].id == param_id) { |
1028 | nvm_param = nvm_params[i]; |
1029 | break; |
1030 | } |
1031 | } |
1032 | |
1033 | if (i == ARRAY_SIZE(nvm_params)) { |
1034 | hwrm_req_drop(bp, req); |
1035 | return -EOPNOTSUPP; |
1036 | } |
1037 | |
1038 | if (nvm_param.dir_type == BNXT_NVM_PORT_CFG) |
1039 | idx = bp->pf.port_id; |
1040 | else if (nvm_param.dir_type == BNXT_NVM_FUNC_CFG) |
1041 | idx = bp->pf.fw_fid - BNXT_FIRST_PF_FID; |
1042 | |
1043 | data = hwrm_req_dma_slice(bp, req, size: sizeof(*data), dma: &data_dma_addr); |
1044 | |
1045 | if (!data) { |
1046 | hwrm_req_drop(bp, req); |
1047 | return -ENOMEM; |
1048 | } |
1049 | |
1050 | req->dest_data_addr = cpu_to_le64(data_dma_addr); |
1051 | req->data_len = cpu_to_le16(nvm_param.nvm_num_bits); |
1052 | req->option_num = cpu_to_le16(nvm_param.offset); |
1053 | req->index_0 = cpu_to_le16(idx); |
1054 | if (idx) |
1055 | req->dimensions = cpu_to_le16(1); |
1056 | |
1057 | resp = hwrm_req_hold(bp, req); |
1058 | if (req->req_type == cpu_to_le16(HWRM_NVM_SET_VARIABLE)) { |
1059 | bnxt_copy_to_nvm_data(dst: data, src: val, nvm_num_bits: nvm_param.nvm_num_bits, |
1060 | dl_num_bytes: nvm_param.dl_num_bytes); |
1061 | rc = hwrm_req_send(bp, req: msg); |
1062 | } else { |
1063 | rc = hwrm_req_send_silent(bp, req: msg); |
1064 | if (!rc) { |
1065 | bnxt_copy_from_nvm_data(dst: val, src: data, |
1066 | nvm_num_bits: nvm_param.nvm_num_bits, |
1067 | dl_num_bytes: nvm_param.dl_num_bytes); |
1068 | } else { |
1069 | if (resp->cmd_err == |
1070 | NVM_GET_VARIABLE_CMD_ERR_CODE_VAR_NOT_EXIST) |
1071 | rc = -EOPNOTSUPP; |
1072 | } |
1073 | } |
1074 | hwrm_req_drop(bp, req); |
1075 | if (rc == -EACCES) |
1076 | netdev_err(dev: bp->dev, format: "PF does not have admin privileges to modify NVM config\n" ); |
1077 | return rc; |
1078 | } |
1079 | |
1080 | static int bnxt_dl_nvm_param_get(struct devlink *dl, u32 id, |
1081 | struct devlink_param_gset_ctx *ctx) |
1082 | { |
1083 | struct bnxt *bp = bnxt_get_bp_from_dl(dl); |
1084 | struct hwrm_nvm_get_variable_input *req; |
1085 | int rc; |
1086 | |
1087 | rc = hwrm_req_init(bp, req, HWRM_NVM_GET_VARIABLE); |
1088 | if (rc) |
1089 | return rc; |
1090 | |
1091 | rc = bnxt_hwrm_nvm_req(bp, param_id: id, msg: req, val: &ctx->val); |
1092 | if (!rc && id == BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK) |
1093 | ctx->val.vbool = !ctx->val.vbool; |
1094 | |
1095 | return rc; |
1096 | } |
1097 | |
1098 | static int bnxt_dl_nvm_param_set(struct devlink *dl, u32 id, |
1099 | struct devlink_param_gset_ctx *ctx) |
1100 | { |
1101 | struct bnxt *bp = bnxt_get_bp_from_dl(dl); |
1102 | struct hwrm_nvm_set_variable_input *req; |
1103 | int rc; |
1104 | |
1105 | rc = hwrm_req_init(bp, req, HWRM_NVM_SET_VARIABLE); |
1106 | if (rc) |
1107 | return rc; |
1108 | |
1109 | if (id == BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK) |
1110 | ctx->val.vbool = !ctx->val.vbool; |
1111 | |
1112 | return bnxt_hwrm_nvm_req(bp, param_id: id, msg: req, val: &ctx->val); |
1113 | } |
1114 | |
1115 | static int bnxt_dl_msix_validate(struct devlink *dl, u32 id, |
1116 | union devlink_param_value val, |
1117 | struct netlink_ext_ack *extack) |
1118 | { |
1119 | int max_val = -1; |
1120 | |
1121 | if (id == DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX) |
1122 | max_val = BNXT_MSIX_VEC_MAX; |
1123 | |
1124 | if (id == DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN) |
1125 | max_val = BNXT_MSIX_VEC_MIN_MAX; |
1126 | |
1127 | if (val.vu32 > max_val) { |
1128 | NL_SET_ERR_MSG_MOD(extack, "MSIX value is exceeding the range" ); |
1129 | return -EINVAL; |
1130 | } |
1131 | |
1132 | return 0; |
1133 | } |
1134 | |
1135 | static int bnxt_remote_dev_reset_get(struct devlink *dl, u32 id, |
1136 | struct devlink_param_gset_ctx *ctx) |
1137 | { |
1138 | struct bnxt *bp = bnxt_get_bp_from_dl(dl); |
1139 | |
1140 | if (~bp->fw_cap & BNXT_FW_CAP_HOT_RESET_IF) |
1141 | return -EOPNOTSUPP; |
1142 | |
1143 | ctx->val.vbool = bnxt_dl_get_remote_reset(dl); |
1144 | return 0; |
1145 | } |
1146 | |
1147 | static int bnxt_remote_dev_reset_set(struct devlink *dl, u32 id, |
1148 | struct devlink_param_gset_ctx *ctx) |
1149 | { |
1150 | struct bnxt *bp = bnxt_get_bp_from_dl(dl); |
1151 | int rc; |
1152 | |
1153 | rc = bnxt_hwrm_remote_dev_reset_set(bp, remote_reset: ctx->val.vbool); |
1154 | if (rc) |
1155 | return rc; |
1156 | |
1157 | bnxt_dl_set_remote_reset(dl, value: ctx->val.vbool); |
1158 | return rc; |
1159 | } |
1160 | |
1161 | static const struct devlink_param bnxt_dl_params[] = { |
1162 | DEVLINK_PARAM_GENERIC(ENABLE_SRIOV, |
1163 | BIT(DEVLINK_PARAM_CMODE_PERMANENT), |
1164 | bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set, |
1165 | NULL), |
1166 | DEVLINK_PARAM_GENERIC(IGNORE_ARI, |
1167 | BIT(DEVLINK_PARAM_CMODE_PERMANENT), |
1168 | bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set, |
1169 | NULL), |
1170 | DEVLINK_PARAM_GENERIC(MSIX_VEC_PER_PF_MAX, |
1171 | BIT(DEVLINK_PARAM_CMODE_PERMANENT), |
1172 | bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set, |
1173 | bnxt_dl_msix_validate), |
1174 | DEVLINK_PARAM_GENERIC(MSIX_VEC_PER_PF_MIN, |
1175 | BIT(DEVLINK_PARAM_CMODE_PERMANENT), |
1176 | bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set, |
1177 | bnxt_dl_msix_validate), |
1178 | DEVLINK_PARAM_DRIVER(BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK, |
1179 | "gre_ver_check" , DEVLINK_PARAM_TYPE_BOOL, |
1180 | BIT(DEVLINK_PARAM_CMODE_PERMANENT), |
1181 | bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set, |
1182 | NULL), |
1183 | /* keep REMOTE_DEV_RESET last, it is excluded based on caps */ |
1184 | DEVLINK_PARAM_GENERIC(ENABLE_REMOTE_DEV_RESET, |
1185 | BIT(DEVLINK_PARAM_CMODE_RUNTIME), |
1186 | bnxt_remote_dev_reset_get, |
1187 | bnxt_remote_dev_reset_set, NULL), |
1188 | }; |
1189 | |
1190 | static int bnxt_dl_params_register(struct bnxt *bp) |
1191 | { |
1192 | int num_params = ARRAY_SIZE(bnxt_dl_params); |
1193 | int rc; |
1194 | |
1195 | if (bp->hwrm_spec_code < 0x10600) |
1196 | return 0; |
1197 | |
1198 | if (~bp->fw_cap & BNXT_FW_CAP_HOT_RESET_IF) |
1199 | num_params--; |
1200 | |
1201 | rc = devlink_params_register(devlink: bp->dl, params: bnxt_dl_params, params_count: num_params); |
1202 | if (rc) |
1203 | netdev_warn(dev: bp->dev, format: "devlink_params_register failed. rc=%d\n" , |
1204 | rc); |
1205 | return rc; |
1206 | } |
1207 | |
1208 | static void bnxt_dl_params_unregister(struct bnxt *bp) |
1209 | { |
1210 | int num_params = ARRAY_SIZE(bnxt_dl_params); |
1211 | |
1212 | if (bp->hwrm_spec_code < 0x10600) |
1213 | return; |
1214 | |
1215 | if (~bp->fw_cap & BNXT_FW_CAP_HOT_RESET_IF) |
1216 | num_params--; |
1217 | |
1218 | devlink_params_unregister(devlink: bp->dl, params: bnxt_dl_params, params_count: num_params); |
1219 | } |
1220 | |
1221 | int bnxt_dl_register(struct bnxt *bp) |
1222 | { |
1223 | const struct devlink_ops *devlink_ops; |
1224 | struct devlink_port_attrs attrs = {}; |
1225 | struct bnxt_dl *bp_dl; |
1226 | struct devlink *dl; |
1227 | int rc; |
1228 | |
1229 | if (BNXT_PF(bp)) |
1230 | devlink_ops = &bnxt_dl_ops; |
1231 | else |
1232 | devlink_ops = &bnxt_vf_dl_ops; |
1233 | |
1234 | dl = devlink_alloc(ops: devlink_ops, priv_size: sizeof(struct bnxt_dl), dev: &bp->pdev->dev); |
1235 | if (!dl) { |
1236 | netdev_warn(dev: bp->dev, format: "devlink_alloc failed\n" ); |
1237 | return -ENOMEM; |
1238 | } |
1239 | |
1240 | bp->dl = dl; |
1241 | bp_dl = devlink_priv(devlink: dl); |
1242 | bp_dl->bp = bp; |
1243 | bnxt_dl_set_remote_reset(dl, value: true); |
1244 | |
1245 | /* Add switchdev eswitch mode setting, if SRIOV supported */ |
1246 | if (pci_find_ext_capability(dev: bp->pdev, PCI_EXT_CAP_ID_SRIOV) && |
1247 | bp->hwrm_spec_code > 0x10803) |
1248 | bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY; |
1249 | |
1250 | if (!BNXT_PF(bp)) |
1251 | goto out; |
1252 | |
1253 | attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; |
1254 | attrs.phys.port_number = bp->pf.port_id; |
1255 | memcpy(attrs.switch_id.id, bp->dsn, sizeof(bp->dsn)); |
1256 | attrs.switch_id.id_len = sizeof(bp->dsn); |
1257 | devlink_port_attrs_set(devlink_port: &bp->dl_port, devlink_port_attrs: &attrs); |
1258 | rc = devlink_port_register(devlink: dl, devlink_port: &bp->dl_port, port_index: bp->pf.port_id); |
1259 | if (rc) { |
1260 | netdev_err(dev: bp->dev, format: "devlink_port_register failed\n" ); |
1261 | goto err_dl_free; |
1262 | } |
1263 | |
1264 | rc = bnxt_dl_params_register(bp); |
1265 | if (rc) |
1266 | goto err_dl_port_unreg; |
1267 | |
1268 | out: |
1269 | devlink_register(devlink: dl); |
1270 | return 0; |
1271 | |
1272 | err_dl_port_unreg: |
1273 | devlink_port_unregister(devlink_port: &bp->dl_port); |
1274 | err_dl_free: |
1275 | devlink_free(devlink: dl); |
1276 | return rc; |
1277 | } |
1278 | |
1279 | void bnxt_dl_unregister(struct bnxt *bp) |
1280 | { |
1281 | struct devlink *dl = bp->dl; |
1282 | |
1283 | devlink_unregister(devlink: dl); |
1284 | if (BNXT_PF(bp)) { |
1285 | bnxt_dl_params_unregister(bp); |
1286 | devlink_port_unregister(devlink_port: &bp->dl_port); |
1287 | } |
1288 | devlink_free(devlink: dl); |
1289 | } |
1290 | |