Loading...
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#include <linux/mutex.h>
9
10#include "core.h"
11#include "core_env.h"
12#include "item.h"
13#include "reg.h"
14
15struct mlxsw_env_module_info {
16 u64 module_overheat_counter;
17 bool is_overheat;
18 int num_ports_mapped;
19 int num_ports_up;
20 enum ethtool_module_power_mode_policy power_mode_policy;
21 enum mlxsw_reg_pmtm_module_type type;
22};
23
24struct mlxsw_env_line_card {
25 u8 module_count;
26 bool active;
27 struct mlxsw_env_module_info module_info[];
28};
29
30struct mlxsw_env {
31 struct mlxsw_core *core;
32 const struct mlxsw_bus_info *bus_info;
33 u8 max_module_count; /* Maximum number of modules per-slot. */
34 u8 num_of_slots; /* Including the main board. */
35 u8 max_eeprom_len; /* Maximum module EEPROM transaction length. */
36 struct mutex line_cards_lock; /* Protects line cards. */
37 struct mlxsw_env_line_card *line_cards[] __counted_by(num_of_slots);
38};
39
40static bool __mlxsw_env_linecard_is_active(struct mlxsw_env *mlxsw_env,
41 u8 slot_index)
42{
43 return mlxsw_env->line_cards[slot_index]->active;
44}
45
46static bool mlxsw_env_linecard_is_active(struct mlxsw_env *mlxsw_env,
47 u8 slot_index)
48{
49 bool active;
50
51 mutex_lock(&mlxsw_env->line_cards_lock);
52 active = __mlxsw_env_linecard_is_active(mlxsw_env, slot_index);
53 mutex_unlock(&mlxsw_env->line_cards_lock);
54
55 return active;
56}
57
58static struct
59mlxsw_env_module_info *mlxsw_env_module_info_get(struct mlxsw_core *mlxsw_core,
60 u8 slot_index, u8 module)
61{
62 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
63
64 return &mlxsw_env->line_cards[slot_index]->module_info[module];
65}
66
67static int __mlxsw_env_validate_module_type(struct mlxsw_core *core,
68 u8 slot_index, u8 module)
69{
70 struct mlxsw_env *mlxsw_env = mlxsw_core_env(core);
71 struct mlxsw_env_module_info *module_info;
72 int err;
73
74 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
75 return 0;
76
77 module_info = mlxsw_env_module_info_get(core, slot_index, module);
78 switch (module_info->type) {
79 case MLXSW_REG_PMTM_MODULE_TYPE_TWISTED_PAIR:
80 err = -EINVAL;
81 break;
82 default:
83 err = 0;
84 }
85
86 return err;
87}
88
89static int mlxsw_env_validate_module_type(struct mlxsw_core *core,
90 u8 slot_index, u8 module)
91{
92 struct mlxsw_env *mlxsw_env = mlxsw_core_env(core);
93 int err;
94
95 mutex_lock(&mlxsw_env->line_cards_lock);
96 err = __mlxsw_env_validate_module_type(core, slot_index, module);
97 mutex_unlock(&mlxsw_env->line_cards_lock);
98
99 return err;
100}
101
102static int
103mlxsw_env_validate_cable_ident(struct mlxsw_core *core, u8 slot_index, int id,
104 bool *qsfp, bool *cmis)
105{
106 char mcia_pl[MLXSW_REG_MCIA_LEN];
107 char *eeprom_tmp;
108 u8 ident;
109 int err;
110
111 err = mlxsw_env_validate_module_type(core, slot_index, id);
112 if (err)
113 return err;
114
115 mlxsw_reg_mcia_pack(mcia_pl, slot_index, id,
116 MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1,
117 MLXSW_REG_MCIA_I2C_ADDR_LOW);
118 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
119 if (err)
120 return err;
121 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
122 ident = eeprom_tmp[0];
123 *cmis = false;
124 switch (ident) {
125 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
126 *qsfp = false;
127 break;
128 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
129 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
130 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
131 *qsfp = true;
132 break;
133 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
134 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_OSFP:
135 *qsfp = true;
136 *cmis = true;
137 break;
138 default:
139 return -EINVAL;
140 }
141
142 return 0;
143}
144
145static int
146mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, u8 slot_index,
147 int module, u16 offset, u16 size, void *data,
148 bool qsfp, unsigned int *p_read_size)
149{
150 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
151 char mcia_pl[MLXSW_REG_MCIA_LEN];
152 char *eeprom_tmp;
153 u16 i2c_addr;
154 u8 page = 0;
155 int status;
156 int err;
157
158 size = min_t(u16, size, mlxsw_env->max_eeprom_len);
159
160 if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
161 offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
162 /* Cross pages read, read until offset 256 in low page */
163 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
164
165 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
166 if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
167 if (qsfp) {
168 /* When reading upper pages 1, 2 and 3 the offset
169 * starts at 128. Please refer to "QSFP+ Memory Map"
170 * figure in SFF-8436 specification and to "CMIS Module
171 * Memory Map" figure in CMIS specification for
172 * graphical depiction.
173 */
174 page = MLXSW_REG_MCIA_PAGE_GET(offset);
175 offset -= MLXSW_REG_MCIA_EEPROM_UP_PAGE_LENGTH * page;
176 if (offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
177 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
178 } else {
179 /* When reading upper pages 1, 2 and 3 the offset
180 * starts at 0 and I2C high address is used. Please refer
181 * to "Memory Organization" figure in SFF-8472
182 * specification for graphical depiction.
183 */
184 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
185 offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
186 }
187 }
188
189 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, page, offset, size,
190 i2c_addr);
191
192 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
193 if (err)
194 return err;
195
196 status = mlxsw_reg_mcia_status_get(mcia_pl);
197 if (status)
198 return -EIO;
199
200 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
201 memcpy(data, eeprom_tmp, size);
202 *p_read_size = size;
203
204 return 0;
205}
206
207int
208mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, u8 slot_index,
209 int module, int off, int *temp)
210{
211 unsigned int module_temp, module_crit, module_emerg;
212 union {
213 u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];
214 u16 temp;
215 } temp_thresh;
216 char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
217 char mtmp_pl[MLXSW_REG_MTMP_LEN];
218 char *eeprom_tmp;
219 bool qsfp, cmis;
220 int page;
221 int err;
222
223 mlxsw_reg_mtmp_pack(mtmp_pl, slot_index,
224 MLXSW_REG_MTMP_MODULE_INDEX_MIN + module, false,
225 false);
226 err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);
227 if (err)
228 return err;
229 mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, &module_crit,
230 &module_emerg, NULL);
231 if (!module_temp) {
232 *temp = 0;
233 return 0;
234 }
235
236 /* Validate if threshold reading is available through MTMP register,
237 * otherwise fallback to read through MCIA.
238 */
239 if (module_emerg) {
240 *temp = off == SFP_TEMP_HIGH_WARN ? module_crit : module_emerg;
241 return 0;
242 }
243
244 /* Read Free Side Device Temperature Thresholds from page 03h
245 * (MSB at lower byte address).
246 * Bytes:
247 * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);
248 * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);
249 * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);
250 * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);
251 */
252
253 /* Validate module identifier value. */
254 err = mlxsw_env_validate_cable_ident(core, slot_index, module, &qsfp,
255 &cmis);
256 if (err)
257 return err;
258
259 if (qsfp) {
260 /* For QSFP/CMIS module-defined thresholds are located in page
261 * 02h, otherwise in page 03h.
262 */
263 if (cmis)
264 page = MLXSW_REG_MCIA_TH_PAGE_CMIS_NUM;
265 else
266 page = MLXSW_REG_MCIA_TH_PAGE_NUM;
267 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, page,
268 MLXSW_REG_MCIA_TH_PAGE_OFF + off,
269 MLXSW_REG_MCIA_TH_ITEM_SIZE,
270 MLXSW_REG_MCIA_I2C_ADDR_LOW);
271 } else {
272 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module,
273 MLXSW_REG_MCIA_PAGE0_LO,
274 off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
275 MLXSW_REG_MCIA_I2C_ADDR_HIGH);
276 }
277
278 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
279 if (err)
280 return err;
281
282 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
283 memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);
284 *temp = temp_thresh.temp * 1000;
285
286 return 0;
287}
288
289int mlxsw_env_get_module_info(struct net_device *netdev,
290 struct mlxsw_core *mlxsw_core, u8 slot_index,
291 int module, struct ethtool_modinfo *modinfo)
292{
293 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
294 u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
295 u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
296 u8 module_rev_id, module_id, diag_mon;
297 unsigned int read_size;
298 int err;
299
300 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {
301 netdev_err(netdev, "Cannot read EEPROM of module on an inactive line card\n");
302 return -EIO;
303 }
304
305 err = mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
306 if (err) {
307 netdev_err(netdev,
308 "EEPROM is not equipped on port module type");
309 return err;
310 }
311
312 err = mlxsw_env_query_module_eeprom(mlxsw_core, slot_index, module, 0,
313 offset, module_info, false,
314 &read_size);
315 if (err)
316 return err;
317
318 if (read_size < offset)
319 return -EIO;
320
321 module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
322 module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
323
324 switch (module_id) {
325 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
326 modinfo->type = ETH_MODULE_SFF_8436;
327 modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
328 break;
329 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
330 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
331 if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
332 module_rev_id >=
333 MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
334 modinfo->type = ETH_MODULE_SFF_8636;
335 modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
336 } else {
337 modinfo->type = ETH_MODULE_SFF_8436;
338 modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
339 }
340 break;
341 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
342 /* Verify if transceiver provides diagnostic monitoring page */
343 err = mlxsw_env_query_module_eeprom(mlxsw_core, slot_index,
344 module, SFP_DIAGMON, 1,
345 &diag_mon, false,
346 &read_size);
347 if (err)
348 return err;
349
350 if (read_size < 1)
351 return -EIO;
352
353 modinfo->type = ETH_MODULE_SFF_8472;
354 if (diag_mon)
355 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
356 else
357 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;
358 break;
359 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
360 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_OSFP:
361 /* Use SFF_8636 as base type. ethtool should recognize specific
362 * type through the identifier value.
363 */
364 modinfo->type = ETH_MODULE_SFF_8636;
365 /* Verify if module EEPROM is a flat memory. In case of flat
366 * memory only page 00h (0-255 bytes) can be read. Otherwise
367 * upper pages 01h and 02h can also be read. Upper pages 10h
368 * and 11h are currently not supported by the driver.
369 */
370 if (module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_TYPE_ID] &
371 MLXSW_REG_MCIA_EEPROM_CMIS_FLAT_MEMORY)
372 modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
373 else
374 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
375 break;
376 default:
377 return -EINVAL;
378 }
379
380 return 0;
381}
382EXPORT_SYMBOL(mlxsw_env_get_module_info);
383
384int mlxsw_env_get_module_eeprom(struct net_device *netdev,
385 struct mlxsw_core *mlxsw_core, u8 slot_index,
386 int module, struct ethtool_eeprom *ee,
387 u8 *data)
388{
389 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
390 int offset = ee->offset;
391 unsigned int read_size;
392 bool qsfp, cmis;
393 int i = 0;
394 int err;
395
396 if (!ee->len)
397 return -EINVAL;
398
399 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {
400 netdev_err(netdev, "Cannot read EEPROM of module on an inactive line card\n");
401 return -EIO;
402 }
403
404 memset(data, 0, ee->len);
405 /* Validate module identifier value. */
406 err = mlxsw_env_validate_cable_ident(mlxsw_core, slot_index, module,
407 &qsfp, &cmis);
408 if (err)
409 return err;
410
411 while (i < ee->len) {
412 err = mlxsw_env_query_module_eeprom(mlxsw_core, slot_index,
413 module, offset,
414 ee->len - i, data + i,
415 qsfp, &read_size);
416 if (err) {
417 netdev_err(netdev, "Eeprom query failed\n");
418 return err;
419 }
420
421 i += read_size;
422 offset += read_size;
423 }
424
425 return 0;
426}
427EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);
428
429static int mlxsw_env_mcia_status_process(const char *mcia_pl,
430 struct netlink_ext_ack *extack)
431{
432 u8 status = mlxsw_reg_mcia_status_get(mcia_pl);
433
434 switch (status) {
435 case MLXSW_REG_MCIA_STATUS_GOOD:
436 return 0;
437 case MLXSW_REG_MCIA_STATUS_NO_EEPROM_MODULE:
438 NL_SET_ERR_MSG_MOD(extack, "No response from module's EEPROM");
439 return -EIO;
440 case MLXSW_REG_MCIA_STATUS_MODULE_NOT_SUPPORTED:
441 NL_SET_ERR_MSG_MOD(extack, "Module type not supported by the device");
442 return -EOPNOTSUPP;
443 case MLXSW_REG_MCIA_STATUS_MODULE_NOT_CONNECTED:
444 NL_SET_ERR_MSG_MOD(extack, "No module present indication");
445 return -EIO;
446 case MLXSW_REG_MCIA_STATUS_I2C_ERROR:
447 NL_SET_ERR_MSG_MOD(extack, "Error occurred while trying to access module's EEPROM using I2C");
448 return -EIO;
449 case MLXSW_REG_MCIA_STATUS_MODULE_DISABLED:
450 NL_SET_ERR_MSG_MOD(extack, "Module is disabled");
451 return -EIO;
452 default:
453 NL_SET_ERR_MSG_MOD(extack, "Unknown error");
454 return -EIO;
455 }
456}
457
458int
459mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core,
460 u8 slot_index, u8 module,
461 const struct ethtool_module_eeprom *page,
462 struct netlink_ext_ack *extack)
463{
464 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
465 u32 bytes_read = 0;
466 u16 device_addr;
467 int err;
468
469 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {
470 NL_SET_ERR_MSG_MOD(extack,
471 "Cannot read EEPROM of module on an inactive line card");
472 return -EIO;
473 }
474
475 err = mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
476 if (err) {
477 NL_SET_ERR_MSG_MOD(extack, "EEPROM is not equipped on port module type");
478 return err;
479 }
480
481 /* Offset cannot be larger than 2 * ETH_MODULE_EEPROM_PAGE_LEN */
482 device_addr = page->offset;
483
484 while (bytes_read < page->length) {
485 char mcia_pl[MLXSW_REG_MCIA_LEN];
486 char *eeprom_tmp;
487 u8 size;
488
489 size = min_t(u8, page->length - bytes_read,
490 mlxsw_env->max_eeprom_len);
491
492 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, page->page,
493 device_addr + bytes_read, size,
494 page->i2c_address);
495 mlxsw_reg_mcia_bank_number_set(mcia_pl, page->bank);
496
497 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
498 if (err) {
499 NL_SET_ERR_MSG_MOD(extack, "Failed to access module's EEPROM");
500 return err;
501 }
502
503 err = mlxsw_env_mcia_status_process(mcia_pl, extack);
504 if (err)
505 return err;
506
507 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
508 memcpy(page->data + bytes_read, eeprom_tmp, size);
509 bytes_read += size;
510 }
511
512 return bytes_read;
513}
514EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page);
515
516int
517mlxsw_env_set_module_eeprom_by_page(struct mlxsw_core *mlxsw_core,
518 u8 slot_index, u8 module,
519 const struct ethtool_module_eeprom *page,
520 struct netlink_ext_ack *extack)
521{
522 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
523 u32 bytes_written = 0;
524 u16 device_addr;
525 int err;
526
527 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {
528 NL_SET_ERR_MSG_MOD(extack,
529 "Cannot write to EEPROM of a module on an inactive line card");
530 return -EIO;
531 }
532
533 err = mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
534 if (err) {
535 NL_SET_ERR_MSG_MOD(extack, "EEPROM is not equipped on port module type");
536 return err;
537 }
538
539 device_addr = page->offset;
540
541 while (bytes_written < page->length) {
542 char mcia_pl[MLXSW_REG_MCIA_LEN];
543 char eeprom_tmp[128] = {};
544 u8 size;
545
546 size = min_t(u8, page->length - bytes_written,
547 mlxsw_env->max_eeprom_len);
548
549 mlxsw_reg_mcia_pack(mcia_pl, slot_index, module, page->page,
550 device_addr + bytes_written, size,
551 page->i2c_address);
552 mlxsw_reg_mcia_bank_number_set(mcia_pl, page->bank);
553 memcpy(eeprom_tmp, page->data + bytes_written, size);
554 mlxsw_reg_mcia_eeprom_memcpy_to(mcia_pl, eeprom_tmp);
555
556 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
557 if (err) {
558 NL_SET_ERR_MSG_MOD(extack, "Failed to access module's EEPROM");
559 return err;
560 }
561
562 err = mlxsw_env_mcia_status_process(mcia_pl, extack);
563 if (err)
564 return err;
565
566 bytes_written += size;
567 }
568
569 return 0;
570}
571EXPORT_SYMBOL(mlxsw_env_set_module_eeprom_by_page);
572
573static int mlxsw_env_module_reset(struct mlxsw_core *mlxsw_core, u8 slot_index,
574 u8 module)
575{
576 char pmaos_pl[MLXSW_REG_PMAOS_LEN];
577
578 mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, module);
579 mlxsw_reg_pmaos_rst_set(pmaos_pl, true);
580
581 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
582}
583
584int mlxsw_env_reset_module(struct net_device *netdev,
585 struct mlxsw_core *mlxsw_core, u8 slot_index,
586 u8 module, u32 *flags)
587{
588 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
589 struct mlxsw_env_module_info *module_info;
590 u32 req = *flags;
591 int err;
592
593 if (!(req & ETH_RESET_PHY) &&
594 !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT)))
595 return 0;
596
597 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {
598 netdev_err(netdev, "Cannot reset module on an inactive line card\n");
599 return -EIO;
600 }
601
602 mutex_lock(&mlxsw_env->line_cards_lock);
603
604 err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
605 if (err) {
606 netdev_err(netdev, "Reset module is not supported on port module type\n");
607 goto out;
608 }
609
610 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
611 if (module_info->num_ports_up) {
612 netdev_err(netdev, "Cannot reset module when ports using it are administratively up\n");
613 err = -EINVAL;
614 goto out;
615 }
616
617 if (module_info->num_ports_mapped > 1 &&
618 !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) {
619 netdev_err(netdev, "Cannot reset module without \"phy-shared\" flag when shared by multiple ports\n");
620 err = -EINVAL;
621 goto out;
622 }
623
624 err = mlxsw_env_module_reset(mlxsw_core, slot_index, module);
625 if (err) {
626 netdev_err(netdev, "Failed to reset module\n");
627 goto out;
628 }
629
630 *flags &= ~(ETH_RESET_PHY | (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT));
631
632out:
633 mutex_unlock(&mlxsw_env->line_cards_lock);
634 return err;
635}
636EXPORT_SYMBOL(mlxsw_env_reset_module);
637
638int
639mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
640 u8 module,
641 struct ethtool_module_power_mode_params *params,
642 struct netlink_ext_ack *extack)
643{
644 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
645 struct mlxsw_env_module_info *module_info;
646 char mcion_pl[MLXSW_REG_MCION_LEN];
647 u32 status_bits;
648 int err = 0;
649
650 mutex_lock(&mlxsw_env->line_cards_lock);
651
652 err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
653 if (err) {
654 NL_SET_ERR_MSG_MOD(extack, "Power mode is not supported on port module type");
655 goto out;
656 }
657
658 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
659 params->policy = module_info->power_mode_policy;
660
661 /* Avoid accessing an inactive line card, as it will result in an error. */
662 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
663 goto out;
664
665 mlxsw_reg_mcion_pack(mcion_pl, slot_index, module);
666 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcion), mcion_pl);
667 if (err) {
668 NL_SET_ERR_MSG_MOD(extack, "Failed to retrieve module's power mode");
669 goto out;
670 }
671
672 status_bits = mlxsw_reg_mcion_module_status_bits_get(mcion_pl);
673 if (!(status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_PRESENT_MASK))
674 goto out;
675
676 if (status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_LOW_POWER_MASK)
677 params->mode = ETHTOOL_MODULE_POWER_MODE_LOW;
678 else
679 params->mode = ETHTOOL_MODULE_POWER_MODE_HIGH;
680
681out:
682 mutex_unlock(&mlxsw_env->line_cards_lock);
683 return err;
684}
685EXPORT_SYMBOL(mlxsw_env_get_module_power_mode);
686
687static int mlxsw_env_module_enable_set(struct mlxsw_core *mlxsw_core,
688 u8 slot_index, u8 module, bool enable)
689{
690 enum mlxsw_reg_pmaos_admin_status admin_status;
691 char pmaos_pl[MLXSW_REG_PMAOS_LEN];
692
693 mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, module);
694 admin_status = enable ? MLXSW_REG_PMAOS_ADMIN_STATUS_ENABLED :
695 MLXSW_REG_PMAOS_ADMIN_STATUS_DISABLED;
696 mlxsw_reg_pmaos_admin_status_set(pmaos_pl, admin_status);
697 mlxsw_reg_pmaos_ase_set(pmaos_pl, true);
698
699 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
700}
701
702static int mlxsw_env_module_low_power_set(struct mlxsw_core *mlxsw_core,
703 u8 slot_index, u8 module,
704 bool low_power)
705{
706 u16 eeprom_override_mask, eeprom_override;
707 char pmmp_pl[MLXSW_REG_PMMP_LEN];
708
709 mlxsw_reg_pmmp_pack(pmmp_pl, slot_index, module);
710 mlxsw_reg_pmmp_sticky_set(pmmp_pl, true);
711 /* Mask all the bits except low power mode. */
712 eeprom_override_mask = ~MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK;
713 mlxsw_reg_pmmp_eeprom_override_mask_set(pmmp_pl, eeprom_override_mask);
714 eeprom_override = low_power ? MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK :
715 0;
716 mlxsw_reg_pmmp_eeprom_override_set(pmmp_pl, eeprom_override);
717
718 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmmp), pmmp_pl);
719}
720
721static int __mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core,
722 u8 slot_index, u8 module,
723 bool low_power,
724 struct netlink_ext_ack *extack)
725{
726 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
727 int err;
728
729 /* Avoid accessing an inactive line card, as it will result in an error.
730 * Cached configuration will be applied by mlxsw_env_got_active() when
731 * line card becomes active.
732 */
733 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
734 return 0;
735
736 err = mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, false);
737 if (err) {
738 NL_SET_ERR_MSG_MOD(extack, "Failed to disable module");
739 return err;
740 }
741
742 err = mlxsw_env_module_low_power_set(mlxsw_core, slot_index, module,
743 low_power);
744 if (err) {
745 NL_SET_ERR_MSG_MOD(extack, "Failed to set module's power mode");
746 goto err_module_low_power_set;
747 }
748
749 err = mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, true);
750 if (err) {
751 NL_SET_ERR_MSG_MOD(extack, "Failed to enable module");
752 goto err_module_enable_set;
753 }
754
755 return 0;
756
757err_module_enable_set:
758 mlxsw_env_module_low_power_set(mlxsw_core, slot_index, module,
759 !low_power);
760err_module_low_power_set:
761 mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, true);
762 return err;
763}
764
765static int
766mlxsw_env_set_module_power_mode_apply(struct mlxsw_core *mlxsw_core,
767 u8 slot_index, u8 module,
768 enum ethtool_module_power_mode_policy policy,
769 struct netlink_ext_ack *extack)
770{
771 struct mlxsw_env_module_info *module_info;
772 bool low_power;
773 int err = 0;
774
775 err = __mlxsw_env_validate_module_type(mlxsw_core, slot_index, module);
776 if (err) {
777 NL_SET_ERR_MSG_MOD(extack,
778 "Power mode set is not supported on port module type");
779 goto out;
780 }
781
782 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
783 if (module_info->power_mode_policy == policy)
784 goto out;
785
786 /* If any ports are up, we are already in high power mode. */
787 if (module_info->num_ports_up)
788 goto out_set_policy;
789
790 low_power = policy == ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO;
791 err = __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module,
792 low_power, extack);
793 if (err)
794 goto out;
795
796out_set_policy:
797 module_info->power_mode_policy = policy;
798out:
799 return err;
800}
801
802int
803mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
804 u8 module,
805 enum ethtool_module_power_mode_policy policy,
806 struct netlink_ext_ack *extack)
807{
808 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
809 int err;
810
811 if (policy != ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH &&
812 policy != ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO) {
813 NL_SET_ERR_MSG_MOD(extack, "Unsupported power mode policy");
814 return -EOPNOTSUPP;
815 }
816
817 mutex_lock(&mlxsw_env->line_cards_lock);
818 err = mlxsw_env_set_module_power_mode_apply(mlxsw_core, slot_index,
819 module, policy, extack);
820 mutex_unlock(&mlxsw_env->line_cards_lock);
821
822 return err;
823}
824EXPORT_SYMBOL(mlxsw_env_set_module_power_mode);
825
826static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core,
827 u8 slot_index, u8 module,
828 bool *p_has_temp_sensor)
829{
830 char mtbr_pl[MLXSW_REG_MTBR_LEN];
831 u16 temp;
832 int err;
833
834 mlxsw_reg_mtbr_pack(mtbr_pl, slot_index,
835 MLXSW_REG_MTBR_BASE_MODULE_INDEX + module);
836 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtbr), mtbr_pl);
837 if (err)
838 return err;
839
840 mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
841
842 switch (temp) {
843 case MLXSW_REG_MTBR_BAD_SENS_INFO:
844 case MLXSW_REG_MTBR_NO_CONN:
845 case MLXSW_REG_MTBR_NO_TEMP_SENS:
846 case MLXSW_REG_MTBR_INDEX_NA:
847 *p_has_temp_sensor = false;
848 break;
849 default:
850 *p_has_temp_sensor = temp ? true : false;
851 }
852 return 0;
853}
854
855static int
856mlxsw_env_temp_event_set(struct mlxsw_core *mlxsw_core, u8 slot_index,
857 u16 sensor_index, bool enable)
858{
859 char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0};
860 enum mlxsw_reg_mtmp_tee tee;
861 int err, threshold_hi;
862
863 mlxsw_reg_mtmp_slot_index_set(mtmp_pl, slot_index);
864 mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, sensor_index);
865 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
866 if (err)
867 return err;
868
869 if (enable) {
870 err = mlxsw_env_module_temp_thresholds_get(mlxsw_core,
871 slot_index,
872 sensor_index -
873 MLXSW_REG_MTMP_MODULE_INDEX_MIN,
874 SFP_TEMP_HIGH_WARN,
875 &threshold_hi);
876 /* In case it is not possible to query the module's threshold,
877 * use the default value.
878 */
879 if (err)
880 threshold_hi = MLXSW_REG_MTMP_THRESH_HI;
881 else
882 /* mlxsw_env_module_temp_thresholds_get() multiplies
883 * Celsius degrees by 1000 whereas MTMP expects
884 * temperature in 0.125 Celsius degrees units.
885 * Convert threshold_hi to correct units.
886 */
887 threshold_hi = threshold_hi / 1000 * 8;
888
889 mlxsw_reg_mtmp_temperature_threshold_hi_set(mtmp_pl, threshold_hi);
890 mlxsw_reg_mtmp_temperature_threshold_lo_set(mtmp_pl, threshold_hi -
891 MLXSW_REG_MTMP_HYSTERESIS_TEMP);
892 }
893 tee = enable ? MLXSW_REG_MTMP_TEE_GENERATE_EVENT : MLXSW_REG_MTMP_TEE_NO_EVENT;
894 mlxsw_reg_mtmp_tee_set(mtmp_pl, tee);
895 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
896}
897
898static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core,
899 u8 slot_index)
900{
901 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
902 int i, err, sensor_index;
903 bool has_temp_sensor;
904
905 for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
906 err = mlxsw_env_module_has_temp_sensor(mlxsw_core, slot_index,
907 i, &has_temp_sensor);
908 if (err)
909 return err;
910
911 if (!has_temp_sensor)
912 continue;
913
914 sensor_index = i + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
915 err = mlxsw_env_temp_event_set(mlxsw_core, slot_index,
916 sensor_index, true);
917 if (err)
918 return err;
919 }
920
921 return 0;
922}
923
924struct mlxsw_env_module_temp_warn_event {
925 struct mlxsw_env *mlxsw_env;
926 char mtwe_pl[MLXSW_REG_MTWE_LEN];
927 struct work_struct work;
928};
929
930static void mlxsw_env_mtwe_event_work(struct work_struct *work)
931{
932 struct mlxsw_env_module_temp_warn_event *event;
933 struct mlxsw_env_module_info *module_info;
934 struct mlxsw_env *mlxsw_env;
935 int i, sensor_warning;
936 bool is_overheat;
937
938 event = container_of(work, struct mlxsw_env_module_temp_warn_event,
939 work);
940 mlxsw_env = event->mlxsw_env;
941
942 for (i = 0; i < mlxsw_env->max_module_count; i++) {
943 /* 64-127 of sensor_index are mapped to the port modules
944 * sequentially (module 0 is mapped to sensor_index 64,
945 * module 1 to sensor_index 65 and so on)
946 */
947 sensor_warning =
948 mlxsw_reg_mtwe_sensor_warning_get(event->mtwe_pl,
949 i + MLXSW_REG_MTMP_MODULE_INDEX_MIN);
950 mutex_lock(&mlxsw_env->line_cards_lock);
951 /* MTWE only supports main board. */
952 module_info = mlxsw_env_module_info_get(mlxsw_env->core, 0, i);
953 is_overheat = module_info->is_overheat;
954
955 if ((is_overheat && sensor_warning) ||
956 (!is_overheat && !sensor_warning)) {
957 /* Current state is "warning" and MTWE still reports
958 * warning OR current state in "no warning" and MTWE
959 * does not report warning.
960 */
961 mutex_unlock(&mlxsw_env->line_cards_lock);
962 continue;
963 } else if (is_overheat && !sensor_warning) {
964 /* MTWE reports "no warning", turn is_overheat off.
965 */
966 module_info->is_overheat = false;
967 mutex_unlock(&mlxsw_env->line_cards_lock);
968 } else {
969 /* Current state is "no warning" and MTWE reports
970 * "warning", increase the counter and turn is_overheat
971 * on.
972 */
973 module_info->is_overheat = true;
974 module_info->module_overheat_counter++;
975 mutex_unlock(&mlxsw_env->line_cards_lock);
976 }
977 }
978
979 kfree(event);
980}
981
982static void
983mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info *reg, char *mtwe_pl,
984 void *priv)
985{
986 struct mlxsw_env_module_temp_warn_event *event;
987 struct mlxsw_env *mlxsw_env = priv;
988
989 event = kmalloc(sizeof(*event), GFP_ATOMIC);
990 if (!event)
991 return;
992
993 event->mlxsw_env = mlxsw_env;
994 memcpy(event->mtwe_pl, mtwe_pl, MLXSW_REG_MTWE_LEN);
995 INIT_WORK(&event->work, mlxsw_env_mtwe_event_work);
996 mlxsw_core_schedule_work(&event->work);
997}
998
999static const struct mlxsw_listener mlxsw_env_temp_warn_listener =
1000 MLXSW_CORE_EVENTL(mlxsw_env_mtwe_listener_func, MTWE);
1001
1002static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core)
1003{
1004 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1005
1006 return mlxsw_core_trap_register(mlxsw_core,
1007 &mlxsw_env_temp_warn_listener,
1008 mlxsw_env);
1009}
1010
1011static void mlxsw_env_temp_warn_event_unregister(struct mlxsw_env *mlxsw_env)
1012{
1013 mlxsw_core_trap_unregister(mlxsw_env->core,
1014 &mlxsw_env_temp_warn_listener, mlxsw_env);
1015}
1016
1017struct mlxsw_env_module_plug_unplug_event {
1018 struct mlxsw_env *mlxsw_env;
1019 u8 slot_index;
1020 u8 module;
1021 struct work_struct work;
1022};
1023
1024static void mlxsw_env_pmpe_event_work(struct work_struct *work)
1025{
1026 struct mlxsw_env_module_plug_unplug_event *event;
1027 struct mlxsw_env_module_info *module_info;
1028 struct mlxsw_env *mlxsw_env;
1029 bool has_temp_sensor;
1030 u16 sensor_index;
1031 int err;
1032
1033 event = container_of(work, struct mlxsw_env_module_plug_unplug_event,
1034 work);
1035 mlxsw_env = event->mlxsw_env;
1036
1037 mutex_lock(&mlxsw_env->line_cards_lock);
1038 module_info = mlxsw_env_module_info_get(mlxsw_env->core,
1039 event->slot_index,
1040 event->module);
1041 module_info->is_overheat = false;
1042 mutex_unlock(&mlxsw_env->line_cards_lock);
1043
1044 err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core,
1045 event->slot_index,
1046 event->module,
1047 &has_temp_sensor);
1048 /* Do not disable events on modules without sensors or faulty sensors
1049 * because FW returns errors.
1050 */
1051 if (err)
1052 goto out;
1053
1054 if (!has_temp_sensor)
1055 goto out;
1056
1057 sensor_index = event->module + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
1058 mlxsw_env_temp_event_set(mlxsw_env->core, event->slot_index,
1059 sensor_index, true);
1060
1061out:
1062 kfree(event);
1063}
1064
1065static void
1066mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl,
1067 void *priv)
1068{
1069 u8 slot_index = mlxsw_reg_pmpe_slot_index_get(pmpe_pl);
1070 struct mlxsw_env_module_plug_unplug_event *event;
1071 enum mlxsw_reg_pmpe_module_status module_status;
1072 u8 module = mlxsw_reg_pmpe_module_get(pmpe_pl);
1073 struct mlxsw_env *mlxsw_env = priv;
1074
1075 if (WARN_ON_ONCE(module >= mlxsw_env->max_module_count ||
1076 slot_index >= mlxsw_env->num_of_slots))
1077 return;
1078
1079 module_status = mlxsw_reg_pmpe_module_status_get(pmpe_pl);
1080 if (module_status != MLXSW_REG_PMPE_MODULE_STATUS_PLUGGED_ENABLED)
1081 return;
1082
1083 event = kmalloc(sizeof(*event), GFP_ATOMIC);
1084 if (!event)
1085 return;
1086
1087 event->mlxsw_env = mlxsw_env;
1088 event->slot_index = slot_index;
1089 event->module = module;
1090 INIT_WORK(&event->work, mlxsw_env_pmpe_event_work);
1091 mlxsw_core_schedule_work(&event->work);
1092}
1093
1094static const struct mlxsw_listener mlxsw_env_module_plug_listener =
1095 MLXSW_CORE_EVENTL(mlxsw_env_pmpe_listener_func, PMPE);
1096
1097static int
1098mlxsw_env_module_plug_event_register(struct mlxsw_core *mlxsw_core)
1099{
1100 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1101
1102 return mlxsw_core_trap_register(mlxsw_core,
1103 &mlxsw_env_module_plug_listener,
1104 mlxsw_env);
1105}
1106
1107static void
1108mlxsw_env_module_plug_event_unregister(struct mlxsw_env *mlxsw_env)
1109{
1110 mlxsw_core_trap_unregister(mlxsw_env->core,
1111 &mlxsw_env_module_plug_listener,
1112 mlxsw_env);
1113}
1114
1115static int
1116mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core,
1117 u8 slot_index)
1118{
1119 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1120 int i, err;
1121
1122 for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
1123 char pmaos_pl[MLXSW_REG_PMAOS_LEN];
1124
1125 mlxsw_reg_pmaos_pack(pmaos_pl, slot_index, i);
1126 mlxsw_reg_pmaos_e_set(pmaos_pl,
1127 MLXSW_REG_PMAOS_E_GENERATE_EVENT);
1128 mlxsw_reg_pmaos_ee_set(pmaos_pl, true);
1129 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
1130 if (err)
1131 return err;
1132 }
1133 return 0;
1134}
1135
1136int
1137mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 slot_index,
1138 u8 module, u64 *p_counter)
1139{
1140 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1141 struct mlxsw_env_module_info *module_info;
1142
1143 mutex_lock(&mlxsw_env->line_cards_lock);
1144 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1145 *p_counter = module_info->module_overheat_counter;
1146 mutex_unlock(&mlxsw_env->line_cards_lock);
1147
1148 return 0;
1149}
1150EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get);
1151
1152void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 slot_index,
1153 u8 module)
1154{
1155 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1156 struct mlxsw_env_module_info *module_info;
1157
1158 mutex_lock(&mlxsw_env->line_cards_lock);
1159 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1160 module_info->num_ports_mapped++;
1161 mutex_unlock(&mlxsw_env->line_cards_lock);
1162}
1163EXPORT_SYMBOL(mlxsw_env_module_port_map);
1164
1165void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 slot_index,
1166 u8 module)
1167{
1168 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1169 struct mlxsw_env_module_info *module_info;
1170
1171 mutex_lock(&mlxsw_env->line_cards_lock);
1172 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1173 module_info->num_ports_mapped--;
1174 mutex_unlock(&mlxsw_env->line_cards_lock);
1175}
1176EXPORT_SYMBOL(mlxsw_env_module_port_unmap);
1177
1178int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 slot_index,
1179 u8 module)
1180{
1181 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1182 struct mlxsw_env_module_info *module_info;
1183 int err = 0;
1184
1185 mutex_lock(&mlxsw_env->line_cards_lock);
1186
1187 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1188 if (module_info->power_mode_policy !=
1189 ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
1190 goto out_inc;
1191
1192 if (module_info->num_ports_up != 0)
1193 goto out_inc;
1194
1195 /* Transition to high power mode following first port using the module
1196 * being put administratively up.
1197 */
1198 err = __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module,
1199 false, NULL);
1200 if (err)
1201 goto out_unlock;
1202
1203out_inc:
1204 module_info->num_ports_up++;
1205out_unlock:
1206 mutex_unlock(&mlxsw_env->line_cards_lock);
1207 return err;
1208}
1209EXPORT_SYMBOL(mlxsw_env_module_port_up);
1210
1211void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 slot_index,
1212 u8 module)
1213{
1214 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1215 struct mlxsw_env_module_info *module_info;
1216
1217 mutex_lock(&mlxsw_env->line_cards_lock);
1218
1219 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1220 module_info->num_ports_up--;
1221
1222 if (module_info->power_mode_policy !=
1223 ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
1224 goto out_unlock;
1225
1226 if (module_info->num_ports_up != 0)
1227 goto out_unlock;
1228
1229 /* Transition to low power mode following last port using the module
1230 * being put administratively down.
1231 */
1232 __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module, true,
1233 NULL);
1234
1235out_unlock:
1236 mutex_unlock(&mlxsw_env->line_cards_lock);
1237}
1238EXPORT_SYMBOL(mlxsw_env_module_port_down);
1239
1240static int mlxsw_env_line_cards_alloc(struct mlxsw_env *env)
1241{
1242 struct mlxsw_env_module_info *module_info;
1243 int i, j;
1244
1245 for (i = 0; i < env->num_of_slots; i++) {
1246 env->line_cards[i] = kzalloc(struct_size(env->line_cards[i],
1247 module_info,
1248 env->max_module_count),
1249 GFP_KERNEL);
1250 if (!env->line_cards[i])
1251 goto kzalloc_err;
1252
1253 /* Firmware defaults to high power mode policy where modules
1254 * are transitioned to high power mode following plug-in.
1255 */
1256 for (j = 0; j < env->max_module_count; j++) {
1257 module_info = &env->line_cards[i]->module_info[j];
1258 module_info->power_mode_policy =
1259 ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH;
1260 }
1261 }
1262
1263 return 0;
1264
1265kzalloc_err:
1266 for (i--; i >= 0; i--)
1267 kfree(env->line_cards[i]);
1268 return -ENOMEM;
1269}
1270
1271static void mlxsw_env_line_cards_free(struct mlxsw_env *env)
1272{
1273 int i = env->num_of_slots;
1274
1275 for (i--; i >= 0; i--)
1276 kfree(env->line_cards[i]);
1277}
1278
1279static int
1280mlxsw_env_module_event_enable(struct mlxsw_env *mlxsw_env, u8 slot_index)
1281{
1282 int err;
1283
1284 err = mlxsw_env_module_oper_state_event_enable(mlxsw_env->core,
1285 slot_index);
1286 if (err)
1287 return err;
1288
1289 err = mlxsw_env_module_temp_event_enable(mlxsw_env->core, slot_index);
1290 if (err)
1291 return err;
1292
1293 return 0;
1294}
1295
1296static void
1297mlxsw_env_module_event_disable(struct mlxsw_env *mlxsw_env, u8 slot_index)
1298{
1299}
1300
1301static int
1302mlxsw_env_module_type_set(struct mlxsw_core *mlxsw_core, u8 slot_index)
1303{
1304 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1305 int i;
1306
1307 for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
1308 struct mlxsw_env_module_info *module_info;
1309 char pmtm_pl[MLXSW_REG_PMTM_LEN];
1310 int err;
1311
1312 mlxsw_reg_pmtm_pack(pmtm_pl, slot_index, i);
1313 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtm), pmtm_pl);
1314 if (err)
1315 return err;
1316
1317 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index,
1318 i);
1319 module_info->type = mlxsw_reg_pmtm_module_type_get(pmtm_pl);
1320 }
1321
1322 return 0;
1323}
1324
1325static void
1326mlxsw_env_linecard_modules_power_mode_apply(struct mlxsw_core *mlxsw_core,
1327 struct mlxsw_env *env,
1328 u8 slot_index)
1329{
1330 int i;
1331
1332 for (i = 0; i < env->line_cards[slot_index]->module_count; i++) {
1333 enum ethtool_module_power_mode_policy policy;
1334 struct mlxsw_env_module_info *module_info;
1335 struct netlink_ext_ack extack;
1336 int err;
1337
1338 module_info = &env->line_cards[slot_index]->module_info[i];
1339 policy = module_info->power_mode_policy;
1340 err = mlxsw_env_set_module_power_mode_apply(mlxsw_core,
1341 slot_index, i,
1342 policy, &extack);
1343 if (err)
1344 dev_err(env->bus_info->dev, "%s\n", extack._msg);
1345 }
1346}
1347
1348static void
1349mlxsw_env_got_active(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv)
1350{
1351 struct mlxsw_env *mlxsw_env = priv;
1352 char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1353 int err;
1354
1355 mutex_lock(&mlxsw_env->line_cards_lock);
1356 if (__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
1357 goto out_unlock;
1358
1359 mlxsw_reg_mgpir_pack(mgpir_pl, slot_index);
1360 err = mlxsw_reg_query(mlxsw_env->core, MLXSW_REG(mgpir), mgpir_pl);
1361 if (err)
1362 goto out_unlock;
1363
1364 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,
1365 &mlxsw_env->line_cards[slot_index]->module_count,
1366 NULL);
1367
1368 err = mlxsw_env_module_event_enable(mlxsw_env, slot_index);
1369 if (err) {
1370 dev_err(mlxsw_env->bus_info->dev, "Failed to enable port module events for line card in slot %d\n",
1371 slot_index);
1372 goto err_mlxsw_env_module_event_enable;
1373 }
1374 err = mlxsw_env_module_type_set(mlxsw_env->core, slot_index);
1375 if (err) {
1376 dev_err(mlxsw_env->bus_info->dev, "Failed to set modules' type for line card in slot %d\n",
1377 slot_index);
1378 goto err_type_set;
1379 }
1380
1381 mlxsw_env->line_cards[slot_index]->active = true;
1382 /* Apply power mode policy. */
1383 mlxsw_env_linecard_modules_power_mode_apply(mlxsw_core, mlxsw_env,
1384 slot_index);
1385 mutex_unlock(&mlxsw_env->line_cards_lock);
1386
1387 return;
1388
1389err_type_set:
1390 mlxsw_env_module_event_disable(mlxsw_env, slot_index);
1391err_mlxsw_env_module_event_enable:
1392out_unlock:
1393 mutex_unlock(&mlxsw_env->line_cards_lock);
1394}
1395
1396static void
1397mlxsw_env_got_inactive(struct mlxsw_core *mlxsw_core, u8 slot_index,
1398 void *priv)
1399{
1400 struct mlxsw_env *mlxsw_env = priv;
1401
1402 mutex_lock(&mlxsw_env->line_cards_lock);
1403 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
1404 goto out_unlock;
1405 mlxsw_env->line_cards[slot_index]->active = false;
1406 mlxsw_env_module_event_disable(mlxsw_env, slot_index);
1407 mlxsw_env->line_cards[slot_index]->module_count = 0;
1408out_unlock:
1409 mutex_unlock(&mlxsw_env->line_cards_lock);
1410}
1411
1412static struct mlxsw_linecards_event_ops mlxsw_env_event_ops = {
1413 .got_active = mlxsw_env_got_active,
1414 .got_inactive = mlxsw_env_got_inactive,
1415};
1416
1417static void mlxsw_env_max_module_eeprom_len_query(struct mlxsw_env *mlxsw_env)
1418{
1419 char mcam_pl[MLXSW_REG_MCAM_LEN];
1420 bool mcia_128b_supported = false;
1421 int err;
1422
1423 mlxsw_reg_mcam_pack(mcam_pl,
1424 MLXSW_REG_MCAM_FEATURE_GROUP_ENHANCED_FEATURES);
1425 err = mlxsw_reg_query(mlxsw_env->core, MLXSW_REG(mcam), mcam_pl);
1426 if (!err)
1427 mlxsw_reg_mcam_unpack(mcam_pl, MLXSW_REG_MCAM_MCIA_128B,
1428 &mcia_128b_supported);
1429
1430 mlxsw_env->max_eeprom_len = mcia_128b_supported ? 128 : 48;
1431}
1432
1433int mlxsw_env_init(struct mlxsw_core *mlxsw_core,
1434 const struct mlxsw_bus_info *bus_info,
1435 struct mlxsw_env **p_env)
1436{
1437 u8 module_count, num_of_slots, max_module_count;
1438 char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1439 struct mlxsw_env *env;
1440 int err;
1441
1442 mlxsw_reg_mgpir_pack(mgpir_pl, 0);
1443 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
1444 if (err)
1445 return err;
1446
1447 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &module_count,
1448 &num_of_slots);
1449 /* If the system is modular, get the maximum number of modules per-slot.
1450 * Otherwise, get the maximum number of modules on the main board.
1451 */
1452 max_module_count = num_of_slots ?
1453 mlxsw_reg_mgpir_max_modules_per_slot_get(mgpir_pl) :
1454 module_count;
1455
1456 env = kzalloc(struct_size(env, line_cards, num_of_slots + 1),
1457 GFP_KERNEL);
1458 if (!env)
1459 return -ENOMEM;
1460
1461 env->core = mlxsw_core;
1462 env->bus_info = bus_info;
1463 env->num_of_slots = num_of_slots + 1;
1464 env->max_module_count = max_module_count;
1465 err = mlxsw_env_line_cards_alloc(env);
1466 if (err)
1467 goto err_mlxsw_env_line_cards_alloc;
1468
1469 mutex_init(&env->line_cards_lock);
1470 *p_env = env;
1471
1472 err = mlxsw_linecards_event_ops_register(env->core,
1473 &mlxsw_env_event_ops, env);
1474 if (err)
1475 goto err_linecards_event_ops_register;
1476
1477 err = mlxsw_env_temp_warn_event_register(mlxsw_core);
1478 if (err)
1479 goto err_temp_warn_event_register;
1480
1481 err = mlxsw_env_module_plug_event_register(mlxsw_core);
1482 if (err)
1483 goto err_module_plug_event_register;
1484
1485 /* Set 'module_count' only for main board. Actual count for line card
1486 * is to be set after line card is activated.
1487 */
1488 env->line_cards[0]->module_count = num_of_slots ? 0 : module_count;
1489 /* Enable events only for main board. Line card events are to be
1490 * configured only after line card is activated. Before that, access to
1491 * modules on line cards is not allowed.
1492 */
1493 err = mlxsw_env_module_event_enable(env, 0);
1494 if (err)
1495 goto err_mlxsw_env_module_event_enable;
1496
1497 err = mlxsw_env_module_type_set(mlxsw_core, 0);
1498 if (err)
1499 goto err_type_set;
1500
1501 mlxsw_env_max_module_eeprom_len_query(env);
1502 env->line_cards[0]->active = true;
1503
1504 return 0;
1505
1506err_type_set:
1507 mlxsw_env_module_event_disable(env, 0);
1508err_mlxsw_env_module_event_enable:
1509 mlxsw_env_module_plug_event_unregister(env);
1510err_module_plug_event_register:
1511 mlxsw_env_temp_warn_event_unregister(env);
1512err_temp_warn_event_register:
1513 mlxsw_linecards_event_ops_unregister(env->core,
1514 &mlxsw_env_event_ops, env);
1515err_linecards_event_ops_register:
1516 mutex_destroy(&env->line_cards_lock);
1517 mlxsw_env_line_cards_free(env);
1518err_mlxsw_env_line_cards_alloc:
1519 kfree(env);
1520 return err;
1521}
1522
1523void mlxsw_env_fini(struct mlxsw_env *env)
1524{
1525 env->line_cards[0]->active = false;
1526 mlxsw_env_module_event_disable(env, 0);
1527 mlxsw_env_module_plug_event_unregister(env);
1528 /* Make sure there is no more event work scheduled. */
1529 mlxsw_core_flush_owq();
1530 mlxsw_env_temp_warn_event_unregister(env);
1531 mlxsw_linecards_event_ops_unregister(env->core,
1532 &mlxsw_env_event_ops, env);
1533 mutex_destroy(&env->line_cards_lock);
1534 mlxsw_env_line_cards_free(env);
1535 kfree(env);
1536}
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
13static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id,
14 bool *qsfp)
15{
16 char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
17 char mcia_pl[MLXSW_REG_MCIA_LEN];
18 u8 ident;
19 int err;
20
21 mlxsw_reg_mcia_pack(mcia_pl, id, 0, MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1,
22 MLXSW_REG_MCIA_I2C_ADDR_LOW);
23 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
24 if (err)
25 return err;
26 mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
27 ident = eeprom_tmp[0];
28 switch (ident) {
29 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
30 *qsfp = false;
31 break;
32 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP: /* fall-through */
33 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
34 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28: /* fall-through */
35 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
36 *qsfp = true;
37 break;
38 default:
39 return -EINVAL;
40 }
41
42 return 0;
43}
44
45static int
46mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, int module,
47 u16 offset, u16 size, void *data,
48 unsigned int *p_read_size)
49{
50 char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
51 char mcia_pl[MLXSW_REG_MCIA_LEN];
52 u16 i2c_addr;
53 int status;
54 int err;
55
56 size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
57
58 if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
59 offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
60 /* Cross pages read, read until offset 256 in low page */
61 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
62
63 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
64 if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
65 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
66 offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
67 }
68
69 mlxsw_reg_mcia_pack(mcia_pl, module, 0, 0, offset, size, i2c_addr);
70
71 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
72 if (err)
73 return err;
74
75 status = mlxsw_reg_mcia_status_get(mcia_pl);
76 if (status)
77 return -EIO;
78
79 mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
80 memcpy(data, eeprom_tmp, size);
81 *p_read_size = size;
82
83 return 0;
84}
85
86int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
87 int off, int *temp)
88{
89 char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE];
90 union {
91 u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];
92 u16 temp;
93 } temp_thresh;
94 char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
95 char mtmp_pl[MLXSW_REG_MTMP_LEN];
96 unsigned int module_temp;
97 bool qsfp;
98 int err;
99
100 mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module,
101 false, false);
102 err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);
103 if (err)
104 return err;
105 mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, NULL);
106 if (!module_temp) {
107 *temp = 0;
108 return 0;
109 }
110
111 /* Read Free Side Device Temperature Thresholds from page 03h
112 * (MSB at lower byte address).
113 * Bytes:
114 * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);
115 * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);
116 * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);
117 * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);
118 */
119
120 /* Validate module identifier value. */
121 err = mlxsw_env_validate_cable_ident(core, module, &qsfp);
122 if (err)
123 return err;
124
125 if (qsfp)
126 mlxsw_reg_mcia_pack(mcia_pl, module, 0,
127 MLXSW_REG_MCIA_TH_PAGE_NUM,
128 MLXSW_REG_MCIA_TH_PAGE_OFF + off,
129 MLXSW_REG_MCIA_TH_ITEM_SIZE,
130 MLXSW_REG_MCIA_I2C_ADDR_LOW);
131 else
132 mlxsw_reg_mcia_pack(mcia_pl, module, 0,
133 MLXSW_REG_MCIA_PAGE0_LO,
134 off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
135 MLXSW_REG_MCIA_I2C_ADDR_HIGH);
136
137 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
138 if (err)
139 return err;
140
141 mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
142 memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);
143 *temp = temp_thresh.temp * 1000;
144
145 return 0;
146}
147
148int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module,
149 struct ethtool_modinfo *modinfo)
150{
151 u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
152 u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
153 u8 module_rev_id, module_id, diag_mon;
154 unsigned int read_size;
155 int err;
156
157 err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 0, offset,
158 module_info, &read_size);
159 if (err)
160 return err;
161
162 if (read_size < offset)
163 return -EIO;
164
165 module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
166 module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
167
168 switch (module_id) {
169 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
170 modinfo->type = ETH_MODULE_SFF_8436;
171 modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
172 break;
173 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */
174 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
175 if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
176 module_rev_id >=
177 MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
178 modinfo->type = ETH_MODULE_SFF_8636;
179 modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
180 } else {
181 modinfo->type = ETH_MODULE_SFF_8436;
182 modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
183 }
184 break;
185 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
186 /* Verify if transceiver provides diagnostic monitoring page */
187 err = mlxsw_env_query_module_eeprom(mlxsw_core, module,
188 SFP_DIAGMON, 1, &diag_mon,
189 &read_size);
190 if (err)
191 return err;
192
193 if (read_size < 1)
194 return -EIO;
195
196 modinfo->type = ETH_MODULE_SFF_8472;
197 if (diag_mon)
198 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
199 else
200 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;
201 break;
202 default:
203 return -EINVAL;
204 }
205
206 return 0;
207}
208EXPORT_SYMBOL(mlxsw_env_get_module_info);
209
210int mlxsw_env_get_module_eeprom(struct net_device *netdev,
211 struct mlxsw_core *mlxsw_core, int module,
212 struct ethtool_eeprom *ee, u8 *data)
213{
214 int offset = ee->offset;
215 unsigned int read_size;
216 int i = 0;
217 int err;
218
219 if (!ee->len)
220 return -EINVAL;
221
222 memset(data, 0, ee->len);
223
224 while (i < ee->len) {
225 err = mlxsw_env_query_module_eeprom(mlxsw_core, module, offset,
226 ee->len - i, data + i,
227 &read_size);
228 if (err) {
229 netdev_err(netdev, "Eeprom query failed\n");
230 return err;
231 }
232
233 i += read_size;
234 offset += read_size;
235 }
236
237 return 0;
238}
239EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);