]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/mlx5/mlx5_core/mlx5_diagnostics.c
MFS r353184, r353186, r353188, r353190, r353192, r353194, r353196, r353198,
[FreeBSD/FreeBSD.git] / sys / dev / mlx5 / mlx5_core / mlx5_diagnostics.c
1 /*-
2  * Copyright (c) 2013-2017, Mellanox Technologies, Ltd.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27
28 #include <dev/mlx5/driver.h>
29 #include <dev/mlx5/port.h>
30 #include <dev/mlx5/diagnostics.h>
31 #include <dev/mlx5/mlx5_core/mlx5_core.h>
32 #include <net/sff8472.h>
33
34 const struct mlx5_core_diagnostics_entry
35         mlx5_core_pci_diagnostics_table[
36                 MLX5_CORE_PCI_DIAGNOSTICS_NUM] = {
37         MLX5_CORE_PCI_DIAGNOSTICS(MLX5_CORE_DIAGNOSTICS_ENTRY)
38 };
39
40 const struct mlx5_core_diagnostics_entry
41         mlx5_core_general_diagnostics_table[
42                 MLX5_CORE_GENERAL_DIAGNOSTICS_NUM] = {
43         MLX5_CORE_GENERAL_DIAGNOSTICS(MLX5_CORE_DIAGNOSTICS_ENTRY)
44 };
45
46 static int mlx5_core_get_index_of_diag_counter(
47         const struct mlx5_core_diagnostics_entry *entry,
48         int size, u16 counter_id)
49 {
50         int x;
51
52         /* check for invalid counter ID */
53         if (counter_id == 0)
54                 return -1;
55
56         /* lookup counter ID in table */
57         for (x = 0; x != size; x++) {
58                 if (entry[x].counter_id == counter_id)
59                         return x;
60         }
61         return -1;
62 }
63
64 static void mlx5_core_put_diag_counter(
65         const struct mlx5_core_diagnostics_entry *entry,
66         u64 *array, int size, u16 counter_id, u64 value)
67 {
68         int x;
69
70         /* check for invalid counter ID */
71         if (counter_id == 0)
72                 return;
73
74         /* lookup counter ID in table */
75         for (x = 0; x != size; x++) {
76                 if (entry[x].counter_id == counter_id) {
77                         array[x] = value;
78                         break;
79                 }
80         }
81 }
82
83 int mlx5_core_set_diagnostics_full(struct mlx5_core_dev *dev,
84                                    u8 enable_pci, u8 enable_general)
85 {
86         void *diag_params_ctx;
87         void *in;
88         int numcounters;
89         int inlen;
90         int err;
91         int x;
92         int y;
93
94         if (MLX5_CAP_GEN(dev, debug) == 0)
95                 return 0;
96
97         numcounters = MLX5_CAP_GEN(dev, num_of_diagnostic_counters);
98         if (numcounters == 0)
99                 return 0;
100
101         inlen = MLX5_ST_SZ_BYTES(set_diagnostic_params_in) +
102             MLX5_ST_SZ_BYTES(diagnostic_counter) * numcounters;
103         in = mlx5_vzalloc(inlen);
104         if (in == NULL)
105                 return -ENOMEM;
106
107         diag_params_ctx = MLX5_ADDR_OF(set_diagnostic_params_in, in,
108                                        diagnostic_params_ctx);
109
110         MLX5_SET(diagnostic_params_context, diag_params_ctx,
111                  enable, enable_pci || enable_general);
112         MLX5_SET(diagnostic_params_context, diag_params_ctx,
113                  single, 1);
114         MLX5_SET(diagnostic_params_context, diag_params_ctx,
115                  on_demand, 1);
116
117         /* collect the counters we want to enable */
118         for (x = y = 0; x != numcounters; x++) {
119                 u16 counter_id =
120                         MLX5_CAP_DEBUG(dev, diagnostic_counter[x].counter_id);
121                 int index = -1;
122
123                 if (index < 0 && enable_pci != 0) {
124                         /* check if counter ID exists in local table */
125                         index = mlx5_core_get_index_of_diag_counter(
126                             mlx5_core_pci_diagnostics_table,
127                             MLX5_CORE_PCI_DIAGNOSTICS_NUM,
128                             counter_id);
129                 }
130                 if (index < 0 && enable_general != 0) {
131                         /* check if counter ID exists in local table */
132                         index = mlx5_core_get_index_of_diag_counter(
133                             mlx5_core_general_diagnostics_table,
134                             MLX5_CORE_GENERAL_DIAGNOSTICS_NUM,
135                             counter_id);
136                 }
137                 if (index < 0)
138                         continue;
139
140                 MLX5_SET(diagnostic_params_context,
141                          diag_params_ctx,
142                          counter_id[y].counter_id,
143                          counter_id);
144                 y++;
145         }
146
147         /* recompute input length */
148         inlen = MLX5_ST_SZ_BYTES(set_diagnostic_params_in) +
149             MLX5_ST_SZ_BYTES(diagnostic_counter) * y;
150
151         /* set number of counters */
152         MLX5_SET(diagnostic_params_context, diag_params_ctx,
153                  num_of_counters, y);
154
155         /* execute firmware command */
156         err = mlx5_set_diagnostic_params(dev, in, inlen);
157
158         kvfree(in);
159
160         return err;
161 }
162
163 int mlx5_core_get_diagnostics_full(struct mlx5_core_dev *dev,
164                                    union mlx5_core_pci_diagnostics *pdiag,
165                                    union mlx5_core_general_diagnostics *pgen)
166 {
167         void *out;
168         void *in;
169         int numcounters;
170         int outlen;
171         int inlen;
172         int err;
173         int x;
174
175         if (MLX5_CAP_GEN(dev, debug) == 0)
176                 return 0;
177
178         numcounters = MLX5_CAP_GEN(dev, num_of_diagnostic_counters);
179         if (numcounters == 0)
180                 return 0;
181
182         outlen = MLX5_ST_SZ_BYTES(query_diagnostic_counters_out) +
183             MLX5_ST_SZ_BYTES(diagnostic_counter) * numcounters;
184
185         out = mlx5_vzalloc(outlen);
186         if (out == NULL)
187                 return -ENOMEM;
188
189         err = mlx5_query_diagnostic_counters(dev, 1, 0, out, outlen);
190         if (err == 0) {
191                 for (x = 0; x != numcounters; x++) {
192                         u16 counter_id = MLX5_GET(
193                             query_diagnostic_counters_out,
194                             out, diag_counter[x].counter_id);
195                         u64 counter_value = MLX5_GET64(
196                             query_diagnostic_counters_out,
197                             out, diag_counter[x].counter_value_h);
198
199                         if (pdiag != NULL) {
200                                 mlx5_core_put_diag_counter(
201                                     mlx5_core_pci_diagnostics_table,
202                                     pdiag->array,
203                                     MLX5_CORE_PCI_DIAGNOSTICS_NUM,
204                                     counter_id, counter_value);
205                         }
206                         if (pgen != NULL) {
207                                 mlx5_core_put_diag_counter(
208                                     mlx5_core_general_diagnostics_table,
209                                     pgen->array,
210                                     MLX5_CORE_GENERAL_DIAGNOSTICS_NUM,
211                                     counter_id, counter_value);
212                         }
213                 }
214         }
215         kvfree(out);
216
217         if (pdiag != NULL) {
218                 inlen = MLX5_ST_SZ_BYTES(mpcnt_reg);
219                 outlen = MLX5_ST_SZ_BYTES(mpcnt_reg);
220
221                 in = mlx5_vzalloc(inlen);
222                 if (in == NULL)
223                         return -ENOMEM;
224
225                 out = mlx5_vzalloc(outlen);
226                 if (out == NULL) {
227                         kvfree(in);
228                         return -ENOMEM;
229                 }
230                 MLX5_SET(mpcnt_reg, in, grp,
231                          MLX5_PCIE_PERFORMANCE_COUNTERS_GROUP);
232
233                 err = mlx5_core_access_reg(dev, in, inlen, out, outlen,
234                                            MLX5_REG_MPCNT, 0, 0);
235                 if (err == 0) {
236                         void *pcounters = MLX5_ADDR_OF(mpcnt_reg, out,
237                             counter_set.pcie_perf_counters);
238
239                         pdiag->counter.rx_pci_errors =
240                             MLX5_GET(pcie_perf_counters,
241                                      pcounters, rx_errors);
242                         pdiag->counter.tx_pci_errors =
243                             MLX5_GET(pcie_perf_counters,
244                                      pcounters, tx_errors);
245                 }
246                 MLX5_SET(mpcnt_reg, in, grp,
247                          MLX5_PCIE_TIMERS_AND_STATES_COUNTERS_GROUP);
248
249                 err = mlx5_core_access_reg(dev, in, inlen, out, outlen,
250                     MLX5_REG_MPCNT, 0, 0);
251                 if (err == 0) {
252                         void *pcounters = MLX5_ADDR_OF(mpcnt_reg, out,
253                             counter_set.pcie_timers_states);
254
255                         pdiag->counter.tx_pci_non_fatal_errors =
256                             MLX5_GET(pcie_timers_states,
257                                      pcounters, non_fatal_err_msg_sent);
258                         pdiag->counter.tx_pci_fatal_errors =
259                             MLX5_GET(pcie_timers_states,
260                                      pcounters, fatal_err_msg_sent);
261                 }
262                 kvfree(in);
263                 kvfree(out);
264         }
265         return 0;
266 }
267
268 int mlx5_core_supports_diagnostics(struct mlx5_core_dev *dev, u16 counter_id)
269 {
270         int numcounters;
271         int x;
272
273         if (MLX5_CAP_GEN(dev, debug) == 0)
274                 return 0;
275
276         /* check for any counter */
277         if (counter_id == 0)
278                 return 1;
279
280         numcounters = MLX5_CAP_GEN(dev, num_of_diagnostic_counters);
281
282         /* check if counter ID exists in debug capability */
283         for (x = 0; x != numcounters; x++) {
284                 if (MLX5_CAP_DEBUG(dev, diagnostic_counter[x].counter_id) ==
285                     counter_id)
286                         return 1;
287         }
288         return 0;                       /* not supported counter */
289 }
290
291 /*
292  * Read the first three bytes of the eeprom in order to get the needed info
293  * for the whole reading.
294  * Byte 0 - Identifier byte
295  * Byte 1 - Revision byte
296  * Byte 2 - Status byte
297  */
298 int
299 mlx5_get_eeprom_info(struct mlx5_core_dev *dev, struct mlx5_eeprom *eeprom)
300 {
301         u32 data = 0;
302         int size_read = 0;
303         int ret;
304
305         ret = mlx5_query_module_num(dev, &eeprom->module_num);
306         if (ret) {
307                 mlx5_core_err(dev, "Failed query module error=%d\n", ret);
308                 return (-ret);
309         }
310
311         /* Read the first three bytes to get Identifier, Revision and Status */
312         ret = mlx5_query_eeprom(dev, eeprom->i2c_addr, eeprom->page_num,
313             eeprom->device_addr, MLX5_EEPROM_INFO_BYTES, eeprom->module_num, &data,
314             &size_read);
315         if (ret) {
316                 mlx5_core_err(dev,
317                     "Failed query EEPROM module error=0x%x\n", ret);
318                 return (-ret);
319         }
320
321         switch (data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK) {
322         case SFF_8024_ID_QSFP:
323                 eeprom->type = MLX5_ETH_MODULE_SFF_8436;
324                 eeprom->len = MLX5_ETH_MODULE_SFF_8436_LEN;
325                 break;
326         case SFF_8024_ID_QSFPPLUS:
327         case SFF_8024_ID_QSFP28:
328                 if ((data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK) == SFF_8024_ID_QSFP28 ||
329                     ((data & MLX5_EEPROM_REVISION_ID_BYTE_MASK) >> 8) >= 0x3) {
330                         eeprom->type = MLX5_ETH_MODULE_SFF_8636;
331                         eeprom->len = MLX5_ETH_MODULE_SFF_8636_LEN;
332                 } else {
333                         eeprom->type = MLX5_ETH_MODULE_SFF_8436;
334                         eeprom->len = MLX5_ETH_MODULE_SFF_8436_LEN;
335                 }
336                 if ((data & MLX5_EEPROM_PAGE_3_VALID_BIT_MASK) == 0)
337                         eeprom->page_valid = 1;
338                 break;
339         case SFF_8024_ID_SFP:
340                 eeprom->type = MLX5_ETH_MODULE_SFF_8472;
341                 eeprom->len = MLX5_ETH_MODULE_SFF_8472_LEN;
342                 break;
343         default:
344                 mlx5_core_err(dev, "Not recognized cable type = 0x%x(%s)\n",
345                     data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK,
346                     sff_8024_id[data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK]);
347                 return (EINVAL);
348         }
349         return (0);
350 }
351
352 /* Read both low and high pages of the eeprom */
353 int
354 mlx5_get_eeprom(struct mlx5_core_dev *dev, struct mlx5_eeprom *ee)
355 {
356         int size_read = 0;
357         int ret;
358
359         if (ee->len == 0)
360                 return (EINVAL);
361
362         /* Read low page of the eeprom */
363         while (ee->device_addr < ee->len) {
364                 ret = mlx5_query_eeprom(dev, ee->i2c_addr, ee->page_num, ee->device_addr,
365                     ee->len - ee->device_addr, ee->module_num,
366                     ee->data + (ee->device_addr / 4), &size_read);
367                 if (ret) {
368                         mlx5_core_err(dev,
369                             "Failed reading EEPROM, error = 0x%02x\n", ret);
370                         return (-ret);
371                 }
372                 ee->device_addr += size_read;
373         }
374
375         /* Read high page of the eeprom */
376         if (ee->page_valid == 1) {
377                 ee->device_addr = MLX5_EEPROM_HIGH_PAGE_OFFSET;
378                 ee->page_num = MLX5_EEPROM_HIGH_PAGE;
379                 size_read = 0;
380                 while (ee->device_addr < MLX5_EEPROM_PAGE_LENGTH) {
381                         ret = mlx5_query_eeprom(dev, ee->i2c_addr, ee->page_num,
382                             ee->device_addr, MLX5_EEPROM_PAGE_LENGTH - ee->device_addr,
383                             ee->module_num, ee->data + (ee->len / 4) +
384                             ((ee->device_addr - MLX5_EEPROM_HIGH_PAGE_OFFSET) / 4),
385                             &size_read);
386                         if (ret) {
387                                 mlx5_core_err(dev,
388                                     "Failed reading EEPROM, error = 0x%02x\n",
389                                     ret);
390                                 return (-ret);
391                         }
392                         ee->device_addr += size_read;
393                 }
394         }
395         return (0);
396 }
397
398 /*
399  * Read cable EEPROM module information by first inspecting the first
400  * three bytes to get the initial information for a whole reading.
401  * Information will be printed to dmesg.
402  */
403 int
404 mlx5_read_eeprom(struct mlx5_core_dev *dev, struct mlx5_eeprom *eeprom)
405 {
406         int error;
407
408         eeprom->i2c_addr = MLX5_I2C_ADDR_LOW;
409         eeprom->device_addr = 0;
410         eeprom->page_num = MLX5_EEPROM_LOW_PAGE;
411         eeprom->page_valid = 0;
412
413         /* Read three first bytes to get important info */
414         error = mlx5_get_eeprom_info(dev, eeprom);
415         if (error) {
416                 mlx5_core_err(dev,
417                     "Failed reading EEPROM initial information\n");
418                 return (error);
419         }
420         /*
421          * Allocate needed length buffer and additional space for
422          * page 0x03
423          */
424         eeprom->data = malloc(eeprom->len + MLX5_EEPROM_PAGE_LENGTH,
425             M_MLX5_EEPROM, M_WAITOK | M_ZERO);
426
427         /* Read the whole eeprom information */
428         error = mlx5_get_eeprom(dev, eeprom);
429         if (error) {
430                 mlx5_core_err(dev, "Failed reading EEPROM\n");
431                 error = 0;
432                 /*
433                  * Continue printing partial information in case of
434                  * an error
435                  */
436         }
437         free(eeprom->data, M_MLX5_EEPROM);
438
439         return (error);
440 }
441
442