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