1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2 /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */ 3 4 #include <linux/kernel.h> 5 #include <linux/err.h> 6 #include <linux/sfp.h> 7 8 #include "core.h" 9 #include "core_env.h" 10 #include "item.h" 11 #include "reg.h" 12 13 struct mlxsw_env_module_info { 14 u64 module_overheat_counter; 15 bool is_overheat; 16 }; 17 18 struct mlxsw_env { 19 struct mlxsw_core *core; 20 u8 module_count; 21 spinlock_t module_info_lock; /* Protects 'module_info'. */ 22 struct mlxsw_env_module_info module_info[]; 23 }; 24 25 static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id, 26 bool *qsfp, bool *cmis) 27 { 28 char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE]; 29 char mcia_pl[MLXSW_REG_MCIA_LEN]; 30 u8 ident; 31 int err; 32 33 mlxsw_reg_mcia_pack(mcia_pl, id, 0, MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1, 34 MLXSW_REG_MCIA_I2C_ADDR_LOW); 35 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl); 36 if (err) 37 return err; 38 mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp); 39 ident = eeprom_tmp[0]; 40 *cmis = false; 41 switch (ident) { 42 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP: 43 *qsfp = false; 44 break; 45 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP: 46 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: 47 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28: 48 *qsfp = true; 49 break; 50 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD: 51 *qsfp = true; 52 *cmis = true; 53 break; 54 default: 55 return -EINVAL; 56 } 57 58 return 0; 59 } 60 61 static int 62 mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, int module, 63 u16 offset, u16 size, void *data, 64 bool qsfp, unsigned int *p_read_size) 65 { 66 char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE]; 67 char mcia_pl[MLXSW_REG_MCIA_LEN]; 68 u16 i2c_addr; 69 u8 page = 0; 70 int status; 71 int err; 72 73 /* MCIA register accepts buffer size <= 48. Page of size 128 should be 74 * read by chunks of size 48, 48, 32. Align the size of the last chunk 75 * to avoid reading after the end of the page. 76 */ 77 size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE); 78 79 if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH && 80 offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) 81 /* Cross pages read, read until offset 256 in low page */ 82 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset; 83 84 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW; 85 if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) { 86 if (qsfp) { 87 /* When reading upper pages 1, 2 and 3 the offset 88 * starts at 128. Please refer to "QSFP+ Memory Map" 89 * figure in SFF-8436 specification and to "CMIS Module 90 * Memory Map" figure in CMIS specification for 91 * graphical depiction. 92 */ 93 page = MLXSW_REG_MCIA_PAGE_GET(offset); 94 offset -= MLXSW_REG_MCIA_EEPROM_UP_PAGE_LENGTH * page; 95 if (offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) 96 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset; 97 } else { 98 /* When reading upper pages 1, 2 and 3 the offset 99 * starts at 0 and I2C high address is used. Please refer 100 * refer to "Memory Organization" figure in SFF-8472 101 * specification for graphical depiction. 102 */ 103 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH; 104 offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH; 105 } 106 } 107 108 mlxsw_reg_mcia_pack(mcia_pl, module, 0, page, offset, size, i2c_addr); 109 110 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl); 111 if (err) 112 return err; 113 114 status = mlxsw_reg_mcia_status_get(mcia_pl); 115 if (status) 116 return -EIO; 117 118 mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp); 119 memcpy(data, eeprom_tmp, size); 120 *p_read_size = size; 121 122 return 0; 123 } 124 125 int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module, 126 int off, int *temp) 127 { 128 char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE]; 129 union { 130 u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE]; 131 u16 temp; 132 } temp_thresh; 133 char mcia_pl[MLXSW_REG_MCIA_LEN] = {0}; 134 char mtmp_pl[MLXSW_REG_MTMP_LEN]; 135 unsigned int module_temp; 136 bool qsfp, cmis; 137 int page; 138 int err; 139 140 mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module, 141 false, false); 142 err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl); 143 if (err) 144 return err; 145 mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, NULL); 146 if (!module_temp) { 147 *temp = 0; 148 return 0; 149 } 150 151 /* Read Free Side Device Temperature Thresholds from page 03h 152 * (MSB at lower byte address). 153 * Bytes: 154 * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM); 155 * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM); 156 * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN); 157 * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN); 158 */ 159 160 /* Validate module identifier value. */ 161 err = mlxsw_env_validate_cable_ident(core, module, &qsfp, &cmis); 162 if (err) 163 return err; 164 165 if (qsfp) { 166 /* For QSFP/CMIS module-defined thresholds are located in page 167 * 02h, otherwise in page 03h. 168 */ 169 if (cmis) 170 page = MLXSW_REG_MCIA_TH_PAGE_CMIS_NUM; 171 else 172 page = MLXSW_REG_MCIA_TH_PAGE_NUM; 173 mlxsw_reg_mcia_pack(mcia_pl, module, 0, page, 174 MLXSW_REG_MCIA_TH_PAGE_OFF + off, 175 MLXSW_REG_MCIA_TH_ITEM_SIZE, 176 MLXSW_REG_MCIA_I2C_ADDR_LOW); 177 } else { 178 mlxsw_reg_mcia_pack(mcia_pl, module, 0, 179 MLXSW_REG_MCIA_PAGE0_LO, 180 off, MLXSW_REG_MCIA_TH_ITEM_SIZE, 181 MLXSW_REG_MCIA_I2C_ADDR_HIGH); 182 } 183 184 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl); 185 if (err) 186 return err; 187 188 mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp); 189 memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE); 190 *temp = temp_thresh.temp * 1000; 191 192 return 0; 193 } 194 195 int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module, 196 struct ethtool_modinfo *modinfo) 197 { 198 u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE]; 199 u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE; 200 u8 module_rev_id, module_id, diag_mon; 201 unsigned int read_size; 202 int err; 203 204 err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 0, offset, 205 module_info, false, &read_size); 206 if (err) 207 return err; 208 209 if (read_size < offset) 210 return -EIO; 211 212 module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID]; 213 module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID]; 214 215 switch (module_id) { 216 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP: 217 modinfo->type = ETH_MODULE_SFF_8436; 218 modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN; 219 break; 220 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: 221 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28: 222 if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 || 223 module_rev_id >= 224 MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) { 225 modinfo->type = ETH_MODULE_SFF_8636; 226 modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN; 227 } else { 228 modinfo->type = ETH_MODULE_SFF_8436; 229 modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN; 230 } 231 break; 232 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP: 233 /* Verify if transceiver provides diagnostic monitoring page */ 234 err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 235 SFP_DIAGMON, 1, &diag_mon, 236 false, &read_size); 237 if (err) 238 return err; 239 240 if (read_size < 1) 241 return -EIO; 242 243 modinfo->type = ETH_MODULE_SFF_8472; 244 if (diag_mon) 245 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; 246 else 247 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2; 248 break; 249 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD: 250 /* Use SFF_8636 as base type. ethtool should recognize specific 251 * type through the identifier value. 252 */ 253 modinfo->type = ETH_MODULE_SFF_8636; 254 /* Verify if module EEPROM is a flat memory. In case of flat 255 * memory only page 00h (0-255 bytes) can be read. Otherwise 256 * upper pages 01h and 02h can also be read. Upper pages 10h 257 * and 11h are currently not supported by the driver. 258 */ 259 if (module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_TYPE_ID] & 260 MLXSW_REG_MCIA_EEPROM_CMIS_FLAT_MEMORY) 261 modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN; 262 else 263 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; 264 break; 265 default: 266 return -EINVAL; 267 } 268 269 return 0; 270 } 271 EXPORT_SYMBOL(mlxsw_env_get_module_info); 272 273 int mlxsw_env_get_module_eeprom(struct net_device *netdev, 274 struct mlxsw_core *mlxsw_core, int module, 275 struct ethtool_eeprom *ee, u8 *data) 276 { 277 int offset = ee->offset; 278 unsigned int read_size; 279 bool qsfp, cmis; 280 int i = 0; 281 int err; 282 283 if (!ee->len) 284 return -EINVAL; 285 286 memset(data, 0, ee->len); 287 /* Validate module identifier value. */ 288 err = mlxsw_env_validate_cable_ident(mlxsw_core, module, &qsfp, &cmis); 289 if (err) 290 return err; 291 292 while (i < ee->len) { 293 err = mlxsw_env_query_module_eeprom(mlxsw_core, module, offset, 294 ee->len - i, data + i, 295 qsfp, &read_size); 296 if (err) { 297 netdev_err(netdev, "Eeprom query failed\n"); 298 return err; 299 } 300 301 i += read_size; 302 offset += read_size; 303 } 304 305 return 0; 306 } 307 EXPORT_SYMBOL(mlxsw_env_get_module_eeprom); 308 309 static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core, 310 u8 module, 311 bool *p_has_temp_sensor) 312 { 313 char mtbr_pl[MLXSW_REG_MTBR_LEN]; 314 u16 temp; 315 int err; 316 317 mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module, 318 1); 319 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtbr), mtbr_pl); 320 if (err) 321 return err; 322 323 mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL); 324 325 switch (temp) { 326 case MLXSW_REG_MTBR_BAD_SENS_INFO: 327 case MLXSW_REG_MTBR_NO_CONN: 328 case MLXSW_REG_MTBR_NO_TEMP_SENS: 329 case MLXSW_REG_MTBR_INDEX_NA: 330 *p_has_temp_sensor = false; 331 break; 332 default: 333 *p_has_temp_sensor = temp ? true : false; 334 } 335 return 0; 336 } 337 338 static int mlxsw_env_temp_event_set(struct mlxsw_core *mlxsw_core, 339 u16 sensor_index, bool enable) 340 { 341 char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0}; 342 enum mlxsw_reg_mtmp_tee tee; 343 int err, threshold_hi; 344 345 mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, sensor_index); 346 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl); 347 if (err) 348 return err; 349 350 if (enable) { 351 err = mlxsw_env_module_temp_thresholds_get(mlxsw_core, 352 sensor_index - 353 MLXSW_REG_MTMP_MODULE_INDEX_MIN, 354 SFP_TEMP_HIGH_WARN, 355 &threshold_hi); 356 /* In case it is not possible to query the module's threshold, 357 * use the default value. 358 */ 359 if (err) 360 threshold_hi = MLXSW_REG_MTMP_THRESH_HI; 361 else 362 /* mlxsw_env_module_temp_thresholds_get() multiplies 363 * Celsius degrees by 1000 whereas MTMP expects 364 * temperature in 0.125 Celsius degrees units. 365 * Convert threshold_hi to correct units. 366 */ 367 threshold_hi = threshold_hi / 1000 * 8; 368 369 mlxsw_reg_mtmp_temperature_threshold_hi_set(mtmp_pl, threshold_hi); 370 mlxsw_reg_mtmp_temperature_threshold_lo_set(mtmp_pl, threshold_hi - 371 MLXSW_REG_MTMP_HYSTERESIS_TEMP); 372 } 373 tee = enable ? MLXSW_REG_MTMP_TEE_GENERATE_EVENT : MLXSW_REG_MTMP_TEE_NO_EVENT; 374 mlxsw_reg_mtmp_tee_set(mtmp_pl, tee); 375 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl); 376 } 377 378 static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core, 379 u8 module_count) 380 { 381 int i, err, sensor_index; 382 bool has_temp_sensor; 383 384 for (i = 0; i < module_count; i++) { 385 err = mlxsw_env_module_has_temp_sensor(mlxsw_core, i, 386 &has_temp_sensor); 387 if (err) 388 return err; 389 390 if (!has_temp_sensor) 391 continue; 392 393 sensor_index = i + MLXSW_REG_MTMP_MODULE_INDEX_MIN; 394 err = mlxsw_env_temp_event_set(mlxsw_core, sensor_index, true); 395 if (err) 396 return err; 397 } 398 399 return 0; 400 } 401 402 static void mlxsw_env_mtwe_event_func(const struct mlxsw_reg_info *reg, 403 char *mtwe_pl, void *priv) 404 { 405 struct mlxsw_env *mlxsw_env = priv; 406 int i, sensor_warning; 407 bool is_overheat; 408 409 for (i = 0; i < mlxsw_env->module_count; i++) { 410 /* 64-127 of sensor_index are mapped to the port modules 411 * sequentially (module 0 is mapped to sensor_index 64, 412 * module 1 to sensor_index 65 and so on) 413 */ 414 sensor_warning = 415 mlxsw_reg_mtwe_sensor_warning_get(mtwe_pl, 416 i + MLXSW_REG_MTMP_MODULE_INDEX_MIN); 417 spin_lock(&mlxsw_env->module_info_lock); 418 is_overheat = 419 mlxsw_env->module_info[i].is_overheat; 420 421 if ((is_overheat && sensor_warning) || 422 (!is_overheat && !sensor_warning)) { 423 /* Current state is "warning" and MTWE still reports 424 * warning OR current state in "no warning" and MTWE 425 * does not report warning. 426 */ 427 spin_unlock(&mlxsw_env->module_info_lock); 428 continue; 429 } else if (is_overheat && !sensor_warning) { 430 /* MTWE reports "no warning", turn is_overheat off. 431 */ 432 mlxsw_env->module_info[i].is_overheat = false; 433 spin_unlock(&mlxsw_env->module_info_lock); 434 } else { 435 /* Current state is "no warning" and MTWE reports 436 * "warning", increase the counter and turn is_overheat 437 * on. 438 */ 439 mlxsw_env->module_info[i].is_overheat = true; 440 mlxsw_env->module_info[i].module_overheat_counter++; 441 spin_unlock(&mlxsw_env->module_info_lock); 442 } 443 } 444 } 445 446 static const struct mlxsw_listener mlxsw_env_temp_warn_listener = 447 MLXSW_EVENTL(mlxsw_env_mtwe_event_func, MTWE, MTWE); 448 449 static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core) 450 { 451 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); 452 453 if (!mlxsw_core_temp_warn_enabled(mlxsw_core)) 454 return 0; 455 456 return mlxsw_core_trap_register(mlxsw_core, 457 &mlxsw_env_temp_warn_listener, 458 mlxsw_env); 459 } 460 461 static void mlxsw_env_temp_warn_event_unregister(struct mlxsw_env *mlxsw_env) 462 { 463 if (!mlxsw_core_temp_warn_enabled(mlxsw_env->core)) 464 return; 465 466 mlxsw_core_trap_unregister(mlxsw_env->core, 467 &mlxsw_env_temp_warn_listener, mlxsw_env); 468 } 469 470 struct mlxsw_env_module_plug_unplug_event { 471 struct mlxsw_env *mlxsw_env; 472 u8 module; 473 struct work_struct work; 474 }; 475 476 static void mlxsw_env_pmpe_event_work(struct work_struct *work) 477 { 478 struct mlxsw_env_module_plug_unplug_event *event; 479 struct mlxsw_env *mlxsw_env; 480 bool has_temp_sensor; 481 u16 sensor_index; 482 int err; 483 484 event = container_of(work, struct mlxsw_env_module_plug_unplug_event, 485 work); 486 mlxsw_env = event->mlxsw_env; 487 488 spin_lock_bh(&mlxsw_env->module_info_lock); 489 mlxsw_env->module_info[event->module].is_overheat = false; 490 spin_unlock_bh(&mlxsw_env->module_info_lock); 491 492 err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core, event->module, 493 &has_temp_sensor); 494 /* Do not disable events on modules without sensors or faulty sensors 495 * because FW returns errors. 496 */ 497 if (err) 498 goto out; 499 500 if (!has_temp_sensor) 501 goto out; 502 503 sensor_index = event->module + MLXSW_REG_MTMP_MODULE_INDEX_MIN; 504 mlxsw_env_temp_event_set(mlxsw_env->core, sensor_index, true); 505 506 out: 507 kfree(event); 508 } 509 510 static void 511 mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl, 512 void *priv) 513 { 514 struct mlxsw_env_module_plug_unplug_event *event; 515 enum mlxsw_reg_pmpe_module_status module_status; 516 u8 module = mlxsw_reg_pmpe_module_get(pmpe_pl); 517 struct mlxsw_env *mlxsw_env = priv; 518 519 if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) 520 return; 521 522 module_status = mlxsw_reg_pmpe_module_status_get(pmpe_pl); 523 if (module_status != MLXSW_REG_PMPE_MODULE_STATUS_PLUGGED_ENABLED) 524 return; 525 526 event = kmalloc(sizeof(*event), GFP_ATOMIC); 527 if (!event) 528 return; 529 530 event->mlxsw_env = mlxsw_env; 531 event->module = module; 532 INIT_WORK(&event->work, mlxsw_env_pmpe_event_work); 533 mlxsw_core_schedule_work(&event->work); 534 } 535 536 static const struct mlxsw_listener mlxsw_env_module_plug_listener = 537 MLXSW_EVENTL(mlxsw_env_pmpe_listener_func, PMPE, PMPE); 538 539 static int 540 mlxsw_env_module_plug_event_register(struct mlxsw_core *mlxsw_core) 541 { 542 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); 543 544 if (!mlxsw_core_temp_warn_enabled(mlxsw_core)) 545 return 0; 546 547 return mlxsw_core_trap_register(mlxsw_core, 548 &mlxsw_env_module_plug_listener, 549 mlxsw_env); 550 } 551 552 static void 553 mlxsw_env_module_plug_event_unregister(struct mlxsw_env *mlxsw_env) 554 { 555 if (!mlxsw_core_temp_warn_enabled(mlxsw_env->core)) 556 return; 557 558 mlxsw_core_trap_unregister(mlxsw_env->core, 559 &mlxsw_env_module_plug_listener, 560 mlxsw_env); 561 } 562 563 static int 564 mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core, 565 u8 module_count) 566 { 567 int i, err; 568 569 for (i = 0; i < module_count; i++) { 570 char pmaos_pl[MLXSW_REG_PMAOS_LEN]; 571 572 mlxsw_reg_pmaos_pack(pmaos_pl, i, 573 MLXSW_REG_PMAOS_E_GENERATE_EVENT); 574 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl); 575 if (err) 576 return err; 577 } 578 return 0; 579 } 580 581 int 582 mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module, 583 u64 *p_counter) 584 { 585 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); 586 587 /* Prevent switch driver from accessing uninitialized data. */ 588 if (!mlxsw_core_is_initialized(mlxsw_core)) { 589 *p_counter = 0; 590 return 0; 591 } 592 593 if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) 594 return -EINVAL; 595 596 spin_lock_bh(&mlxsw_env->module_info_lock); 597 *p_counter = mlxsw_env->module_info[module].module_overheat_counter; 598 spin_unlock_bh(&mlxsw_env->module_info_lock); 599 600 return 0; 601 } 602 EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get); 603 604 int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env) 605 { 606 char mgpir_pl[MLXSW_REG_MGPIR_LEN]; 607 struct mlxsw_env *env; 608 u8 module_count; 609 int err; 610 611 mlxsw_reg_mgpir_pack(mgpir_pl); 612 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl); 613 if (err) 614 return err; 615 616 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &module_count); 617 618 env = kzalloc(struct_size(env, module_info, module_count), GFP_KERNEL); 619 if (!env) 620 return -ENOMEM; 621 622 spin_lock_init(&env->module_info_lock); 623 env->core = mlxsw_core; 624 env->module_count = module_count; 625 *p_env = env; 626 627 err = mlxsw_env_temp_warn_event_register(mlxsw_core); 628 if (err) 629 goto err_temp_warn_event_register; 630 631 err = mlxsw_env_module_plug_event_register(mlxsw_core); 632 if (err) 633 goto err_module_plug_event_register; 634 635 err = mlxsw_env_module_oper_state_event_enable(mlxsw_core, 636 env->module_count); 637 if (err) 638 goto err_oper_state_event_enable; 639 640 err = mlxsw_env_module_temp_event_enable(mlxsw_core, env->module_count); 641 if (err) 642 goto err_temp_event_enable; 643 644 return 0; 645 646 err_temp_event_enable: 647 err_oper_state_event_enable: 648 mlxsw_env_module_plug_event_unregister(env); 649 err_module_plug_event_register: 650 mlxsw_env_temp_warn_event_unregister(env); 651 err_temp_warn_event_register: 652 kfree(env); 653 return err; 654 } 655 656 void mlxsw_env_fini(struct mlxsw_env *env) 657 { 658 mlxsw_env_module_plug_event_unregister(env); 659 /* Make sure there is no more event work scheduled. */ 660 mlxsw_core_flush_owq(); 661 mlxsw_env_temp_warn_event_unregister(env); 662 kfree(env); 663 } 664