brintos

brintos / linux-shallow public Read only

0
0
Text · 42.6 KiB · 294e758 Raw
1537 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */3 4#include <linux/kernel.h>5#include <linux/err.h>6#include <linux/ethtool.h>7#include <linux/sfp.h>8#include <linux/mutex.h>9 10#include "core.h"11#include "core_env.h"12#include "item.h"13#include "reg.h"14 15struct mlxsw_env_module_info {16	u64 module_overheat_counter;17	bool is_overheat;18	int num_ports_mapped;19	int num_ports_up;20	enum ethtool_module_power_mode_policy power_mode_policy;21	enum mlxsw_reg_pmtm_module_type type;22};23 24struct mlxsw_env_line_card {25	u8 module_count;26	bool active;27	struct mlxsw_env_module_info module_info[];28};29 30struct mlxsw_env {31	struct mlxsw_core *core;32	const struct mlxsw_bus_info *bus_info;33	u8 max_module_count; /* Maximum number of modules per-slot. */34	u8 num_of_slots; /* Including the main board. */35	u8 max_eeprom_len; /* Maximum module EEPROM transaction length. */36	struct mutex line_cards_lock; /* Protects line cards. */37	struct mlxsw_env_line_card *line_cards[] __counted_by(num_of_slots);38};39 40static bool __mlxsw_env_linecard_is_active(struct mlxsw_env *mlxsw_env,41					   u8 slot_index)42{43	return mlxsw_env->line_cards[slot_index]->active;44}45 46static bool mlxsw_env_linecard_is_active(struct mlxsw_env *mlxsw_env,47					 u8 slot_index)48{49	bool active;50 51	mutex_lock(&mlxsw_env->line_cards_lock);52	active = __mlxsw_env_linecard_is_active(mlxsw_env, slot_index);53	mutex_unlock(&mlxsw_env->line_cards_lock);54 55	return active;56}57 58static struct59mlxsw_env_module_info *mlxsw_env_module_info_get(struct mlxsw_core *mlxsw_core,60						 u8 slot_index, u8 module)61{62	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);63 64	return &mlxsw_env->line_cards[slot_index]->module_info[module];65}66 67static int __mlxsw_env_validate_module_type(struct mlxsw_core *core,68					    u8 slot_index, u8 module)69{70	struct mlxsw_env *mlxsw_env = mlxsw_core_env(core);71	struct mlxsw_env_module_info *module_info;72	int err;73 74	if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))75		return 0;76 77	module_info = mlxsw_env_module_info_get(core, slot_index, module);78	switch (module_info->type) {79	case MLXSW_REG_PMTM_MODULE_TYPE_TWISTED_PAIR:80		err = -EINVAL;81		break;82	default:83		err = 0;84	}85 86	return err;87}88 89static int mlxsw_env_validate_module_type(struct mlxsw_core *core,90					  u8 slot_index, u8 module)91{92	struct mlxsw_env *mlxsw_env = mlxsw_core_env(core);93	int err;94 95	mutex_lock(&mlxsw_env->line_cards_lock);96	err = __mlxsw_env_validate_module_type(core, slot_index, module);97	mutex_unlock(&mlxsw_env->line_cards_lock);98 99	return err;100}101 102static int103mlxsw_env_validate_cable_ident(struct mlxsw_core *core, u8 slot_index, int id,104			       bool *qsfp, bool *cmis)105{106	char mcia_pl[MLXSW_REG_MCIA_LEN];107	char *eeprom_tmp;108	u8 ident;109	int err;110 111	err = mlxsw_env_validate_module_type(core, slot_index, id);112	if (err)113		return err;114 115	mlxsw_reg_mcia_pack(mcia_pl, slot_index, id,116			    MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1,117			    MLXSW_REG_MCIA_I2C_ADDR_LOW);118	err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);119	if (err)120		return err;121	eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);122	ident = eeprom_tmp[0];123	*cmis = false;124	switch (ident) {125	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:126		*qsfp = false;127		break;128	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:129	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:130	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:131		*qsfp = true;132		break;133	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:134	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_OSFP:135		*qsfp = true;136		*cmis = true;137		break;138	default:139		return -EINVAL;140	}141 142	return 0;143}144 145static int146mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, u8 slot_index,147			      int module, u16 offset, u16 size, void *data,148			      bool qsfp, unsigned int *p_read_size)149{150	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);151	char mcia_pl[MLXSW_REG_MCIA_LEN];152	char *eeprom_tmp;153	u16 i2c_addr;154	u8 page = 0;155	int status;156	int err;157 158	size = min_t(u16, size, mlxsw_env->max_eeprom_len);159 160	if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&161	    offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)162		/* Cross pages read, read until offset 256 in low page */163		size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;164 165	i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;166	if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {167		if (qsfp) {168			/* When reading upper pages 1, 2 and 3 the offset169			 * starts at 128. Please refer to "QSFP+ Memory Map"170			 * figure in SFF-8436 specification and to "CMIS Module171			 * Memory Map" figure in CMIS specification for172			 * graphical depiction.173			 */174			page = MLXSW_REG_MCIA_PAGE_GET(offset);175			offset -= MLXSW_REG_MCIA_EEPROM_UP_PAGE_LENGTH * page;176			if (offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)177				size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;178		} else {179			/* When reading upper pages 1, 2 and 3 the offset180			 * starts at 0 and I2C high address is used. Please refer181			 * to "Memory Organization" figure in SFF-8472182			 * specification for graphical depiction.183			 */184			i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;185			offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;186		}187	}188 189	mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, page, offset, size,190			    i2c_addr);191 192	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);193	if (err)194		return err;195 196	status = mlxsw_reg_mcia_status_get(mcia_pl);197	if (status)198		return -EIO;199 200	eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);201	memcpy(data, eeprom_tmp, size);202	*p_read_size = size;203 204	return 0;205}206 207int208mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, u8 slot_index,209				     int module, int off, int *temp)210{211	unsigned int module_temp, module_crit, module_emerg;212	union {213		u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];214		u16 temp;215	} temp_thresh;216	char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};217	char mtmp_pl[MLXSW_REG_MTMP_LEN];218	char *eeprom_tmp;219	bool qsfp, cmis;220	int page;221	int err;222 223	mlxsw_reg_mtmp_pack(mtmp_pl, slot_index,224			    MLXSW_REG_MTMP_MODULE_INDEX_MIN + module, false,225			    false);226	err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);227	if (err)228		return err;229	mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, &module_crit,230			      &module_emerg, NULL);231	if (!module_temp) {232		*temp = 0;233		return 0;234	}235 236	/* Validate if threshold reading is available through MTMP register,237	 * otherwise fallback to read through MCIA.238	 */239	if (module_emerg) {240		*temp = off == SFP_TEMP_HIGH_WARN ? module_crit : module_emerg;241		return 0;242	}243 244	/* Read Free Side Device Temperature Thresholds from page 03h245	 * (MSB at lower byte address).246	 * Bytes:247	 * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);248	 * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);249	 * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);250	 * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);251	 */252 253	/* Validate module identifier value. */254	err = mlxsw_env_validate_cable_ident(core, slot_index, module, &qsfp,255					     &cmis);256	if (err)257		return err;258 259	if (qsfp) {260		/* For QSFP/CMIS module-defined thresholds are located in page261		 * 02h, otherwise in page 03h.262		 */263		if (cmis)264			page = MLXSW_REG_MCIA_TH_PAGE_CMIS_NUM;265		else266			page = MLXSW_REG_MCIA_TH_PAGE_NUM;267		mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, page,268				    MLXSW_REG_MCIA_TH_PAGE_OFF + off,269				    MLXSW_REG_MCIA_TH_ITEM_SIZE,270				    MLXSW_REG_MCIA_I2C_ADDR_LOW);271	} else {272		mlxsw_reg_mcia_pack(mcia_pl, slot_index, module,273				    MLXSW_REG_MCIA_PAGE0_LO,274				    off, MLXSW_REG_MCIA_TH_ITEM_SIZE,275				    MLXSW_REG_MCIA_I2C_ADDR_HIGH);276	}277 278	err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);279	if (err)280		return err;281 282	eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);283	memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);284	*temp = temp_thresh.temp * 1000;285 286	return 0;287}288 289int mlxsw_env_get_module_info(struct net_device *netdev,290			      struct mlxsw_core *mlxsw_core, u8 slot_index,291			      int module, struct ethtool_modinfo *modinfo)292{293	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);294	u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];295	u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;296	u8 module_rev_id, module_id, diag_mon;297	unsigned int read_size;298	int err;299 300	if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {301		netdev_err(netdev, "Cannot read EEPROM of module on an inactive line card\n");302		return -EIO;303	}304 305	err = mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);306	if (err) {307		netdev_err(netdev,308			   "EEPROM is not equipped on port module type");309		return err;310	}311 312	err = mlxsw_env_query_module_eeprom(mlxsw_core, slot_index, module, 0,313					    offset, module_info, false,314					    &read_size);315	if (err)316		return err;317 318	if (read_size < offset)319		return -EIO;320 321	module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];322	module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];323 324	switch (module_id) {325	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:326		modinfo->type       = ETH_MODULE_SFF_8436;327		modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;328		break;329	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:330	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:331		if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||332		    module_rev_id >=333		    MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {334			modinfo->type       = ETH_MODULE_SFF_8636;335			modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;336		} else {337			modinfo->type       = ETH_MODULE_SFF_8436;338			modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;339		}340		break;341	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:342		/* Verify if transceiver provides diagnostic monitoring page */343		err = mlxsw_env_query_module_eeprom(mlxsw_core, slot_index,344						    module, SFP_DIAGMON, 1,345						    &diag_mon, false,346						    &read_size);347		if (err)348			return err;349 350		if (read_size < 1)351			return -EIO;352 353		modinfo->type       = ETH_MODULE_SFF_8472;354		if (diag_mon)355			modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;356		else357			modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;358		break;359	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:360	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_OSFP:361		/* Use SFF_8636 as base type. ethtool should recognize specific362		 * type through the identifier value.363		 */364		modinfo->type       = ETH_MODULE_SFF_8636;365		/* Verify if module EEPROM is a flat memory. In case of flat366		 * memory only page 00h (0-255 bytes) can be read. Otherwise367		 * upper pages 01h and 02h can also be read. Upper pages 10h368		 * and 11h are currently not supported by the driver.369		 */370		if (module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_TYPE_ID] &371		    MLXSW_REG_MCIA_EEPROM_CMIS_FLAT_MEMORY)372			modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;373		else374			modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;375		break;376	default:377		return -EINVAL;378	}379 380	return 0;381}382EXPORT_SYMBOL(mlxsw_env_get_module_info);383 384int mlxsw_env_get_module_eeprom(struct net_device *netdev,385				struct mlxsw_core *mlxsw_core, u8 slot_index,386				int module, struct ethtool_eeprom *ee,387				u8 *data)388{389	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);390	int offset = ee->offset;391	unsigned int read_size;392	bool qsfp, cmis;393	int i = 0;394	int err;395 396	if (!ee->len)397		return -EINVAL;398 399	if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {400		netdev_err(netdev, "Cannot read EEPROM of module on an inactive line card\n");401		return -EIO;402	}403 404	memset(data, 0, ee->len);405	/* Validate module identifier value. */406	err = mlxsw_env_validate_cable_ident(mlxsw_core, slot_index, module,407					     &qsfp, &cmis);408	if (err)409		return err;410 411	while (i < ee->len) {412		err = mlxsw_env_query_module_eeprom(mlxsw_core, slot_index,413						    module, offset,414						    ee->len - i, data + i,415						    qsfp, &read_size);416		if (err) {417			netdev_err(netdev, "Eeprom query failed\n");418			return err;419		}420 421		i += read_size;422		offset += read_size;423	}424 425	return 0;426}427EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);428 429static int mlxsw_env_mcia_status_process(const char *mcia_pl,430					 struct netlink_ext_ack *extack)431{432	u8 status = mlxsw_reg_mcia_status_get(mcia_pl);433 434	switch (status) {435	case MLXSW_REG_MCIA_STATUS_GOOD:436		return 0;437	case MLXSW_REG_MCIA_STATUS_NO_EEPROM_MODULE:438		NL_SET_ERR_MSG_MOD(extack, "No response from module's EEPROM");439		return -EIO;440	case MLXSW_REG_MCIA_STATUS_MODULE_NOT_SUPPORTED:441		NL_SET_ERR_MSG_MOD(extack, "Module type not supported by the device");442		return -EOPNOTSUPP;443	case MLXSW_REG_MCIA_STATUS_MODULE_NOT_CONNECTED:444		NL_SET_ERR_MSG_MOD(extack, "No module present indication");445		return -EIO;446	case MLXSW_REG_MCIA_STATUS_I2C_ERROR:447		NL_SET_ERR_MSG_MOD(extack, "Error occurred while trying to access module's EEPROM using I2C");448		return -EIO;449	case MLXSW_REG_MCIA_STATUS_MODULE_DISABLED:450		NL_SET_ERR_MSG_MOD(extack, "Module is disabled");451		return -EIO;452	default:453		NL_SET_ERR_MSG_MOD(extack, "Unknown error");454		return -EIO;455	}456}457 458int459mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core,460				    u8 slot_index, u8 module,461				    const struct ethtool_module_eeprom *page,462				    struct netlink_ext_ack *extack)463{464	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);465	u32 bytes_read = 0;466	u16 device_addr;467	int err;468 469	if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {470		NL_SET_ERR_MSG_MOD(extack,471				   "Cannot read EEPROM of module on an inactive line card");472		return -EIO;473	}474 475	err = mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);476	if (err) {477		NL_SET_ERR_MSG_MOD(extack, "EEPROM is not equipped on port module type");478		return err;479	}480 481	/* Offset cannot be larger than 2 * ETH_MODULE_EEPROM_PAGE_LEN */482	device_addr = page->offset;483 484	while (bytes_read < page->length) {485		char mcia_pl[MLXSW_REG_MCIA_LEN];486		char *eeprom_tmp;487		u8 size;488 489		size = min_t(u8, page->length - bytes_read,490			     mlxsw_env->max_eeprom_len);491 492		mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, page->page,493				    device_addr + bytes_read, size,494				    page->i2c_address);495		mlxsw_reg_mcia_bank_number_set(mcia_pl, page->bank);496 497		err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);498		if (err) {499			NL_SET_ERR_MSG_MOD(extack, "Failed to access module's EEPROM");500			return err;501		}502 503		err = mlxsw_env_mcia_status_process(mcia_pl, extack);504		if (err)505			return err;506 507		eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);508		memcpy(page->data + bytes_read, eeprom_tmp, size);509		bytes_read += size;510	}511 512	return bytes_read;513}514EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page);515 516int517mlxsw_env_set_module_eeprom_by_page(struct mlxsw_core *mlxsw_core,518				    u8 slot_index, u8 module,519				    const struct ethtool_module_eeprom *page,520				    struct netlink_ext_ack *extack)521{522	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);523	u32 bytes_written = 0;524	u16 device_addr;525	int err;526 527	if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {528		NL_SET_ERR_MSG_MOD(extack,529				   "Cannot write to EEPROM of a module on an inactive line card");530		return -EIO;531	}532 533	err = mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);534	if (err) {535		NL_SET_ERR_MSG_MOD(extack, "EEPROM is not equipped on port module type");536		return err;537	}538 539	device_addr = page->offset;540 541	while (bytes_written < page->length) {542		char mcia_pl[MLXSW_REG_MCIA_LEN];543		char eeprom_tmp[128] = {};544		u8 size;545 546		size = min_t(u8, page->length - bytes_written,547			     mlxsw_env->max_eeprom_len);548 549		mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, page->page,550				    device_addr + bytes_written, size,551				    page->i2c_address);552		mlxsw_reg_mcia_bank_number_set(mcia_pl, page->bank);553		memcpy(eeprom_tmp, page->data + bytes_written, size);554		mlxsw_reg_mcia_eeprom_memcpy_to(mcia_pl, eeprom_tmp);555 556		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcia), mcia_pl);557		if (err) {558			NL_SET_ERR_MSG_MOD(extack, "Failed to access module's EEPROM");559			return err;560		}561 562		err = mlxsw_env_mcia_status_process(mcia_pl, extack);563		if (err)564			return err;565 566		bytes_written += size;567	}568 569	return 0;570}571EXPORT_SYMBOL(mlxsw_env_set_module_eeprom_by_page);572 573static int mlxsw_env_module_reset(struct mlxsw_core *mlxsw_core, u8 slot_index,574				  u8 module)575{576	char pmaos_pl[MLXSW_REG_PMAOS_LEN];577 578	mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, module);579	mlxsw_reg_pmaos_rst_set(pmaos_pl, true);580 581	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);582}583 584int mlxsw_env_reset_module(struct net_device *netdev,585			   struct mlxsw_core *mlxsw_core, u8 slot_index,586			   u8 module, u32 *flags)587{588	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);589	struct mlxsw_env_module_info *module_info;590	u32 req = *flags;591	int err;592 593	if (!(req & ETH_RESET_PHY) &&594	    !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT)))595		return 0;596 597	if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {598		netdev_err(netdev, "Cannot reset module on an inactive line card\n");599		return -EIO;600	}601 602	mutex_lock(&mlxsw_env->line_cards_lock);603 604	err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);605	if (err) {606		netdev_err(netdev, "Reset module is not supported on port module type\n");607		goto out;608	}609 610	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);611	if (module_info->num_ports_up) {612		netdev_err(netdev, "Cannot reset module when ports using it are administratively up\n");613		err = -EINVAL;614		goto out;615	}616 617	if (module_info->num_ports_mapped > 1 &&618	    !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) {619		netdev_err(netdev, "Cannot reset module without \"phy-shared\" flag when shared by multiple ports\n");620		err = -EINVAL;621		goto out;622	}623 624	err = mlxsw_env_module_reset(mlxsw_core, slot_index, module);625	if (err) {626		netdev_err(netdev, "Failed to reset module\n");627		goto out;628	}629 630	*flags &= ~(ETH_RESET_PHY | (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT));631 632out:633	mutex_unlock(&mlxsw_env->line_cards_lock);634	return err;635}636EXPORT_SYMBOL(mlxsw_env_reset_module);637 638int639mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,640				u8 module,641				struct ethtool_module_power_mode_params *params,642				struct netlink_ext_ack *extack)643{644	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);645	struct mlxsw_env_module_info *module_info;646	char mcion_pl[MLXSW_REG_MCION_LEN];647	u32 status_bits;648	int err = 0;649 650	mutex_lock(&mlxsw_env->line_cards_lock);651 652	err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);653	if (err) {654		NL_SET_ERR_MSG_MOD(extack, "Power mode is not supported on port module type");655		goto out;656	}657 658	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);659	params->policy = module_info->power_mode_policy;660 661	/* Avoid accessing an inactive line card, as it will result in an error. */662	if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))663		goto out;664 665	mlxsw_reg_mcion_pack(mcion_pl, slot_index, module);666	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcion), mcion_pl);667	if (err) {668		NL_SET_ERR_MSG_MOD(extack, "Failed to retrieve module's power mode");669		goto out;670	}671 672	status_bits = mlxsw_reg_mcion_module_status_bits_get(mcion_pl);673	if (!(status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_PRESENT_MASK))674		goto out;675 676	if (status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_LOW_POWER_MASK)677		params->mode = ETHTOOL_MODULE_POWER_MODE_LOW;678	else679		params->mode = ETHTOOL_MODULE_POWER_MODE_HIGH;680 681out:682	mutex_unlock(&mlxsw_env->line_cards_lock);683	return err;684}685EXPORT_SYMBOL(mlxsw_env_get_module_power_mode);686 687static int mlxsw_env_module_enable_set(struct mlxsw_core *mlxsw_core,688				       u8 slot_index, u8 module, bool enable)689{690	enum mlxsw_reg_pmaos_admin_status admin_status;691	char pmaos_pl[MLXSW_REG_PMAOS_LEN];692 693	mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, module);694	admin_status = enable ? MLXSW_REG_PMAOS_ADMIN_STATUS_ENABLED :695				MLXSW_REG_PMAOS_ADMIN_STATUS_DISABLED;696	mlxsw_reg_pmaos_admin_status_set(pmaos_pl, admin_status);697	mlxsw_reg_pmaos_ase_set(pmaos_pl, true);698 699	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);700}701 702static int mlxsw_env_module_low_power_set(struct mlxsw_core *mlxsw_core,703					  u8 slot_index, u8 module,704					  bool low_power)705{706	u16 eeprom_override_mask, eeprom_override;707	char pmmp_pl[MLXSW_REG_PMMP_LEN];708 709	mlxsw_reg_pmmp_pack(pmmp_pl, slot_index, module);710	mlxsw_reg_pmmp_sticky_set(pmmp_pl, true);711	/* Mask all the bits except low power mode. */712	eeprom_override_mask = ~MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK;713	mlxsw_reg_pmmp_eeprom_override_mask_set(pmmp_pl, eeprom_override_mask);714	eeprom_override = low_power ? MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK :715				      0;716	mlxsw_reg_pmmp_eeprom_override_set(pmmp_pl, eeprom_override);717 718	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmmp), pmmp_pl);719}720 721static int __mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core,722					     u8 slot_index, u8 module,723					     bool low_power,724					     struct netlink_ext_ack *extack)725{726	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);727	int err;728 729	/* Avoid accessing an inactive line card, as it will result in an error.730	 * Cached configuration will be applied by mlxsw_env_got_active() when731	 * line card becomes active.732	 */733	if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))734		return 0;735 736	err = mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, false);737	if (err) {738		NL_SET_ERR_MSG_MOD(extack, "Failed to disable module");739		return err;740	}741 742	err = mlxsw_env_module_low_power_set(mlxsw_core, slot_index, module,743					     low_power);744	if (err) {745		NL_SET_ERR_MSG_MOD(extack, "Failed to set module's power mode");746		goto err_module_low_power_set;747	}748 749	err = mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, true);750	if (err) {751		NL_SET_ERR_MSG_MOD(extack, "Failed to enable module");752		goto err_module_enable_set;753	}754 755	return 0;756 757err_module_enable_set:758	mlxsw_env_module_low_power_set(mlxsw_core, slot_index, module,759				       !low_power);760err_module_low_power_set:761	mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, true);762	return err;763}764 765static int766mlxsw_env_set_module_power_mode_apply(struct mlxsw_core *mlxsw_core,767				      u8 slot_index, u8 module,768				      enum ethtool_module_power_mode_policy policy,769				      struct netlink_ext_ack *extack)770{771	struct mlxsw_env_module_info *module_info;772	bool low_power;773	int err = 0;774 775	err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);776	if (err) {777		NL_SET_ERR_MSG_MOD(extack,778				   "Power mode set is not supported on port module type");779		goto out;780	}781 782	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);783	if (module_info->power_mode_policy == policy)784		goto out;785 786	/* If any ports are up, we are already in high power mode. */787	if (module_info->num_ports_up)788		goto out_set_policy;789 790	low_power = policy == ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO;791	err = __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module,792						low_power, extack);793	if (err)794		goto out;795 796out_set_policy:797	module_info->power_mode_policy = policy;798out:799	return err;800}801 802int803mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,804				u8 module,805				enum ethtool_module_power_mode_policy policy,806				struct netlink_ext_ack *extack)807{808	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);809	int err;810 811	if (policy != ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH &&812	    policy != ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO) {813		NL_SET_ERR_MSG_MOD(extack, "Unsupported power mode policy");814		return -EOPNOTSUPP;815	}816 817	mutex_lock(&mlxsw_env->line_cards_lock);818	err = mlxsw_env_set_module_power_mode_apply(mlxsw_core, slot_index,819						    module, policy, extack);820	mutex_unlock(&mlxsw_env->line_cards_lock);821 822	return err;823}824EXPORT_SYMBOL(mlxsw_env_set_module_power_mode);825 826static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core,827					    u8 slot_index, u8 module,828					    bool *p_has_temp_sensor)829{830	char mtbr_pl[MLXSW_REG_MTBR_LEN];831	u16 temp;832	int err;833 834	mlxsw_reg_mtbr_pack(mtbr_pl, slot_index,835			    MLXSW_REG_MTBR_BASE_MODULE_INDEX + module);836	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtbr), mtbr_pl);837	if (err)838		return err;839 840	mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);841 842	switch (temp) {843	case MLXSW_REG_MTBR_BAD_SENS_INFO:844	case MLXSW_REG_MTBR_NO_CONN:845	case MLXSW_REG_MTBR_NO_TEMP_SENS:846	case MLXSW_REG_MTBR_INDEX_NA:847		*p_has_temp_sensor = false;848		break;849	default:850		*p_has_temp_sensor = temp ? true : false;851	}852	return 0;853}854 855static int856mlxsw_env_temp_event_set(struct mlxsw_core *mlxsw_core, u8 slot_index,857			 u16 sensor_index, bool enable)858{859	char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0};860	enum mlxsw_reg_mtmp_tee tee;861	int err, threshold_hi;862 863	mlxsw_reg_mtmp_slot_index_set(mtmp_pl, slot_index);864	mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, sensor_index);865	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);866	if (err)867		return err;868 869	if (enable) {870		err = mlxsw_env_module_temp_thresholds_get(mlxsw_core,871							   slot_index,872							   sensor_index -873							   MLXSW_REG_MTMP_MODULE_INDEX_MIN,874							   SFP_TEMP_HIGH_WARN,875							   &threshold_hi);876		/* In case it is not possible to query the module's threshold,877		 * use the default value.878		 */879		if (err)880			threshold_hi = MLXSW_REG_MTMP_THRESH_HI;881		else882			/* mlxsw_env_module_temp_thresholds_get() multiplies883			 * Celsius degrees by 1000 whereas MTMP expects884			 * temperature in 0.125 Celsius degrees units.885			 * Convert threshold_hi to correct units.886			 */887			threshold_hi = threshold_hi / 1000 * 8;888 889		mlxsw_reg_mtmp_temperature_threshold_hi_set(mtmp_pl, threshold_hi);890		mlxsw_reg_mtmp_temperature_threshold_lo_set(mtmp_pl, threshold_hi -891							    MLXSW_REG_MTMP_HYSTERESIS_TEMP);892	}893	tee = enable ? MLXSW_REG_MTMP_TEE_GENERATE_EVENT : MLXSW_REG_MTMP_TEE_NO_EVENT;894	mlxsw_reg_mtmp_tee_set(mtmp_pl, tee);895	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);896}897 898static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core,899					      u8 slot_index)900{901	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);902	int i, err, sensor_index;903	bool has_temp_sensor;904 905	for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {906		err = mlxsw_env_module_has_temp_sensor(mlxsw_core, slot_index,907						       i, &has_temp_sensor);908		if (err)909			return err;910 911		if (!has_temp_sensor)912			continue;913 914		sensor_index = i + MLXSW_REG_MTMP_MODULE_INDEX_MIN;915		err = mlxsw_env_temp_event_set(mlxsw_core, slot_index,916					       sensor_index, true);917		if (err)918			return err;919	}920 921	return 0;922}923 924struct mlxsw_env_module_temp_warn_event {925	struct mlxsw_env *mlxsw_env;926	char mtwe_pl[MLXSW_REG_MTWE_LEN];927	struct work_struct work;928};929 930static void mlxsw_env_mtwe_event_work(struct work_struct *work)931{932	struct mlxsw_env_module_temp_warn_event *event;933	struct mlxsw_env_module_info *module_info;934	struct mlxsw_env *mlxsw_env;935	int i, sensor_warning;936	bool is_overheat;937 938	event = container_of(work, struct mlxsw_env_module_temp_warn_event,939			     work);940	mlxsw_env = event->mlxsw_env;941 942	for (i = 0; i < mlxsw_env->max_module_count; i++) {943		/* 64-127 of sensor_index are mapped to the port modules944		 * sequentially (module 0 is mapped to sensor_index 64,945		 * module 1 to sensor_index 65 and so on)946		 */947		sensor_warning =948			mlxsw_reg_mtwe_sensor_warning_get(event->mtwe_pl,949							  i + MLXSW_REG_MTMP_MODULE_INDEX_MIN);950		mutex_lock(&mlxsw_env->line_cards_lock);951		/* MTWE only supports main board. */952		module_info = mlxsw_env_module_info_get(mlxsw_env->core, 0, i);953		is_overheat = module_info->is_overheat;954 955		if ((is_overheat && sensor_warning) ||956		    (!is_overheat && !sensor_warning)) {957			/* Current state is "warning" and MTWE still reports958			 * warning OR current state in "no warning" and MTWE959			 * does not report warning.960			 */961			mutex_unlock(&mlxsw_env->line_cards_lock);962			continue;963		} else if (is_overheat && !sensor_warning) {964			/* MTWE reports "no warning", turn is_overheat off.965			 */966			module_info->is_overheat = false;967			mutex_unlock(&mlxsw_env->line_cards_lock);968		} else {969			/* Current state is "no warning" and MTWE reports970			 * "warning", increase the counter and turn is_overheat971			 * on.972			 */973			module_info->is_overheat = true;974			module_info->module_overheat_counter++;975			mutex_unlock(&mlxsw_env->line_cards_lock);976		}977	}978 979	kfree(event);980}981 982static void983mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info *reg, char *mtwe_pl,984			     void *priv)985{986	struct mlxsw_env_module_temp_warn_event *event;987	struct mlxsw_env *mlxsw_env = priv;988 989	event = kmalloc(sizeof(*event), GFP_ATOMIC);990	if (!event)991		return;992 993	event->mlxsw_env = mlxsw_env;994	memcpy(event->mtwe_pl, mtwe_pl, MLXSW_REG_MTWE_LEN);995	INIT_WORK(&event->work, mlxsw_env_mtwe_event_work);996	mlxsw_core_schedule_work(&event->work);997}998 999static const struct mlxsw_listener mlxsw_env_temp_warn_listener =1000	MLXSW_CORE_EVENTL(mlxsw_env_mtwe_listener_func, MTWE);1001 1002static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core)1003{1004	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1005 1006	return mlxsw_core_trap_register(mlxsw_core,1007					&mlxsw_env_temp_warn_listener,1008					mlxsw_env);1009}1010 1011static void mlxsw_env_temp_warn_event_unregister(struct mlxsw_env *mlxsw_env)1012{1013	mlxsw_core_trap_unregister(mlxsw_env->core,1014				   &mlxsw_env_temp_warn_listener, mlxsw_env);1015}1016 1017struct mlxsw_env_module_plug_unplug_event {1018	struct mlxsw_env *mlxsw_env;1019	u8 slot_index;1020	u8 module;1021	struct work_struct work;1022};1023 1024static void mlxsw_env_pmpe_event_work(struct work_struct *work)1025{1026	struct mlxsw_env_module_plug_unplug_event *event;1027	struct mlxsw_env_module_info *module_info;1028	struct mlxsw_env *mlxsw_env;1029	bool has_temp_sensor;1030	u16 sensor_index;1031	int err;1032 1033	event = container_of(work, struct mlxsw_env_module_plug_unplug_event,1034			     work);1035	mlxsw_env = event->mlxsw_env;1036 1037	mutex_lock(&mlxsw_env->line_cards_lock);1038	module_info = mlxsw_env_module_info_get(mlxsw_env->core,1039						event->slot_index,1040						event->module);1041	module_info->is_overheat = false;1042	mutex_unlock(&mlxsw_env->line_cards_lock);1043 1044	err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core,1045					       event->slot_index,1046					       event->module,1047					       &has_temp_sensor);1048	/* Do not disable events on modules without sensors or faulty sensors1049	 * because FW returns errors.1050	 */1051	if (err)1052		goto out;1053 1054	if (!has_temp_sensor)1055		goto out;1056 1057	sensor_index = event->module + MLXSW_REG_MTMP_MODULE_INDEX_MIN;1058	mlxsw_env_temp_event_set(mlxsw_env->core, event->slot_index,1059				 sensor_index, true);1060 1061out:1062	kfree(event);1063}1064 1065static void1066mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl,1067			     void *priv)1068{1069	u8 slot_index = mlxsw_reg_pmpe_slot_index_get(pmpe_pl);1070	struct mlxsw_env_module_plug_unplug_event *event;1071	enum mlxsw_reg_pmpe_module_status module_status;1072	u8 module = mlxsw_reg_pmpe_module_get(pmpe_pl);1073	struct mlxsw_env *mlxsw_env = priv;1074 1075	if (WARN_ON_ONCE(module >= mlxsw_env->max_module_count ||1076			 slot_index >= mlxsw_env->num_of_slots))1077		return;1078 1079	module_status = mlxsw_reg_pmpe_module_status_get(pmpe_pl);1080	if (module_status != MLXSW_REG_PMPE_MODULE_STATUS_PLUGGED_ENABLED)1081		return;1082 1083	event = kmalloc(sizeof(*event), GFP_ATOMIC);1084	if (!event)1085		return;1086 1087	event->mlxsw_env = mlxsw_env;1088	event->slot_index = slot_index;1089	event->module = module;1090	INIT_WORK(&event->work, mlxsw_env_pmpe_event_work);1091	mlxsw_core_schedule_work(&event->work);1092}1093 1094static const struct mlxsw_listener mlxsw_env_module_plug_listener =1095	MLXSW_CORE_EVENTL(mlxsw_env_pmpe_listener_func, PMPE);1096 1097static int1098mlxsw_env_module_plug_event_register(struct mlxsw_core *mlxsw_core)1099{1100	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1101 1102	return mlxsw_core_trap_register(mlxsw_core,1103					&mlxsw_env_module_plug_listener,1104					mlxsw_env);1105}1106 1107static void1108mlxsw_env_module_plug_event_unregister(struct mlxsw_env *mlxsw_env)1109{1110	mlxsw_core_trap_unregister(mlxsw_env->core,1111				   &mlxsw_env_module_plug_listener,1112				   mlxsw_env);1113}1114 1115static int1116mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core,1117					 u8 slot_index)1118{1119	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1120	int i, err;1121 1122	for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {1123		char pmaos_pl[MLXSW_REG_PMAOS_LEN];1124 1125		mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, i);1126		mlxsw_reg_pmaos_e_set(pmaos_pl,1127				      MLXSW_REG_PMAOS_E_GENERATE_EVENT);1128		mlxsw_reg_pmaos_ee_set(pmaos_pl, true);1129		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);1130		if (err)1131			return err;1132	}1133	return 0;1134}1135 1136int1137mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 slot_index,1138				      u8 module, u64 *p_counter)1139{1140	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1141	struct mlxsw_env_module_info *module_info;1142 1143	mutex_lock(&mlxsw_env->line_cards_lock);1144	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);1145	*p_counter = module_info->module_overheat_counter;1146	mutex_unlock(&mlxsw_env->line_cards_lock);1147 1148	return 0;1149}1150EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get);1151 1152void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 slot_index,1153			       u8 module)1154{1155	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1156	struct mlxsw_env_module_info *module_info;1157 1158	mutex_lock(&mlxsw_env->line_cards_lock);1159	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);1160	module_info->num_ports_mapped++;1161	mutex_unlock(&mlxsw_env->line_cards_lock);1162}1163EXPORT_SYMBOL(mlxsw_env_module_port_map);1164 1165void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 slot_index,1166				 u8 module)1167{1168	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1169	struct mlxsw_env_module_info *module_info;1170 1171	mutex_lock(&mlxsw_env->line_cards_lock);1172	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);1173	module_info->num_ports_mapped--;1174	mutex_unlock(&mlxsw_env->line_cards_lock);1175}1176EXPORT_SYMBOL(mlxsw_env_module_port_unmap);1177 1178int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 slot_index,1179			     u8 module)1180{1181	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1182	struct mlxsw_env_module_info *module_info;1183	int err = 0;1184 1185	mutex_lock(&mlxsw_env->line_cards_lock);1186 1187	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);1188	if (module_info->power_mode_policy !=1189	    ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)1190		goto out_inc;1191 1192	if (module_info->num_ports_up != 0)1193		goto out_inc;1194 1195	/* Transition to high power mode following first port using the module1196	 * being put administratively up.1197	 */1198	err = __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module,1199						false, NULL);1200	if (err)1201		goto out_unlock;1202 1203out_inc:1204	module_info->num_ports_up++;1205out_unlock:1206	mutex_unlock(&mlxsw_env->line_cards_lock);1207	return err;1208}1209EXPORT_SYMBOL(mlxsw_env_module_port_up);1210 1211void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 slot_index,1212				u8 module)1213{1214	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1215	struct mlxsw_env_module_info *module_info;1216 1217	mutex_lock(&mlxsw_env->line_cards_lock);1218 1219	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);1220	module_info->num_ports_up--;1221 1222	if (module_info->power_mode_policy !=1223	    ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)1224		goto out_unlock;1225 1226	if (module_info->num_ports_up != 0)1227		goto out_unlock;1228 1229	/* Transition to low power mode following last port using the module1230	 * being put administratively down.1231	 */1232	__mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module, true,1233					  NULL);1234 1235out_unlock:1236	mutex_unlock(&mlxsw_env->line_cards_lock);1237}1238EXPORT_SYMBOL(mlxsw_env_module_port_down);1239 1240static int mlxsw_env_line_cards_alloc(struct mlxsw_env *env)1241{1242	struct mlxsw_env_module_info *module_info;1243	int i, j;1244 1245	for (i = 0; i < env->num_of_slots; i++) {1246		env->line_cards[i] = kzalloc(struct_size(env->line_cards[i],1247							 module_info,1248							 env->max_module_count),1249							 GFP_KERNEL);1250		if (!env->line_cards[i])1251			goto kzalloc_err;1252 1253		/* Firmware defaults to high power mode policy where modules1254		 * are transitioned to high power mode following plug-in.1255		 */1256		for (j = 0; j < env->max_module_count; j++) {1257			module_info = &env->line_cards[i]->module_info[j];1258			module_info->power_mode_policy =1259					ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH;1260		}1261	}1262 1263	return 0;1264 1265kzalloc_err:1266	for (i--; i >= 0; i--)1267		kfree(env->line_cards[i]);1268	return -ENOMEM;1269}1270 1271static void mlxsw_env_line_cards_free(struct mlxsw_env *env)1272{1273	int i = env->num_of_slots;1274 1275	for (i--; i >= 0; i--)1276		kfree(env->line_cards[i]);1277}1278 1279static int1280mlxsw_env_module_event_enable(struct mlxsw_env *mlxsw_env, u8 slot_index)1281{1282	int err;1283 1284	err = mlxsw_env_module_oper_state_event_enable(mlxsw_env->core,1285						       slot_index);1286	if (err)1287		return err;1288 1289	err = mlxsw_env_module_temp_event_enable(mlxsw_env->core, slot_index);1290	if (err)1291		return err;1292 1293	return 0;1294}1295 1296static void1297mlxsw_env_module_event_disable(struct mlxsw_env *mlxsw_env, u8 slot_index)1298{1299}1300 1301static int1302mlxsw_env_module_type_set(struct mlxsw_core *mlxsw_core, u8 slot_index)1303{1304	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);1305	int i;1306 1307	for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {1308		struct mlxsw_env_module_info *module_info;1309		char pmtm_pl[MLXSW_REG_PMTM_LEN];1310		int err;1311 1312		mlxsw_reg_pmtm_pack(pmtm_pl, slot_index, i);1313		err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtm), pmtm_pl);1314		if (err)1315			return err;1316 1317		module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index,1318							i);1319		module_info->type = mlxsw_reg_pmtm_module_type_get(pmtm_pl);1320	}1321 1322	return 0;1323}1324 1325static void1326mlxsw_env_linecard_modules_power_mode_apply(struct mlxsw_core *mlxsw_core,1327					    struct mlxsw_env *env,1328					    u8 slot_index)1329{1330	int i;1331 1332	for (i = 0; i < env->line_cards[slot_index]->module_count; i++) {1333		enum ethtool_module_power_mode_policy policy;1334		struct mlxsw_env_module_info *module_info;1335		struct netlink_ext_ack extack;1336		int err;1337 1338		module_info = &env->line_cards[slot_index]->module_info[i];1339		policy = module_info->power_mode_policy;1340		err = mlxsw_env_set_module_power_mode_apply(mlxsw_core,1341							    slot_index, i,1342							    policy, &extack);1343		if (err)1344			dev_err(env->bus_info->dev, "%s\n", extack._msg);1345	}1346}1347 1348static void1349mlxsw_env_got_active(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv)1350{1351	struct mlxsw_env *mlxsw_env = priv;1352	char mgpir_pl[MLXSW_REG_MGPIR_LEN];1353	int err;1354 1355	mutex_lock(&mlxsw_env->line_cards_lock);1356	if (__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))1357		goto out_unlock;1358 1359	mlxsw_reg_mgpir_pack(mgpir_pl, slot_index);1360	err = mlxsw_reg_query(mlxsw_env->core, MLXSW_REG(mgpir), mgpir_pl);1361	if (err)1362		goto out_unlock;1363 1364	mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,1365			       &mlxsw_env->line_cards[slot_index]->module_count,1366			       NULL);1367 1368	err = mlxsw_env_module_event_enable(mlxsw_env, slot_index);1369	if (err) {1370		dev_err(mlxsw_env->bus_info->dev, "Failed to enable port module events for line card in slot %d\n",1371			slot_index);1372		goto err_mlxsw_env_module_event_enable;1373	}1374	err = mlxsw_env_module_type_set(mlxsw_env->core, slot_index);1375	if (err) {1376		dev_err(mlxsw_env->bus_info->dev, "Failed to set modules' type for line card in slot %d\n",1377			slot_index);1378		goto err_type_set;1379	}1380 1381	mlxsw_env->line_cards[slot_index]->active = true;1382	/* Apply power mode policy. */1383	mlxsw_env_linecard_modules_power_mode_apply(mlxsw_core, mlxsw_env,1384						    slot_index);1385	mutex_unlock(&mlxsw_env->line_cards_lock);1386 1387	return;1388 1389err_type_set:1390	mlxsw_env_module_event_disable(mlxsw_env, slot_index);1391err_mlxsw_env_module_event_enable:1392out_unlock:1393	mutex_unlock(&mlxsw_env->line_cards_lock);1394}1395 1396static void1397mlxsw_env_got_inactive(struct mlxsw_core *mlxsw_core, u8 slot_index,1398		       void *priv)1399{1400	struct mlxsw_env *mlxsw_env = priv;1401 1402	mutex_lock(&mlxsw_env->line_cards_lock);1403	if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))1404		goto out_unlock;1405	mlxsw_env->line_cards[slot_index]->active = false;1406	mlxsw_env_module_event_disable(mlxsw_env, slot_index);1407	mlxsw_env->line_cards[slot_index]->module_count = 0;1408out_unlock:1409	mutex_unlock(&mlxsw_env->line_cards_lock);1410}1411 1412static struct mlxsw_linecards_event_ops mlxsw_env_event_ops = {1413	.got_active = mlxsw_env_got_active,1414	.got_inactive = mlxsw_env_got_inactive,1415};1416 1417static void mlxsw_env_max_module_eeprom_len_query(struct mlxsw_env *mlxsw_env)1418{1419	char mcam_pl[MLXSW_REG_MCAM_LEN];1420	bool mcia_128b_supported = false;1421	int err;1422 1423	mlxsw_reg_mcam_pack(mcam_pl,1424			    MLXSW_REG_MCAM_FEATURE_GROUP_ENHANCED_FEATURES);1425	err = mlxsw_reg_query(mlxsw_env->core, MLXSW_REG(mcam), mcam_pl);1426	if (!err)1427		mlxsw_reg_mcam_unpack(mcam_pl, MLXSW_REG_MCAM_MCIA_128B,1428				      &mcia_128b_supported);1429 1430	mlxsw_env->max_eeprom_len = mcia_128b_supported ? 128 : 48;1431}1432 1433int mlxsw_env_init(struct mlxsw_core *mlxsw_core,1434		   const struct mlxsw_bus_info *bus_info,1435		   struct mlxsw_env **p_env)1436{1437	u8 module_count, num_of_slots, max_module_count;1438	char mgpir_pl[MLXSW_REG_MGPIR_LEN];1439	struct mlxsw_env *env;1440	int err;1441 1442	mlxsw_reg_mgpir_pack(mgpir_pl, 0);1443	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);1444	if (err)1445		return err;1446 1447	mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &module_count,1448			       &num_of_slots);1449	/* If the system is modular, get the maximum number of modules per-slot.1450	 * Otherwise, get the maximum number of modules on the main board.1451	 */1452	max_module_count = num_of_slots ?1453			   mlxsw_reg_mgpir_max_modules_per_slot_get(mgpir_pl) :1454			   module_count;1455 1456	env = kzalloc(struct_size(env, line_cards, num_of_slots + 1),1457		      GFP_KERNEL);1458	if (!env)1459		return -ENOMEM;1460 1461	env->core = mlxsw_core;1462	env->bus_info = bus_info;1463	env->num_of_slots = num_of_slots + 1;1464	env->max_module_count = max_module_count;1465	err = mlxsw_env_line_cards_alloc(env);1466	if (err)1467		goto err_mlxsw_env_line_cards_alloc;1468 1469	mutex_init(&env->line_cards_lock);1470	*p_env = env;1471 1472	err = mlxsw_linecards_event_ops_register(env->core,1473						 &mlxsw_env_event_ops, env);1474	if (err)1475		goto err_linecards_event_ops_register;1476 1477	err = mlxsw_env_temp_warn_event_register(mlxsw_core);1478	if (err)1479		goto err_temp_warn_event_register;1480 1481	err = mlxsw_env_module_plug_event_register(mlxsw_core);1482	if (err)1483		goto err_module_plug_event_register;1484 1485	/* Set 'module_count' only for main board. Actual count for line card1486	 * is to be set after line card is activated.1487	 */1488	env->line_cards[0]->module_count = num_of_slots ? 0 : module_count;1489	/* Enable events only for main board. Line card events are to be1490	 * configured only after line card is activated. Before that, access to1491	 * modules on line cards is not allowed.1492	 */1493	err = mlxsw_env_module_event_enable(env, 0);1494	if (err)1495		goto err_mlxsw_env_module_event_enable;1496 1497	err = mlxsw_env_module_type_set(mlxsw_core, 0);1498	if (err)1499		goto err_type_set;1500 1501	mlxsw_env_max_module_eeprom_len_query(env);1502	env->line_cards[0]->active = true;1503 1504	return 0;1505 1506err_type_set:1507	mlxsw_env_module_event_disable(env, 0);1508err_mlxsw_env_module_event_enable:1509	mlxsw_env_module_plug_event_unregister(env);1510err_module_plug_event_register:1511	mlxsw_env_temp_warn_event_unregister(env);1512err_temp_warn_event_register:1513	mlxsw_linecards_event_ops_unregister(env->core,1514					     &mlxsw_env_event_ops, env);1515err_linecards_event_ops_register:1516	mutex_destroy(&env->line_cards_lock);1517	mlxsw_env_line_cards_free(env);1518err_mlxsw_env_line_cards_alloc:1519	kfree(env);1520	return err;1521}1522 1523void mlxsw_env_fini(struct mlxsw_env *env)1524{1525	env->line_cards[0]->active = false;1526	mlxsw_env_module_event_disable(env, 0);1527	mlxsw_env_module_plug_event_unregister(env);1528	/* Make sure there is no more event work scheduled. */1529	mlxsw_core_flush_owq();1530	mlxsw_env_temp_warn_event_unregister(env);1531	mlxsw_linecards_event_ops_unregister(env->core,1532					     &mlxsw_env_event_ops, env);1533	mutex_destroy(&env->line_cards_lock);1534	mlxsw_env_line_cards_free(env);1535	kfree(env);1536}1537