19948a064SJiri Pirko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
29948a064SJiri Pirko /* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved */
389309da3SJiri Pirko 
489309da3SJiri Pirko #include <linux/kernel.h>
589309da3SJiri Pirko #include <linux/types.h>
689309da3SJiri Pirko #include <linux/device.h>
789309da3SJiri Pirko #include <linux/sysfs.h>
889309da3SJiri Pirko #include <linux/hwmon.h>
989309da3SJiri Pirko #include <linux/err.h>
105c42eaa0SVadim Pasternak #include <linux/sfp.h>
1189309da3SJiri Pirko 
1289309da3SJiri Pirko #include "core.h"
135c42eaa0SVadim Pasternak #include "core_env.h"
1489309da3SJiri Pirko 
1502bed4e8SAmit Cohen #define MLXSW_HWMON_SENSORS_MAX_COUNT 64
1602bed4e8SAmit Cohen #define MLXSW_HWMON_MODULES_MAX_COUNT 64
1702bed4e8SAmit Cohen #define MLXSW_HWMON_GEARBOXES_MAX_COUNT 32
1802bed4e8SAmit Cohen 
1902bed4e8SAmit Cohen #define MLXSW_HWMON_ATTR_PER_SENSOR 3
2091df5d3aSAmit Cohen #define MLXSW_HWMON_ATTR_PER_MODULE 7
2102bed4e8SAmit Cohen #define MLXSW_HWMON_ATTR_PER_GEARBOX 4
2202bed4e8SAmit Cohen 
2302bed4e8SAmit Cohen #define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_SENSORS_MAX_COUNT * MLXSW_HWMON_ATTR_PER_SENSOR + \
2402bed4e8SAmit Cohen 				MLXSW_HWMON_MODULES_MAX_COUNT * MLXSW_HWMON_ATTR_PER_MODULE + \
2502bed4e8SAmit Cohen 				MLXSW_HWMON_GEARBOXES_MAX_COUNT * MLXSW_HWMON_ATTR_PER_GEARBOX + \
2652581961SJiri Pirko 				MLXSW_MFCR_TACHOS_MAX + MLXSW_MFCR_PWMS_MAX)
2789309da3SJiri Pirko 
2889309da3SJiri Pirko struct mlxsw_hwmon_attr {
2989309da3SJiri Pirko 	struct device_attribute dev_attr;
3089309da3SJiri Pirko 	struct mlxsw_hwmon *hwmon;
3189309da3SJiri Pirko 	unsigned int type_index;
32e7bc73cbSJiri Pirko 	char name[32];
3389309da3SJiri Pirko };
3489309da3SJiri Pirko 
352e265a8bSVadim Pasternak static int mlxsw_hwmon_get_attr_index(int index, int count)
362e265a8bSVadim Pasternak {
372e265a8bSVadim Pasternak 	if (index >= count)
382e265a8bSVadim Pasternak 		return index % count + MLXSW_REG_MTMP_GBOX_INDEX_MIN;
392e265a8bSVadim Pasternak 
402e265a8bSVadim Pasternak 	return index;
412e265a8bSVadim Pasternak }
422e265a8bSVadim Pasternak 
4389309da3SJiri Pirko struct mlxsw_hwmon {
4489309da3SJiri Pirko 	struct mlxsw_core *core;
4589309da3SJiri Pirko 	const struct mlxsw_bus_info *bus_info;
4689309da3SJiri Pirko 	struct device *hwmon_dev;
4789309da3SJiri Pirko 	struct attribute_group group;
4889309da3SJiri Pirko 	const struct attribute_group *groups[2];
4989309da3SJiri Pirko 	struct attribute *attrs[MLXSW_HWMON_ATTR_COUNT + 1];
5089309da3SJiri Pirko 	struct mlxsw_hwmon_attr hwmon_attrs[MLXSW_HWMON_ATTR_COUNT];
5189309da3SJiri Pirko 	unsigned int attrs_count;
525c42eaa0SVadim Pasternak 	u8 sensor_count;
53ea30a92aSVadim Pasternak 	u8 module_sensor_max;
5489309da3SJiri Pirko };
5589309da3SJiri Pirko 
5689309da3SJiri Pirko static ssize_t mlxsw_hwmon_temp_show(struct device *dev,
5789309da3SJiri Pirko 				     struct device_attribute *attr,
5889309da3SJiri Pirko 				     char *buf)
5989309da3SJiri Pirko {
6089309da3SJiri Pirko 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
6189309da3SJiri Pirko 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
6289309da3SJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
6389309da3SJiri Pirko 	char mtmp_pl[MLXSW_REG_MTMP_LEN];
64f485cc36SVadim Pasternak 	int temp, index;
6589309da3SJiri Pirko 	int err;
6689309da3SJiri Pirko 
672e265a8bSVadim Pasternak 	index = mlxsw_hwmon_get_attr_index(mlwsw_hwmon_attr->type_index,
68ea30a92aSVadim Pasternak 					   mlxsw_hwmon->module_sensor_max);
692e265a8bSVadim Pasternak 	mlxsw_reg_mtmp_pack(mtmp_pl, index, false, false);
7089309da3SJiri Pirko 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
7189309da3SJiri Pirko 	if (err) {
7289309da3SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");
7389309da3SJiri Pirko 		return err;
7489309da3SJiri Pirko 	}
75*314dbb19SMykola Kostenok 	mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL, NULL, NULL);
76f485cc36SVadim Pasternak 	return sprintf(buf, "%d\n", temp);
7789309da3SJiri Pirko }
7889309da3SJiri Pirko 
7989309da3SJiri Pirko static ssize_t mlxsw_hwmon_temp_max_show(struct device *dev,
8089309da3SJiri Pirko 					 struct device_attribute *attr,
8189309da3SJiri Pirko 					 char *buf)
8289309da3SJiri Pirko {
8389309da3SJiri Pirko 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
8489309da3SJiri Pirko 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
8589309da3SJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
8689309da3SJiri Pirko 	char mtmp_pl[MLXSW_REG_MTMP_LEN];
87f485cc36SVadim Pasternak 	int temp_max, index;
8889309da3SJiri Pirko 	int err;
8989309da3SJiri Pirko 
902e265a8bSVadim Pasternak 	index = mlxsw_hwmon_get_attr_index(mlwsw_hwmon_attr->type_index,
91ea30a92aSVadim Pasternak 					   mlxsw_hwmon->module_sensor_max);
922e265a8bSVadim Pasternak 	mlxsw_reg_mtmp_pack(mtmp_pl, index, false, false);
9389309da3SJiri Pirko 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
9489309da3SJiri Pirko 	if (err) {
9589309da3SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");
9689309da3SJiri Pirko 		return err;
9789309da3SJiri Pirko 	}
98*314dbb19SMykola Kostenok 	mlxsw_reg_mtmp_unpack(mtmp_pl, NULL, &temp_max, NULL, NULL, NULL);
99f485cc36SVadim Pasternak 	return sprintf(buf, "%d\n", temp_max);
10089309da3SJiri Pirko }
10189309da3SJiri Pirko 
102e7bc73cbSJiri Pirko static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev,
103e7bc73cbSJiri Pirko 					  struct device_attribute *attr,
104e7bc73cbSJiri Pirko 					  const char *buf, size_t len)
105e7bc73cbSJiri Pirko {
106e7bc73cbSJiri Pirko 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
107e7bc73cbSJiri Pirko 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
108e7bc73cbSJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
109fb1292f8SAmit Cohen 	char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0};
110e7bc73cbSJiri Pirko 	unsigned long val;
1112e265a8bSVadim Pasternak 	int index;
112e7bc73cbSJiri Pirko 	int err;
113e7bc73cbSJiri Pirko 
114e7bc73cbSJiri Pirko 	err = kstrtoul(buf, 10, &val);
115e7bc73cbSJiri Pirko 	if (err)
116e7bc73cbSJiri Pirko 		return err;
117e7bc73cbSJiri Pirko 	if (val != 1)
118e7bc73cbSJiri Pirko 		return -EINVAL;
119e7bc73cbSJiri Pirko 
1202e265a8bSVadim Pasternak 	index = mlxsw_hwmon_get_attr_index(mlwsw_hwmon_attr->type_index,
121ea30a92aSVadim Pasternak 					   mlxsw_hwmon->module_sensor_max);
122fb1292f8SAmit Cohen 
123fb1292f8SAmit Cohen 	mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, index);
124fb1292f8SAmit Cohen 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
125fb1292f8SAmit Cohen 	if (err)
126fb1292f8SAmit Cohen 		return err;
127fb1292f8SAmit Cohen 	mlxsw_reg_mtmp_mte_set(mtmp_pl, true);
128fb1292f8SAmit Cohen 	mlxsw_reg_mtmp_mtr_set(mtmp_pl, true);
129e7bc73cbSJiri Pirko 	err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
130e7bc73cbSJiri Pirko 	if (err) {
131e7bc73cbSJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to reset temp sensor history\n");
132e7bc73cbSJiri Pirko 		return err;
133e7bc73cbSJiri Pirko 	}
134719255d0SDan Carpenter 	return len;
135e7bc73cbSJiri Pirko }
136e7bc73cbSJiri Pirko 
13752581961SJiri Pirko static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev,
13852581961SJiri Pirko 					struct device_attribute *attr,
13952581961SJiri Pirko 					char *buf)
14052581961SJiri Pirko {
14152581961SJiri Pirko 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
14252581961SJiri Pirko 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
14352581961SJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
14452581961SJiri Pirko 	char mfsm_pl[MLXSW_REG_MFSM_LEN];
14552581961SJiri Pirko 	int err;
14652581961SJiri Pirko 
14752581961SJiri Pirko 	mlxsw_reg_mfsm_pack(mfsm_pl, mlwsw_hwmon_attr->type_index);
14852581961SJiri Pirko 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsm), mfsm_pl);
14952581961SJiri Pirko 	if (err) {
15052581961SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n");
15152581961SJiri Pirko 		return err;
15252581961SJiri Pirko 	}
15352581961SJiri Pirko 	return sprintf(buf, "%u\n", mlxsw_reg_mfsm_rpm_get(mfsm_pl));
15452581961SJiri Pirko }
15552581961SJiri Pirko 
1562c6a33cdSVadim Pasternak static ssize_t mlxsw_hwmon_fan_fault_show(struct device *dev,
1572c6a33cdSVadim Pasternak 					  struct device_attribute *attr,
1582c6a33cdSVadim Pasternak 					  char *buf)
1592c6a33cdSVadim Pasternak {
1602c6a33cdSVadim Pasternak 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
1612c6a33cdSVadim Pasternak 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
1622c6a33cdSVadim Pasternak 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
1632c6a33cdSVadim Pasternak 	char fore_pl[MLXSW_REG_FORE_LEN];
1642c6a33cdSVadim Pasternak 	bool fault;
1652c6a33cdSVadim Pasternak 	int err;
1662c6a33cdSVadim Pasternak 
1672c6a33cdSVadim Pasternak 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(fore), fore_pl);
1682c6a33cdSVadim Pasternak 	if (err) {
1692c6a33cdSVadim Pasternak 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n");
1702c6a33cdSVadim Pasternak 		return err;
1712c6a33cdSVadim Pasternak 	}
1722c6a33cdSVadim Pasternak 	mlxsw_reg_fore_unpack(fore_pl, mlwsw_hwmon_attr->type_index, &fault);
1732c6a33cdSVadim Pasternak 
1742c6a33cdSVadim Pasternak 	return sprintf(buf, "%u\n", fault);
1752c6a33cdSVadim Pasternak }
1762c6a33cdSVadim Pasternak 
17752581961SJiri Pirko static ssize_t mlxsw_hwmon_pwm_show(struct device *dev,
17852581961SJiri Pirko 				    struct device_attribute *attr,
17952581961SJiri Pirko 				    char *buf)
18052581961SJiri Pirko {
18152581961SJiri Pirko 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
18252581961SJiri Pirko 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
18352581961SJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
18452581961SJiri Pirko 	char mfsc_pl[MLXSW_REG_MFSC_LEN];
18552581961SJiri Pirko 	int err;
18652581961SJiri Pirko 
18752581961SJiri Pirko 	mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, 0);
18852581961SJiri Pirko 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl);
18952581961SJiri Pirko 	if (err) {
19052581961SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query PWM\n");
19152581961SJiri Pirko 		return err;
19252581961SJiri Pirko 	}
19352581961SJiri Pirko 	return sprintf(buf, "%u\n",
19452581961SJiri Pirko 		       mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl));
19552581961SJiri Pirko }
19652581961SJiri Pirko 
19752581961SJiri Pirko static ssize_t mlxsw_hwmon_pwm_store(struct device *dev,
19852581961SJiri Pirko 				     struct device_attribute *attr,
19952581961SJiri Pirko 				     const char *buf, size_t len)
20052581961SJiri Pirko {
20152581961SJiri Pirko 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
20252581961SJiri Pirko 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
20352581961SJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
20452581961SJiri Pirko 	char mfsc_pl[MLXSW_REG_MFSC_LEN];
20552581961SJiri Pirko 	unsigned long val;
20652581961SJiri Pirko 	int err;
20752581961SJiri Pirko 
20852581961SJiri Pirko 	err = kstrtoul(buf, 10, &val);
20952581961SJiri Pirko 	if (err)
21052581961SJiri Pirko 		return err;
21152581961SJiri Pirko 	if (val > 255)
21252581961SJiri Pirko 		return -EINVAL;
21352581961SJiri Pirko 
21452581961SJiri Pirko 	mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, val);
21552581961SJiri Pirko 	err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl);
21652581961SJiri Pirko 	if (err) {
21752581961SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to write PWM\n");
21852581961SJiri Pirko 		return err;
21952581961SJiri Pirko 	}
220515123e2SDan Carpenter 	return len;
22152581961SJiri Pirko }
22252581961SJiri Pirko 
223ad38d47bSAmit Cohen static int mlxsw_hwmon_module_temp_get(struct device *dev,
2245c42eaa0SVadim Pasternak 				       struct device_attribute *attr,
225ad38d47bSAmit Cohen 				       int *p_temp)
2265c42eaa0SVadim Pasternak {
2275c42eaa0SVadim Pasternak 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
2285c42eaa0SVadim Pasternak 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
2295c42eaa0SVadim Pasternak 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
230e4e93d6dSVadim Pasternak 	char mtmp_pl[MLXSW_REG_MTMP_LEN];
2315c42eaa0SVadim Pasternak 	u8 module;
2325c42eaa0SVadim Pasternak 	int err;
2335c42eaa0SVadim Pasternak 
2345c42eaa0SVadim Pasternak 	module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
235e4e93d6dSVadim Pasternak 	mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module,
236e4e93d6dSVadim Pasternak 			    false, false);
237e4e93d6dSVadim Pasternak 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
238ad38d47bSAmit Cohen 	if (err) {
239ad38d47bSAmit Cohen 		dev_err(dev, "Failed to query module temperature\n");
240ad38d47bSAmit Cohen 		return err;
241ad38d47bSAmit Cohen 	}
242*314dbb19SMykola Kostenok 	mlxsw_reg_mtmp_unpack(mtmp_pl, p_temp, NULL, NULL, NULL, NULL);
243ad38d47bSAmit Cohen 
244ad38d47bSAmit Cohen 	return 0;
245ad38d47bSAmit Cohen }
246ad38d47bSAmit Cohen 
247ad38d47bSAmit Cohen static ssize_t mlxsw_hwmon_module_temp_show(struct device *dev,
248ad38d47bSAmit Cohen 					    struct device_attribute *attr,
249ad38d47bSAmit Cohen 					    char *buf)
250ad38d47bSAmit Cohen {
251ad38d47bSAmit Cohen 	int err, temp;
252ad38d47bSAmit Cohen 
253ad38d47bSAmit Cohen 	err = mlxsw_hwmon_module_temp_get(dev, attr, &temp);
254e4e93d6dSVadim Pasternak 	if (err)
2555c42eaa0SVadim Pasternak 		return err;
2565c42eaa0SVadim Pasternak 
257f485cc36SVadim Pasternak 	return sprintf(buf, "%d\n", temp);
2585c42eaa0SVadim Pasternak }
2595c42eaa0SVadim Pasternak 
2605c42eaa0SVadim Pasternak static ssize_t mlxsw_hwmon_module_temp_fault_show(struct device *dev,
2615c42eaa0SVadim Pasternak 						  struct device_attribute *attr,
2625c42eaa0SVadim Pasternak 						  char *buf)
2635c42eaa0SVadim Pasternak {
2645c42eaa0SVadim Pasternak 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
2655c42eaa0SVadim Pasternak 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
2665c42eaa0SVadim Pasternak 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
2675c42eaa0SVadim Pasternak 	char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0};
2685c42eaa0SVadim Pasternak 	u8 module, fault;
2695c42eaa0SVadim Pasternak 	u16 temp;
2705c42eaa0SVadim Pasternak 	int err;
2715c42eaa0SVadim Pasternak 
2725c42eaa0SVadim Pasternak 	module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
2735c42eaa0SVadim Pasternak 	mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
2745c42eaa0SVadim Pasternak 			    1);
2755c42eaa0SVadim Pasternak 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtbr), mtbr_pl);
2765c42eaa0SVadim Pasternak 	if (err) {
27759e6158aSColin Ian King 		dev_err(dev, "Failed to query module temperature sensor\n");
2785c42eaa0SVadim Pasternak 		return err;
2795c42eaa0SVadim Pasternak 	}
2805c42eaa0SVadim Pasternak 
2815c42eaa0SVadim Pasternak 	mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
2825c42eaa0SVadim Pasternak 
2835c42eaa0SVadim Pasternak 	/* Update status and temperature cache. */
2845c42eaa0SVadim Pasternak 	switch (temp) {
2855c42eaa0SVadim Pasternak 	case MLXSW_REG_MTBR_BAD_SENS_INFO:
2865c42eaa0SVadim Pasternak 		/* Untrusted cable is connected. Reading temperature from its
2875c42eaa0SVadim Pasternak 		 * sensor is faulty.
2885c42eaa0SVadim Pasternak 		 */
2895c42eaa0SVadim Pasternak 		fault = 1;
2905c42eaa0SVadim Pasternak 		break;
291df561f66SGustavo A. R. Silva 	case MLXSW_REG_MTBR_NO_CONN:
292df561f66SGustavo A. R. Silva 	case MLXSW_REG_MTBR_NO_TEMP_SENS:
2935c42eaa0SVadim Pasternak 	case MLXSW_REG_MTBR_INDEX_NA:
2945c42eaa0SVadim Pasternak 	default:
2955c42eaa0SVadim Pasternak 		fault = 0;
2965c42eaa0SVadim Pasternak 		break;
2975c42eaa0SVadim Pasternak 	}
2985c42eaa0SVadim Pasternak 
2995c42eaa0SVadim Pasternak 	return sprintf(buf, "%u\n", fault);
3005c42eaa0SVadim Pasternak }
3015c42eaa0SVadim Pasternak 
302ad38d47bSAmit Cohen static int mlxsw_hwmon_module_temp_critical_get(struct device *dev,
303ad38d47bSAmit Cohen 						struct device_attribute *attr,
304ad38d47bSAmit Cohen 						int *p_temp)
3055c42eaa0SVadim Pasternak {
3065c42eaa0SVadim Pasternak 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
3075c42eaa0SVadim Pasternak 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
3085c42eaa0SVadim Pasternak 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
3095c42eaa0SVadim Pasternak 	u8 module;
3105c42eaa0SVadim Pasternak 	int err;
3115c42eaa0SVadim Pasternak 
3125c42eaa0SVadim Pasternak 	module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
3135c42eaa0SVadim Pasternak 	err = mlxsw_env_module_temp_thresholds_get(mlxsw_hwmon->core, module,
314ad38d47bSAmit Cohen 						   SFP_TEMP_HIGH_WARN, p_temp);
3155c42eaa0SVadim Pasternak 	if (err) {
31659e6158aSColin Ian King 		dev_err(dev, "Failed to query module temperature thresholds\n");
3175c42eaa0SVadim Pasternak 		return err;
3185c42eaa0SVadim Pasternak 	}
3195c42eaa0SVadim Pasternak 
320ad38d47bSAmit Cohen 	return 0;
321ad38d47bSAmit Cohen }
322ad38d47bSAmit Cohen 
323ad38d47bSAmit Cohen static ssize_t
324ad38d47bSAmit Cohen mlxsw_hwmon_module_temp_critical_show(struct device *dev,
325ad38d47bSAmit Cohen 				      struct device_attribute *attr, char *buf)
326ad38d47bSAmit Cohen {
327ad38d47bSAmit Cohen 	int err, temp;
328ad38d47bSAmit Cohen 
329ad38d47bSAmit Cohen 	err = mlxsw_hwmon_module_temp_critical_get(dev, attr, &temp);
330ad38d47bSAmit Cohen 	if (err)
331ad38d47bSAmit Cohen 		return err;
332ad38d47bSAmit Cohen 
3335c42eaa0SVadim Pasternak 	return sprintf(buf, "%u\n", temp);
3345c42eaa0SVadim Pasternak }
3355c42eaa0SVadim Pasternak 
336ad38d47bSAmit Cohen static int mlxsw_hwmon_module_temp_emergency_get(struct device *dev,
337ad38d47bSAmit Cohen 						 struct device_attribute *attr,
338ad38d47bSAmit Cohen 						 int *p_temp)
339ad38d47bSAmit Cohen {
340ad38d47bSAmit Cohen 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
341ad38d47bSAmit Cohen 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
342ad38d47bSAmit Cohen 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
343ad38d47bSAmit Cohen 	u8 module;
344ad38d47bSAmit Cohen 	int err;
345ad38d47bSAmit Cohen 
346ad38d47bSAmit Cohen 	module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
347ad38d47bSAmit Cohen 	err = mlxsw_env_module_temp_thresholds_get(mlxsw_hwmon->core, module,
348ad38d47bSAmit Cohen 						   SFP_TEMP_HIGH_ALARM, p_temp);
349ad38d47bSAmit Cohen 	if (err) {
350ad38d47bSAmit Cohen 		dev_err(dev, "Failed to query module temperature thresholds\n");
351ad38d47bSAmit Cohen 		return err;
352ad38d47bSAmit Cohen 	}
353ad38d47bSAmit Cohen 
354ad38d47bSAmit Cohen 	return 0;
355ad38d47bSAmit Cohen }
356ad38d47bSAmit Cohen 
3575c42eaa0SVadim Pasternak static ssize_t
3585c42eaa0SVadim Pasternak mlxsw_hwmon_module_temp_emergency_show(struct device *dev,
3595c42eaa0SVadim Pasternak 				       struct device_attribute *attr,
3605c42eaa0SVadim Pasternak 				       char *buf)
3615c42eaa0SVadim Pasternak {
362ad38d47bSAmit Cohen 	int err, temp;
3635c42eaa0SVadim Pasternak 
364ad38d47bSAmit Cohen 	err = mlxsw_hwmon_module_temp_emergency_get(dev, attr, &temp);
365ad38d47bSAmit Cohen 	if (err)
3665c42eaa0SVadim Pasternak 		return err;
3675c42eaa0SVadim Pasternak 
3685c42eaa0SVadim Pasternak 	return sprintf(buf, "%u\n", temp);
3695c42eaa0SVadim Pasternak }
3705c42eaa0SVadim Pasternak 
371a53779deSVadim Pasternak static ssize_t
372a53779deSVadim Pasternak mlxsw_hwmon_module_temp_label_show(struct device *dev,
373a53779deSVadim Pasternak 				   struct device_attribute *attr,
374a53779deSVadim Pasternak 				   char *buf)
375a53779deSVadim Pasternak {
376a53779deSVadim Pasternak 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
377a53779deSVadim Pasternak 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
378a53779deSVadim Pasternak 
379a53779deSVadim Pasternak 	return sprintf(buf, "front panel %03u\n",
380a53779deSVadim Pasternak 		       mlwsw_hwmon_attr->type_index);
381a53779deSVadim Pasternak }
382a53779deSVadim Pasternak 
3832e265a8bSVadim Pasternak static ssize_t
3842e265a8bSVadim Pasternak mlxsw_hwmon_gbox_temp_label_show(struct device *dev,
3852e265a8bSVadim Pasternak 				 struct device_attribute *attr,
3862e265a8bSVadim Pasternak 				 char *buf)
3872e265a8bSVadim Pasternak {
3882e265a8bSVadim Pasternak 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
3892e265a8bSVadim Pasternak 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
3902e265a8bSVadim Pasternak 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
3912e265a8bSVadim Pasternak 	int index = mlwsw_hwmon_attr->type_index -
392ea30a92aSVadim Pasternak 		    mlxsw_hwmon->module_sensor_max + 1;
3932e265a8bSVadim Pasternak 
3942e265a8bSVadim Pasternak 	return sprintf(buf, "gearbox %03u\n", index);
3952e265a8bSVadim Pasternak }
3962e265a8bSVadim Pasternak 
39791df5d3aSAmit Cohen static ssize_t mlxsw_hwmon_temp_critical_alarm_show(struct device *dev,
39891df5d3aSAmit Cohen 						    struct device_attribute *attr,
39991df5d3aSAmit Cohen 						    char *buf)
40091df5d3aSAmit Cohen {
40191df5d3aSAmit Cohen 	int err, temp, emergency_temp, critic_temp;
40291df5d3aSAmit Cohen 
40391df5d3aSAmit Cohen 	err = mlxsw_hwmon_module_temp_get(dev, attr, &temp);
40491df5d3aSAmit Cohen 	if (err)
40591df5d3aSAmit Cohen 		return err;
40691df5d3aSAmit Cohen 
40791df5d3aSAmit Cohen 	if (temp <= 0)
40891df5d3aSAmit Cohen 		return sprintf(buf, "%d\n", false);
40991df5d3aSAmit Cohen 
41091df5d3aSAmit Cohen 	err = mlxsw_hwmon_module_temp_emergency_get(dev, attr, &emergency_temp);
41191df5d3aSAmit Cohen 	if (err)
41291df5d3aSAmit Cohen 		return err;
41391df5d3aSAmit Cohen 
41491df5d3aSAmit Cohen 	if (temp >= emergency_temp)
41591df5d3aSAmit Cohen 		return sprintf(buf, "%d\n", false);
41691df5d3aSAmit Cohen 
41791df5d3aSAmit Cohen 	err = mlxsw_hwmon_module_temp_critical_get(dev, attr, &critic_temp);
41891df5d3aSAmit Cohen 	if (err)
41991df5d3aSAmit Cohen 		return err;
42091df5d3aSAmit Cohen 
42191df5d3aSAmit Cohen 	return sprintf(buf, "%d\n", temp >= critic_temp);
42291df5d3aSAmit Cohen }
42391df5d3aSAmit Cohen 
42491df5d3aSAmit Cohen static ssize_t mlxsw_hwmon_temp_emergency_alarm_show(struct device *dev,
42591df5d3aSAmit Cohen 						     struct device_attribute *attr,
42691df5d3aSAmit Cohen 						     char *buf)
42791df5d3aSAmit Cohen {
42891df5d3aSAmit Cohen 	int err, temp, emergency_temp;
42991df5d3aSAmit Cohen 
43091df5d3aSAmit Cohen 	err = mlxsw_hwmon_module_temp_get(dev, attr, &temp);
43191df5d3aSAmit Cohen 	if (err)
43291df5d3aSAmit Cohen 		return err;
43391df5d3aSAmit Cohen 
43491df5d3aSAmit Cohen 	if (temp <= 0)
43591df5d3aSAmit Cohen 		return sprintf(buf, "%d\n", false);
43691df5d3aSAmit Cohen 
43791df5d3aSAmit Cohen 	err = mlxsw_hwmon_module_temp_emergency_get(dev, attr, &emergency_temp);
43891df5d3aSAmit Cohen 	if (err)
43991df5d3aSAmit Cohen 		return err;
44091df5d3aSAmit Cohen 
44191df5d3aSAmit Cohen 	return sprintf(buf, "%d\n", temp >= emergency_temp);
44291df5d3aSAmit Cohen }
44391df5d3aSAmit Cohen 
44489309da3SJiri Pirko enum mlxsw_hwmon_attr_type {
44589309da3SJiri Pirko 	MLXSW_HWMON_ATTR_TYPE_TEMP,
44689309da3SJiri Pirko 	MLXSW_HWMON_ATTR_TYPE_TEMP_MAX,
447e7bc73cbSJiri Pirko 	MLXSW_HWMON_ATTR_TYPE_TEMP_RST,
44852581961SJiri Pirko 	MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
4492c6a33cdSVadim Pasternak 	MLXSW_HWMON_ATTR_TYPE_FAN_FAULT,
45052581961SJiri Pirko 	MLXSW_HWMON_ATTR_TYPE_PWM,
4515c42eaa0SVadim Pasternak 	MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE,
4525c42eaa0SVadim Pasternak 	MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT,
4535c42eaa0SVadim Pasternak 	MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT,
4545c42eaa0SVadim Pasternak 	MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG,
455a53779deSVadim Pasternak 	MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL,
4562e265a8bSVadim Pasternak 	MLXSW_HWMON_ATTR_TYPE_TEMP_GBOX_LABEL,
45791df5d3aSAmit Cohen 	MLXSW_HWMON_ATTR_TYPE_TEMP_CRIT_ALARM,
45891df5d3aSAmit Cohen 	MLXSW_HWMON_ATTR_TYPE_TEMP_EMERGENCY_ALARM,
45989309da3SJiri Pirko };
46089309da3SJiri Pirko 
46189309da3SJiri Pirko static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
46289309da3SJiri Pirko 				 enum mlxsw_hwmon_attr_type attr_type,
46389309da3SJiri Pirko 				 unsigned int type_index, unsigned int num) {
46489309da3SJiri Pirko 	struct mlxsw_hwmon_attr *mlxsw_hwmon_attr;
46589309da3SJiri Pirko 	unsigned int attr_index;
46689309da3SJiri Pirko 
46789309da3SJiri Pirko 	attr_index = mlxsw_hwmon->attrs_count;
46889309da3SJiri Pirko 	mlxsw_hwmon_attr = &mlxsw_hwmon->hwmon_attrs[attr_index];
46989309da3SJiri Pirko 
47089309da3SJiri Pirko 	switch (attr_type) {
47189309da3SJiri Pirko 	case MLXSW_HWMON_ATTR_TYPE_TEMP:
47289309da3SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_show;
473d3757ba4SJoe Perches 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
47489309da3SJiri Pirko 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
47589309da3SJiri Pirko 			 "temp%u_input", num + 1);
47689309da3SJiri Pirko 		break;
47789309da3SJiri Pirko 	case MLXSW_HWMON_ATTR_TYPE_TEMP_MAX:
47889309da3SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_max_show;
479d3757ba4SJoe Perches 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
48089309da3SJiri Pirko 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
48189309da3SJiri Pirko 			 "temp%u_highest", num + 1);
48289309da3SJiri Pirko 		break;
483e7bc73cbSJiri Pirko 	case MLXSW_HWMON_ATTR_TYPE_TEMP_RST:
484e7bc73cbSJiri Pirko 		mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_temp_rst_store;
485d3757ba4SJoe Perches 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0200;
486e7bc73cbSJiri Pirko 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
487e7bc73cbSJiri Pirko 			 "temp%u_reset_history", num + 1);
488e7bc73cbSJiri Pirko 		break;
48952581961SJiri Pirko 	case MLXSW_HWMON_ATTR_TYPE_FAN_RPM:
49052581961SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_rpm_show;
491d3757ba4SJoe Perches 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
49252581961SJiri Pirko 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
49352581961SJiri Pirko 			 "fan%u_input", num + 1);
49452581961SJiri Pirko 		break;
4952c6a33cdSVadim Pasternak 	case MLXSW_HWMON_ATTR_TYPE_FAN_FAULT:
4962c6a33cdSVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_fault_show;
4972c6a33cdSVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
4982c6a33cdSVadim Pasternak 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
4992c6a33cdSVadim Pasternak 			 "fan%u_fault", num + 1);
5002c6a33cdSVadim Pasternak 		break;
50152581961SJiri Pirko 	case MLXSW_HWMON_ATTR_TYPE_PWM:
50252581961SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_pwm_show;
50352581961SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_pwm_store;
504d3757ba4SJoe Perches 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0644;
50552581961SJiri Pirko 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
50652581961SJiri Pirko 			 "pwm%u", num + 1);
50752581961SJiri Pirko 		break;
5085c42eaa0SVadim Pasternak 	case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE:
5095c42eaa0SVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_module_temp_show;
5105c42eaa0SVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
5115c42eaa0SVadim Pasternak 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
5125c42eaa0SVadim Pasternak 			 "temp%u_input", num + 1);
5135c42eaa0SVadim Pasternak 		break;
5145c42eaa0SVadim Pasternak 	case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT:
5155c42eaa0SVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.show =
5165c42eaa0SVadim Pasternak 					mlxsw_hwmon_module_temp_fault_show;
5175c42eaa0SVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
5185c42eaa0SVadim Pasternak 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
5195c42eaa0SVadim Pasternak 			 "temp%u_fault", num + 1);
5205c42eaa0SVadim Pasternak 		break;
5215c42eaa0SVadim Pasternak 	case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT:
5225c42eaa0SVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.show =
5235c42eaa0SVadim Pasternak 			mlxsw_hwmon_module_temp_critical_show;
5245c42eaa0SVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
5255c42eaa0SVadim Pasternak 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
5265c42eaa0SVadim Pasternak 			 "temp%u_crit", num + 1);
5275c42eaa0SVadim Pasternak 		break;
5285c42eaa0SVadim Pasternak 	case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG:
5295c42eaa0SVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.show =
5305c42eaa0SVadim Pasternak 			mlxsw_hwmon_module_temp_emergency_show;
5315c42eaa0SVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
5325c42eaa0SVadim Pasternak 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
5335c42eaa0SVadim Pasternak 			 "temp%u_emergency", num + 1);
5345c42eaa0SVadim Pasternak 		break;
535a53779deSVadim Pasternak 	case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL:
536a53779deSVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.show =
537a53779deSVadim Pasternak 			mlxsw_hwmon_module_temp_label_show;
538a53779deSVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
539a53779deSVadim Pasternak 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
540a53779deSVadim Pasternak 			 "temp%u_label", num + 1);
541a53779deSVadim Pasternak 		break;
5422e265a8bSVadim Pasternak 	case MLXSW_HWMON_ATTR_TYPE_TEMP_GBOX_LABEL:
5432e265a8bSVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.show =
5442e265a8bSVadim Pasternak 			mlxsw_hwmon_gbox_temp_label_show;
5452e265a8bSVadim Pasternak 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
5462e265a8bSVadim Pasternak 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
5472e265a8bSVadim Pasternak 			 "temp%u_label", num + 1);
5482e265a8bSVadim Pasternak 		break;
54991df5d3aSAmit Cohen 	case MLXSW_HWMON_ATTR_TYPE_TEMP_CRIT_ALARM:
55091df5d3aSAmit Cohen 		mlxsw_hwmon_attr->dev_attr.show =
55191df5d3aSAmit Cohen 			mlxsw_hwmon_temp_critical_alarm_show;
55291df5d3aSAmit Cohen 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
55391df5d3aSAmit Cohen 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
55491df5d3aSAmit Cohen 			 "temp%u_crit_alarm", num + 1);
55591df5d3aSAmit Cohen 		break;
55691df5d3aSAmit Cohen 	case MLXSW_HWMON_ATTR_TYPE_TEMP_EMERGENCY_ALARM:
55791df5d3aSAmit Cohen 		mlxsw_hwmon_attr->dev_attr.show =
55891df5d3aSAmit Cohen 			mlxsw_hwmon_temp_emergency_alarm_show;
55991df5d3aSAmit Cohen 		mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
56091df5d3aSAmit Cohen 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
56191df5d3aSAmit Cohen 			 "temp%u_emergency_alarm", num + 1);
56291df5d3aSAmit Cohen 		break;
56389309da3SJiri Pirko 	default:
5646b20da4dSJiri Pirko 		WARN_ON(1);
56589309da3SJiri Pirko 	}
56689309da3SJiri Pirko 
56789309da3SJiri Pirko 	mlxsw_hwmon_attr->type_index = type_index;
56889309da3SJiri Pirko 	mlxsw_hwmon_attr->hwmon = mlxsw_hwmon;
56989309da3SJiri Pirko 	mlxsw_hwmon_attr->dev_attr.attr.name = mlxsw_hwmon_attr->name;
57089309da3SJiri Pirko 	sysfs_attr_init(&mlxsw_hwmon_attr->dev_attr.attr);
57189309da3SJiri Pirko 
57289309da3SJiri Pirko 	mlxsw_hwmon->attrs[attr_index] = &mlxsw_hwmon_attr->dev_attr.attr;
57389309da3SJiri Pirko 	mlxsw_hwmon->attrs_count++;
57489309da3SJiri Pirko }
57589309da3SJiri Pirko 
57689309da3SJiri Pirko static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon)
57789309da3SJiri Pirko {
5785b090740SElad Raz 	char mtcap_pl[MLXSW_REG_MTCAP_LEN] = {0};
57989309da3SJiri Pirko 	int i;
58089309da3SJiri Pirko 	int err;
58189309da3SJiri Pirko 
58289309da3SJiri Pirko 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtcap), mtcap_pl);
58389309da3SJiri Pirko 	if (err) {
58489309da3SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get number of temp sensors\n");
58589309da3SJiri Pirko 		return err;
58689309da3SJiri Pirko 	}
5875c42eaa0SVadim Pasternak 	mlxsw_hwmon->sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl);
5885c42eaa0SVadim Pasternak 	for (i = 0; i < mlxsw_hwmon->sensor_count; i++) {
589fb1292f8SAmit Cohen 		char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0};
590fb1292f8SAmit Cohen 
591fb1292f8SAmit Cohen 		mlxsw_reg_mtmp_sensor_index_set(mtmp_pl, i);
592fb1292f8SAmit Cohen 		err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp),
593fb1292f8SAmit Cohen 				      mtmp_pl);
594fb1292f8SAmit Cohen 		if (err)
595fb1292f8SAmit Cohen 			return err;
596fb1292f8SAmit Cohen 		mlxsw_reg_mtmp_mte_set(mtmp_pl, true);
597fb1292f8SAmit Cohen 		mlxsw_reg_mtmp_mtr_set(mtmp_pl, true);
59889309da3SJiri Pirko 		err = mlxsw_reg_write(mlxsw_hwmon->core,
59989309da3SJiri Pirko 				      MLXSW_REG(mtmp), mtmp_pl);
60089309da3SJiri Pirko 		if (err) {
60189309da3SJiri Pirko 			dev_err(mlxsw_hwmon->bus_info->dev, "Failed to setup temp sensor number %d\n",
60289309da3SJiri Pirko 				i);
60389309da3SJiri Pirko 			return err;
60489309da3SJiri Pirko 		}
60589309da3SJiri Pirko 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
60689309da3SJiri Pirko 				     MLXSW_HWMON_ATTR_TYPE_TEMP, i, i);
60789309da3SJiri Pirko 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
60889309da3SJiri Pirko 				     MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, i, i);
609e7bc73cbSJiri Pirko 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
610e7bc73cbSJiri Pirko 				     MLXSW_HWMON_ATTR_TYPE_TEMP_RST, i, i);
61189309da3SJiri Pirko 	}
61289309da3SJiri Pirko 	return 0;
61389309da3SJiri Pirko }
61489309da3SJiri Pirko 
61552581961SJiri Pirko static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon)
61652581961SJiri Pirko {
6175b090740SElad Raz 	char mfcr_pl[MLXSW_REG_MFCR_LEN] = {0};
61852581961SJiri Pirko 	enum mlxsw_reg_mfcr_pwm_frequency freq;
61952581961SJiri Pirko 	unsigned int type_index;
62052581961SJiri Pirko 	unsigned int num;
62152581961SJiri Pirko 	u16 tacho_active;
62252581961SJiri Pirko 	u8 pwm_active;
62352581961SJiri Pirko 	int err;
62452581961SJiri Pirko 
62552581961SJiri Pirko 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfcr), mfcr_pl);
62652581961SJiri Pirko 	if (err) {
62752581961SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get to probe PWMs and Tachometers\n");
62852581961SJiri Pirko 		return err;
62952581961SJiri Pirko 	}
63052581961SJiri Pirko 	mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active);
63152581961SJiri Pirko 	num = 0;
63252581961SJiri Pirko 	for (type_index = 0; type_index < MLXSW_MFCR_TACHOS_MAX; type_index++) {
6332c6a33cdSVadim Pasternak 		if (tacho_active & BIT(type_index)) {
63452581961SJiri Pirko 			mlxsw_hwmon_attr_add(mlxsw_hwmon,
63552581961SJiri Pirko 					     MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
6362c6a33cdSVadim Pasternak 					     type_index, num);
6372c6a33cdSVadim Pasternak 			mlxsw_hwmon_attr_add(mlxsw_hwmon,
6382c6a33cdSVadim Pasternak 					     MLXSW_HWMON_ATTR_TYPE_FAN_FAULT,
63952581961SJiri Pirko 					     type_index, num++);
64052581961SJiri Pirko 		}
6412c6a33cdSVadim Pasternak 	}
64252581961SJiri Pirko 	num = 0;
64352581961SJiri Pirko 	for (type_index = 0; type_index < MLXSW_MFCR_PWMS_MAX; type_index++) {
64452581961SJiri Pirko 		if (pwm_active & BIT(type_index))
64552581961SJiri Pirko 			mlxsw_hwmon_attr_add(mlxsw_hwmon,
64652581961SJiri Pirko 					     MLXSW_HWMON_ATTR_TYPE_PWM,
64752581961SJiri Pirko 					     type_index, num++);
64852581961SJiri Pirko 	}
64952581961SJiri Pirko 	return 0;
65052581961SJiri Pirko }
65152581961SJiri Pirko 
6525c42eaa0SVadim Pasternak static int mlxsw_hwmon_module_init(struct mlxsw_hwmon *mlxsw_hwmon)
6535c42eaa0SVadim Pasternak {
654ea30a92aSVadim Pasternak 	char mgpir_pl[MLXSW_REG_MGPIR_LEN];
655ea30a92aSVadim Pasternak 	u8 module_sensor_max;
656ea30a92aSVadim Pasternak 	int i, err;
6575c42eaa0SVadim Pasternak 
658c52ecff7SVadim Pasternak 	if (!mlxsw_core_res_query_enabled(mlxsw_hwmon->core))
659c52ecff7SVadim Pasternak 		return 0;
660c52ecff7SVadim Pasternak 
661ea30a92aSVadim Pasternak 	mlxsw_reg_mgpir_pack(mgpir_pl);
662ea30a92aSVadim Pasternak 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mgpir), mgpir_pl);
663ea30a92aSVadim Pasternak 	if (err)
664ea30a92aSVadim Pasternak 		return err;
665ea30a92aSVadim Pasternak 
666ea30a92aSVadim Pasternak 	mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,
667ea30a92aSVadim Pasternak 			       &module_sensor_max);
668ea30a92aSVadim Pasternak 
6695c42eaa0SVadim Pasternak 	/* Add extra attributes for module temperature. Sensor index is
6705c42eaa0SVadim Pasternak 	 * assigned to sensor_count value, while all indexed before
6715c42eaa0SVadim Pasternak 	 * sensor_count are already utilized by the sensors connected through
6725c42eaa0SVadim Pasternak 	 * mtmp register by mlxsw_hwmon_temp_init().
6735c42eaa0SVadim Pasternak 	 */
674ea30a92aSVadim Pasternak 	mlxsw_hwmon->module_sensor_max = mlxsw_hwmon->sensor_count +
675ea30a92aSVadim Pasternak 					 module_sensor_max;
676ea30a92aSVadim Pasternak 	for (i = mlxsw_hwmon->sensor_count;
677ea30a92aSVadim Pasternak 	     i < mlxsw_hwmon->module_sensor_max; i++) {
6785c42eaa0SVadim Pasternak 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
679ea30a92aSVadim Pasternak 				     MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE, i, i);
6805c42eaa0SVadim Pasternak 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
6815c42eaa0SVadim Pasternak 				     MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT,
682ea30a92aSVadim Pasternak 				     i, i);
6835c42eaa0SVadim Pasternak 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
684ea30a92aSVadim Pasternak 				     MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT, i,
685ea30a92aSVadim Pasternak 				     i);
6865c42eaa0SVadim Pasternak 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
6875c42eaa0SVadim Pasternak 				     MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG,
688ea30a92aSVadim Pasternak 				     i, i);
689a53779deSVadim Pasternak 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
690a53779deSVadim Pasternak 				     MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL,
691ea30a92aSVadim Pasternak 				     i, i);
69291df5d3aSAmit Cohen 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
69391df5d3aSAmit Cohen 				     MLXSW_HWMON_ATTR_TYPE_TEMP_CRIT_ALARM,
69491df5d3aSAmit Cohen 				     i, i);
69591df5d3aSAmit Cohen 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
69691df5d3aSAmit Cohen 				     MLXSW_HWMON_ATTR_TYPE_TEMP_EMERGENCY_ALARM,
69791df5d3aSAmit Cohen 				     i, i);
6985c42eaa0SVadim Pasternak 	}
6992e265a8bSVadim Pasternak 
7002e265a8bSVadim Pasternak 	return 0;
7012e265a8bSVadim Pasternak }
7022e265a8bSVadim Pasternak 
7032e265a8bSVadim Pasternak static int mlxsw_hwmon_gearbox_init(struct mlxsw_hwmon *mlxsw_hwmon)
7042e265a8bSVadim Pasternak {
70536844c85SVadim Pasternak 	enum mlxsw_reg_mgpir_device_type device_type;
7062e265a8bSVadim Pasternak 	int index, max_index, sensor_index;
7072e265a8bSVadim Pasternak 	char mgpir_pl[MLXSW_REG_MGPIR_LEN];
7082e265a8bSVadim Pasternak 	char mtmp_pl[MLXSW_REG_MTMP_LEN];
7092e265a8bSVadim Pasternak 	u8 gbox_num;
7102e265a8bSVadim Pasternak 	int err;
7112e265a8bSVadim Pasternak 
7122e265a8bSVadim Pasternak 	mlxsw_reg_mgpir_pack(mgpir_pl);
7132e265a8bSVadim Pasternak 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mgpir), mgpir_pl);
7142e265a8bSVadim Pasternak 	if (err)
7152e265a8bSVadim Pasternak 		return err;
7162e265a8bSVadim Pasternak 
71736844c85SVadim Pasternak 	mlxsw_reg_mgpir_unpack(mgpir_pl, &gbox_num, &device_type, NULL, NULL);
71836844c85SVadim Pasternak 	if (device_type != MLXSW_REG_MGPIR_DEVICE_TYPE_GEARBOX_DIE ||
71936844c85SVadim Pasternak 	    !gbox_num)
7202e265a8bSVadim Pasternak 		return 0;
7212e265a8bSVadim Pasternak 
722ea30a92aSVadim Pasternak 	index = mlxsw_hwmon->module_sensor_max;
723ea30a92aSVadim Pasternak 	max_index = mlxsw_hwmon->module_sensor_max + gbox_num;
7242e265a8bSVadim Pasternak 	while (index < max_index) {
725ea30a92aSVadim Pasternak 		sensor_index = index % mlxsw_hwmon->module_sensor_max +
7262e265a8bSVadim Pasternak 			       MLXSW_REG_MTMP_GBOX_INDEX_MIN;
7272e265a8bSVadim Pasternak 		mlxsw_reg_mtmp_pack(mtmp_pl, sensor_index, true, true);
7282e265a8bSVadim Pasternak 		err = mlxsw_reg_write(mlxsw_hwmon->core,
7292e265a8bSVadim Pasternak 				      MLXSW_REG(mtmp), mtmp_pl);
7302e265a8bSVadim Pasternak 		if (err) {
7312e265a8bSVadim Pasternak 			dev_err(mlxsw_hwmon->bus_info->dev, "Failed to setup temp sensor number %d\n",
7322e265a8bSVadim Pasternak 				sensor_index);
7332e265a8bSVadim Pasternak 			return err;
7342e265a8bSVadim Pasternak 		}
7352e265a8bSVadim Pasternak 		mlxsw_hwmon_attr_add(mlxsw_hwmon, MLXSW_HWMON_ATTR_TYPE_TEMP,
7362e265a8bSVadim Pasternak 				     index, index);
7372e265a8bSVadim Pasternak 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
7382e265a8bSVadim Pasternak 				     MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, index,
7392e265a8bSVadim Pasternak 				     index);
7402e265a8bSVadim Pasternak 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
7412e265a8bSVadim Pasternak 				     MLXSW_HWMON_ATTR_TYPE_TEMP_RST, index,
7422e265a8bSVadim Pasternak 				     index);
7432e265a8bSVadim Pasternak 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
7442e265a8bSVadim Pasternak 				     MLXSW_HWMON_ATTR_TYPE_TEMP_GBOX_LABEL,
7452e265a8bSVadim Pasternak 				     index, index);
7462e265a8bSVadim Pasternak 		index++;
7472e265a8bSVadim Pasternak 	}
7485c42eaa0SVadim Pasternak 
7495c42eaa0SVadim Pasternak 	return 0;
7505c42eaa0SVadim Pasternak }
7515c42eaa0SVadim Pasternak 
75289309da3SJiri Pirko int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
75389309da3SJiri Pirko 		     const struct mlxsw_bus_info *mlxsw_bus_info,
75489309da3SJiri Pirko 		     struct mlxsw_hwmon **p_hwmon)
75589309da3SJiri Pirko {
75689309da3SJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon;
75789309da3SJiri Pirko 	struct device *hwmon_dev;
75889309da3SJiri Pirko 	int err;
75989309da3SJiri Pirko 
7609b3bc7dbSIdo Schimmel 	mlxsw_hwmon = kzalloc(sizeof(*mlxsw_hwmon), GFP_KERNEL);
76189309da3SJiri Pirko 	if (!mlxsw_hwmon)
76289309da3SJiri Pirko 		return -ENOMEM;
76389309da3SJiri Pirko 	mlxsw_hwmon->core = mlxsw_core;
76489309da3SJiri Pirko 	mlxsw_hwmon->bus_info = mlxsw_bus_info;
76589309da3SJiri Pirko 
76689309da3SJiri Pirko 	err = mlxsw_hwmon_temp_init(mlxsw_hwmon);
76789309da3SJiri Pirko 	if (err)
76889309da3SJiri Pirko 		goto err_temp_init;
76989309da3SJiri Pirko 
77052581961SJiri Pirko 	err = mlxsw_hwmon_fans_init(mlxsw_hwmon);
77152581961SJiri Pirko 	if (err)
77252581961SJiri Pirko 		goto err_fans_init;
77352581961SJiri Pirko 
7745c42eaa0SVadim Pasternak 	err = mlxsw_hwmon_module_init(mlxsw_hwmon);
7755c42eaa0SVadim Pasternak 	if (err)
7765c42eaa0SVadim Pasternak 		goto err_temp_module_init;
7775c42eaa0SVadim Pasternak 
7782e265a8bSVadim Pasternak 	err = mlxsw_hwmon_gearbox_init(mlxsw_hwmon);
7792e265a8bSVadim Pasternak 	if (err)
7802e265a8bSVadim Pasternak 		goto err_temp_gearbox_init;
7812e265a8bSVadim Pasternak 
78289309da3SJiri Pirko 	mlxsw_hwmon->groups[0] = &mlxsw_hwmon->group;
78389309da3SJiri Pirko 	mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs;
78489309da3SJiri Pirko 
7859b3bc7dbSIdo Schimmel 	hwmon_dev = hwmon_device_register_with_groups(mlxsw_bus_info->dev,
7869b3bc7dbSIdo Schimmel 						      "mlxsw", mlxsw_hwmon,
78789309da3SJiri Pirko 						      mlxsw_hwmon->groups);
78889309da3SJiri Pirko 	if (IS_ERR(hwmon_dev)) {
78989309da3SJiri Pirko 		err = PTR_ERR(hwmon_dev);
79089309da3SJiri Pirko 		goto err_hwmon_register;
79189309da3SJiri Pirko 	}
79289309da3SJiri Pirko 
79389309da3SJiri Pirko 	mlxsw_hwmon->hwmon_dev = hwmon_dev;
79489309da3SJiri Pirko 	*p_hwmon = mlxsw_hwmon;
79589309da3SJiri Pirko 	return 0;
79689309da3SJiri Pirko 
79789309da3SJiri Pirko err_hwmon_register:
7982e265a8bSVadim Pasternak err_temp_gearbox_init:
7995c42eaa0SVadim Pasternak err_temp_module_init:
80052581961SJiri Pirko err_fans_init:
80189309da3SJiri Pirko err_temp_init:
8029b3bc7dbSIdo Schimmel 	kfree(mlxsw_hwmon);
80389309da3SJiri Pirko 	return err;
80489309da3SJiri Pirko }
8059b3bc7dbSIdo Schimmel 
8069b3bc7dbSIdo Schimmel void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon)
8079b3bc7dbSIdo Schimmel {
8089b3bc7dbSIdo Schimmel 	hwmon_device_unregister(mlxsw_hwmon->hwmon_dev);
8099b3bc7dbSIdo Schimmel 	kfree(mlxsw_hwmon);
8109b3bc7dbSIdo Schimmel }
811