152306deeSIgor Russkikh // SPDX-License-Identifier: GPL-2.0-or-later 252306deeSIgor Russkikh /* Marvell/Qlogic FastLinQ NIC driver 352306deeSIgor Russkikh * 452306deeSIgor Russkikh * Copyright (C) 2020 Marvell International Ltd. 552306deeSIgor Russkikh */ 652306deeSIgor Russkikh 752306deeSIgor Russkikh #include <linux/kernel.h> 8755f982bSIgor Russkikh #include <linux/qed/qed_if.h> 927fed787SIgor Russkikh #include <linux/vmalloc.h> 1052306deeSIgor Russkikh #include "qed.h" 1152306deeSIgor Russkikh #include "qed_devlink.h" 1252306deeSIgor Russkikh 1352306deeSIgor Russkikh enum qed_devlink_param_id { 1452306deeSIgor Russkikh QED_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, 1552306deeSIgor Russkikh QED_DEVLINK_PARAM_ID_IWARP_CMT, 1652306deeSIgor Russkikh }; 1752306deeSIgor Russkikh 184f5a8db2SIgor Russkikh struct qed_fw_fatal_ctx { 194f5a8db2SIgor Russkikh enum qed_hw_err_type err_type; 204f5a8db2SIgor Russkikh }; 214f5a8db2SIgor Russkikh 224f5a8db2SIgor Russkikh int qed_report_fatal_error(struct devlink *devlink, enum qed_hw_err_type err_type) 234f5a8db2SIgor Russkikh { 244f5a8db2SIgor Russkikh struct qed_devlink *qdl = devlink_priv(devlink); 254f5a8db2SIgor Russkikh struct qed_fw_fatal_ctx fw_fatal_ctx = { 264f5a8db2SIgor Russkikh .err_type = err_type, 274f5a8db2SIgor Russkikh }; 284f5a8db2SIgor Russkikh 294f5a8db2SIgor Russkikh if (qdl->fw_reporter) 304f5a8db2SIgor Russkikh devlink_health_report(qdl->fw_reporter, 314f5a8db2SIgor Russkikh "Fatal error occurred", &fw_fatal_ctx); 324f5a8db2SIgor Russkikh 334f5a8db2SIgor Russkikh return 0; 344f5a8db2SIgor Russkikh } 354f5a8db2SIgor Russkikh 36b228cb16SIgor Russkikh static int 3727fed787SIgor Russkikh qed_fw_fatal_reporter_dump(struct devlink_health_reporter *reporter, 3827fed787SIgor Russkikh struct devlink_fmsg *fmsg, void *priv_ctx, 3927fed787SIgor Russkikh struct netlink_ext_ack *extack) 4027fed787SIgor Russkikh { 4127fed787SIgor Russkikh struct qed_devlink *qdl = devlink_health_reporter_priv(reporter); 4227fed787SIgor Russkikh struct qed_fw_fatal_ctx *fw_fatal_ctx = priv_ctx; 4327fed787SIgor Russkikh struct qed_dev *cdev = qdl->cdev; 4427fed787SIgor Russkikh u32 dbg_data_buf_size; 4527fed787SIgor Russkikh u8 *p_dbg_data_buf; 4627fed787SIgor Russkikh int err; 4727fed787SIgor Russkikh 4827fed787SIgor Russkikh /* Having context means that was a dump request after fatal, 4927fed787SIgor Russkikh * so we enable extra debugging while gathering the dump, 5027fed787SIgor Russkikh * just in case 5127fed787SIgor Russkikh */ 5227fed787SIgor Russkikh cdev->print_dbg_data = fw_fatal_ctx ? true : false; 5327fed787SIgor Russkikh 5427fed787SIgor Russkikh dbg_data_buf_size = qed_dbg_all_data_size(cdev); 5527fed787SIgor Russkikh p_dbg_data_buf = vzalloc(dbg_data_buf_size); 5627fed787SIgor Russkikh if (!p_dbg_data_buf) { 5727fed787SIgor Russkikh DP_NOTICE(cdev, 5827fed787SIgor Russkikh "Failed to allocate memory for a debug data buffer\n"); 5927fed787SIgor Russkikh return -ENOMEM; 6027fed787SIgor Russkikh } 6127fed787SIgor Russkikh 6227fed787SIgor Russkikh err = qed_dbg_all_data(cdev, p_dbg_data_buf); 6327fed787SIgor Russkikh if (err) { 6427fed787SIgor Russkikh DP_NOTICE(cdev, "Failed to obtain debug data\n"); 6527fed787SIgor Russkikh vfree(p_dbg_data_buf); 6627fed787SIgor Russkikh return err; 6727fed787SIgor Russkikh } 6827fed787SIgor Russkikh 6927fed787SIgor Russkikh err = devlink_fmsg_binary_pair_put(fmsg, "dump_data", 7027fed787SIgor Russkikh p_dbg_data_buf, dbg_data_buf_size); 7127fed787SIgor Russkikh 7227fed787SIgor Russkikh vfree(p_dbg_data_buf); 7327fed787SIgor Russkikh 7427fed787SIgor Russkikh return err; 7527fed787SIgor Russkikh } 7627fed787SIgor Russkikh 7727fed787SIgor Russkikh static int 78b228cb16SIgor Russkikh qed_fw_fatal_reporter_recover(struct devlink_health_reporter *reporter, 79b228cb16SIgor Russkikh void *priv_ctx, 80b228cb16SIgor Russkikh struct netlink_ext_ack *extack) 81b228cb16SIgor Russkikh { 82b228cb16SIgor Russkikh struct qed_devlink *qdl = devlink_health_reporter_priv(reporter); 83b228cb16SIgor Russkikh struct qed_dev *cdev = qdl->cdev; 84b228cb16SIgor Russkikh 85b228cb16SIgor Russkikh qed_recovery_process(cdev); 86b228cb16SIgor Russkikh 87b228cb16SIgor Russkikh return 0; 88b228cb16SIgor Russkikh } 89b228cb16SIgor Russkikh 909524067bSIgor Russkikh static const struct devlink_health_reporter_ops qed_fw_fatal_reporter_ops = { 919524067bSIgor Russkikh .name = "fw_fatal", 92b228cb16SIgor Russkikh .recover = qed_fw_fatal_reporter_recover, 9327fed787SIgor Russkikh .dump = qed_fw_fatal_reporter_dump, 949524067bSIgor Russkikh }; 959524067bSIgor Russkikh 969524067bSIgor Russkikh #define QED_REPORTER_FW_GRACEFUL_PERIOD 1200000 979524067bSIgor Russkikh 989524067bSIgor Russkikh void qed_fw_reporters_create(struct devlink *devlink) 999524067bSIgor Russkikh { 1009524067bSIgor Russkikh struct qed_devlink *dl = devlink_priv(devlink); 1019524067bSIgor Russkikh 1029524067bSIgor Russkikh dl->fw_reporter = devlink_health_reporter_create(devlink, &qed_fw_fatal_reporter_ops, 1039524067bSIgor Russkikh QED_REPORTER_FW_GRACEFUL_PERIOD, dl); 1049524067bSIgor Russkikh if (IS_ERR(dl->fw_reporter)) { 1059524067bSIgor Russkikh DP_NOTICE(dl->cdev, "Failed to create fw reporter, err = %ld\n", 1069524067bSIgor Russkikh PTR_ERR(dl->fw_reporter)); 1079524067bSIgor Russkikh dl->fw_reporter = NULL; 1089524067bSIgor Russkikh } 1099524067bSIgor Russkikh } 1109524067bSIgor Russkikh 1119524067bSIgor Russkikh void qed_fw_reporters_destroy(struct devlink *devlink) 1129524067bSIgor Russkikh { 1139524067bSIgor Russkikh struct qed_devlink *dl = devlink_priv(devlink); 1149524067bSIgor Russkikh struct devlink_health_reporter *rep; 1159524067bSIgor Russkikh 1169524067bSIgor Russkikh rep = dl->fw_reporter; 1179524067bSIgor Russkikh 1189524067bSIgor Russkikh if (!IS_ERR_OR_NULL(rep)) 1199524067bSIgor Russkikh devlink_health_reporter_destroy(rep); 1209524067bSIgor Russkikh } 1219524067bSIgor Russkikh 12252306deeSIgor Russkikh static int qed_dl_param_get(struct devlink *dl, u32 id, 12352306deeSIgor Russkikh struct devlink_param_gset_ctx *ctx) 12452306deeSIgor Russkikh { 125755f982bSIgor Russkikh struct qed_devlink *qed_dl = devlink_priv(dl); 12652306deeSIgor Russkikh struct qed_dev *cdev; 12752306deeSIgor Russkikh 12852306deeSIgor Russkikh cdev = qed_dl->cdev; 12952306deeSIgor Russkikh ctx->val.vbool = cdev->iwarp_cmt; 13052306deeSIgor Russkikh 13152306deeSIgor Russkikh return 0; 13252306deeSIgor Russkikh } 13352306deeSIgor Russkikh 13452306deeSIgor Russkikh static int qed_dl_param_set(struct devlink *dl, u32 id, 13552306deeSIgor Russkikh struct devlink_param_gset_ctx *ctx) 13652306deeSIgor Russkikh { 137755f982bSIgor Russkikh struct qed_devlink *qed_dl = devlink_priv(dl); 13852306deeSIgor Russkikh struct qed_dev *cdev; 13952306deeSIgor Russkikh 14052306deeSIgor Russkikh cdev = qed_dl->cdev; 14152306deeSIgor Russkikh cdev->iwarp_cmt = ctx->val.vbool; 14252306deeSIgor Russkikh 14352306deeSIgor Russkikh return 0; 14452306deeSIgor Russkikh } 14552306deeSIgor Russkikh 14652306deeSIgor Russkikh static const struct devlink_param qed_devlink_params[] = { 14752306deeSIgor Russkikh DEVLINK_PARAM_DRIVER(QED_DEVLINK_PARAM_ID_IWARP_CMT, 14852306deeSIgor Russkikh "iwarp_cmt", DEVLINK_PARAM_TYPE_BOOL, 14952306deeSIgor Russkikh BIT(DEVLINK_PARAM_CMODE_RUNTIME), 15052306deeSIgor Russkikh qed_dl_param_get, qed_dl_param_set, NULL), 15152306deeSIgor Russkikh }; 15252306deeSIgor Russkikh 15353916a67SIgor Russkikh static int qed_devlink_info_get(struct devlink *devlink, 15453916a67SIgor Russkikh struct devlink_info_req *req, 15553916a67SIgor Russkikh struct netlink_ext_ack *extack) 15653916a67SIgor Russkikh { 15753916a67SIgor Russkikh struct qed_devlink *qed_dl = devlink_priv(devlink); 15853916a67SIgor Russkikh struct qed_dev *cdev = qed_dl->cdev; 15953916a67SIgor Russkikh struct qed_dev_info *dev_info; 16053916a67SIgor Russkikh char buf[100]; 16153916a67SIgor Russkikh int err; 16253916a67SIgor Russkikh 16353916a67SIgor Russkikh dev_info = &cdev->common_dev_info; 16453916a67SIgor Russkikh 16553916a67SIgor Russkikh err = devlink_info_driver_name_put(req, KBUILD_MODNAME); 16653916a67SIgor Russkikh if (err) 16753916a67SIgor Russkikh return err; 16853916a67SIgor Russkikh 16953916a67SIgor Russkikh memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num)); 17053916a67SIgor Russkikh buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0; 17153916a67SIgor Russkikh 17253916a67SIgor Russkikh if (buf[0]) { 17353916a67SIgor Russkikh err = devlink_info_board_serial_number_put(req, buf); 17453916a67SIgor Russkikh if (err) 17553916a67SIgor Russkikh return err; 17653916a67SIgor Russkikh } 17753916a67SIgor Russkikh 17853916a67SIgor Russkikh snprintf(buf, sizeof(buf), "%d.%d.%d.%d", 17953916a67SIgor Russkikh GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_3), 18053916a67SIgor Russkikh GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_2), 18153916a67SIgor Russkikh GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_1), 18253916a67SIgor Russkikh GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_0)); 18353916a67SIgor Russkikh 18453916a67SIgor Russkikh err = devlink_info_version_stored_put(req, 18553916a67SIgor Russkikh DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf); 18653916a67SIgor Russkikh if (err) 18753916a67SIgor Russkikh return err; 18853916a67SIgor Russkikh 18953916a67SIgor Russkikh snprintf(buf, sizeof(buf), "%d.%d.%d.%d", 19053916a67SIgor Russkikh dev_info->fw_major, 19153916a67SIgor Russkikh dev_info->fw_minor, 19253916a67SIgor Russkikh dev_info->fw_rev, 19353916a67SIgor Russkikh dev_info->fw_eng); 19453916a67SIgor Russkikh 19553916a67SIgor Russkikh return devlink_info_version_running_put(req, 19653916a67SIgor Russkikh DEVLINK_INFO_VERSION_GENERIC_FW_APP, buf); 19753916a67SIgor Russkikh } 19853916a67SIgor Russkikh 19953916a67SIgor Russkikh static const struct devlink_ops qed_dl_ops = { 20053916a67SIgor Russkikh .info_get = qed_devlink_info_get, 20153916a67SIgor Russkikh }; 20252306deeSIgor Russkikh 203755f982bSIgor Russkikh struct devlink *qed_devlink_register(struct qed_dev *cdev) 20452306deeSIgor Russkikh { 20552306deeSIgor Russkikh union devlink_param_value value; 206755f982bSIgor Russkikh struct qed_devlink *qdevlink; 20752306deeSIgor Russkikh struct devlink *dl; 20852306deeSIgor Russkikh int rc; 20952306deeSIgor Russkikh 210*919d13a7SLeon Romanovsky dl = devlink_alloc(&qed_dl_ops, sizeof(struct qed_devlink), 211*919d13a7SLeon Romanovsky &cdev->pdev->dev); 21252306deeSIgor Russkikh if (!dl) 213755f982bSIgor Russkikh return ERR_PTR(-ENOMEM); 21452306deeSIgor Russkikh 215755f982bSIgor Russkikh qdevlink = devlink_priv(dl); 216755f982bSIgor Russkikh qdevlink->cdev = cdev; 21752306deeSIgor Russkikh 218*919d13a7SLeon Romanovsky rc = devlink_register(dl); 21952306deeSIgor Russkikh if (rc) 22052306deeSIgor Russkikh goto err_free; 22152306deeSIgor Russkikh 22252306deeSIgor Russkikh rc = devlink_params_register(dl, qed_devlink_params, 22352306deeSIgor Russkikh ARRAY_SIZE(qed_devlink_params)); 22452306deeSIgor Russkikh if (rc) 22552306deeSIgor Russkikh goto err_unregister; 22652306deeSIgor Russkikh 22752306deeSIgor Russkikh value.vbool = false; 22852306deeSIgor Russkikh devlink_param_driverinit_value_set(dl, 22952306deeSIgor Russkikh QED_DEVLINK_PARAM_ID_IWARP_CMT, 23052306deeSIgor Russkikh value); 23152306deeSIgor Russkikh 23252306deeSIgor Russkikh devlink_params_publish(dl); 23352306deeSIgor Russkikh cdev->iwarp_cmt = false; 23452306deeSIgor Russkikh 2359524067bSIgor Russkikh qed_fw_reporters_create(dl); 2369524067bSIgor Russkikh 237755f982bSIgor Russkikh return dl; 23852306deeSIgor Russkikh 23952306deeSIgor Russkikh err_unregister: 24052306deeSIgor Russkikh devlink_unregister(dl); 24152306deeSIgor Russkikh 24252306deeSIgor Russkikh err_free: 24352306deeSIgor Russkikh devlink_free(dl); 24452306deeSIgor Russkikh 245755f982bSIgor Russkikh return ERR_PTR(rc); 24652306deeSIgor Russkikh } 24752306deeSIgor Russkikh 248755f982bSIgor Russkikh void qed_devlink_unregister(struct devlink *devlink) 24952306deeSIgor Russkikh { 250755f982bSIgor Russkikh if (!devlink) 25152306deeSIgor Russkikh return; 25252306deeSIgor Russkikh 2539524067bSIgor Russkikh qed_fw_reporters_destroy(devlink); 2549524067bSIgor Russkikh 255755f982bSIgor Russkikh devlink_params_unregister(devlink, qed_devlink_params, 25652306deeSIgor Russkikh ARRAY_SIZE(qed_devlink_params)); 25752306deeSIgor Russkikh 258755f982bSIgor Russkikh devlink_unregister(devlink); 259755f982bSIgor Russkikh devlink_free(devlink); 26052306deeSIgor Russkikh } 261