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 "qed.h"
10 #include "qed_devlink.h"
11 
12 enum qed_devlink_param_id {
13 	QED_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
14 	QED_DEVLINK_PARAM_ID_IWARP_CMT,
15 };
16 
17 struct qed_fw_fatal_ctx {
18 	enum qed_hw_err_type err_type;
19 };
20 
21 int qed_report_fatal_error(struct devlink *devlink, enum qed_hw_err_type err_type)
22 {
23 	struct qed_devlink *qdl = devlink_priv(devlink);
24 	struct qed_fw_fatal_ctx fw_fatal_ctx = {
25 		.err_type = err_type,
26 	};
27 
28 	if (qdl->fw_reporter)
29 		devlink_health_report(qdl->fw_reporter,
30 				      "Fatal error occurred", &fw_fatal_ctx);
31 
32 	return 0;
33 }
34 
35 static int
36 qed_fw_fatal_reporter_recover(struct devlink_health_reporter *reporter,
37 			      void *priv_ctx,
38 			      struct netlink_ext_ack *extack)
39 {
40 	struct qed_devlink *qdl = devlink_health_reporter_priv(reporter);
41 	struct qed_dev *cdev = qdl->cdev;
42 
43 	qed_recovery_process(cdev);
44 
45 	return 0;
46 }
47 
48 static const struct devlink_health_reporter_ops qed_fw_fatal_reporter_ops = {
49 		.name = "fw_fatal",
50 		.recover = qed_fw_fatal_reporter_recover,
51 };
52 
53 #define QED_REPORTER_FW_GRACEFUL_PERIOD 1200000
54 
55 void qed_fw_reporters_create(struct devlink *devlink)
56 {
57 	struct qed_devlink *dl = devlink_priv(devlink);
58 
59 	dl->fw_reporter = devlink_health_reporter_create(devlink, &qed_fw_fatal_reporter_ops,
60 							 QED_REPORTER_FW_GRACEFUL_PERIOD, dl);
61 	if (IS_ERR(dl->fw_reporter)) {
62 		DP_NOTICE(dl->cdev, "Failed to create fw reporter, err = %ld\n",
63 			  PTR_ERR(dl->fw_reporter));
64 		dl->fw_reporter = NULL;
65 	}
66 }
67 
68 void qed_fw_reporters_destroy(struct devlink *devlink)
69 {
70 	struct qed_devlink *dl = devlink_priv(devlink);
71 	struct devlink_health_reporter *rep;
72 
73 	rep = dl->fw_reporter;
74 
75 	if (!IS_ERR_OR_NULL(rep))
76 		devlink_health_reporter_destroy(rep);
77 }
78 
79 static int qed_dl_param_get(struct devlink *dl, u32 id,
80 			    struct devlink_param_gset_ctx *ctx)
81 {
82 	struct qed_devlink *qed_dl = devlink_priv(dl);
83 	struct qed_dev *cdev;
84 
85 	cdev = qed_dl->cdev;
86 	ctx->val.vbool = cdev->iwarp_cmt;
87 
88 	return 0;
89 }
90 
91 static int qed_dl_param_set(struct devlink *dl, u32 id,
92 			    struct devlink_param_gset_ctx *ctx)
93 {
94 	struct qed_devlink *qed_dl = devlink_priv(dl);
95 	struct qed_dev *cdev;
96 
97 	cdev = qed_dl->cdev;
98 	cdev->iwarp_cmt = ctx->val.vbool;
99 
100 	return 0;
101 }
102 
103 static const struct devlink_param qed_devlink_params[] = {
104 	DEVLINK_PARAM_DRIVER(QED_DEVLINK_PARAM_ID_IWARP_CMT,
105 			     "iwarp_cmt", DEVLINK_PARAM_TYPE_BOOL,
106 			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
107 			     qed_dl_param_get, qed_dl_param_set, NULL),
108 };
109 
110 static int qed_devlink_info_get(struct devlink *devlink,
111 				struct devlink_info_req *req,
112 				struct netlink_ext_ack *extack)
113 {
114 	struct qed_devlink *qed_dl = devlink_priv(devlink);
115 	struct qed_dev *cdev = qed_dl->cdev;
116 	struct qed_dev_info *dev_info;
117 	char buf[100];
118 	int err;
119 
120 	dev_info = &cdev->common_dev_info;
121 
122 	err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
123 	if (err)
124 		return err;
125 
126 	memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num));
127 	buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0;
128 
129 	if (buf[0]) {
130 		err = devlink_info_board_serial_number_put(req, buf);
131 		if (err)
132 			return err;
133 	}
134 
135 	snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
136 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_3),
137 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_2),
138 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_1),
139 		 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_0));
140 
141 	err = devlink_info_version_stored_put(req,
142 					      DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf);
143 	if (err)
144 		return err;
145 
146 	snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
147 		 dev_info->fw_major,
148 		 dev_info->fw_minor,
149 		 dev_info->fw_rev,
150 		 dev_info->fw_eng);
151 
152 	return devlink_info_version_running_put(req,
153 						DEVLINK_INFO_VERSION_GENERIC_FW_APP, buf);
154 }
155 
156 static const struct devlink_ops qed_dl_ops = {
157 	.info_get = qed_devlink_info_get,
158 };
159 
160 struct devlink *qed_devlink_register(struct qed_dev *cdev)
161 {
162 	union devlink_param_value value;
163 	struct qed_devlink *qdevlink;
164 	struct devlink *dl;
165 	int rc;
166 
167 	dl = devlink_alloc(&qed_dl_ops, sizeof(struct qed_devlink));
168 	if (!dl)
169 		return ERR_PTR(-ENOMEM);
170 
171 	qdevlink = devlink_priv(dl);
172 	qdevlink->cdev = cdev;
173 
174 	rc = devlink_register(dl, &cdev->pdev->dev);
175 	if (rc)
176 		goto err_free;
177 
178 	rc = devlink_params_register(dl, qed_devlink_params,
179 				     ARRAY_SIZE(qed_devlink_params));
180 	if (rc)
181 		goto err_unregister;
182 
183 	value.vbool = false;
184 	devlink_param_driverinit_value_set(dl,
185 					   QED_DEVLINK_PARAM_ID_IWARP_CMT,
186 					   value);
187 
188 	devlink_params_publish(dl);
189 	cdev->iwarp_cmt = false;
190 
191 	qed_fw_reporters_create(dl);
192 
193 	return dl;
194 
195 err_unregister:
196 	devlink_unregister(dl);
197 
198 err_free:
199 	devlink_free(dl);
200 
201 	return ERR_PTR(rc);
202 }
203 
204 void qed_devlink_unregister(struct devlink *devlink)
205 {
206 	if (!devlink)
207 		return;
208 
209 	qed_fw_reporters_destroy(devlink);
210 
211 	devlink_params_unregister(devlink, qed_devlink_params,
212 				  ARRAY_SIZE(qed_devlink_params));
213 
214 	devlink_unregister(devlink);
215 	devlink_free(devlink);
216 }
217