xref: /linux/drivers/net/ethernet/qlogic/qed/qed_devlink.c (revision a7ddedc84c59a645ef970b992f7cda5bffc70cc0)
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 {
126 	struct qed_devlink *qed_dl = devlink_priv(dl);
127 	struct qed_dev *cdev;
128 
129 	cdev = qed_dl->cdev;
130 	ctx->val.vbool = cdev->iwarp_cmt;
131 
132 	return 0;
133 }
134 
135 static int qed_dl_param_set(struct devlink *dl, u32 id,
136 			    struct devlink_param_gset_ctx *ctx,
137 			    struct netlink_ext_ack *extack)
138 {
139 	struct qed_devlink *qed_dl = devlink_priv(dl);
140 	struct qed_dev *cdev;
141 
142 	cdev = qed_dl->cdev;
143 	cdev->iwarp_cmt = ctx->val.vbool;
144 
145 	return 0;
146 }
147 
148 static const struct devlink_param qed_devlink_params[] = {
149 	DEVLINK_PARAM_DRIVER(QED_DEVLINK_PARAM_ID_IWARP_CMT,
150 			     "iwarp_cmt", DEVLINK_PARAM_TYPE_BOOL,
151 			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
152 			     qed_dl_param_get, qed_dl_param_set, NULL),
153 };
154 
155 static int qed_devlink_info_get(struct devlink *devlink,
156 				struct devlink_info_req *req,
157 				struct netlink_ext_ack *extack)
158 {
159 	struct qed_devlink *qed_dl = devlink_priv(devlink);
160 	struct qed_dev *cdev = qed_dl->cdev;
161 	struct qed_dev_info *dev_info;
162 	char buf[100];
163 	int err;
164 
165 	dev_info = &cdev->common_dev_info;
166 
167 	memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num));
168 	buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0;
169 
170 	if (buf[0]) {
171 		err = devlink_info_board_serial_number_put(req, buf);
172 		if (err)
173 			return err;
174 	}
175 
176 	snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
177 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_3),
178 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_2),
179 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_1),
180 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_0));
181 
182 	err = devlink_info_version_stored_put(req,
183 					      DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf);
184 	if (err)
185 		return err;
186 
187 	snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
188 		 dev_info->fw_major,
189 		 dev_info->fw_minor,
190 		 dev_info->fw_rev,
191 		 dev_info->fw_eng);
192 
193 	return devlink_info_version_running_put(req,
194 						DEVLINK_INFO_VERSION_GENERIC_FW_APP, buf);
195 }
196 
197 static const struct devlink_ops qed_dl_ops = {
198 	.info_get = qed_devlink_info_get,
199 };
200 
201 struct devlink *qed_devlink_register(struct qed_dev *cdev)
202 {
203 	struct qed_devlink *qdevlink;
204 	struct devlink *dl;
205 	int rc;
206 
207 	dl = devlink_alloc(&qed_dl_ops, sizeof(struct qed_devlink),
208 			   &cdev->pdev->dev);
209 	if (!dl)
210 		return ERR_PTR(-ENOMEM);
211 
212 	qdevlink = devlink_priv(dl);
213 	qdevlink->cdev = cdev;
214 
215 	rc = devlink_params_register(dl, qed_devlink_params,
216 				     ARRAY_SIZE(qed_devlink_params));
217 	if (rc)
218 		goto err_unregister;
219 
220 	cdev->iwarp_cmt = false;
221 
222 	qed_fw_reporters_create(dl);
223 	devlink_register(dl);
224 	return dl;
225 
226 err_unregister:
227 	devlink_free(dl);
228 
229 	return ERR_PTR(rc);
230 }
231 
232 void qed_devlink_unregister(struct devlink *devlink)
233 {
234 	if (!devlink)
235 		return;
236 
237 	devlink_unregister(devlink);
238 	qed_fw_reporters_destroy(devlink);
239 
240 	devlink_params_unregister(devlink, qed_devlink_params,
241 				  ARRAY_SIZE(qed_devlink_params));
242 
243 	devlink_free(devlink);
244 }
245