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 
13 struct mlxsw_env_module_info {
14 	u64 module_overheat_counter;
15 	bool is_overheat;
16 };
17 
18 struct mlxsw_env {
19 	struct mlxsw_core *core;
20 	u8 module_count;
21 	spinlock_t module_info_lock; /* Protects 'module_info'. */
22 	struct mlxsw_env_module_info module_info[];
23 };
24 
25 static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id,
26 					  bool *qsfp, bool *cmis)
27 {
28 	char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
29 	char mcia_pl[MLXSW_REG_MCIA_LEN];
30 	u8 ident;
31 	int err;
32 
33 	mlxsw_reg_mcia_pack(mcia_pl, id, 0, MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1,
34 			    MLXSW_REG_MCIA_I2C_ADDR_LOW);
35 	err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
36 	if (err)
37 		return err;
38 	mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
39 	ident = eeprom_tmp[0];
40 	*cmis = false;
41 	switch (ident) {
42 	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
43 		*qsfp = false;
44 		break;
45 	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
46 	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
47 	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
48 		*qsfp = true;
49 		break;
50 	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
51 		*qsfp = true;
52 		*cmis = true;
53 		break;
54 	default:
55 		return -EINVAL;
56 	}
57 
58 	return 0;
59 }
60 
61 static int
62 mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, int module,
63 			      u16 offset, u16 size, void *data,
64 			      bool qsfp, unsigned int *p_read_size)
65 {
66 	char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
67 	char mcia_pl[MLXSW_REG_MCIA_LEN];
68 	u16 i2c_addr;
69 	u8 page = 0;
70 	int status;
71 	int err;
72 
73 	/* MCIA register accepts buffer size <= 48. Page of size 128 should be
74 	 * read by chunks of size 48, 48, 32. Align the size of the last chunk
75 	 * to avoid reading after the end of the page.
76 	 */
77 	size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
78 
79 	if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
80 	    offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
81 		/* Cross pages read, read until offset 256 in low page */
82 		size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
83 
84 	i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
85 	if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
86 		if (qsfp) {
87 			/* When reading upper pages 1, 2 and 3 the offset
88 			 * starts at 128. Please refer to "QSFP+ Memory Map"
89 			 * figure in SFF-8436 specification and to "CMIS Module
90 			 * Memory Map" figure in CMIS specification for
91 			 * graphical depiction.
92 			 */
93 			page = MLXSW_REG_MCIA_PAGE_GET(offset);
94 			offset -= MLXSW_REG_MCIA_EEPROM_UP_PAGE_LENGTH * page;
95 			if (offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
96 				size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
97 		} else {
98 			/* When reading upper pages 1, 2 and 3 the offset
99 			 * starts at 0 and I2C high address is used. Please refer
100 			 * refer to "Memory Organization" figure in SFF-8472
101 			 * specification for graphical depiction.
102 			 */
103 			i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
104 			offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
105 		}
106 	}
107 
108 	mlxsw_reg_mcia_pack(mcia_pl, module, 0, page, offset, size, i2c_addr);
109 
110 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
111 	if (err)
112 		return err;
113 
114 	status = mlxsw_reg_mcia_status_get(mcia_pl);
115 	if (status)
116 		return -EIO;
117 
118 	mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
119 	memcpy(data, eeprom_tmp, size);
120 	*p_read_size = size;
121 
122 	return 0;
123 }
124 
125 int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
126 					 int off, int *temp)
127 {
128 	char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
129 	union {
130 		u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];
131 		u16 temp;
132 	} temp_thresh;
133 	char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
134 	char mtmp_pl[MLXSW_REG_MTMP_LEN];
135 	unsigned int module_temp;
136 	bool qsfp, cmis;
137 	int page;
138 	int err;
139 
140 	mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module,
141 			    false, false);
142 	err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);
143 	if (err)
144 		return err;
145 	mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, NULL);
146 	if (!module_temp) {
147 		*temp = 0;
148 		return 0;
149 	}
150 
151 	/* Read Free Side Device Temperature Thresholds from page 03h
152 	 * (MSB at lower byte address).
153 	 * Bytes:
154 	 * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);
155 	 * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);
156 	 * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);
157 	 * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);
158 	 */
159 
160 	/* Validate module identifier value. */
161 	err = mlxsw_env_validate_cable_ident(core, module, &qsfp, &cmis);
162 	if (err)
163 		return err;
164 
165 	if (qsfp) {
166 		/* For QSFP/CMIS module-defined thresholds are located in page
167 		 * 02h, otherwise in page 03h.
168 		 */
169 		if (cmis)
170 			page = MLXSW_REG_MCIA_TH_PAGE_CMIS_NUM;
171 		else
172 			page = MLXSW_REG_MCIA_TH_PAGE_NUM;
173 		mlxsw_reg_mcia_pack(mcia_pl, module, 0, page,
174 				    MLXSW_REG_MCIA_TH_PAGE_OFF + off,
175 				    MLXSW_REG_MCIA_TH_ITEM_SIZE,
176 				    MLXSW_REG_MCIA_I2C_ADDR_LOW);
177 	} else {
178 		mlxsw_reg_mcia_pack(mcia_pl, module, 0,
179 				    MLXSW_REG_MCIA_PAGE0_LO,
180 				    off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
181 				    MLXSW_REG_MCIA_I2C_ADDR_HIGH);
182 	}
183 
184 	err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
185 	if (err)
186 		return err;
187 
188 	mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
189 	memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);
190 	*temp = temp_thresh.temp * 1000;
191 
192 	return 0;
193 }
194 
195 int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module,
196 			      struct ethtool_modinfo *modinfo)
197 {
198 	u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
199 	u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
200 	u8 module_rev_id, module_id, diag_mon;
201 	unsigned int read_size;
202 	int err;
203 
204 	err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 0, offset,
205 					    module_info, false, &read_size);
206 	if (err)
207 		return err;
208 
209 	if (read_size < offset)
210 		return -EIO;
211 
212 	module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
213 	module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
214 
215 	switch (module_id) {
216 	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
217 		modinfo->type       = ETH_MODULE_SFF_8436;
218 		modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
219 		break;
220 	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
221 	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
222 		if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
223 		    module_rev_id >=
224 		    MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
225 			modinfo->type       = ETH_MODULE_SFF_8636;
226 			modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
227 		} else {
228 			modinfo->type       = ETH_MODULE_SFF_8436;
229 			modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
230 		}
231 		break;
232 	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
233 		/* Verify if transceiver provides diagnostic monitoring page */
234 		err = mlxsw_env_query_module_eeprom(mlxsw_core, module,
235 						    SFP_DIAGMON, 1, &diag_mon,
236 						    false, &read_size);
237 		if (err)
238 			return err;
239 
240 		if (read_size < 1)
241 			return -EIO;
242 
243 		modinfo->type       = ETH_MODULE_SFF_8472;
244 		if (diag_mon)
245 			modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
246 		else
247 			modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;
248 		break;
249 	case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
250 		/* Use SFF_8636 as base type. ethtool should recognize specific
251 		 * type through the identifier value.
252 		 */
253 		modinfo->type       = ETH_MODULE_SFF_8636;
254 		/* Verify if module EEPROM is a flat memory. In case of flat
255 		 * memory only page 00h (0-255 bytes) can be read. Otherwise
256 		 * upper pages 01h and 02h can also be read. Upper pages 10h
257 		 * and 11h are currently not supported by the driver.
258 		 */
259 		if (module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_TYPE_ID] &
260 		    MLXSW_REG_MCIA_EEPROM_CMIS_FLAT_MEMORY)
261 			modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
262 		else
263 			modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
264 		break;
265 	default:
266 		return -EINVAL;
267 	}
268 
269 	return 0;
270 }
271 EXPORT_SYMBOL(mlxsw_env_get_module_info);
272 
273 int mlxsw_env_get_module_eeprom(struct net_device *netdev,
274 				struct mlxsw_core *mlxsw_core, int module,
275 				struct ethtool_eeprom *ee, u8 *data)
276 {
277 	int offset = ee->offset;
278 	unsigned int read_size;
279 	bool qsfp, cmis;
280 	int i = 0;
281 	int err;
282 
283 	if (!ee->len)
284 		return -EINVAL;
285 
286 	memset(data, 0, ee->len);
287 	/* Validate module identifier value. */
288 	err = mlxsw_env_validate_cable_ident(mlxsw_core, module, &qsfp, &cmis);
289 	if (err)
290 		return err;
291 
292 	while (i < ee->len) {
293 		err = mlxsw_env_query_module_eeprom(mlxsw_core, module, offset,
294 						    ee->len - i, data + i,
295 						    qsfp, &read_size);
296 		if (err) {
297 			netdev_err(netdev, "Eeprom query failed\n");
298 			return err;
299 		}
300 
301 		i += read_size;
302 		offset += read_size;
303 	}
304 
305 	return 0;
306 }
307 EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);
308 
309 static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core,
310 					    u8 module,
311 					    bool *p_has_temp_sensor)
312 {
313 	char mtbr_pl[MLXSW_REG_MTBR_LEN];
314 	u16 temp;
315 	int err;
316 
317 	mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
318 			    1);
319 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtbr), mtbr_pl);
320 	if (err)
321 		return err;
322 
323 	mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
324 
325 	switch (temp) {
326 	case MLXSW_REG_MTBR_BAD_SENS_INFO:
327 	case MLXSW_REG_MTBR_NO_CONN:
328 	case MLXSW_REG_MTBR_NO_TEMP_SENS:
329 	case MLXSW_REG_MTBR_INDEX_NA:
330 		*p_has_temp_sensor = false;
331 		break;
332 	default:
333 		*p_has_temp_sensor = temp ? true : false;
334 	}
335 	return 0;
336 }
337 
338 static int mlxsw_env_temp_event_set(struct mlxsw_core *mlxsw_core,
339 				    u16 sensor_index, bool enable)
340 {
341 	char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0};
342 	enum mlxsw_reg_mtmp_tee tee;
343 	int err, threshold_hi;
344 
345 	mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, sensor_index);
346 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
347 	if (err)
348 		return err;
349 
350 	if (enable) {
351 		err = mlxsw_env_module_temp_thresholds_get(mlxsw_core,
352 							   sensor_index -
353 							   MLXSW_REG_MTMP_MODULE_INDEX_MIN,
354 							   SFP_TEMP_HIGH_WARN,
355 							   &threshold_hi);
356 		/* In case it is not possible to query the module's threshold,
357 		 * use the default value.
358 		 */
359 		if (err)
360 			threshold_hi = MLXSW_REG_MTMP_THRESH_HI;
361 		else
362 			/* mlxsw_env_module_temp_thresholds_get() multiplies
363 			 * Celsius degrees by 1000 whereas MTMP expects
364 			 * temperature in 0.125 Celsius degrees units.
365 			 * Convert threshold_hi to correct units.
366 			 */
367 			threshold_hi = threshold_hi / 1000 * 8;
368 
369 		mlxsw_reg_mtmp_temperature_threshold_hi_set(mtmp_pl, threshold_hi);
370 		mlxsw_reg_mtmp_temperature_threshold_lo_set(mtmp_pl, threshold_hi -
371 							    MLXSW_REG_MTMP_HYSTERESIS_TEMP);
372 	}
373 	tee = enable ? MLXSW_REG_MTMP_TEE_GENERATE_EVENT : MLXSW_REG_MTMP_TEE_NO_EVENT;
374 	mlxsw_reg_mtmp_tee_set(mtmp_pl, tee);
375 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
376 }
377 
378 static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core,
379 					      u8 module_count)
380 {
381 	int i, err, sensor_index;
382 	bool has_temp_sensor;
383 
384 	for (i = 0; i < module_count; i++) {
385 		err = mlxsw_env_module_has_temp_sensor(mlxsw_core, i,
386 						       &has_temp_sensor);
387 		if (err)
388 			return err;
389 
390 		if (!has_temp_sensor)
391 			continue;
392 
393 		sensor_index = i + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
394 		err = mlxsw_env_temp_event_set(mlxsw_core, sensor_index, true);
395 		if (err)
396 			return err;
397 	}
398 
399 	return 0;
400 }
401 
402 static void mlxsw_env_mtwe_event_func(const struct mlxsw_reg_info *reg,
403 				      char *mtwe_pl, void *priv)
404 {
405 	struct mlxsw_env *mlxsw_env = priv;
406 	int i, sensor_warning;
407 	bool is_overheat;
408 
409 	for (i = 0; i < mlxsw_env->module_count; i++) {
410 		/* 64-127 of sensor_index are mapped to the port modules
411 		 * sequentially (module 0 is mapped to sensor_index 64,
412 		 * module 1 to sensor_index 65 and so on)
413 		 */
414 		sensor_warning =
415 			mlxsw_reg_mtwe_sensor_warning_get(mtwe_pl,
416 							  i + MLXSW_REG_MTMP_MODULE_INDEX_MIN);
417 		spin_lock(&mlxsw_env->module_info_lock);
418 		is_overheat =
419 			mlxsw_env->module_info[i].is_overheat;
420 
421 		if ((is_overheat && sensor_warning) ||
422 		    (!is_overheat && !sensor_warning)) {
423 			/* Current state is "warning" and MTWE still reports
424 			 * warning OR current state in "no warning" and MTWE
425 			 * does not report warning.
426 			 */
427 			spin_unlock(&mlxsw_env->module_info_lock);
428 			continue;
429 		} else if (is_overheat && !sensor_warning) {
430 			/* MTWE reports "no warning", turn is_overheat off.
431 			 */
432 			mlxsw_env->module_info[i].is_overheat = false;
433 			spin_unlock(&mlxsw_env->module_info_lock);
434 		} else {
435 			/* Current state is "no warning" and MTWE reports
436 			 * "warning", increase the counter and turn is_overheat
437 			 * on.
438 			 */
439 			mlxsw_env->module_info[i].is_overheat = true;
440 			mlxsw_env->module_info[i].module_overheat_counter++;
441 			spin_unlock(&mlxsw_env->module_info_lock);
442 		}
443 	}
444 }
445 
446 static const struct mlxsw_listener mlxsw_env_temp_warn_listener =
447 	MLXSW_EVENTL(mlxsw_env_mtwe_event_func, MTWE, MTWE);
448 
449 static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core)
450 {
451 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
452 
453 	if (!mlxsw_core_temp_warn_enabled(mlxsw_core))
454 		return 0;
455 
456 	return mlxsw_core_trap_register(mlxsw_core,
457 					&mlxsw_env_temp_warn_listener,
458 					mlxsw_env);
459 }
460 
461 static void mlxsw_env_temp_warn_event_unregister(struct mlxsw_env *mlxsw_env)
462 {
463 	if (!mlxsw_core_temp_warn_enabled(mlxsw_env->core))
464 		return;
465 
466 	mlxsw_core_trap_unregister(mlxsw_env->core,
467 				   &mlxsw_env_temp_warn_listener, mlxsw_env);
468 }
469 
470 struct mlxsw_env_module_plug_unplug_event {
471 	struct mlxsw_env *mlxsw_env;
472 	u8 module;
473 	struct work_struct work;
474 };
475 
476 static void mlxsw_env_pmpe_event_work(struct work_struct *work)
477 {
478 	struct mlxsw_env_module_plug_unplug_event *event;
479 	struct mlxsw_env *mlxsw_env;
480 	bool has_temp_sensor;
481 	u16 sensor_index;
482 	int err;
483 
484 	event = container_of(work, struct mlxsw_env_module_plug_unplug_event,
485 			     work);
486 	mlxsw_env = event->mlxsw_env;
487 
488 	spin_lock_bh(&mlxsw_env->module_info_lock);
489 	mlxsw_env->module_info[event->module].is_overheat = false;
490 	spin_unlock_bh(&mlxsw_env->module_info_lock);
491 
492 	err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core, event->module,
493 					       &has_temp_sensor);
494 	/* Do not disable events on modules without sensors or faulty sensors
495 	 * because FW returns errors.
496 	 */
497 	if (err)
498 		goto out;
499 
500 	if (!has_temp_sensor)
501 		goto out;
502 
503 	sensor_index = event->module + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
504 	mlxsw_env_temp_event_set(mlxsw_env->core, sensor_index, true);
505 
506 out:
507 	kfree(event);
508 }
509 
510 static void
511 mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl,
512 			     void *priv)
513 {
514 	struct mlxsw_env_module_plug_unplug_event *event;
515 	enum mlxsw_reg_pmpe_module_status module_status;
516 	u8 module = mlxsw_reg_pmpe_module_get(pmpe_pl);
517 	struct mlxsw_env *mlxsw_env = priv;
518 
519 	if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
520 		return;
521 
522 	module_status = mlxsw_reg_pmpe_module_status_get(pmpe_pl);
523 	if (module_status != MLXSW_REG_PMPE_MODULE_STATUS_PLUGGED_ENABLED)
524 		return;
525 
526 	event = kmalloc(sizeof(*event), GFP_ATOMIC);
527 	if (!event)
528 		return;
529 
530 	event->mlxsw_env = mlxsw_env;
531 	event->module = module;
532 	INIT_WORK(&event->work, mlxsw_env_pmpe_event_work);
533 	mlxsw_core_schedule_work(&event->work);
534 }
535 
536 static const struct mlxsw_listener mlxsw_env_module_plug_listener =
537 	MLXSW_EVENTL(mlxsw_env_pmpe_listener_func, PMPE, PMPE);
538 
539 static int
540 mlxsw_env_module_plug_event_register(struct mlxsw_core *mlxsw_core)
541 {
542 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
543 
544 	if (!mlxsw_core_temp_warn_enabled(mlxsw_core))
545 		return 0;
546 
547 	return mlxsw_core_trap_register(mlxsw_core,
548 					&mlxsw_env_module_plug_listener,
549 					mlxsw_env);
550 }
551 
552 static void
553 mlxsw_env_module_plug_event_unregister(struct mlxsw_env *mlxsw_env)
554 {
555 	if (!mlxsw_core_temp_warn_enabled(mlxsw_env->core))
556 		return;
557 
558 	mlxsw_core_trap_unregister(mlxsw_env->core,
559 				   &mlxsw_env_module_plug_listener,
560 				   mlxsw_env);
561 }
562 
563 static int
564 mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core,
565 					 u8 module_count)
566 {
567 	int i, err;
568 
569 	for (i = 0; i < module_count; i++) {
570 		char pmaos_pl[MLXSW_REG_PMAOS_LEN];
571 
572 		mlxsw_reg_pmaos_pack(pmaos_pl, i,
573 				     MLXSW_REG_PMAOS_E_GENERATE_EVENT);
574 		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
575 		if (err)
576 			return err;
577 	}
578 	return 0;
579 }
580 
581 int
582 mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module,
583 				      u64 *p_counter)
584 {
585 	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
586 
587 	/* Prevent switch driver from accessing uninitialized data. */
588 	if (!mlxsw_core_is_initialized(mlxsw_core)) {
589 		*p_counter = 0;
590 		return 0;
591 	}
592 
593 	if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
594 		return -EINVAL;
595 
596 	spin_lock_bh(&mlxsw_env->module_info_lock);
597 	*p_counter = mlxsw_env->module_info[module].module_overheat_counter;
598 	spin_unlock_bh(&mlxsw_env->module_info_lock);
599 
600 	return 0;
601 }
602 EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get);
603 
604 int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env)
605 {
606 	char mgpir_pl[MLXSW_REG_MGPIR_LEN];
607 	struct mlxsw_env *env;
608 	u8 module_count;
609 	int err;
610 
611 	mlxsw_reg_mgpir_pack(mgpir_pl);
612 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
613 	if (err)
614 		return err;
615 
616 	mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &module_count);
617 
618 	env = kzalloc(struct_size(env, module_info, module_count), GFP_KERNEL);
619 	if (!env)
620 		return -ENOMEM;
621 
622 	spin_lock_init(&env->module_info_lock);
623 	env->core = mlxsw_core;
624 	env->module_count = module_count;
625 	*p_env = env;
626 
627 	err = mlxsw_env_temp_warn_event_register(mlxsw_core);
628 	if (err)
629 		goto err_temp_warn_event_register;
630 
631 	err = mlxsw_env_module_plug_event_register(mlxsw_core);
632 	if (err)
633 		goto err_module_plug_event_register;
634 
635 	err = mlxsw_env_module_oper_state_event_enable(mlxsw_core,
636 						       env->module_count);
637 	if (err)
638 		goto err_oper_state_event_enable;
639 
640 	err = mlxsw_env_module_temp_event_enable(mlxsw_core, env->module_count);
641 	if (err)
642 		goto err_temp_event_enable;
643 
644 	return 0;
645 
646 err_temp_event_enable:
647 err_oper_state_event_enable:
648 	mlxsw_env_module_plug_event_unregister(env);
649 err_module_plug_event_register:
650 	mlxsw_env_temp_warn_event_unregister(env);
651 err_temp_warn_event_register:
652 	kfree(env);
653 	return err;
654 }
655 
656 void mlxsw_env_fini(struct mlxsw_env *env)
657 {
658 	mlxsw_env_module_plug_event_unregister(env);
659 	/* Make sure there is no more event work scheduled. */
660 	mlxsw_core_flush_owq();
661 	mlxsw_env_temp_warn_event_unregister(env);
662 	kfree(env);
663 }
664