152306deeSIgor Russkikh // SPDX-License-Identifier: GPL-2.0-or-later
252306deeSIgor Russkikh /* Marvell/Qlogic FastLinQ NIC driver
352306deeSIgor Russkikh  *
452306deeSIgor Russkikh  * Copyright (C) 2020 Marvell International Ltd.
552306deeSIgor Russkikh  */
652306deeSIgor Russkikh 
752306deeSIgor Russkikh #include <linux/kernel.h>
8755f982bSIgor Russkikh #include <linux/qed/qed_if.h>
927fed787SIgor Russkikh #include <linux/vmalloc.h>
1052306deeSIgor Russkikh #include "qed.h"
1152306deeSIgor Russkikh #include "qed_devlink.h"
1252306deeSIgor Russkikh 
1352306deeSIgor Russkikh enum qed_devlink_param_id {
1452306deeSIgor Russkikh 	QED_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
1552306deeSIgor Russkikh 	QED_DEVLINK_PARAM_ID_IWARP_CMT,
1652306deeSIgor Russkikh };
1752306deeSIgor Russkikh 
184f5a8db2SIgor Russkikh struct qed_fw_fatal_ctx {
194f5a8db2SIgor Russkikh 	enum qed_hw_err_type err_type;
204f5a8db2SIgor Russkikh };
214f5a8db2SIgor Russkikh 
qed_report_fatal_error(struct devlink * devlink,enum qed_hw_err_type err_type)224f5a8db2SIgor Russkikh int qed_report_fatal_error(struct devlink *devlink, enum qed_hw_err_type err_type)
234f5a8db2SIgor Russkikh {
244f5a8db2SIgor Russkikh 	struct qed_devlink *qdl = devlink_priv(devlink);
254f5a8db2SIgor Russkikh 	struct qed_fw_fatal_ctx fw_fatal_ctx = {
264f5a8db2SIgor Russkikh 		.err_type = err_type,
274f5a8db2SIgor Russkikh 	};
284f5a8db2SIgor Russkikh 
294f5a8db2SIgor Russkikh 	if (qdl->fw_reporter)
304f5a8db2SIgor Russkikh 		devlink_health_report(qdl->fw_reporter,
314f5a8db2SIgor Russkikh 				      "Fatal error occurred", &fw_fatal_ctx);
324f5a8db2SIgor Russkikh 
334f5a8db2SIgor Russkikh 	return 0;
344f5a8db2SIgor Russkikh }
354f5a8db2SIgor Russkikh 
36b228cb16SIgor Russkikh static int
qed_fw_fatal_reporter_dump(struct devlink_health_reporter * reporter,struct devlink_fmsg * fmsg,void * priv_ctx,struct netlink_ext_ack * extack)3727fed787SIgor Russkikh qed_fw_fatal_reporter_dump(struct devlink_health_reporter *reporter,
3827fed787SIgor Russkikh 			   struct devlink_fmsg *fmsg, void *priv_ctx,
3927fed787SIgor Russkikh 			   struct netlink_ext_ack *extack)
4027fed787SIgor Russkikh {
4127fed787SIgor Russkikh 	struct qed_devlink *qdl = devlink_health_reporter_priv(reporter);
4227fed787SIgor Russkikh 	struct qed_fw_fatal_ctx *fw_fatal_ctx = priv_ctx;
4327fed787SIgor Russkikh 	struct qed_dev *cdev = qdl->cdev;
4427fed787SIgor Russkikh 	u32 dbg_data_buf_size;
4527fed787SIgor Russkikh 	u8 *p_dbg_data_buf;
4627fed787SIgor Russkikh 	int err;
4727fed787SIgor Russkikh 
4827fed787SIgor Russkikh 	/* Having context means that was a dump request after fatal,
4927fed787SIgor Russkikh 	 * so we enable extra debugging while gathering the dump,
5027fed787SIgor Russkikh 	 * just in case
5127fed787SIgor Russkikh 	 */
5227fed787SIgor Russkikh 	cdev->print_dbg_data = fw_fatal_ctx ? true : false;
5327fed787SIgor Russkikh 
5427fed787SIgor Russkikh 	dbg_data_buf_size = qed_dbg_all_data_size(cdev);
5527fed787SIgor Russkikh 	p_dbg_data_buf = vzalloc(dbg_data_buf_size);
5627fed787SIgor Russkikh 	if (!p_dbg_data_buf) {
5727fed787SIgor Russkikh 		DP_NOTICE(cdev,
5827fed787SIgor Russkikh 			  "Failed to allocate memory for a debug data buffer\n");
5927fed787SIgor Russkikh 		return -ENOMEM;
6027fed787SIgor Russkikh 	}
6127fed787SIgor Russkikh 
6227fed787SIgor Russkikh 	err = qed_dbg_all_data(cdev, p_dbg_data_buf);
6327fed787SIgor Russkikh 	if (err) {
6427fed787SIgor Russkikh 		DP_NOTICE(cdev, "Failed to obtain debug data\n");
6527fed787SIgor Russkikh 		vfree(p_dbg_data_buf);
6627fed787SIgor Russkikh 		return err;
6727fed787SIgor Russkikh 	}
6827fed787SIgor Russkikh 
6927fed787SIgor Russkikh 	err = devlink_fmsg_binary_pair_put(fmsg, "dump_data",
7027fed787SIgor Russkikh 					   p_dbg_data_buf, dbg_data_buf_size);
7127fed787SIgor Russkikh 
7227fed787SIgor Russkikh 	vfree(p_dbg_data_buf);
7327fed787SIgor Russkikh 
7427fed787SIgor Russkikh 	return err;
7527fed787SIgor Russkikh }
7627fed787SIgor Russkikh 
7727fed787SIgor Russkikh static int
qed_fw_fatal_reporter_recover(struct devlink_health_reporter * reporter,void * priv_ctx,struct netlink_ext_ack * extack)78b228cb16SIgor Russkikh qed_fw_fatal_reporter_recover(struct devlink_health_reporter *reporter,
79b228cb16SIgor Russkikh 			      void *priv_ctx,
80b228cb16SIgor Russkikh 			      struct netlink_ext_ack *extack)
81b228cb16SIgor Russkikh {
82b228cb16SIgor Russkikh 	struct qed_devlink *qdl = devlink_health_reporter_priv(reporter);
83b228cb16SIgor Russkikh 	struct qed_dev *cdev = qdl->cdev;
84b228cb16SIgor Russkikh 
85b228cb16SIgor Russkikh 	qed_recovery_process(cdev);
86b228cb16SIgor Russkikh 
87b228cb16SIgor Russkikh 	return 0;
88b228cb16SIgor Russkikh }
89b228cb16SIgor Russkikh 
909524067bSIgor Russkikh static const struct devlink_health_reporter_ops qed_fw_fatal_reporter_ops = {
919524067bSIgor Russkikh 		.name = "fw_fatal",
92b228cb16SIgor Russkikh 		.recover = qed_fw_fatal_reporter_recover,
9327fed787SIgor Russkikh 		.dump = qed_fw_fatal_reporter_dump,
949524067bSIgor Russkikh };
959524067bSIgor Russkikh 
96755f9053SAlok Prasad #define QED_REPORTER_FW_GRACEFUL_PERIOD 0
979524067bSIgor Russkikh 
qed_fw_reporters_create(struct devlink * devlink)989524067bSIgor Russkikh void qed_fw_reporters_create(struct devlink *devlink)
999524067bSIgor Russkikh {
1009524067bSIgor Russkikh 	struct qed_devlink *dl = devlink_priv(devlink);
1019524067bSIgor Russkikh 
1029524067bSIgor Russkikh 	dl->fw_reporter = devlink_health_reporter_create(devlink, &qed_fw_fatal_reporter_ops,
1039524067bSIgor Russkikh 							 QED_REPORTER_FW_GRACEFUL_PERIOD, dl);
1049524067bSIgor Russkikh 	if (IS_ERR(dl->fw_reporter)) {
1059524067bSIgor Russkikh 		DP_NOTICE(dl->cdev, "Failed to create fw reporter, err = %ld\n",
1069524067bSIgor Russkikh 			  PTR_ERR(dl->fw_reporter));
1079524067bSIgor Russkikh 		dl->fw_reporter = NULL;
1089524067bSIgor Russkikh 	}
1099524067bSIgor Russkikh }
1109524067bSIgor Russkikh 
qed_fw_reporters_destroy(struct devlink * devlink)1119524067bSIgor Russkikh void qed_fw_reporters_destroy(struct devlink *devlink)
1129524067bSIgor Russkikh {
1139524067bSIgor Russkikh 	struct qed_devlink *dl = devlink_priv(devlink);
1149524067bSIgor Russkikh 	struct devlink_health_reporter *rep;
1159524067bSIgor Russkikh 
1169524067bSIgor Russkikh 	rep = dl->fw_reporter;
1179524067bSIgor Russkikh 
1189524067bSIgor Russkikh 	if (!IS_ERR_OR_NULL(rep))
1199524067bSIgor Russkikh 		devlink_health_reporter_destroy(rep);
1209524067bSIgor Russkikh }
1219524067bSIgor Russkikh 
qed_dl_param_get(struct devlink * dl,u32 id,struct devlink_param_gset_ctx * ctx)12252306deeSIgor Russkikh static int qed_dl_param_get(struct devlink *dl, u32 id,
12352306deeSIgor Russkikh 			    struct devlink_param_gset_ctx *ctx)
12452306deeSIgor Russkikh {
125755f982bSIgor Russkikh 	struct qed_devlink *qed_dl = devlink_priv(dl);
12652306deeSIgor Russkikh 	struct qed_dev *cdev;
12752306deeSIgor Russkikh 
12852306deeSIgor Russkikh 	cdev = qed_dl->cdev;
12952306deeSIgor Russkikh 	ctx->val.vbool = cdev->iwarp_cmt;
13052306deeSIgor Russkikh 
13152306deeSIgor Russkikh 	return 0;
13252306deeSIgor Russkikh }
13352306deeSIgor Russkikh 
qed_dl_param_set(struct devlink * dl,u32 id,struct devlink_param_gset_ctx * ctx)13452306deeSIgor Russkikh static int qed_dl_param_set(struct devlink *dl, u32 id,
13552306deeSIgor Russkikh 			    struct devlink_param_gset_ctx *ctx)
13652306deeSIgor Russkikh {
137755f982bSIgor Russkikh 	struct qed_devlink *qed_dl = devlink_priv(dl);
13852306deeSIgor Russkikh 	struct qed_dev *cdev;
13952306deeSIgor Russkikh 
14052306deeSIgor Russkikh 	cdev = qed_dl->cdev;
14152306deeSIgor Russkikh 	cdev->iwarp_cmt = ctx->val.vbool;
14252306deeSIgor Russkikh 
14352306deeSIgor Russkikh 	return 0;
14452306deeSIgor Russkikh }
14552306deeSIgor Russkikh 
14652306deeSIgor Russkikh static const struct devlink_param qed_devlink_params[] = {
14752306deeSIgor Russkikh 	DEVLINK_PARAM_DRIVER(QED_DEVLINK_PARAM_ID_IWARP_CMT,
14852306deeSIgor Russkikh 			     "iwarp_cmt", DEVLINK_PARAM_TYPE_BOOL,
14952306deeSIgor Russkikh 			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
15052306deeSIgor Russkikh 			     qed_dl_param_get, qed_dl_param_set, NULL),
15152306deeSIgor Russkikh };
15252306deeSIgor Russkikh 
qed_devlink_info_get(struct devlink * devlink,struct devlink_info_req * req,struct netlink_ext_ack * extack)15353916a67SIgor Russkikh static int qed_devlink_info_get(struct devlink *devlink,
15453916a67SIgor Russkikh 				struct devlink_info_req *req,
15553916a67SIgor Russkikh 				struct netlink_ext_ack *extack)
15653916a67SIgor Russkikh {
15753916a67SIgor Russkikh 	struct qed_devlink *qed_dl = devlink_priv(devlink);
15853916a67SIgor Russkikh 	struct qed_dev *cdev = qed_dl->cdev;
15953916a67SIgor Russkikh 	struct qed_dev_info *dev_info;
16053916a67SIgor Russkikh 	char buf[100];
16153916a67SIgor Russkikh 	int err;
16253916a67SIgor Russkikh 
16353916a67SIgor Russkikh 	dev_info = &cdev->common_dev_info;
16453916a67SIgor Russkikh 
16553916a67SIgor Russkikh 	memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num));
16653916a67SIgor Russkikh 	buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0;
16753916a67SIgor Russkikh 
16853916a67SIgor Russkikh 	if (buf[0]) {
16953916a67SIgor Russkikh 		err = devlink_info_board_serial_number_put(req, buf);
17053916a67SIgor Russkikh 		if (err)
17153916a67SIgor Russkikh 			return err;
17253916a67SIgor Russkikh 	}
17353916a67SIgor Russkikh 
17453916a67SIgor Russkikh 	snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
17553916a67SIgor Russkikh 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_3),
17653916a67SIgor Russkikh 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_2),
17753916a67SIgor Russkikh 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_1),
17853916a67SIgor Russkikh 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_0));
17953916a67SIgor Russkikh 
18053916a67SIgor Russkikh 	err = devlink_info_version_stored_put(req,
18153916a67SIgor Russkikh 					      DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf);
18253916a67SIgor Russkikh 	if (err)
18353916a67SIgor Russkikh 		return err;
18453916a67SIgor Russkikh 
18553916a67SIgor Russkikh 	snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
18653916a67SIgor Russkikh 		 dev_info->fw_major,
18753916a67SIgor Russkikh 		 dev_info->fw_minor,
18853916a67SIgor Russkikh 		 dev_info->fw_rev,
18953916a67SIgor Russkikh 		 dev_info->fw_eng);
19053916a67SIgor Russkikh 
19153916a67SIgor Russkikh 	return devlink_info_version_running_put(req,
19253916a67SIgor Russkikh 						DEVLINK_INFO_VERSION_GENERIC_FW_APP, buf);
19353916a67SIgor Russkikh }
19453916a67SIgor Russkikh 
19553916a67SIgor Russkikh static const struct devlink_ops qed_dl_ops = {
19653916a67SIgor Russkikh 	.info_get = qed_devlink_info_get,
19753916a67SIgor Russkikh };
19852306deeSIgor Russkikh 
qed_devlink_register(struct qed_dev * cdev)199755f982bSIgor Russkikh struct devlink *qed_devlink_register(struct qed_dev *cdev)
20052306deeSIgor Russkikh {
201755f982bSIgor Russkikh 	struct qed_devlink *qdevlink;
20252306deeSIgor Russkikh 	struct devlink *dl;
20352306deeSIgor Russkikh 	int rc;
20452306deeSIgor Russkikh 
205919d13a7SLeon Romanovsky 	dl = devlink_alloc(&qed_dl_ops, sizeof(struct qed_devlink),
206919d13a7SLeon Romanovsky 			   &cdev->pdev->dev);
20752306deeSIgor Russkikh 	if (!dl)
208755f982bSIgor Russkikh 		return ERR_PTR(-ENOMEM);
20952306deeSIgor Russkikh 
210755f982bSIgor Russkikh 	qdevlink = devlink_priv(dl);
211755f982bSIgor Russkikh 	qdevlink->cdev = cdev;
21252306deeSIgor Russkikh 
21352306deeSIgor Russkikh 	rc = devlink_params_register(dl, qed_devlink_params,
21452306deeSIgor Russkikh 				     ARRAY_SIZE(qed_devlink_params));
21552306deeSIgor Russkikh 	if (rc)
21652306deeSIgor Russkikh 		goto err_unregister;
21752306deeSIgor Russkikh 
21852306deeSIgor Russkikh 	cdev->iwarp_cmt = false;
21952306deeSIgor Russkikh 
2209524067bSIgor Russkikh 	qed_fw_reporters_create(dl);
221*1b8e0bdbSLeon Romanovsky 	devlink_register(dl);
222755f982bSIgor Russkikh 	return dl;
22352306deeSIgor Russkikh 
22452306deeSIgor Russkikh err_unregister:
22552306deeSIgor Russkikh 	devlink_free(dl);
22652306deeSIgor Russkikh 
227755f982bSIgor Russkikh 	return ERR_PTR(rc);
22852306deeSIgor Russkikh }
22952306deeSIgor Russkikh 
qed_devlink_unregister(struct devlink * devlink)230755f982bSIgor Russkikh void qed_devlink_unregister(struct devlink *devlink)
23152306deeSIgor Russkikh {
232755f982bSIgor Russkikh 	if (!devlink)
23352306deeSIgor Russkikh 		return;
23452306deeSIgor Russkikh 
235*1b8e0bdbSLeon Romanovsky 	devlink_unregister(devlink);
2369524067bSIgor Russkikh 	qed_fw_reporters_destroy(devlink);
2379524067bSIgor Russkikh 
238755f982bSIgor Russkikh 	devlink_params_unregister(devlink, qed_devlink_params,
23952306deeSIgor Russkikh 				  ARRAY_SIZE(qed_devlink_params));
24052306deeSIgor Russkikh 
241755f982bSIgor Russkikh 	devlink_free(devlink);
24252306deeSIgor Russkikh }
243