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 
15 struct 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 
24 struct mlxsw_env_line_card {
25 	u8 module_count;
26 	bool active;
27 	struct mlxsw_env_module_info module_info[];
28 };
29 
30 struct 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[];
38 };
39 
__mlxsw_env_linecard_is_active(struct mlxsw_env * mlxsw_env,u8 slot_index)40 static 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 
mlxsw_env_linecard_is_active(struct mlxsw_env * mlxsw_env,u8 slot_index)46 static 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 
58 static struct
mlxsw_env_module_info_get(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module)59 mlxsw_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 
__mlxsw_env_validate_module_type(struct mlxsw_core * core,u8 slot_index,u8 module)67 static 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 
mlxsw_env_validate_module_type(struct mlxsw_core * core,u8 slot_index,u8 module)89 static 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 
102 static int
mlxsw_env_validate_cable_ident(struct mlxsw_core * core,u8 slot_index,int id,bool * qsfp,bool * cmis)103 mlxsw_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 
145 static int
mlxsw_env_query_module_eeprom(struct mlxsw_core * mlxsw_core,u8 slot_index,int module,u16 offset,u16 size,void * data,bool qsfp,unsigned int * p_read_size)146 mlxsw_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 
207 int
mlxsw_env_module_temp_thresholds_get(struct mlxsw_core * core,u8 slot_index,int module,int off,int * temp)208 mlxsw_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 
mlxsw_env_get_module_info(struct net_device * netdev,struct mlxsw_core * mlxsw_core,u8 slot_index,int module,struct ethtool_modinfo * modinfo)289 int 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 }
382 EXPORT_SYMBOL(mlxsw_env_get_module_info);
383 
mlxsw_env_get_module_eeprom(struct net_device * netdev,struct mlxsw_core * mlxsw_core,u8 slot_index,int module,struct ethtool_eeprom * ee,u8 * data)384 int 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 }
427 EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);
428 
mlxsw_env_mcia_status_process(const char * mcia_pl,struct netlink_ext_ack * extack)429 static 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 
458 int
mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module,const struct ethtool_module_eeprom * page,struct netlink_ext_ack * extack)459 mlxsw_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 }
514 EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page);
515 
mlxsw_env_module_reset(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module)516 static int mlxsw_env_module_reset(struct mlxsw_core *mlxsw_core, u8 slot_index,
517 				  u8 module)
518 {
519 	char pmaos_pl[MLXSW_REG_PMAOS_LEN];
520 
521 	mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, module);
522 	mlxsw_reg_pmaos_rst_set(pmaos_pl, true);
523 
524 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
525 }
526 
mlxsw_env_reset_module(struct net_device * netdev,struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module,u32 * flags)527 int mlxsw_env_reset_module(struct net_device *netdev,
528 			   struct mlxsw_core *mlxsw_core, u8 slot_index,
529 			   u8 module, u32 *flags)
530 {
531 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
532 	struct mlxsw_env_module_info *module_info;
533 	u32 req = *flags;
534 	int err;
535 
536 	if (!(req & ETH_RESET_PHY) &&
537 	    !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT)))
538 		return 0;
539 
540 	if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {
541 		netdev_err(netdev, "Cannot reset module on an inactive line card\n");
542 		return -EIO;
543 	}
544 
545 	mutex_lock(&mlxsw_env->line_cards_lock);
546 
547 	err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
548 	if (err) {
549 		netdev_err(netdev, "Reset module is not supported on port module type\n");
550 		goto out;
551 	}
552 
553 	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
554 	if (module_info->num_ports_up) {
555 		netdev_err(netdev, "Cannot reset module when ports using it are administratively up\n");
556 		err = -EINVAL;
557 		goto out;
558 	}
559 
560 	if (module_info->num_ports_mapped > 1 &&
561 	    !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) {
562 		netdev_err(netdev, "Cannot reset module without \"phy-shared\" flag when shared by multiple ports\n");
563 		err = -EINVAL;
564 		goto out;
565 	}
566 
567 	err = mlxsw_env_module_reset(mlxsw_core, slot_index, module);
568 	if (err) {
569 		netdev_err(netdev, "Failed to reset module\n");
570 		goto out;
571 	}
572 
573 	*flags &= ~(ETH_RESET_PHY | (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT));
574 
575 out:
576 	mutex_unlock(&mlxsw_env->line_cards_lock);
577 	return err;
578 }
579 EXPORT_SYMBOL(mlxsw_env_reset_module);
580 
581 int
mlxsw_env_get_module_power_mode(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module,struct ethtool_module_power_mode_params * params,struct netlink_ext_ack * extack)582 mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
583 				u8 module,
584 				struct ethtool_module_power_mode_params *params,
585 				struct netlink_ext_ack *extack)
586 {
587 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
588 	struct mlxsw_env_module_info *module_info;
589 	char mcion_pl[MLXSW_REG_MCION_LEN];
590 	u32 status_bits;
591 	int err = 0;
592 
593 	mutex_lock(&mlxsw_env->line_cards_lock);
594 
595 	err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
596 	if (err) {
597 		NL_SET_ERR_MSG_MOD(extack, "Power mode is not supported on port module type");
598 		goto out;
599 	}
600 
601 	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
602 	params->policy = module_info->power_mode_policy;
603 
604 	/* Avoid accessing an inactive line card, as it will result in an error. */
605 	if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
606 		goto out;
607 
608 	mlxsw_reg_mcion_pack(mcion_pl, slot_index, module);
609 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcion), mcion_pl);
610 	if (err) {
611 		NL_SET_ERR_MSG_MOD(extack, "Failed to retrieve module's power mode");
612 		goto out;
613 	}
614 
615 	status_bits = mlxsw_reg_mcion_module_status_bits_get(mcion_pl);
616 	if (!(status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_PRESENT_MASK))
617 		goto out;
618 
619 	if (status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_LOW_POWER_MASK)
620 		params->mode = ETHTOOL_MODULE_POWER_MODE_LOW;
621 	else
622 		params->mode = ETHTOOL_MODULE_POWER_MODE_HIGH;
623 
624 out:
625 	mutex_unlock(&mlxsw_env->line_cards_lock);
626 	return err;
627 }
628 EXPORT_SYMBOL(mlxsw_env_get_module_power_mode);
629 
mlxsw_env_module_enable_set(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module,bool enable)630 static int mlxsw_env_module_enable_set(struct mlxsw_core *mlxsw_core,
631 				       u8 slot_index, u8 module, bool enable)
632 {
633 	enum mlxsw_reg_pmaos_admin_status admin_status;
634 	char pmaos_pl[MLXSW_REG_PMAOS_LEN];
635 
636 	mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, module);
637 	admin_status = enable ? MLXSW_REG_PMAOS_ADMIN_STATUS_ENABLED :
638 				MLXSW_REG_PMAOS_ADMIN_STATUS_DISABLED;
639 	mlxsw_reg_pmaos_admin_status_set(pmaos_pl, admin_status);
640 	mlxsw_reg_pmaos_ase_set(pmaos_pl, true);
641 
642 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
643 }
644 
mlxsw_env_module_low_power_set(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module,bool low_power)645 static int mlxsw_env_module_low_power_set(struct mlxsw_core *mlxsw_core,
646 					  u8 slot_index, u8 module,
647 					  bool low_power)
648 {
649 	u16 eeprom_override_mask, eeprom_override;
650 	char pmmp_pl[MLXSW_REG_PMMP_LEN];
651 
652 	mlxsw_reg_pmmp_pack(pmmp_pl, slot_index, module);
653 	mlxsw_reg_pmmp_sticky_set(pmmp_pl, true);
654 	/* Mask all the bits except low power mode. */
655 	eeprom_override_mask = ~MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK;
656 	mlxsw_reg_pmmp_eeprom_override_mask_set(pmmp_pl, eeprom_override_mask);
657 	eeprom_override = low_power ? MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK :
658 				      0;
659 	mlxsw_reg_pmmp_eeprom_override_set(pmmp_pl, eeprom_override);
660 
661 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmmp), pmmp_pl);
662 }
663 
__mlxsw_env_set_module_power_mode(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module,bool low_power,struct netlink_ext_ack * extack)664 static int __mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core,
665 					     u8 slot_index, u8 module,
666 					     bool low_power,
667 					     struct netlink_ext_ack *extack)
668 {
669 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
670 	int err;
671 
672 	/* Avoid accessing an inactive line card, as it will result in an error.
673 	 * Cached configuration will be applied by mlxsw_env_got_active() when
674 	 * line card becomes active.
675 	 */
676 	if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
677 		return 0;
678 
679 	err = mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, false);
680 	if (err) {
681 		NL_SET_ERR_MSG_MOD(extack, "Failed to disable module");
682 		return err;
683 	}
684 
685 	err = mlxsw_env_module_low_power_set(mlxsw_core, slot_index, module,
686 					     low_power);
687 	if (err) {
688 		NL_SET_ERR_MSG_MOD(extack, "Failed to set module's power mode");
689 		goto err_module_low_power_set;
690 	}
691 
692 	err = mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, true);
693 	if (err) {
694 		NL_SET_ERR_MSG_MOD(extack, "Failed to enable module");
695 		goto err_module_enable_set;
696 	}
697 
698 	return 0;
699 
700 err_module_enable_set:
701 	mlxsw_env_module_low_power_set(mlxsw_core, slot_index, module,
702 				       !low_power);
703 err_module_low_power_set:
704 	mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, true);
705 	return err;
706 }
707 
708 static int
mlxsw_env_set_module_power_mode_apply(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module,enum ethtool_module_power_mode_policy policy,struct netlink_ext_ack * extack)709 mlxsw_env_set_module_power_mode_apply(struct mlxsw_core *mlxsw_core,
710 				      u8 slot_index, u8 module,
711 				      enum ethtool_module_power_mode_policy policy,
712 				      struct netlink_ext_ack *extack)
713 {
714 	struct mlxsw_env_module_info *module_info;
715 	bool low_power;
716 	int err = 0;
717 
718 	err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
719 	if (err) {
720 		NL_SET_ERR_MSG_MOD(extack,
721 				   "Power mode set is not supported on port module type");
722 		goto out;
723 	}
724 
725 	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
726 	if (module_info->power_mode_policy == policy)
727 		goto out;
728 
729 	/* If any ports are up, we are already in high power mode. */
730 	if (module_info->num_ports_up)
731 		goto out_set_policy;
732 
733 	low_power = policy == ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO;
734 	err = __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module,
735 						low_power, extack);
736 	if (err)
737 		goto out;
738 
739 out_set_policy:
740 	module_info->power_mode_policy = policy;
741 out:
742 	return err;
743 }
744 
745 int
mlxsw_env_set_module_power_mode(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module,enum ethtool_module_power_mode_policy policy,struct netlink_ext_ack * extack)746 mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
747 				u8 module,
748 				enum ethtool_module_power_mode_policy policy,
749 				struct netlink_ext_ack *extack)
750 {
751 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
752 	int err;
753 
754 	if (policy != ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH &&
755 	    policy != ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO) {
756 		NL_SET_ERR_MSG_MOD(extack, "Unsupported power mode policy");
757 		return -EOPNOTSUPP;
758 	}
759 
760 	mutex_lock(&mlxsw_env->line_cards_lock);
761 	err = mlxsw_env_set_module_power_mode_apply(mlxsw_core, slot_index,
762 						    module, policy, extack);
763 	mutex_unlock(&mlxsw_env->line_cards_lock);
764 
765 	return err;
766 }
767 EXPORT_SYMBOL(mlxsw_env_set_module_power_mode);
768 
mlxsw_env_module_has_temp_sensor(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module,bool * p_has_temp_sensor)769 static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core,
770 					    u8 slot_index, u8 module,
771 					    bool *p_has_temp_sensor)
772 {
773 	char mtbr_pl[MLXSW_REG_MTBR_LEN];
774 	u16 temp;
775 	int err;
776 
777 	mlxsw_reg_mtbr_pack(mtbr_pl, slot_index,
778 			    MLXSW_REG_MTBR_BASE_MODULE_INDEX + module, 1);
779 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtbr), mtbr_pl);
780 	if (err)
781 		return err;
782 
783 	mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
784 
785 	switch (temp) {
786 	case MLXSW_REG_MTBR_BAD_SENS_INFO:
787 	case MLXSW_REG_MTBR_NO_CONN:
788 	case MLXSW_REG_MTBR_NO_TEMP_SENS:
789 	case MLXSW_REG_MTBR_INDEX_NA:
790 		*p_has_temp_sensor = false;
791 		break;
792 	default:
793 		*p_has_temp_sensor = temp ? true : false;
794 	}
795 	return 0;
796 }
797 
798 static int
mlxsw_env_temp_event_set(struct mlxsw_core * mlxsw_core,u8 slot_index,u16 sensor_index,bool enable)799 mlxsw_env_temp_event_set(struct mlxsw_core *mlxsw_core, u8 slot_index,
800 			 u16 sensor_index, bool enable)
801 {
802 	char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0};
803 	enum mlxsw_reg_mtmp_tee tee;
804 	int err, threshold_hi;
805 
806 	mlxsw_reg_mtmp_slot_index_set(mtmp_pl, slot_index);
807 	mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, sensor_index);
808 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
809 	if (err)
810 		return err;
811 
812 	if (enable) {
813 		err = mlxsw_env_module_temp_thresholds_get(mlxsw_core,
814 							   slot_index,
815 							   sensor_index -
816 							   MLXSW_REG_MTMP_MODULE_INDEX_MIN,
817 							   SFP_TEMP_HIGH_WARN,
818 							   &threshold_hi);
819 		/* In case it is not possible to query the module's threshold,
820 		 * use the default value.
821 		 */
822 		if (err)
823 			threshold_hi = MLXSW_REG_MTMP_THRESH_HI;
824 		else
825 			/* mlxsw_env_module_temp_thresholds_get() multiplies
826 			 * Celsius degrees by 1000 whereas MTMP expects
827 			 * temperature in 0.125 Celsius degrees units.
828 			 * Convert threshold_hi to correct units.
829 			 */
830 			threshold_hi = threshold_hi / 1000 * 8;
831 
832 		mlxsw_reg_mtmp_temperature_threshold_hi_set(mtmp_pl, threshold_hi);
833 		mlxsw_reg_mtmp_temperature_threshold_lo_set(mtmp_pl, threshold_hi -
834 							    MLXSW_REG_MTMP_HYSTERESIS_TEMP);
835 	}
836 	tee = enable ? MLXSW_REG_MTMP_TEE_GENERATE_EVENT : MLXSW_REG_MTMP_TEE_NO_EVENT;
837 	mlxsw_reg_mtmp_tee_set(mtmp_pl, tee);
838 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
839 }
840 
mlxsw_env_module_temp_event_enable(struct mlxsw_core * mlxsw_core,u8 slot_index)841 static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core,
842 					      u8 slot_index)
843 {
844 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
845 	int i, err, sensor_index;
846 	bool has_temp_sensor;
847 
848 	for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
849 		err = mlxsw_env_module_has_temp_sensor(mlxsw_core, slot_index,
850 						       i, &has_temp_sensor);
851 		if (err)
852 			return err;
853 
854 		if (!has_temp_sensor)
855 			continue;
856 
857 		sensor_index = i + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
858 		err = mlxsw_env_temp_event_set(mlxsw_core, slot_index,
859 					       sensor_index, true);
860 		if (err)
861 			return err;
862 	}
863 
864 	return 0;
865 }
866 
867 struct mlxsw_env_module_temp_warn_event {
868 	struct mlxsw_env *mlxsw_env;
869 	char mtwe_pl[MLXSW_REG_MTWE_LEN];
870 	struct work_struct work;
871 };
872 
mlxsw_env_mtwe_event_work(struct work_struct * work)873 static void mlxsw_env_mtwe_event_work(struct work_struct *work)
874 {
875 	struct mlxsw_env_module_temp_warn_event *event;
876 	struct mlxsw_env_module_info *module_info;
877 	struct mlxsw_env *mlxsw_env;
878 	int i, sensor_warning;
879 	bool is_overheat;
880 
881 	event = container_of(work, struct mlxsw_env_module_temp_warn_event,
882 			     work);
883 	mlxsw_env = event->mlxsw_env;
884 
885 	for (i = 0; i < mlxsw_env->max_module_count; i++) {
886 		/* 64-127 of sensor_index are mapped to the port modules
887 		 * sequentially (module 0 is mapped to sensor_index 64,
888 		 * module 1 to sensor_index 65 and so on)
889 		 */
890 		sensor_warning =
891 			mlxsw_reg_mtwe_sensor_warning_get(event->mtwe_pl,
892 							  i + MLXSW_REG_MTMP_MODULE_INDEX_MIN);
893 		mutex_lock(&mlxsw_env->line_cards_lock);
894 		/* MTWE only supports main board. */
895 		module_info = mlxsw_env_module_info_get(mlxsw_env->core, 0, i);
896 		is_overheat = module_info->is_overheat;
897 
898 		if ((is_overheat && sensor_warning) ||
899 		    (!is_overheat && !sensor_warning)) {
900 			/* Current state is "warning" and MTWE still reports
901 			 * warning OR current state in "no warning" and MTWE
902 			 * does not report warning.
903 			 */
904 			mutex_unlock(&mlxsw_env->line_cards_lock);
905 			continue;
906 		} else if (is_overheat && !sensor_warning) {
907 			/* MTWE reports "no warning", turn is_overheat off.
908 			 */
909 			module_info->is_overheat = false;
910 			mutex_unlock(&mlxsw_env->line_cards_lock);
911 		} else {
912 			/* Current state is "no warning" and MTWE reports
913 			 * "warning", increase the counter and turn is_overheat
914 			 * on.
915 			 */
916 			module_info->is_overheat = true;
917 			module_info->module_overheat_counter++;
918 			mutex_unlock(&mlxsw_env->line_cards_lock);
919 		}
920 	}
921 
922 	kfree(event);
923 }
924 
925 static void
mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info * reg,char * mtwe_pl,void * priv)926 mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info *reg, char *mtwe_pl,
927 			     void *priv)
928 {
929 	struct mlxsw_env_module_temp_warn_event *event;
930 	struct mlxsw_env *mlxsw_env = priv;
931 
932 	event = kmalloc(sizeof(*event), GFP_ATOMIC);
933 	if (!event)
934 		return;
935 
936 	event->mlxsw_env = mlxsw_env;
937 	memcpy(event->mtwe_pl, mtwe_pl, MLXSW_REG_MTWE_LEN);
938 	INIT_WORK(&event->work, mlxsw_env_mtwe_event_work);
939 	mlxsw_core_schedule_work(&event->work);
940 }
941 
942 static const struct mlxsw_listener mlxsw_env_temp_warn_listener =
943 	MLXSW_CORE_EVENTL(mlxsw_env_mtwe_listener_func, MTWE);
944 
mlxsw_env_temp_warn_event_register(struct mlxsw_core * mlxsw_core)945 static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core)
946 {
947 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
948 
949 	return mlxsw_core_trap_register(mlxsw_core,
950 					&mlxsw_env_temp_warn_listener,
951 					mlxsw_env);
952 }
953 
mlxsw_env_temp_warn_event_unregister(struct mlxsw_env * mlxsw_env)954 static void mlxsw_env_temp_warn_event_unregister(struct mlxsw_env *mlxsw_env)
955 {
956 	mlxsw_core_trap_unregister(mlxsw_env->core,
957 				   &mlxsw_env_temp_warn_listener, mlxsw_env);
958 }
959 
960 struct mlxsw_env_module_plug_unplug_event {
961 	struct mlxsw_env *mlxsw_env;
962 	u8 slot_index;
963 	u8 module;
964 	struct work_struct work;
965 };
966 
mlxsw_env_pmpe_event_work(struct work_struct * work)967 static void mlxsw_env_pmpe_event_work(struct work_struct *work)
968 {
969 	struct mlxsw_env_module_plug_unplug_event *event;
970 	struct mlxsw_env_module_info *module_info;
971 	struct mlxsw_env *mlxsw_env;
972 	bool has_temp_sensor;
973 	u16 sensor_index;
974 	int err;
975 
976 	event = container_of(work, struct mlxsw_env_module_plug_unplug_event,
977 			     work);
978 	mlxsw_env = event->mlxsw_env;
979 
980 	mutex_lock(&mlxsw_env->line_cards_lock);
981 	module_info = mlxsw_env_module_info_get(mlxsw_env->core,
982 						event->slot_index,
983 						event->module);
984 	module_info->is_overheat = false;
985 	mutex_unlock(&mlxsw_env->line_cards_lock);
986 
987 	err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core,
988 					       event->slot_index,
989 					       event->module,
990 					       &has_temp_sensor);
991 	/* Do not disable events on modules without sensors or faulty sensors
992 	 * because FW returns errors.
993 	 */
994 	if (err)
995 		goto out;
996 
997 	if (!has_temp_sensor)
998 		goto out;
999 
1000 	sensor_index = event->module + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
1001 	mlxsw_env_temp_event_set(mlxsw_env->core, event->slot_index,
1002 				 sensor_index, true);
1003 
1004 out:
1005 	kfree(event);
1006 }
1007 
1008 static void
mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info * reg,char * pmpe_pl,void * priv)1009 mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl,
1010 			     void *priv)
1011 {
1012 	u8 slot_index = mlxsw_reg_pmpe_slot_index_get(pmpe_pl);
1013 	struct mlxsw_env_module_plug_unplug_event *event;
1014 	enum mlxsw_reg_pmpe_module_status module_status;
1015 	u8 module = mlxsw_reg_pmpe_module_get(pmpe_pl);
1016 	struct mlxsw_env *mlxsw_env = priv;
1017 
1018 	if (WARN_ON_ONCE(module >= mlxsw_env->max_module_count ||
1019 			 slot_index >= mlxsw_env->num_of_slots))
1020 		return;
1021 
1022 	module_status = mlxsw_reg_pmpe_module_status_get(pmpe_pl);
1023 	if (module_status != MLXSW_REG_PMPE_MODULE_STATUS_PLUGGED_ENABLED)
1024 		return;
1025 
1026 	event = kmalloc(sizeof(*event), GFP_ATOMIC);
1027 	if (!event)
1028 		return;
1029 
1030 	event->mlxsw_env = mlxsw_env;
1031 	event->slot_index = slot_index;
1032 	event->module = module;
1033 	INIT_WORK(&event->work, mlxsw_env_pmpe_event_work);
1034 	mlxsw_core_schedule_work(&event->work);
1035 }
1036 
1037 static const struct mlxsw_listener mlxsw_env_module_plug_listener =
1038 	MLXSW_CORE_EVENTL(mlxsw_env_pmpe_listener_func, PMPE);
1039 
1040 static int
mlxsw_env_module_plug_event_register(struct mlxsw_core * mlxsw_core)1041 mlxsw_env_module_plug_event_register(struct mlxsw_core *mlxsw_core)
1042 {
1043 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1044 
1045 	return mlxsw_core_trap_register(mlxsw_core,
1046 					&mlxsw_env_module_plug_listener,
1047 					mlxsw_env);
1048 }
1049 
1050 static void
mlxsw_env_module_plug_event_unregister(struct mlxsw_env * mlxsw_env)1051 mlxsw_env_module_plug_event_unregister(struct mlxsw_env *mlxsw_env)
1052 {
1053 	mlxsw_core_trap_unregister(mlxsw_env->core,
1054 				   &mlxsw_env_module_plug_listener,
1055 				   mlxsw_env);
1056 }
1057 
1058 static int
mlxsw_env_module_oper_state_event_enable(struct mlxsw_core * mlxsw_core,u8 slot_index)1059 mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core,
1060 					 u8 slot_index)
1061 {
1062 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1063 	int i, err;
1064 
1065 	for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
1066 		char pmaos_pl[MLXSW_REG_PMAOS_LEN];
1067 
1068 		mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, i);
1069 		mlxsw_reg_pmaos_e_set(pmaos_pl,
1070 				      MLXSW_REG_PMAOS_E_GENERATE_EVENT);
1071 		mlxsw_reg_pmaos_ee_set(pmaos_pl, true);
1072 		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
1073 		if (err)
1074 			return err;
1075 	}
1076 	return 0;
1077 }
1078 
1079 int
mlxsw_env_module_overheat_counter_get(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module,u64 * p_counter)1080 mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 slot_index,
1081 				      u8 module, u64 *p_counter)
1082 {
1083 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1084 	struct mlxsw_env_module_info *module_info;
1085 
1086 	mutex_lock(&mlxsw_env->line_cards_lock);
1087 	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1088 	*p_counter = module_info->module_overheat_counter;
1089 	mutex_unlock(&mlxsw_env->line_cards_lock);
1090 
1091 	return 0;
1092 }
1093 EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get);
1094 
mlxsw_env_module_port_map(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module)1095 void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 slot_index,
1096 			       u8 module)
1097 {
1098 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1099 	struct mlxsw_env_module_info *module_info;
1100 
1101 	mutex_lock(&mlxsw_env->line_cards_lock);
1102 	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1103 	module_info->num_ports_mapped++;
1104 	mutex_unlock(&mlxsw_env->line_cards_lock);
1105 }
1106 EXPORT_SYMBOL(mlxsw_env_module_port_map);
1107 
mlxsw_env_module_port_unmap(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module)1108 void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 slot_index,
1109 				 u8 module)
1110 {
1111 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1112 	struct mlxsw_env_module_info *module_info;
1113 
1114 	mutex_lock(&mlxsw_env->line_cards_lock);
1115 	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1116 	module_info->num_ports_mapped--;
1117 	mutex_unlock(&mlxsw_env->line_cards_lock);
1118 }
1119 EXPORT_SYMBOL(mlxsw_env_module_port_unmap);
1120 
mlxsw_env_module_port_up(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module)1121 int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 slot_index,
1122 			     u8 module)
1123 {
1124 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1125 	struct mlxsw_env_module_info *module_info;
1126 	int err = 0;
1127 
1128 	mutex_lock(&mlxsw_env->line_cards_lock);
1129 
1130 	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1131 	if (module_info->power_mode_policy !=
1132 	    ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
1133 		goto out_inc;
1134 
1135 	if (module_info->num_ports_up != 0)
1136 		goto out_inc;
1137 
1138 	/* Transition to high power mode following first port using the module
1139 	 * being put administratively up.
1140 	 */
1141 	err = __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module,
1142 						false, NULL);
1143 	if (err)
1144 		goto out_unlock;
1145 
1146 out_inc:
1147 	module_info->num_ports_up++;
1148 out_unlock:
1149 	mutex_unlock(&mlxsw_env->line_cards_lock);
1150 	return err;
1151 }
1152 EXPORT_SYMBOL(mlxsw_env_module_port_up);
1153 
mlxsw_env_module_port_down(struct mlxsw_core * mlxsw_core,u8 slot_index,u8 module)1154 void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 slot_index,
1155 				u8 module)
1156 {
1157 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1158 	struct mlxsw_env_module_info *module_info;
1159 
1160 	mutex_lock(&mlxsw_env->line_cards_lock);
1161 
1162 	module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1163 	module_info->num_ports_up--;
1164 
1165 	if (module_info->power_mode_policy !=
1166 	    ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
1167 		goto out_unlock;
1168 
1169 	if (module_info->num_ports_up != 0)
1170 		goto out_unlock;
1171 
1172 	/* Transition to low power mode following last port using the module
1173 	 * being put administratively down.
1174 	 */
1175 	__mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module, true,
1176 					  NULL);
1177 
1178 out_unlock:
1179 	mutex_unlock(&mlxsw_env->line_cards_lock);
1180 }
1181 EXPORT_SYMBOL(mlxsw_env_module_port_down);
1182 
mlxsw_env_line_cards_alloc(struct mlxsw_env * env)1183 static int mlxsw_env_line_cards_alloc(struct mlxsw_env *env)
1184 {
1185 	struct mlxsw_env_module_info *module_info;
1186 	int i, j;
1187 
1188 	for (i = 0; i < env->num_of_slots; i++) {
1189 		env->line_cards[i] = kzalloc(struct_size(env->line_cards[i],
1190 							 module_info,
1191 							 env->max_module_count),
1192 							 GFP_KERNEL);
1193 		if (!env->line_cards[i])
1194 			goto kzalloc_err;
1195 
1196 		/* Firmware defaults to high power mode policy where modules
1197 		 * are transitioned to high power mode following plug-in.
1198 		 */
1199 		for (j = 0; j < env->max_module_count; j++) {
1200 			module_info = &env->line_cards[i]->module_info[j];
1201 			module_info->power_mode_policy =
1202 					ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH;
1203 		}
1204 	}
1205 
1206 	return 0;
1207 
1208 kzalloc_err:
1209 	for (i--; i >= 0; i--)
1210 		kfree(env->line_cards[i]);
1211 	return -ENOMEM;
1212 }
1213 
mlxsw_env_line_cards_free(struct mlxsw_env * env)1214 static void mlxsw_env_line_cards_free(struct mlxsw_env *env)
1215 {
1216 	int i = env->num_of_slots;
1217 
1218 	for (i--; i >= 0; i--)
1219 		kfree(env->line_cards[i]);
1220 }
1221 
1222 static int
mlxsw_env_module_event_enable(struct mlxsw_env * mlxsw_env,u8 slot_index)1223 mlxsw_env_module_event_enable(struct mlxsw_env *mlxsw_env, u8 slot_index)
1224 {
1225 	int err;
1226 
1227 	err = mlxsw_env_module_oper_state_event_enable(mlxsw_env->core,
1228 						       slot_index);
1229 	if (err)
1230 		return err;
1231 
1232 	err = mlxsw_env_module_temp_event_enable(mlxsw_env->core, slot_index);
1233 	if (err)
1234 		return err;
1235 
1236 	return 0;
1237 }
1238 
1239 static void
mlxsw_env_module_event_disable(struct mlxsw_env * mlxsw_env,u8 slot_index)1240 mlxsw_env_module_event_disable(struct mlxsw_env *mlxsw_env, u8 slot_index)
1241 {
1242 }
1243 
1244 static int
mlxsw_env_module_type_set(struct mlxsw_core * mlxsw_core,u8 slot_index)1245 mlxsw_env_module_type_set(struct mlxsw_core *mlxsw_core, u8 slot_index)
1246 {
1247 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1248 	int i;
1249 
1250 	for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
1251 		struct mlxsw_env_module_info *module_info;
1252 		char pmtm_pl[MLXSW_REG_PMTM_LEN];
1253 		int err;
1254 
1255 		mlxsw_reg_pmtm_pack(pmtm_pl, slot_index, i);
1256 		err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtm), pmtm_pl);
1257 		if (err)
1258 			return err;
1259 
1260 		module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index,
1261 							i);
1262 		module_info->type = mlxsw_reg_pmtm_module_type_get(pmtm_pl);
1263 	}
1264 
1265 	return 0;
1266 }
1267 
1268 static void
mlxsw_env_linecard_modules_power_mode_apply(struct mlxsw_core * mlxsw_core,struct mlxsw_env * env,u8 slot_index)1269 mlxsw_env_linecard_modules_power_mode_apply(struct mlxsw_core *mlxsw_core,
1270 					    struct mlxsw_env *env,
1271 					    u8 slot_index)
1272 {
1273 	int i;
1274 
1275 	for (i = 0; i < env->line_cards[slot_index]->module_count; i++) {
1276 		enum ethtool_module_power_mode_policy policy;
1277 		struct mlxsw_env_module_info *module_info;
1278 		struct netlink_ext_ack extack;
1279 		int err;
1280 
1281 		module_info = &env->line_cards[slot_index]->module_info[i];
1282 		policy = module_info->power_mode_policy;
1283 		err = mlxsw_env_set_module_power_mode_apply(mlxsw_core,
1284 							    slot_index, i,
1285 							    policy, &extack);
1286 		if (err)
1287 			dev_err(env->bus_info->dev, "%s\n", extack._msg);
1288 	}
1289 }
1290 
1291 static void
mlxsw_env_got_active(struct mlxsw_core * mlxsw_core,u8 slot_index,void * priv)1292 mlxsw_env_got_active(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv)
1293 {
1294 	struct mlxsw_env *mlxsw_env = priv;
1295 	char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1296 	int err;
1297 
1298 	mutex_lock(&mlxsw_env->line_cards_lock);
1299 	if (__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
1300 		goto out_unlock;
1301 
1302 	mlxsw_reg_mgpir_pack(mgpir_pl, slot_index);
1303 	err = mlxsw_reg_query(mlxsw_env->core, MLXSW_REG(mgpir), mgpir_pl);
1304 	if (err)
1305 		goto out_unlock;
1306 
1307 	mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,
1308 			       &mlxsw_env->line_cards[slot_index]->module_count,
1309 			       NULL);
1310 
1311 	err = mlxsw_env_module_event_enable(mlxsw_env, slot_index);
1312 	if (err) {
1313 		dev_err(mlxsw_env->bus_info->dev, "Failed to enable port module events for line card in slot %d\n",
1314 			slot_index);
1315 		goto err_mlxsw_env_module_event_enable;
1316 	}
1317 	err = mlxsw_env_module_type_set(mlxsw_env->core, slot_index);
1318 	if (err) {
1319 		dev_err(mlxsw_env->bus_info->dev, "Failed to set modules' type for line card in slot %d\n",
1320 			slot_index);
1321 		goto err_type_set;
1322 	}
1323 
1324 	mlxsw_env->line_cards[slot_index]->active = true;
1325 	/* Apply power mode policy. */
1326 	mlxsw_env_linecard_modules_power_mode_apply(mlxsw_core, mlxsw_env,
1327 						    slot_index);
1328 	mutex_unlock(&mlxsw_env->line_cards_lock);
1329 
1330 	return;
1331 
1332 err_type_set:
1333 	mlxsw_env_module_event_disable(mlxsw_env, slot_index);
1334 err_mlxsw_env_module_event_enable:
1335 out_unlock:
1336 	mutex_unlock(&mlxsw_env->line_cards_lock);
1337 }
1338 
1339 static void
mlxsw_env_got_inactive(struct mlxsw_core * mlxsw_core,u8 slot_index,void * priv)1340 mlxsw_env_got_inactive(struct mlxsw_core *mlxsw_core, u8 slot_index,
1341 		       void *priv)
1342 {
1343 	struct mlxsw_env *mlxsw_env = priv;
1344 
1345 	mutex_lock(&mlxsw_env->line_cards_lock);
1346 	if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
1347 		goto out_unlock;
1348 	mlxsw_env->line_cards[slot_index]->active = false;
1349 	mlxsw_env_module_event_disable(mlxsw_env, slot_index);
1350 	mlxsw_env->line_cards[slot_index]->module_count = 0;
1351 out_unlock:
1352 	mutex_unlock(&mlxsw_env->line_cards_lock);
1353 }
1354 
1355 static struct mlxsw_linecards_event_ops mlxsw_env_event_ops = {
1356 	.got_active = mlxsw_env_got_active,
1357 	.got_inactive = mlxsw_env_got_inactive,
1358 };
1359 
mlxsw_env_max_module_eeprom_len_query(struct mlxsw_env * mlxsw_env)1360 static int mlxsw_env_max_module_eeprom_len_query(struct mlxsw_env *mlxsw_env)
1361 {
1362 	char mcam_pl[MLXSW_REG_MCAM_LEN];
1363 	bool mcia_128b_supported;
1364 	int err;
1365 
1366 	mlxsw_reg_mcam_pack(mcam_pl,
1367 			    MLXSW_REG_MCAM_FEATURE_GROUP_ENHANCED_FEATURES);
1368 	err = mlxsw_reg_query(mlxsw_env->core, MLXSW_REG(mcam), mcam_pl);
1369 	if (err)
1370 		return err;
1371 
1372 	mlxsw_reg_mcam_unpack(mcam_pl, MLXSW_REG_MCAM_MCIA_128B,
1373 			      &mcia_128b_supported);
1374 
1375 	mlxsw_env->max_eeprom_len = mcia_128b_supported ? 128 : 48;
1376 
1377 	return 0;
1378 }
1379 
mlxsw_env_init(struct mlxsw_core * mlxsw_core,const struct mlxsw_bus_info * bus_info,struct mlxsw_env ** p_env)1380 int mlxsw_env_init(struct mlxsw_core *mlxsw_core,
1381 		   const struct mlxsw_bus_info *bus_info,
1382 		   struct mlxsw_env **p_env)
1383 {
1384 	u8 module_count, num_of_slots, max_module_count;
1385 	char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1386 	struct mlxsw_env *env;
1387 	int err;
1388 
1389 	mlxsw_reg_mgpir_pack(mgpir_pl, 0);
1390 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
1391 	if (err)
1392 		return err;
1393 
1394 	mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &module_count,
1395 			       &num_of_slots);
1396 	/* If the system is modular, get the maximum number of modules per-slot.
1397 	 * Otherwise, get the maximum number of modules on the main board.
1398 	 */
1399 	max_module_count = num_of_slots ?
1400 			   mlxsw_reg_mgpir_max_modules_per_slot_get(mgpir_pl) :
1401 			   module_count;
1402 
1403 	env = kzalloc(struct_size(env, line_cards, num_of_slots + 1),
1404 		      GFP_KERNEL);
1405 	if (!env)
1406 		return -ENOMEM;
1407 
1408 	env->core = mlxsw_core;
1409 	env->bus_info = bus_info;
1410 	env->num_of_slots = num_of_slots + 1;
1411 	env->max_module_count = max_module_count;
1412 	err = mlxsw_env_line_cards_alloc(env);
1413 	if (err)
1414 		goto err_mlxsw_env_line_cards_alloc;
1415 
1416 	mutex_init(&env->line_cards_lock);
1417 	*p_env = env;
1418 
1419 	err = mlxsw_linecards_event_ops_register(env->core,
1420 						 &mlxsw_env_event_ops, env);
1421 	if (err)
1422 		goto err_linecards_event_ops_register;
1423 
1424 	err = mlxsw_env_temp_warn_event_register(mlxsw_core);
1425 	if (err)
1426 		goto err_temp_warn_event_register;
1427 
1428 	err = mlxsw_env_module_plug_event_register(mlxsw_core);
1429 	if (err)
1430 		goto err_module_plug_event_register;
1431 
1432 	/* Set 'module_count' only for main board. Actual count for line card
1433 	 * is to be set after line card is activated.
1434 	 */
1435 	env->line_cards[0]->module_count = num_of_slots ? 0 : module_count;
1436 	/* Enable events only for main board. Line card events are to be
1437 	 * configured only after line card is activated. Before that, access to
1438 	 * modules on line cards is not allowed.
1439 	 */
1440 	err = mlxsw_env_module_event_enable(env, 0);
1441 	if (err)
1442 		goto err_mlxsw_env_module_event_enable;
1443 
1444 	err = mlxsw_env_module_type_set(mlxsw_core, 0);
1445 	if (err)
1446 		goto err_type_set;
1447 
1448 	err = mlxsw_env_max_module_eeprom_len_query(env);
1449 	if (err)
1450 		goto err_eeprom_len_query;
1451 
1452 	env->line_cards[0]->active = true;
1453 
1454 	return 0;
1455 
1456 err_eeprom_len_query:
1457 err_type_set:
1458 	mlxsw_env_module_event_disable(env, 0);
1459 err_mlxsw_env_module_event_enable:
1460 	mlxsw_env_module_plug_event_unregister(env);
1461 err_module_plug_event_register:
1462 	mlxsw_env_temp_warn_event_unregister(env);
1463 err_temp_warn_event_register:
1464 	mlxsw_linecards_event_ops_unregister(env->core,
1465 					     &mlxsw_env_event_ops, env);
1466 err_linecards_event_ops_register:
1467 	mutex_destroy(&env->line_cards_lock);
1468 	mlxsw_env_line_cards_free(env);
1469 err_mlxsw_env_line_cards_alloc:
1470 	kfree(env);
1471 	return err;
1472 }
1473 
mlxsw_env_fini(struct mlxsw_env * env)1474 void mlxsw_env_fini(struct mlxsw_env *env)
1475 {
1476 	env->line_cards[0]->active = false;
1477 	mlxsw_env_module_event_disable(env, 0);
1478 	mlxsw_env_module_plug_event_unregister(env);
1479 	/* Make sure there is no more event work scheduled. */
1480 	mlxsw_core_flush_owq();
1481 	mlxsw_env_temp_warn_event_unregister(env);
1482 	mlxsw_linecards_event_ops_unregister(env->core,
1483 					     &mlxsw_env_event_ops, env);
1484 	mutex_destroy(&env->line_cards_lock);
1485 	mlxsw_env_line_cards_free(env);
1486 	kfree(env);
1487 }
1488