mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 01:53:29 +02:00
Add ATTR CSR bit field definitions for the DMA engine TLP header configuration registers: AW_CFG: RDE_ATTR[17:15], RQM_ATTR[14:12], TQM_ATTR[11:9] AR_CFG: TDE_ATTR[17:15], RQM_ATTR[14:12], TQM_ATTR[11:9] These fields control the PCIe TLP attribute bits for outbound transactions from the TQM, RQM, RDE (write path), and TDE (read path) DMA engines. An enum is added with standard PCIe TLP attribute values: NS (No Snoop), RO (Relaxed Ordering), and IDO (ID-based Ordering). Read the PCIe Relaxed Ordering capability at probe time and store it in fbnic_dev. Configure Relaxed Ordering on the PCIe TLP attributes in fbnic_mbx_init_desc_ring when the capability is enabled. For the write path (AW_CFG), set RO on RDE and TQM attributes. For the read path (AR_CFG), set RO on all three attributes (TDE, RQM, TQM). This allows the PCIe fabric to reorder these transactions for improved throughput. Signed-off-by: Alexander Duyck <alexanderduyck@fb.com> Signed-off-by: Dimitri Daskalakis <daskald@meta.com> Link: https://patch.msgid.link/20260327204445.3074446-1-dimitri.daskalakis1@gmail.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
670 lines
16 KiB
C
670 lines
16 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
|
|
|
|
#include <linux/unaligned.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pldmfw.h>
|
|
#include <linux/types.h>
|
|
#include <net/devlink.h>
|
|
|
|
#include "fbnic.h"
|
|
#include "fbnic_fw.h"
|
|
#include "fbnic_tlv.h"
|
|
|
|
#define FBNIC_SN_STR_LEN 24
|
|
|
|
static int fbnic_version_running_put(struct devlink_info_req *req,
|
|
struct fbnic_fw_ver *fw_ver,
|
|
char *ver_name)
|
|
{
|
|
char running_ver[FBNIC_FW_VER_MAX_SIZE];
|
|
int err;
|
|
|
|
fbnic_mk_fw_ver_str(fw_ver->version, running_ver);
|
|
err = devlink_info_version_running_put(req, ver_name, running_ver);
|
|
if (err)
|
|
return err;
|
|
|
|
if (strlen(fw_ver->commit) > 0) {
|
|
char commit_name[FBNIC_SN_STR_LEN];
|
|
|
|
snprintf(commit_name, FBNIC_SN_STR_LEN, "%s.commit", ver_name);
|
|
err = devlink_info_version_running_put(req, commit_name,
|
|
fw_ver->commit);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fbnic_version_stored_put(struct devlink_info_req *req,
|
|
struct fbnic_fw_ver *fw_ver,
|
|
char *ver_name)
|
|
{
|
|
char stored_ver[FBNIC_FW_VER_MAX_SIZE];
|
|
int err;
|
|
|
|
fbnic_mk_fw_ver_str(fw_ver->version, stored_ver);
|
|
err = devlink_info_version_stored_put(req, ver_name, stored_ver);
|
|
if (err)
|
|
return err;
|
|
|
|
if (strlen(fw_ver->commit) > 0) {
|
|
char commit_name[FBNIC_SN_STR_LEN];
|
|
|
|
snprintf(commit_name, FBNIC_SN_STR_LEN, "%s.commit", ver_name);
|
|
err = devlink_info_version_stored_put(req, commit_name,
|
|
fw_ver->commit);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fbnic_devlink_info_get(struct devlink *devlink,
|
|
struct devlink_info_req *req,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct fbnic_dev *fbd = devlink_priv(devlink);
|
|
int err;
|
|
|
|
err = fbnic_version_running_put(req, &fbd->fw_cap.running.mgmt,
|
|
DEVLINK_INFO_VERSION_GENERIC_FW);
|
|
if (err)
|
|
return err;
|
|
|
|
err = fbnic_version_running_put(req, &fbd->fw_cap.running.bootloader,
|
|
DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER);
|
|
if (err)
|
|
return err;
|
|
|
|
err = fbnic_version_stored_put(req, &fbd->fw_cap.stored.mgmt,
|
|
DEVLINK_INFO_VERSION_GENERIC_FW);
|
|
if (err)
|
|
return err;
|
|
|
|
err = fbnic_version_stored_put(req, &fbd->fw_cap.stored.bootloader,
|
|
DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER);
|
|
if (err)
|
|
return err;
|
|
|
|
err = fbnic_version_stored_put(req, &fbd->fw_cap.stored.undi,
|
|
DEVLINK_INFO_VERSION_GENERIC_FW_UNDI);
|
|
if (err)
|
|
return err;
|
|
|
|
if (fbd->dsn) {
|
|
unsigned char serial[FBNIC_SN_STR_LEN];
|
|
u8 dsn[8];
|
|
|
|
put_unaligned_be64(fbd->dsn, dsn);
|
|
err = snprintf(serial, FBNIC_SN_STR_LEN, "%8phD", dsn);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
err = devlink_info_serial_number_put(req, serial);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
fbnic_pldm_match_record(struct pldmfw *context, struct pldmfw_record *record)
|
|
{
|
|
struct pldmfw_desc_tlv *desc;
|
|
u32 anti_rollback_ver = 0;
|
|
struct devlink *devlink;
|
|
struct fbnic_dev *fbd;
|
|
struct pci_dev *pdev;
|
|
|
|
/* First, use the standard PCI matching function */
|
|
if (!pldmfw_op_pci_match_record(context, record))
|
|
return false;
|
|
|
|
pdev = to_pci_dev(context->dev);
|
|
fbd = pci_get_drvdata(pdev);
|
|
devlink = priv_to_devlink(fbd);
|
|
|
|
/* If PCI match is successful, check for vendor-specific descriptors */
|
|
list_for_each_entry(desc, &record->descs, entry) {
|
|
if (desc->type != PLDM_DESC_ID_VENDOR_DEFINED)
|
|
continue;
|
|
|
|
if (desc->size < 21 || desc->data[0] != 1 ||
|
|
desc->data[1] != 15)
|
|
continue;
|
|
|
|
if (memcmp(desc->data + 2, "AntiRollbackVer", 15) != 0)
|
|
continue;
|
|
|
|
anti_rollback_ver = get_unaligned_le32(desc->data + 17);
|
|
break;
|
|
}
|
|
|
|
/* Compare versions and return error if they do not match */
|
|
if (anti_rollback_ver < fbd->fw_cap.anti_rollback_version) {
|
|
char buf[128];
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
"New firmware anti-rollback version (0x%x) is older than device version (0x%x)!",
|
|
anti_rollback_ver, fbd->fw_cap.anti_rollback_version);
|
|
devlink_flash_update_status_notify(devlink, buf,
|
|
"Anti-Rollback", 0, 0);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static int
|
|
fbnic_flash_start(struct fbnic_dev *fbd, struct pldmfw_component *component)
|
|
{
|
|
struct fbnic_fw_completion *cmpl;
|
|
int err;
|
|
|
|
cmpl = fbnic_fw_alloc_cmpl(FBNIC_TLV_MSG_ID_FW_START_UPGRADE_REQ);
|
|
if (!cmpl)
|
|
return -ENOMEM;
|
|
|
|
err = fbnic_fw_xmit_fw_start_upgrade(fbd, cmpl,
|
|
component->identifier,
|
|
component->component_size);
|
|
if (err)
|
|
goto cmpl_free;
|
|
|
|
/* Wait for firmware to ack firmware upgrade start */
|
|
if (fbnic_mbx_wait_for_cmpl(cmpl))
|
|
err = cmpl->result;
|
|
else
|
|
err = -ETIMEDOUT;
|
|
|
|
fbnic_mbx_clear_cmpl(fbd, cmpl);
|
|
cmpl_free:
|
|
fbnic_fw_put_cmpl(cmpl);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
fbnic_flash_component(struct pldmfw *context,
|
|
struct pldmfw_component *component)
|
|
{
|
|
const u8 *data = component->component_data;
|
|
const u32 size = component->component_size;
|
|
struct fbnic_fw_completion *cmpl;
|
|
const char *component_name;
|
|
struct devlink *devlink;
|
|
struct fbnic_dev *fbd;
|
|
struct pci_dev *pdev;
|
|
u32 offset = 0;
|
|
u32 length = 0;
|
|
char buf[32];
|
|
int err;
|
|
|
|
pdev = to_pci_dev(context->dev);
|
|
fbd = pci_get_drvdata(pdev);
|
|
devlink = priv_to_devlink(fbd);
|
|
|
|
switch (component->identifier) {
|
|
case QSPI_SECTION_CMRT:
|
|
component_name = "boot1";
|
|
break;
|
|
case QSPI_SECTION_CONTROL_FW:
|
|
component_name = "boot2";
|
|
break;
|
|
case QSPI_SECTION_OPTION_ROM:
|
|
component_name = "option-rom";
|
|
break;
|
|
default:
|
|
snprintf(buf, sizeof(buf), "Unknown component ID %u!",
|
|
component->identifier);
|
|
devlink_flash_update_status_notify(devlink, buf, NULL, 0,
|
|
size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Once firmware receives the request to start upgrading it responds
|
|
* with two messages:
|
|
* 1. An ACK that it received the message and possible error code
|
|
* indicating that an upgrade is not currently possible.
|
|
* 2. A request for the first chunk of data
|
|
*
|
|
* Setup completions for write before issuing the start message so
|
|
* the driver can catch both messages.
|
|
*/
|
|
cmpl = fbnic_fw_alloc_cmpl(FBNIC_TLV_MSG_ID_FW_WRITE_CHUNK_REQ);
|
|
if (!cmpl)
|
|
return -ENOMEM;
|
|
|
|
err = fbnic_mbx_set_cmpl(fbd, cmpl);
|
|
if (err)
|
|
goto cmpl_free;
|
|
|
|
devlink_flash_update_timeout_notify(devlink, "Initializing",
|
|
component_name, 15);
|
|
err = fbnic_flash_start(fbd, component);
|
|
if (err)
|
|
goto err_no_msg;
|
|
|
|
while (offset < size) {
|
|
if (!fbnic_mbx_wait_for_cmpl(cmpl)) {
|
|
err = -ETIMEDOUT;
|
|
break;
|
|
}
|
|
|
|
err = cmpl->result;
|
|
if (err)
|
|
break;
|
|
|
|
/* Verify firmware is requesting the next chunk in the seq. */
|
|
if (cmpl->u.fw_update.offset != offset + length) {
|
|
err = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
offset = cmpl->u.fw_update.offset;
|
|
length = cmpl->u.fw_update.length;
|
|
|
|
if (length > TLV_MAX_DATA || offset + length > size) {
|
|
err = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
devlink_flash_update_status_notify(devlink, "Flashing",
|
|
component_name,
|
|
offset, size);
|
|
|
|
/* Mailbox will set length to 0 once it receives the finish
|
|
* message.
|
|
*/
|
|
if (!length)
|
|
continue;
|
|
|
|
reinit_completion(&cmpl->done);
|
|
err = fbnic_fw_xmit_fw_write_chunk(fbd, data, offset, length,
|
|
0);
|
|
if (err)
|
|
break;
|
|
}
|
|
|
|
if (err) {
|
|
fbnic_fw_xmit_fw_write_chunk(fbd, NULL, 0, 0, err);
|
|
err_no_msg:
|
|
snprintf(buf, sizeof(buf), "Mailbox encountered error %d!",
|
|
err);
|
|
devlink_flash_update_status_notify(devlink, buf,
|
|
component_name, 0, 0);
|
|
}
|
|
|
|
fbnic_mbx_clear_cmpl(fbd, cmpl);
|
|
cmpl_free:
|
|
fbnic_fw_put_cmpl(cmpl);
|
|
|
|
return err;
|
|
}
|
|
|
|
static const struct pldmfw_ops fbnic_pldmfw_ops = {
|
|
.match_record = fbnic_pldm_match_record,
|
|
.flash_component = fbnic_flash_component,
|
|
};
|
|
|
|
static int
|
|
fbnic_devlink_flash_update(struct devlink *devlink,
|
|
struct devlink_flash_update_params *params,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct fbnic_dev *fbd = devlink_priv(devlink);
|
|
const struct firmware *fw = params->fw;
|
|
struct device *dev = fbd->dev;
|
|
struct pldmfw context;
|
|
char *err_msg;
|
|
int err;
|
|
|
|
context.ops = &fbnic_pldmfw_ops;
|
|
context.dev = dev;
|
|
|
|
err = pldmfw_flash_image(&context, fw);
|
|
if (err) {
|
|
switch (err) {
|
|
case -EINVAL:
|
|
err_msg = "Invalid image";
|
|
break;
|
|
case -EOPNOTSUPP:
|
|
err_msg = "Unsupported image";
|
|
break;
|
|
case -ENOMEM:
|
|
err_msg = "Out of memory";
|
|
break;
|
|
case -EFAULT:
|
|
err_msg = "Invalid header";
|
|
break;
|
|
case -ENOENT:
|
|
err_msg = "No matching record";
|
|
break;
|
|
case -ENODEV:
|
|
err_msg = "No matching device";
|
|
break;
|
|
case -ETIMEDOUT:
|
|
err_msg = "Timed out waiting for reply";
|
|
break;
|
|
default:
|
|
err_msg = "Unknown error";
|
|
break;
|
|
}
|
|
|
|
NL_SET_ERR_MSG_FMT_MOD(extack,
|
|
"Failed to flash PLDM Image: %s (error: %d)",
|
|
err_msg, err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static const struct devlink_ops fbnic_devlink_ops = {
|
|
.info_get = fbnic_devlink_info_get,
|
|
.flash_update = fbnic_devlink_flash_update,
|
|
};
|
|
|
|
static int fbnic_fw_reporter_dump(struct devlink_health_reporter *reporter,
|
|
struct devlink_fmsg *fmsg, void *priv_ctx,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct fbnic_dev *fbd = devlink_health_reporter_priv(reporter);
|
|
u32 offset, index, index_count, length, size;
|
|
struct fbnic_fw_completion *fw_cmpl;
|
|
u8 *dump_data, **data;
|
|
int err;
|
|
|
|
fw_cmpl = fbnic_fw_alloc_cmpl(FBNIC_TLV_MSG_ID_COREDUMP_GET_INFO_RESP);
|
|
if (!fw_cmpl)
|
|
return -ENOMEM;
|
|
|
|
err = fbnic_fw_xmit_coredump_info_msg(fbd, fw_cmpl, true);
|
|
if (err) {
|
|
NL_SET_ERR_MSG_MOD(extack,
|
|
"Failed to transmit core dump info msg");
|
|
goto cmpl_free;
|
|
}
|
|
if (!fbnic_mbx_wait_for_cmpl(fw_cmpl)) {
|
|
NL_SET_ERR_MSG_MOD(extack,
|
|
"Timed out waiting on core dump info");
|
|
err = -ETIMEDOUT;
|
|
goto cmpl_cleanup;
|
|
}
|
|
|
|
size = fw_cmpl->u.coredump_info.size;
|
|
err = fw_cmpl->result;
|
|
|
|
fbnic_mbx_clear_cmpl(fbd, fw_cmpl);
|
|
fbnic_fw_put_cmpl(fw_cmpl);
|
|
|
|
/* Handle error returned by firmware */
|
|
if (err) {
|
|
NL_SET_ERR_MSG_MOD(extack, "Firmware core dump returned error");
|
|
return err;
|
|
}
|
|
if (!size) {
|
|
NL_SET_ERR_MSG_MOD(extack,
|
|
"Firmware core dump returned size 0");
|
|
return -EIO;
|
|
}
|
|
|
|
/* Read the dump, we can only transfer TLV_MAX_DATA at a time */
|
|
index_count = DIV_ROUND_UP(size, TLV_MAX_DATA);
|
|
|
|
fw_cmpl = __fbnic_fw_alloc_cmpl(FBNIC_TLV_MSG_ID_COREDUMP_READ_RESP,
|
|
sizeof(void *) * index_count + size);
|
|
if (!fw_cmpl)
|
|
return -ENOMEM;
|
|
|
|
/* Populate pointer table w/ pointer offsets */
|
|
dump_data = (void *)&fw_cmpl->u.coredump.data[index_count];
|
|
data = fw_cmpl->u.coredump.data;
|
|
fw_cmpl->u.coredump.size = size;
|
|
fw_cmpl->u.coredump.stride = TLV_MAX_DATA;
|
|
|
|
for (index = 0; index < index_count; index++) {
|
|
/* First iteration installs completion */
|
|
struct fbnic_fw_completion *cmpl_arg = index ? NULL : fw_cmpl;
|
|
|
|
offset = index * TLV_MAX_DATA;
|
|
length = min(size - offset, TLV_MAX_DATA);
|
|
|
|
data[index] = dump_data + offset;
|
|
err = fbnic_fw_xmit_coredump_read_msg(fbd, cmpl_arg,
|
|
offset, length);
|
|
if (err) {
|
|
NL_SET_ERR_MSG_MOD(extack,
|
|
"Failed to transmit core dump msg");
|
|
if (cmpl_arg)
|
|
goto cmpl_free;
|
|
else
|
|
goto cmpl_cleanup;
|
|
}
|
|
|
|
if (fbnic_mbx_wait_for_cmpl(fw_cmpl)) {
|
|
reinit_completion(&fw_cmpl->done);
|
|
} else {
|
|
NL_SET_ERR_MSG_FMT_MOD(extack,
|
|
"Timed out waiting on core dump (%d/%d)",
|
|
index + 1, index_count);
|
|
err = -ETIMEDOUT;
|
|
goto cmpl_cleanup;
|
|
}
|
|
|
|
/* If we didn't see the reply record as incomplete */
|
|
if (fw_cmpl->u.coredump.data[index]) {
|
|
NL_SET_ERR_MSG_FMT_MOD(extack,
|
|
"No data for core dump chunk (%d/%d)",
|
|
index + 1, index_count);
|
|
err = -EIO;
|
|
goto cmpl_cleanup;
|
|
}
|
|
}
|
|
|
|
devlink_fmsg_binary_pair_nest_start(fmsg, "FW coredump");
|
|
|
|
for (offset = 0; offset < size; offset += length) {
|
|
length = min_t(u32, size - offset, TLV_MAX_DATA);
|
|
|
|
devlink_fmsg_binary_put(fmsg, dump_data + offset, length);
|
|
}
|
|
|
|
devlink_fmsg_binary_pair_nest_end(fmsg);
|
|
|
|
cmpl_cleanup:
|
|
fbnic_mbx_clear_cmpl(fbd, fw_cmpl);
|
|
cmpl_free:
|
|
fbnic_fw_put_cmpl(fw_cmpl);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
fbnic_fw_reporter_diagnose(struct devlink_health_reporter *reporter,
|
|
struct devlink_fmsg *fmsg,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct fbnic_dev *fbd = devlink_health_reporter_priv(reporter);
|
|
u32 sec, msec;
|
|
|
|
/* Device is most likely down, we're not exchanging heartbeats */
|
|
if (!fbd->prev_firmware_time)
|
|
return 0;
|
|
|
|
sec = div_u64_rem(fbd->firmware_time, MSEC_PER_SEC, &msec);
|
|
|
|
devlink_fmsg_pair_nest_start(fmsg, "last_heartbeat");
|
|
devlink_fmsg_obj_nest_start(fmsg);
|
|
devlink_fmsg_pair_nest_start(fmsg, "fw_uptime");
|
|
devlink_fmsg_obj_nest_start(fmsg);
|
|
devlink_fmsg_u32_pair_put(fmsg, "sec", sec);
|
|
devlink_fmsg_u32_pair_put(fmsg, "msec", msec);
|
|
devlink_fmsg_obj_nest_end(fmsg);
|
|
devlink_fmsg_pair_nest_end(fmsg);
|
|
devlink_fmsg_obj_nest_end(fmsg);
|
|
devlink_fmsg_pair_nest_end(fmsg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void __printf(2, 3)
|
|
fbnic_devlink_fw_report(struct fbnic_dev *fbd, const char *format, ...)
|
|
{
|
|
char msg[FBNIC_FW_LOG_MAX_SIZE];
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
vsnprintf(msg, FBNIC_FW_LOG_MAX_SIZE, format, args);
|
|
va_end(args);
|
|
|
|
devlink_health_report(fbd->fw_reporter, msg, fbd);
|
|
if (fbnic_fw_log_ready(fbd))
|
|
fbnic_fw_log_write(fbd, 0, fbd->firmware_time, msg);
|
|
}
|
|
|
|
static const struct devlink_health_reporter_ops fbnic_fw_ops = {
|
|
.name = "fw",
|
|
.dump = fbnic_fw_reporter_dump,
|
|
.diagnose = fbnic_fw_reporter_diagnose,
|
|
};
|
|
|
|
static u32 fbnic_read_otp_status(struct fbnic_dev *fbd)
|
|
{
|
|
return fbnic_fw_rd32(fbd, FBNIC_NS_OTP_STATUS);
|
|
}
|
|
|
|
static int
|
|
fbnic_otp_reporter_dump(struct devlink_health_reporter *reporter,
|
|
struct devlink_fmsg *fmsg, void *priv_ctx,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct fbnic_dev *fbd = devlink_health_reporter_priv(reporter);
|
|
u32 otp_status, otp_write_status, m;
|
|
|
|
otp_status = fbnic_read_otp_status(fbd);
|
|
otp_write_status = fbnic_fw_rd32(fbd, FBNIC_NS_OTP_WRITE_STATUS);
|
|
|
|
/* Dump OTP status */
|
|
devlink_fmsg_pair_nest_start(fmsg, "OTP");
|
|
devlink_fmsg_obj_nest_start(fmsg);
|
|
|
|
devlink_fmsg_u32_pair_put(fmsg, "Status", otp_status);
|
|
|
|
/* Extract OTP Write Data status */
|
|
m = FBNIC_NS_OTP_WRITE_DATA_STATUS_MASK;
|
|
devlink_fmsg_u32_pair_put(fmsg, "Data",
|
|
FIELD_GET(m, otp_write_status));
|
|
|
|
/* Extract OTP Write ECC status */
|
|
m = FBNIC_NS_OTP_WRITE_ECC_STATUS_MASK;
|
|
devlink_fmsg_u32_pair_put(fmsg, "ECC",
|
|
FIELD_GET(m, otp_write_status));
|
|
|
|
devlink_fmsg_obj_nest_end(fmsg);
|
|
devlink_fmsg_pair_nest_end(fmsg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void fbnic_devlink_otp_check(struct fbnic_dev *fbd, const char *msg)
|
|
{
|
|
/* Check if there is anything to report */
|
|
if (!fbnic_read_otp_status(fbd))
|
|
return;
|
|
|
|
devlink_health_report(fbd->otp_reporter, msg, fbd);
|
|
if (fbnic_fw_log_ready(fbd))
|
|
fbnic_fw_log_write(fbd, 0, fbd->firmware_time, msg);
|
|
}
|
|
|
|
static const struct devlink_health_reporter_ops fbnic_otp_ops = {
|
|
.name = "otp",
|
|
.dump = fbnic_otp_reporter_dump,
|
|
};
|
|
|
|
int fbnic_devlink_health_create(struct fbnic_dev *fbd)
|
|
{
|
|
fbd->fw_reporter = devlink_health_reporter_create(priv_to_devlink(fbd),
|
|
&fbnic_fw_ops, fbd);
|
|
if (IS_ERR(fbd->fw_reporter)) {
|
|
dev_warn(fbd->dev,
|
|
"Failed to create FW fault reporter: %pe\n",
|
|
fbd->fw_reporter);
|
|
return PTR_ERR(fbd->fw_reporter);
|
|
}
|
|
|
|
fbd->otp_reporter = devlink_health_reporter_create(priv_to_devlink(fbd),
|
|
&fbnic_otp_ops, fbd);
|
|
if (IS_ERR(fbd->otp_reporter)) {
|
|
devlink_health_reporter_destroy(fbd->fw_reporter);
|
|
dev_warn(fbd->dev,
|
|
"Failed to create OTP fault reporter: %pe\n",
|
|
fbd->otp_reporter);
|
|
return PTR_ERR(fbd->otp_reporter);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void fbnic_devlink_health_destroy(struct fbnic_dev *fbd)
|
|
{
|
|
devlink_health_reporter_destroy(fbd->otp_reporter);
|
|
devlink_health_reporter_destroy(fbd->fw_reporter);
|
|
}
|
|
|
|
void fbnic_devlink_free(struct fbnic_dev *fbd)
|
|
{
|
|
struct devlink *devlink = priv_to_devlink(fbd);
|
|
|
|
devlink_free(devlink);
|
|
}
|
|
|
|
struct fbnic_dev *fbnic_devlink_alloc(struct pci_dev *pdev)
|
|
{
|
|
void __iomem * const *iomap_table;
|
|
struct devlink *devlink;
|
|
struct fbnic_dev *fbd;
|
|
|
|
devlink = devlink_alloc(&fbnic_devlink_ops, sizeof(struct fbnic_dev),
|
|
&pdev->dev);
|
|
if (!devlink)
|
|
return NULL;
|
|
|
|
fbd = devlink_priv(devlink);
|
|
pci_set_drvdata(pdev, fbd);
|
|
fbd->dev = &pdev->dev;
|
|
|
|
iomap_table = pcim_iomap_table(pdev);
|
|
fbd->uc_addr0 = iomap_table[0];
|
|
fbd->uc_addr4 = iomap_table[4];
|
|
|
|
fbd->dsn = pci_get_dsn(pdev);
|
|
fbd->mps = pcie_get_mps(pdev);
|
|
fbd->readrq = pcie_get_readrq(pdev);
|
|
fbd->relaxed_ord = pcie_relaxed_ordering_enabled(pdev);
|
|
|
|
fbd->mac_addr_boundary = FBNIC_RPC_TCAM_MACDA_DEFAULT_BOUNDARY;
|
|
|
|
return fbd;
|
|
}
|
|
|
|
void fbnic_devlink_register(struct fbnic_dev *fbd)
|
|
{
|
|
struct devlink *devlink = priv_to_devlink(fbd);
|
|
|
|
devlink_register(devlink);
|
|
}
|
|
|
|
void fbnic_devlink_unregister(struct fbnic_dev *fbd)
|
|
{
|
|
struct devlink *devlink = priv_to_devlink(fbd);
|
|
|
|
devlink_unregister(devlink);
|
|
}
|