bnxt_en: Add devlink health reset reporter.
[linux-2.6-microblaze.git] / drivers / net / ethernet / broadcom / bnxt / bnxt_devlink.c
1 /* Broadcom NetXtreme-C/E network driver.
2  *
3  * Copyright (c) 2017 Broadcom Limited
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation.
8  */
9
10 #include <linux/pci.h>
11 #include <linux/netdevice.h>
12 #include <net/devlink.h>
13 #include "bnxt_hsi.h"
14 #include "bnxt.h"
15 #include "bnxt_vfr.h"
16 #include "bnxt_devlink.h"
17
18 static int bnxt_fw_reporter_diagnose(struct devlink_health_reporter *reporter,
19                                      struct devlink_fmsg *fmsg)
20 {
21         struct bnxt *bp = devlink_health_reporter_priv(reporter);
22         struct bnxt_fw_health *health = bp->fw_health;
23         u32 val, health_status;
24         int rc;
25
26         if (!health || test_bit(BNXT_STATE_IN_FW_RESET, &bp->state))
27                 return 0;
28
29         val = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG);
30         health_status = val & 0xffff;
31
32         if (health_status == BNXT_FW_STATUS_HEALTHY) {
33                 rc = devlink_fmsg_string_pair_put(fmsg, "FW status",
34                                                   "Healthy;");
35                 if (rc)
36                         return rc;
37         } else if (health_status < BNXT_FW_STATUS_HEALTHY) {
38                 rc = devlink_fmsg_string_pair_put(fmsg, "FW status",
39                                                   "Not yet completed initialization;");
40                 if (rc)
41                         return rc;
42         } else if (health_status > BNXT_FW_STATUS_HEALTHY) {
43                 rc = devlink_fmsg_string_pair_put(fmsg, "FW status",
44                                                   "Encountered fatal error and cannot recover;");
45                 if (rc)
46                         return rc;
47         }
48
49         if (val >> 16) {
50                 rc = devlink_fmsg_u32_pair_put(fmsg, "Error", val >> 16);
51                 if (rc)
52                         return rc;
53         }
54
55         val = bnxt_fw_health_readl(bp, BNXT_FW_RESET_CNT_REG);
56         rc = devlink_fmsg_u32_pair_put(fmsg, "Reset count", val);
57         if (rc)
58                 return rc;
59
60         return 0;
61 }
62
63 static const struct devlink_health_reporter_ops bnxt_dl_fw_reporter_ops = {
64         .name = "fw",
65         .diagnose = bnxt_fw_reporter_diagnose,
66 };
67
68 static int bnxt_fw_reset_recover(struct devlink_health_reporter *reporter,
69                                  void *priv_ctx)
70 {
71         struct bnxt *bp = devlink_health_reporter_priv(reporter);
72
73         if (!priv_ctx)
74                 return -EOPNOTSUPP;
75
76         bnxt_fw_reset(bp);
77         return 0;
78 }
79
80 static const
81 struct devlink_health_reporter_ops bnxt_dl_fw_reset_reporter_ops = {
82         .name = "fw_reset",
83         .recover = bnxt_fw_reset_recover,
84 };
85
86 static void bnxt_dl_fw_reporters_create(struct bnxt *bp)
87 {
88         struct bnxt_fw_health *health = bp->fw_health;
89
90         if (!health)
91                 return;
92
93         health->fw_reporter =
94                 devlink_health_reporter_create(bp->dl, &bnxt_dl_fw_reporter_ops,
95                                                0, false, bp);
96         if (IS_ERR(health->fw_reporter)) {
97                 netdev_warn(bp->dev, "Failed to create FW health reporter, rc = %ld\n",
98                             PTR_ERR(health->fw_reporter));
99                 health->fw_reporter = NULL;
100         }
101
102         health->fw_reset_reporter =
103                 devlink_health_reporter_create(bp->dl,
104                                                &bnxt_dl_fw_reset_reporter_ops,
105                                                0, true, bp);
106         if (IS_ERR(health->fw_reset_reporter)) {
107                 netdev_warn(bp->dev, "Failed to create FW fatal health reporter, rc = %ld\n",
108                             PTR_ERR(health->fw_reset_reporter));
109                 health->fw_reset_reporter = NULL;
110         }
111 }
112
113 static void bnxt_dl_fw_reporters_destroy(struct bnxt *bp)
114 {
115         struct bnxt_fw_health *health = bp->fw_health;
116
117         if (!health)
118                 return;
119
120         if (health->fw_reporter)
121                 devlink_health_reporter_destroy(health->fw_reporter);
122
123         if (health->fw_reset_reporter)
124                 devlink_health_reporter_destroy(health->fw_reset_reporter);
125 }
126
127 void bnxt_devlink_health_report(struct bnxt *bp, unsigned long event)
128 {
129         struct bnxt_fw_health *fw_health = bp->fw_health;
130         struct bnxt_fw_reporter_ctx fw_reporter_ctx;
131
132         if (!fw_health)
133                 return;
134
135         fw_reporter_ctx.sp_event = event;
136         switch (event) {
137         case BNXT_FW_RESET_NOTIFY_SP_EVENT:
138                 if (!fw_health->fw_reset_reporter)
139                         return;
140
141                 devlink_health_report(fw_health->fw_reset_reporter,
142                                       "FW non-fatal reset event received",
143                                       &fw_reporter_ctx);
144                 return;
145         }
146 }
147
148 static const struct devlink_ops bnxt_dl_ops = {
149 #ifdef CONFIG_BNXT_SRIOV
150         .eswitch_mode_set = bnxt_dl_eswitch_mode_set,
151         .eswitch_mode_get = bnxt_dl_eswitch_mode_get,
152 #endif /* CONFIG_BNXT_SRIOV */
153 };
154
155 enum bnxt_dl_param_id {
156         BNXT_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
157         BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK,
158 };
159
160 static const struct bnxt_dl_nvm_param nvm_params[] = {
161         {DEVLINK_PARAM_GENERIC_ID_ENABLE_SRIOV, NVM_OFF_ENABLE_SRIOV,
162          BNXT_NVM_SHARED_CFG, 1},
163         {DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI, NVM_OFF_IGNORE_ARI,
164          BNXT_NVM_SHARED_CFG, 1},
165         {DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX,
166          NVM_OFF_MSIX_VEC_PER_PF_MAX, BNXT_NVM_SHARED_CFG, 10},
167         {DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN,
168          NVM_OFF_MSIX_VEC_PER_PF_MIN, BNXT_NVM_SHARED_CFG, 7},
169         {BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK, NVM_OFF_DIS_GRE_VER_CHECK,
170          BNXT_NVM_SHARED_CFG, 1},
171 };
172
173 static int bnxt_hwrm_nvm_req(struct bnxt *bp, u32 param_id, void *msg,
174                              int msg_len, union devlink_param_value *val)
175 {
176         struct hwrm_nvm_get_variable_input *req = msg;
177         void *data_addr = NULL, *buf = NULL;
178         struct bnxt_dl_nvm_param nvm_param;
179         int bytesize, idx = 0, rc, i;
180         dma_addr_t data_dma_addr;
181
182         /* Get/Set NVM CFG parameter is supported only on PFs */
183         if (BNXT_VF(bp))
184                 return -EPERM;
185
186         for (i = 0; i < ARRAY_SIZE(nvm_params); i++) {
187                 if (nvm_params[i].id == param_id) {
188                         nvm_param = nvm_params[i];
189                         break;
190                 }
191         }
192
193         if (i == ARRAY_SIZE(nvm_params))
194                 return -EOPNOTSUPP;
195
196         if (nvm_param.dir_type == BNXT_NVM_PORT_CFG)
197                 idx = bp->pf.port_id;
198         else if (nvm_param.dir_type == BNXT_NVM_FUNC_CFG)
199                 idx = bp->pf.fw_fid - BNXT_FIRST_PF_FID;
200
201         bytesize = roundup(nvm_param.num_bits, BITS_PER_BYTE) / BITS_PER_BYTE;
202         switch (bytesize) {
203         case 1:
204                 if (nvm_param.num_bits == 1)
205                         buf = &val->vbool;
206                 else
207                         buf = &val->vu8;
208                 break;
209         case 2:
210                 buf = &val->vu16;
211                 break;
212         case 4:
213                 buf = &val->vu32;
214                 break;
215         default:
216                 return -EFAULT;
217         }
218
219         data_addr = dma_alloc_coherent(&bp->pdev->dev, bytesize,
220                                        &data_dma_addr, GFP_KERNEL);
221         if (!data_addr)
222                 return -ENOMEM;
223
224         req->dest_data_addr = cpu_to_le64(data_dma_addr);
225         req->data_len = cpu_to_le16(nvm_param.num_bits);
226         req->option_num = cpu_to_le16(nvm_param.offset);
227         req->index_0 = cpu_to_le16(idx);
228         if (idx)
229                 req->dimensions = cpu_to_le16(1);
230
231         if (req->req_type == cpu_to_le16(HWRM_NVM_SET_VARIABLE)) {
232                 memcpy(data_addr, buf, bytesize);
233                 rc = hwrm_send_message(bp, msg, msg_len, HWRM_CMD_TIMEOUT);
234         } else {
235                 rc = hwrm_send_message_silent(bp, msg, msg_len,
236                                               HWRM_CMD_TIMEOUT);
237         }
238         if (!rc && req->req_type == cpu_to_le16(HWRM_NVM_GET_VARIABLE))
239                 memcpy(buf, data_addr, bytesize);
240
241         dma_free_coherent(&bp->pdev->dev, bytesize, data_addr, data_dma_addr);
242         if (rc == -EACCES)
243                 netdev_err(bp->dev, "PF does not have admin privileges to modify NVM config\n");
244         return rc;
245 }
246
247 static int bnxt_dl_nvm_param_get(struct devlink *dl, u32 id,
248                                  struct devlink_param_gset_ctx *ctx)
249 {
250         struct hwrm_nvm_get_variable_input req = {0};
251         struct bnxt *bp = bnxt_get_bp_from_dl(dl);
252         int rc;
253
254         bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_NVM_GET_VARIABLE, -1, -1);
255         rc = bnxt_hwrm_nvm_req(bp, id, &req, sizeof(req), &ctx->val);
256         if (!rc)
257                 if (id == BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK)
258                         ctx->val.vbool = !ctx->val.vbool;
259
260         return rc;
261 }
262
263 static int bnxt_dl_nvm_param_set(struct devlink *dl, u32 id,
264                                  struct devlink_param_gset_ctx *ctx)
265 {
266         struct hwrm_nvm_set_variable_input req = {0};
267         struct bnxt *bp = bnxt_get_bp_from_dl(dl);
268
269         bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_NVM_SET_VARIABLE, -1, -1);
270
271         if (id == BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK)
272                 ctx->val.vbool = !ctx->val.vbool;
273
274         return bnxt_hwrm_nvm_req(bp, id, &req, sizeof(req), &ctx->val);
275 }
276
277 static int bnxt_dl_msix_validate(struct devlink *dl, u32 id,
278                                  union devlink_param_value val,
279                                  struct netlink_ext_ack *extack)
280 {
281         int max_val = -1;
282
283         if (id == DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX)
284                 max_val = BNXT_MSIX_VEC_MAX;
285
286         if (id == DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN)
287                 max_val = BNXT_MSIX_VEC_MIN_MAX;
288
289         if (val.vu32 > max_val) {
290                 NL_SET_ERR_MSG_MOD(extack, "MSIX value is exceeding the range");
291                 return -EINVAL;
292         }
293
294         return 0;
295 }
296
297 static const struct devlink_param bnxt_dl_params[] = {
298         DEVLINK_PARAM_GENERIC(ENABLE_SRIOV,
299                               BIT(DEVLINK_PARAM_CMODE_PERMANENT),
300                               bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
301                               NULL),
302         DEVLINK_PARAM_GENERIC(IGNORE_ARI,
303                               BIT(DEVLINK_PARAM_CMODE_PERMANENT),
304                               bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
305                               NULL),
306         DEVLINK_PARAM_GENERIC(MSIX_VEC_PER_PF_MAX,
307                               BIT(DEVLINK_PARAM_CMODE_PERMANENT),
308                               bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
309                               bnxt_dl_msix_validate),
310         DEVLINK_PARAM_GENERIC(MSIX_VEC_PER_PF_MIN,
311                               BIT(DEVLINK_PARAM_CMODE_PERMANENT),
312                               bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
313                               bnxt_dl_msix_validate),
314         DEVLINK_PARAM_DRIVER(BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK,
315                              "gre_ver_check", DEVLINK_PARAM_TYPE_BOOL,
316                              BIT(DEVLINK_PARAM_CMODE_PERMANENT),
317                              bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
318                              NULL),
319 };
320
321 static const struct devlink_param bnxt_dl_port_params[] = {
322 };
323
324 int bnxt_dl_register(struct bnxt *bp)
325 {
326         struct devlink *dl;
327         int rc;
328
329         if (bp->hwrm_spec_code < 0x10600) {
330                 netdev_warn(bp->dev, "Firmware does not support NVM params");
331                 return -ENOTSUPP;
332         }
333
334         dl = devlink_alloc(&bnxt_dl_ops, sizeof(struct bnxt_dl));
335         if (!dl) {
336                 netdev_warn(bp->dev, "devlink_alloc failed");
337                 return -ENOMEM;
338         }
339
340         bnxt_link_bp_to_dl(bp, dl);
341
342         /* Add switchdev eswitch mode setting, if SRIOV supported */
343         if (pci_find_ext_capability(bp->pdev, PCI_EXT_CAP_ID_SRIOV) &&
344             bp->hwrm_spec_code > 0x10803)
345                 bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY;
346
347         rc = devlink_register(dl, &bp->pdev->dev);
348         if (rc) {
349                 netdev_warn(bp->dev, "devlink_register failed. rc=%d", rc);
350                 goto err_dl_free;
351         }
352
353         rc = devlink_params_register(dl, bnxt_dl_params,
354                                      ARRAY_SIZE(bnxt_dl_params));
355         if (rc) {
356                 netdev_warn(bp->dev, "devlink_params_register failed. rc=%d",
357                             rc);
358                 goto err_dl_unreg;
359         }
360
361         devlink_port_attrs_set(&bp->dl_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
362                                bp->pf.port_id, false, 0,
363                                bp->switch_id, sizeof(bp->switch_id));
364         rc = devlink_port_register(dl, &bp->dl_port, bp->pf.port_id);
365         if (rc) {
366                 netdev_err(bp->dev, "devlink_port_register failed");
367                 goto err_dl_param_unreg;
368         }
369         devlink_port_type_eth_set(&bp->dl_port, bp->dev);
370
371         rc = devlink_port_params_register(&bp->dl_port, bnxt_dl_port_params,
372                                           ARRAY_SIZE(bnxt_dl_port_params));
373         if (rc) {
374                 netdev_err(bp->dev, "devlink_port_params_register failed");
375                 goto err_dl_port_unreg;
376         }
377
378         devlink_params_publish(dl);
379
380         bnxt_dl_fw_reporters_create(bp);
381
382         return 0;
383
384 err_dl_port_unreg:
385         devlink_port_unregister(&bp->dl_port);
386 err_dl_param_unreg:
387         devlink_params_unregister(dl, bnxt_dl_params,
388                                   ARRAY_SIZE(bnxt_dl_params));
389 err_dl_unreg:
390         devlink_unregister(dl);
391 err_dl_free:
392         bnxt_link_bp_to_dl(bp, NULL);
393         devlink_free(dl);
394         return rc;
395 }
396
397 void bnxt_dl_unregister(struct bnxt *bp)
398 {
399         struct devlink *dl = bp->dl;
400
401         if (!dl)
402                 return;
403
404         bnxt_dl_fw_reporters_destroy(bp);
405         devlink_port_params_unregister(&bp->dl_port, bnxt_dl_port_params,
406                                        ARRAY_SIZE(bnxt_dl_port_params));
407         devlink_port_unregister(&bp->dl_port);
408         devlink_params_unregister(dl, bnxt_dl_params,
409                                   ARRAY_SIZE(bnxt_dl_params));
410         devlink_unregister(dl);
411         devlink_free(dl);
412 }