1
2
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
14struct mlxsw_env_module_info {
15 u64 module_overheat_counter;
16 bool is_overheat;
17};
18
19struct mlxsw_env {
20 struct mlxsw_core *core;
21 u8 module_count;
22 spinlock_t module_info_lock;
23 struct mlxsw_env_module_info module_info[];
24};
25
26static 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
62static int
63mlxsw_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
75
76
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
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
89
90
91
92
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
100
101
102
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
126int 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
154
155
156 if (module_emerg) {
157 *temp = off == SFP_TEMP_HIGH_WARN ? module_crit : module_emerg;
158 return 0;
159 }
160
161
162
163
164
165
166
167
168
169
170
171 err = mlxsw_env_validate_cable_ident(core, module, &qsfp, &cmis);
172 if (err)
173 return err;
174
175 if (qsfp) {
176
177
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
205int 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
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
261
262
263 modinfo->type = ETH_MODULE_SFF_8636;
264
265
266
267
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}
281EXPORT_SYMBOL(mlxsw_env_get_module_info);
282
283int 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
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}
317EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);
318
319static 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
348int
349mlxsw_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
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}
390EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page);
391
392static 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
421static 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
440
441
442 if (err)
443 threshold_hi = MLXSW_REG_MTMP_THRESH_HI;
444 else
445
446
447
448
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
461static 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
485static 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
494
495
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
507
508
509
510 spin_unlock(&mlxsw_env->module_info_lock);
511 continue;
512 } else if (is_overheat && !sensor_warning) {
513
514
515 mlxsw_env->module_info[i].is_overheat = false;
516 spin_unlock(&mlxsw_env->module_info_lock);
517 } else {
518
519
520
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
529static const struct mlxsw_listener mlxsw_env_temp_warn_listener =
530 MLXSW_EVENTL(mlxsw_env_mtwe_event_func, MTWE, MTWE);
531
532static 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
544static 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
553struct mlxsw_env_module_plug_unplug_event {
554 struct mlxsw_env *mlxsw_env;
555 u8 module;
556 struct work_struct work;
557};
558
559static 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
578
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
589out:
590 kfree(event);
591}
592
593static void
594mlxsw_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
619static const struct mlxsw_listener mlxsw_env_module_plug_listener =
620 MLXSW_EVENTL(mlxsw_env_pmpe_listener_func, PMPE, PMPE);
621
622static int
623mlxsw_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
635static void
636mlxsw_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
646static int
647mlxsw_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
664int
665mlxsw_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
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}
685EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get);
686
687int 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
729err_temp_event_enable:
730err_oper_state_event_enable:
731 mlxsw_env_module_plug_event_unregister(env);
732err_module_plug_event_register:
733 mlxsw_env_temp_warn_event_unregister(env);
734err_temp_warn_event_register:
735 kfree(env);
736 return err;
737}
738
739void mlxsw_env_fini(struct mlxsw_env *env)
740{
741 mlxsw_env_module_plug_event_unregister(env);
742
743 mlxsw_core_flush_owq();
744 mlxsw_env_temp_warn_event_unregister(env);
745 kfree(env);
746}
747