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
13 enum qed_devlink_param_id {
14 QED_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
15 QED_DEVLINK_PARAM_ID_IWARP_CMT,
16 };
17
18 struct qed_fw_fatal_ctx {
19 enum qed_hw_err_type err_type;
20 };
21
qed_report_fatal_error(struct devlink * devlink,enum qed_hw_err_type err_type)22 int 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
36 static int
qed_fw_fatal_reporter_dump(struct devlink_health_reporter * reporter,struct devlink_fmsg * fmsg,void * priv_ctx,struct netlink_ext_ack * extack)37 qed_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
77 static int
qed_fw_fatal_reporter_recover(struct devlink_health_reporter * reporter,void * priv_ctx,struct netlink_ext_ack * extack)78 qed_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
90 static 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
qed_fw_reporters_create(struct devlink * devlink)98 void 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
qed_fw_reporters_destroy(struct devlink * devlink)111 void 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
qed_dl_param_get(struct devlink * dl,u32 id,struct devlink_param_gset_ctx * ctx)122 static 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
qed_dl_param_set(struct devlink * dl,u32 id,struct devlink_param_gset_ctx * ctx,struct netlink_ext_ack * extack)134 static 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
147 static 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
qed_devlink_info_get(struct devlink * devlink,struct devlink_info_req * req,struct netlink_ext_ack * extack)154 static 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
196 static const struct devlink_ops qed_dl_ops = {
197 .info_get = qed_devlink_info_get,
198 };
199
qed_devlink_register(struct qed_dev * cdev)200 struct 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
225 err_unregister:
226 devlink_free(dl);
227
228 return ERR_PTR(rc);
229 }
230
qed_devlink_unregister(struct devlink * devlink)231 void 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 }
244