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(reporter: qdl->fw_reporter,
31 msg: "Fatal error occurred", priv_ctx: &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, buffer: p_dbg_data_buf);
63 if (err) {
64 DP_NOTICE(cdev, "Failed to obtain debug data\n");
65 vfree(addr: p_dbg_data_buf);
66 return err;
67 }
68
69 devlink_fmsg_binary_pair_put(fmsg, name: "dump_data", value: p_dbg_data_buf,
70 value_len: dbg_data_buf_size);
71
72 vfree(addr: 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
90#define QED_REPORTER_FW_GRACEFUL_PERIOD 0
91
92static const struct devlink_health_reporter_ops qed_fw_fatal_reporter_ops = {
93 .name = "fw_fatal",
94 .recover = qed_fw_fatal_reporter_recover,
95 .dump = qed_fw_fatal_reporter_dump,
96 .default_graceful_period = QED_REPORTER_FW_GRACEFUL_PERIOD,
97};
98
99void qed_fw_reporters_create(struct devlink *devlink)
100{
101 struct qed_devlink *dl = devlink_priv(devlink);
102
103 dl->fw_reporter = devlink_health_reporter_create(devlink,
104 ops: &qed_fw_fatal_reporter_ops, priv: dl);
105 if (IS_ERR(ptr: dl->fw_reporter)) {
106 DP_NOTICE(dl->cdev, "Failed to create fw reporter, err = %ld\n",
107 PTR_ERR(dl->fw_reporter));
108 dl->fw_reporter = NULL;
109 }
110}
111
112void qed_fw_reporters_destroy(struct devlink *devlink)
113{
114 struct qed_devlink *dl = devlink_priv(devlink);
115 struct devlink_health_reporter *rep;
116
117 rep = dl->fw_reporter;
118
119 if (!IS_ERR_OR_NULL(ptr: rep))
120 devlink_health_reporter_destroy(reporter: rep);
121}
122
123static int qed_dl_param_get(struct devlink *dl, u32 id,
124 struct devlink_param_gset_ctx *ctx,
125 struct netlink_ext_ack *extack)
126{
127 struct qed_devlink *qed_dl = devlink_priv(devlink: dl);
128 struct qed_dev *cdev;
129
130 cdev = qed_dl->cdev;
131 ctx->val.vbool = cdev->iwarp_cmt;
132
133 return 0;
134}
135
136static int qed_dl_param_set(struct devlink *dl, u32 id,
137 struct devlink_param_gset_ctx *ctx,
138 struct netlink_ext_ack *extack)
139{
140 struct qed_devlink *qed_dl = devlink_priv(devlink: dl);
141 struct qed_dev *cdev;
142
143 cdev = qed_dl->cdev;
144 cdev->iwarp_cmt = ctx->val.vbool;
145
146 return 0;
147}
148
149static const struct devlink_param qed_devlink_params[] = {
150 DEVLINK_PARAM_DRIVER(QED_DEVLINK_PARAM_ID_IWARP_CMT,
151 "iwarp_cmt", DEVLINK_PARAM_TYPE_BOOL,
152 BIT(DEVLINK_PARAM_CMODE_RUNTIME),
153 qed_dl_param_get, qed_dl_param_set, NULL),
154};
155
156static int qed_devlink_info_get(struct devlink *devlink,
157 struct devlink_info_req *req,
158 struct netlink_ext_ack *extack)
159{
160 struct qed_devlink *qed_dl = devlink_priv(devlink);
161 struct qed_dev *cdev = qed_dl->cdev;
162 struct qed_dev_info *dev_info;
163 char buf[100];
164 int err;
165
166 dev_info = &cdev->common_dev_info;
167
168 memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num));
169 buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0;
170
171 if (buf[0]) {
172 err = devlink_info_board_serial_number_put(req, bsn: buf);
173 if (err)
174 return err;
175 }
176
177 snprintf(buf, size: sizeof(buf), fmt: "%d.%d.%d.%d",
178 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_3),
179 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_2),
180 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_1),
181 GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_0));
182
183 err = devlink_info_version_stored_put(req,
184 DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, version_value: buf);
185 if (err)
186 return err;
187
188 snprintf(buf, size: sizeof(buf), fmt: "%d.%d.%d.%d",
189 dev_info->fw_major,
190 dev_info->fw_minor,
191 dev_info->fw_rev,
192 dev_info->fw_eng);
193
194 return devlink_info_version_running_put(req,
195 DEVLINK_INFO_VERSION_GENERIC_FW_APP, version_value: buf);
196}
197
198static const struct devlink_ops qed_dl_ops = {
199 .info_get = qed_devlink_info_get,
200};
201
202struct devlink *qed_devlink_register(struct qed_dev *cdev)
203{
204 struct qed_devlink *qdevlink;
205 struct devlink *dl;
206 int rc;
207
208 dl = devlink_alloc(ops: &qed_dl_ops, priv_size: sizeof(struct qed_devlink),
209 dev: &cdev->pdev->dev);
210 if (!dl)
211 return ERR_PTR(error: -ENOMEM);
212
213 qdevlink = devlink_priv(devlink: dl);
214 qdevlink->cdev = cdev;
215
216 rc = devlink_params_register(devlink: dl, params: qed_devlink_params,
217 ARRAY_SIZE(qed_devlink_params));
218 if (rc)
219 goto err_unregister;
220
221 cdev->iwarp_cmt = false;
222
223 qed_fw_reporters_create(devlink: dl);
224 devlink_register(devlink: dl);
225 return dl;
226
227err_unregister:
228 devlink_free(devlink: dl);
229
230 return ERR_PTR(error: rc);
231}
232
233void qed_devlink_unregister(struct devlink *devlink)
234{
235 if (!devlink)
236 return;
237
238 devlink_unregister(devlink);
239 qed_fw_reporters_destroy(devlink);
240
241 devlink_params_unregister(devlink, params: qed_devlink_params,
242 ARRAY_SIZE(qed_devlink_params));
243
244 devlink_free(devlink);
245}
246

source code of linux/drivers/net/ethernet/qlogic/qed/qed_devlink.c