1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* Marvell/Qlogic FastLinQ NIC driver 3 * 4 * Copyright (C) 2020 Marvell International Ltd. 5 */ 6 7 #include <linux/kernel.h> 8 #include <linux/qed/qed_if.h> 9 #include "qed.h" 10 #include "qed_devlink.h" 11 12 enum qed_devlink_param_id { 13 QED_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, 14 QED_DEVLINK_PARAM_ID_IWARP_CMT, 15 }; 16 17 struct qed_fw_fatal_ctx { 18 enum qed_hw_err_type err_type; 19 }; 20 21 int qed_report_fatal_error(struct devlink *devlink, enum qed_hw_err_type err_type) 22 { 23 struct qed_devlink *qdl = devlink_priv(devlink); 24 struct qed_fw_fatal_ctx fw_fatal_ctx = { 25 .err_type = err_type, 26 }; 27 28 if (qdl->fw_reporter) 29 devlink_health_report(qdl->fw_reporter, 30 "Fatal error occurred", &fw_fatal_ctx); 31 32 return 0; 33 } 34 35 static int 36 qed_fw_fatal_reporter_recover(struct devlink_health_reporter *reporter, 37 void *priv_ctx, 38 struct netlink_ext_ack *extack) 39 { 40 struct qed_devlink *qdl = devlink_health_reporter_priv(reporter); 41 struct qed_dev *cdev = qdl->cdev; 42 43 qed_recovery_process(cdev); 44 45 return 0; 46 } 47 48 static const struct devlink_health_reporter_ops qed_fw_fatal_reporter_ops = { 49 .name = "fw_fatal", 50 .recover = qed_fw_fatal_reporter_recover, 51 }; 52 53 #define QED_REPORTER_FW_GRACEFUL_PERIOD 1200000 54 55 void qed_fw_reporters_create(struct devlink *devlink) 56 { 57 struct qed_devlink *dl = devlink_priv(devlink); 58 59 dl->fw_reporter = devlink_health_reporter_create(devlink, &qed_fw_fatal_reporter_ops, 60 QED_REPORTER_FW_GRACEFUL_PERIOD, dl); 61 if (IS_ERR(dl->fw_reporter)) { 62 DP_NOTICE(dl->cdev, "Failed to create fw reporter, err = %ld\n", 63 PTR_ERR(dl->fw_reporter)); 64 dl->fw_reporter = NULL; 65 } 66 } 67 68 void qed_fw_reporters_destroy(struct devlink *devlink) 69 { 70 struct qed_devlink *dl = devlink_priv(devlink); 71 struct devlink_health_reporter *rep; 72 73 rep = dl->fw_reporter; 74 75 if (!IS_ERR_OR_NULL(rep)) 76 devlink_health_reporter_destroy(rep); 77 } 78 79 static int qed_dl_param_get(struct devlink *dl, u32 id, 80 struct devlink_param_gset_ctx *ctx) 81 { 82 struct qed_devlink *qed_dl = devlink_priv(dl); 83 struct qed_dev *cdev; 84 85 cdev = qed_dl->cdev; 86 ctx->val.vbool = cdev->iwarp_cmt; 87 88 return 0; 89 } 90 91 static int qed_dl_param_set(struct devlink *dl, u32 id, 92 struct devlink_param_gset_ctx *ctx) 93 { 94 struct qed_devlink *qed_dl = devlink_priv(dl); 95 struct qed_dev *cdev; 96 97 cdev = qed_dl->cdev; 98 cdev->iwarp_cmt = ctx->val.vbool; 99 100 return 0; 101 } 102 103 static const struct devlink_param qed_devlink_params[] = { 104 DEVLINK_PARAM_DRIVER(QED_DEVLINK_PARAM_ID_IWARP_CMT, 105 "iwarp_cmt", DEVLINK_PARAM_TYPE_BOOL, 106 BIT(DEVLINK_PARAM_CMODE_RUNTIME), 107 qed_dl_param_get, qed_dl_param_set, NULL), 108 }; 109 110 static int qed_devlink_info_get(struct devlink *devlink, 111 struct devlink_info_req *req, 112 struct netlink_ext_ack *extack) 113 { 114 struct qed_devlink *qed_dl = devlink_priv(devlink); 115 struct qed_dev *cdev = qed_dl->cdev; 116 struct qed_dev_info *dev_info; 117 char buf[100]; 118 int err; 119 120 dev_info = &cdev->common_dev_info; 121 122 err = devlink_info_driver_name_put(req, KBUILD_MODNAME); 123 if (err) 124 return err; 125 126 memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num)); 127 buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0; 128 129 if (buf[0]) { 130 err = devlink_info_board_serial_number_put(req, buf); 131 if (err) 132 return err; 133 } 134 135 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", 136 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_3), 137 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_2), 138 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_1), 139 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_0)); 140 141 err = devlink_info_version_stored_put(req, 142 DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf); 143 if (err) 144 return err; 145 146 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", 147 dev_info->fw_major, 148 dev_info->fw_minor, 149 dev_info->fw_rev, 150 dev_info->fw_eng); 151 152 return devlink_info_version_running_put(req, 153 DEVLINK_INFO_VERSION_GENERIC_FW_APP, buf); 154 } 155 156 static const struct devlink_ops qed_dl_ops = { 157 .info_get = qed_devlink_info_get, 158 }; 159 160 struct devlink *qed_devlink_register(struct qed_dev *cdev) 161 { 162 union devlink_param_value value; 163 struct qed_devlink *qdevlink; 164 struct devlink *dl; 165 int rc; 166 167 dl = devlink_alloc(&qed_dl_ops, sizeof(struct qed_devlink)); 168 if (!dl) 169 return ERR_PTR(-ENOMEM); 170 171 qdevlink = devlink_priv(dl); 172 qdevlink->cdev = cdev; 173 174 rc = devlink_register(dl, &cdev->pdev->dev); 175 if (rc) 176 goto err_free; 177 178 rc = devlink_params_register(dl, qed_devlink_params, 179 ARRAY_SIZE(qed_devlink_params)); 180 if (rc) 181 goto err_unregister; 182 183 value.vbool = false; 184 devlink_param_driverinit_value_set(dl, 185 QED_DEVLINK_PARAM_ID_IWARP_CMT, 186 value); 187 188 devlink_params_publish(dl); 189 cdev->iwarp_cmt = false; 190 191 qed_fw_reporters_create(dl); 192 193 return dl; 194 195 err_unregister: 196 devlink_unregister(dl); 197 198 err_free: 199 devlink_free(dl); 200 201 return ERR_PTR(rc); 202 } 203 204 void qed_devlink_unregister(struct devlink *devlink) 205 { 206 if (!devlink) 207 return; 208 209 qed_fw_reporters_destroy(devlink); 210 211 devlink_params_unregister(devlink, qed_devlink_params, 212 ARRAY_SIZE(qed_devlink_params)); 213 214 devlink_unregister(devlink); 215 devlink_free(devlink); 216 } 217