Linux Audio

Check our new training course

Linux BSP upgrade and security maintenance

Need help to get security updates for your Linux BSP?
Loading...
v6.13.7
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/* 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 struct
  59mlxsw_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 int
 103mlxsw_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 int
 146mlxsw_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 offset
 169			 * starts at 128. Please refer to "QSFP+ Memory Map"
 170			 * figure in SFF-8436 specification and to "CMIS Module
 171			 * Memory Map" figure in CMIS specification for
 172			 * 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 offset
 180			 * starts at 0 and I2C high address is used. Please refer
 181			 * to "Memory Organization" figure in SFF-8472
 182			 * 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
 207int
 208mlxsw_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 03h
 245	 * (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 page
 261		 * 02h, otherwise in page 03h.
 262		 */
 263		if (cmis)
 264			page = MLXSW_REG_MCIA_TH_PAGE_CMIS_NUM;
 265		else
 266			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		else
 357			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 specific
 362		 * 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 flat
 366		 * memory only page 00h (0-255 bytes) can be read. Otherwise
 367		 * upper pages 01h and 02h can also be read. Upper pages 10h
 368		 * 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		else
 374			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
 458int
 459mlxsw_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
 516int
 517mlxsw_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
 638int
 639mlxsw_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	else
 679		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() when
 731	 * 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 int
 766mlxsw_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
 802int
 803mlxsw_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 int
 856mlxsw_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		else
 882			/* mlxsw_env_module_temp_thresholds_get() multiplies
 883			 * Celsius degrees by 1000 whereas MTMP expects
 884			 * 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 modules
 944		 * 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 reports
 958			 * warning OR current state in "no warning" and MTWE
 959			 * 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 reports
 970			 * "warning", increase the counter and turn is_overheat
 971			 * 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 void
 983mlxsw_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 sensors
1049	 * 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 void
1066mlxsw_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 int
1098mlxsw_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 void
1108mlxsw_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 int
1116mlxsw_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
1136int
1137mlxsw_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 module
1196	 * 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 module
1230	 * 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 modules
1254		 * 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 int
1280mlxsw_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 void
1297mlxsw_env_module_event_disable(struct mlxsw_env *mlxsw_env, u8 slot_index)
1298{
1299}
1300
1301static int
1302mlxsw_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 void
1326mlxsw_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 void
1349mlxsw_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 void
1397mlxsw_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 card
1486	 * 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 be
1490	 * configured only after line card is activated. Before that, access to
1491	 * 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}
v5.4
  1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
  2/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
  3
  4#include <linux/kernel.h>
  5#include <linux/err.h>
 
  6#include <linux/sfp.h>
 
  7
  8#include "core.h"
  9#include "core_env.h"
 10#include "item.h"
 11#include "reg.h"
 12
 13static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id,
 14					  bool *qsfp)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 15{
 16	char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
 17	char mcia_pl[MLXSW_REG_MCIA_LEN];
 
 18	u8 ident;
 19	int err;
 20
 21	mlxsw_reg_mcia_pack(mcia_pl, id, 0, MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1,
 
 
 
 
 
 22			    MLXSW_REG_MCIA_I2C_ADDR_LOW);
 23	err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
 24	if (err)
 25		return err;
 26	mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
 27	ident = eeprom_tmp[0];
 
 28	switch (ident) {
 29	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
 30		*qsfp = false;
 31		break;
 32	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP: /* fall-through */
 33	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
 34	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28: /* fall-through */
 
 
 35	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
 
 36		*qsfp = true;
 
 37		break;
 38	default:
 39		return -EINVAL;
 40	}
 41
 42	return 0;
 43}
 44
 45static int
 46mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, int module,
 47			      u16 offset, u16 size, void *data,
 48			      unsigned int *p_read_size)
 49{
 50	char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
 51	char mcia_pl[MLXSW_REG_MCIA_LEN];
 
 52	u16 i2c_addr;
 
 53	int status;
 54	int err;
 55
 56	size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
 57
 58	if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
 59	    offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
 60		/* Cross pages read, read until offset 256 in low page */
 61		size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
 62
 63	i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
 64	if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
 65		i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
 66		offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 67	}
 68
 69	mlxsw_reg_mcia_pack(mcia_pl, module, 0, 0, offset, size, i2c_addr);
 
 70
 71	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
 72	if (err)
 73		return err;
 74
 75	status = mlxsw_reg_mcia_status_get(mcia_pl);
 76	if (status)
 77		return -EIO;
 78
 79	mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
 80	memcpy(data, eeprom_tmp, size);
 81	*p_read_size = size;
 82
 83	return 0;
 84}
 85
 86int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
 87					 int off, int *temp)
 
 88{
 89	char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
 90	union {
 91		u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];
 92		u16 temp;
 93	} temp_thresh;
 94	char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
 95	char mtmp_pl[MLXSW_REG_MTMP_LEN];
 96	unsigned int module_temp;
 97	bool qsfp;
 
 98	int err;
 99
100	mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module,
101			    false, false);
 
102	err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);
103	if (err)
104		return err;
105	mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, NULL);
 
106	if (!module_temp) {
107		*temp = 0;
108		return 0;
109	}
110
 
 
 
 
 
 
 
 
111	/* Read Free Side Device Temperature Thresholds from page 03h
112	 * (MSB at lower byte address).
113	 * Bytes:
114	 * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);
115	 * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);
116	 * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);
117	 * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);
118	 */
119
120	/* Validate module identifier value. */
121	err = mlxsw_env_validate_cable_ident(core, module, &qsfp);
 
122	if (err)
123		return err;
124
125	if (qsfp)
126		mlxsw_reg_mcia_pack(mcia_pl, module, 0,
127				    MLXSW_REG_MCIA_TH_PAGE_NUM,
 
 
 
 
 
 
128				    MLXSW_REG_MCIA_TH_PAGE_OFF + off,
129				    MLXSW_REG_MCIA_TH_ITEM_SIZE,
130				    MLXSW_REG_MCIA_I2C_ADDR_LOW);
131	else
132		mlxsw_reg_mcia_pack(mcia_pl, module, 0,
133				    MLXSW_REG_MCIA_PAGE0_LO,
134				    off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
135				    MLXSW_REG_MCIA_I2C_ADDR_HIGH);
 
136
137	err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
138	if (err)
139		return err;
140
141	mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
142	memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);
143	*temp = temp_thresh.temp * 1000;
144
145	return 0;
146}
147
148int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module,
149			      struct ethtool_modinfo *modinfo)
 
150{
 
151	u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
152	u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
153	u8 module_rev_id, module_id, diag_mon;
154	unsigned int read_size;
155	int err;
156
157	err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 0, offset,
158					    module_info, &read_size);
 
 
 
 
 
 
 
 
 
 
 
 
 
159	if (err)
160		return err;
161
162	if (read_size < offset)
163		return -EIO;
164
165	module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
166	module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
167
168	switch (module_id) {
169	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
170		modinfo->type       = ETH_MODULE_SFF_8436;
171		modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
172		break;
173	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
174	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
175		if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
176		    module_rev_id >=
177		    MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
178			modinfo->type       = ETH_MODULE_SFF_8636;
179			modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
180		} else {
181			modinfo->type       = ETH_MODULE_SFF_8436;
182			modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
183		}
184		break;
185	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
186		/* Verify if transceiver provides diagnostic monitoring page */
187		err = mlxsw_env_query_module_eeprom(mlxsw_core, module,
188						    SFP_DIAGMON, 1, &diag_mon,
 
189						    &read_size);
190		if (err)
191			return err;
192
193		if (read_size < 1)
194			return -EIO;
195
196		modinfo->type       = ETH_MODULE_SFF_8472;
197		if (diag_mon)
198			modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
199		else
200			modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;
201		break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202	default:
203		return -EINVAL;
204	}
205
206	return 0;
207}
208EXPORT_SYMBOL(mlxsw_env_get_module_info);
209
210int mlxsw_env_get_module_eeprom(struct net_device *netdev,
211				struct mlxsw_core *mlxsw_core, int module,
212				struct ethtool_eeprom *ee, u8 *data)
 
213{
 
214	int offset = ee->offset;
215	unsigned int read_size;
 
216	int i = 0;
217	int err;
218
219	if (!ee->len)
220		return -EINVAL;
221
 
 
 
 
 
222	memset(data, 0, ee->len);
 
 
 
 
 
223
224	while (i < ee->len) {
225		err = mlxsw_env_query_module_eeprom(mlxsw_core, module, offset,
 
226						    ee->len - i, data + i,
227						    &read_size);
228		if (err) {
229			netdev_err(netdev, "Eeprom query failed\n");
230			return err;
231		}
232
233		i += read_size;
234		offset += read_size;
235	}
236
237	return 0;
238}
239EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);