brintos

brintos / linux-shallow public Read only

0
0
Text · 28.3 KiB · 9c12e1f Raw
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