GNU Linux-libre 5.10.215-gnu1
[releases.git] / drivers / net / ethernet / qlogic / qed / qed_devlink.c
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
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
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         err = devlink_fmsg_binary_pair_put(fmsg, "dump_data",
70                                            p_dbg_data_buf, dbg_data_buf_size);
71
72         vfree(p_dbg_data_buf);
73
74         return err;
75 }
76
77 static int
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 1200000
97
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
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
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
134 static int qed_dl_param_set(struct devlink *dl, u32 id,
135                             struct devlink_param_gset_ctx *ctx)
136 {
137         struct qed_devlink *qed_dl = devlink_priv(dl);
138         struct qed_dev *cdev;
139
140         cdev = qed_dl->cdev;
141         cdev->iwarp_cmt = ctx->val.vbool;
142
143         return 0;
144 }
145
146 static const struct devlink_param qed_devlink_params[] = {
147         DEVLINK_PARAM_DRIVER(QED_DEVLINK_PARAM_ID_IWARP_CMT,
148                              "iwarp_cmt", DEVLINK_PARAM_TYPE_BOOL,
149                              BIT(DEVLINK_PARAM_CMODE_RUNTIME),
150                              qed_dl_param_get, qed_dl_param_set, NULL),
151 };
152
153 static int qed_devlink_info_get(struct devlink *devlink,
154                                 struct devlink_info_req *req,
155                                 struct netlink_ext_ack *extack)
156 {
157         struct qed_devlink *qed_dl = devlink_priv(devlink);
158         struct qed_dev *cdev = qed_dl->cdev;
159         struct qed_dev_info *dev_info;
160         char buf[100];
161         int err;
162
163         dev_info = &cdev->common_dev_info;
164
165         err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
166         if (err)
167                 return err;
168
169         memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num));
170         buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0;
171
172         if (buf[0]) {
173                 err = devlink_info_board_serial_number_put(req, buf);
174                 if (err)
175                         return err;
176         }
177
178         snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
179                  GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_3),
180                  GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_2),
181                  GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_1),
182                  GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_0));
183
184         err = devlink_info_version_stored_put(req,
185                                               DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf);
186         if (err)
187                 return err;
188
189         snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
190                  dev_info->fw_major,
191                  dev_info->fw_minor,
192                  dev_info->fw_rev,
193                  dev_info->fw_eng);
194
195         return devlink_info_version_running_put(req,
196                                                 DEVLINK_INFO_VERSION_GENERIC_FW_APP, buf);
197 }
198
199 static const struct devlink_ops qed_dl_ops = {
200         .info_get = qed_devlink_info_get,
201 };
202
203 struct devlink *qed_devlink_register(struct qed_dev *cdev)
204 {
205         union devlink_param_value value;
206         struct qed_devlink *qdevlink;
207         struct devlink *dl;
208         int rc;
209
210         dl = devlink_alloc(&qed_dl_ops, sizeof(struct qed_devlink));
211         if (!dl)
212                 return ERR_PTR(-ENOMEM);
213
214         qdevlink = devlink_priv(dl);
215         qdevlink->cdev = cdev;
216
217         rc = devlink_register(dl, &cdev->pdev->dev);
218         if (rc)
219                 goto err_free;
220
221         rc = devlink_params_register(dl, qed_devlink_params,
222                                      ARRAY_SIZE(qed_devlink_params));
223         if (rc)
224                 goto err_unregister;
225
226         value.vbool = false;
227         devlink_param_driverinit_value_set(dl,
228                                            QED_DEVLINK_PARAM_ID_IWARP_CMT,
229                                            value);
230
231         devlink_params_publish(dl);
232         cdev->iwarp_cmt = false;
233
234         qed_fw_reporters_create(dl);
235
236         return dl;
237
238 err_unregister:
239         devlink_unregister(dl);
240
241 err_free:
242         devlink_free(dl);
243
244         return ERR_PTR(rc);
245 }
246
247 void qed_devlink_unregister(struct devlink *devlink)
248 {
249         if (!devlink)
250                 return;
251
252         qed_fw_reporters_destroy(devlink);
253
254         devlink_params_unregister(devlink, qed_devlink_params,
255                                   ARRAY_SIZE(qed_devlink_params));
256
257         devlink_unregister(devlink);
258         devlink_free(devlink);
259 }