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
mlx5_core_get_index_of_diag_counter(const struct mlx5_core_diagnostics_entry * entry,int size,u16 counter_id)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
mlx5_core_put_diag_counter(const struct mlx5_core_diagnostics_entry * entry,u64 * array,int size,u16 counter_id,u64 value)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
mlx5_core_set_diagnostics_full(struct mlx5_core_dev * dev,u8 enable_pci,u8 enable_general)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
mlx5_core_get_diagnostics_full(struct mlx5_core_dev * dev,union mlx5_core_pci_diagnostics * pdiag,union mlx5_core_general_diagnostics * pgen)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
mlx5_core_supports_diagnostics(struct mlx5_core_dev * dev,u16 counter_id)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
mlx5_get_eeprom_info(struct mlx5_core_dev * dev,struct mlx5_eeprom * eeprom)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
mlx5_get_eeprom(struct mlx5_core_dev * dev,struct mlx5_eeprom * ee)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
mlx5_read_eeprom(struct mlx5_core_dev * dev,struct mlx5_eeprom * eeprom)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
443