Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
0002 /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
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; /* Maximum number of modules per-slot. */
0034     u8 num_of_slots; /* Including the main board. */
0035     struct mutex line_cards_lock; /* Protects line cards. */
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     /* MCIA register accepts buffer size <= 48. Page of size 128 should be
0157      * read by chunks of size 48, 48, 32. Align the size of the last chunk
0158      * to avoid reading after the end of the page.
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         /* Cross pages read, read until offset 256 in low page */
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             /* When reading upper pages 1, 2 and 3 the offset
0171              * starts at 128. Please refer to "QSFP+ Memory Map"
0172              * figure in SFF-8436 specification and to "CMIS Module
0173              * Memory Map" figure in CMIS specification for
0174              * graphical depiction.
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             /* When reading upper pages 1, 2 and 3 the offset
0182              * starts at 0 and I2C high address is used. Please refer
0183              * to "Memory Organization" figure in SFF-8472
0184              * specification for graphical depiction.
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     /* Validate if threshold reading is available through MTMP register,
0239      * otherwise fallback to read through MCIA.
0240      */
0241     if (module_emerg) {
0242         *temp = off == SFP_TEMP_HIGH_WARN ? module_crit : module_emerg;
0243         return 0;
0244     }
0245 
0246     /* Read Free Side Device Temperature Thresholds from page 03h
0247      * (MSB at lower byte address).
0248      * Bytes:
0249      * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);
0250      * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);
0251      * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);
0252      * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);
0253      */
0254 
0255     /* Validate module identifier value. */
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         /* For QSFP/CMIS module-defined thresholds are located in page
0263          * 02h, otherwise in page 03h.
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         /* Verify if transceiver provides diagnostic monitoring page */
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         /* Use SFF_8636 as base type. ethtool should recognize specific
0364          * type through the identifier value.
0365          */
0366         modinfo->type       = ETH_MODULE_SFF_8636;
0367         /* Verify if module EEPROM is a flat memory. In case of flat
0368          * memory only page 00h (0-255 bytes) can be read. Otherwise
0369          * upper pages 01h and 02h can also be read. Upper pages 10h
0370          * and 11h are currently not supported by the driver.
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     /* Validate module identifier value. */
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     /* Offset cannot be larger than 2 * ETH_MODULE_EEPROM_PAGE_LEN */
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     /* Avoid accessing an inactive line card, as it will result in an error. */
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     /* Mask all the bits except low power mode. */
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     /* Avoid accessing an inactive line card, as it will result in an error.
0675      * Cached configuration will be applied by mlxsw_env_got_active() when
0676      * line card becomes active.
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     /* If any ports are up, we are already in high power mode. */
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         /* In case it is not possible to query the module's threshold,
0822          * use the default value.
0823          */
0824         if (err)
0825             threshold_hi = MLXSW_REG_MTMP_THRESH_HI;
0826         else
0827             /* mlxsw_env_module_temp_thresholds_get() multiplies
0828              * Celsius degrees by 1000 whereas MTMP expects
0829              * temperature in 0.125 Celsius degrees units.
0830              * Convert threshold_hi to correct units.
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         /* 64-127 of sensor_index are mapped to the port modules
0889          * sequentially (module 0 is mapped to sensor_index 64,
0890          * module 1 to sensor_index 65 and so on)
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         /* MTWE only supports main board. */
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             /* Current state is "warning" and MTWE still reports
0903              * warning OR current state in "no warning" and MTWE
0904              * does not report warning.
0905              */
0906             mutex_unlock(&mlxsw_env->line_cards_lock);
0907             continue;
0908         } else if (is_overheat && !sensor_warning) {
0909             /* MTWE reports "no warning", turn is_overheat off.
0910              */
0911             module_info->is_overheat = false;
0912             mutex_unlock(&mlxsw_env->line_cards_lock);
0913         } else {
0914             /* Current state is "no warning" and MTWE reports
0915              * "warning", increase the counter and turn is_overheat
0916              * on.
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     /* Do not disable events on modules without sensors or faulty sensors
0994      * because FW returns errors.
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     /* Transition to high power mode following first port using the module
1141      * being put administratively up.
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     /* Transition to low power mode following last port using the module
1175      * being put administratively down.
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         /* Firmware defaults to high power mode policy where modules
1199          * are transitioned to high power mode following plug-in.
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     /* Apply power mode policy. */
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     /* If the system is modular, get the maximum number of modules per-slot.
1379      * Otherwise, get the maximum number of modules on the main board.
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     /* Set 'module_count' only for main board. Actual count for line card
1415      * is to be set after line card is activated.
1416      */
1417     env->line_cards[0]->module_count = num_of_slots ? 0 : module_count;
1418     /* Enable events only for main board. Line card events are to be
1419      * configured only after line card is activated. Before that, access to
1420      * modules on line cards is not allowed.
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     /* Make sure there is no more event work scheduled. */
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 }