diff mbox series

[net-next,2/9] mlxsw: core: Extend port module data structures for line cards

Message ID 20220413151733.2738867-3-idosch@nvidia.com (mailing list archive)
State Accepted
Commit e5b6a5bac8cc12790eccf69e01372e135f9e4af2
Delegated to: Netdev Maintainers
Headers show
Series mlxsw: Preparations for line cards support | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 6 of 6 maintainers
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 531 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Ido Schimmel April 13, 2022, 3:17 p.m. UTC
From: Vadim Pasternak <vadimp@nvidia.com>

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>
---
 .../net/ethernet/mellanox/mlxsw/core_env.c    | 242 ++++++++++++------
 1 file changed, 169 insertions(+), 73 deletions(-)
diff mbox series

Patch

diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c
index 95fbfb1ca421..9adaa8978d68 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_env.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.c
@@ -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);
 }