942 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved */3 4#include <linux/kernel.h>5#include <linux/types.h>6#include <linux/device.h>7#include <linux/sysfs.h>8#include <linux/hwmon.h>9#include <linux/err.h>10#include <linux/sfp.h>11 12#include "core.h"13#include "core_env.h"14 15#define MLXSW_HWMON_SENSORS_MAX_COUNT 6416#define MLXSW_HWMON_MODULES_MAX_COUNT 6417#define MLXSW_HWMON_GEARBOXES_MAX_COUNT 3218 19#define MLXSW_HWMON_ATTR_PER_SENSOR 320#define MLXSW_HWMON_ATTR_PER_MODULE 721#define MLXSW_HWMON_ATTR_PER_GEARBOX 422#define MLXSW_HWMON_DEV_NAME_LEN_MAX 1623 24#define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_SENSORS_MAX_COUNT * MLXSW_HWMON_ATTR_PER_SENSOR + \25 MLXSW_HWMON_MODULES_MAX_COUNT * MLXSW_HWMON_ATTR_PER_MODULE + \26 MLXSW_HWMON_GEARBOXES_MAX_COUNT * MLXSW_HWMON_ATTR_PER_GEARBOX + \27 MLXSW_MFCR_TACHOS_MAX + MLXSW_MFCR_PWMS_MAX)28 29struct mlxsw_hwmon_attr {30 struct device_attribute dev_attr;31 struct mlxsw_hwmon_dev *mlxsw_hwmon_dev;32 unsigned int type_index;33 char name[32];34};35 36static int mlxsw_hwmon_get_attr_index(int index, int count)37{38 if (index >= count)39 return index % count + MLXSW_REG_MTMP_GBOX_INDEX_MIN;40 41 return index;42}43 44struct mlxsw_hwmon_dev {45 char name[MLXSW_HWMON_DEV_NAME_LEN_MAX];46 struct mlxsw_hwmon *hwmon;47 struct device *hwmon_dev;48 struct attribute_group group;49 const struct attribute_group *groups[2];50 struct attribute *attrs[MLXSW_HWMON_ATTR_COUNT + 1];51 struct mlxsw_hwmon_attr hwmon_attrs[MLXSW_HWMON_ATTR_COUNT];52 unsigned int attrs_count;53 u8 sensor_count;54 u8 module_sensor_max;55 u8 slot_index;56 bool active;57};58 59struct mlxsw_hwmon {60 struct mlxsw_core *core;61 const struct mlxsw_bus_info *bus_info;62 struct mlxsw_hwmon_dev line_cards[];63};64 65static ssize_t mlxsw_hwmon_temp_show(struct device *dev,66 struct device_attribute *attr,67 char *buf)68{69 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr =70 container_of(attr, struct mlxsw_hwmon_attr, dev_attr);71 struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev;72 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;73 char mtmp_pl[MLXSW_REG_MTMP_LEN];74 int temp, index;75 int err;76 77 index = mlxsw_hwmon_get_attr_index(mlxsw_hwmon_attr->type_index,78 mlxsw_hwmon_dev->module_sensor_max);79 mlxsw_reg_mtmp_pack(mtmp_pl, mlxsw_hwmon_dev->slot_index, index, false,80 false);81 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);82 if (err) {83 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");84 return err;85 }86 mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL, NULL, NULL);87 return sprintf(buf, "%d\n", temp);88}89 90static ssize_t mlxsw_hwmon_temp_max_show(struct device *dev,91 struct device_attribute *attr,92 char *buf)93{94 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr =95 container_of(attr, struct mlxsw_hwmon_attr, dev_attr);96 struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev;97 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;98 char mtmp_pl[MLXSW_REG_MTMP_LEN];99 int temp_max, index;100 int err;101 102 index = mlxsw_hwmon_get_attr_index(mlxsw_hwmon_attr->type_index,103 mlxsw_hwmon_dev->module_sensor_max);104 mlxsw_reg_mtmp_pack(mtmp_pl, mlxsw_hwmon_dev->slot_index, index, false,105 false);106 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);107 if (err) {108 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");109 return err;110 }111 mlxsw_reg_mtmp_unpack(mtmp_pl, NULL, &temp_max, NULL, NULL, NULL);112 return sprintf(buf, "%d\n", temp_max);113}114 115static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev,116 struct device_attribute *attr,117 const char *buf, size_t len)118{119 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr =120 container_of(attr, struct mlxsw_hwmon_attr, dev_attr);121 struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev;122 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;123 char mtmp_pl[MLXSW_REG_MTMP_LEN];124 unsigned long val;125 int index;126 int err;127 128 err = kstrtoul(buf, 10, &val);129 if (err)130 return err;131 if (val != 1)132 return -EINVAL;133 134 index = mlxsw_hwmon_get_attr_index(mlxsw_hwmon_attr->type_index,135 mlxsw_hwmon_dev->module_sensor_max);136 137 mlxsw_reg_mtmp_slot_index_set(mtmp_pl, mlxsw_hwmon_dev->slot_index);138 mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, index);139 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);140 if (err)141 return err;142 mlxsw_reg_mtmp_mte_set(mtmp_pl, true);143 mlxsw_reg_mtmp_mtr_set(mtmp_pl, true);144 err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);145 if (err) {146 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to reset temp sensor history\n");147 return err;148 }149 return len;150}151 152static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev,153 struct device_attribute *attr,154 char *buf)155{156 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr =157 container_of(attr, struct mlxsw_hwmon_attr, dev_attr);158 struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev;159 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;160 char mfsm_pl[MLXSW_REG_MFSM_LEN];161 int err;162 163 mlxsw_reg_mfsm_pack(mfsm_pl, mlxsw_hwmon_attr->type_index);164 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsm), mfsm_pl);165 if (err) {166 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n");167 return err;168 }169 return sprintf(buf, "%u\n", mlxsw_reg_mfsm_rpm_get(mfsm_pl));170}171 172static ssize_t mlxsw_hwmon_fan_fault_show(struct device *dev,173 struct device_attribute *attr,174 char *buf)175{176 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr =177 container_of(attr, struct mlxsw_hwmon_attr, dev_attr);178 struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev;179 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;180 char fore_pl[MLXSW_REG_FORE_LEN];181 bool fault;182 int err;183 184 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(fore), fore_pl);185 if (err) {186 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n");187 return err;188 }189 mlxsw_reg_fore_unpack(fore_pl, mlxsw_hwmon_attr->type_index, &fault);190 191 return sprintf(buf, "%u\n", fault);192}193 194static ssize_t mlxsw_hwmon_pwm_show(struct device *dev,195 struct device_attribute *attr,196 char *buf)197{198 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr =199 container_of(attr, struct mlxsw_hwmon_attr, dev_attr);200 struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev;201 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;202 char mfsc_pl[MLXSW_REG_MFSC_LEN];203 int err;204 205 mlxsw_reg_mfsc_pack(mfsc_pl, mlxsw_hwmon_attr->type_index, 0);206 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl);207 if (err) {208 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query PWM\n");209 return err;210 }211 return sprintf(buf, "%u\n",212 mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl));213}214 215static ssize_t mlxsw_hwmon_pwm_store(struct device *dev,216 struct device_attribute *attr,217 const char *buf, size_t len)218{219 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr =220 container_of(attr, struct mlxsw_hwmon_attr, dev_attr);221 struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev;222 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;223 char mfsc_pl[MLXSW_REG_MFSC_LEN];224 unsigned long val;225 int err;226 227 err = kstrtoul(buf, 10, &val);228 if (err)229 return err;230 if (val > 255)231 return -EINVAL;232 233 mlxsw_reg_mfsc_pack(mfsc_pl, mlxsw_hwmon_attr->type_index, val);234 err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl);235 if (err) {236 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to write PWM\n");237 return err;238 }239 return len;240}241 242static int mlxsw_hwmon_module_temp_get(struct device *dev,243 struct device_attribute *attr,244 int *p_temp)245{246 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr =247 container_of(attr, struct mlxsw_hwmon_attr, dev_attr);248 struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev;249 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;250 char mtmp_pl[MLXSW_REG_MTMP_LEN];251 u8 module;252 int err;253 254 module = mlxsw_hwmon_attr->type_index - mlxsw_hwmon_dev->sensor_count;255 mlxsw_reg_mtmp_pack(mtmp_pl, mlxsw_hwmon_dev->slot_index,256 MLXSW_REG_MTMP_MODULE_INDEX_MIN + module, false,257 false);258 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);259 if (err) {260 dev_err(dev, "Failed to query module temperature\n");261 return err;262 }263 mlxsw_reg_mtmp_unpack(mtmp_pl, p_temp, NULL, NULL, NULL, NULL);264 265 return 0;266}267 268static ssize_t mlxsw_hwmon_module_temp_show(struct device *dev,269 struct device_attribute *attr,270 char *buf)271{272 int err, temp;273 274 err = mlxsw_hwmon_module_temp_get(dev, attr, &temp);275 if (err)276 return err;277 278 return sprintf(buf, "%d\n", temp);279}280 281static ssize_t mlxsw_hwmon_module_temp_fault_show(struct device *dev,282 struct device_attribute *attr,283 char *buf)284{285 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr =286 container_of(attr, struct mlxsw_hwmon_attr, dev_attr);287 struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev;288 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;289 char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0};290 u8 module, fault;291 u16 temp;292 int err;293 294 module = mlxsw_hwmon_attr->type_index - mlxsw_hwmon_dev->sensor_count;295 mlxsw_reg_mtbr_pack(mtbr_pl, mlxsw_hwmon_dev->slot_index,296 MLXSW_REG_MTBR_BASE_MODULE_INDEX + module);297 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtbr), mtbr_pl);298 if (err) {299 dev_err(dev, "Failed to query module temperature sensor\n");300 return err;301 }302 303 mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);304 305 /* Update status and temperature cache. */306 switch (temp) {307 case MLXSW_REG_MTBR_BAD_SENS_INFO:308 /* Untrusted cable is connected. Reading temperature from its309 * sensor is faulty.310 */311 fault = 1;312 break;313 case MLXSW_REG_MTBR_NO_CONN:314 case MLXSW_REG_MTBR_NO_TEMP_SENS:315 case MLXSW_REG_MTBR_INDEX_NA:316 default:317 fault = 0;318 break;319 }320 321 return sprintf(buf, "%u\n", fault);322}323 324static int mlxsw_hwmon_module_temp_critical_get(struct device *dev,325 struct device_attribute *attr,326 int *p_temp)327{328 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr =329 container_of(attr, struct mlxsw_hwmon_attr, dev_attr);330 struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev;331 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;332 u8 module;333 int err;334 335 module = mlxsw_hwmon_attr->type_index - mlxsw_hwmon_dev->sensor_count;336 err = mlxsw_env_module_temp_thresholds_get(mlxsw_hwmon->core,337 mlxsw_hwmon_dev->slot_index,338 module, SFP_TEMP_HIGH_WARN,339 p_temp);340 if (err) {341 dev_err(dev, "Failed to query module temperature thresholds\n");342 return err;343 }344 345 return 0;346}347 348static ssize_t349mlxsw_hwmon_module_temp_critical_show(struct device *dev,350 struct device_attribute *attr, char *buf)351{352 int err, temp;353 354 err = mlxsw_hwmon_module_temp_critical_get(dev, attr, &temp);355 if (err)356 return err;357 358 return sprintf(buf, "%u\n", temp);359}360 361static int mlxsw_hwmon_module_temp_emergency_get(struct device *dev,362 struct device_attribute *attr,363 int *p_temp)364{365 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr =366 container_of(attr, struct mlxsw_hwmon_attr, dev_attr);367 struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev;368 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;369 u8 module;370 int err;371 372 module = mlxsw_hwmon_attr->type_index - mlxsw_hwmon_dev->sensor_count;373 err = mlxsw_env_module_temp_thresholds_get(mlxsw_hwmon->core,374 mlxsw_hwmon_dev->slot_index,375 module, SFP_TEMP_HIGH_ALARM,376 p_temp);377 if (err) {378 dev_err(dev, "Failed to query module temperature thresholds\n");379 return err;380 }381 382 return 0;383}384 385static ssize_t386mlxsw_hwmon_module_temp_emergency_show(struct device *dev,387 struct device_attribute *attr,388 char *buf)389{390 int err, temp;391 392 err = mlxsw_hwmon_module_temp_emergency_get(dev, attr, &temp);393 if (err)394 return err;395 396 return sprintf(buf, "%u\n", temp);397}398 399static ssize_t400mlxsw_hwmon_module_temp_label_show(struct device *dev,401 struct device_attribute *attr,402 char *buf)403{404 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr =405 container_of(attr, struct mlxsw_hwmon_attr, dev_attr);406 407 return sprintf(buf, "front panel %03u\n",408 mlxsw_hwmon_attr->type_index + 1 -409 mlxsw_hwmon_attr->mlxsw_hwmon_dev->sensor_count);410}411 412static ssize_t413mlxsw_hwmon_gbox_temp_label_show(struct device *dev,414 struct device_attribute *attr,415 char *buf)416{417 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr =418 container_of(attr, struct mlxsw_hwmon_attr, dev_attr);419 struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev;420 int index = mlxsw_hwmon_attr->type_index -421 mlxsw_hwmon_dev->module_sensor_max + 1;422 423 return sprintf(buf, "gearbox %03u\n", index);424}425 426static ssize_t mlxsw_hwmon_temp_critical_alarm_show(struct device *dev,427 struct device_attribute *attr,428 char *buf)429{430 int err, temp, emergency_temp, critic_temp;431 432 err = mlxsw_hwmon_module_temp_get(dev, attr, &temp);433 if (err)434 return err;435 436 if (temp <= 0)437 return sprintf(buf, "%d\n", false);438 439 err = mlxsw_hwmon_module_temp_emergency_get(dev, attr, &emergency_temp);440 if (err)441 return err;442 443 if (temp >= emergency_temp)444 return sprintf(buf, "%d\n", false);445 446 err = mlxsw_hwmon_module_temp_critical_get(dev, attr, &critic_temp);447 if (err)448 return err;449 450 return sprintf(buf, "%d\n", temp >= critic_temp);451}452 453static ssize_t mlxsw_hwmon_temp_emergency_alarm_show(struct device *dev,454 struct device_attribute *attr,455 char *buf)456{457 int err, temp, emergency_temp;458 459 err = mlxsw_hwmon_module_temp_get(dev, attr, &temp);460 if (err)461 return err;462 463 if (temp <= 0)464 return sprintf(buf, "%d\n", false);465 466 err = mlxsw_hwmon_module_temp_emergency_get(dev, attr, &emergency_temp);467 if (err)468 return err;469 470 return sprintf(buf, "%d\n", temp >= emergency_temp);471}472 473enum mlxsw_hwmon_attr_type {474 MLXSW_HWMON_ATTR_TYPE_TEMP,475 MLXSW_HWMON_ATTR_TYPE_TEMP_MAX,476 MLXSW_HWMON_ATTR_TYPE_TEMP_RST,477 MLXSW_HWMON_ATTR_TYPE_FAN_RPM,478 MLXSW_HWMON_ATTR_TYPE_FAN_FAULT,479 MLXSW_HWMON_ATTR_TYPE_PWM,480 MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE,481 MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT,482 MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT,483 MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG,484 MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL,485 MLXSW_HWMON_ATTR_TYPE_TEMP_GBOX_LABEL,486 MLXSW_HWMON_ATTR_TYPE_TEMP_CRIT_ALARM,487 MLXSW_HWMON_ATTR_TYPE_TEMP_EMERGENCY_ALARM,488};489 490static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon_dev *mlxsw_hwmon_dev,491 enum mlxsw_hwmon_attr_type attr_type,492 unsigned int type_index, unsigned int num)493{494 struct mlxsw_hwmon_attr *mlxsw_hwmon_attr;495 unsigned int attr_index;496 497 attr_index = mlxsw_hwmon_dev->attrs_count;498 mlxsw_hwmon_attr = &mlxsw_hwmon_dev->hwmon_attrs[attr_index];499 500 switch (attr_type) {501 case MLXSW_HWMON_ATTR_TYPE_TEMP:502 mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_show;503 mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;504 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),505 "temp%u_input", num + 1);506 break;507 case MLXSW_HWMON_ATTR_TYPE_TEMP_MAX:508 mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_max_show;509 mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;510 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),511 "temp%u_highest", num + 1);512 break;513 case MLXSW_HWMON_ATTR_TYPE_TEMP_RST:514 mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_temp_rst_store;515 mlxsw_hwmon_attr->dev_attr.attr.mode = 0200;516 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),517 "temp%u_reset_history", num + 1);518 break;519 case MLXSW_HWMON_ATTR_TYPE_FAN_RPM:520 mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_rpm_show;521 mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;522 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),523 "fan%u_input", num + 1);524 break;525 case MLXSW_HWMON_ATTR_TYPE_FAN_FAULT:526 mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_fault_show;527 mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;528 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),529 "fan%u_fault", num + 1);530 break;531 case MLXSW_HWMON_ATTR_TYPE_PWM:532 mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_pwm_show;533 mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_pwm_store;534 mlxsw_hwmon_attr->dev_attr.attr.mode = 0644;535 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),536 "pwm%u", num + 1);537 break;538 case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE:539 mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_module_temp_show;540 mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;541 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),542 "temp%u_input", num + 1);543 break;544 case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT:545 mlxsw_hwmon_attr->dev_attr.show =546 mlxsw_hwmon_module_temp_fault_show;547 mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;548 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),549 "temp%u_fault", num + 1);550 break;551 case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT:552 mlxsw_hwmon_attr->dev_attr.show =553 mlxsw_hwmon_module_temp_critical_show;554 mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;555 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),556 "temp%u_crit", num + 1);557 break;558 case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG:559 mlxsw_hwmon_attr->dev_attr.show =560 mlxsw_hwmon_module_temp_emergency_show;561 mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;562 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),563 "temp%u_emergency", num + 1);564 break;565 case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL:566 mlxsw_hwmon_attr->dev_attr.show =567 mlxsw_hwmon_module_temp_label_show;568 mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;569 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),570 "temp%u_label", num + 1);571 break;572 case MLXSW_HWMON_ATTR_TYPE_TEMP_GBOX_LABEL:573 mlxsw_hwmon_attr->dev_attr.show =574 mlxsw_hwmon_gbox_temp_label_show;575 mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;576 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),577 "temp%u_label", num + 1);578 break;579 case MLXSW_HWMON_ATTR_TYPE_TEMP_CRIT_ALARM:580 mlxsw_hwmon_attr->dev_attr.show =581 mlxsw_hwmon_temp_critical_alarm_show;582 mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;583 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),584 "temp%u_crit_alarm", num + 1);585 break;586 case MLXSW_HWMON_ATTR_TYPE_TEMP_EMERGENCY_ALARM:587 mlxsw_hwmon_attr->dev_attr.show =588 mlxsw_hwmon_temp_emergency_alarm_show;589 mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;590 snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),591 "temp%u_emergency_alarm", num + 1);592 break;593 default:594 WARN_ON(1);595 }596 597 mlxsw_hwmon_attr->type_index = type_index;598 mlxsw_hwmon_attr->mlxsw_hwmon_dev = mlxsw_hwmon_dev;599 mlxsw_hwmon_attr->dev_attr.attr.name = mlxsw_hwmon_attr->name;600 sysfs_attr_init(&mlxsw_hwmon_attr->dev_attr.attr);601 602 mlxsw_hwmon_dev->attrs[attr_index] = &mlxsw_hwmon_attr->dev_attr.attr;603 mlxsw_hwmon_dev->attrs_count++;604}605 606static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon_dev *mlxsw_hwmon_dev)607{608 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;609 char mtcap_pl[MLXSW_REG_MTCAP_LEN] = {0};610 int i;611 int err;612 613 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtcap), mtcap_pl);614 if (err) {615 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get number of temp sensors\n");616 return err;617 }618 mlxsw_hwmon_dev->sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl);619 for (i = 0; i < mlxsw_hwmon_dev->sensor_count; i++) {620 char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0};621 622 mlxsw_reg_mtmp_slot_index_set(mtmp_pl,623 mlxsw_hwmon_dev->slot_index);624 mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, i);625 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp),626 mtmp_pl);627 if (err)628 return err;629 mlxsw_reg_mtmp_mte_set(mtmp_pl, true);630 mlxsw_reg_mtmp_mtr_set(mtmp_pl, true);631 err = mlxsw_reg_write(mlxsw_hwmon->core,632 MLXSW_REG(mtmp), mtmp_pl);633 if (err) {634 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to setup temp sensor number %d\n",635 i);636 return err;637 }638 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,639 MLXSW_HWMON_ATTR_TYPE_TEMP, i, i);640 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,641 MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, i, i);642 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,643 MLXSW_HWMON_ATTR_TYPE_TEMP_RST, i, i);644 }645 return 0;646}647 648static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon_dev *mlxsw_hwmon_dev)649{650 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;651 char mfcr_pl[MLXSW_REG_MFCR_LEN] = {0};652 enum mlxsw_reg_mfcr_pwm_frequency freq;653 unsigned int type_index;654 unsigned int num;655 u16 tacho_active;656 u8 pwm_active;657 int err;658 659 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfcr), mfcr_pl);660 if (err) {661 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get to probe PWMs and Tachometers\n");662 return err;663 }664 mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active);665 num = 0;666 for (type_index = 0; type_index < MLXSW_MFCR_TACHOS_MAX; type_index++) {667 if (tacho_active & BIT(type_index)) {668 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,669 MLXSW_HWMON_ATTR_TYPE_FAN_RPM,670 type_index, num);671 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,672 MLXSW_HWMON_ATTR_TYPE_FAN_FAULT,673 type_index, num++);674 }675 }676 num = 0;677 for (type_index = 0; type_index < MLXSW_MFCR_PWMS_MAX; type_index++) {678 if (pwm_active & BIT(type_index))679 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,680 MLXSW_HWMON_ATTR_TYPE_PWM,681 type_index, num++);682 }683 return 0;684}685 686static int mlxsw_hwmon_module_init(struct mlxsw_hwmon_dev *mlxsw_hwmon_dev)687{688 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;689 char mgpir_pl[MLXSW_REG_MGPIR_LEN];690 u8 module_sensor_max;691 int i, err;692 693 mlxsw_reg_mgpir_pack(mgpir_pl, mlxsw_hwmon_dev->slot_index);694 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mgpir), mgpir_pl);695 if (err)696 return err;697 698 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,699 &module_sensor_max, NULL);700 701 /* Add extra attributes for module temperature. Sensor index is702 * assigned to sensor_count value, while all indexed before703 * sensor_count are already utilized by the sensors connected through704 * mtmp register by mlxsw_hwmon_temp_init().705 */706 mlxsw_hwmon_dev->module_sensor_max = mlxsw_hwmon_dev->sensor_count +707 module_sensor_max;708 for (i = mlxsw_hwmon_dev->sensor_count;709 i < mlxsw_hwmon_dev->module_sensor_max; i++) {710 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,711 MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE, i, i);712 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,713 MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT,714 i, i);715 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,716 MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT, i,717 i);718 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,719 MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG,720 i, i);721 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,722 MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL,723 i, i);724 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,725 MLXSW_HWMON_ATTR_TYPE_TEMP_CRIT_ALARM,726 i, i);727 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,728 MLXSW_HWMON_ATTR_TYPE_TEMP_EMERGENCY_ALARM,729 i, i);730 }731 732 return 0;733}734 735static int mlxsw_hwmon_gearbox_init(struct mlxsw_hwmon_dev *mlxsw_hwmon_dev)736{737 struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon;738 enum mlxsw_reg_mgpir_device_type device_type;739 int index, max_index, sensor_index;740 char mgpir_pl[MLXSW_REG_MGPIR_LEN];741 char mtmp_pl[MLXSW_REG_MTMP_LEN];742 u8 gbox_num;743 int err;744 745 mlxsw_reg_mgpir_pack(mgpir_pl, mlxsw_hwmon_dev->slot_index);746 err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mgpir), mgpir_pl);747 if (err)748 return err;749 750 mlxsw_reg_mgpir_unpack(mgpir_pl, &gbox_num, &device_type, NULL, NULL,751 NULL);752 if (device_type != MLXSW_REG_MGPIR_DEVICE_TYPE_GEARBOX_DIE ||753 !gbox_num)754 return 0;755 756 index = mlxsw_hwmon_dev->module_sensor_max;757 max_index = mlxsw_hwmon_dev->module_sensor_max + gbox_num;758 while (index < max_index) {759 sensor_index = index % mlxsw_hwmon_dev->module_sensor_max +760 MLXSW_REG_MTMP_GBOX_INDEX_MIN;761 mlxsw_reg_mtmp_pack(mtmp_pl, mlxsw_hwmon_dev->slot_index,762 sensor_index, true, true);763 err = mlxsw_reg_write(mlxsw_hwmon->core,764 MLXSW_REG(mtmp), mtmp_pl);765 if (err) {766 dev_err(mlxsw_hwmon->bus_info->dev, "Failed to setup temp sensor number %d\n",767 sensor_index);768 return err;769 }770 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,771 MLXSW_HWMON_ATTR_TYPE_TEMP, index, index);772 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,773 MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, index,774 index);775 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,776 MLXSW_HWMON_ATTR_TYPE_TEMP_RST, index,777 index);778 mlxsw_hwmon_attr_add(mlxsw_hwmon_dev,779 MLXSW_HWMON_ATTR_TYPE_TEMP_GBOX_LABEL,780 index, index);781 index++;782 }783 784 return 0;785}786 787static void788mlxsw_hwmon_got_active(struct mlxsw_core *mlxsw_core, u8 slot_index,789 void *priv)790{791 struct mlxsw_hwmon *hwmon = priv;792 struct mlxsw_hwmon_dev *linecard;793 struct device *dev;794 int err;795 796 dev = hwmon->bus_info->dev;797 linecard = &hwmon->line_cards[slot_index];798 if (linecard->active)799 return;800 /* For the main board, module sensor indexes start from 1, sensor index801 * 0 is used for the ASIC. Use the same numbering for line cards.802 */803 linecard->sensor_count = 1;804 linecard->slot_index = slot_index;805 linecard->hwmon = hwmon;806 err = mlxsw_hwmon_module_init(linecard);807 if (err) {808 dev_err(dev, "Failed to configure hwmon objects for line card modules in slot %d\n",809 slot_index);810 return;811 }812 813 err = mlxsw_hwmon_gearbox_init(linecard);814 if (err) {815 dev_err(dev, "Failed to configure hwmon objects for line card gearboxes in slot %d\n",816 slot_index);817 return;818 }819 820 linecard->groups[0] = &linecard->group;821 linecard->group.attrs = linecard->attrs;822 sprintf(linecard->name, "%s#%02u", "linecard", slot_index);823 linecard->hwmon_dev =824 hwmon_device_register_with_groups(dev, linecard->name,825 linecard, linecard->groups);826 if (IS_ERR(linecard->hwmon_dev)) {827 dev_err(dev, "Failed to register hwmon objects for line card in slot %d\n",828 slot_index);829 return;830 }831 832 linecard->active = true;833}834 835static void836mlxsw_hwmon_got_inactive(struct mlxsw_core *mlxsw_core, u8 slot_index,837 void *priv)838{839 struct mlxsw_hwmon *hwmon = priv;840 struct mlxsw_hwmon_dev *linecard;841 842 linecard = &hwmon->line_cards[slot_index];843 if (!linecard->active)844 return;845 linecard->active = false;846 hwmon_device_unregister(linecard->hwmon_dev);847 /* Reset attributes counter */848 linecard->attrs_count = 0;849}850 851static struct mlxsw_linecards_event_ops mlxsw_hwmon_event_ops = {852 .got_active = mlxsw_hwmon_got_active,853 .got_inactive = mlxsw_hwmon_got_inactive,854};855 856int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,857 const struct mlxsw_bus_info *mlxsw_bus_info,858 struct mlxsw_hwmon **p_hwmon)859{860 char mgpir_pl[MLXSW_REG_MGPIR_LEN];861 struct mlxsw_hwmon *mlxsw_hwmon;862 struct device *hwmon_dev;863 u8 num_of_slots;864 int err;865 866 mlxsw_reg_mgpir_pack(mgpir_pl, 0);867 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);868 if (err)869 return err;870 871 mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, NULL,872 &num_of_slots);873 874 mlxsw_hwmon = kzalloc(struct_size(mlxsw_hwmon, line_cards,875 num_of_slots + 1), GFP_KERNEL);876 if (!mlxsw_hwmon)877 return -ENOMEM;878 879 mlxsw_hwmon->core = mlxsw_core;880 mlxsw_hwmon->bus_info = mlxsw_bus_info;881 mlxsw_hwmon->line_cards[0].hwmon = mlxsw_hwmon;882 mlxsw_hwmon->line_cards[0].slot_index = 0;883 884 err = mlxsw_hwmon_temp_init(&mlxsw_hwmon->line_cards[0]);885 if (err)886 goto err_temp_init;887 888 err = mlxsw_hwmon_fans_init(&mlxsw_hwmon->line_cards[0]);889 if (err)890 goto err_fans_init;891 892 err = mlxsw_hwmon_module_init(&mlxsw_hwmon->line_cards[0]);893 if (err)894 goto err_temp_module_init;895 896 err = mlxsw_hwmon_gearbox_init(&mlxsw_hwmon->line_cards[0]);897 if (err)898 goto err_temp_gearbox_init;899 900 mlxsw_hwmon->line_cards[0].groups[0] = &mlxsw_hwmon->line_cards[0].group;901 mlxsw_hwmon->line_cards[0].group.attrs = mlxsw_hwmon->line_cards[0].attrs;902 903 hwmon_dev = hwmon_device_register_with_groups(mlxsw_bus_info->dev,904 "mlxsw",905 &mlxsw_hwmon->line_cards[0],906 mlxsw_hwmon->line_cards[0].groups);907 if (IS_ERR(hwmon_dev)) {908 err = PTR_ERR(hwmon_dev);909 goto err_hwmon_register;910 }911 912 err = mlxsw_linecards_event_ops_register(mlxsw_hwmon->core,913 &mlxsw_hwmon_event_ops,914 mlxsw_hwmon);915 if (err)916 goto err_linecards_event_ops_register;917 918 mlxsw_hwmon->line_cards[0].hwmon_dev = hwmon_dev;919 mlxsw_hwmon->line_cards[0].active = true;920 *p_hwmon = mlxsw_hwmon;921 return 0;922 923err_linecards_event_ops_register:924 hwmon_device_unregister(mlxsw_hwmon->line_cards[0].hwmon_dev);925err_hwmon_register:926err_temp_gearbox_init:927err_temp_module_init:928err_fans_init:929err_temp_init:930 kfree(mlxsw_hwmon);931 return err;932}933 934void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon)935{936 mlxsw_hwmon->line_cards[0].active = false;937 mlxsw_linecards_event_ops_unregister(mlxsw_hwmon->core,938 &mlxsw_hwmon_event_ops, mlxsw_hwmon);939 hwmon_device_unregister(mlxsw_hwmon->line_cards[0].hwmon_dev);940 kfree(mlxsw_hwmon);941}942