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 <linux/vmalloc.h> 10 #include "qed.h" 11 #include "qed_devlink.h" 12 13 enum qed_devlink_param_id { 14 QED_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, 15 QED_DEVLINK_PARAM_ID_IWARP_CMT, 16 }; 17 18 struct qed_fw_fatal_ctx { 19 enum qed_hw_err_type err_type; 20 }; 21 22 int qed_report_fatal_error(struct devlink *devlink, enum qed_hw_err_type err_type) 23 { 24 struct qed_devlink *qdl = devlink_priv(devlink); 25 struct qed_fw_fatal_ctx fw_fatal_ctx = { 26 .err_type = err_type, 27 }; 28 29 if (qdl->fw_reporter) 30 devlink_health_report(qdl->fw_reporter, 31 "Fatal error occurred", &fw_fatal_ctx); 32 33 return 0; 34 } 35 36 static int 37 qed_fw_fatal_reporter_dump(struct devlink_health_reporter *reporter, 38 struct devlink_fmsg *fmsg, void *priv_ctx, 39 struct netlink_ext_ack *extack) 40 { 41 struct qed_devlink *qdl = devlink_health_reporter_priv(reporter); 42 struct qed_fw_fatal_ctx *fw_fatal_ctx = priv_ctx; 43 struct qed_dev *cdev = qdl->cdev; 44 u32 dbg_data_buf_size; 45 u8 *p_dbg_data_buf; 46 int err; 47 48 /* Having context means that was a dump request after fatal, 49 * so we enable extra debugging while gathering the dump, 50 * just in case 51 */ 52 cdev->print_dbg_data = fw_fatal_ctx ? true : false; 53 54 dbg_data_buf_size = qed_dbg_all_data_size(cdev); 55 p_dbg_data_buf = vzalloc(dbg_data_buf_size); 56 if (!p_dbg_data_buf) { 57 DP_NOTICE(cdev, 58 "Failed to allocate memory for a debug data buffer\n"); 59 return -ENOMEM; 60 } 61 62 err = qed_dbg_all_data(cdev, p_dbg_data_buf); 63 if (err) { 64 DP_NOTICE(cdev, "Failed to obtain debug data\n"); 65 vfree(p_dbg_data_buf); 66 return err; 67 } 68 69 devlink_fmsg_binary_pair_put(fmsg, "dump_data", p_dbg_data_buf, 70 dbg_data_buf_size); 71 72 vfree(p_dbg_data_buf); 73 74 return 0; 75 } 76 77 static int 78 qed_fw_fatal_reporter_recover(struct devlink_health_reporter *reporter, 79 void *priv_ctx, 80 struct netlink_ext_ack *extack) 81 { 82 struct qed_devlink *qdl = devlink_health_reporter_priv(reporter); 83 struct qed_dev *cdev = qdl->cdev; 84 85 qed_recovery_process(cdev); 86 87 return 0; 88 } 89 90 #define QED_REPORTER_FW_GRACEFUL_PERIOD 0 91 92 static const struct devlink_health_reporter_ops qed_fw_fatal_reporter_ops = { 93 .name = "fw_fatal", 94 .recover = qed_fw_fatal_reporter_recover, 95 .dump = qed_fw_fatal_reporter_dump, 96 .default_graceful_period = QED_REPORTER_FW_GRACEFUL_PERIOD, 97 }; 98 99 void qed_fw_reporters_create(struct devlink *devlink) 100 { 101 struct qed_devlink *dl = devlink_priv(devlink); 102 103 dl->fw_reporter = devlink_health_reporter_create(devlink, 104 &qed_fw_fatal_reporter_ops, dl); 105 if (IS_ERR(dl->fw_reporter)) { 106 DP_NOTICE(dl->cdev, "Failed to create fw reporter, err = %ld\n", 107 PTR_ERR(dl->fw_reporter)); 108 dl->fw_reporter = NULL; 109 } 110 } 111 112 void qed_fw_reporters_destroy(struct devlink *devlink) 113 { 114 struct qed_devlink *dl = devlink_priv(devlink); 115 struct devlink_health_reporter *rep; 116 117 rep = dl->fw_reporter; 118 119 if (!IS_ERR_OR_NULL(rep)) 120 devlink_health_reporter_destroy(rep); 121 } 122 123 static int qed_dl_param_get(struct devlink *dl, u32 id, 124 struct devlink_param_gset_ctx *ctx, 125 struct netlink_ext_ack *extack) 126 { 127 struct qed_devlink *qed_dl = devlink_priv(dl); 128 struct qed_dev *cdev; 129 130 cdev = qed_dl->cdev; 131 ctx->val.vbool = cdev->iwarp_cmt; 132 133 return 0; 134 } 135 136 static int qed_dl_param_set(struct devlink *dl, u32 id, 137 struct devlink_param_gset_ctx *ctx, 138 struct netlink_ext_ack *extack) 139 { 140 struct qed_devlink *qed_dl = devlink_priv(dl); 141 struct qed_dev *cdev; 142 143 cdev = qed_dl->cdev; 144 cdev->iwarp_cmt = ctx->val.vbool; 145 146 return 0; 147 } 148 149 static const struct devlink_param qed_devlink_params[] = { 150 DEVLINK_PARAM_DRIVER(QED_DEVLINK_PARAM_ID_IWARP_CMT, 151 "iwarp_cmt", DEVLINK_PARAM_TYPE_BOOL, 152 BIT(DEVLINK_PARAM_CMODE_RUNTIME), 153 qed_dl_param_get, qed_dl_param_set, NULL), 154 }; 155 156 static int qed_devlink_info_get(struct devlink *devlink, 157 struct devlink_info_req *req, 158 struct netlink_ext_ack *extack) 159 { 160 struct qed_devlink *qed_dl = devlink_priv(devlink); 161 struct qed_dev *cdev = qed_dl->cdev; 162 struct qed_dev_info *dev_info; 163 char buf[100]; 164 int err; 165 166 dev_info = &cdev->common_dev_info; 167 168 memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num)); 169 buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0; 170 171 if (buf[0]) { 172 err = devlink_info_board_serial_number_put(req, buf); 173 if (err) 174 return err; 175 } 176 177 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", 178 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_3), 179 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_2), 180 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_1), 181 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_0)); 182 183 err = devlink_info_version_stored_put(req, 184 DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf); 185 if (err) 186 return err; 187 188 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", 189 dev_info->fw_major, 190 dev_info->fw_minor, 191 dev_info->fw_rev, 192 dev_info->fw_eng); 193 194 return devlink_info_version_running_put(req, 195 DEVLINK_INFO_VERSION_GENERIC_FW_APP, buf); 196 } 197 198 static const struct devlink_ops qed_dl_ops = { 199 .info_get = qed_devlink_info_get, 200 }; 201 202 struct devlink *qed_devlink_register(struct qed_dev *cdev) 203 { 204 struct qed_devlink *qdevlink; 205 struct devlink *dl; 206 int rc; 207 208 dl = devlink_alloc(&qed_dl_ops, sizeof(struct qed_devlink), 209 &cdev->pdev->dev); 210 if (!dl) 211 return ERR_PTR(-ENOMEM); 212 213 qdevlink = devlink_priv(dl); 214 qdevlink->cdev = cdev; 215 216 rc = devlink_params_register(dl, qed_devlink_params, 217 ARRAY_SIZE(qed_devlink_params)); 218 if (rc) 219 goto err_unregister; 220 221 cdev->iwarp_cmt = false; 222 223 qed_fw_reporters_create(dl); 224 devlink_register(dl); 225 return dl; 226 227 err_unregister: 228 devlink_free(dl); 229 230 return ERR_PTR(rc); 231 } 232 233 void qed_devlink_unregister(struct devlink *devlink) 234 { 235 if (!devlink) 236 return; 237 238 devlink_unregister(devlink); 239 qed_fw_reporters_destroy(devlink); 240 241 devlink_params_unregister(devlink, qed_devlink_params, 242 ARRAY_SIZE(qed_devlink_params)); 243 244 devlink_free(devlink); 245 } 246