Commit e5b6a5ba authored by Vadim Pasternak's avatar Vadim Pasternak Committed by David S. Miller
Browse files

mlxsw: core: Extend port module data structures for line cards



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: default avatarVadim Pasternak <vadimp@nvidia.com>
Signed-off-by: default avatarIdo Schimmel <idosch@nvidia.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 34945452
Loading
Loading
Loading
Loading
+169 −73
Original line number Diff line number Diff line
@@ -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 @@ int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env)
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);
}