1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) Meta Platforms, Inc. and affiliates. */
3
4 #include <linux/unaligned.h>
5 #include <linux/pci.h>
6 #include <linux/pldmfw.h>
7 #include <linux/types.h>
8 #include <net/devlink.h>
9
10 #include "fbnic.h"
11 #include "fbnic_fw.h"
12 #include "fbnic_tlv.h"
13
14 #define FBNIC_SN_STR_LEN 24
15
fbnic_version_running_put(struct devlink_info_req * req,struct fbnic_fw_ver * fw_ver,char * ver_name)16 static int fbnic_version_running_put(struct devlink_info_req *req,
17 struct fbnic_fw_ver *fw_ver,
18 char *ver_name)
19 {
20 char running_ver[FBNIC_FW_VER_MAX_SIZE];
21 int err;
22
23 fbnic_mk_fw_ver_str(fw_ver->version, running_ver);
24 err = devlink_info_version_running_put(req, ver_name, running_ver);
25 if (err)
26 return err;
27
28 if (strlen(fw_ver->commit) > 0) {
29 char commit_name[FBNIC_SN_STR_LEN];
30
31 snprintf(commit_name, FBNIC_SN_STR_LEN, "%s.commit", ver_name);
32 err = devlink_info_version_running_put(req, commit_name,
33 fw_ver->commit);
34 if (err)
35 return err;
36 }
37
38 return 0;
39 }
40
fbnic_version_stored_put(struct devlink_info_req * req,struct fbnic_fw_ver * fw_ver,char * ver_name)41 static int fbnic_version_stored_put(struct devlink_info_req *req,
42 struct fbnic_fw_ver *fw_ver,
43 char *ver_name)
44 {
45 char stored_ver[FBNIC_FW_VER_MAX_SIZE];
46 int err;
47
48 fbnic_mk_fw_ver_str(fw_ver->version, stored_ver);
49 err = devlink_info_version_stored_put(req, ver_name, stored_ver);
50 if (err)
51 return err;
52
53 if (strlen(fw_ver->commit) > 0) {
54 char commit_name[FBNIC_SN_STR_LEN];
55
56 snprintf(commit_name, FBNIC_SN_STR_LEN, "%s.commit", ver_name);
57 err = devlink_info_version_stored_put(req, commit_name,
58 fw_ver->commit);
59 if (err)
60 return err;
61 }
62
63 return 0;
64 }
65
fbnic_devlink_info_get(struct devlink * devlink,struct devlink_info_req * req,struct netlink_ext_ack * extack)66 static int fbnic_devlink_info_get(struct devlink *devlink,
67 struct devlink_info_req *req,
68 struct netlink_ext_ack *extack)
69 {
70 struct fbnic_dev *fbd = devlink_priv(devlink);
71 int err;
72
73 err = fbnic_version_running_put(req, &fbd->fw_cap.running.mgmt,
74 DEVLINK_INFO_VERSION_GENERIC_FW);
75 if (err)
76 return err;
77
78 err = fbnic_version_running_put(req, &fbd->fw_cap.running.bootloader,
79 DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER);
80 if (err)
81 return err;
82
83 err = fbnic_version_stored_put(req, &fbd->fw_cap.stored.mgmt,
84 DEVLINK_INFO_VERSION_GENERIC_FW);
85 if (err)
86 return err;
87
88 err = fbnic_version_stored_put(req, &fbd->fw_cap.stored.bootloader,
89 DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER);
90 if (err)
91 return err;
92
93 err = fbnic_version_stored_put(req, &fbd->fw_cap.stored.undi,
94 DEVLINK_INFO_VERSION_GENERIC_FW_UNDI);
95 if (err)
96 return err;
97
98 if (fbd->dsn) {
99 unsigned char serial[FBNIC_SN_STR_LEN];
100 u8 dsn[8];
101
102 put_unaligned_be64(fbd->dsn, dsn);
103 err = snprintf(serial, FBNIC_SN_STR_LEN, "%8phD", dsn);
104 if (err < 0)
105 return err;
106
107 err = devlink_info_serial_number_put(req, serial);
108 if (err)
109 return err;
110 }
111
112 return 0;
113 }
114
115 static bool
fbnic_pldm_match_record(struct pldmfw * context,struct pldmfw_record * record)116 fbnic_pldm_match_record(struct pldmfw *context, struct pldmfw_record *record)
117 {
118 struct pldmfw_desc_tlv *desc;
119 u32 anti_rollback_ver = 0;
120 struct devlink *devlink;
121 struct fbnic_dev *fbd;
122 struct pci_dev *pdev;
123
124 /* First, use the standard PCI matching function */
125 if (!pldmfw_op_pci_match_record(context, record))
126 return false;
127
128 pdev = to_pci_dev(context->dev);
129 fbd = pci_get_drvdata(pdev);
130 devlink = priv_to_devlink(fbd);
131
132 /* If PCI match is successful, check for vendor-specific descriptors */
133 list_for_each_entry(desc, &record->descs, entry) {
134 if (desc->type != PLDM_DESC_ID_VENDOR_DEFINED)
135 continue;
136
137 if (desc->size < 21 || desc->data[0] != 1 ||
138 desc->data[1] != 15)
139 continue;
140
141 if (memcmp(desc->data + 2, "AntiRollbackVer", 15) != 0)
142 continue;
143
144 anti_rollback_ver = get_unaligned_le32(desc->data + 17);
145 break;
146 }
147
148 /* Compare versions and return error if they do not match */
149 if (anti_rollback_ver < fbd->fw_cap.anti_rollback_version) {
150 char buf[128];
151
152 snprintf(buf, sizeof(buf),
153 "New firmware anti-rollback version (0x%x) is older than device version (0x%x)!",
154 anti_rollback_ver, fbd->fw_cap.anti_rollback_version);
155 devlink_flash_update_status_notify(devlink, buf,
156 "Anti-Rollback", 0, 0);
157
158 return false;
159 }
160
161 return true;
162 }
163
164 static int
fbnic_flash_start(struct fbnic_dev * fbd,struct pldmfw_component * component)165 fbnic_flash_start(struct fbnic_dev *fbd, struct pldmfw_component *component)
166 {
167 struct fbnic_fw_completion *cmpl;
168 int err;
169
170 cmpl = fbnic_fw_alloc_cmpl(FBNIC_TLV_MSG_ID_FW_START_UPGRADE_REQ);
171 if (!cmpl)
172 return -ENOMEM;
173
174 err = fbnic_fw_xmit_fw_start_upgrade(fbd, cmpl,
175 component->identifier,
176 component->component_size);
177 if (err)
178 goto cmpl_free;
179
180 /* Wait for firmware to ack firmware upgrade start */
181 if (fbnic_mbx_wait_for_cmpl(cmpl))
182 err = cmpl->result;
183 else
184 err = -ETIMEDOUT;
185
186 fbnic_mbx_clear_cmpl(fbd, cmpl);
187 cmpl_free:
188 fbnic_fw_put_cmpl(cmpl);
189
190 return err;
191 }
192
193 static int
fbnic_flash_component(struct pldmfw * context,struct pldmfw_component * component)194 fbnic_flash_component(struct pldmfw *context,
195 struct pldmfw_component *component)
196 {
197 const u8 *data = component->component_data;
198 const u32 size = component->component_size;
199 struct fbnic_fw_completion *cmpl;
200 const char *component_name;
201 struct devlink *devlink;
202 struct fbnic_dev *fbd;
203 struct pci_dev *pdev;
204 u32 offset = 0;
205 u32 length = 0;
206 char buf[32];
207 int err;
208
209 pdev = to_pci_dev(context->dev);
210 fbd = pci_get_drvdata(pdev);
211 devlink = priv_to_devlink(fbd);
212
213 switch (component->identifier) {
214 case QSPI_SECTION_CMRT:
215 component_name = "boot1";
216 break;
217 case QSPI_SECTION_CONTROL_FW:
218 component_name = "boot2";
219 break;
220 case QSPI_SECTION_OPTION_ROM:
221 component_name = "option-rom";
222 break;
223 default:
224 snprintf(buf, sizeof(buf), "Unknown component ID %u!",
225 component->identifier);
226 devlink_flash_update_status_notify(devlink, buf, NULL, 0,
227 size);
228 return -EINVAL;
229 }
230
231 /* Once firmware receives the request to start upgrading it responds
232 * with two messages:
233 * 1. An ACK that it received the message and possible error code
234 * indicating that an upgrade is not currently possible.
235 * 2. A request for the first chunk of data
236 *
237 * Setup completions for write before issuing the start message so
238 * the driver can catch both messages.
239 */
240 cmpl = fbnic_fw_alloc_cmpl(FBNIC_TLV_MSG_ID_FW_WRITE_CHUNK_REQ);
241 if (!cmpl)
242 return -ENOMEM;
243
244 err = fbnic_mbx_set_cmpl(fbd, cmpl);
245 if (err)
246 goto cmpl_free;
247
248 devlink_flash_update_timeout_notify(devlink, "Initializing",
249 component_name, 15);
250 err = fbnic_flash_start(fbd, component);
251 if (err)
252 goto err_no_msg;
253
254 while (offset < size) {
255 if (!fbnic_mbx_wait_for_cmpl(cmpl)) {
256 err = -ETIMEDOUT;
257 break;
258 }
259
260 err = cmpl->result;
261 if (err)
262 break;
263
264 /* Verify firmware is requesting the next chunk in the seq. */
265 if (cmpl->u.fw_update.offset != offset + length) {
266 err = -EFAULT;
267 break;
268 }
269
270 offset = cmpl->u.fw_update.offset;
271 length = cmpl->u.fw_update.length;
272
273 if (length > TLV_MAX_DATA || offset + length > size) {
274 err = -EFAULT;
275 break;
276 }
277
278 devlink_flash_update_status_notify(devlink, "Flashing",
279 component_name,
280 offset, size);
281
282 /* Mailbox will set length to 0 once it receives the finish
283 * message.
284 */
285 if (!length)
286 continue;
287
288 reinit_completion(&cmpl->done);
289 err = fbnic_fw_xmit_fw_write_chunk(fbd, data, offset, length,
290 0);
291 if (err)
292 break;
293 }
294
295 if (err) {
296 fbnic_fw_xmit_fw_write_chunk(fbd, NULL, 0, 0, err);
297 err_no_msg:
298 snprintf(buf, sizeof(buf), "Mailbox encountered error %d!",
299 err);
300 devlink_flash_update_status_notify(devlink, buf,
301 component_name, 0, 0);
302 }
303
304 fbnic_mbx_clear_cmpl(fbd, cmpl);
305 cmpl_free:
306 fbnic_fw_put_cmpl(cmpl);
307
308 return err;
309 }
310
311 static const struct pldmfw_ops fbnic_pldmfw_ops = {
312 .match_record = fbnic_pldm_match_record,
313 .flash_component = fbnic_flash_component,
314 };
315
316 static int
fbnic_devlink_flash_update(struct devlink * devlink,struct devlink_flash_update_params * params,struct netlink_ext_ack * extack)317 fbnic_devlink_flash_update(struct devlink *devlink,
318 struct devlink_flash_update_params *params,
319 struct netlink_ext_ack *extack)
320 {
321 struct fbnic_dev *fbd = devlink_priv(devlink);
322 const struct firmware *fw = params->fw;
323 struct device *dev = fbd->dev;
324 struct pldmfw context;
325 char *err_msg;
326 int err;
327
328 context.ops = &fbnic_pldmfw_ops;
329 context.dev = dev;
330
331 err = pldmfw_flash_image(&context, fw);
332 if (err) {
333 switch (err) {
334 case -EINVAL:
335 err_msg = "Invalid image";
336 break;
337 case -EOPNOTSUPP:
338 err_msg = "Unsupported image";
339 break;
340 case -ENOMEM:
341 err_msg = "Out of memory";
342 break;
343 case -EFAULT:
344 err_msg = "Invalid header";
345 break;
346 case -ENOENT:
347 err_msg = "No matching record";
348 break;
349 case -ENODEV:
350 err_msg = "No matching device";
351 break;
352 case -ETIMEDOUT:
353 err_msg = "Timed out waiting for reply";
354 break;
355 default:
356 err_msg = "Unknown error";
357 break;
358 }
359
360 NL_SET_ERR_MSG_FMT_MOD(extack,
361 "Failed to flash PLDM Image: %s (error: %d)",
362 err_msg, err);
363 }
364
365 return err;
366 }
367
368 static const struct devlink_ops fbnic_devlink_ops = {
369 .info_get = fbnic_devlink_info_get,
370 .flash_update = fbnic_devlink_flash_update,
371 };
372
fbnic_fw_reporter_dump(struct devlink_health_reporter * reporter,struct devlink_fmsg * fmsg,void * priv_ctx,struct netlink_ext_ack * extack)373 static int fbnic_fw_reporter_dump(struct devlink_health_reporter *reporter,
374 struct devlink_fmsg *fmsg, void *priv_ctx,
375 struct netlink_ext_ack *extack)
376 {
377 struct fbnic_dev *fbd = devlink_health_reporter_priv(reporter);
378 u32 offset, index, index_count, length, size;
379 struct fbnic_fw_completion *fw_cmpl;
380 u8 *dump_data, **data;
381 int err;
382
383 fw_cmpl = fbnic_fw_alloc_cmpl(FBNIC_TLV_MSG_ID_COREDUMP_GET_INFO_RESP);
384 if (!fw_cmpl)
385 return -ENOMEM;
386
387 err = fbnic_fw_xmit_coredump_info_msg(fbd, fw_cmpl, true);
388 if (err) {
389 NL_SET_ERR_MSG_MOD(extack,
390 "Failed to transmit core dump info msg");
391 goto cmpl_free;
392 }
393 if (!fbnic_mbx_wait_for_cmpl(fw_cmpl)) {
394 NL_SET_ERR_MSG_MOD(extack,
395 "Timed out waiting on core dump info");
396 err = -ETIMEDOUT;
397 goto cmpl_cleanup;
398 }
399
400 size = fw_cmpl->u.coredump_info.size;
401 err = fw_cmpl->result;
402
403 fbnic_mbx_clear_cmpl(fbd, fw_cmpl);
404 fbnic_fw_put_cmpl(fw_cmpl);
405
406 /* Handle error returned by firmware */
407 if (err) {
408 NL_SET_ERR_MSG_MOD(extack, "Firmware core dump returned error");
409 return err;
410 }
411 if (!size) {
412 NL_SET_ERR_MSG_MOD(extack,
413 "Firmware core dump returned size 0");
414 return -EIO;
415 }
416
417 /* Read the dump, we can only transfer TLV_MAX_DATA at a time */
418 index_count = DIV_ROUND_UP(size, TLV_MAX_DATA);
419
420 fw_cmpl = __fbnic_fw_alloc_cmpl(FBNIC_TLV_MSG_ID_COREDUMP_READ_RESP,
421 sizeof(void *) * index_count + size);
422 if (!fw_cmpl)
423 return -ENOMEM;
424
425 /* Populate pointer table w/ pointer offsets */
426 dump_data = (void *)&fw_cmpl->u.coredump.data[index_count];
427 data = fw_cmpl->u.coredump.data;
428 fw_cmpl->u.coredump.size = size;
429 fw_cmpl->u.coredump.stride = TLV_MAX_DATA;
430
431 for (index = 0; index < index_count; index++) {
432 /* First iteration installs completion */
433 struct fbnic_fw_completion *cmpl_arg = index ? NULL : fw_cmpl;
434
435 offset = index * TLV_MAX_DATA;
436 length = min(size - offset, TLV_MAX_DATA);
437
438 data[index] = dump_data + offset;
439 err = fbnic_fw_xmit_coredump_read_msg(fbd, cmpl_arg,
440 offset, length);
441 if (err) {
442 NL_SET_ERR_MSG_MOD(extack,
443 "Failed to transmit core dump msg");
444 if (cmpl_arg)
445 goto cmpl_free;
446 else
447 goto cmpl_cleanup;
448 }
449
450 if (fbnic_mbx_wait_for_cmpl(fw_cmpl)) {
451 reinit_completion(&fw_cmpl->done);
452 } else {
453 NL_SET_ERR_MSG_FMT_MOD(extack,
454 "Timed out waiting on core dump (%d/%d)",
455 index + 1, index_count);
456 err = -ETIMEDOUT;
457 goto cmpl_cleanup;
458 }
459
460 /* If we didn't see the reply record as incomplete */
461 if (fw_cmpl->u.coredump.data[index]) {
462 NL_SET_ERR_MSG_FMT_MOD(extack,
463 "No data for core dump chunk (%d/%d)",
464 index + 1, index_count);
465 err = -EIO;
466 goto cmpl_cleanup;
467 }
468 }
469
470 devlink_fmsg_binary_pair_nest_start(fmsg, "FW coredump");
471
472 for (offset = 0; offset < size; offset += length) {
473 length = min_t(u32, size - offset, TLV_MAX_DATA);
474
475 devlink_fmsg_binary_put(fmsg, dump_data + offset, length);
476 }
477
478 devlink_fmsg_binary_pair_nest_end(fmsg);
479
480 cmpl_cleanup:
481 fbnic_mbx_clear_cmpl(fbd, fw_cmpl);
482 cmpl_free:
483 fbnic_fw_put_cmpl(fw_cmpl);
484
485 return err;
486 }
487
488 static int
fbnic_fw_reporter_diagnose(struct devlink_health_reporter * reporter,struct devlink_fmsg * fmsg,struct netlink_ext_ack * extack)489 fbnic_fw_reporter_diagnose(struct devlink_health_reporter *reporter,
490 struct devlink_fmsg *fmsg,
491 struct netlink_ext_ack *extack)
492 {
493 struct fbnic_dev *fbd = devlink_health_reporter_priv(reporter);
494 u32 sec, msec;
495
496 /* Device is most likely down, we're not exchanging heartbeats */
497 if (!fbd->prev_firmware_time)
498 return 0;
499
500 sec = div_u64_rem(fbd->firmware_time, MSEC_PER_SEC, &msec);
501
502 devlink_fmsg_pair_nest_start(fmsg, "last_heartbeat");
503 devlink_fmsg_obj_nest_start(fmsg);
504 devlink_fmsg_pair_nest_start(fmsg, "fw_uptime");
505 devlink_fmsg_obj_nest_start(fmsg);
506 devlink_fmsg_u32_pair_put(fmsg, "sec", sec);
507 devlink_fmsg_u32_pair_put(fmsg, "msec", msec);
508 devlink_fmsg_obj_nest_end(fmsg);
509 devlink_fmsg_pair_nest_end(fmsg);
510 devlink_fmsg_obj_nest_end(fmsg);
511 devlink_fmsg_pair_nest_end(fmsg);
512
513 return 0;
514 }
515
516 void __printf(2, 3)
fbnic_devlink_fw_report(struct fbnic_dev * fbd,const char * format,...)517 fbnic_devlink_fw_report(struct fbnic_dev *fbd, const char *format, ...)
518 {
519 char msg[FBNIC_FW_LOG_MAX_SIZE];
520 va_list args;
521
522 va_start(args, format);
523 vsnprintf(msg, FBNIC_FW_LOG_MAX_SIZE, format, args);
524 va_end(args);
525
526 devlink_health_report(fbd->fw_reporter, msg, fbd);
527 if (fbnic_fw_log_ready(fbd))
528 fbnic_fw_log_write(fbd, 0, fbd->firmware_time, msg);
529 }
530
531 static const struct devlink_health_reporter_ops fbnic_fw_ops = {
532 .name = "fw",
533 .dump = fbnic_fw_reporter_dump,
534 .diagnose = fbnic_fw_reporter_diagnose,
535 };
536
fbnic_read_otp_status(struct fbnic_dev * fbd)537 static u32 fbnic_read_otp_status(struct fbnic_dev *fbd)
538 {
539 return fbnic_fw_rd32(fbd, FBNIC_NS_OTP_STATUS);
540 }
541
542 static int
fbnic_otp_reporter_dump(struct devlink_health_reporter * reporter,struct devlink_fmsg * fmsg,void * priv_ctx,struct netlink_ext_ack * extack)543 fbnic_otp_reporter_dump(struct devlink_health_reporter *reporter,
544 struct devlink_fmsg *fmsg, void *priv_ctx,
545 struct netlink_ext_ack *extack)
546 {
547 struct fbnic_dev *fbd = devlink_health_reporter_priv(reporter);
548 u32 otp_status, otp_write_status, m;
549
550 otp_status = fbnic_read_otp_status(fbd);
551 otp_write_status = fbnic_fw_rd32(fbd, FBNIC_NS_OTP_WRITE_STATUS);
552
553 /* Dump OTP status */
554 devlink_fmsg_pair_nest_start(fmsg, "OTP");
555 devlink_fmsg_obj_nest_start(fmsg);
556
557 devlink_fmsg_u32_pair_put(fmsg, "Status", otp_status);
558
559 /* Extract OTP Write Data status */
560 m = FBNIC_NS_OTP_WRITE_DATA_STATUS_MASK;
561 devlink_fmsg_u32_pair_put(fmsg, "Data",
562 FIELD_GET(m, otp_write_status));
563
564 /* Extract OTP Write ECC status */
565 m = FBNIC_NS_OTP_WRITE_ECC_STATUS_MASK;
566 devlink_fmsg_u32_pair_put(fmsg, "ECC",
567 FIELD_GET(m, otp_write_status));
568
569 devlink_fmsg_obj_nest_end(fmsg);
570 devlink_fmsg_pair_nest_end(fmsg);
571
572 return 0;
573 }
574
fbnic_devlink_otp_check(struct fbnic_dev * fbd,const char * msg)575 void fbnic_devlink_otp_check(struct fbnic_dev *fbd, const char *msg)
576 {
577 /* Check if there is anything to report */
578 if (!fbnic_read_otp_status(fbd))
579 return;
580
581 devlink_health_report(fbd->otp_reporter, msg, fbd);
582 if (fbnic_fw_log_ready(fbd))
583 fbnic_fw_log_write(fbd, 0, fbd->firmware_time, msg);
584 }
585
586 static const struct devlink_health_reporter_ops fbnic_otp_ops = {
587 .name = "otp",
588 .dump = fbnic_otp_reporter_dump,
589 };
590
fbnic_devlink_health_create(struct fbnic_dev * fbd)591 int fbnic_devlink_health_create(struct fbnic_dev *fbd)
592 {
593 fbd->fw_reporter = devlink_health_reporter_create(priv_to_devlink(fbd),
594 &fbnic_fw_ops, fbd);
595 if (IS_ERR(fbd->fw_reporter)) {
596 dev_warn(fbd->dev,
597 "Failed to create FW fault reporter: %pe\n",
598 fbd->fw_reporter);
599 return PTR_ERR(fbd->fw_reporter);
600 }
601
602 fbd->otp_reporter = devlink_health_reporter_create(priv_to_devlink(fbd),
603 &fbnic_otp_ops, fbd);
604 if (IS_ERR(fbd->otp_reporter)) {
605 devlink_health_reporter_destroy(fbd->fw_reporter);
606 dev_warn(fbd->dev,
607 "Failed to create OTP fault reporter: %pe\n",
608 fbd->otp_reporter);
609 return PTR_ERR(fbd->otp_reporter);
610 }
611
612 return 0;
613 }
614
fbnic_devlink_health_destroy(struct fbnic_dev * fbd)615 void fbnic_devlink_health_destroy(struct fbnic_dev *fbd)
616 {
617 devlink_health_reporter_destroy(fbd->otp_reporter);
618 devlink_health_reporter_destroy(fbd->fw_reporter);
619 }
620
fbnic_devlink_free(struct fbnic_dev * fbd)621 void fbnic_devlink_free(struct fbnic_dev *fbd)
622 {
623 struct devlink *devlink = priv_to_devlink(fbd);
624
625 devlink_free(devlink);
626 }
627
fbnic_devlink_alloc(struct pci_dev * pdev)628 struct fbnic_dev *fbnic_devlink_alloc(struct pci_dev *pdev)
629 {
630 void __iomem * const *iomap_table;
631 struct devlink *devlink;
632 struct fbnic_dev *fbd;
633
634 devlink = devlink_alloc(&fbnic_devlink_ops, sizeof(struct fbnic_dev),
635 &pdev->dev);
636 if (!devlink)
637 return NULL;
638
639 fbd = devlink_priv(devlink);
640 pci_set_drvdata(pdev, fbd);
641 fbd->dev = &pdev->dev;
642
643 iomap_table = pcim_iomap_table(pdev);
644 fbd->uc_addr0 = iomap_table[0];
645 fbd->uc_addr4 = iomap_table[4];
646
647 fbd->dsn = pci_get_dsn(pdev);
648 fbd->mps = pcie_get_mps(pdev);
649 fbd->readrq = pcie_get_readrq(pdev);
650
651 fbd->mac_addr_boundary = FBNIC_RPC_TCAM_MACDA_DEFAULT_BOUNDARY;
652
653 return fbd;
654 }
655
fbnic_devlink_register(struct fbnic_dev * fbd)656 void fbnic_devlink_register(struct fbnic_dev *fbd)
657 {
658 struct devlink *devlink = priv_to_devlink(fbd);
659
660 devlink_register(devlink);
661 }
662
fbnic_devlink_unregister(struct fbnic_dev * fbd)663 void fbnic_devlink_unregister(struct fbnic_dev *fbd)
664 {
665 struct devlink *devlink = priv_to_devlink(fbd);
666
667 devlink_unregister(devlink);
668 }
669