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
15 struct 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 };
22
23 struct mlxsw_env {
24 struct mlxsw_core *core;
25 u8 module_count;
26 struct mutex module_info_lock; /* Protects 'module_info'. */
27 struct mlxsw_env_module_info module_info[];
28 };
29
mlxsw_env_validate_cable_ident(struct mlxsw_core * core,int id,bool * qsfp,bool * cmis)30 static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id,
31 bool *qsfp, bool *cmis)
32 {
33 char mcia_pl[MLXSW_REG_MCIA_LEN];
34 char *eeprom_tmp;
35 u8 ident;
36 int err;
37
38 mlxsw_reg_mcia_pack(mcia_pl, id, 0, MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1,
39 MLXSW_REG_MCIA_I2C_ADDR_LOW);
40 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
41 if (err)
42 return err;
43 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
44 ident = eeprom_tmp[0];
45 *cmis = false;
46 switch (ident) {
47 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
48 *qsfp = false;
49 break;
50 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
51 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
52 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
53 *qsfp = true;
54 break;
55 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
56 *qsfp = true;
57 *cmis = true;
58 break;
59 default:
60 return -EINVAL;
61 }
62
63 return 0;
64 }
65
66 static int
mlxsw_env_query_module_eeprom(struct mlxsw_core * mlxsw_core,int module,u16 offset,u16 size,void * data,bool qsfp,unsigned int * p_read_size)67 mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, int module,
68 u16 offset, u16 size, void *data,
69 bool qsfp, unsigned int *p_read_size)
70 {
71 char mcia_pl[MLXSW_REG_MCIA_LEN];
72 char *eeprom_tmp;
73 u16 i2c_addr;
74 u8 page = 0;
75 int status;
76 int err;
77
78 /* MCIA register accepts buffer size <= 48. Page of size 128 should be
79 * read by chunks of size 48, 48, 32. Align the size of the last chunk
80 * to avoid reading after the end of the page.
81 */
82 size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE);
83
84 if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH &&
85 offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
86 /* Cross pages read, read until offset 256 in low page */
87 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
88
89 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW;
90 if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) {
91 if (qsfp) {
92 /* When reading upper pages 1, 2 and 3 the offset
93 * starts at 128. Please refer to "QSFP+ Memory Map"
94 * figure in SFF-8436 specification and to "CMIS Module
95 * Memory Map" figure in CMIS specification for
96 * graphical depiction.
97 */
98 page = MLXSW_REG_MCIA_PAGE_GET(offset);
99 offset -= MLXSW_REG_MCIA_EEPROM_UP_PAGE_LENGTH * page;
100 if (offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH)
101 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset;
102 } else {
103 /* When reading upper pages 1, 2 and 3 the offset
104 * starts at 0 and I2C high address is used. Please refer
105 * refer to "Memory Organization" figure in SFF-8472
106 * specification for graphical depiction.
107 */
108 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH;
109 offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH;
110 }
111 }
112
113 mlxsw_reg_mcia_pack(mcia_pl, module, 0, page, offset, size, i2c_addr);
114
115 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
116 if (err)
117 return err;
118
119 status = mlxsw_reg_mcia_status_get(mcia_pl);
120 if (status)
121 return -EIO;
122
123 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
124 memcpy(data, eeprom_tmp, size);
125 *p_read_size = size;
126
127 return 0;
128 }
129
mlxsw_env_module_temp_thresholds_get(struct mlxsw_core * core,int module,int off,int * temp)130 int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
131 int off, int *temp)
132 {
133 unsigned int module_temp, module_crit, module_emerg;
134 union {
135 u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE];
136 u16 temp;
137 } temp_thresh;
138 char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
139 char mtmp_pl[MLXSW_REG_MTMP_LEN];
140 char *eeprom_tmp;
141 bool qsfp, cmis;
142 int page;
143 int err;
144
145 mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module,
146 false, false);
147 err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);
148 if (err)
149 return err;
150 mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, &module_crit,
151 &module_emerg, NULL);
152 if (!module_temp) {
153 *temp = 0;
154 return 0;
155 }
156
157 /* Validate if threshold reading is available through MTMP register,
158 * otherwise fallback to read through MCIA.
159 */
160 if (module_emerg) {
161 *temp = off == SFP_TEMP_HIGH_WARN ? module_crit : module_emerg;
162 return 0;
163 }
164
165 /* Read Free Side Device Temperature Thresholds from page 03h
166 * (MSB at lower byte address).
167 * Bytes:
168 * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM);
169 * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM);
170 * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN);
171 * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN);
172 */
173
174 /* Validate module identifier value. */
175 err = mlxsw_env_validate_cable_ident(core, module, &qsfp, &cmis);
176 if (err)
177 return err;
178
179 if (qsfp) {
180 /* For QSFP/CMIS module-defined thresholds are located in page
181 * 02h, otherwise in page 03h.
182 */
183 if (cmis)
184 page = MLXSW_REG_MCIA_TH_PAGE_CMIS_NUM;
185 else
186 page = MLXSW_REG_MCIA_TH_PAGE_NUM;
187 mlxsw_reg_mcia_pack(mcia_pl, module, 0, page,
188 MLXSW_REG_MCIA_TH_PAGE_OFF + off,
189 MLXSW_REG_MCIA_TH_ITEM_SIZE,
190 MLXSW_REG_MCIA_I2C_ADDR_LOW);
191 } else {
192 mlxsw_reg_mcia_pack(mcia_pl, module, 0,
193 MLXSW_REG_MCIA_PAGE0_LO,
194 off, MLXSW_REG_MCIA_TH_ITEM_SIZE,
195 MLXSW_REG_MCIA_I2C_ADDR_HIGH);
196 }
197
198 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl);
199 if (err)
200 return err;
201
202 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
203 memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE);
204 *temp = temp_thresh.temp * 1000;
205
206 return 0;
207 }
208
mlxsw_env_get_module_info(struct mlxsw_core * mlxsw_core,int module,struct ethtool_modinfo * modinfo)209 int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module,
210 struct ethtool_modinfo *modinfo)
211 {
212 u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE];
213 u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE;
214 u8 module_rev_id, module_id, diag_mon;
215 unsigned int read_size;
216 int err;
217
218 err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 0, offset,
219 module_info, false, &read_size);
220 if (err)
221 return err;
222
223 if (read_size < offset)
224 return -EIO;
225
226 module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID];
227 module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID];
228
229 switch (module_id) {
230 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP:
231 modinfo->type = ETH_MODULE_SFF_8436;
232 modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
233 break;
234 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
235 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28:
236 if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 ||
237 module_rev_id >=
238 MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) {
239 modinfo->type = ETH_MODULE_SFF_8636;
240 modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
241 } else {
242 modinfo->type = ETH_MODULE_SFF_8436;
243 modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
244 }
245 break;
246 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP:
247 /* Verify if transceiver provides diagnostic monitoring page */
248 err = mlxsw_env_query_module_eeprom(mlxsw_core, module,
249 SFP_DIAGMON, 1, &diag_mon,
250 false, &read_size);
251 if (err)
252 return err;
253
254 if (read_size < 1)
255 return -EIO;
256
257 modinfo->type = ETH_MODULE_SFF_8472;
258 if (diag_mon)
259 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
260 else
261 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2;
262 break;
263 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD:
264 /* Use SFF_8636 as base type. ethtool should recognize specific
265 * type through the identifier value.
266 */
267 modinfo->type = ETH_MODULE_SFF_8636;
268 /* Verify if module EEPROM is a flat memory. In case of flat
269 * memory only page 00h (0-255 bytes) can be read. Otherwise
270 * upper pages 01h and 02h can also be read. Upper pages 10h
271 * and 11h are currently not supported by the driver.
272 */
273 if (module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_TYPE_ID] &
274 MLXSW_REG_MCIA_EEPROM_CMIS_FLAT_MEMORY)
275 modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
276 else
277 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
278 break;
279 default:
280 return -EINVAL;
281 }
282
283 return 0;
284 }
285 EXPORT_SYMBOL(mlxsw_env_get_module_info);
286
mlxsw_env_get_module_eeprom(struct net_device * netdev,struct mlxsw_core * mlxsw_core,int module,struct ethtool_eeprom * ee,u8 * data)287 int mlxsw_env_get_module_eeprom(struct net_device *netdev,
288 struct mlxsw_core *mlxsw_core, int module,
289 struct ethtool_eeprom *ee, u8 *data)
290 {
291 int offset = ee->offset;
292 unsigned int read_size;
293 bool qsfp, cmis;
294 int i = 0;
295 int err;
296
297 if (!ee->len)
298 return -EINVAL;
299
300 memset(data, 0, ee->len);
301 /* Validate module identifier value. */
302 err = mlxsw_env_validate_cable_ident(mlxsw_core, module, &qsfp, &cmis);
303 if (err)
304 return err;
305
306 while (i < ee->len) {
307 err = mlxsw_env_query_module_eeprom(mlxsw_core, module, offset,
308 ee->len - i, data + i,
309 qsfp, &read_size);
310 if (err) {
311 netdev_err(netdev, "Eeprom query failed\n");
312 return err;
313 }
314
315 i += read_size;
316 offset += read_size;
317 }
318
319 return 0;
320 }
321 EXPORT_SYMBOL(mlxsw_env_get_module_eeprom);
322
mlxsw_env_mcia_status_process(const char * mcia_pl,struct netlink_ext_ack * extack)323 static int mlxsw_env_mcia_status_process(const char *mcia_pl,
324 struct netlink_ext_ack *extack)
325 {
326 u8 status = mlxsw_reg_mcia_status_get(mcia_pl);
327
328 switch (status) {
329 case MLXSW_REG_MCIA_STATUS_GOOD:
330 return 0;
331 case MLXSW_REG_MCIA_STATUS_NO_EEPROM_MODULE:
332 NL_SET_ERR_MSG_MOD(extack, "No response from module's EEPROM");
333 return -EIO;
334 case MLXSW_REG_MCIA_STATUS_MODULE_NOT_SUPPORTED:
335 NL_SET_ERR_MSG_MOD(extack, "Module type not supported by the device");
336 return -EOPNOTSUPP;
337 case MLXSW_REG_MCIA_STATUS_MODULE_NOT_CONNECTED:
338 NL_SET_ERR_MSG_MOD(extack, "No module present indication");
339 return -EIO;
340 case MLXSW_REG_MCIA_STATUS_I2C_ERROR:
341 NL_SET_ERR_MSG_MOD(extack, "Error occurred while trying to access module's EEPROM using I2C");
342 return -EIO;
343 case MLXSW_REG_MCIA_STATUS_MODULE_DISABLED:
344 NL_SET_ERR_MSG_MOD(extack, "Module is disabled");
345 return -EIO;
346 default:
347 NL_SET_ERR_MSG_MOD(extack, "Unknown error");
348 return -EIO;
349 }
350 }
351
352 int
mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core * mlxsw_core,u8 module,const struct ethtool_module_eeprom * page,struct netlink_ext_ack * extack)353 mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, u8 module,
354 const struct ethtool_module_eeprom *page,
355 struct netlink_ext_ack *extack)
356 {
357 u32 bytes_read = 0;
358 u16 device_addr;
359
360 /* Offset cannot be larger than 2 * ETH_MODULE_EEPROM_PAGE_LEN */
361 device_addr = page->offset;
362
363 while (bytes_read < page->length) {
364 char mcia_pl[MLXSW_REG_MCIA_LEN];
365 char *eeprom_tmp;
366 u8 size;
367 int err;
368
369 size = min_t(u8, page->length - bytes_read,
370 MLXSW_REG_MCIA_EEPROM_SIZE);
371
372 mlxsw_reg_mcia_pack(mcia_pl, module, 0, page->page,
373 device_addr + bytes_read, size,
374 page->i2c_address);
375 mlxsw_reg_mcia_bank_number_set(mcia_pl, page->bank);
376
377 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl);
378 if (err) {
379 NL_SET_ERR_MSG_MOD(extack, "Failed to access module's EEPROM");
380 return err;
381 }
382
383 err = mlxsw_env_mcia_status_process(mcia_pl, extack);
384 if (err)
385 return err;
386
387 eeprom_tmp = mlxsw_reg_mcia_eeprom_data(mcia_pl);
388 memcpy(page->data + bytes_read, eeprom_tmp, size);
389 bytes_read += size;
390 }
391
392 return bytes_read;
393 }
394 EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page);
395
mlxsw_env_module_reset(struct mlxsw_core * mlxsw_core,u8 module)396 static int mlxsw_env_module_reset(struct mlxsw_core *mlxsw_core, u8 module)
397 {
398 char pmaos_pl[MLXSW_REG_PMAOS_LEN];
399
400 mlxsw_reg_pmaos_pack(pmaos_pl, module);
401 mlxsw_reg_pmaos_rst_set(pmaos_pl, true);
402
403 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
404 }
405
mlxsw_env_reset_module(struct net_device * netdev,struct mlxsw_core * mlxsw_core,u8 module,u32 * flags)406 int mlxsw_env_reset_module(struct net_device *netdev,
407 struct mlxsw_core *mlxsw_core, u8 module, u32 *flags)
408 {
409 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
410 u32 req = *flags;
411 int err;
412
413 if (!(req & ETH_RESET_PHY) &&
414 !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT)))
415 return 0;
416
417 if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
418 return -EINVAL;
419
420 mutex_lock(&mlxsw_env->module_info_lock);
421
422 if (mlxsw_env->module_info[module].num_ports_up) {
423 netdev_err(netdev, "Cannot reset module when ports using it are administratively up\n");
424 err = -EINVAL;
425 goto out;
426 }
427
428 if (mlxsw_env->module_info[module].num_ports_mapped > 1 &&
429 !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) {
430 netdev_err(netdev, "Cannot reset module without \"phy-shared\" flag when shared by multiple ports\n");
431 err = -EINVAL;
432 goto out;
433 }
434
435 err = mlxsw_env_module_reset(mlxsw_core, module);
436 if (err) {
437 netdev_err(netdev, "Failed to reset module\n");
438 goto out;
439 }
440
441 *flags &= ~(ETH_RESET_PHY | (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT));
442
443 out:
444 mutex_unlock(&mlxsw_env->module_info_lock);
445 return err;
446 }
447 EXPORT_SYMBOL(mlxsw_env_reset_module);
448
449 int
mlxsw_env_get_module_power_mode(struct mlxsw_core * mlxsw_core,u8 module,struct ethtool_module_power_mode_params * params,struct netlink_ext_ack * extack)450 mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 module,
451 struct ethtool_module_power_mode_params *params,
452 struct netlink_ext_ack *extack)
453 {
454 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
455 char mcion_pl[MLXSW_REG_MCION_LEN];
456 u32 status_bits;
457 int err;
458
459 if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
460 return -EINVAL;
461
462 mutex_lock(&mlxsw_env->module_info_lock);
463
464 params->policy = mlxsw_env->module_info[module].power_mode_policy;
465
466 mlxsw_reg_mcion_pack(mcion_pl, module);
467 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcion), mcion_pl);
468 if (err) {
469 NL_SET_ERR_MSG_MOD(extack, "Failed to retrieve module's power mode");
470 goto out;
471 }
472
473 status_bits = mlxsw_reg_mcion_module_status_bits_get(mcion_pl);
474 if (!(status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_PRESENT_MASK))
475 goto out;
476
477 if (status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_LOW_POWER_MASK)
478 params->mode = ETHTOOL_MODULE_POWER_MODE_LOW;
479 else
480 params->mode = ETHTOOL_MODULE_POWER_MODE_HIGH;
481
482 out:
483 mutex_unlock(&mlxsw_env->module_info_lock);
484 return err;
485 }
486 EXPORT_SYMBOL(mlxsw_env_get_module_power_mode);
487
mlxsw_env_module_enable_set(struct mlxsw_core * mlxsw_core,u8 module,bool enable)488 static int mlxsw_env_module_enable_set(struct mlxsw_core *mlxsw_core,
489 u8 module, bool enable)
490 {
491 enum mlxsw_reg_pmaos_admin_status admin_status;
492 char pmaos_pl[MLXSW_REG_PMAOS_LEN];
493
494 mlxsw_reg_pmaos_pack(pmaos_pl, module);
495 admin_status = enable ? MLXSW_REG_PMAOS_ADMIN_STATUS_ENABLED :
496 MLXSW_REG_PMAOS_ADMIN_STATUS_DISABLED;
497 mlxsw_reg_pmaos_admin_status_set(pmaos_pl, admin_status);
498 mlxsw_reg_pmaos_ase_set(pmaos_pl, true);
499
500 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
501 }
502
mlxsw_env_module_low_power_set(struct mlxsw_core * mlxsw_core,u8 module,bool low_power)503 static int mlxsw_env_module_low_power_set(struct mlxsw_core *mlxsw_core,
504 u8 module, bool low_power)
505 {
506 u16 eeprom_override_mask, eeprom_override;
507 char pmmp_pl[MLXSW_REG_PMMP_LEN];
508
509 mlxsw_reg_pmmp_pack(pmmp_pl, module);
510 mlxsw_reg_pmmp_sticky_set(pmmp_pl, true);
511 /* Mask all the bits except low power mode. */
512 eeprom_override_mask = ~MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK;
513 mlxsw_reg_pmmp_eeprom_override_mask_set(pmmp_pl, eeprom_override_mask);
514 eeprom_override = low_power ? MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK :
515 0;
516 mlxsw_reg_pmmp_eeprom_override_set(pmmp_pl, eeprom_override);
517
518 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmmp), pmmp_pl);
519 }
520
__mlxsw_env_set_module_power_mode(struct mlxsw_core * mlxsw_core,u8 module,bool low_power,struct netlink_ext_ack * extack)521 static int __mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core,
522 u8 module, bool low_power,
523 struct netlink_ext_ack *extack)
524 {
525 int err;
526
527 err = mlxsw_env_module_enable_set(mlxsw_core, module, false);
528 if (err) {
529 NL_SET_ERR_MSG_MOD(extack, "Failed to disable module");
530 return err;
531 }
532
533 err = mlxsw_env_module_low_power_set(mlxsw_core, module, low_power);
534 if (err) {
535 NL_SET_ERR_MSG_MOD(extack, "Failed to set module's power mode");
536 goto err_module_low_power_set;
537 }
538
539 err = mlxsw_env_module_enable_set(mlxsw_core, module, true);
540 if (err) {
541 NL_SET_ERR_MSG_MOD(extack, "Failed to enable module");
542 goto err_module_enable_set;
543 }
544
545 return 0;
546
547 err_module_enable_set:
548 mlxsw_env_module_low_power_set(mlxsw_core, module, !low_power);
549 err_module_low_power_set:
550 mlxsw_env_module_enable_set(mlxsw_core, module, true);
551 return err;
552 }
553
554 int
mlxsw_env_set_module_power_mode(struct mlxsw_core * mlxsw_core,u8 module,enum ethtool_module_power_mode_policy policy,struct netlink_ext_ack * extack)555 mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 module,
556 enum ethtool_module_power_mode_policy policy,
557 struct netlink_ext_ack *extack)
558 {
559 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
560 bool low_power;
561 int err = 0;
562
563 if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
564 return -EINVAL;
565
566 if (policy != ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH &&
567 policy != ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO) {
568 NL_SET_ERR_MSG_MOD(extack, "Unsupported power mode policy");
569 return -EOPNOTSUPP;
570 }
571
572 mutex_lock(&mlxsw_env->module_info_lock);
573
574 if (mlxsw_env->module_info[module].power_mode_policy == policy)
575 goto out;
576
577 /* If any ports are up, we are already in high power mode. */
578 if (mlxsw_env->module_info[module].num_ports_up)
579 goto out_set_policy;
580
581 low_power = policy == ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO;
582 err = __mlxsw_env_set_module_power_mode(mlxsw_core, module, low_power,
583 extack);
584 if (err)
585 goto out;
586
587 out_set_policy:
588 mlxsw_env->module_info[module].power_mode_policy = policy;
589 out:
590 mutex_unlock(&mlxsw_env->module_info_lock);
591 return err;
592 }
593 EXPORT_SYMBOL(mlxsw_env_set_module_power_mode);
594
mlxsw_env_module_has_temp_sensor(struct mlxsw_core * mlxsw_core,u8 module,bool * p_has_temp_sensor)595 static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core,
596 u8 module,
597 bool *p_has_temp_sensor)
598 {
599 char mtbr_pl[MLXSW_REG_MTBR_LEN];
600 u16 temp;
601 int err;
602
603 mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
604 1);
605 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtbr), mtbr_pl);
606 if (err)
607 return err;
608
609 mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
610
611 switch (temp) {
612 case MLXSW_REG_MTBR_BAD_SENS_INFO:
613 case MLXSW_REG_MTBR_NO_CONN:
614 case MLXSW_REG_MTBR_NO_TEMP_SENS:
615 case MLXSW_REG_MTBR_INDEX_NA:
616 *p_has_temp_sensor = false;
617 break;
618 default:
619 *p_has_temp_sensor = temp ? true : false;
620 }
621 return 0;
622 }
623
mlxsw_env_temp_event_set(struct mlxsw_core * mlxsw_core,u16 sensor_index,bool enable)624 static int mlxsw_env_temp_event_set(struct mlxsw_core *mlxsw_core,
625 u16 sensor_index, bool enable)
626 {
627 char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0};
628 enum mlxsw_reg_mtmp_tee tee;
629 int err, threshold_hi;
630
631 mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, sensor_index);
632 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
633 if (err)
634 return err;
635
636 if (enable) {
637 err = mlxsw_env_module_temp_thresholds_get(mlxsw_core,
638 sensor_index -
639 MLXSW_REG_MTMP_MODULE_INDEX_MIN,
640 SFP_TEMP_HIGH_WARN,
641 &threshold_hi);
642 /* In case it is not possible to query the module's threshold,
643 * use the default value.
644 */
645 if (err)
646 threshold_hi = MLXSW_REG_MTMP_THRESH_HI;
647 else
648 /* mlxsw_env_module_temp_thresholds_get() multiplies
649 * Celsius degrees by 1000 whereas MTMP expects
650 * temperature in 0.125 Celsius degrees units.
651 * Convert threshold_hi to correct units.
652 */
653 threshold_hi = threshold_hi / 1000 * 8;
654
655 mlxsw_reg_mtmp_temperature_threshold_hi_set(mtmp_pl, threshold_hi);
656 mlxsw_reg_mtmp_temperature_threshold_lo_set(mtmp_pl, threshold_hi -
657 MLXSW_REG_MTMP_HYSTERESIS_TEMP);
658 }
659 tee = enable ? MLXSW_REG_MTMP_TEE_GENERATE_EVENT : MLXSW_REG_MTMP_TEE_NO_EVENT;
660 mlxsw_reg_mtmp_tee_set(mtmp_pl, tee);
661 return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtmp), mtmp_pl);
662 }
663
mlxsw_env_module_temp_event_enable(struct mlxsw_core * mlxsw_core,u8 module_count)664 static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core,
665 u8 module_count)
666 {
667 int i, err, sensor_index;
668 bool has_temp_sensor;
669
670 for (i = 0; i < module_count; i++) {
671 err = mlxsw_env_module_has_temp_sensor(mlxsw_core, i,
672 &has_temp_sensor);
673 if (err)
674 return err;
675
676 if (!has_temp_sensor)
677 continue;
678
679 sensor_index = i + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
680 err = mlxsw_env_temp_event_set(mlxsw_core, sensor_index, true);
681 if (err)
682 return err;
683 }
684
685 return 0;
686 }
687
688 struct mlxsw_env_module_temp_warn_event {
689 struct mlxsw_env *mlxsw_env;
690 char mtwe_pl[MLXSW_REG_MTWE_LEN];
691 struct work_struct work;
692 };
693
mlxsw_env_mtwe_event_work(struct work_struct * work)694 static void mlxsw_env_mtwe_event_work(struct work_struct *work)
695 {
696 struct mlxsw_env_module_temp_warn_event *event;
697 struct mlxsw_env *mlxsw_env;
698 int i, sensor_warning;
699 bool is_overheat;
700
701 event = container_of(work, struct mlxsw_env_module_temp_warn_event,
702 work);
703 mlxsw_env = event->mlxsw_env;
704
705 for (i = 0; i < mlxsw_env->module_count; i++) {
706 /* 64-127 of sensor_index are mapped to the port modules
707 * sequentially (module 0 is mapped to sensor_index 64,
708 * module 1 to sensor_index 65 and so on)
709 */
710 sensor_warning =
711 mlxsw_reg_mtwe_sensor_warning_get(event->mtwe_pl,
712 i + MLXSW_REG_MTMP_MODULE_INDEX_MIN);
713 mutex_lock(&mlxsw_env->module_info_lock);
714 is_overheat =
715 mlxsw_env->module_info[i].is_overheat;
716
717 if ((is_overheat && sensor_warning) ||
718 (!is_overheat && !sensor_warning)) {
719 /* Current state is "warning" and MTWE still reports
720 * warning OR current state in "no warning" and MTWE
721 * does not report warning.
722 */
723 mutex_unlock(&mlxsw_env->module_info_lock);
724 continue;
725 } else if (is_overheat && !sensor_warning) {
726 /* MTWE reports "no warning", turn is_overheat off.
727 */
728 mlxsw_env->module_info[i].is_overheat = false;
729 mutex_unlock(&mlxsw_env->module_info_lock);
730 } else {
731 /* Current state is "no warning" and MTWE reports
732 * "warning", increase the counter and turn is_overheat
733 * on.
734 */
735 mlxsw_env->module_info[i].is_overheat = true;
736 mlxsw_env->module_info[i].module_overheat_counter++;
737 mutex_unlock(&mlxsw_env->module_info_lock);
738 }
739 }
740
741 kfree(event);
742 }
743
744 static void
mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info * reg,char * mtwe_pl,void * priv)745 mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info *reg, char *mtwe_pl,
746 void *priv)
747 {
748 struct mlxsw_env_module_temp_warn_event *event;
749 struct mlxsw_env *mlxsw_env = priv;
750
751 event = kmalloc(sizeof(*event), GFP_ATOMIC);
752 if (!event)
753 return;
754
755 event->mlxsw_env = mlxsw_env;
756 memcpy(event->mtwe_pl, mtwe_pl, MLXSW_REG_MTWE_LEN);
757 INIT_WORK(&event->work, mlxsw_env_mtwe_event_work);
758 mlxsw_core_schedule_work(&event->work);
759 }
760
761 static const struct mlxsw_listener mlxsw_env_temp_warn_listener =
762 MLXSW_EVENTL(mlxsw_env_mtwe_listener_func, MTWE, MTWE);
763
mlxsw_env_temp_warn_event_register(struct mlxsw_core * mlxsw_core)764 static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core)
765 {
766 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
767
768 if (!mlxsw_core_temp_warn_enabled(mlxsw_core))
769 return 0;
770
771 return mlxsw_core_trap_register(mlxsw_core,
772 &mlxsw_env_temp_warn_listener,
773 mlxsw_env);
774 }
775
mlxsw_env_temp_warn_event_unregister(struct mlxsw_env * mlxsw_env)776 static void mlxsw_env_temp_warn_event_unregister(struct mlxsw_env *mlxsw_env)
777 {
778 if (!mlxsw_core_temp_warn_enabled(mlxsw_env->core))
779 return;
780
781 mlxsw_core_trap_unregister(mlxsw_env->core,
782 &mlxsw_env_temp_warn_listener, mlxsw_env);
783 }
784
785 struct mlxsw_env_module_plug_unplug_event {
786 struct mlxsw_env *mlxsw_env;
787 u8 module;
788 struct work_struct work;
789 };
790
mlxsw_env_pmpe_event_work(struct work_struct * work)791 static void mlxsw_env_pmpe_event_work(struct work_struct *work)
792 {
793 struct mlxsw_env_module_plug_unplug_event *event;
794 struct mlxsw_env *mlxsw_env;
795 bool has_temp_sensor;
796 u16 sensor_index;
797 int err;
798
799 event = container_of(work, struct mlxsw_env_module_plug_unplug_event,
800 work);
801 mlxsw_env = event->mlxsw_env;
802
803 mutex_lock(&mlxsw_env->module_info_lock);
804 mlxsw_env->module_info[event->module].is_overheat = false;
805 mutex_unlock(&mlxsw_env->module_info_lock);
806
807 err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core, event->module,
808 &has_temp_sensor);
809 /* Do not disable events on modules without sensors or faulty sensors
810 * because FW returns errors.
811 */
812 if (err)
813 goto out;
814
815 if (!has_temp_sensor)
816 goto out;
817
818 sensor_index = event->module + MLXSW_REG_MTMP_MODULE_INDEX_MIN;
819 mlxsw_env_temp_event_set(mlxsw_env->core, sensor_index, true);
820
821 out:
822 kfree(event);
823 }
824
825 static void
mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info * reg,char * pmpe_pl,void * priv)826 mlxsw_env_pmpe_listener_func(const struct mlxsw_reg_info *reg, char *pmpe_pl,
827 void *priv)
828 {
829 struct mlxsw_env_module_plug_unplug_event *event;
830 enum mlxsw_reg_pmpe_module_status module_status;
831 u8 module = mlxsw_reg_pmpe_module_get(pmpe_pl);
832 struct mlxsw_env *mlxsw_env = priv;
833
834 if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
835 return;
836
837 module_status = mlxsw_reg_pmpe_module_status_get(pmpe_pl);
838 if (module_status != MLXSW_REG_PMPE_MODULE_STATUS_PLUGGED_ENABLED)
839 return;
840
841 event = kmalloc(sizeof(*event), GFP_ATOMIC);
842 if (!event)
843 return;
844
845 event->mlxsw_env = mlxsw_env;
846 event->module = module;
847 INIT_WORK(&event->work, mlxsw_env_pmpe_event_work);
848 mlxsw_core_schedule_work(&event->work);
849 }
850
851 static const struct mlxsw_listener mlxsw_env_module_plug_listener =
852 MLXSW_EVENTL(mlxsw_env_pmpe_listener_func, PMPE, PMPE);
853
854 static int
mlxsw_env_module_plug_event_register(struct mlxsw_core * mlxsw_core)855 mlxsw_env_module_plug_event_register(struct mlxsw_core *mlxsw_core)
856 {
857 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
858
859 if (!mlxsw_core_temp_warn_enabled(mlxsw_core))
860 return 0;
861
862 return mlxsw_core_trap_register(mlxsw_core,
863 &mlxsw_env_module_plug_listener,
864 mlxsw_env);
865 }
866
867 static void
mlxsw_env_module_plug_event_unregister(struct mlxsw_env * mlxsw_env)868 mlxsw_env_module_plug_event_unregister(struct mlxsw_env *mlxsw_env)
869 {
870 if (!mlxsw_core_temp_warn_enabled(mlxsw_env->core))
871 return;
872
873 mlxsw_core_trap_unregister(mlxsw_env->core,
874 &mlxsw_env_module_plug_listener,
875 mlxsw_env);
876 }
877
878 static int
mlxsw_env_module_oper_state_event_enable(struct mlxsw_core * mlxsw_core,u8 module_count)879 mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core,
880 u8 module_count)
881 {
882 int i, err;
883
884 for (i = 0; i < module_count; i++) {
885 char pmaos_pl[MLXSW_REG_PMAOS_LEN];
886
887 mlxsw_reg_pmaos_pack(pmaos_pl, i);
888 mlxsw_reg_pmaos_e_set(pmaos_pl,
889 MLXSW_REG_PMAOS_E_GENERATE_EVENT);
890 mlxsw_reg_pmaos_ee_set(pmaos_pl, true);
891 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
892 if (err)
893 return err;
894 }
895 return 0;
896 }
897
898 int
mlxsw_env_module_overheat_counter_get(struct mlxsw_core * mlxsw_core,u8 module,u64 * p_counter)899 mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module,
900 u64 *p_counter)
901 {
902 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
903
904 if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
905 return -EINVAL;
906
907 mutex_lock(&mlxsw_env->module_info_lock);
908 *p_counter = mlxsw_env->module_info[module].module_overheat_counter;
909 mutex_unlock(&mlxsw_env->module_info_lock);
910
911 return 0;
912 }
913 EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get);
914
mlxsw_env_module_port_map(struct mlxsw_core * mlxsw_core,u8 module)915 void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 module)
916 {
917 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
918
919 if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
920 return;
921
922 mutex_lock(&mlxsw_env->module_info_lock);
923 mlxsw_env->module_info[module].num_ports_mapped++;
924 mutex_unlock(&mlxsw_env->module_info_lock);
925 }
926 EXPORT_SYMBOL(mlxsw_env_module_port_map);
927
mlxsw_env_module_port_unmap(struct mlxsw_core * mlxsw_core,u8 module)928 void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 module)
929 {
930 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
931
932 if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
933 return;
934
935 mutex_lock(&mlxsw_env->module_info_lock);
936 mlxsw_env->module_info[module].num_ports_mapped--;
937 mutex_unlock(&mlxsw_env->module_info_lock);
938 }
939 EXPORT_SYMBOL(mlxsw_env_module_port_unmap);
940
mlxsw_env_module_port_up(struct mlxsw_core * mlxsw_core,u8 module)941 int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 module)
942 {
943 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
944 int err = 0;
945
946 if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
947 return -EINVAL;
948
949 mutex_lock(&mlxsw_env->module_info_lock);
950
951 if (mlxsw_env->module_info[module].power_mode_policy !=
952 ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
953 goto out_inc;
954
955 if (mlxsw_env->module_info[module].num_ports_up != 0)
956 goto out_inc;
957
958 /* Transition to high power mode following first port using the module
959 * being put administratively up.
960 */
961 err = __mlxsw_env_set_module_power_mode(mlxsw_core, module, false,
962 NULL);
963 if (err)
964 goto out_unlock;
965
966 out_inc:
967 mlxsw_env->module_info[module].num_ports_up++;
968 out_unlock:
969 mutex_unlock(&mlxsw_env->module_info_lock);
970 return err;
971 }
972 EXPORT_SYMBOL(mlxsw_env_module_port_up);
973
mlxsw_env_module_port_down(struct mlxsw_core * mlxsw_core,u8 module)974 void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 module)
975 {
976 struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
977
978 if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
979 return;
980
981 mutex_lock(&mlxsw_env->module_info_lock);
982
983 mlxsw_env->module_info[module].num_ports_up--;
984
985 if (mlxsw_env->module_info[module].power_mode_policy !=
986 ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO)
987 goto out_unlock;
988
989 if (mlxsw_env->module_info[module].num_ports_up != 0)
990 goto out_unlock;
991
992 /* Transition to low power mode following last port using the module
993 * being put administratively down.
994 */
995 __mlxsw_env_set_module_power_mode(mlxsw_core, module, true, NULL);
996
997 out_unlock:
998 mutex_unlock(&mlxsw_env->module_info_lock);
999 }
1000 EXPORT_SYMBOL(mlxsw_env_module_port_down);
1001
mlxsw_env_init(struct mlxsw_core * mlxsw_core,struct mlxsw_env ** p_env)1002 int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env)
1003 {
1004 char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1005 struct mlxsw_env *env;
1006 u8 module_count;
1007 int i, err;
1008
1009 mlxsw_reg_mgpir_pack(mgpir_pl);
1010 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
1011 if (err)
1012 return err;
1013
1014 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &module_count);
1015
1016 env = kzalloc(struct_size(env, module_info, module_count), GFP_KERNEL);
1017 if (!env)
1018 return -ENOMEM;
1019
1020 /* Firmware defaults to high power mode policy where modules are
1021 * transitioned to high power mode following plug-in.
1022 */
1023 for (i = 0; i < module_count; i++)
1024 env->module_info[i].power_mode_policy =
1025 ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH;
1026
1027 mutex_init(&env->module_info_lock);
1028 env->core = mlxsw_core;
1029 env->module_count = module_count;
1030 *p_env = env;
1031
1032 err = mlxsw_env_temp_warn_event_register(mlxsw_core);
1033 if (err)
1034 goto err_temp_warn_event_register;
1035
1036 err = mlxsw_env_module_plug_event_register(mlxsw_core);
1037 if (err)
1038 goto err_module_plug_event_register;
1039
1040 err = mlxsw_env_module_oper_state_event_enable(mlxsw_core,
1041 env->module_count);
1042 if (err)
1043 goto err_oper_state_event_enable;
1044
1045 err = mlxsw_env_module_temp_event_enable(mlxsw_core, env->module_count);
1046 if (err)
1047 goto err_temp_event_enable;
1048
1049 return 0;
1050
1051 err_temp_event_enable:
1052 err_oper_state_event_enable:
1053 mlxsw_env_module_plug_event_unregister(env);
1054 err_module_plug_event_register:
1055 mlxsw_env_temp_warn_event_unregister(env);
1056 err_temp_warn_event_register:
1057 mutex_destroy(&env->module_info_lock);
1058 kfree(env);
1059 return err;
1060 }
1061
mlxsw_env_fini(struct mlxsw_env * env)1062 void mlxsw_env_fini(struct mlxsw_env *env)
1063 {
1064 mlxsw_env_module_plug_event_unregister(env);
1065 /* Make sure there is no more event work scheduled. */
1066 mlxsw_core_flush_owq();
1067 mlxsw_env_temp_warn_event_unregister(env);
1068 mutex_destroy(&env->module_info_lock);
1069 kfree(env);
1070 }
1071