0001
0002
0003
0004 #include <linux/kernel.h>
0005 #include <linux/err.h>
0006 #include <linux/ethtool.h>
0007 #include <linux/sfp.h>
0008 #include <linux/mutex.h>
0009
0010 #include "core.h"
0011 #include "core_env.h"
0012 #include "item.h"
0013 #include "reg.h"
0014
0015 struct mlxsw_env_module_info {
0016 u64 module_overheat_counter;
0017 bool is_overheat;
0018 int num_ports_mapped;
0019 int num_ports_up;
0020 enum ethtool_module_power_mode_policy power_mode_policy;
0021 enum mlxsw_reg_pmtm_module_type type;
0022 };
0023
0024 struct mlxsw_env_line_card {
0025 u8 module_count;
0026 bool active;
0027 struct mlxsw_env_module_info module_info[];
0028 };
0029
0030 struct mlxsw_env {
0031 struct mlxsw_core *core;
0032 const struct mlxsw_bus_info *bus_info;
0033 u8 max_module_count;
0034 u8 num_of_slots;
0035 struct mutex line_cards_lock;
0036 struct mlxsw_env_line_card *line_cards[];
0037 };
0038
0039 static bool __mlxsw_env_linecard_is_active(struct mlxsw_env *mlxsw_env,
0040 u8 slot_index)
0041 {
0042 return mlxsw_env->line_cards[slot_index]->active;
0043 }
0044
0045 static bool mlxsw_env_linecard_is_active(struct mlxsw_env *mlxsw_env,
0046 u8 slot_index)
0047 {
0048 bool active;
0049
0050 mutex_lock(&mlxsw_env->line_cards_lock);
0051 active = __mlxsw_env_linecard_is_active(mlxsw_env, slot_index);
0052 mutex_unlock(&mlxsw_env->line_cards_lock);
0053
0054 return active;
0055 }
0056
0057 static struct
0058 mlxsw_env_module_info *mlxsw_env_module_info_get(struct mlxsw_core *mlxsw_core,
0059 u8 slot_index, u8 module)
0060 {
0061 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
0062
0063 return &mlxsw_env->line_cards[slot_index]->module_info[module];
0064 }
0065
0066 static int __mlxsw_env_validate_module_type(struct mlxsw_core *core,
0067 u8 slot_index, u8 module)
0068 {
0069 struct mlxsw_env *mlxsw_env = mlxsw_core_env(core);
0070 struct mlxsw_env_module_info *module_info;
0071 int err;
0072
0073 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
0074 return 0;
0075
0076 module_info = mlxsw_env_module_info_get(core, slot_index, module);
0077 switch (module_info->type) {
0078 case MLXSW_REG_PMTM_MODULE_TYPE_TWISTED_PAIR:
0079 err = -EINVAL;
0080 break;
0081 default:
0082 err = 0;
0083 }
0084
0085 return err;
0086 }
0087
0088 static int mlxsw_env_validate_module_type(struct mlxsw_core *core,
0089 u8 slot_index, u8 module)
0090 {
0091 struct mlxsw_env *mlxsw_env = mlxsw_core_env(core);
0092 int err;
0093
0094 mutex_lock(&mlxsw_env->line_cards_lock);
0095 err = __mlxsw_env_validate_module_type(core, slot_index, module);
0096 mutex_unlock(&mlxsw_env->line_cards_lock);
0097
0098 return err;
0099 }
0100
0101 static int
0102 mlxsw_env_validate_cable_ident(struct mlxsw_core *core, u8 slot_index, int id,
0103 bool *qsfp, bool *cmis)
0104 {
0105 char mcia_pl[MLXSW_REG_MCIA_LEN];
0106 char *eeprom_tmp;
0107 u8 ident;
0108 int err;
0109
0110 err = mlxsw_env_validate_module_type(core, slot_index, id);
0111 if (err)
0112 return err;
0113
0114 mlxsw_reg_mcia_pack(mcia_pl, slot_index, id, 0,
0115 MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1,
0116 MLXSW_REG_MCIA_I2C_ADDR_LOW);
0117 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
0118 if (err)
0119 return err;
0120 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
0121 ident = eeprom_tmp[0];
0122 *cmis = false;
0123 switch (ident) {
0124 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
0125 *qsfp = false;
0126 break;
0127 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
0128 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
0129 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
0130 *qsfp = true;
0131 break;
0132 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
0133 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_OSFP:
0134 *qsfp = true;
0135 *cmis = true;
0136 break;
0137 default:
0138 return -EINVAL;
0139 }
0140
0141 return 0;
0142 }
0143
0144 static int
0145 mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, u8 slot_index,
0146 int module, u16 offset, u16 size, void *data,
0147 bool qsfp, unsigned int *p_read_size)
0148 {
0149 char mcia_pl[MLXSW_REG_MCIA_LEN];
0150 char *eeprom_tmp;
0151 u16 i2c_addr;
0152 u8 page = 0;
0153 int status;
0154 int err;
0155
0156
0157
0158
0159
0160 size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
0161
0162 if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
0163 offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
0164
0165 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
0166
0167 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
0168 if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
0169 if (qsfp) {
0170
0171
0172
0173
0174
0175
0176 page = MLXSW_REG_MCIA_PAGE_GET(offset);
0177 offset -= MLXSW_REG_MCIA_EEPROM_UP_PAGE_LENGTH * page;
0178 if (offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
0179 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
0180 } else {
0181
0182
0183
0184
0185
0186 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
0187 offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
0188 }
0189 }
0190
0191 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, 0, page, offset, size,
0192 i2c_addr);
0193
0194 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
0195 if (err)
0196 return err;
0197
0198 status = mlxsw_reg_mcia_status_get(mcia_pl);
0199 if (status)
0200 return -EIO;
0201
0202 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
0203 memcpy(data, eeprom_tmp, size);
0204 *p_read_size = size;
0205
0206 return 0;
0207 }
0208
0209 int
0210 mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, u8 slot_index,
0211 int module, int off, int *temp)
0212 {
0213 unsigned int module_temp, module_crit, module_emerg;
0214 union {
0215 u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];
0216 u16 temp;
0217 } temp_thresh;
0218 char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
0219 char mtmp_pl[MLXSW_REG_MTMP_LEN];
0220 char *eeprom_tmp;
0221 bool qsfp, cmis;
0222 int page;
0223 int err;
0224
0225 mlxsw_reg_mtmp_pack(mtmp_pl, slot_index,
0226 MLXSW_REG_MTMP_MODULE_INDEX_MIN + module, false,
0227 false);
0228 err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);
0229 if (err)
0230 return err;
0231 mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, &module_crit,
0232 &module_emerg, NULL);
0233 if (!module_temp) {
0234 *temp = 0;
0235 return 0;
0236 }
0237
0238
0239
0240
0241 if (module_emerg) {
0242 *temp = off == SFP_TEMP_HIGH_WARN ? module_crit : module_emerg;
0243 return 0;
0244 }
0245
0246
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256 err = mlxsw_env_validate_cable_ident(core, slot_index, module, &qsfp,
0257 &cmis);
0258 if (err)
0259 return err;
0260
0261 if (qsfp) {
0262
0263
0264
0265 if (cmis)
0266 page = MLXSW_REG_MCIA_TH_PAGE_CMIS_NUM;
0267 else
0268 page = MLXSW_REG_MCIA_TH_PAGE_NUM;
0269 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, 0, page,
0270 MLXSW_REG_MCIA_TH_PAGE_OFF + off,
0271 MLXSW_REG_MCIA_TH_ITEM_SIZE,
0272 MLXSW_REG_MCIA_I2C_ADDR_LOW);
0273 } else {
0274 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, 0,
0275 MLXSW_REG_MCIA_PAGE0_LO,
0276 off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
0277 MLXSW_REG_MCIA_I2C_ADDR_HIGH);
0278 }
0279
0280 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
0281 if (err)
0282 return err;
0283
0284 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
0285 memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);
0286 *temp = temp_thresh.temp * 1000;
0287
0288 return 0;
0289 }
0290
0291 int mlxsw_env_get_module_info(struct net_device *netdev,
0292 struct mlxsw_core *mlxsw_core, u8 slot_index,
0293 int module, struct ethtool_modinfo *modinfo)
0294 {
0295 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
0296 u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
0297 u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
0298 u8 module_rev_id, module_id, diag_mon;
0299 unsigned int read_size;
0300 int err;
0301
0302 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {
0303 netdev_err(netdev, "Cannot read EEPROM of module on an inactive line card\n");
0304 return -EIO;
0305 }
0306
0307 err = mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
0308 if (err) {
0309 netdev_err(netdev,
0310 "EEPROM is not equipped on port module type");
0311 return err;
0312 }
0313
0314 err = mlxsw_env_query_module_eeprom(mlxsw_core, slot_index, module, 0,
0315 offset, module_info, false,
0316 &read_size);
0317 if (err)
0318 return err;
0319
0320 if (read_size < offset)
0321 return -EIO;
0322
0323 module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
0324 module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
0325
0326 switch (module_id) {
0327 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
0328 modinfo->type = ETH_MODULE_SFF_8436;
0329 modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
0330 break;
0331 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
0332 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
0333 if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
0334 module_rev_id >=
0335 MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
0336 modinfo->type = ETH_MODULE_SFF_8636;
0337 modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
0338 } else {
0339 modinfo->type = ETH_MODULE_SFF_8436;
0340 modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
0341 }
0342 break;
0343 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
0344
0345 err = mlxsw_env_query_module_eeprom(mlxsw_core, slot_index,
0346 module, SFP_DIAGMON, 1,
0347 &diag_mon, false,
0348 &read_size);
0349 if (err)
0350 return err;
0351
0352 if (read_size < 1)
0353 return -EIO;
0354
0355 modinfo->type = ETH_MODULE_SFF_8472;
0356 if (diag_mon)
0357 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
0358 else
0359 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;
0360 break;
0361 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
0362 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_OSFP:
0363
0364
0365
0366 modinfo->type = ETH_MODULE_SFF_8636;
0367
0368
0369
0370
0371
0372 if (module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_TYPE_ID] &
0373 MLXSW_REG_MCIA_EEPROM_CMIS_FLAT_MEMORY)
0374 modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
0375 else
0376 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
0377 break;
0378 default:
0379 return -EINVAL;
0380 }
0381
0382 return 0;
0383 }
0384 EXPORT_SYMBOL(mlxsw_env_get_module_info);
0385
0386 int mlxsw_env_get_module_eeprom(struct net_device *netdev,
0387 struct mlxsw_core *mlxsw_core, u8 slot_index,
0388 int module, struct ethtool_eeprom *ee,
0389 u8 *data)
0390 {
0391 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
0392 int offset = ee->offset;
0393 unsigned int read_size;
0394 bool qsfp, cmis;
0395 int i = 0;
0396 int err;
0397
0398 if (!ee->len)
0399 return -EINVAL;
0400
0401 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {
0402 netdev_err(netdev, "Cannot read EEPROM of module on an inactive line card\n");
0403 return -EIO;
0404 }
0405
0406 memset(data, 0, ee->len);
0407
0408 err = mlxsw_env_validate_cable_ident(mlxsw_core, slot_index, module,
0409 &qsfp, &cmis);
0410 if (err)
0411 return err;
0412
0413 while (i < ee->len) {
0414 err = mlxsw_env_query_module_eeprom(mlxsw_core, slot_index,
0415 module, offset,
0416 ee->len - i, data + i,
0417 qsfp, &read_size);
0418 if (err) {
0419 netdev_err(netdev, "Eeprom query failed\n");
0420 return err;
0421 }
0422
0423 i += read_size;
0424 offset += read_size;
0425 }
0426
0427 return 0;
0428 }
0429 EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);
0430
0431 static int mlxsw_env_mcia_status_process(const char *mcia_pl,
0432 struct netlink_ext_ack *extack)
0433 {
0434 u8 status = mlxsw_reg_mcia_status_get(mcia_pl);
0435
0436 switch (status) {
0437 case MLXSW_REG_MCIA_STATUS_GOOD:
0438 return 0;
0439 case MLXSW_REG_MCIA_STATUS_NO_EEPROM_MODULE:
0440 NL_SET_ERR_MSG_MOD(extack, "No response from module's EEPROM");
0441 return -EIO;
0442 case MLXSW_REG_MCIA_STATUS_MODULE_NOT_SUPPORTED:
0443 NL_SET_ERR_MSG_MOD(extack, "Module type not supported by the device");
0444 return -EOPNOTSUPP;
0445 case MLXSW_REG_MCIA_STATUS_MODULE_NOT_CONNECTED:
0446 NL_SET_ERR_MSG_MOD(extack, "No module present indication");
0447 return -EIO;
0448 case MLXSW_REG_MCIA_STATUS_I2C_ERROR:
0449 NL_SET_ERR_MSG_MOD(extack, "Error occurred while trying to access module's EEPROM using I2C");
0450 return -EIO;
0451 case MLXSW_REG_MCIA_STATUS_MODULE_DISABLED:
0452 NL_SET_ERR_MSG_MOD(extack, "Module is disabled");
0453 return -EIO;
0454 default:
0455 NL_SET_ERR_MSG_MOD(extack, "Unknown error");
0456 return -EIO;
0457 }
0458 }
0459
0460 int
0461 mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core,
0462 u8 slot_index, u8 module,
0463 const struct ethtool_module_eeprom *page,
0464 struct netlink_ext_ack *extack)
0465 {
0466 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
0467 u32 bytes_read = 0;
0468 u16 device_addr;
0469 int err;
0470
0471 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {
0472 NL_SET_ERR_MSG_MOD(extack,
0473 "Cannot read EEPROM of module on an inactive line card");
0474 return -EIO;
0475 }
0476
0477 err = mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
0478 if (err) {
0479 NL_SET_ERR_MSG_MOD(extack, "EEPROM is not equipped on port module type");
0480 return err;
0481 }
0482
0483
0484 device_addr = page->offset;
0485
0486 while (bytes_read < page->length) {
0487 char mcia_pl[MLXSW_REG_MCIA_LEN];
0488 char *eeprom_tmp;
0489 u8 size;
0490
0491 size = min_t(u8, page->length - bytes_read,
0492 MLXSW_REG_MCIA_EEPROM_SIZE);
0493
0494 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, 0, page->page,
0495 device_addr + bytes_read, size,
0496 page->i2c_address);
0497 mlxsw_reg_mcia_bank_number_set(mcia_pl, page->bank);
0498
0499 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
0500 if (err) {
0501 NL_SET_ERR_MSG_MOD(extack, "Failed to access module's EEPROM");
0502 return err;
0503 }
0504
0505 err = mlxsw_env_mcia_status_process(mcia_pl, extack);
0506 if (err)
0507 return err;
0508
0509 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
0510 memcpy(page->data + bytes_read, eeprom_tmp, size);
0511 bytes_read += size;
0512 }
0513
0514 return bytes_read;
0515 }
0516 EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page);
0517
0518 static int mlxsw_env_module_reset(struct mlxsw_core *mlxsw_core, u8 slot_index,
0519 u8 module)
0520 {
0521 char pmaos_pl[MLXSW_REG_PMAOS_LEN];
0522
0523 mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, module);
0524 mlxsw_reg_pmaos_rst_set(pmaos_pl, true);
0525
0526 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
0527 }
0528
0529 int mlxsw_env_reset_module(struct net_device *netdev,
0530 struct mlxsw_core *mlxsw_core, u8 slot_index,
0531 u8 module, u32 *flags)
0532 {
0533 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
0534 struct mlxsw_env_module_info *module_info;
0535 u32 req = *flags;
0536 int err;
0537
0538 if (!(req & ETH_RESET_PHY) &&
0539 !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT)))
0540 return 0;
0541
0542 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {
0543 netdev_err(netdev, "Cannot reset module on an inactive line card\n");
0544 return -EIO;
0545 }
0546
0547 mutex_lock(&mlxsw_env->line_cards_lock);
0548
0549 err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
0550 if (err) {
0551 netdev_err(netdev, "Reset module is not supported on port module type\n");
0552 goto out;
0553 }
0554
0555 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
0556 if (module_info->num_ports_up) {
0557 netdev_err(netdev, "Cannot reset module when ports using it are administratively up\n");
0558 err = -EINVAL;
0559 goto out;
0560 }
0561
0562 if (module_info->num_ports_mapped > 1 &&
0563 !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) {
0564 netdev_err(netdev, "Cannot reset module without \"phy-shared\" flag when shared by multiple ports\n");
0565 err = -EINVAL;
0566 goto out;
0567 }
0568
0569 err = mlxsw_env_module_reset(mlxsw_core, slot_index, module);
0570 if (err) {
0571 netdev_err(netdev, "Failed to reset module\n");
0572 goto out;
0573 }
0574
0575 *flags &= ~(ETH_RESET_PHY | (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT));
0576
0577 out:
0578 mutex_unlock(&mlxsw_env->line_cards_lock);
0579 return err;
0580 }
0581 EXPORT_SYMBOL(mlxsw_env_reset_module);
0582
0583 int
0584 mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
0585 u8 module,
0586 struct ethtool_module_power_mode_params *params,
0587 struct netlink_ext_ack *extack)
0588 {
0589 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
0590 struct mlxsw_env_module_info *module_info;
0591 char mcion_pl[MLXSW_REG_MCION_LEN];
0592 u32 status_bits;
0593 int err = 0;
0594
0595 mutex_lock(&mlxsw_env->line_cards_lock);
0596
0597 err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
0598 if (err) {
0599 NL_SET_ERR_MSG_MOD(extack, "Power mode is not supported on port module type");
0600 goto out;
0601 }
0602
0603 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
0604 params->policy = module_info->power_mode_policy;
0605
0606
0607 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
0608 goto out;
0609
0610 mlxsw_reg_mcion_pack(mcion_pl, slot_index, module);
0611 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcion), mcion_pl);
0612 if (err) {
0613 NL_SET_ERR_MSG_MOD(extack, "Failed to retrieve module's power mode");
0614 goto out;
0615 }
0616
0617 status_bits = mlxsw_reg_mcion_module_status_bits_get(mcion_pl);
0618 if (!(status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_PRESENT_MASK))
0619 goto out;
0620
0621 if (status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_LOW_POWER_MASK)
0622 params->mode = ETHTOOL_MODULE_POWER_MODE_LOW;
0623 else
0624 params->mode = ETHTOOL_MODULE_POWER_MODE_HIGH;
0625
0626 out:
0627 mutex_unlock(&mlxsw_env->line_cards_lock);
0628 return err;
0629 }
0630 EXPORT_SYMBOL(mlxsw_env_get_module_power_mode);
0631
0632 static int mlxsw_env_module_enable_set(struct mlxsw_core *mlxsw_core,
0633 u8 slot_index, u8 module, bool enable)
0634 {
0635 enum mlxsw_reg_pmaos_admin_status admin_status;
0636 char pmaos_pl[MLXSW_REG_PMAOS_LEN];
0637
0638 mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, module);
0639 admin_status = enable ? MLXSW_REG_PMAOS_ADMIN_STATUS_ENABLED :
0640 MLXSW_REG_PMAOS_ADMIN_STATUS_DISABLED;
0641 mlxsw_reg_pmaos_admin_status_set(pmaos_pl, admin_status);
0642 mlxsw_reg_pmaos_ase_set(pmaos_pl, true);
0643
0644 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
0645 }
0646
0647 static int mlxsw_env_module_low_power_set(struct mlxsw_core *mlxsw_core,
0648 u8 slot_index, u8 module,
0649 bool low_power)
0650 {
0651 u16 eeprom_override_mask, eeprom_override;
0652 char pmmp_pl[MLXSW_REG_PMMP_LEN];
0653
0654 mlxsw_reg_pmmp_pack(pmmp_pl, slot_index, module);
0655 mlxsw_reg_pmmp_sticky_set(pmmp_pl, true);
0656
0657 eeprom_override_mask = ~MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK;
0658 mlxsw_reg_pmmp_eeprom_override_mask_set(pmmp_pl, eeprom_override_mask);
0659 eeprom_override = low_power ? MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK :
0660 0;
0661 mlxsw_reg_pmmp_eeprom_override_set(pmmp_pl, eeprom_override);
0662
0663 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmmp), pmmp_pl);
0664 }
0665
0666 static int __mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core,
0667 u8 slot_index, u8 module,
0668 bool low_power,
0669 struct netlink_ext_ack *extack)
0670 {
0671 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
0672 int err;
0673
0674
0675
0676
0677
0678 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
0679 return 0;
0680
0681 err = mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, false);
0682 if (err) {
0683 NL_SET_ERR_MSG_MOD(extack, "Failed to disable module");
0684 return err;
0685 }
0686
0687 err = mlxsw_env_module_low_power_set(mlxsw_core, slot_index, module,
0688 low_power);
0689 if (err) {
0690 NL_SET_ERR_MSG_MOD(extack, "Failed to set module's power mode");
0691 goto err_module_low_power_set;
0692 }
0693
0694 err = mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, true);
0695 if (err) {
0696 NL_SET_ERR_MSG_MOD(extack, "Failed to enable module");
0697 goto err_module_enable_set;
0698 }
0699
0700 return 0;
0701
0702 err_module_enable_set:
0703 mlxsw_env_module_low_power_set(mlxsw_core, slot_index, module,
0704 !low_power);
0705 err_module_low_power_set:
0706 mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, true);
0707 return err;
0708 }
0709
0710 static int
0711 mlxsw_env_set_module_power_mode_apply(struct mlxsw_core *mlxsw_core,
0712 u8 slot_index, u8 module,
0713 enum ethtool_module_power_mode_policy policy,
0714 struct netlink_ext_ack *extack)
0715 {
0716 struct mlxsw_env_module_info *module_info;
0717 bool low_power;
0718 int err = 0;
0719
0720 err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
0721 if (err) {
0722 NL_SET_ERR_MSG_MOD(extack,
0723 "Power mode set is not supported on port module type");
0724 goto out;
0725 }
0726
0727 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
0728 if (module_info->power_mode_policy == policy)
0729 goto out;
0730
0731
0732 if (module_info->num_ports_up)
0733 goto out_set_policy;
0734
0735 low_power = policy == ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO;
0736 err = __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module,
0737 low_power, extack);
0738 if (err)
0739 goto out;
0740
0741 out_set_policy:
0742 module_info->power_mode_policy = policy;
0743 out:
0744 return err;
0745 }
0746
0747 int
0748 mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
0749 u8 module,
0750 enum ethtool_module_power_mode_policy policy,
0751 struct netlink_ext_ack *extack)
0752 {
0753 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
0754 int err;
0755
0756 if (policy != ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH &&
0757 policy != ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO) {
0758 NL_SET_ERR_MSG_MOD(extack, "Unsupported power mode policy");
0759 return -EOPNOTSUPP;
0760 }
0761
0762 mutex_lock(&mlxsw_env->line_cards_lock);
0763 err = mlxsw_env_set_module_power_mode_apply(mlxsw_core, slot_index,
0764 module, policy, extack);
0765 mutex_unlock(&mlxsw_env->line_cards_lock);
0766
0767 return err;
0768 }
0769 EXPORT_SYMBOL(mlxsw_env_set_module_power_mode);
0770
0771 static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core,
0772 u8 slot_index, u8 module,
0773 bool *p_has_temp_sensor)
0774 {
0775 char mtbr_pl[MLXSW_REG_MTBR_LEN];
0776 u16 temp;
0777 int err;
0778
0779 mlxsw_reg_mtbr_pack(mtbr_pl, slot_index,
0780 MLXSW_REG_MTBR_BASE_MODULE_INDEX + module, 1);
0781 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtbr), mtbr_pl);
0782 if (err)
0783 return err;
0784
0785 mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
0786
0787 switch (temp) {
0788 case MLXSW_REG_MTBR_BAD_SENS_INFO:
0789 case MLXSW_REG_MTBR_NO_CONN:
0790 case MLXSW_REG_MTBR_NO_TEMP_SENS:
0791 case MLXSW_REG_MTBR_INDEX_NA:
0792 *p_has_temp_sensor = false;
0793 break;
0794 default:
0795 *p_has_temp_sensor = temp ? true : false;
0796 }
0797 return 0;
0798 }
0799
0800 static int
0801 mlxsw_env_temp_event_set(struct mlxsw_core *mlxsw_core, u8 slot_index,
0802 u16 sensor_index, bool enable)
0803 {
0804 char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0};
0805 enum mlxsw_reg_mtmp_tee tee;
0806 int err, threshold_hi;
0807
0808 mlxsw_reg_mtmp_slot_index_set(mtmp_pl, slot_index);
0809 mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, sensor_index);
0810 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
0811 if (err)
0812 return err;
0813
0814 if (enable) {
0815 err = mlxsw_env_module_temp_thresholds_get(mlxsw_core,
0816 slot_index,
0817 sensor_index -
0818 MLXSW_REG_MTMP_MODULE_INDEX_MIN,
0819 SFP_TEMP_HIGH_WARN,
0820 &threshold_hi);
0821
0822
0823
0824 if (err)
0825 threshold_hi = MLXSW_REG_MTMP_THRESH_HI;
0826 else
0827
0828
0829
0830
0831
0832 threshold_hi = threshold_hi / 1000 * 8;
0833
0834 mlxsw_reg_mtmp_temperature_threshold_hi_set(mtmp_pl, threshold_hi);
0835 mlxsw_reg_mtmp_temperature_threshold_lo_set(mtmp_pl, threshold_hi -
0836 MLXSW_REG_MTMP_HYSTERESIS_TEMP);
0837 }
0838 tee = enable ? MLXSW_REG_MTMP_TEE_GENERATE_EVENT : MLXSW_REG_MTMP_TEE_NO_EVENT;
0839 mlxsw_reg_mtmp_tee_set(mtmp_pl, tee);
0840 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
0841 }
0842
0843 static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core,
0844 u8 slot_index)
0845 {
0846 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
0847 int i, err, sensor_index;
0848 bool has_temp_sensor;
0849
0850 for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
0851 err = mlxsw_env_module_has_temp_sensor(mlxsw_core, slot_index,
0852 i, &has_temp_sensor);
0853 if (err)
0854 return err;
0855
0856 if (!has_temp_sensor)
0857 continue;
0858
0859 sensor_index = i + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
0860 err = mlxsw_env_temp_event_set(mlxsw_core, slot_index,
0861 sensor_index, true);
0862 if (err)
0863 return err;
0864 }
0865
0866 return 0;
0867 }
0868
0869 struct mlxsw_env_module_temp_warn_event {
0870 struct mlxsw_env *mlxsw_env;
0871 char mtwe_pl[MLXSW_REG_MTWE_LEN];
0872 struct work_struct work;
0873 };
0874
0875 static void mlxsw_env_mtwe_event_work(struct work_struct *work)
0876 {
0877 struct mlxsw_env_module_temp_warn_event *event;
0878 struct mlxsw_env_module_info *module_info;
0879 struct mlxsw_env *mlxsw_env;
0880 int i, sensor_warning;
0881 bool is_overheat;
0882
0883 event = container_of(work, struct mlxsw_env_module_temp_warn_event,
0884 work);
0885 mlxsw_env = event->mlxsw_env;
0886
0887 for (i = 0; i < mlxsw_env->max_module_count; i++) {
0888
0889
0890
0891
0892 sensor_warning =
0893 mlxsw_reg_mtwe_sensor_warning_get(event->mtwe_pl,
0894 i + MLXSW_REG_MTMP_MODULE_INDEX_MIN);
0895 mutex_lock(&mlxsw_env->line_cards_lock);
0896
0897 module_info = mlxsw_env_module_info_get(mlxsw_env->core, 0, i);
0898 is_overheat = module_info->is_overheat;
0899
0900 if ((is_overheat && sensor_warning) ||
0901 (!is_overheat && !sensor_warning)) {
0902
0903
0904
0905
0906 mutex_unlock(&mlxsw_env->line_cards_lock);
0907 continue;
0908 } else if (is_overheat && !sensor_warning) {
0909
0910
0911 module_info->is_overheat = false;
0912 mutex_unlock(&mlxsw_env->line_cards_lock);
0913 } else {
0914
0915
0916
0917
0918 module_info->is_overheat = true;
0919 module_info->module_overheat_counter++;
0920 mutex_unlock(&mlxsw_env->line_cards_lock);
0921 }
0922 }
0923
0924 kfree(event);
0925 }
0926
0927 static void
0928 mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info *reg, char *mtwe_pl,
0929 void *priv)
0930 {
0931 struct mlxsw_env_module_temp_warn_event *event;
0932 struct mlxsw_env *mlxsw_env = priv;
0933
0934 event = kmalloc(sizeof(*event), GFP_ATOMIC);
0935 if (!event)
0936 return;
0937
0938 event->mlxsw_env = mlxsw_env;
0939 memcpy(event->mtwe_pl, mtwe_pl, MLXSW_REG_MTWE_LEN);
0940 INIT_WORK(&event->work, mlxsw_env_mtwe_event_work);
0941 mlxsw_core_schedule_work(&event->work);
0942 }
0943
0944 static const struct mlxsw_listener mlxsw_env_temp_warn_listener =
0945 MLXSW_CORE_EVENTL(mlxsw_env_mtwe_listener_func, MTWE);
0946
0947 static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core)
0948 {
0949 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
0950
0951 return mlxsw_core_trap_register(mlxsw_core,
0952 &mlxsw_env_temp_warn_listener,
0953 mlxsw_env);
0954 }
0955
0956 static void mlxsw_env_temp_warn_event_unregister(struct mlxsw_env *mlxsw_env)
0957 {
0958 mlxsw_core_trap_unregister(mlxsw_env->core,
0959 &mlxsw_env_temp_warn_listener, mlxsw_env);
0960 }
0961
0962 struct mlxsw_env_module_plug_unplug_event {
0963 struct mlxsw_env *mlxsw_env;
0964 u8 slot_index;
0965 u8 module;
0966 struct work_struct work;
0967 };
0968
0969 static void mlxsw_env_pmpe_event_work(struct work_struct *work)
0970 {
0971 struct mlxsw_env_module_plug_unplug_event *event;
0972 struct mlxsw_env_module_info *module_info;
0973 struct mlxsw_env *mlxsw_env;
0974 bool has_temp_sensor;
0975 u16 sensor_index;
0976 int err;
0977
0978 event = container_of(work, struct mlxsw_env_module_plug_unplug_event,
0979 work);
0980 mlxsw_env = event->mlxsw_env;
0981
0982 mutex_lock(&mlxsw_env->line_cards_lock);
0983 module_info = mlxsw_env_module_info_get(mlxsw_env->core,
0984 event->slot_index,
0985 event->module);
0986 module_info->is_overheat = false;
0987 mutex_unlock(&mlxsw_env->line_cards_lock);
0988
0989 err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core,
0990 event->slot_index,
0991 event->module,
0992 &has_temp_sensor);
0993
0994
0995
0996 if (err)
0997 goto out;
0998
0999 if (!has_temp_sensor)
1000 goto out;
1001
1002 sensor_index = event->module + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
1003 mlxsw_env_temp_event_set(mlxsw_env->core, event->slot_index,
1004 sensor_index, true);
1005
1006 out:
1007 kfree(event);
1008 }
1009
1010 static void
1011 mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl,
1012 void *priv)
1013 {
1014 u8 slot_index = mlxsw_reg_pmpe_slot_index_get(pmpe_pl);
1015 struct mlxsw_env_module_plug_unplug_event *event;
1016 enum mlxsw_reg_pmpe_module_status module_status;
1017 u8 module = mlxsw_reg_pmpe_module_get(pmpe_pl);
1018 struct mlxsw_env *mlxsw_env = priv;
1019
1020 if (WARN_ON_ONCE(module >= mlxsw_env->max_module_count ||
1021 slot_index >= mlxsw_env->num_of_slots))
1022 return;
1023
1024 module_status = mlxsw_reg_pmpe_module_status_get(pmpe_pl);
1025 if (module_status != MLXSW_REG_PMPE_MODULE_STATUS_PLUGGED_ENABLED)
1026 return;
1027
1028 event = kmalloc(sizeof(*event), GFP_ATOMIC);
1029 if (!event)
1030 return;
1031
1032 event->mlxsw_env = mlxsw_env;
1033 event->slot_index = slot_index;
1034 event->module = module;
1035 INIT_WORK(&event->work, mlxsw_env_pmpe_event_work);
1036 mlxsw_core_schedule_work(&event->work);
1037 }
1038
1039 static const struct mlxsw_listener mlxsw_env_module_plug_listener =
1040 MLXSW_CORE_EVENTL(mlxsw_env_pmpe_listener_func, PMPE);
1041
1042 static int
1043 mlxsw_env_module_plug_event_register(struct mlxsw_core *mlxsw_core)
1044 {
1045 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1046
1047 return mlxsw_core_trap_register(mlxsw_core,
1048 &mlxsw_env_module_plug_listener,
1049 mlxsw_env);
1050 }
1051
1052 static void
1053 mlxsw_env_module_plug_event_unregister(struct mlxsw_env *mlxsw_env)
1054 {
1055 mlxsw_core_trap_unregister(mlxsw_env->core,
1056 &mlxsw_env_module_plug_listener,
1057 mlxsw_env);
1058 }
1059
1060 static int
1061 mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core,
1062 u8 slot_index)
1063 {
1064 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1065 int i, err;
1066
1067 for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
1068 char pmaos_pl[MLXSW_REG_PMAOS_LEN];
1069
1070 mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, i);
1071 mlxsw_reg_pmaos_e_set(pmaos_pl,
1072 MLXSW_REG_PMAOS_E_GENERATE_EVENT);
1073 mlxsw_reg_pmaos_ee_set(pmaos_pl, true);
1074 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
1075 if (err)
1076 return err;
1077 }
1078 return 0;
1079 }
1080
1081 int
1082 mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 slot_index,
1083 u8 module, u64 *p_counter)
1084 {
1085 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1086 struct mlxsw_env_module_info *module_info;
1087
1088 mutex_lock(&mlxsw_env->line_cards_lock);
1089 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1090 *p_counter = module_info->module_overheat_counter;
1091 mutex_unlock(&mlxsw_env->line_cards_lock);
1092
1093 return 0;
1094 }
1095 EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get);
1096
1097 void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 slot_index,
1098 u8 module)
1099 {
1100 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1101 struct mlxsw_env_module_info *module_info;
1102
1103 mutex_lock(&mlxsw_env->line_cards_lock);
1104 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1105 module_info->num_ports_mapped++;
1106 mutex_unlock(&mlxsw_env->line_cards_lock);
1107 }
1108 EXPORT_SYMBOL(mlxsw_env_module_port_map);
1109
1110 void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 slot_index,
1111 u8 module)
1112 {
1113 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1114 struct mlxsw_env_module_info *module_info;
1115
1116 mutex_lock(&mlxsw_env->line_cards_lock);
1117 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1118 module_info->num_ports_mapped--;
1119 mutex_unlock(&mlxsw_env->line_cards_lock);
1120 }
1121 EXPORT_SYMBOL(mlxsw_env_module_port_unmap);
1122
1123 int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 slot_index,
1124 u8 module)
1125 {
1126 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1127 struct mlxsw_env_module_info *module_info;
1128 int err = 0;
1129
1130 mutex_lock(&mlxsw_env->line_cards_lock);
1131
1132 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1133 if (module_info->power_mode_policy !=
1134 ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
1135 goto out_inc;
1136
1137 if (module_info->num_ports_up != 0)
1138 goto out_inc;
1139
1140
1141
1142
1143 err = __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module,
1144 false, NULL);
1145 if (err)
1146 goto out_unlock;
1147
1148 out_inc:
1149 module_info->num_ports_up++;
1150 out_unlock:
1151 mutex_unlock(&mlxsw_env->line_cards_lock);
1152 return err;
1153 }
1154 EXPORT_SYMBOL(mlxsw_env_module_port_up);
1155
1156 void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 slot_index,
1157 u8 module)
1158 {
1159 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1160 struct mlxsw_env_module_info *module_info;
1161
1162 mutex_lock(&mlxsw_env->line_cards_lock);
1163
1164 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1165 module_info->num_ports_up--;
1166
1167 if (module_info->power_mode_policy !=
1168 ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
1169 goto out_unlock;
1170
1171 if (module_info->num_ports_up != 0)
1172 goto out_unlock;
1173
1174
1175
1176
1177 __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module, true,
1178 NULL);
1179
1180 out_unlock:
1181 mutex_unlock(&mlxsw_env->line_cards_lock);
1182 }
1183 EXPORT_SYMBOL(mlxsw_env_module_port_down);
1184
1185 static int mlxsw_env_line_cards_alloc(struct mlxsw_env *env)
1186 {
1187 struct mlxsw_env_module_info *module_info;
1188 int i, j;
1189
1190 for (i = 0; i < env->num_of_slots; i++) {
1191 env->line_cards[i] = kzalloc(struct_size(env->line_cards[i],
1192 module_info,
1193 env->max_module_count),
1194 GFP_KERNEL);
1195 if (!env->line_cards[i])
1196 goto kzalloc_err;
1197
1198
1199
1200
1201 for (j = 0; j < env->max_module_count; j++) {
1202 module_info = &env->line_cards[i]->module_info[j];
1203 module_info->power_mode_policy =
1204 ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH;
1205 }
1206 }
1207
1208 return 0;
1209
1210 kzalloc_err:
1211 for (i--; i >= 0; i--)
1212 kfree(env->line_cards[i]);
1213 return -ENOMEM;
1214 }
1215
1216 static void mlxsw_env_line_cards_free(struct mlxsw_env *env)
1217 {
1218 int i = env->num_of_slots;
1219
1220 for (i--; i >= 0; i--)
1221 kfree(env->line_cards[i]);
1222 }
1223
1224 static int
1225 mlxsw_env_module_event_enable(struct mlxsw_env *mlxsw_env, u8 slot_index)
1226 {
1227 int err;
1228
1229 err = mlxsw_env_module_oper_state_event_enable(mlxsw_env->core,
1230 slot_index);
1231 if (err)
1232 return err;
1233
1234 err = mlxsw_env_module_temp_event_enable(mlxsw_env->core, slot_index);
1235 if (err)
1236 return err;
1237
1238 return 0;
1239 }
1240
1241 static void
1242 mlxsw_env_module_event_disable(struct mlxsw_env *mlxsw_env, u8 slot_index)
1243 {
1244 }
1245
1246 static int
1247 mlxsw_env_module_type_set(struct mlxsw_core *mlxsw_core, u8 slot_index)
1248 {
1249 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1250 int i;
1251
1252 for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
1253 struct mlxsw_env_module_info *module_info;
1254 char pmtm_pl[MLXSW_REG_PMTM_LEN];
1255 int err;
1256
1257 mlxsw_reg_pmtm_pack(pmtm_pl, slot_index, i);
1258 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtm), pmtm_pl);
1259 if (err)
1260 return err;
1261
1262 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index,
1263 i);
1264 module_info->type = mlxsw_reg_pmtm_module_type_get(pmtm_pl);
1265 }
1266
1267 return 0;
1268 }
1269
1270 static void
1271 mlxsw_env_linecard_modules_power_mode_apply(struct mlxsw_core *mlxsw_core,
1272 struct mlxsw_env *env,
1273 u8 slot_index)
1274 {
1275 int i;
1276
1277 for (i = 0; i < env->line_cards[slot_index]->module_count; i++) {
1278 enum ethtool_module_power_mode_policy policy;
1279 struct mlxsw_env_module_info *module_info;
1280 struct netlink_ext_ack extack;
1281 int err;
1282
1283 module_info = &env->line_cards[slot_index]->module_info[i];
1284 policy = module_info->power_mode_policy;
1285 err = mlxsw_env_set_module_power_mode_apply(mlxsw_core,
1286 slot_index, i,
1287 policy, &extack);
1288 if (err)
1289 dev_err(env->bus_info->dev, "%s\n", extack._msg);
1290 }
1291 }
1292
1293 static void
1294 mlxsw_env_got_active(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv)
1295 {
1296 struct mlxsw_env *mlxsw_env = priv;
1297 char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1298 int err;
1299
1300 mutex_lock(&mlxsw_env->line_cards_lock);
1301 if (__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
1302 goto out_unlock;
1303
1304 mlxsw_reg_mgpir_pack(mgpir_pl, slot_index);
1305 err = mlxsw_reg_query(mlxsw_env->core, MLXSW_REG(mgpir), mgpir_pl);
1306 if (err)
1307 goto out_unlock;
1308
1309 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,
1310 &mlxsw_env->line_cards[slot_index]->module_count,
1311 NULL);
1312
1313 err = mlxsw_env_module_event_enable(mlxsw_env, slot_index);
1314 if (err) {
1315 dev_err(mlxsw_env->bus_info->dev, "Failed to enable port module events for line card in slot %d\n",
1316 slot_index);
1317 goto err_mlxsw_env_module_event_enable;
1318 }
1319 err = mlxsw_env_module_type_set(mlxsw_env->core, slot_index);
1320 if (err) {
1321 dev_err(mlxsw_env->bus_info->dev, "Failed to set modules' type for line card in slot %d\n",
1322 slot_index);
1323 goto err_type_set;
1324 }
1325
1326 mlxsw_env->line_cards[slot_index]->active = true;
1327
1328 mlxsw_env_linecard_modules_power_mode_apply(mlxsw_core, mlxsw_env,
1329 slot_index);
1330 mutex_unlock(&mlxsw_env->line_cards_lock);
1331
1332 return;
1333
1334 err_type_set:
1335 mlxsw_env_module_event_disable(mlxsw_env, slot_index);
1336 err_mlxsw_env_module_event_enable:
1337 out_unlock:
1338 mutex_unlock(&mlxsw_env->line_cards_lock);
1339 }
1340
1341 static void
1342 mlxsw_env_got_inactive(struct mlxsw_core *mlxsw_core, u8 slot_index,
1343 void *priv)
1344 {
1345 struct mlxsw_env *mlxsw_env = priv;
1346
1347 mutex_lock(&mlxsw_env->line_cards_lock);
1348 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
1349 goto out_unlock;
1350 mlxsw_env->line_cards[slot_index]->active = false;
1351 mlxsw_env_module_event_disable(mlxsw_env, slot_index);
1352 mlxsw_env->line_cards[slot_index]->module_count = 0;
1353 out_unlock:
1354 mutex_unlock(&mlxsw_env->line_cards_lock);
1355 }
1356
1357 static struct mlxsw_linecards_event_ops mlxsw_env_event_ops = {
1358 .got_active = mlxsw_env_got_active,
1359 .got_inactive = mlxsw_env_got_inactive,
1360 };
1361
1362 int mlxsw_env_init(struct mlxsw_core *mlxsw_core,
1363 const struct mlxsw_bus_info *bus_info,
1364 struct mlxsw_env **p_env)
1365 {
1366 u8 module_count, num_of_slots, max_module_count;
1367 char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1368 struct mlxsw_env *env;
1369 int err;
1370
1371 mlxsw_reg_mgpir_pack(mgpir_pl, 0);
1372 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
1373 if (err)
1374 return err;
1375
1376 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &module_count,
1377 &num_of_slots);
1378
1379
1380
1381 max_module_count = num_of_slots ?
1382 mlxsw_reg_mgpir_max_modules_per_slot_get(mgpir_pl) :
1383 module_count;
1384
1385 env = kzalloc(struct_size(env, line_cards, num_of_slots + 1),
1386 GFP_KERNEL);
1387 if (!env)
1388 return -ENOMEM;
1389
1390 env->core = mlxsw_core;
1391 env->bus_info = bus_info;
1392 env->num_of_slots = num_of_slots + 1;
1393 env->max_module_count = max_module_count;
1394 err = mlxsw_env_line_cards_alloc(env);
1395 if (err)
1396 goto err_mlxsw_env_line_cards_alloc;
1397
1398 mutex_init(&env->line_cards_lock);
1399 *p_env = env;
1400
1401 err = mlxsw_linecards_event_ops_register(env->core,
1402 &mlxsw_env_event_ops, env);
1403 if (err)
1404 goto err_linecards_event_ops_register;
1405
1406 err = mlxsw_env_temp_warn_event_register(mlxsw_core);
1407 if (err)
1408 goto err_temp_warn_event_register;
1409
1410 err = mlxsw_env_module_plug_event_register(mlxsw_core);
1411 if (err)
1412 goto err_module_plug_event_register;
1413
1414
1415
1416
1417 env->line_cards[0]->module_count = num_of_slots ? 0 : module_count;
1418
1419
1420
1421
1422 err = mlxsw_env_module_event_enable(env, 0);
1423 if (err)
1424 goto err_mlxsw_env_module_event_enable;
1425
1426 err = mlxsw_env_module_type_set(mlxsw_core, 0);
1427 if (err)
1428 goto err_type_set;
1429
1430 env->line_cards[0]->active = true;
1431
1432 return 0;
1433
1434 err_type_set:
1435 mlxsw_env_module_event_disable(env, 0);
1436 err_mlxsw_env_module_event_enable:
1437 mlxsw_env_module_plug_event_unregister(env);
1438 err_module_plug_event_register:
1439 mlxsw_env_temp_warn_event_unregister(env);
1440 err_temp_warn_event_register:
1441 mlxsw_linecards_event_ops_unregister(env->core,
1442 &mlxsw_env_event_ops, env);
1443 err_linecards_event_ops_register:
1444 mutex_destroy(&env->line_cards_lock);
1445 mlxsw_env_line_cards_free(env);
1446 err_mlxsw_env_line_cards_alloc:
1447 kfree(env);
1448 return err;
1449 }
1450
1451 void mlxsw_env_fini(struct mlxsw_env *env)
1452 {
1453 env->line_cards[0]->active = false;
1454 mlxsw_env_module_event_disable(env, 0);
1455 mlxsw_env_module_plug_event_unregister(env);
1456
1457 mlxsw_core_flush_owq();
1458 mlxsw_env_temp_warn_event_unregister(env);
1459 mlxsw_linecards_event_ops_unregister(env->core,
1460 &mlxsw_env_event_ops, env);
1461 mutex_destroy(&env->line_cards_lock);
1462 mlxsw_env_line_cards_free(env);
1463 kfree(env);
1464 }