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