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 
443