Linux Audio

Check our new training course

Real-Time Linux with PREEMPT_RT training

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