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(lock: &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(mlxsw_core: 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(mlxsw_core: 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(mlxsw_core: 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(lock: &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, module: id);
112 if (err)
113 return err;
114
115 mlxsw_reg_mcia_pack(payload: mcia_pl, slot_index, module: id,
116 MLXSW_REG_MCIA_PAGE0_LO_OFF, device_addr: 0, size: 1,
117 MLXSW_REG_MCIA_I2C_ADDR_LOW);
118 err = mlxsw_reg_query(mlxsw_core: core, MLXSW_REG(mcia), payload: mcia_pl);
119 if (err)
120 return err;
121 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(buf: 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(payload: mcia_pl, slot_index, module, page_number: page, device_addr: offset, size,
190 i2c_device_addr: i2c_addr);
191
192 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), payload: mcia_pl);
193 if (err)
194 return err;
195
196 status = mlxsw_reg_mcia_status_get(buf: mcia_pl);
197 if (status)
198 return -EIO;
199
200 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(buf: 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(payload: mtmp_pl, slot_index,
224 MLXSW_REG_MTMP_MODULE_INDEX_MIN + module, max_temp_enable: false,
225 max_temp_reset: false);
226 err = mlxsw_reg_query(mlxsw_core: core, MLXSW_REG(mtmp), payload: mtmp_pl);
227 if (err)
228 return err;
229 mlxsw_reg_mtmp_unpack(payload: mtmp_pl, p_temp: &module_temp, NULL, p_temp_hi: &module_crit,
230 p_max_oper_temp: &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, id: module, qsfp: &qsfp,
255 cmis: &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(payload: mcia_pl, slot_index, module, page_number: 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(payload: mcia_pl, slot_index, module,
273 MLXSW_REG_MCIA_PAGE0_LO,
274 device_addr: off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
275 MLXSW_REG_MCIA_I2C_ADDR_HIGH);
276 }
277
278 err = mlxsw_reg_query(mlxsw_core: core, MLXSW_REG(mcia), payload: mcia_pl);
279 if (err)
280 return err;
281
282 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(buf: 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(dev: netdev, format: "Cannot read EEPROM of module on an inactive line card\n");
302 return -EIO;
303 }
304
305 err = mlxsw_env_validate_module_type(core: mlxsw_core, slot_index, module);
306 if (err) {
307 netdev_err(dev: netdev,
308 format: "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, offset: 0,
313 size: offset, data: module_info, qsfp: false,
314 p_read_size: &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, offset: SFP_DIAGMON, size: 1,
345 data: &diag_mon, qsfp: false,
346 p_read_size: &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(dev: netdev, format: "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(core: mlxsw_core, slot_index, id: module,
407 qsfp: &qsfp, cmis: &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 size: ee->len - i, data: data + i,
415 qsfp, p_read_size: &read_size);
416 if (err) {
417 netdev_err(dev: netdev, format: "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(buf: 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(core: 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(payload: mcia_pl, slot_index, module, page_number: page->page,
493 device_addr: device_addr + bytes_read, size,
494 i2c_device_addr: page->i2c_address);
495 mlxsw_reg_mcia_bank_number_set(buf: mcia_pl, val: page->bank);
496
497 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), payload: 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(buf: 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
516static int mlxsw_env_module_reset(struct mlxsw_core *mlxsw_core, u8 slot_index,
517 u8 module)
518{
519 char pmaos_pl[MLXSW_REG_PMAOS_LEN];
520
521 mlxsw_reg_pmaos_pack(payload: pmaos_pl, slot_index, module);
522 mlxsw_reg_pmaos_rst_set(buf: pmaos_pl, val: true);
523
524 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), payload: pmaos_pl);
525}
526
527int mlxsw_env_reset_module(struct net_device *netdev,
528 struct mlxsw_core *mlxsw_core, u8 slot_index,
529 u8 module, u32 *flags)
530{
531 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
532 struct mlxsw_env_module_info *module_info;
533 u32 req = *flags;
534 int err;
535
536 if (!(req & ETH_RESET_PHY) &&
537 !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT)))
538 return 0;
539
540 if (!mlxsw_env_linecard_is_active(mlxsw_env, slot_index)) {
541 netdev_err(dev: netdev, format: "Cannot reset module on an inactive line card\n");
542 return -EIO;
543 }
544
545 mutex_lock(&mlxsw_env->line_cards_lock);
546
547 err = __mlxsw_env_validate_module_type(core: mlxsw_core, slot_index, module);
548 if (err) {
549 netdev_err(dev: netdev, format: "Reset module is not supported on port module type\n");
550 goto out;
551 }
552
553 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
554 if (module_info->num_ports_up) {
555 netdev_err(dev: netdev, format: "Cannot reset module when ports using it are administratively up\n");
556 err = -EINVAL;
557 goto out;
558 }
559
560 if (module_info->num_ports_mapped > 1 &&
561 !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) {
562 netdev_err(dev: netdev, format: "Cannot reset module without \"phy-shared\" flag when shared by multiple ports\n");
563 err = -EINVAL;
564 goto out;
565 }
566
567 err = mlxsw_env_module_reset(mlxsw_core, slot_index, module);
568 if (err) {
569 netdev_err(dev: netdev, format: "Failed to reset module\n");
570 goto out;
571 }
572
573 *flags &= ~(ETH_RESET_PHY | (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT));
574
575out:
576 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
577 return err;
578}
579EXPORT_SYMBOL(mlxsw_env_reset_module);
580
581int
582mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
583 u8 module,
584 struct ethtool_module_power_mode_params *params,
585 struct netlink_ext_ack *extack)
586{
587 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
588 struct mlxsw_env_module_info *module_info;
589 char mcion_pl[MLXSW_REG_MCION_LEN];
590 u32 status_bits;
591 int err = 0;
592
593 mutex_lock(&mlxsw_env->line_cards_lock);
594
595 err = __mlxsw_env_validate_module_type(core: mlxsw_core, slot_index, module);
596 if (err) {
597 NL_SET_ERR_MSG_MOD(extack, "Power mode is not supported on port module type");
598 goto out;
599 }
600
601 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
602 params->policy = module_info->power_mode_policy;
603
604 /* Avoid accessing an inactive line card, as it will result in an error. */
605 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
606 goto out;
607
608 mlxsw_reg_mcion_pack(payload: mcion_pl, slot_index, module);
609 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcion), payload: mcion_pl);
610 if (err) {
611 NL_SET_ERR_MSG_MOD(extack, "Failed to retrieve module's power mode");
612 goto out;
613 }
614
615 status_bits = mlxsw_reg_mcion_module_status_bits_get(buf: mcion_pl);
616 if (!(status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_PRESENT_MASK))
617 goto out;
618
619 if (status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_LOW_POWER_MASK)
620 params->mode = ETHTOOL_MODULE_POWER_MODE_LOW;
621 else
622 params->mode = ETHTOOL_MODULE_POWER_MODE_HIGH;
623
624out:
625 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
626 return err;
627}
628EXPORT_SYMBOL(mlxsw_env_get_module_power_mode);
629
630static int mlxsw_env_module_enable_set(struct mlxsw_core *mlxsw_core,
631 u8 slot_index, u8 module, bool enable)
632{
633 enum mlxsw_reg_pmaos_admin_status admin_status;
634 char pmaos_pl[MLXSW_REG_PMAOS_LEN];
635
636 mlxsw_reg_pmaos_pack(payload: pmaos_pl, slot_index, module);
637 admin_status = enable ? MLXSW_REG_PMAOS_ADMIN_STATUS_ENABLED :
638 MLXSW_REG_PMAOS_ADMIN_STATUS_DISABLED;
639 mlxsw_reg_pmaos_admin_status_set(buf: pmaos_pl, val: admin_status);
640 mlxsw_reg_pmaos_ase_set(buf: pmaos_pl, val: true);
641
642 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), payload: pmaos_pl);
643}
644
645static int mlxsw_env_module_low_power_set(struct mlxsw_core *mlxsw_core,
646 u8 slot_index, u8 module,
647 bool low_power)
648{
649 u16 eeprom_override_mask, eeprom_override;
650 char pmmp_pl[MLXSW_REG_PMMP_LEN];
651
652 mlxsw_reg_pmmp_pack(payload: pmmp_pl, slot_index, module);
653 mlxsw_reg_pmmp_sticky_set(buf: pmmp_pl, val: true);
654 /* Mask all the bits except low power mode. */
655 eeprom_override_mask = ~MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK;
656 mlxsw_reg_pmmp_eeprom_override_mask_set(buf: pmmp_pl, val: eeprom_override_mask);
657 eeprom_override = low_power ? MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK :
658 0;
659 mlxsw_reg_pmmp_eeprom_override_set(buf: pmmp_pl, val: eeprom_override);
660
661 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmmp), payload: pmmp_pl);
662}
663
664static int __mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core,
665 u8 slot_index, u8 module,
666 bool low_power,
667 struct netlink_ext_ack *extack)
668{
669 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
670 int err;
671
672 /* Avoid accessing an inactive line card, as it will result in an error.
673 * Cached configuration will be applied by mlxsw_env_got_active() when
674 * line card becomes active.
675 */
676 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
677 return 0;
678
679 err = mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, enable: false);
680 if (err) {
681 NL_SET_ERR_MSG_MOD(extack, "Failed to disable module");
682 return err;
683 }
684
685 err = mlxsw_env_module_low_power_set(mlxsw_core, slot_index, module,
686 low_power);
687 if (err) {
688 NL_SET_ERR_MSG_MOD(extack, "Failed to set module's power mode");
689 goto err_module_low_power_set;
690 }
691
692 err = mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, enable: true);
693 if (err) {
694 NL_SET_ERR_MSG_MOD(extack, "Failed to enable module");
695 goto err_module_enable_set;
696 }
697
698 return 0;
699
700err_module_enable_set:
701 mlxsw_env_module_low_power_set(mlxsw_core, slot_index, module,
702 low_power: !low_power);
703err_module_low_power_set:
704 mlxsw_env_module_enable_set(mlxsw_core, slot_index, module, enable: true);
705 return err;
706}
707
708static int
709mlxsw_env_set_module_power_mode_apply(struct mlxsw_core *mlxsw_core,
710 u8 slot_index, u8 module,
711 enum ethtool_module_power_mode_policy policy,
712 struct netlink_ext_ack *extack)
713{
714 struct mlxsw_env_module_info *module_info;
715 bool low_power;
716 int err = 0;
717
718 err = __mlxsw_env_validate_module_type(core: mlxsw_core, slot_index, module);
719 if (err) {
720 NL_SET_ERR_MSG_MOD(extack,
721 "Power mode set is not supported on port module type");
722 goto out;
723 }
724
725 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
726 if (module_info->power_mode_policy == policy)
727 goto out;
728
729 /* If any ports are up, we are already in high power mode. */
730 if (module_info->num_ports_up)
731 goto out_set_policy;
732
733 low_power = policy == ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO;
734 err = __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module,
735 low_power, extack);
736 if (err)
737 goto out;
738
739out_set_policy:
740 module_info->power_mode_policy = policy;
741out:
742 return err;
743}
744
745int
746mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 slot_index,
747 u8 module,
748 enum ethtool_module_power_mode_policy policy,
749 struct netlink_ext_ack *extack)
750{
751 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
752 int err;
753
754 if (policy != ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH &&
755 policy != ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO) {
756 NL_SET_ERR_MSG_MOD(extack, "Unsupported power mode policy");
757 return -EOPNOTSUPP;
758 }
759
760 mutex_lock(&mlxsw_env->line_cards_lock);
761 err = mlxsw_env_set_module_power_mode_apply(mlxsw_core, slot_index,
762 module, policy, extack);
763 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
764
765 return err;
766}
767EXPORT_SYMBOL(mlxsw_env_set_module_power_mode);
768
769static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core,
770 u8 slot_index, u8 module,
771 bool *p_has_temp_sensor)
772{
773 char mtbr_pl[MLXSW_REG_MTBR_LEN];
774 u16 temp;
775 int err;
776
777 mlxsw_reg_mtbr_pack(payload: mtbr_pl, slot_index,
778 MLXSW_REG_MTBR_BASE_MODULE_INDEX + module);
779 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtbr), payload: mtbr_pl);
780 if (err)
781 return err;
782
783 mlxsw_reg_mtbr_temp_unpack(payload: mtbr_pl, rec_ind: 0, p_temp: &temp, NULL);
784
785 switch (temp) {
786 case MLXSW_REG_MTBR_BAD_SENS_INFO:
787 case MLXSW_REG_MTBR_NO_CONN:
788 case MLXSW_REG_MTBR_NO_TEMP_SENS:
789 case MLXSW_REG_MTBR_INDEX_NA:
790 *p_has_temp_sensor = false;
791 break;
792 default:
793 *p_has_temp_sensor = temp ? true : false;
794 }
795 return 0;
796}
797
798static int
799mlxsw_env_temp_event_set(struct mlxsw_core *mlxsw_core, u8 slot_index,
800 u16 sensor_index, bool enable)
801{
802 char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0};
803 enum mlxsw_reg_mtmp_tee tee;
804 int err, threshold_hi;
805
806 mlxsw_reg_mtmp_slot_index_set(buf: mtmp_pl, val: slot_index);
807 mlxsw_reg_mtmp_sensor_index_set(buf: mtmp_pl, val: sensor_index);
808 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtmp), payload: mtmp_pl);
809 if (err)
810 return err;
811
812 if (enable) {
813 err = mlxsw_env_module_temp_thresholds_get(core: mlxsw_core,
814 slot_index,
815 module: sensor_index -
816 MLXSW_REG_MTMP_MODULE_INDEX_MIN,
817 off: SFP_TEMP_HIGH_WARN,
818 temp: &threshold_hi);
819 /* In case it is not possible to query the module's threshold,
820 * use the default value.
821 */
822 if (err)
823 threshold_hi = MLXSW_REG_MTMP_THRESH_HI;
824 else
825 /* mlxsw_env_module_temp_thresholds_get() multiplies
826 * Celsius degrees by 1000 whereas MTMP expects
827 * temperature in 0.125 Celsius degrees units.
828 * Convert threshold_hi to correct units.
829 */
830 threshold_hi = threshold_hi / 1000 * 8;
831
832 mlxsw_reg_mtmp_temperature_threshold_hi_set(buf: mtmp_pl, val: threshold_hi);
833 mlxsw_reg_mtmp_temperature_threshold_lo_set(buf: mtmp_pl, val: threshold_hi -
834 MLXSW_REG_MTMP_HYSTERESIS_TEMP);
835 }
836 tee = enable ? MLXSW_REG_MTMP_TEE_GENERATE_EVENT : MLXSW_REG_MTMP_TEE_NO_EVENT;
837 mlxsw_reg_mtmp_tee_set(buf: mtmp_pl, val: tee);
838 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtmp), payload: mtmp_pl);
839}
840
841static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core,
842 u8 slot_index)
843{
844 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
845 int i, err, sensor_index;
846 bool has_temp_sensor;
847
848 for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
849 err = mlxsw_env_module_has_temp_sensor(mlxsw_core, slot_index,
850 module: i, p_has_temp_sensor: &has_temp_sensor);
851 if (err)
852 return err;
853
854 if (!has_temp_sensor)
855 continue;
856
857 sensor_index = i + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
858 err = mlxsw_env_temp_event_set(mlxsw_core, slot_index,
859 sensor_index, enable: true);
860 if (err)
861 return err;
862 }
863
864 return 0;
865}
866
867struct mlxsw_env_module_temp_warn_event {
868 struct mlxsw_env *mlxsw_env;
869 char mtwe_pl[MLXSW_REG_MTWE_LEN];
870 struct work_struct work;
871};
872
873static void mlxsw_env_mtwe_event_work(struct work_struct *work)
874{
875 struct mlxsw_env_module_temp_warn_event *event;
876 struct mlxsw_env_module_info *module_info;
877 struct mlxsw_env *mlxsw_env;
878 int i, sensor_warning;
879 bool is_overheat;
880
881 event = container_of(work, struct mlxsw_env_module_temp_warn_event,
882 work);
883 mlxsw_env = event->mlxsw_env;
884
885 for (i = 0; i < mlxsw_env->max_module_count; i++) {
886 /* 64-127 of sensor_index are mapped to the port modules
887 * sequentially (module 0 is mapped to sensor_index 64,
888 * module 1 to sensor_index 65 and so on)
889 */
890 sensor_warning =
891 mlxsw_reg_mtwe_sensor_warning_get(buf: event->mtwe_pl,
892 index: i + MLXSW_REG_MTMP_MODULE_INDEX_MIN);
893 mutex_lock(&mlxsw_env->line_cards_lock);
894 /* MTWE only supports main board. */
895 module_info = mlxsw_env_module_info_get(mlxsw_core: mlxsw_env->core, slot_index: 0, module: i);
896 is_overheat = module_info->is_overheat;
897
898 if ((is_overheat && sensor_warning) ||
899 (!is_overheat && !sensor_warning)) {
900 /* Current state is "warning" and MTWE still reports
901 * warning OR current state in "no warning" and MTWE
902 * does not report warning.
903 */
904 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
905 continue;
906 } else if (is_overheat && !sensor_warning) {
907 /* MTWE reports "no warning", turn is_overheat off.
908 */
909 module_info->is_overheat = false;
910 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
911 } else {
912 /* Current state is "no warning" and MTWE reports
913 * "warning", increase the counter and turn is_overheat
914 * on.
915 */
916 module_info->is_overheat = true;
917 module_info->module_overheat_counter++;
918 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
919 }
920 }
921
922 kfree(objp: event);
923}
924
925static void
926mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info *reg, char *mtwe_pl,
927 void *priv)
928{
929 struct mlxsw_env_module_temp_warn_event *event;
930 struct mlxsw_env *mlxsw_env = priv;
931
932 event = kmalloc(size: sizeof(*event), GFP_ATOMIC);
933 if (!event)
934 return;
935
936 event->mlxsw_env = mlxsw_env;
937 memcpy(event->mtwe_pl, mtwe_pl, MLXSW_REG_MTWE_LEN);
938 INIT_WORK(&event->work, mlxsw_env_mtwe_event_work);
939 mlxsw_core_schedule_work(work: &event->work);
940}
941
942static const struct mlxsw_listener mlxsw_env_temp_warn_listener =
943 MLXSW_CORE_EVENTL(mlxsw_env_mtwe_listener_func, MTWE);
944
945static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core)
946{
947 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
948
949 return mlxsw_core_trap_register(mlxsw_core,
950 listener: &mlxsw_env_temp_warn_listener,
951 priv: mlxsw_env);
952}
953
954static void mlxsw_env_temp_warn_event_unregister(struct mlxsw_env *mlxsw_env)
955{
956 mlxsw_core_trap_unregister(mlxsw_core: mlxsw_env->core,
957 listener: &mlxsw_env_temp_warn_listener, priv: mlxsw_env);
958}
959
960struct mlxsw_env_module_plug_unplug_event {
961 struct mlxsw_env *mlxsw_env;
962 u8 slot_index;
963 u8 module;
964 struct work_struct work;
965};
966
967static void mlxsw_env_pmpe_event_work(struct work_struct *work)
968{
969 struct mlxsw_env_module_plug_unplug_event *event;
970 struct mlxsw_env_module_info *module_info;
971 struct mlxsw_env *mlxsw_env;
972 bool has_temp_sensor;
973 u16 sensor_index;
974 int err;
975
976 event = container_of(work, struct mlxsw_env_module_plug_unplug_event,
977 work);
978 mlxsw_env = event->mlxsw_env;
979
980 mutex_lock(&mlxsw_env->line_cards_lock);
981 module_info = mlxsw_env_module_info_get(mlxsw_core: mlxsw_env->core,
982 slot_index: event->slot_index,
983 module: event->module);
984 module_info->is_overheat = false;
985 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
986
987 err = mlxsw_env_module_has_temp_sensor(mlxsw_core: mlxsw_env->core,
988 slot_index: event->slot_index,
989 module: event->module,
990 p_has_temp_sensor: &has_temp_sensor);
991 /* Do not disable events on modules without sensors or faulty sensors
992 * because FW returns errors.
993 */
994 if (err)
995 goto out;
996
997 if (!has_temp_sensor)
998 goto out;
999
1000 sensor_index = event->module + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
1001 mlxsw_env_temp_event_set(mlxsw_core: mlxsw_env->core, slot_index: event->slot_index,
1002 sensor_index, enable: true);
1003
1004out:
1005 kfree(objp: event);
1006}
1007
1008static void
1009mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl,
1010 void *priv)
1011{
1012 u8 slot_index = mlxsw_reg_pmpe_slot_index_get(buf: pmpe_pl);
1013 struct mlxsw_env_module_plug_unplug_event *event;
1014 enum mlxsw_reg_pmpe_module_status module_status;
1015 u8 module = mlxsw_reg_pmpe_module_get(buf: pmpe_pl);
1016 struct mlxsw_env *mlxsw_env = priv;
1017
1018 if (WARN_ON_ONCE(module >= mlxsw_env->max_module_count ||
1019 slot_index >= mlxsw_env->num_of_slots))
1020 return;
1021
1022 module_status = mlxsw_reg_pmpe_module_status_get(buf: pmpe_pl);
1023 if (module_status != MLXSW_REG_PMPE_MODULE_STATUS_PLUGGED_ENABLED)
1024 return;
1025
1026 event = kmalloc(size: sizeof(*event), GFP_ATOMIC);
1027 if (!event)
1028 return;
1029
1030 event->mlxsw_env = mlxsw_env;
1031 event->slot_index = slot_index;
1032 event->module = module;
1033 INIT_WORK(&event->work, mlxsw_env_pmpe_event_work);
1034 mlxsw_core_schedule_work(work: &event->work);
1035}
1036
1037static const struct mlxsw_listener mlxsw_env_module_plug_listener =
1038 MLXSW_CORE_EVENTL(mlxsw_env_pmpe_listener_func, PMPE);
1039
1040static int
1041mlxsw_env_module_plug_event_register(struct mlxsw_core *mlxsw_core)
1042{
1043 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1044
1045 return mlxsw_core_trap_register(mlxsw_core,
1046 listener: &mlxsw_env_module_plug_listener,
1047 priv: mlxsw_env);
1048}
1049
1050static void
1051mlxsw_env_module_plug_event_unregister(struct mlxsw_env *mlxsw_env)
1052{
1053 mlxsw_core_trap_unregister(mlxsw_core: mlxsw_env->core,
1054 listener: &mlxsw_env_module_plug_listener,
1055 priv: mlxsw_env);
1056}
1057
1058static int
1059mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core,
1060 u8 slot_index)
1061{
1062 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1063 int i, err;
1064
1065 for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
1066 char pmaos_pl[MLXSW_REG_PMAOS_LEN];
1067
1068 mlxsw_reg_pmaos_pack(payload: pmaos_pl, slot_index, module: i);
1069 mlxsw_reg_pmaos_e_set(buf: pmaos_pl,
1070 val: MLXSW_REG_PMAOS_E_GENERATE_EVENT);
1071 mlxsw_reg_pmaos_ee_set(buf: pmaos_pl, val: true);
1072 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), payload: pmaos_pl);
1073 if (err)
1074 return err;
1075 }
1076 return 0;
1077}
1078
1079int
1080mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 slot_index,
1081 u8 module, u64 *p_counter)
1082{
1083 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1084 struct mlxsw_env_module_info *module_info;
1085
1086 mutex_lock(&mlxsw_env->line_cards_lock);
1087 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1088 *p_counter = module_info->module_overheat_counter;
1089 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
1090
1091 return 0;
1092}
1093EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get);
1094
1095void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 slot_index,
1096 u8 module)
1097{
1098 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1099 struct mlxsw_env_module_info *module_info;
1100
1101 mutex_lock(&mlxsw_env->line_cards_lock);
1102 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1103 module_info->num_ports_mapped++;
1104 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
1105}
1106EXPORT_SYMBOL(mlxsw_env_module_port_map);
1107
1108void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 slot_index,
1109 u8 module)
1110{
1111 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1112 struct mlxsw_env_module_info *module_info;
1113
1114 mutex_lock(&mlxsw_env->line_cards_lock);
1115 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1116 module_info->num_ports_mapped--;
1117 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
1118}
1119EXPORT_SYMBOL(mlxsw_env_module_port_unmap);
1120
1121int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 slot_index,
1122 u8 module)
1123{
1124 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1125 struct mlxsw_env_module_info *module_info;
1126 int err = 0;
1127
1128 mutex_lock(&mlxsw_env->line_cards_lock);
1129
1130 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1131 if (module_info->power_mode_policy !=
1132 ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
1133 goto out_inc;
1134
1135 if (module_info->num_ports_up != 0)
1136 goto out_inc;
1137
1138 /* Transition to high power mode following first port using the module
1139 * being put administratively up.
1140 */
1141 err = __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module,
1142 low_power: false, NULL);
1143 if (err)
1144 goto out_unlock;
1145
1146out_inc:
1147 module_info->num_ports_up++;
1148out_unlock:
1149 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
1150 return err;
1151}
1152EXPORT_SYMBOL(mlxsw_env_module_port_up);
1153
1154void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 slot_index,
1155 u8 module)
1156{
1157 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1158 struct mlxsw_env_module_info *module_info;
1159
1160 mutex_lock(&mlxsw_env->line_cards_lock);
1161
1162 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index, module);
1163 module_info->num_ports_up--;
1164
1165 if (module_info->power_mode_policy !=
1166 ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
1167 goto out_unlock;
1168
1169 if (module_info->num_ports_up != 0)
1170 goto out_unlock;
1171
1172 /* Transition to low power mode following last port using the module
1173 * being put administratively down.
1174 */
1175 __mlxsw_env_set_module_power_mode(mlxsw_core, slot_index, module, low_power: true,
1176 NULL);
1177
1178out_unlock:
1179 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
1180}
1181EXPORT_SYMBOL(mlxsw_env_module_port_down);
1182
1183static int mlxsw_env_line_cards_alloc(struct mlxsw_env *env)
1184{
1185 struct mlxsw_env_module_info *module_info;
1186 int i, j;
1187
1188 for (i = 0; i < env->num_of_slots; i++) {
1189 env->line_cards[i] = kzalloc(struct_size(env->line_cards[i],
1190 module_info,
1191 env->max_module_count),
1192 GFP_KERNEL);
1193 if (!env->line_cards[i])
1194 goto kzalloc_err;
1195
1196 /* Firmware defaults to high power mode policy where modules
1197 * are transitioned to high power mode following plug-in.
1198 */
1199 for (j = 0; j < env->max_module_count; j++) {
1200 module_info = &env->line_cards[i]->module_info[j];
1201 module_info->power_mode_policy =
1202 ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH;
1203 }
1204 }
1205
1206 return 0;
1207
1208kzalloc_err:
1209 for (i--; i >= 0; i--)
1210 kfree(objp: env->line_cards[i]);
1211 return -ENOMEM;
1212}
1213
1214static void mlxsw_env_line_cards_free(struct mlxsw_env *env)
1215{
1216 int i = env->num_of_slots;
1217
1218 for (i--; i >= 0; i--)
1219 kfree(objp: env->line_cards[i]);
1220}
1221
1222static int
1223mlxsw_env_module_event_enable(struct mlxsw_env *mlxsw_env, u8 slot_index)
1224{
1225 int err;
1226
1227 err = mlxsw_env_module_oper_state_event_enable(mlxsw_core: mlxsw_env->core,
1228 slot_index);
1229 if (err)
1230 return err;
1231
1232 err = mlxsw_env_module_temp_event_enable(mlxsw_core: mlxsw_env->core, slot_index);
1233 if (err)
1234 return err;
1235
1236 return 0;
1237}
1238
1239static void
1240mlxsw_env_module_event_disable(struct mlxsw_env *mlxsw_env, u8 slot_index)
1241{
1242}
1243
1244static int
1245mlxsw_env_module_type_set(struct mlxsw_core *mlxsw_core, u8 slot_index)
1246{
1247 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
1248 int i;
1249
1250 for (i = 0; i < mlxsw_env->line_cards[slot_index]->module_count; i++) {
1251 struct mlxsw_env_module_info *module_info;
1252 char pmtm_pl[MLXSW_REG_PMTM_LEN];
1253 int err;
1254
1255 mlxsw_reg_pmtm_pack(payload: pmtm_pl, slot_index, module: i);
1256 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtm), payload: pmtm_pl);
1257 if (err)
1258 return err;
1259
1260 module_info = mlxsw_env_module_info_get(mlxsw_core, slot_index,
1261 module: i);
1262 module_info->type = mlxsw_reg_pmtm_module_type_get(buf: pmtm_pl);
1263 }
1264
1265 return 0;
1266}
1267
1268static void
1269mlxsw_env_linecard_modules_power_mode_apply(struct mlxsw_core *mlxsw_core,
1270 struct mlxsw_env *env,
1271 u8 slot_index)
1272{
1273 int i;
1274
1275 for (i = 0; i < env->line_cards[slot_index]->module_count; i++) {
1276 enum ethtool_module_power_mode_policy policy;
1277 struct mlxsw_env_module_info *module_info;
1278 struct netlink_ext_ack extack;
1279 int err;
1280
1281 module_info = &env->line_cards[slot_index]->module_info[i];
1282 policy = module_info->power_mode_policy;
1283 err = mlxsw_env_set_module_power_mode_apply(mlxsw_core,
1284 slot_index, module: i,
1285 policy, extack: &extack);
1286 if (err)
1287 dev_err(env->bus_info->dev, "%s\n", extack._msg);
1288 }
1289}
1290
1291static void
1292mlxsw_env_got_active(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv)
1293{
1294 struct mlxsw_env *mlxsw_env = priv;
1295 char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1296 int err;
1297
1298 mutex_lock(&mlxsw_env->line_cards_lock);
1299 if (__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
1300 goto out_unlock;
1301
1302 mlxsw_reg_mgpir_pack(payload: mgpir_pl, slot_index);
1303 err = mlxsw_reg_query(mlxsw_core: mlxsw_env->core, MLXSW_REG(mgpir), payload: mgpir_pl);
1304 if (err)
1305 goto out_unlock;
1306
1307 mlxsw_reg_mgpir_unpack(payload: mgpir_pl, NULL, NULL, NULL,
1308 num_of_modules: &mlxsw_env->line_cards[slot_index]->module_count,
1309 NULL);
1310
1311 err = mlxsw_env_module_event_enable(mlxsw_env, slot_index);
1312 if (err) {
1313 dev_err(mlxsw_env->bus_info->dev, "Failed to enable port module events for line card in slot %d\n",
1314 slot_index);
1315 goto err_mlxsw_env_module_event_enable;
1316 }
1317 err = mlxsw_env_module_type_set(mlxsw_core: mlxsw_env->core, slot_index);
1318 if (err) {
1319 dev_err(mlxsw_env->bus_info->dev, "Failed to set modules' type for line card in slot %d\n",
1320 slot_index);
1321 goto err_type_set;
1322 }
1323
1324 mlxsw_env->line_cards[slot_index]->active = true;
1325 /* Apply power mode policy. */
1326 mlxsw_env_linecard_modules_power_mode_apply(mlxsw_core, env: mlxsw_env,
1327 slot_index);
1328 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
1329
1330 return;
1331
1332err_type_set:
1333 mlxsw_env_module_event_disable(mlxsw_env, slot_index);
1334err_mlxsw_env_module_event_enable:
1335out_unlock:
1336 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
1337}
1338
1339static void
1340mlxsw_env_got_inactive(struct mlxsw_core *mlxsw_core, u8 slot_index,
1341 void *priv)
1342{
1343 struct mlxsw_env *mlxsw_env = priv;
1344
1345 mutex_lock(&mlxsw_env->line_cards_lock);
1346 if (!__mlxsw_env_linecard_is_active(mlxsw_env, slot_index))
1347 goto out_unlock;
1348 mlxsw_env->line_cards[slot_index]->active = false;
1349 mlxsw_env_module_event_disable(mlxsw_env, slot_index);
1350 mlxsw_env->line_cards[slot_index]->module_count = 0;
1351out_unlock:
1352 mutex_unlock(lock: &mlxsw_env->line_cards_lock);
1353}
1354
1355static struct mlxsw_linecards_event_ops mlxsw_env_event_ops = {
1356 .got_active = mlxsw_env_got_active,
1357 .got_inactive = mlxsw_env_got_inactive,
1358};
1359
1360static int mlxsw_env_max_module_eeprom_len_query(struct mlxsw_env *mlxsw_env)
1361{
1362 char mcam_pl[MLXSW_REG_MCAM_LEN];
1363 bool mcia_128b_supported;
1364 int err;
1365
1366 mlxsw_reg_mcam_pack(payload: mcam_pl,
1367 feat_group: MLXSW_REG_MCAM_FEATURE_GROUP_ENHANCED_FEATURES);
1368 err = mlxsw_reg_query(mlxsw_core: mlxsw_env->core, MLXSW_REG(mcam), payload: mcam_pl);
1369 if (err)
1370 return err;
1371
1372 mlxsw_reg_mcam_unpack(payload: mcam_pl, bit: MLXSW_REG_MCAM_MCIA_128B,
1373 p_mng_feature_cap_val: &mcia_128b_supported);
1374
1375 mlxsw_env->max_eeprom_len = mcia_128b_supported ? 128 : 48;
1376
1377 return 0;
1378}
1379
1380int mlxsw_env_init(struct mlxsw_core *mlxsw_core,
1381 const struct mlxsw_bus_info *bus_info,
1382 struct mlxsw_env **p_env)
1383{
1384 u8 module_count, num_of_slots, max_module_count;
1385 char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1386 struct mlxsw_env *env;
1387 int err;
1388
1389 mlxsw_reg_mgpir_pack(payload: mgpir_pl, slot_index: 0);
1390 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), payload: mgpir_pl);
1391 if (err)
1392 return err;
1393
1394 mlxsw_reg_mgpir_unpack(payload: mgpir_pl, NULL, NULL, NULL, num_of_modules: &module_count,
1395 num_of_slots: &num_of_slots);
1396 /* If the system is modular, get the maximum number of modules per-slot.
1397 * Otherwise, get the maximum number of modules on the main board.
1398 */
1399 max_module_count = num_of_slots ?
1400 mlxsw_reg_mgpir_max_modules_per_slot_get(buf: mgpir_pl) :
1401 module_count;
1402
1403 env = kzalloc(struct_size(env, line_cards, num_of_slots + 1),
1404 GFP_KERNEL);
1405 if (!env)
1406 return -ENOMEM;
1407
1408 env->core = mlxsw_core;
1409 env->bus_info = bus_info;
1410 env->num_of_slots = num_of_slots + 1;
1411 env->max_module_count = max_module_count;
1412 err = mlxsw_env_line_cards_alloc(env);
1413 if (err)
1414 goto err_mlxsw_env_line_cards_alloc;
1415
1416 mutex_init(&env->line_cards_lock);
1417 *p_env = env;
1418
1419 err = mlxsw_linecards_event_ops_register(mlxsw_core: env->core,
1420 ops: &mlxsw_env_event_ops, priv: env);
1421 if (err)
1422 goto err_linecards_event_ops_register;
1423
1424 err = mlxsw_env_temp_warn_event_register(mlxsw_core);
1425 if (err)
1426 goto err_temp_warn_event_register;
1427
1428 err = mlxsw_env_module_plug_event_register(mlxsw_core);
1429 if (err)
1430 goto err_module_plug_event_register;
1431
1432 /* Set 'module_count' only for main board. Actual count for line card
1433 * is to be set after line card is activated.
1434 */
1435 env->line_cards[0]->module_count = num_of_slots ? 0 : module_count;
1436 /* Enable events only for main board. Line card events are to be
1437 * configured only after line card is activated. Before that, access to
1438 * modules on line cards is not allowed.
1439 */
1440 err = mlxsw_env_module_event_enable(mlxsw_env: env, slot_index: 0);
1441 if (err)
1442 goto err_mlxsw_env_module_event_enable;
1443
1444 err = mlxsw_env_module_type_set(mlxsw_core, slot_index: 0);
1445 if (err)
1446 goto err_type_set;
1447
1448 err = mlxsw_env_max_module_eeprom_len_query(mlxsw_env: env);
1449 if (err)
1450 goto err_eeprom_len_query;
1451
1452 env->line_cards[0]->active = true;
1453
1454 return 0;
1455
1456err_eeprom_len_query:
1457err_type_set:
1458 mlxsw_env_module_event_disable(mlxsw_env: env, slot_index: 0);
1459err_mlxsw_env_module_event_enable:
1460 mlxsw_env_module_plug_event_unregister(mlxsw_env: env);
1461err_module_plug_event_register:
1462 mlxsw_env_temp_warn_event_unregister(mlxsw_env: env);
1463err_temp_warn_event_register:
1464 mlxsw_linecards_event_ops_unregister(mlxsw_core: env->core,
1465 ops: &mlxsw_env_event_ops, priv: env);
1466err_linecards_event_ops_register:
1467 mutex_destroy(lock: &env->line_cards_lock);
1468 mlxsw_env_line_cards_free(env);
1469err_mlxsw_env_line_cards_alloc:
1470 kfree(objp: env);
1471 return err;
1472}
1473
1474void mlxsw_env_fini(struct mlxsw_env *env)
1475{
1476 env->line_cards[0]->active = false;
1477 mlxsw_env_module_event_disable(mlxsw_env: env, slot_index: 0);
1478 mlxsw_env_module_plug_event_unregister(mlxsw_env: env);
1479 /* Make sure there is no more event work scheduled. */
1480 mlxsw_core_flush_owq();
1481 mlxsw_env_temp_warn_event_unregister(mlxsw_env: env);
1482 mlxsw_linecards_event_ops_unregister(mlxsw_core: env->core,
1483 ops: &mlxsw_env_event_ops, priv: env);
1484 mutex_destroy(lock: &env->line_cards_lock);
1485 mlxsw_env_line_cards_free(env);
1486 kfree(objp: env);
1487}
1488

source code of linux/drivers/net/ethernet/mellanox/mlxsw/core_env.c