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