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 "opt_rss.h"
29 #include "opt_ratelimit.h"
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 
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 
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 
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 
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 
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
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
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
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