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