mlxsw: core: Extend port module data structures for line cards
authorVadim Pasternak <vadimp@nvidia.com>
Wed, 13 Apr 2022 15:17:26 +0000 (18:17 +0300)
committerDavid S. Miller <davem@davemloft.net>
Fri, 15 Apr 2022 10:06:12 +0000 (11:06 +0100)
The port module core is tasked with module operations such as setting
power mode policy and reset. The per-module information is currently
stored in one large array suited for non-modular systems where only the
main board is present (i.e., slot index 0).

As a preparation for line cards support, allocate a per line card array
according to the queried number of slots in the system. For each line
card, allocate a module array according to the queried maximum number of
modules per-slot.

Signed-off-by: Vadim Pasternak <vadimp@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/core_env.c

index 95fbfb1ca421cc9ff18769bebb2330bb412e27b2..9adaa8978d6816d52d04dfd12592aaf8a538a0d4 100644 (file)
@@ -21,20 +21,36 @@ struct mlxsw_env_module_info {
        enum mlxsw_reg_pmtm_module_type type;
 };
 
-struct mlxsw_env {
-       struct mlxsw_core *core;
+struct mlxsw_env_line_card {
        u8 module_count;
-       struct mutex module_info_lock; /* Protects 'module_info'. */
        struct mlxsw_env_module_info module_info[];
 };
 
+struct mlxsw_env {
+       struct mlxsw_core *core;
+       u8 max_module_count; /* Maximum number of modules per-slot. */
+       u8 num_of_slots; /* Including the main board. */
+       struct mutex line_cards_lock; /* Protects line cards. */
+       struct mlxsw_env_line_card *line_cards[];
+};
+
+static struct
+mlxsw_env_module_info *mlxsw_env_module_info_get(struct mlxsw_core *mlxsw_core,
+                                                u8 slot_index, u8 module)
+{
+       struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
+
+       return &mlxsw_env->line_cards[slot_index]->module_info[module];
+}
+
 static int __mlxsw_env_validate_module_type(struct mlxsw_core *core,
                                            u8 slot_index, u8 module)
 {
-       struct mlxsw_env *mlxsw_env = mlxsw_core_env(core);
+       struct mlxsw_env_module_info *module_info;
        int err;
 
-       switch (mlxsw_env->module_info[module].type) {
+       module_info = mlxsw_env_module_info_get(core, slot_index, module);
+       switch (module_info->type) {
        case MLXSW_REG_PMTM_MODULE_TYPE_TWISTED_PAIR:
                err = -EINVAL;
                break;
@@ -51,9 +67,9 @@ static int mlxsw_env_validate_module_type(struct mlxsw_core *core,
        struct mlxsw_env *mlxsw_env = mlxsw_core_env(core);
        int err;
 
-       mutex_lock(&mlxsw_env->module_info_lock);
+       mutex_lock(&mlxsw_env->line_cards_lock);
        err = __mlxsw_env_validate_module_type(core, slot_index, module);
-       mutex_unlock(&mlxsw_env->module_info_lock);
+       mutex_unlock(&mlxsw_env->line_cards_lock);
 
        return err;
 }
@@ -472,6 +488,7 @@ int mlxsw_env_reset_module(struct net_device *netdev,
                           u8 module, u32 *flags)
 {
        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
+       struct mlxsw_env_module_info *module_info;
        u32 req = *flags;
        int err;
 
@@ -479,7 +496,7 @@ int mlxsw_env_reset_module(struct net_device *netdev,
            !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT)))
                return 0;
 
-       mutex_lock(&mlxsw_env->module_info_lock);
+       mutex_lock(&mlxsw_env->line_cards_lock);
 
        err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
        if (err) {
@@ -487,13 +504,14 @@ int mlxsw_env_reset_module(struct net_device *netdev,
                goto out;
        }
 
-       if (mlxsw_env->module_info[module].num_ports_up) {
+       module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
+       if (module_info->num_ports_up) {
                netdev_err(netdev, "Cannot reset module when ports using it are administratively up\n");
                err = -EINVAL;
                goto out;
        }
 
-       if (mlxsw_env->module_info[module].num_ports_mapped > 1 &&
+       if (module_info->num_ports_mapped > 1 &&
            !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) {
                netdev_err(netdev, "Cannot reset module without \"phy-shared\" flag when shared by multiple ports\n");
                err = -EINVAL;
@@ -509,7 +527,7 @@ int mlxsw_env_reset_module(struct net_device *netdev,
        *flags &= ~(ETH_RESET_PHY | (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT));
 
 out:
-       mutex_unlock(&mlxsw_env->module_info_lock);
+       mutex_unlock(&mlxsw_env->line_cards_lock);
        return err;
 }
 EXPORT_SYMBOL(mlxsw_env_reset_module);
@@ -521,11 +539,12 @@ mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
                                struct netlink_ext_ack *extack)
 {
        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
+       struct mlxsw_env_module_info *module_info;
        char mcion_pl[MLXSW_REG_MCION_LEN];
        u32 status_bits;
        int err;
 
-       mutex_lock(&mlxsw_env->module_info_lock);
+       mutex_lock(&mlxsw_env->line_cards_lock);
 
        err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
        if (err) {
@@ -533,7 +552,8 @@ mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
                goto out;
        }
 
-       params->policy = mlxsw_env->module_info[module].power_mode_policy;
+       module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
+       params->policy = module_info->power_mode_policy;
 
        mlxsw_reg_mcion_pack(mcion_pl, slot_index, module);
        err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcion), mcion_pl);
@@ -552,7 +572,7 @@ mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
                params->mode = ETHTOOL_MODULE_POWER_MODE_HIGH;
 
 out:
-       mutex_unlock(&mlxsw_env->module_info_lock);
+       mutex_unlock(&mlxsw_env->line_cards_lock);
        return err;
 }
 EXPORT_SYMBOL(mlxsw_env_get_module_power_mode);
@@ -634,6 +654,7 @@ mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
                                struct netlink_ext_ack *extack)
 {
        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
+       struct mlxsw_env_module_info *module_info;
        bool low_power;
        int err = 0;
 
@@ -643,7 +664,7 @@ mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
                return -EOPNOTSUPP;
        }
 
-       mutex_lock(&mlxsw_env->module_info_lock);
+       mutex_lock(&mlxsw_env->line_cards_lock);
 
        err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
        if (err) {
@@ -652,11 +673,12 @@ mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
                goto out;
        }
 
-       if (mlxsw_env->module_info[module].power_mode_policy == policy)
+       module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
+       if (module_info->power_mode_policy == policy)
                goto out;
 
        /* If any ports are up, we are already in high power mode. */
-       if (mlxsw_env->module_info[module].num_ports_up)
+       if (module_info->num_ports_up)
                goto out_set_policy;
 
        low_power = policy == ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO;
@@ -666,9 +688,9 @@ mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
                goto out;
 
 out_set_policy:
-       mlxsw_env->module_info[module].power_mode_policy = policy;
+       module_info->power_mode_policy = policy;
 out:
-       mutex_unlock(&mlxsw_env->module_info_lock);
+       mutex_unlock(&mlxsw_env->line_cards_lock);
        return err;
 }
 EXPORT_SYMBOL(mlxsw_env_set_module_power_mode);
@@ -748,10 +770,11 @@ mlxsw_env_temp_event_set(struct mlxsw_core *mlxsw_core, u8 slot_index,
 static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core,
                                              u8 slot_index)
 {
+       struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
        int i, err, sensor_index;
        bool has_temp_sensor;
 
-       for (i = 0; i < mlxsw_core_env(mlxsw_core)->module_count; i++) {
+       for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
                err = mlxsw_env_module_has_temp_sensor(mlxsw_core, slot_index,
                                                       i, &has_temp_sensor);
                if (err)
@@ -779,6 +802,7 @@ struct mlxsw_env_module_temp_warn_event {
 static void mlxsw_env_mtwe_event_work(struct work_struct *work)
 {
        struct mlxsw_env_module_temp_warn_event *event;
+       struct mlxsw_env_module_info *module_info;
        struct mlxsw_env *mlxsw_env;
        int i, sensor_warning;
        bool is_overheat;
@@ -787,7 +811,7 @@ static void mlxsw_env_mtwe_event_work(struct work_struct *work)
                             work);
        mlxsw_env = event->mlxsw_env;
 
-       for (i = 0; i < mlxsw_env->module_count; i++) {
+       for (i = 0; i < mlxsw_env->max_module_count; i++) {
                /* 64-127 of sensor_index are mapped to the port modules
                 * sequentially (module 0 is mapped to sensor_index 64,
                 * module 1 to sensor_index 65 and so on)
@@ -795,9 +819,10 @@ static void mlxsw_env_mtwe_event_work(struct work_struct *work)
                sensor_warning =
                        mlxsw_reg_mtwe_sensor_warning_get(event->mtwe_pl,
                                                          i + MLXSW_REG_MTMP_MODULE_INDEX_MIN);
-               mutex_lock(&mlxsw_env->module_info_lock);
-               is_overheat =
-                       mlxsw_env->module_info[i].is_overheat;
+               mutex_lock(&mlxsw_env->line_cards_lock);
+               /* MTWE only supports main board. */
+               module_info = mlxsw_env_module_info_get(mlxsw_env->core, 0, i);
+               is_overheat = module_info->is_overheat;
 
                if ((is_overheat && sensor_warning) ||
                    (!is_overheat && !sensor_warning)) {
@@ -805,21 +830,21 @@ static void mlxsw_env_mtwe_event_work(struct work_struct *work)
                         * warning OR current state in "no warning" and MTWE
                         * does not report warning.
                         */
-                       mutex_unlock(&mlxsw_env->module_info_lock);
+                       mutex_unlock(&mlxsw_env->line_cards_lock);
                        continue;
                } else if (is_overheat && !sensor_warning) {
                        /* MTWE reports "no warning", turn is_overheat off.
                         */
-                       mlxsw_env->module_info[i].is_overheat = false;
-                       mutex_unlock(&mlxsw_env->module_info_lock);
+                       module_info->is_overheat = false;
+                       mutex_unlock(&mlxsw_env->line_cards_lock);
                } else {
                        /* Current state is "no warning" and MTWE reports
                         * "warning", increase the counter and turn is_overheat
                         * on.
                         */
-                       mlxsw_env->module_info[i].is_overheat = true;
-                       mlxsw_env->module_info[i].module_overheat_counter++;
-                       mutex_unlock(&mlxsw_env->module_info_lock);
+                       module_info->is_overheat = true;
+                       module_info->module_overheat_counter++;
+                       mutex_unlock(&mlxsw_env->line_cards_lock);
                }
        }
 
@@ -871,6 +896,7 @@ struct mlxsw_env_module_plug_unplug_event {
 static void mlxsw_env_pmpe_event_work(struct work_struct *work)
 {
        struct mlxsw_env_module_plug_unplug_event *event;
+       struct mlxsw_env_module_info *module_info;
        struct mlxsw_env *mlxsw_env;
        bool has_temp_sensor;
        u16 sensor_index;
@@ -880,9 +906,12 @@ static void mlxsw_env_pmpe_event_work(struct work_struct *work)
                             work);
        mlxsw_env = event->mlxsw_env;
 
-       mutex_lock(&mlxsw_env->module_info_lock);
-       mlxsw_env->module_info[event->module].is_overheat = false;
-       mutex_unlock(&mlxsw_env->module_info_lock);
+       mutex_lock(&mlxsw_env->line_cards_lock);
+       module_info = mlxsw_env_module_info_get(mlxsw_env->core,
+                                               event->slot_index,
+                                               event->module);
+       module_info->is_overheat = false;
+       mutex_unlock(&mlxsw_env->line_cards_lock);
 
        err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core,
                                               event->slot_index,
@@ -909,12 +938,14 @@ static void
 mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl,
                             void *priv)
 {
+       u8 slot_index = mlxsw_reg_pmpe_slot_index_get(pmpe_pl);
        struct mlxsw_env_module_plug_unplug_event *event;
        enum mlxsw_reg_pmpe_module_status module_status;
        u8 module = mlxsw_reg_pmpe_module_get(pmpe_pl);
        struct mlxsw_env *mlxsw_env = priv;
 
-       if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
+       if (WARN_ON_ONCE(module >= mlxsw_env->max_module_count ||
+                        slot_index >= mlxsw_env->num_of_slots))
                return;
 
        module_status = mlxsw_reg_pmpe_module_status_get(pmpe_pl);
@@ -926,7 +957,7 @@ mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl,
                return;
 
        event->mlxsw_env = mlxsw_env;
-       event->slot_index = 0;
+       event->slot_index = slot_index;
        event->module = module;
        INIT_WORK(&event->work, mlxsw_env_pmpe_event_work);
        mlxsw_core_schedule_work(&event->work);
@@ -957,9 +988,10 @@ static int
 mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core,
                                         u8 slot_index)
 {
+       struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
        int i, err;
 
-       for (i = 0; i < mlxsw_core_env(mlxsw_core)->module_count; i++) {
+       for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
                char pmaos_pl[MLXSW_REG_PMAOS_LEN];
 
                mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, i);
@@ -978,10 +1010,12 @@ mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 slot_ind
                                      u8 module, u64 *p_counter)
 {
        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
+       struct mlxsw_env_module_info *module_info;
 
-       mutex_lock(&mlxsw_env->module_info_lock);
-       *p_counter = mlxsw_env->module_info[module].module_overheat_counter;
-       mutex_unlock(&mlxsw_env->module_info_lock);
+       mutex_lock(&mlxsw_env->line_cards_lock);
+       module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
+       *p_counter = module_info->module_overheat_counter;
+       mutex_unlock(&mlxsw_env->line_cards_lock);
 
        return 0;
 }
@@ -991,10 +1025,12 @@ void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 slot_index,
                               u8 module)
 {
        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
+       struct mlxsw_env_module_info *module_info;
 
-       mutex_lock(&mlxsw_env->module_info_lock);
-       mlxsw_env->module_info[module].num_ports_mapped++;
-       mutex_unlock(&mlxsw_env->module_info_lock);
+       mutex_lock(&mlxsw_env->line_cards_lock);
+       module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
+       module_info->num_ports_mapped++;
+       mutex_unlock(&mlxsw_env->line_cards_lock);
 }
 EXPORT_SYMBOL(mlxsw_env_module_port_map);
 
@@ -1002,10 +1038,12 @@ void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 slot_index,
                                 u8 module)
 {
        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
+       struct mlxsw_env_module_info *module_info;
 
-       mutex_lock(&mlxsw_env->module_info_lock);
-       mlxsw_env->module_info[module].num_ports_mapped--;
-       mutex_unlock(&mlxsw_env->module_info_lock);
+       mutex_lock(&mlxsw_env->line_cards_lock);
+       module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
+       module_info->num_ports_mapped--;
+       mutex_unlock(&mlxsw_env->line_cards_lock);
 }
 EXPORT_SYMBOL(mlxsw_env_module_port_unmap);
 
@@ -1013,15 +1051,17 @@ int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 slot_index,
                             u8 module)
 {
        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
+       struct mlxsw_env_module_info *module_info;
        int err = 0;
 
-       mutex_lock(&mlxsw_env->module_info_lock);
+       mutex_lock(&mlxsw_env->line_cards_lock);
 
-       if (mlxsw_env->module_info[module].power_mode_policy !=
+       module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
+       if (module_info->power_mode_policy !=
            ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
                goto out_inc;
 
-       if (mlxsw_env->module_info[module].num_ports_up != 0)
+       if (module_info->num_ports_up != 0)
                goto out_inc;
 
        /* Transition to high power mode following first port using the module
@@ -1033,9 +1073,9 @@ int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 slot_index,
                goto out_unlock;
 
 out_inc:
-       mlxsw_env->module_info[module].num_ports_up++;
+       module_info->num_ports_up++;
 out_unlock:
-       mutex_unlock(&mlxsw_env->module_info_lock);
+       mutex_unlock(&mlxsw_env->line_cards_lock);
        return err;
 }
 EXPORT_SYMBOL(mlxsw_env_module_port_up);
@@ -1044,16 +1084,18 @@ void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 slot_index,
                                u8 module)
 {
        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
+       struct mlxsw_env_module_info *module_info;
 
-       mutex_lock(&mlxsw_env->module_info_lock);
+       mutex_lock(&mlxsw_env->line_cards_lock);
 
-       mlxsw_env->module_info[module].num_ports_up--;
+       module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
+       module_info->num_ports_up--;
 
-       if (mlxsw_env->module_info[module].power_mode_policy !=
+       if (module_info->power_mode_policy !=
            ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
                goto out_unlock;
 
-       if (mlxsw_env->module_info[module].num_ports_up != 0)
+       if (module_info->num_ports_up != 0)
                goto out_unlock;
 
        /* Transition to low power mode following last port using the module
@@ -1063,17 +1105,57 @@ void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 slot_index,
                                          NULL);
 
 out_unlock:
-       mutex_unlock(&mlxsw_env->module_info_lock);
+       mutex_unlock(&mlxsw_env->line_cards_lock);
 }
 EXPORT_SYMBOL(mlxsw_env_module_port_down);
 
+static int mlxsw_env_line_cards_alloc(struct mlxsw_env *env)
+{
+       struct mlxsw_env_module_info *module_info;
+       int i, j;
+
+       for (i = 0; i < env->num_of_slots; i++) {
+               env->line_cards[i] = kzalloc(struct_size(env->line_cards[i],
+                                                        module_info,
+                                                        env->max_module_count),
+                                                        GFP_KERNEL);
+               if (!env->line_cards[i])
+                       goto kzalloc_err;
+
+               /* Firmware defaults to high power mode policy where modules
+                * are transitioned to high power mode following plug-in.
+                */
+               for (j = 0; j < env->max_module_count; j++) {
+                       module_info = &env->line_cards[i]->module_info[j];
+                       module_info->power_mode_policy =
+                                       ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH;
+               }
+       }
+
+       return 0;
+
+kzalloc_err:
+       for (i--; i >= 0; i--)
+               kfree(env->line_cards[i]);
+       return -ENOMEM;
+}
+
+static void mlxsw_env_line_cards_free(struct mlxsw_env *env)
+{
+       int i = env->num_of_slots;
+
+       for (i--; i >= 0; i--)
+               kfree(env->line_cards[i]);
+}
+
 static int
 mlxsw_env_module_type_set(struct mlxsw_core *mlxsw_core, u8 slot_index)
 {
        struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
        int i;
 
-       for (i = 0; i < mlxsw_env->module_count; i++) {
+       for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
+               struct mlxsw_env_module_info *module_info;
                char pmtm_pl[MLXSW_REG_PMTM_LEN];
                int err;
 
@@ -1082,8 +1164,9 @@ mlxsw_env_module_type_set(struct mlxsw_core *mlxsw_core, u8 slot_index)
                if (err)
                        return err;
 
-               mlxsw_env->module_info[i].type =
-                       mlxsw_reg_pmtm_module_type_get(pmtm_pl);
+               module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index,
+                                                       i);
+               module_info->type = mlxsw_reg_pmtm_module_type_get(pmtm_pl);
        }
 
        return 0;
@@ -1091,32 +1174,38 @@ mlxsw_env_module_type_set(struct mlxsw_core *mlxsw_core, u8 slot_index)
 
 int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env)
 {
+       u8 module_count, num_of_slots, max_module_count;
        char mgpir_pl[MLXSW_REG_MGPIR_LEN];
        struct mlxsw_env *env;
-       u8 module_count;
-       int i, err;
+       int err;
 
        mlxsw_reg_mgpir_pack(mgpir_pl, 0);
        err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
        if (err)
                return err;
 
-       mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &module_count, NULL);
+       mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &module_count,
+                              &num_of_slots);
+       /* If the system is modular, get the maximum number of modules per-slot.
+        * Otherwise, get the maximum number of modules on the main board.
+        */
+       max_module_count = num_of_slots ?
+                          mlxsw_reg_mgpir_max_modules_per_slot_get(mgpir_pl) :
+                          module_count;
 
-       env = kzalloc(struct_size(env, module_info, module_count), GFP_KERNEL);
+       env = kzalloc(struct_size(env, line_cards, num_of_slots + 1),
+                     GFP_KERNEL);
        if (!env)
                return -ENOMEM;
 
-       /* Firmware defaults to high power mode policy where modules are
-        * transitioned to high power mode following plug-in.
-        */
-       for (i = 0; i < module_count; i++)
-               env->module_info[i].power_mode_policy =
-                       ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH;
-
-       mutex_init(&env->module_info_lock);
        env->core = mlxsw_core;
-       env->module_count = module_count;
+       env->num_of_slots = num_of_slots + 1;
+       env->max_module_count = max_module_count;
+       err = mlxsw_env_line_cards_alloc(env);
+       if (err)
+               goto err_mlxsw_env_line_cards_alloc;
+
+       mutex_init(&env->line_cards_lock);
        *p_env = env;
 
        err = mlxsw_env_temp_warn_event_register(mlxsw_core);
@@ -1127,6 +1216,10 @@ int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env)
        if (err)
                goto err_module_plug_event_register;
 
+       /* Set 'module_count' only for main board. Actual count for line card
+        * is to be set after line card is activated.
+        */
+       env->line_cards[0]->module_count = num_of_slots ? 0 : module_count;
        err = mlxsw_env_module_oper_state_event_enable(mlxsw_core, 0);
        if (err)
                goto err_oper_state_event_enable;
@@ -1148,7 +1241,9 @@ err_oper_state_event_enable:
 err_module_plug_event_register:
        mlxsw_env_temp_warn_event_unregister(env);
 err_temp_warn_event_register:
-       mutex_destroy(&env->module_info_lock);
+       mutex_destroy(&env->line_cards_lock);
+       mlxsw_env_line_cards_free(env);
+err_mlxsw_env_line_cards_alloc:
        kfree(env);
        return err;
 }
@@ -1159,6 +1254,7 @@ void mlxsw_env_fini(struct mlxsw_env *env)
        /* Make sure there is no more event work scheduled. */
        mlxsw_core_flush_owq();
        mlxsw_env_temp_warn_event_unregister(env);
-       mutex_destroy(&env->module_info_lock);
+       mutex_destroy(&env->line_cards_lock);
+       mlxsw_env_line_cards_free(env);
        kfree(env);
 }