1537 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */3 4#include <linux/kernel.h>5#include <linux/err.h>6#include <linux/ethtool.h>7#include <linux/sfp.h>8#include <linux/mutex.h>9 10#include "core.h"11#include "core_env.h"12#include "item.h"13#include "reg.h"14 15struct mlxsw_env_module_info {16 u64 module_overheat_counter;17 bool is_overheat;18 int num_ports_mapped;19 int num_ports_up;20 enum ethtool_module_power_mode_policy power_mode_policy;21 enum mlxsw_reg_pmtm_module_type type;22};23 24struct mlxsw_env_line_card {25 u8 module_count;26 bool active;27 struct mlxsw_env_module_info module_info[];28};29 30struct mlxsw_env {31 struct mlxsw_core *core;32 const struct mlxsw_bus_info *bus_info;33 u8 max_module_count; /* Maximum number of modules per-slot. */34 u8 num_of_slots; /* Including the main board. */35 u8 max_eeprom_len; /* Maximum module EEPROM transaction length. */36 struct mutex line_cards_lock; /* Protects line cards. */37 struct mlxsw_env_line_card *line_cards[] __counted_by(num_of_slots);38};39 40static bool __mlxsw_env_linecard_is_active(struct mlxsw_env *mlxsw_env,41 u8 slot_index)42{43 return mlxsw_env->line_cards[slot_index]->active;44}45 46static bool mlxsw_env_linecard_is_active(struct mlxsw_env *mlxsw_env,47 u8 slot_index)48{49 bool active;50 51 mutex_lock(&mlxsw_env->line_cards_lock);52 active = __mlxsw_env_linecard_is_active(mlxsw_env, slot_index);53 mutex_unlock(&mlxsw_env->line_cards_lock);54 55 return active;56}57 58static struct59mlxsw_env_module_info *mlxsw_env_module_info_get(struct mlxsw_core *mlxsw_core,60 u8 slot_index, u8 module)61{62 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);63 64 return &mlxsw_env->line_cards[slot_index]->module_info[module];65}66 67static int __mlxsw_env_validate_module_type(struct mlxsw_core *core,68 u8 slot_index, u8 module)69{70 struct mlxsw_env *mlxsw_env = mlxsw_core_env(core);71 struct mlxsw_env_module_info *module_info;72 int err;73 74 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))75 return 0;76 77 module_info = mlxsw_env_module_info_get(core, slot_index, module);78 switch (module_info->type) {79 case MLXSW_REG_PMTM_MODULE_TYPE_TWISTED_PAIR:80 err = -EINVAL;81 break;82 default:83 err = 0;84 }85 86 return err;87}88 89static int mlxsw_env_validate_module_type(struct mlxsw_core *core,90 u8 slot_index, u8 module)91{92 struct mlxsw_env *mlxsw_env = mlxsw_core_env(core);93 int err;94 95 mutex_lock(&mlxsw_env->line_cards_lock);96 err = __mlxsw_env_validate_module_type(core, slot_index, module);97 mutex_unlock(&mlxsw_env->line_cards_lock);98 99 return err;100}101 102static int103mlxsw_env_validate_cable_ident(struct mlxsw_core *core, u8 slot_index, int id,104 bool *qsfp, bool *cmis)105{106 char mcia_pl[MLXSW_REG_MCIA_LEN];107 char *eeprom_tmp;108 u8 ident;109 int err;110 111 err = mlxsw_env_validate_module_type(core, slot_index, id);112 if (err)113 return err;114 115 mlxsw_reg_mcia_pack(mcia_pl, slot_index, id,116 MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1,117 MLXSW_REG_MCIA_I2C_ADDR_LOW);118 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);119 if (err)120 return err;121 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);122 ident = eeprom_tmp[0];123 *cmis = false;124 switch (ident) {125 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:126 *qsfp = false;127 break;128 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:129 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:130 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:131 *qsfp = true;132 break;133 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:134 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_OSFP:135 *qsfp = true;136 *cmis = true;137 break;138 default:139 return -EINVAL;140 }141 142 return 0;143}144 145static int146mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, u8 slot_index,147 int module, u16 offset, u16 size, void *data,148 bool qsfp, unsigned int *p_read_size)149{150 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);151 char mcia_pl[MLXSW_REG_MCIA_LEN];152 char *eeprom_tmp;153 u16 i2c_addr;154 u8 page = 0;155 int status;156 int err;157 158 size = min_t(u16, size, mlxsw_env->max_eeprom_len);159 160 if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&161 offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)162 /* Cross pages read, read until offset 256 in low page */163 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;164 165 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;166 if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {167 if (qsfp) {168 /* When reading upper pages 1, 2 and 3 the offset169 * starts at 128. Please refer to "QSFP+ Memory Map"170 * figure in SFF-8436 specification and to "CMIS Module171 * Memory Map" figure in CMIS specification for172 * graphical depiction.173 */174 page = MLXSW_REG_MCIA_PAGE_GET(offset);175 offset -= MLXSW_REG_MCIA_EEPROM_UP_PAGE_LENGTH * page;176 if (offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)177 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;178 } else {179 /* When reading upper pages 1, 2 and 3 the offset180 * starts at 0 and I2C high address is used. Please refer181 * to "Memory Organization" figure in SFF-8472182 * specification for graphical depiction.183 */184 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;185 offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;186 }187 }188 189 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, page, offset, size,190 i2c_addr);191 192 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);193 if (err)194 return err;195 196 status = mlxsw_reg_mcia_status_get(mcia_pl);197 if (status)198 return -EIO;199 200 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);201 memcpy(data, eeprom_tmp, size);202 *p_read_size = size;203 204 return 0;205}206 207int208mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, u8 slot_index,209 int module, int off, int *temp)210{211 unsigned int module_temp, module_crit, module_emerg;212 union {213 u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];214 u16 temp;215 } temp_thresh;216 char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};217 char mtmp_pl[MLXSW_REG_MTMP_LEN];218 char *eeprom_tmp;219 bool qsfp, cmis;220 int page;221 int err;222 223 mlxsw_reg_mtmp_pack(mtmp_pl, slot_index,224 MLXSW_REG_MTMP_MODULE_INDEX_MIN + module, false,225 false);226 err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);227 if (err)228 return err;229 mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, &module_crit,230 &module_emerg, NULL);231 if (!module_temp) {232 *temp = 0;233 return 0;234 }235 236 /* Validate if threshold reading is available through MTMP register,237 * otherwise fallback to read through MCIA.238 */239 if (module_emerg) {240 *temp = off == SFP_TEMP_HIGH_WARN ? module_crit : module_emerg;241 return 0;242 }243 244 /* Read Free Side Device Temperature Thresholds from page 03h245 * (MSB at lower byte address).246 * Bytes:247 * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);248 * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);249 * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);250 * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);251 */252 253 /* Validate module identifier value. */254 err = mlxsw_env_validate_cable_ident(core, slot_index, module, &qsfp,255 &cmis);256 if (err)257 return err;258 259 if (qsfp) {260 /* For QSFP/CMIS module-defined thresholds are located in page261 * 02h, otherwise in page 03h.262 */263 if (cmis)264 page = MLXSW_REG_MCIA_TH_PAGE_CMIS_NUM;265 else266 page = MLXSW_REG_MCIA_TH_PAGE_NUM;267 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, page,268 MLXSW_REG_MCIA_TH_PAGE_OFF + off,269 MLXSW_REG_MCIA_TH_ITEM_SIZE,270 MLXSW_REG_MCIA_I2C_ADDR_LOW);271 } else {272 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module,273 MLXSW_REG_MCIA_PAGE0_LO,274 off, MLXSW_REG_MCIA_TH_ITEM_SIZE,275 MLXSW_REG_MCIA_I2C_ADDR_HIGH);276 }277 278 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);279 if (err)280 return err;281 282 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);283 memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);284 *temp = temp_thresh.temp * 1000;285 286 return 0;287}288 289int mlxsw_env_get_module_info(struct net_device *netdev,290 struct mlxsw_core *mlxsw_core, u8 slot_index,291 int module, struct ethtool_modinfo *modinfo)292{293 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);294 u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];295 u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;296 u8 module_rev_id, module_id, diag_mon;297 unsigned int read_size;298 int err;299 300 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {301 netdev_err(netdev, "Cannot read EEPROM of module on an inactive line card\n");302 return -EIO;303 }304 305 err = mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);306 if (err) {307 netdev_err(netdev,308 "EEPROM is not equipped on port module type");309 return err;310 }311 312 err = mlxsw_env_query_module_eeprom(mlxsw_core, slot_index, module, 0,313 offset, module_info, false,314 &read_size);315 if (err)316 return err;317 318 if (read_size < offset)319 return -EIO;320 321 module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];322 module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];323 324 switch (module_id) {325 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:326 modinfo->type = ETH_MODULE_SFF_8436;327 modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;328 break;329 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:330 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:331 if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||332 module_rev_id >=333 MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {334 modinfo->type = ETH_MODULE_SFF_8636;335 modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;336 } else {337 modinfo->type = ETH_MODULE_SFF_8436;338 modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;339 }340 break;341 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:342 /* Verify if transceiver provides diagnostic monitoring page */343 err = mlxsw_env_query_module_eeprom(mlxsw_core, slot_index,344 module, SFP_DIAGMON, 1,345 &diag_mon, false,346 &read_size);347 if (err)348 return err;349 350 if (read_size < 1)351 return -EIO;352 353 modinfo->type = ETH_MODULE_SFF_8472;354 if (diag_mon)355 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;356 else357 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;358 break;359 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:360 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_OSFP:361 /* Use SFF_8636 as base type. ethtool should recognize specific362 * type through the identifier value.363 */364 modinfo->type = ETH_MODULE_SFF_8636;365 /* Verify if module EEPROM is a flat memory. In case of flat366 * memory only page 00h (0-255 bytes) can be read. Otherwise367 * upper pages 01h and 02h can also be read. Upper pages 10h368 * and 11h are currently not supported by the driver.369 */370 if (module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_TYPE_ID] &371 MLXSW_REG_MCIA_EEPROM_CMIS_FLAT_MEMORY)372 modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;373 else374 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;375 break;376 default:377 return -EINVAL;378 }379 380 return 0;381}382EXPORT_SYMBOL(mlxsw_env_get_module_info);383 384int mlxsw_env_get_module_eeprom(struct net_device *netdev,385 struct mlxsw_core *mlxsw_core, u8 slot_index,386 int module, struct ethtool_eeprom *ee,387 u8 *data)388{389 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);390 int offset = ee->offset;391 unsigned int read_size;392 bool qsfp, cmis;393 int i = 0;394 int err;395 396 if (!ee->len)397 return -EINVAL;398 399 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {400 netdev_err(netdev, "Cannot read EEPROM of module on an inactive line card\n");401 return -EIO;402 }403 404 memset(data, 0, ee->len);405 /* Validate module identifier value. */406 err = mlxsw_env_validate_cable_ident(mlxsw_core, slot_index, module,407 &qsfp, &cmis);408 if (err)409 return err;410 411 while (i < ee->len) {412 err = mlxsw_env_query_module_eeprom(mlxsw_core, slot_index,413 module, offset,414 ee->len - i, data + i,415 qsfp, &read_size);416 if (err) {417 netdev_err(netdev, "Eeprom query failed\n");418 return err;419 }420 421 i += read_size;422 offset += read_size;423 }424 425 return 0;426}427EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);428 429static int mlxsw_env_mcia_status_process(const char *mcia_pl,430 struct netlink_ext_ack *extack)431{432 u8 status = mlxsw_reg_mcia_status_get(mcia_pl);433 434 switch (status) {435 case MLXSW_REG_MCIA_STATUS_GOOD:436 return 0;437 case MLXSW_REG_MCIA_STATUS_NO_EEPROM_MODULE:438 NL_SET_ERR_MSG_MOD(extack, "No response from module's EEPROM");439 return -EIO;440 case MLXSW_REG_MCIA_STATUS_MODULE_NOT_SUPPORTED:441 NL_SET_ERR_MSG_MOD(extack, "Module type not supported by the device");442 return -EOPNOTSUPP;443 case MLXSW_REG_MCIA_STATUS_MODULE_NOT_CONNECTED:444 NL_SET_ERR_MSG_MOD(extack, "No module present indication");445 return -EIO;446 case MLXSW_REG_MCIA_STATUS_I2C_ERROR:447 NL_SET_ERR_MSG_MOD(extack, "Error occurred while trying to access module's EEPROM using I2C");448 return -EIO;449 case MLXSW_REG_MCIA_STATUS_MODULE_DISABLED:450 NL_SET_ERR_MSG_MOD(extack, "Module is disabled");451 return -EIO;452 default:453 NL_SET_ERR_MSG_MOD(extack, "Unknown error");454 return -EIO;455 }456}457 458int459mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core,460 u8 slot_index, u8 module,461 const struct ethtool_module_eeprom *page,462 struct netlink_ext_ack *extack)463{464 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);465 u32 bytes_read = 0;466 u16 device_addr;467 int err;468 469 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {470 NL_SET_ERR_MSG_MOD(extack,471 "Cannot read EEPROM of module on an inactive line card");472 return -EIO;473 }474 475 err = mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);476 if (err) {477 NL_SET_ERR_MSG_MOD(extack, "EEPROM is not equipped on port module type");478 return err;479 }480 481 /* Offset cannot be larger than 2 * ETH_MODULE_EEPROM_PAGE_LEN */482 device_addr = page->offset;483 484 while (bytes_read < page->length) {485 char mcia_pl[MLXSW_REG_MCIA_LEN];486 char *eeprom_tmp;487 u8 size;488 489 size = min_t(u8, page->length - bytes_read,490 mlxsw_env->max_eeprom_len);491 492 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, page->page,493 device_addr + bytes_read, size,494 page->i2c_address);495 mlxsw_reg_mcia_bank_number_set(mcia_pl, page->bank);496 497 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);498 if (err) {499 NL_SET_ERR_MSG_MOD(extack, "Failed to access module's EEPROM");500 return err;501 }502 503 err = mlxsw_env_mcia_status_process(mcia_pl, extack);504 if (err)505 return err;506 507 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);508 memcpy(page->data + bytes_read, eeprom_tmp, size);509 bytes_read += size;510 }511 512 return bytes_read;513}514EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page);515 516int517mlxsw_env_set_module_eeprom_by_page(struct mlxsw_core *mlxsw_core,518 u8 slot_index, u8 module,519 const struct ethtool_module_eeprom *page,520 struct netlink_ext_ack *extack)521{522 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);523 u32 bytes_written = 0;524 u16 device_addr;525 int err;526 527 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {528 NL_SET_ERR_MSG_MOD(extack,529 "Cannot write to EEPROM of a module on an inactive line card");530 return -EIO;531 }532 533 err = mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);534 if (err) {535 NL_SET_ERR_MSG_MOD(extack, "EEPROM is not equipped on port module type");536 return err;537 }538 539 device_addr = page->offset;540 541 while (bytes_written < page->length) {542 char mcia_pl[MLXSW_REG_MCIA_LEN];543 char eeprom_tmp[128] = {};544 u8 size;545 546 size = min_t(u8, page->length - bytes_written,547 mlxsw_env->max_eeprom_len);548 549 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, page->page,550 device_addr + bytes_written, size,551 page->i2c_address);552 mlxsw_reg_mcia_bank_number_set(mcia_pl, page->bank);553 memcpy(eeprom_tmp, page->data + bytes_written, size);554 mlxsw_reg_mcia_eeprom_memcpy_to(mcia_pl, eeprom_tmp);555 556 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcia), mcia_pl);557 if (err) {558 NL_SET_ERR_MSG_MOD(extack, "Failed to access module's EEPROM");559 return err;560 }561 562 err = mlxsw_env_mcia_status_process(mcia_pl, extack);563 if (err)564 return err;565 566 bytes_written += size;567 }568 569 return 0;570}571EXPORT_SYMBOL(mlxsw_env_set_module_eeprom_by_page);572 573static int mlxsw_env_module_reset(struct mlxsw_core *mlxsw_core, u8 slot_index,574 u8 module)575{576 char pmaos_pl[MLXSW_REG_PMAOS_LEN];577 578 mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, module);579 mlxsw_reg_pmaos_rst_set(pmaos_pl, true);580 581 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);582}583 584int mlxsw_env_reset_module(struct net_device *netdev,585 struct mlxsw_core *mlxsw_core, u8 slot_index,586 u8 module, u32 *flags)587{588 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);589 struct mlxsw_env_module_info *module_info;590 u32 req = *flags;591 int err;592 593 if (!(req & ETH_RESET_PHY) &&594 !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT)))595 return 0;596 597 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {598 netdev_err(netdev, "Cannot reset module on an inactive line card\n");599 return -EIO;600 }601 602 mutex_lock(&mlxsw_env->line_cards_lock);603 604 err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);605 if (err) {606 netdev_err(netdev, "Reset module is not supported on port module type\n");607 goto out;608 }609 610 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);611 if (module_info->num_ports_up) {612 netdev_err(netdev, "Cannot reset module when ports using it are administratively up\n");613 err = -EINVAL;614 goto out;615 }616 617 if (module_info->num_ports_mapped > 1 &&618 !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) {619 netdev_err(netdev, "Cannot reset module without \"phy-shared\" flag when shared by multiple ports\n");620 err = -EINVAL;621 goto out;622 }623 624 err = mlxsw_env_module_reset(mlxsw_core, slot_index, module);625 if (err) {626 netdev_err(netdev, "Failed to reset module\n");627 goto out;628 }629 630 *flags &= ~(ETH_RESET_PHY | (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT));631 632out:633 mutex_unlock(&mlxsw_env->line_cards_lock);634 return err;635}636EXPORT_SYMBOL(mlxsw_env_reset_module);637 638int639mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,640 u8 module,641 struct ethtool_module_power_mode_params *params,642 struct netlink_ext_ack *extack)643{644 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);645 struct mlxsw_env_module_info *module_info;646 char mcion_pl[MLXSW_REG_MCION_LEN];647 u32 status_bits;648 int err = 0;649 650 mutex_lock(&mlxsw_env->line_cards_lock);651 652 err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);653 if (err) {654 NL_SET_ERR_MSG_MOD(extack, "Power mode is not supported on port module type");655 goto out;656 }657 658 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);659 params->policy = module_info->power_mode_policy;660 661 /* Avoid accessing an inactive line card, as it will result in an error. */662 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))663 goto out;664 665 mlxsw_reg_mcion_pack(mcion_pl, slot_index, module);666 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcion), mcion_pl);667 if (err) {668 NL_SET_ERR_MSG_MOD(extack, "Failed to retrieve module's power mode");669 goto out;670 }671 672 status_bits = mlxsw_reg_mcion_module_status_bits_get(mcion_pl);673 if (!(status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_PRESENT_MASK))674 goto out;675 676 if (status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_LOW_POWER_MASK)677 params->mode = ETHTOOL_MODULE_POWER_MODE_LOW;678 else679 params->mode = ETHTOOL_MODULE_POWER_MODE_HIGH;680 681out:682 mutex_unlock(&mlxsw_env->line_cards_lock);683 return err;684}685EXPORT_SYMBOL(mlxsw_env_get_module_power_mode);686 687static int mlxsw_env_module_enable_set(struct mlxsw_core *mlxsw_core,688 u8 slot_index, u8 module, bool enable)689{690 enum mlxsw_reg_pmaos_admin_status admin_status;691 char pmaos_pl[MLXSW_REG_PMAOS_LEN];692 693 mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, module);694 admin_status = enable ? MLXSW_REG_PMAOS_ADMIN_STATUS_ENABLED :695 MLXSW_REG_PMAOS_ADMIN_STATUS_DISABLED;696 mlxsw_reg_pmaos_admin_status_set(pmaos_pl, admin_status);697 mlxsw_reg_pmaos_ase_set(pmaos_pl, true);698 699 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);700}701 702static int mlxsw_env_module_low_power_set(struct mlxsw_core *mlxsw_core,703 u8 slot_index, u8 module,704 bool low_power)705{706 u16 eeprom_override_mask, eeprom_override;707 char pmmp_pl[MLXSW_REG_PMMP_LEN];708 709 mlxsw_reg_pmmp_pack(pmmp_pl, slot_index, module);710 mlxsw_reg_pmmp_sticky_set(pmmp_pl, true);711 /* Mask all the bits except low power mode. */712 eeprom_override_mask = ~MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK;713 mlxsw_reg_pmmp_eeprom_override_mask_set(pmmp_pl, eeprom_override_mask);714 eeprom_override = low_power ? MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK :715 0;716 mlxsw_reg_pmmp_eeprom_override_set(pmmp_pl, eeprom_override);717 718 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmmp), pmmp_pl);719}720 721static int __mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core,722 u8 slot_index, u8 module,723 bool low_power,724 struct netlink_ext_ack *extack)725{726 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);727 int err;728 729 /* Avoid accessing an inactive line card, as it will result in an error.730 * Cached configuration will be applied by mlxsw_env_got_active() when731 * line card becomes active.732 */733 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))734 return 0;735 736 err = mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, false);737 if (err) {738 NL_SET_ERR_MSG_MOD(extack, "Failed to disable module");739 return err;740 }741 742 err = mlxsw_env_module_low_power_set(mlxsw_core, slot_index, module,743 low_power);744 if (err) {745 NL_SET_ERR_MSG_MOD(extack, "Failed to set module's power mode");746 goto err_module_low_power_set;747 }748 749 err = mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, true);750 if (err) {751 NL_SET_ERR_MSG_MOD(extack, "Failed to enable module");752 goto err_module_enable_set;753 }754 755 return 0;756 757err_module_enable_set:758 mlxsw_env_module_low_power_set(mlxsw_core, slot_index, module,759 !low_power);760err_module_low_power_set:761 mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, true);762 return err;763}764 765static int766mlxsw_env_set_module_power_mode_apply(struct mlxsw_core *mlxsw_core,767 u8 slot_index, u8 module,768 enum ethtool_module_power_mode_policy policy,769 struct netlink_ext_ack *extack)770{771 struct mlxsw_env_module_info *module_info;772 bool low_power;773 int err = 0;774 775 err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);776 if (err) {777 NL_SET_ERR_MSG_MOD(extack,778 "Power mode set is not supported on port module type");779 goto out;780 }781 782 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);783 if (module_info->power_mode_policy == policy)784 goto out;785 786 /* If any ports are up, we are already in high power mode. */787 if (module_info->num_ports_up)788 goto out_set_policy;789 790 low_power = policy == ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO;791 err = __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module,792 low_power, extack);793 if (err)794 goto out;795 796out_set_policy:797 module_info->power_mode_policy = policy;798out:799 return err;800}801 802int803mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,804 u8 module,805 enum ethtool_module_power_mode_policy policy,806 struct netlink_ext_ack *extack)807{808 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);809 int err;810 811 if (policy != ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH &&812 policy != ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO) {813 NL_SET_ERR_MSG_MOD(extack, "Unsupported power mode policy");814 return -EOPNOTSUPP;815 }816 817 mutex_lock(&mlxsw_env->line_cards_lock);818 err = mlxsw_env_set_module_power_mode_apply(mlxsw_core, slot_index,819 module, policy, extack);820 mutex_unlock(&mlxsw_env->line_cards_lock);821 822 return err;823}824EXPORT_SYMBOL(mlxsw_env_set_module_power_mode);825 826static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core,827 u8 slot_index, u8 module,828 bool *p_has_temp_sensor)829{830 char mtbr_pl[MLXSW_REG_MTBR_LEN];831 u16 temp;832 int err;833 834 mlxsw_reg_mtbr_pack(mtbr_pl, slot_index,835 MLXSW_REG_MTBR_BASE_MODULE_INDEX + module);836 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtbr), mtbr_pl);837 if (err)838 return err;839 840 mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);841 842 switch (temp) {843 case MLXSW_REG_MTBR_BAD_SENS_INFO:844 case MLXSW_REG_MTBR_NO_CONN:845 case MLXSW_REG_MTBR_NO_TEMP_SENS:846 case MLXSW_REG_MTBR_INDEX_NA:847 *p_has_temp_sensor = false;848 break;849 default:850 *p_has_temp_sensor = temp ? true : false;851 }852 return 0;853}854 855static int856mlxsw_env_temp_event_set(struct mlxsw_core *mlxsw_core, u8 slot_index,857 u16 sensor_index, bool enable)858{859 char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0};860 enum mlxsw_reg_mtmp_tee tee;861 int err, threshold_hi;862 863 mlxsw_reg_mtmp_slot_index_set(mtmp_pl, slot_index);864 mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, sensor_index);865 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);866 if (err)867 return err;868 869 if (enable) {870 err = mlxsw_env_module_temp_thresholds_get(mlxsw_core,871 slot_index,872 sensor_index -873 MLXSW_REG_MTMP_MODULE_INDEX_MIN,874 SFP_TEMP_HIGH_WARN,875 &threshold_hi);876 /* In case it is not possible to query the module's threshold,877 * use the default value.878 */879 if (err)880 threshold_hi = MLXSW_REG_MTMP_THRESH_HI;881 else882 /* mlxsw_env_module_temp_thresholds_get() multiplies883 * Celsius degrees by 1000 whereas MTMP expects884 * temperature in 0.125 Celsius degrees units.885 * Convert threshold_hi to correct units.886 */887 threshold_hi = threshold_hi / 1000 * 8;888 889 mlxsw_reg_mtmp_temperature_threshold_hi_set(mtmp_pl, threshold_hi);890 mlxsw_reg_mtmp_temperature_threshold_lo_set(mtmp_pl, threshold_hi -891 MLXSW_REG_MTMP_HYSTERESIS_TEMP);892 }893 tee = enable ? MLXSW_REG_MTMP_TEE_GENERATE_EVENT : MLXSW_REG_MTMP_TEE_NO_EVENT;894 mlxsw_reg_mtmp_tee_set(mtmp_pl, tee);895 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);896}897 898static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core,899 u8 slot_index)900{901 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);902 int i, err, sensor_index;903 bool has_temp_sensor;904 905 for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {906 err = mlxsw_env_module_has_temp_sensor(mlxsw_core, slot_index,907 i, &has_temp_sensor);908 if (err)909 return err;910 911 if (!has_temp_sensor)912 continue;913 914 sensor_index = i + MLXSW_REG_MTMP_MODULE_INDEX_MIN;915 err = mlxsw_env_temp_event_set(mlxsw_core, slot_index,916 sensor_index, true);917 if (err)918 return err;919 }920 921 return 0;922}923 924struct mlxsw_env_module_temp_warn_event {925 struct mlxsw_env *mlxsw_env;926 char mtwe_pl[MLXSW_REG_MTWE_LEN];927 struct work_struct work;928};929 930static void mlxsw_env_mtwe_event_work(struct work_struct *work)931{932 struct mlxsw_env_module_temp_warn_event *event;933 struct mlxsw_env_module_info *module_info;934 struct mlxsw_env *mlxsw_env;935 int i, sensor_warning;936 bool is_overheat;937 938 event = container_of(work, struct mlxsw_env_module_temp_warn_event,939 work);940 mlxsw_env = event->mlxsw_env;941 942 for (i = 0; i < mlxsw_env->max_module_count; i++) {943 /* 64-127 of sensor_index are mapped to the port modules944 * sequentially (module 0 is mapped to sensor_index 64,945 * module 1 to sensor_index 65 and so on)946 */947 sensor_warning =948 mlxsw_reg_mtwe_sensor_warning_get(event->mtwe_pl,949 i + MLXSW_REG_MTMP_MODULE_INDEX_MIN);950 mutex_lock(&mlxsw_env->line_cards_lock);951 /* MTWE only supports main board. */952 module_info = mlxsw_env_module_info_get(mlxsw_env->core, 0, i);953 is_overheat = module_info->is_overheat;954 955 if ((is_overheat && sensor_warning) ||956 (!is_overheat && !sensor_warning)) {957 /* Current state is "warning" and MTWE still reports958 * warning OR current state in "no warning" and MTWE959 * does not report warning.960 */961 mutex_unlock(&mlxsw_env->line_cards_lock);962 continue;963 } else if (is_overheat && !sensor_warning) {964 /* MTWE reports "no warning", turn is_overheat off.965 */966 module_info->is_overheat = false;967 mutex_unlock(&mlxsw_env->line_cards_lock);968 } else {969 /* Current state is "no warning" and MTWE reports970 * "warning", increase the counter and turn is_overheat971 * on.972 */973 module_info->is_overheat = true;974 module_info->module_overheat_counter++;975 mutex_unlock(&mlxsw_env->line_cards_lock);976 }977 }978 979 kfree(event);980}981 982static void983mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info *reg, char *mtwe_pl,984 void *priv)985{986 struct mlxsw_env_module_temp_warn_event *event;987 struct mlxsw_env *mlxsw_env = priv;988 989 event = kmalloc(sizeof(*event), GFP_ATOMIC);990 if (!event)991 return;992 993 event->mlxsw_env = mlxsw_env;994 memcpy(event->mtwe_pl, mtwe_pl, MLXSW_REG_MTWE_LEN);995 INIT_WORK(&event->work, mlxsw_env_mtwe_event_work);996 mlxsw_core_schedule_work(&event->work);997}998 999static const struct mlxsw_listener mlxsw_env_temp_warn_listener =1000 MLXSW_CORE_EVENTL(mlxsw_env_mtwe_listener_func, MTWE);1001 1002static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core)1003{1004 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1005 1006 return mlxsw_core_trap_register(mlxsw_core,1007 &mlxsw_env_temp_warn_listener,1008 mlxsw_env);1009}1010 1011static void mlxsw_env_temp_warn_event_unregister(struct mlxsw_env *mlxsw_env)1012{1013 mlxsw_core_trap_unregister(mlxsw_env->core,1014 &mlxsw_env_temp_warn_listener, mlxsw_env);1015}1016 1017struct mlxsw_env_module_plug_unplug_event {1018 struct mlxsw_env *mlxsw_env;1019 u8 slot_index;1020 u8 module;1021 struct work_struct work;1022};1023 1024static void mlxsw_env_pmpe_event_work(struct work_struct *work)1025{1026 struct mlxsw_env_module_plug_unplug_event *event;1027 struct mlxsw_env_module_info *module_info;1028 struct mlxsw_env *mlxsw_env;1029 bool has_temp_sensor;1030 u16 sensor_index;1031 int err;1032 1033 event = container_of(work, struct mlxsw_env_module_plug_unplug_event,1034 work);1035 mlxsw_env = event->mlxsw_env;1036 1037 mutex_lock(&mlxsw_env->line_cards_lock);1038 module_info = mlxsw_env_module_info_get(mlxsw_env->core,1039 event->slot_index,1040 event->module);1041 module_info->is_overheat = false;1042 mutex_unlock(&mlxsw_env->line_cards_lock);1043 1044 err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core,1045 event->slot_index,1046 event->module,1047 &has_temp_sensor);1048 /* Do not disable events on modules without sensors or faulty sensors1049 * because FW returns errors.1050 */1051 if (err)1052 goto out;1053 1054 if (!has_temp_sensor)1055 goto out;1056 1057 sensor_index = event->module + MLXSW_REG_MTMP_MODULE_INDEX_MIN;1058 mlxsw_env_temp_event_set(mlxsw_env->core, event->slot_index,1059 sensor_index, true);1060 1061out:1062 kfree(event);1063}1064 1065static void1066mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl,1067 void *priv)1068{1069 u8 slot_index = mlxsw_reg_pmpe_slot_index_get(pmpe_pl);1070 struct mlxsw_env_module_plug_unplug_event *event;1071 enum mlxsw_reg_pmpe_module_status module_status;1072 u8 module = mlxsw_reg_pmpe_module_get(pmpe_pl);1073 struct mlxsw_env *mlxsw_env = priv;1074 1075 if (WARN_ON_ONCE(module >= mlxsw_env->max_module_count ||1076 slot_index >= mlxsw_env->num_of_slots))1077 return;1078 1079 module_status = mlxsw_reg_pmpe_module_status_get(pmpe_pl);1080 if (module_status != MLXSW_REG_PMPE_MODULE_STATUS_PLUGGED_ENABLED)1081 return;1082 1083 event = kmalloc(sizeof(*event), GFP_ATOMIC);1084 if (!event)1085 return;1086 1087 event->mlxsw_env = mlxsw_env;1088 event->slot_index = slot_index;1089 event->module = module;1090 INIT_WORK(&event->work, mlxsw_env_pmpe_event_work);1091 mlxsw_core_schedule_work(&event->work);1092}1093 1094static const struct mlxsw_listener mlxsw_env_module_plug_listener =1095 MLXSW_CORE_EVENTL(mlxsw_env_pmpe_listener_func, PMPE);1096 1097static int1098mlxsw_env_module_plug_event_register(struct mlxsw_core *mlxsw_core)1099{1100 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1101 1102 return mlxsw_core_trap_register(mlxsw_core,1103 &mlxsw_env_module_plug_listener,1104 mlxsw_env);1105}1106 1107static void1108mlxsw_env_module_plug_event_unregister(struct mlxsw_env *mlxsw_env)1109{1110 mlxsw_core_trap_unregister(mlxsw_env->core,1111 &mlxsw_env_module_plug_listener,1112 mlxsw_env);1113}1114 1115static int1116mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core,1117 u8 slot_index)1118{1119 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1120 int i, err;1121 1122 for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {1123 char pmaos_pl[MLXSW_REG_PMAOS_LEN];1124 1125 mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, i);1126 mlxsw_reg_pmaos_e_set(pmaos_pl,1127 MLXSW_REG_PMAOS_E_GENERATE_EVENT);1128 mlxsw_reg_pmaos_ee_set(pmaos_pl, true);1129 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);1130 if (err)1131 return err;1132 }1133 return 0;1134}1135 1136int1137mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 slot_index,1138 u8 module, u64 *p_counter)1139{1140 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1141 struct mlxsw_env_module_info *module_info;1142 1143 mutex_lock(&mlxsw_env->line_cards_lock);1144 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);1145 *p_counter = module_info->module_overheat_counter;1146 mutex_unlock(&mlxsw_env->line_cards_lock);1147 1148 return 0;1149}1150EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get);1151 1152void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 slot_index,1153 u8 module)1154{1155 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1156 struct mlxsw_env_module_info *module_info;1157 1158 mutex_lock(&mlxsw_env->line_cards_lock);1159 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);1160 module_info->num_ports_mapped++;1161 mutex_unlock(&mlxsw_env->line_cards_lock);1162}1163EXPORT_SYMBOL(mlxsw_env_module_port_map);1164 1165void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 slot_index,1166 u8 module)1167{1168 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1169 struct mlxsw_env_module_info *module_info;1170 1171 mutex_lock(&mlxsw_env->line_cards_lock);1172 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);1173 module_info->num_ports_mapped--;1174 mutex_unlock(&mlxsw_env->line_cards_lock);1175}1176EXPORT_SYMBOL(mlxsw_env_module_port_unmap);1177 1178int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 slot_index,1179 u8 module)1180{1181 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1182 struct mlxsw_env_module_info *module_info;1183 int err = 0;1184 1185 mutex_lock(&mlxsw_env->line_cards_lock);1186 1187 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);1188 if (module_info->power_mode_policy !=1189 ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)1190 goto out_inc;1191 1192 if (module_info->num_ports_up != 0)1193 goto out_inc;1194 1195 /* Transition to high power mode following first port using the module1196 * being put administratively up.1197 */1198 err = __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module,1199 false, NULL);1200 if (err)1201 goto out_unlock;1202 1203out_inc:1204 module_info->num_ports_up++;1205out_unlock:1206 mutex_unlock(&mlxsw_env->line_cards_lock);1207 return err;1208}1209EXPORT_SYMBOL(mlxsw_env_module_port_up);1210 1211void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 slot_index,1212 u8 module)1213{1214 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1215 struct mlxsw_env_module_info *module_info;1216 1217 mutex_lock(&mlxsw_env->line_cards_lock);1218 1219 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);1220 module_info->num_ports_up--;1221 1222 if (module_info->power_mode_policy !=1223 ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)1224 goto out_unlock;1225 1226 if (module_info->num_ports_up != 0)1227 goto out_unlock;1228 1229 /* Transition to low power mode following last port using the module1230 * being put administratively down.1231 */1232 __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module, true,1233 NULL);1234 1235out_unlock:1236 mutex_unlock(&mlxsw_env->line_cards_lock);1237}1238EXPORT_SYMBOL(mlxsw_env_module_port_down);1239 1240static int mlxsw_env_line_cards_alloc(struct mlxsw_env *env)1241{1242 struct mlxsw_env_module_info *module_info;1243 int i, j;1244 1245 for (i = 0; i < env->num_of_slots; i++) {1246 env->line_cards[i] = kzalloc(struct_size(env->line_cards[i],1247 module_info,1248 env->max_module_count),1249 GFP_KERNEL);1250 if (!env->line_cards[i])1251 goto kzalloc_err;1252 1253 /* Firmware defaults to high power mode policy where modules1254 * are transitioned to high power mode following plug-in.1255 */1256 for (j = 0; j < env->max_module_count; j++) {1257 module_info = &env->line_cards[i]->module_info[j];1258 module_info->power_mode_policy =1259 ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH;1260 }1261 }1262 1263 return 0;1264 1265kzalloc_err:1266 for (i--; i >= 0; i--)1267 kfree(env->line_cards[i]);1268 return -ENOMEM;1269}1270 1271static void mlxsw_env_line_cards_free(struct mlxsw_env *env)1272{1273 int i = env->num_of_slots;1274 1275 for (i--; i >= 0; i--)1276 kfree(env->line_cards[i]);1277}1278 1279static int1280mlxsw_env_module_event_enable(struct mlxsw_env *mlxsw_env, u8 slot_index)1281{1282 int err;1283 1284 err = mlxsw_env_module_oper_state_event_enable(mlxsw_env->core,1285 slot_index);1286 if (err)1287 return err;1288 1289 err = mlxsw_env_module_temp_event_enable(mlxsw_env->core, slot_index);1290 if (err)1291 return err;1292 1293 return 0;1294}1295 1296static void1297mlxsw_env_module_event_disable(struct mlxsw_env *mlxsw_env, u8 slot_index)1298{1299}1300 1301static int1302mlxsw_env_module_type_set(struct mlxsw_core *mlxsw_core, u8 slot_index)1303{1304 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1305 int i;1306 1307 for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {1308 struct mlxsw_env_module_info *module_info;1309 char pmtm_pl[MLXSW_REG_PMTM_LEN];1310 int err;1311 1312 mlxsw_reg_pmtm_pack(pmtm_pl, slot_index, i);1313 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtm), pmtm_pl);1314 if (err)1315 return err;1316 1317 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index,1318 i);1319 module_info->type = mlxsw_reg_pmtm_module_type_get(pmtm_pl);1320 }1321 1322 return 0;1323}1324 1325static void1326mlxsw_env_linecard_modules_power_mode_apply(struct mlxsw_core *mlxsw_core,1327 struct mlxsw_env *env,1328 u8 slot_index)1329{1330 int i;1331 1332 for (i = 0; i < env->line_cards[slot_index]->module_count; i++) {1333 enum ethtool_module_power_mode_policy policy;1334 struct mlxsw_env_module_info *module_info;1335 struct netlink_ext_ack extack;1336 int err;1337 1338 module_info = &env->line_cards[slot_index]->module_info[i];1339 policy = module_info->power_mode_policy;1340 err = mlxsw_env_set_module_power_mode_apply(mlxsw_core,1341 slot_index, i,1342 policy, &extack);1343 if (err)1344 dev_err(env->bus_info->dev, "%s\n", extack._msg);1345 }1346}1347 1348static void1349mlxsw_env_got_active(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv)1350{1351 struct mlxsw_env *mlxsw_env = priv;1352 char mgpir_pl[MLXSW_REG_MGPIR_LEN];1353 int err;1354 1355 mutex_lock(&mlxsw_env->line_cards_lock);1356 if (__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))1357 goto out_unlock;1358 1359 mlxsw_reg_mgpir_pack(mgpir_pl, slot_index);1360 err = mlxsw_reg_query(mlxsw_env->core, MLXSW_REG(mgpir), mgpir_pl);1361 if (err)1362 goto out_unlock;1363 1364 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,1365 &mlxsw_env->line_cards[slot_index]->module_count,1366 NULL);1367 1368 err = mlxsw_env_module_event_enable(mlxsw_env, slot_index);1369 if (err) {1370 dev_err(mlxsw_env->bus_info->dev, "Failed to enable port module events for line card in slot %d\n",1371 slot_index);1372 goto err_mlxsw_env_module_event_enable;1373 }1374 err = mlxsw_env_module_type_set(mlxsw_env->core, slot_index);1375 if (err) {1376 dev_err(mlxsw_env->bus_info->dev, "Failed to set modules' type for line card in slot %d\n",1377 slot_index);1378 goto err_type_set;1379 }1380 1381 mlxsw_env->line_cards[slot_index]->active = true;1382 /* Apply power mode policy. */1383 mlxsw_env_linecard_modules_power_mode_apply(mlxsw_core, mlxsw_env,1384 slot_index);1385 mutex_unlock(&mlxsw_env->line_cards_lock);1386 1387 return;1388 1389err_type_set:1390 mlxsw_env_module_event_disable(mlxsw_env, slot_index);1391err_mlxsw_env_module_event_enable:1392out_unlock:1393 mutex_unlock(&mlxsw_env->line_cards_lock);1394}1395 1396static void1397mlxsw_env_got_inactive(struct mlxsw_core *mlxsw_core, u8 slot_index,1398 void *priv)1399{1400 struct mlxsw_env *mlxsw_env = priv;1401 1402 mutex_lock(&mlxsw_env->line_cards_lock);1403 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))1404 goto out_unlock;1405 mlxsw_env->line_cards[slot_index]->active = false;1406 mlxsw_env_module_event_disable(mlxsw_env, slot_index);1407 mlxsw_env->line_cards[slot_index]->module_count = 0;1408out_unlock:1409 mutex_unlock(&mlxsw_env->line_cards_lock);1410}1411 1412static struct mlxsw_linecards_event_ops mlxsw_env_event_ops = {1413 .got_active = mlxsw_env_got_active,1414 .got_inactive = mlxsw_env_got_inactive,1415};1416 1417static void mlxsw_env_max_module_eeprom_len_query(struct mlxsw_env *mlxsw_env)1418{1419 char mcam_pl[MLXSW_REG_MCAM_LEN];1420 bool mcia_128b_supported = false;1421 int err;1422 1423 mlxsw_reg_mcam_pack(mcam_pl,1424 MLXSW_REG_MCAM_FEATURE_GROUP_ENHANCED_FEATURES);1425 err = mlxsw_reg_query(mlxsw_env->core, MLXSW_REG(mcam), mcam_pl);1426 if (!err)1427 mlxsw_reg_mcam_unpack(mcam_pl, MLXSW_REG_MCAM_MCIA_128B,1428 &mcia_128b_supported);1429 1430 mlxsw_env->max_eeprom_len = mcia_128b_supported ? 128 : 48;1431}1432 1433int mlxsw_env_init(struct mlxsw_core *mlxsw_core,1434 const struct mlxsw_bus_info *bus_info,1435 struct mlxsw_env **p_env)1436{1437 u8 module_count, num_of_slots, max_module_count;1438 char mgpir_pl[MLXSW_REG_MGPIR_LEN];1439 struct mlxsw_env *env;1440 int err;1441 1442 mlxsw_reg_mgpir_pack(mgpir_pl, 0);1443 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);1444 if (err)1445 return err;1446 1447 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &module_count,1448 &num_of_slots);1449 /* If the system is modular, get the maximum number of modules per-slot.1450 * Otherwise, get the maximum number of modules on the main board.1451 */1452 max_module_count = num_of_slots ?1453 mlxsw_reg_mgpir_max_modules_per_slot_get(mgpir_pl) :1454 module_count;1455 1456 env = kzalloc(struct_size(env, line_cards, num_of_slots + 1),1457 GFP_KERNEL);1458 if (!env)1459 return -ENOMEM;1460 1461 env->core = mlxsw_core;1462 env->bus_info = bus_info;1463 env->num_of_slots = num_of_slots + 1;1464 env->max_module_count = max_module_count;1465 err = mlxsw_env_line_cards_alloc(env);1466 if (err)1467 goto err_mlxsw_env_line_cards_alloc;1468 1469 mutex_init(&env->line_cards_lock);1470 *p_env = env;1471 1472 err = mlxsw_linecards_event_ops_register(env->core,1473 &mlxsw_env_event_ops, env);1474 if (err)1475 goto err_linecards_event_ops_register;1476 1477 err = mlxsw_env_temp_warn_event_register(mlxsw_core);1478 if (err)1479 goto err_temp_warn_event_register;1480 1481 err = mlxsw_env_module_plug_event_register(mlxsw_core);1482 if (err)1483 goto err_module_plug_event_register;1484 1485 /* Set 'module_count' only for main board. Actual count for line card1486 * is to be set after line card is activated.1487 */1488 env->line_cards[0]->module_count = num_of_slots ? 0 : module_count;1489 /* Enable events only for main board. Line card events are to be1490 * configured only after line card is activated. Before that, access to1491 * modules on line cards is not allowed.1492 */1493 err = mlxsw_env_module_event_enable(env, 0);1494 if (err)1495 goto err_mlxsw_env_module_event_enable;1496 1497 err = mlxsw_env_module_type_set(mlxsw_core, 0);1498 if (err)1499 goto err_type_set;1500 1501 mlxsw_env_max_module_eeprom_len_query(env);1502 env->line_cards[0]->active = true;1503 1504 return 0;1505 1506err_type_set:1507 mlxsw_env_module_event_disable(env, 0);1508err_mlxsw_env_module_event_enable:1509 mlxsw_env_module_plug_event_unregister(env);1510err_module_plug_event_register:1511 mlxsw_env_temp_warn_event_unregister(env);1512err_temp_warn_event_register:1513 mlxsw_linecards_event_ops_unregister(env->core,1514 &mlxsw_env_event_ops, env);1515err_linecards_event_ops_register:1516 mutex_destroy(&env->line_cards_lock);1517 mlxsw_env_line_cards_free(env);1518err_mlxsw_env_line_cards_alloc:1519 kfree(env);1520 return err;1521}1522 1523void mlxsw_env_fini(struct mlxsw_env *env)1524{1525 env->line_cards[0]->active = false;1526 mlxsw_env_module_event_disable(env, 0);1527 mlxsw_env_module_plug_event_unregister(env);1528 /* Make sure there is no more event work scheduled. */1529 mlxsw_core_flush_owq();1530 mlxsw_env_temp_warn_event_unregister(env);1531 mlxsw_linecards_event_ops_unregister(env->core,1532 &mlxsw_env_event_ops, env);1533 mutex_destroy(&env->line_cards_lock);1534 mlxsw_env_line_cards_free(env);1535 kfree(env);1536}1537