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