1 /*- 2 * Copyright (c) 2015 Mellanox Technologies. 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 "en.h" 29 #include <net/sff8472.h> 30 31 void 32 mlx5e_create_stats(struct sysctl_ctx_list *ctx, 33 struct sysctl_oid_list *parent, const char *buffer, 34 const char **desc, unsigned num, u64 * arg) 35 { 36 struct sysctl_oid *node; 37 unsigned x; 38 39 sysctl_ctx_init(ctx); 40 41 node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, 42 buffer, CTLFLAG_RD, NULL, "Statistics"); 43 if (node == NULL) 44 return; 45 for (x = 0; x != num; x++) { 46 SYSCTL_ADD_UQUAD(ctx, SYSCTL_CHILDREN(node), OID_AUTO, 47 desc[2 * x], CTLFLAG_RD, arg + x, desc[2 * x + 1]); 48 } 49 } 50 51 static int 52 mlx5e_ethtool_handler(SYSCTL_HANDLER_ARGS) 53 { 54 struct mlx5e_priv *priv = arg1; 55 uint64_t value; 56 int was_opened; 57 int error; 58 59 PRIV_LOCK(priv); 60 value = priv->params_ethtool.arg[arg2]; 61 if (req != NULL) { 62 error = sysctl_handle_64(oidp, &value, 0, req); 63 if (error || req->newptr == NULL || 64 value == priv->params_ethtool.arg[arg2]) 65 goto done; 66 67 /* assign new value */ 68 priv->params_ethtool.arg[arg2] = value; 69 } else { 70 error = 0; 71 } 72 /* check if device is gone */ 73 if (priv->gone) { 74 error = ENXIO; 75 goto done; 76 } 77 /* import RX coal time */ 78 if (priv->params_ethtool.rx_coalesce_usecs < 1) 79 priv->params_ethtool.rx_coalesce_usecs = 0; 80 else if (priv->params_ethtool.rx_coalesce_usecs > 81 MLX5E_FLD_MAX(cqc, cq_period)) { 82 priv->params_ethtool.rx_coalesce_usecs = 83 MLX5E_FLD_MAX(cqc, cq_period); 84 } 85 priv->params.rx_cq_moderation_usec = priv->params_ethtool.rx_coalesce_usecs; 86 87 /* import RX coal pkts */ 88 if (priv->params_ethtool.rx_coalesce_pkts < 1) 89 priv->params_ethtool.rx_coalesce_pkts = 0; 90 else if (priv->params_ethtool.rx_coalesce_pkts > 91 MLX5E_FLD_MAX(cqc, cq_max_count)) { 92 priv->params_ethtool.rx_coalesce_pkts = 93 MLX5E_FLD_MAX(cqc, cq_max_count); 94 } 95 priv->params.rx_cq_moderation_pkts = priv->params_ethtool.rx_coalesce_pkts; 96 97 /* import TX coal time */ 98 if (priv->params_ethtool.tx_coalesce_usecs < 1) 99 priv->params_ethtool.tx_coalesce_usecs = 0; 100 else if (priv->params_ethtool.tx_coalesce_usecs > 101 MLX5E_FLD_MAX(cqc, cq_period)) { 102 priv->params_ethtool.tx_coalesce_usecs = 103 MLX5E_FLD_MAX(cqc, cq_period); 104 } 105 priv->params.tx_cq_moderation_usec = priv->params_ethtool.tx_coalesce_usecs; 106 107 /* import TX coal pkts */ 108 if (priv->params_ethtool.tx_coalesce_pkts < 1) 109 priv->params_ethtool.tx_coalesce_pkts = 0; 110 else if (priv->params_ethtool.tx_coalesce_pkts > 111 MLX5E_FLD_MAX(cqc, cq_max_count)) { 112 priv->params_ethtool.tx_coalesce_pkts = MLX5E_FLD_MAX(cqc, cq_max_count); 113 } 114 priv->params.tx_cq_moderation_pkts = priv->params_ethtool.tx_coalesce_pkts; 115 116 was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); 117 if (was_opened) { 118 u64 *xarg = priv->params_ethtool.arg + arg2; 119 120 if (xarg == &priv->params_ethtool.tx_coalesce_pkts || 121 xarg == &priv->params_ethtool.rx_coalesce_pkts || 122 xarg == &priv->params_ethtool.tx_coalesce_usecs || 123 xarg == &priv->params_ethtool.rx_coalesce_usecs) { 124 /* avoid downing and upping the network interface */ 125 error = mlx5e_refresh_channel_params(priv); 126 goto done; 127 } 128 mlx5e_close_locked(priv->ifp); 129 } 130 /* import TX queue size */ 131 if (priv->params_ethtool.tx_queue_size < 132 (1 << MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE)) { 133 priv->params_ethtool.tx_queue_size = 134 (1 << MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE); 135 } else if (priv->params_ethtool.tx_queue_size > 136 priv->params_ethtool.tx_queue_size_max) { 137 priv->params_ethtool.tx_queue_size = 138 priv->params_ethtool.tx_queue_size_max; 139 } 140 priv->params.log_sq_size = 141 order_base_2(priv->params_ethtool.tx_queue_size); 142 143 /* import RX queue size */ 144 if (priv->params_ethtool.rx_queue_size < 145 (1 << MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE)) { 146 priv->params_ethtool.rx_queue_size = 147 (1 << MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE); 148 } else if (priv->params_ethtool.rx_queue_size > 149 priv->params_ethtool.rx_queue_size_max) { 150 priv->params_ethtool.rx_queue_size = 151 priv->params_ethtool.rx_queue_size_max; 152 } 153 priv->params.log_rq_size = 154 order_base_2(priv->params_ethtool.rx_queue_size); 155 156 priv->params.min_rx_wqes = min_t (u16, 157 priv->params_ethtool.rx_queue_size - 1, 158 MLX5E_PARAMS_DEFAULT_MIN_RX_WQES); 159 160 /* import number of channels */ 161 if (priv->params_ethtool.channels < 1) 162 priv->params_ethtool.channels = 1; 163 else if (priv->params_ethtool.channels > 164 (u64) priv->mdev->priv.eq_table.num_comp_vectors) { 165 priv->params_ethtool.channels = 166 (u64) priv->mdev->priv.eq_table.num_comp_vectors; 167 } 168 priv->params.num_channels = priv->params_ethtool.channels; 169 170 /* import RX mode */ 171 if (priv->params_ethtool.rx_coalesce_mode != 0) 172 priv->params_ethtool.rx_coalesce_mode = 1; 173 priv->params.rx_cq_moderation_mode = priv->params_ethtool.rx_coalesce_mode; 174 175 /* import TX mode */ 176 if (priv->params_ethtool.tx_coalesce_mode != 0) 177 priv->params_ethtool.tx_coalesce_mode = 1; 178 priv->params.tx_cq_moderation_mode = priv->params_ethtool.tx_coalesce_mode; 179 180 /* we always agree to turn off HW LRO - but not always to turn on */ 181 if (priv->params_ethtool.hw_lro != 0) { 182 if ((priv->ifp->if_capenable & IFCAP_LRO) && 183 MLX5_CAP_ETH(priv->mdev, lro_cap)) { 184 priv->params.hw_lro_en = 1; 185 priv->params_ethtool.hw_lro = 1; 186 } else { 187 priv->params.hw_lro_en = 0; 188 priv->params_ethtool.hw_lro = 0; 189 error = EINVAL; 190 191 if_printf(priv->ifp, "Can't enable HW LRO: " 192 "The HW or SW LRO feature is disabled"); 193 } 194 } else { 195 priv->params.hw_lro_en = 0; 196 } 197 198 if (&priv->params_ethtool.arg[arg2] == 199 &priv->params_ethtool.cqe_zipping) { 200 if (priv->params_ethtool.cqe_zipping && 201 MLX5_CAP_GEN(priv->mdev, cqe_compression)) { 202 priv->params.cqe_zipping_en = true; 203 priv->params_ethtool.cqe_zipping = 1; 204 } else { 205 priv->params.cqe_zipping_en = false; 206 priv->params_ethtool.cqe_zipping = 0; 207 } 208 } 209 if (was_opened) 210 mlx5e_open_locked(priv->ifp); 211 done: 212 PRIV_UNLOCK(priv); 213 return (error); 214 } 215 216 /* 217 * Read the first three bytes of the eeprom in order to get the needed info 218 * for the whole reading. 219 * Byte 0 - Identifier byte 220 * Byte 1 - Revision byte 221 * Byte 2 - Status byte 222 */ 223 static int 224 mlx5e_get_eeprom_info(struct mlx5e_priv *priv, struct mlx5e_eeprom *eeprom) 225 { 226 struct mlx5_core_dev *dev = priv->mdev; 227 u32 data = 0; 228 int size_read = 0; 229 int ret; 230 231 ret = mlx5_query_module_num(dev, &eeprom->module_num); 232 if (ret) { 233 if_printf(priv->ifp, "%s:%d: Failed query module error=%d\n", 234 __func__, __LINE__, ret); 235 return (ret); 236 } 237 238 /* Read the first three bytes to get Identifier, Revision and Status */ 239 ret = mlx5_query_eeprom(dev, eeprom->i2c_addr, eeprom->page_num, 240 eeprom->device_addr, MLX5E_EEPROM_INFO_BYTES, eeprom->module_num, &data, 241 &size_read); 242 if (ret) { 243 if_printf(priv->ifp, "%s:%d: Failed query eeprom module error=0x%x\n", 244 __func__, __LINE__, ret); 245 return (ret); 246 } 247 248 switch (data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK) { 249 case SFF_8024_ID_QSFP: 250 eeprom->type = MLX5E_ETH_MODULE_SFF_8436; 251 eeprom->len = MLX5E_ETH_MODULE_SFF_8436_LEN; 252 break; 253 case SFF_8024_ID_QSFPPLUS: 254 case SFF_8024_ID_QSFP28: 255 if ((data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK) == SFF_8024_ID_QSFP28 || 256 ((data & MLX5_EEPROM_REVISION_ID_BYTE_MASK) >> 8) >= 0x3) { 257 eeprom->type = MLX5E_ETH_MODULE_SFF_8636; 258 eeprom->len = MLX5E_ETH_MODULE_SFF_8636_LEN; 259 } else { 260 eeprom->type = MLX5E_ETH_MODULE_SFF_8436; 261 eeprom->len = MLX5E_ETH_MODULE_SFF_8436_LEN; 262 } 263 if ((data & MLX5_EEPROM_PAGE_3_VALID_BIT_MASK) == 0) 264 eeprom->page_valid = 1; 265 break; 266 case SFF_8024_ID_SFP: 267 eeprom->type = MLX5E_ETH_MODULE_SFF_8472; 268 eeprom->len = MLX5E_ETH_MODULE_SFF_8472_LEN; 269 break; 270 default: 271 if_printf(priv->ifp, "%s:%d: Not recognized cable type = 0x%x(%s)\n", 272 __func__, __LINE__, data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK, 273 sff_8024_id[data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK]); 274 return (EINVAL); 275 } 276 return (0); 277 } 278 279 /* Read both low and high pages of the eeprom */ 280 static int 281 mlx5e_get_eeprom(struct mlx5e_priv *priv, struct mlx5e_eeprom *ee) 282 { 283 struct mlx5_core_dev *dev = priv->mdev; 284 int size_read = 0; 285 int ret; 286 287 if (ee->len == 0) 288 return (EINVAL); 289 290 /* Read low page of the eeprom */ 291 while (ee->device_addr < ee->len) { 292 ret = mlx5_query_eeprom(dev, ee->i2c_addr, ee->page_num, ee->device_addr, 293 ee->len - ee->device_addr, ee->module_num, 294 ee->data + (ee->device_addr / 4), &size_read); 295 if (ret) { 296 if_printf(priv->ifp, "%s:%d: Failed reading eeprom, " 297 "error = 0x%02x\n", __func__, __LINE__, ret); 298 return (ret); 299 } 300 ee->device_addr += size_read; 301 } 302 303 /* Read high page of the eeprom */ 304 if (ee->page_valid) { 305 ee->device_addr = MLX5E_EEPROM_HIGH_PAGE_OFFSET; 306 ee->page_num = MLX5E_EEPROM_HIGH_PAGE; 307 size_read = 0; 308 while (ee->device_addr < MLX5E_EEPROM_PAGE_LENGTH) { 309 ret = mlx5_query_eeprom(dev, ee->i2c_addr, ee->page_num, 310 ee->device_addr, MLX5E_EEPROM_PAGE_LENGTH - ee->device_addr, 311 ee->module_num, ee->data + (ee->len / 4) + 312 ((ee->device_addr - MLX5E_EEPROM_HIGH_PAGE_OFFSET) / 4), 313 &size_read); 314 if (ret) { 315 if_printf(priv->ifp, "%s:%d: Failed reading eeprom, " 316 "error = 0x%02x\n", __func__, __LINE__, ret); 317 return (ret); 318 } 319 ee->device_addr += size_read; 320 } 321 } 322 return (0); 323 } 324 325 static void 326 mlx5e_print_eeprom(struct mlx5e_eeprom *eeprom) 327 { 328 int row; 329 int index_in_row; 330 int byte_to_write = 0; 331 int line_length = 16; 332 333 printf("\nOffset\t\tValues\n"); 334 printf("------\t\t------"); 335 while (byte_to_write < eeprom->len) { 336 printf("\n0x%04X\t\t", byte_to_write); 337 for (index_in_row = 0; index_in_row < line_length; index_in_row++) { 338 printf("%02X ", ((u8 *)eeprom->data)[byte_to_write]); 339 byte_to_write++; 340 } 341 } 342 343 if (eeprom->page_valid) { 344 row = MLX5E_EEPROM_HIGH_PAGE_OFFSET; 345 printf("\n\nUpper Page 0x03\n"); 346 printf("\nOffset\t\tValues\n"); 347 printf("------\t\t------"); 348 while (row < MLX5E_EEPROM_PAGE_LENGTH) { 349 printf("\n0x%04X\t\t", row); 350 for (index_in_row = 0; index_in_row < line_length; index_in_row++) { 351 printf("%02X ", ((u8 *)eeprom->data)[byte_to_write]); 352 byte_to_write++; 353 row++; 354 } 355 } 356 } 357 } 358 359 /* 360 * Read cable EEPROM module information by first inspecting the first 361 * three bytes to get the initial information for a whole reading. 362 * Information will be printed to dmesg. 363 */ 364 static int 365 mlx5e_read_eeprom(SYSCTL_HANDLER_ARGS) 366 { 367 struct mlx5e_priv *priv = arg1; 368 struct mlx5e_eeprom eeprom; 369 int error; 370 int result = 0; 371 372 PRIV_LOCK(priv); 373 error = sysctl_handle_int(oidp, &result, 0, req); 374 if (error || !req->newptr) 375 goto done; 376 377 /* Check if device is gone */ 378 if (priv->gone) { 379 error = ENXIO; 380 goto done; 381 } 382 383 if (result == 1) { 384 eeprom.i2c_addr = MLX5E_I2C_ADDR_LOW; 385 eeprom.device_addr = 0; 386 eeprom.page_num = MLX5E_EEPROM_LOW_PAGE; 387 eeprom.page_valid = 0; 388 389 /* Read three first bytes to get important info */ 390 error = mlx5e_get_eeprom_info(priv, &eeprom); 391 if (error) { 392 if_printf(priv->ifp, "%s:%d: Failed reading eeprom's " 393 "initial information\n", __func__, __LINE__); 394 error = 0; 395 goto done; 396 } 397 /* 398 * Allocate needed length buffer and additional space for 399 * page 0x03 400 */ 401 eeprom.data = malloc(eeprom.len + MLX5E_EEPROM_PAGE_LENGTH, 402 M_MLX5EN, M_WAITOK | M_ZERO); 403 404 /* Read the whole eeprom information */ 405 error = mlx5e_get_eeprom(priv, &eeprom); 406 if (error) { 407 if_printf(priv->ifp, "%s:%d: Failed reading eeprom\n", 408 __func__, __LINE__); 409 error = 0; 410 /* 411 * Continue printing partial information in case of 412 * an error 413 */ 414 } 415 mlx5e_print_eeprom(&eeprom); 416 free(eeprom.data, M_MLX5EN); 417 } 418 done: 419 PRIV_UNLOCK(priv); 420 return (error); 421 } 422 423 static const char *mlx5e_params_desc[] = { 424 MLX5E_PARAMS(MLX5E_STATS_DESC) 425 }; 426 427 static const char *mlx5e_port_stats_debug_desc[] = { 428 MLX5E_PORT_STATS_DEBUG(MLX5E_STATS_DESC) 429 }; 430 431 static int 432 mlx5e_ethtool_debug_stats(SYSCTL_HANDLER_ARGS) 433 { 434 struct mlx5e_priv *priv = arg1; 435 int error; 436 int sys_debug; 437 438 sys_debug = priv->sysctl_debug; 439 error = sysctl_handle_int(oidp, &priv->sysctl_debug, 0, req); 440 if (error || !req->newptr) 441 return (error); 442 priv->sysctl_debug = !!priv->sysctl_debug; 443 if (sys_debug == priv->sysctl_debug) 444 return (error); 445 if (priv->sysctl_debug) 446 mlx5e_create_stats(&priv->stats.port_stats_debug.ctx, 447 SYSCTL_CHILDREN(priv->sysctl_ifnet), "debug_stats", 448 mlx5e_port_stats_debug_desc, MLX5E_PORT_STATS_DEBUG_NUM, 449 priv->stats.port_stats_debug.arg); 450 else 451 sysctl_ctx_free(&priv->stats.port_stats_debug.ctx); 452 return (error); 453 } 454 455 void 456 mlx5e_create_ethtool(struct mlx5e_priv *priv) 457 { 458 struct sysctl_oid *node; 459 const char *pnameunit; 460 unsigned x; 461 462 /* set some defaults */ 463 priv->params_ethtool.tx_queue_size_max = 1 << MLX5E_PARAMS_MAXIMUM_LOG_SQ_SIZE; 464 priv->params_ethtool.rx_queue_size_max = 1 << MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE; 465 priv->params_ethtool.tx_queue_size = 1 << priv->params.log_sq_size; 466 priv->params_ethtool.rx_queue_size = 1 << priv->params.log_rq_size; 467 priv->params_ethtool.channels = priv->params.num_channels; 468 priv->params_ethtool.coalesce_pkts_max = MLX5E_FLD_MAX(cqc, cq_max_count); 469 priv->params_ethtool.coalesce_usecs_max = MLX5E_FLD_MAX(cqc, cq_period); 470 priv->params_ethtool.rx_coalesce_mode = priv->params.rx_cq_moderation_mode; 471 priv->params_ethtool.rx_coalesce_usecs = priv->params.rx_cq_moderation_usec; 472 priv->params_ethtool.rx_coalesce_pkts = priv->params.rx_cq_moderation_pkts; 473 priv->params_ethtool.tx_coalesce_mode = priv->params.tx_cq_moderation_mode; 474 priv->params_ethtool.tx_coalesce_usecs = priv->params.tx_cq_moderation_usec; 475 priv->params_ethtool.tx_coalesce_pkts = priv->params.tx_cq_moderation_pkts; 476 priv->params_ethtool.hw_lro = priv->params.hw_lro_en; 477 priv->params_ethtool.cqe_zipping = priv->params.cqe_zipping_en; 478 479 /* create root node */ 480 node = SYSCTL_ADD_NODE(&priv->sysctl_ctx, 481 SYSCTL_CHILDREN(priv->sysctl_ifnet), OID_AUTO, 482 "conf", CTLFLAG_RW, NULL, "Configuration"); 483 if (node == NULL) 484 return; 485 for (x = 0; x != MLX5E_PARAMS_NUM; x++) { 486 /* check for read-only parameter */ 487 if (strstr(mlx5e_params_desc[2 * x], "_max") != NULL) { 488 SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO, 489 mlx5e_params_desc[2 * x], CTLTYPE_U64 | CTLFLAG_RD | 490 CTLFLAG_MPSAFE, priv, x, &mlx5e_ethtool_handler, "QU", 491 mlx5e_params_desc[2 * x + 1]); 492 } else { 493 #if (__FreeBSD_version < 1100000) 494 char path[64]; 495 #endif 496 /* 497 * NOTE: In FreeBSD-11 and newer the 498 * CTLFLAG_RWTUN flag will take care of 499 * loading default sysctl value from the 500 * kernel environment, if any: 501 */ 502 SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO, 503 mlx5e_params_desc[2 * x], CTLTYPE_U64 | CTLFLAG_RWTUN | 504 CTLFLAG_MPSAFE, priv, x, &mlx5e_ethtool_handler, "QU", 505 mlx5e_params_desc[2 * x + 1]); 506 507 #if (__FreeBSD_version < 1100000) 508 /* compute path for sysctl */ 509 snprintf(path, sizeof(path), "dev.mce.%d.conf.%s", 510 device_get_unit(priv->mdev->pdev->dev.bsddev), 511 mlx5e_params_desc[2 * x]); 512 513 /* try to fetch tunable, if any */ 514 if (TUNABLE_QUAD_FETCH(path, &priv->params_ethtool.arg[x])) 515 mlx5e_ethtool_handler(NULL, priv, x, NULL); 516 #endif 517 } 518 } 519 520 SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO, 521 "debug_stats", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, priv, 522 0, &mlx5e_ethtool_debug_stats, "I", "Extended debug statistics"); 523 524 pnameunit = device_get_nameunit(priv->mdev->pdev->dev.bsddev); 525 526 SYSCTL_ADD_STRING(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), 527 OID_AUTO, "device_name", CTLFLAG_RD, 528 __DECONST(void *, pnameunit), 0, 529 "PCI device name"); 530 531 /* EEPROM support */ 532 SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO, "eeprom_info", 533 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, priv, 0, 534 mlx5e_read_eeprom, "I", "EEPROM information"); 535 } 536