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
13enum qed_devlink_param_id {
14	QED_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
15	QED_DEVLINK_PARAM_ID_IWARP_CMT,
16};
17
18struct qed_fw_fatal_ctx {
19	enum qed_hw_err_type err_type;
20};
21
22int 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
36static int
37qed_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	err = devlink_fmsg_binary_pair_put(fmsg, "dump_data",
70					   p_dbg_data_buf, dbg_data_buf_size);
71
72	vfree(p_dbg_data_buf);
73
74	return err;
75}
76
77static int
78qed_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
90static const struct devlink_health_reporter_ops qed_fw_fatal_reporter_ops = {
91		.name = "fw_fatal",
92		.recover = qed_fw_fatal_reporter_recover,
93		.dump = qed_fw_fatal_reporter_dump,
94};
95
96#define QED_REPORTER_FW_GRACEFUL_PERIOD 1200000
97
98void qed_fw_reporters_create(struct devlink *devlink)
99{
100	struct qed_devlink *dl = devlink_priv(devlink);
101
102	dl->fw_reporter = devlink_health_reporter_create(devlink, &qed_fw_fatal_reporter_ops,
103							 QED_REPORTER_FW_GRACEFUL_PERIOD, dl);
104	if (IS_ERR(dl->fw_reporter)) {
105		DP_NOTICE(dl->cdev, "Failed to create fw reporter, err = %ld\n",
106			  PTR_ERR(dl->fw_reporter));
107		dl->fw_reporter = NULL;
108	}
109}
110
111void qed_fw_reporters_destroy(struct devlink *devlink)
112{
113	struct qed_devlink *dl = devlink_priv(devlink);
114	struct devlink_health_reporter *rep;
115
116	rep = dl->fw_reporter;
117
118	if (!IS_ERR_OR_NULL(rep))
119		devlink_health_reporter_destroy(rep);
120}
121
122static int qed_dl_param_get(struct devlink *dl, u32 id,
123			    struct devlink_param_gset_ctx *ctx)
124{
125	struct qed_devlink *qed_dl = devlink_priv(dl);
126	struct qed_dev *cdev;
127
128	cdev = qed_dl->cdev;
129	ctx->val.vbool = cdev->iwarp_cmt;
130
131	return 0;
132}
133
134static int qed_dl_param_set(struct devlink *dl, u32 id,
135			    struct devlink_param_gset_ctx *ctx)
136{
137	struct qed_devlink *qed_dl = devlink_priv(dl);
138	struct qed_dev *cdev;
139
140	cdev = qed_dl->cdev;
141	cdev->iwarp_cmt = ctx->val.vbool;
142
143	return 0;
144}
145
146static const struct devlink_param qed_devlink_params[] = {
147	DEVLINK_PARAM_DRIVER(QED_DEVLINK_PARAM_ID_IWARP_CMT,
148			     "iwarp_cmt", DEVLINK_PARAM_TYPE_BOOL,
149			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
150			     qed_dl_param_get, qed_dl_param_set, NULL),
151};
152
153static int qed_devlink_info_get(struct devlink *devlink,
154				struct devlink_info_req *req,
155				struct netlink_ext_ack *extack)
156{
157	struct qed_devlink *qed_dl = devlink_priv(devlink);
158	struct qed_dev *cdev = qed_dl->cdev;
159	struct qed_dev_info *dev_info;
160	char buf[100];
161	int err;
162
163	dev_info = &cdev->common_dev_info;
164
165	err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
166	if (err)
167		return err;
168
169	memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num));
170	buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0;
171
172	if (buf[0]) {
173		err = devlink_info_board_serial_number_put(req, buf);
174		if (err)
175			return err;
176	}
177
178	snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
179		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_3),
180		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_2),
181		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_1),
182		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_0));
183
184	err = devlink_info_version_stored_put(req,
185					      DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf);
186	if (err)
187		return err;
188
189	snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
190		 dev_info->fw_major,
191		 dev_info->fw_minor,
192		 dev_info->fw_rev,
193		 dev_info->fw_eng);
194
195	return devlink_info_version_running_put(req,
196						DEVLINK_INFO_VERSION_GENERIC_FW_APP, buf);
197}
198
199static const struct devlink_ops qed_dl_ops = {
200	.info_get = qed_devlink_info_get,
201};
202
203struct devlink *qed_devlink_register(struct qed_dev *cdev)
204{
205	union devlink_param_value value;
206	struct qed_devlink *qdevlink;
207	struct devlink *dl;
208	int rc;
209
210	dl = devlink_alloc(&qed_dl_ops, sizeof(struct qed_devlink));
211	if (!dl)
212		return ERR_PTR(-ENOMEM);
213
214	qdevlink = devlink_priv(dl);
215	qdevlink->cdev = cdev;
216
217	rc = devlink_register(dl, &cdev->pdev->dev);
218	if (rc)
219		goto err_free;
220
221	rc = devlink_params_register(dl, qed_devlink_params,
222				     ARRAY_SIZE(qed_devlink_params));
223	if (rc)
224		goto err_unregister;
225
226	value.vbool = false;
227	devlink_param_driverinit_value_set(dl,
228					   QED_DEVLINK_PARAM_ID_IWARP_CMT,
229					   value);
230
231	devlink_params_publish(dl);
232	cdev->iwarp_cmt = false;
233
234	qed_fw_reporters_create(dl);
235
236	return dl;
237
238err_unregister:
239	devlink_unregister(dl);
240
241err_free:
242	devlink_free(dl);
243
244	return ERR_PTR(rc);
245}
246
247void qed_devlink_unregister(struct devlink *devlink)
248{
249	if (!devlink)
250		return;
251
252	qed_fw_reporters_destroy(devlink);
253
254	devlink_params_unregister(devlink, qed_devlink_params,
255				  ARRAY_SIZE(qed_devlink_params));
256
257	devlink_unregister(devlink);
258	devlink_free(devlink);
259}
260