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>
952306deeSIgor Russkikh #include "qed.h"
1052306deeSIgor Russkikh #include "qed_devlink.h"
1152306deeSIgor Russkikh 
1252306deeSIgor Russkikh enum qed_devlink_param_id {
1352306deeSIgor Russkikh 	QED_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
1452306deeSIgor Russkikh 	QED_DEVLINK_PARAM_ID_IWARP_CMT,
1552306deeSIgor Russkikh };
1652306deeSIgor Russkikh 
174f5a8db2SIgor Russkikh struct qed_fw_fatal_ctx {
184f5a8db2SIgor Russkikh 	enum qed_hw_err_type err_type;
194f5a8db2SIgor Russkikh };
204f5a8db2SIgor Russkikh 
214f5a8db2SIgor Russkikh int qed_report_fatal_error(struct devlink *devlink, enum qed_hw_err_type err_type)
224f5a8db2SIgor Russkikh {
234f5a8db2SIgor Russkikh 	struct qed_devlink *qdl = devlink_priv(devlink);
244f5a8db2SIgor Russkikh 	struct qed_fw_fatal_ctx fw_fatal_ctx = {
254f5a8db2SIgor Russkikh 		.err_type = err_type,
264f5a8db2SIgor Russkikh 	};
274f5a8db2SIgor Russkikh 
284f5a8db2SIgor Russkikh 	if (qdl->fw_reporter)
294f5a8db2SIgor Russkikh 		devlink_health_report(qdl->fw_reporter,
304f5a8db2SIgor Russkikh 				      "Fatal error occurred", &fw_fatal_ctx);
314f5a8db2SIgor Russkikh 
324f5a8db2SIgor Russkikh 	return 0;
334f5a8db2SIgor Russkikh }
344f5a8db2SIgor Russkikh 
359524067bSIgor Russkikh static const struct devlink_health_reporter_ops qed_fw_fatal_reporter_ops = {
369524067bSIgor Russkikh 		.name = "fw_fatal",
379524067bSIgor Russkikh };
389524067bSIgor Russkikh 
399524067bSIgor Russkikh #define QED_REPORTER_FW_GRACEFUL_PERIOD 1200000
409524067bSIgor Russkikh 
419524067bSIgor Russkikh void qed_fw_reporters_create(struct devlink *devlink)
429524067bSIgor Russkikh {
439524067bSIgor Russkikh 	struct qed_devlink *dl = devlink_priv(devlink);
449524067bSIgor Russkikh 
459524067bSIgor Russkikh 	dl->fw_reporter = devlink_health_reporter_create(devlink, &qed_fw_fatal_reporter_ops,
469524067bSIgor Russkikh 							 QED_REPORTER_FW_GRACEFUL_PERIOD, dl);
479524067bSIgor Russkikh 	if (IS_ERR(dl->fw_reporter)) {
489524067bSIgor Russkikh 		DP_NOTICE(dl->cdev, "Failed to create fw reporter, err = %ld\n",
499524067bSIgor Russkikh 			  PTR_ERR(dl->fw_reporter));
509524067bSIgor Russkikh 		dl->fw_reporter = NULL;
519524067bSIgor Russkikh 	}
529524067bSIgor Russkikh }
539524067bSIgor Russkikh 
549524067bSIgor Russkikh void qed_fw_reporters_destroy(struct devlink *devlink)
559524067bSIgor Russkikh {
569524067bSIgor Russkikh 	struct qed_devlink *dl = devlink_priv(devlink);
579524067bSIgor Russkikh 	struct devlink_health_reporter *rep;
589524067bSIgor Russkikh 
599524067bSIgor Russkikh 	rep = dl->fw_reporter;
609524067bSIgor Russkikh 
619524067bSIgor Russkikh 	if (!IS_ERR_OR_NULL(rep))
629524067bSIgor Russkikh 		devlink_health_reporter_destroy(rep);
639524067bSIgor Russkikh }
649524067bSIgor Russkikh 
6552306deeSIgor Russkikh static int qed_dl_param_get(struct devlink *dl, u32 id,
6652306deeSIgor Russkikh 			    struct devlink_param_gset_ctx *ctx)
6752306deeSIgor Russkikh {
68755f982bSIgor Russkikh 	struct qed_devlink *qed_dl = devlink_priv(dl);
6952306deeSIgor Russkikh 	struct qed_dev *cdev;
7052306deeSIgor Russkikh 
7152306deeSIgor Russkikh 	cdev = qed_dl->cdev;
7252306deeSIgor Russkikh 	ctx->val.vbool = cdev->iwarp_cmt;
7352306deeSIgor Russkikh 
7452306deeSIgor Russkikh 	return 0;
7552306deeSIgor Russkikh }
7652306deeSIgor Russkikh 
7752306deeSIgor Russkikh static int qed_dl_param_set(struct devlink *dl, u32 id,
7852306deeSIgor Russkikh 			    struct devlink_param_gset_ctx *ctx)
7952306deeSIgor Russkikh {
80755f982bSIgor Russkikh 	struct qed_devlink *qed_dl = devlink_priv(dl);
8152306deeSIgor Russkikh 	struct qed_dev *cdev;
8252306deeSIgor Russkikh 
8352306deeSIgor Russkikh 	cdev = qed_dl->cdev;
8452306deeSIgor Russkikh 	cdev->iwarp_cmt = ctx->val.vbool;
8552306deeSIgor Russkikh 
8652306deeSIgor Russkikh 	return 0;
8752306deeSIgor Russkikh }
8852306deeSIgor Russkikh 
8952306deeSIgor Russkikh static const struct devlink_param qed_devlink_params[] = {
9052306deeSIgor Russkikh 	DEVLINK_PARAM_DRIVER(QED_DEVLINK_PARAM_ID_IWARP_CMT,
9152306deeSIgor Russkikh 			     "iwarp_cmt", DEVLINK_PARAM_TYPE_BOOL,
9252306deeSIgor Russkikh 			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
9352306deeSIgor Russkikh 			     qed_dl_param_get, qed_dl_param_set, NULL),
9452306deeSIgor Russkikh };
9552306deeSIgor Russkikh 
9653916a67SIgor Russkikh static int qed_devlink_info_get(struct devlink *devlink,
9753916a67SIgor Russkikh 				struct devlink_info_req *req,
9853916a67SIgor Russkikh 				struct netlink_ext_ack *extack)
9953916a67SIgor Russkikh {
10053916a67SIgor Russkikh 	struct qed_devlink *qed_dl = devlink_priv(devlink);
10153916a67SIgor Russkikh 	struct qed_dev *cdev = qed_dl->cdev;
10253916a67SIgor Russkikh 	struct qed_dev_info *dev_info;
10353916a67SIgor Russkikh 	char buf[100];
10453916a67SIgor Russkikh 	int err;
10553916a67SIgor Russkikh 
10653916a67SIgor Russkikh 	dev_info = &cdev->common_dev_info;
10753916a67SIgor Russkikh 
10853916a67SIgor Russkikh 	err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
10953916a67SIgor Russkikh 	if (err)
11053916a67SIgor Russkikh 		return err;
11153916a67SIgor Russkikh 
11253916a67SIgor Russkikh 	memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num));
11353916a67SIgor Russkikh 	buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0;
11453916a67SIgor Russkikh 
11553916a67SIgor Russkikh 	if (buf[0]) {
11653916a67SIgor Russkikh 		err = devlink_info_board_serial_number_put(req, buf);
11753916a67SIgor Russkikh 		if (err)
11853916a67SIgor Russkikh 			return err;
11953916a67SIgor Russkikh 	}
12053916a67SIgor Russkikh 
12153916a67SIgor Russkikh 	snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
12253916a67SIgor Russkikh 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_3),
12353916a67SIgor Russkikh 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_2),
12453916a67SIgor Russkikh 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_1),
12553916a67SIgor Russkikh 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_0));
12653916a67SIgor Russkikh 
12753916a67SIgor Russkikh 	err = devlink_info_version_stored_put(req,
12853916a67SIgor Russkikh 					      DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf);
12953916a67SIgor Russkikh 	if (err)
13053916a67SIgor Russkikh 		return err;
13153916a67SIgor Russkikh 
13253916a67SIgor Russkikh 	snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
13353916a67SIgor Russkikh 		 dev_info->fw_major,
13453916a67SIgor Russkikh 		 dev_info->fw_minor,
13553916a67SIgor Russkikh 		 dev_info->fw_rev,
13653916a67SIgor Russkikh 		 dev_info->fw_eng);
13753916a67SIgor Russkikh 
13853916a67SIgor Russkikh 	return devlink_info_version_running_put(req,
13953916a67SIgor Russkikh 						DEVLINK_INFO_VERSION_GENERIC_FW_APP, buf);
14053916a67SIgor Russkikh }
14153916a67SIgor Russkikh 
14253916a67SIgor Russkikh static const struct devlink_ops qed_dl_ops = {
14353916a67SIgor Russkikh 	.info_get = qed_devlink_info_get,
14453916a67SIgor Russkikh };
14552306deeSIgor Russkikh 
146755f982bSIgor Russkikh struct devlink *qed_devlink_register(struct qed_dev *cdev)
14752306deeSIgor Russkikh {
14852306deeSIgor Russkikh 	union devlink_param_value value;
149755f982bSIgor Russkikh 	struct qed_devlink *qdevlink;
15052306deeSIgor Russkikh 	struct devlink *dl;
15152306deeSIgor Russkikh 	int rc;
15252306deeSIgor Russkikh 
153755f982bSIgor Russkikh 	dl = devlink_alloc(&qed_dl_ops, sizeof(struct qed_devlink));
15452306deeSIgor Russkikh 	if (!dl)
155755f982bSIgor Russkikh 		return ERR_PTR(-ENOMEM);
15652306deeSIgor Russkikh 
157755f982bSIgor Russkikh 	qdevlink = devlink_priv(dl);
158755f982bSIgor Russkikh 	qdevlink->cdev = cdev;
15952306deeSIgor Russkikh 
16052306deeSIgor Russkikh 	rc = devlink_register(dl, &cdev->pdev->dev);
16152306deeSIgor Russkikh 	if (rc)
16252306deeSIgor Russkikh 		goto err_free;
16352306deeSIgor Russkikh 
16452306deeSIgor Russkikh 	rc = devlink_params_register(dl, qed_devlink_params,
16552306deeSIgor Russkikh 				     ARRAY_SIZE(qed_devlink_params));
16652306deeSIgor Russkikh 	if (rc)
16752306deeSIgor Russkikh 		goto err_unregister;
16852306deeSIgor Russkikh 
16952306deeSIgor Russkikh 	value.vbool = false;
17052306deeSIgor Russkikh 	devlink_param_driverinit_value_set(dl,
17152306deeSIgor Russkikh 					   QED_DEVLINK_PARAM_ID_IWARP_CMT,
17252306deeSIgor Russkikh 					   value);
17352306deeSIgor Russkikh 
17452306deeSIgor Russkikh 	devlink_params_publish(dl);
17552306deeSIgor Russkikh 	cdev->iwarp_cmt = false;
17652306deeSIgor Russkikh 
1779524067bSIgor Russkikh 	qed_fw_reporters_create(dl);
1789524067bSIgor Russkikh 
179755f982bSIgor Russkikh 	return dl;
18052306deeSIgor Russkikh 
18152306deeSIgor Russkikh err_unregister:
18252306deeSIgor Russkikh 	devlink_unregister(dl);
18352306deeSIgor Russkikh 
18452306deeSIgor Russkikh err_free:
18552306deeSIgor Russkikh 	devlink_free(dl);
18652306deeSIgor Russkikh 
187755f982bSIgor Russkikh 	return ERR_PTR(rc);
18852306deeSIgor Russkikh }
18952306deeSIgor Russkikh 
190755f982bSIgor Russkikh void qed_devlink_unregister(struct devlink *devlink)
19152306deeSIgor Russkikh {
192755f982bSIgor Russkikh 	if (!devlink)
19352306deeSIgor Russkikh 		return;
19452306deeSIgor Russkikh 
1959524067bSIgor Russkikh 	qed_fw_reporters_destroy(devlink);
1969524067bSIgor Russkikh 
197755f982bSIgor Russkikh 	devlink_params_unregister(devlink, qed_devlink_params,
19852306deeSIgor Russkikh 				  ARRAY_SIZE(qed_devlink_params));
19952306deeSIgor Russkikh 
200755f982bSIgor Russkikh 	devlink_unregister(devlink);
201755f982bSIgor Russkikh 	devlink_free(devlink);
20252306deeSIgor Russkikh }
203