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
mlxsw_env_validate_cable_ident(struct mlxsw_core * core,int id,bool * qsfp,bool * cmis)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
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)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
mlxsw_env_module_temp_thresholds_get(struct mlxsw_core * core,int module,int off,int * temp)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
mlxsw_env_get_module_info(struct mlxsw_core * mlxsw_core,int module,struct ethtool_modinfo * modinfo)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
mlxsw_env_get_module_eeprom(struct net_device * netdev,struct mlxsw_core * mlxsw_core,int module,struct ethtool_eeprom * ee,u8 * data)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
mlxsw_env_module_has_temp_sensor(struct mlxsw_core * mlxsw_core,u8 module,bool * p_has_temp_sensor)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
mlxsw_env_temp_event_set(struct mlxsw_core * mlxsw_core,u16 sensor_index,bool enable)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
mlxsw_env_module_temp_event_enable(struct mlxsw_core * mlxsw_core,u8 module_count)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
mlxsw_env_mtwe_event_func(const struct mlxsw_reg_info * reg,char * mtwe_pl,void * priv)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
mlxsw_env_temp_warn_event_register(struct mlxsw_core * mlxsw_core)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
mlxsw_env_temp_warn_event_unregister(struct mlxsw_env * mlxsw_env)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
mlxsw_env_pmpe_event_work(struct work_struct * work)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
mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info * reg,char * pmpe_pl,void * priv)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
mlxsw_env_module_plug_event_register(struct mlxsw_core * mlxsw_core)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
mlxsw_env_module_plug_event_unregister(struct mlxsw_env * mlxsw_env)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
mlxsw_env_module_oper_state_event_enable(struct mlxsw_core * mlxsw_core,u8 module_count)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
mlxsw_env_module_overheat_counter_get(struct mlxsw_core * mlxsw_core,u8 module,u64 * p_counter)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
mlxsw_env_init(struct mlxsw_core * mlxsw_core,struct mlxsw_env ** p_env)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
mlxsw_env_fini(struct mlxsw_env * env)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