mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
259 lines
6.1 KiB
259 lines
6.1 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* Marvell/Qlogic FastLinQ NIC driver |
|
* |
|
* Copyright (C) 2020 Marvell International Ltd. |
|
*/ |
|
|
|
#include <linux/kernel.h> |
|
#include <linux/qed/qed_if.h> |
|
#include <linux/vmalloc.h> |
|
#include "qed.h" |
|
#include "qed_devlink.h" |
|
|
|
enum qed_devlink_param_id { |
|
QED_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, |
|
QED_DEVLINK_PARAM_ID_IWARP_CMT, |
|
}; |
|
|
|
struct qed_fw_fatal_ctx { |
|
enum qed_hw_err_type err_type; |
|
}; |
|
|
|
int qed_report_fatal_error(struct devlink *devlink, enum qed_hw_err_type err_type) |
|
{ |
|
struct qed_devlink *qdl = devlink_priv(devlink); |
|
struct qed_fw_fatal_ctx fw_fatal_ctx = { |
|
.err_type = err_type, |
|
}; |
|
|
|
if (qdl->fw_reporter) |
|
devlink_health_report(qdl->fw_reporter, |
|
"Fatal error occurred", &fw_fatal_ctx); |
|
|
|
return 0; |
|
} |
|
|
|
static int |
|
qed_fw_fatal_reporter_dump(struct devlink_health_reporter *reporter, |
|
struct devlink_fmsg *fmsg, void *priv_ctx, |
|
struct netlink_ext_ack *extack) |
|
{ |
|
struct qed_devlink *qdl = devlink_health_reporter_priv(reporter); |
|
struct qed_fw_fatal_ctx *fw_fatal_ctx = priv_ctx; |
|
struct qed_dev *cdev = qdl->cdev; |
|
u32 dbg_data_buf_size; |
|
u8 *p_dbg_data_buf; |
|
int err; |
|
|
|
/* Having context means that was a dump request after fatal, |
|
* so we enable extra debugging while gathering the dump, |
|
* just in case |
|
*/ |
|
cdev->print_dbg_data = fw_fatal_ctx ? true : false; |
|
|
|
dbg_data_buf_size = qed_dbg_all_data_size(cdev); |
|
p_dbg_data_buf = vzalloc(dbg_data_buf_size); |
|
if (!p_dbg_data_buf) { |
|
DP_NOTICE(cdev, |
|
"Failed to allocate memory for a debug data buffer\n"); |
|
return -ENOMEM; |
|
} |
|
|
|
err = qed_dbg_all_data(cdev, p_dbg_data_buf); |
|
if (err) { |
|
DP_NOTICE(cdev, "Failed to obtain debug data\n"); |
|
vfree(p_dbg_data_buf); |
|
return err; |
|
} |
|
|
|
err = devlink_fmsg_binary_pair_put(fmsg, "dump_data", |
|
p_dbg_data_buf, dbg_data_buf_size); |
|
|
|
vfree(p_dbg_data_buf); |
|
|
|
return err; |
|
} |
|
|
|
static int |
|
qed_fw_fatal_reporter_recover(struct devlink_health_reporter *reporter, |
|
void *priv_ctx, |
|
struct netlink_ext_ack *extack) |
|
{ |
|
struct qed_devlink *qdl = devlink_health_reporter_priv(reporter); |
|
struct qed_dev *cdev = qdl->cdev; |
|
|
|
qed_recovery_process(cdev); |
|
|
|
return 0; |
|
} |
|
|
|
static const struct devlink_health_reporter_ops qed_fw_fatal_reporter_ops = { |
|
.name = "fw_fatal", |
|
.recover = qed_fw_fatal_reporter_recover, |
|
.dump = qed_fw_fatal_reporter_dump, |
|
}; |
|
|
|
#define QED_REPORTER_FW_GRACEFUL_PERIOD 1200000 |
|
|
|
void qed_fw_reporters_create(struct devlink *devlink) |
|
{ |
|
struct qed_devlink *dl = devlink_priv(devlink); |
|
|
|
dl->fw_reporter = devlink_health_reporter_create(devlink, &qed_fw_fatal_reporter_ops, |
|
QED_REPORTER_FW_GRACEFUL_PERIOD, dl); |
|
if (IS_ERR(dl->fw_reporter)) { |
|
DP_NOTICE(dl->cdev, "Failed to create fw reporter, err = %ld\n", |
|
PTR_ERR(dl->fw_reporter)); |
|
dl->fw_reporter = NULL; |
|
} |
|
} |
|
|
|
void qed_fw_reporters_destroy(struct devlink *devlink) |
|
{ |
|
struct qed_devlink *dl = devlink_priv(devlink); |
|
struct devlink_health_reporter *rep; |
|
|
|
rep = dl->fw_reporter; |
|
|
|
if (!IS_ERR_OR_NULL(rep)) |
|
devlink_health_reporter_destroy(rep); |
|
} |
|
|
|
static int qed_dl_param_get(struct devlink *dl, u32 id, |
|
struct devlink_param_gset_ctx *ctx) |
|
{ |
|
struct qed_devlink *qed_dl = devlink_priv(dl); |
|
struct qed_dev *cdev; |
|
|
|
cdev = qed_dl->cdev; |
|
ctx->val.vbool = cdev->iwarp_cmt; |
|
|
|
return 0; |
|
} |
|
|
|
static int qed_dl_param_set(struct devlink *dl, u32 id, |
|
struct devlink_param_gset_ctx *ctx) |
|
{ |
|
struct qed_devlink *qed_dl = devlink_priv(dl); |
|
struct qed_dev *cdev; |
|
|
|
cdev = qed_dl->cdev; |
|
cdev->iwarp_cmt = ctx->val.vbool; |
|
|
|
return 0; |
|
} |
|
|
|
static const struct devlink_param qed_devlink_params[] = { |
|
DEVLINK_PARAM_DRIVER(QED_DEVLINK_PARAM_ID_IWARP_CMT, |
|
"iwarp_cmt", DEVLINK_PARAM_TYPE_BOOL, |
|
BIT(DEVLINK_PARAM_CMODE_RUNTIME), |
|
qed_dl_param_get, qed_dl_param_set, NULL), |
|
}; |
|
|
|
static int qed_devlink_info_get(struct devlink *devlink, |
|
struct devlink_info_req *req, |
|
struct netlink_ext_ack *extack) |
|
{ |
|
struct qed_devlink *qed_dl = devlink_priv(devlink); |
|
struct qed_dev *cdev = qed_dl->cdev; |
|
struct qed_dev_info *dev_info; |
|
char buf[100]; |
|
int err; |
|
|
|
dev_info = &cdev->common_dev_info; |
|
|
|
err = devlink_info_driver_name_put(req, KBUILD_MODNAME); |
|
if (err) |
|
return err; |
|
|
|
memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num)); |
|
buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0; |
|
|
|
if (buf[0]) { |
|
err = devlink_info_board_serial_number_put(req, buf); |
|
if (err) |
|
return err; |
|
} |
|
|
|
snprintf(buf, sizeof(buf), "%d.%d.%d.%d", |
|
GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_3), |
|
GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_2), |
|
GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_1), |
|
GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_0)); |
|
|
|
err = devlink_info_version_stored_put(req, |
|
DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf); |
|
if (err) |
|
return err; |
|
|
|
snprintf(buf, sizeof(buf), "%d.%d.%d.%d", |
|
dev_info->fw_major, |
|
dev_info->fw_minor, |
|
dev_info->fw_rev, |
|
dev_info->fw_eng); |
|
|
|
return devlink_info_version_running_put(req, |
|
DEVLINK_INFO_VERSION_GENERIC_FW_APP, buf); |
|
} |
|
|
|
static const struct devlink_ops qed_dl_ops = { |
|
.info_get = qed_devlink_info_get, |
|
}; |
|
|
|
struct devlink *qed_devlink_register(struct qed_dev *cdev) |
|
{ |
|
union devlink_param_value value; |
|
struct qed_devlink *qdevlink; |
|
struct devlink *dl; |
|
int rc; |
|
|
|
dl = devlink_alloc(&qed_dl_ops, sizeof(struct qed_devlink)); |
|
if (!dl) |
|
return ERR_PTR(-ENOMEM); |
|
|
|
qdevlink = devlink_priv(dl); |
|
qdevlink->cdev = cdev; |
|
|
|
rc = devlink_register(dl, &cdev->pdev->dev); |
|
if (rc) |
|
goto err_free; |
|
|
|
rc = devlink_params_register(dl, qed_devlink_params, |
|
ARRAY_SIZE(qed_devlink_params)); |
|
if (rc) |
|
goto err_unregister; |
|
|
|
value.vbool = false; |
|
devlink_param_driverinit_value_set(dl, |
|
QED_DEVLINK_PARAM_ID_IWARP_CMT, |
|
value); |
|
|
|
devlink_params_publish(dl); |
|
cdev->iwarp_cmt = false; |
|
|
|
qed_fw_reporters_create(dl); |
|
|
|
return dl; |
|
|
|
err_unregister: |
|
devlink_unregister(dl); |
|
|
|
err_free: |
|
devlink_free(dl); |
|
|
|
return ERR_PTR(rc); |
|
} |
|
|
|
void qed_devlink_unregister(struct devlink *devlink) |
|
{ |
|
if (!devlink) |
|
return; |
|
|
|
qed_fw_reporters_destroy(devlink); |
|
|
|
devlink_params_unregister(devlink, qed_devlink_params, |
|
ARRAY_SIZE(qed_devlink_params)); |
|
|
|
devlink_unregister(devlink); |
|
devlink_free(devlink); |
|
}
|
|
|