189309da3SJiri Pirko /*
289309da3SJiri Pirko  * drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
389309da3SJiri Pirko  * Copyright (c) 2015 Mellanox Technologies. All rights reserved.
489309da3SJiri Pirko  * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com>
589309da3SJiri Pirko  *
689309da3SJiri Pirko  * Redistribution and use in source and binary forms, with or without
789309da3SJiri Pirko  * modification, are permitted provided that the following conditions are met:
889309da3SJiri Pirko  *
989309da3SJiri Pirko  * 1. Redistributions of source code must retain the above copyright
1089309da3SJiri Pirko  *    notice, this list of conditions and the following disclaimer.
1189309da3SJiri Pirko  * 2. Redistributions in binary form must reproduce the above copyright
1289309da3SJiri Pirko  *    notice, this list of conditions and the following disclaimer in the
1389309da3SJiri Pirko  *    documentation and/or other materials provided with the distribution.
1489309da3SJiri Pirko  * 3. Neither the names of the copyright holders nor the names of its
1589309da3SJiri Pirko  *    contributors may be used to endorse or promote products derived from
1689309da3SJiri Pirko  *    this software without specific prior written permission.
1789309da3SJiri Pirko  *
1889309da3SJiri Pirko  * Alternatively, this software may be distributed under the terms of the
1989309da3SJiri Pirko  * GNU General Public License ("GPL") version 2 as published by the Free
2089309da3SJiri Pirko  * Software Foundation.
2189309da3SJiri Pirko  *
2289309da3SJiri Pirko  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2389309da3SJiri Pirko  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2489309da3SJiri Pirko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2589309da3SJiri Pirko  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2689309da3SJiri Pirko  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2789309da3SJiri Pirko  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2889309da3SJiri Pirko  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2989309da3SJiri Pirko  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3089309da3SJiri Pirko  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3189309da3SJiri Pirko  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3289309da3SJiri Pirko  * POSSIBILITY OF SUCH DAMAGE.
3389309da3SJiri Pirko  */
3489309da3SJiri Pirko 
3589309da3SJiri Pirko #include <linux/kernel.h>
3689309da3SJiri Pirko #include <linux/types.h>
3789309da3SJiri Pirko #include <linux/device.h>
3889309da3SJiri Pirko #include <linux/sysfs.h>
3989309da3SJiri Pirko #include <linux/hwmon.h>
4089309da3SJiri Pirko #include <linux/err.h>
4189309da3SJiri Pirko 
4289309da3SJiri Pirko #include "core.h"
4389309da3SJiri Pirko 
4489309da3SJiri Pirko #define MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT 127
4552581961SJiri Pirko #define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT * 4 + \
4652581961SJiri Pirko 				MLXSW_MFCR_TACHOS_MAX + MLXSW_MFCR_PWMS_MAX)
4789309da3SJiri Pirko 
4889309da3SJiri Pirko struct mlxsw_hwmon_attr {
4989309da3SJiri Pirko 	struct device_attribute dev_attr;
5089309da3SJiri Pirko 	struct mlxsw_hwmon *hwmon;
5189309da3SJiri Pirko 	unsigned int type_index;
52e7bc73cbSJiri Pirko 	char name[32];
5389309da3SJiri Pirko };
5489309da3SJiri Pirko 
5589309da3SJiri Pirko struct mlxsw_hwmon {
5689309da3SJiri Pirko 	struct mlxsw_core *core;
5789309da3SJiri Pirko 	const struct mlxsw_bus_info *bus_info;
5889309da3SJiri Pirko 	struct device *hwmon_dev;
5989309da3SJiri Pirko 	struct attribute_group group;
6089309da3SJiri Pirko 	const struct attribute_group *groups[2];
6189309da3SJiri Pirko 	struct attribute *attrs[MLXSW_HWMON_ATTR_COUNT + 1];
6289309da3SJiri Pirko 	struct mlxsw_hwmon_attr hwmon_attrs[MLXSW_HWMON_ATTR_COUNT];
6389309da3SJiri Pirko 	unsigned int attrs_count;
6489309da3SJiri Pirko };
6589309da3SJiri Pirko 
6689309da3SJiri Pirko static ssize_t mlxsw_hwmon_temp_show(struct device *dev,
6789309da3SJiri Pirko 				     struct device_attribute *attr,
6889309da3SJiri Pirko 				     char *buf)
6989309da3SJiri Pirko {
7089309da3SJiri Pirko 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
7189309da3SJiri Pirko 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
7289309da3SJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
7389309da3SJiri Pirko 	char mtmp_pl[MLXSW_REG_MTMP_LEN];
7489309da3SJiri Pirko 	unsigned int temp;
7589309da3SJiri Pirko 	int err;
7689309da3SJiri Pirko 
7789309da3SJiri Pirko 	mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index,
7889309da3SJiri Pirko 			    false, false);
7989309da3SJiri Pirko 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
8089309da3SJiri Pirko 	if (err) {
8189309da3SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");
8289309da3SJiri Pirko 		return err;
8389309da3SJiri Pirko 	}
8489309da3SJiri Pirko 	mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL);
8589309da3SJiri Pirko 	return sprintf(buf, "%u\n", temp);
8689309da3SJiri Pirko }
8789309da3SJiri Pirko 
8889309da3SJiri Pirko static ssize_t mlxsw_hwmon_temp_max_show(struct device *dev,
8989309da3SJiri Pirko 					 struct device_attribute *attr,
9089309da3SJiri Pirko 					 char *buf)
9189309da3SJiri Pirko {
9289309da3SJiri Pirko 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
9389309da3SJiri Pirko 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
9489309da3SJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
9589309da3SJiri Pirko 	char mtmp_pl[MLXSW_REG_MTMP_LEN];
9689309da3SJiri Pirko 	unsigned int temp_max;
9789309da3SJiri Pirko 	int err;
9889309da3SJiri Pirko 
9989309da3SJiri Pirko 	mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index,
10089309da3SJiri Pirko 			    false, false);
10189309da3SJiri Pirko 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
10289309da3SJiri Pirko 	if (err) {
10389309da3SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");
10489309da3SJiri Pirko 		return err;
10589309da3SJiri Pirko 	}
10689309da3SJiri Pirko 	mlxsw_reg_mtmp_unpack(mtmp_pl, NULL, &temp_max, NULL);
10789309da3SJiri Pirko 	return sprintf(buf, "%u\n", temp_max);
10889309da3SJiri Pirko }
10989309da3SJiri Pirko 
110e7bc73cbSJiri Pirko static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev,
111e7bc73cbSJiri Pirko 					  struct device_attribute *attr,
112e7bc73cbSJiri Pirko 					  const char *buf, size_t len)
113e7bc73cbSJiri Pirko {
114e7bc73cbSJiri Pirko 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
115e7bc73cbSJiri Pirko 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
116e7bc73cbSJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
117e7bc73cbSJiri Pirko 	char mtmp_pl[MLXSW_REG_MTMP_LEN];
118e7bc73cbSJiri Pirko 	unsigned long val;
119e7bc73cbSJiri Pirko 	int err;
120e7bc73cbSJiri Pirko 
121e7bc73cbSJiri Pirko 	err = kstrtoul(buf, 10, &val);
122e7bc73cbSJiri Pirko 	if (err)
123e7bc73cbSJiri Pirko 		return err;
124e7bc73cbSJiri Pirko 	if (val != 1)
125e7bc73cbSJiri Pirko 		return -EINVAL;
126e7bc73cbSJiri Pirko 
127e7bc73cbSJiri Pirko 	mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index, true, true);
128e7bc73cbSJiri Pirko 	err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
129e7bc73cbSJiri Pirko 	if (err) {
130e7bc73cbSJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to reset temp sensor history\n");
131e7bc73cbSJiri Pirko 		return err;
132e7bc73cbSJiri Pirko 	}
133719255d0SDan Carpenter 	return len;
134e7bc73cbSJiri Pirko }
135e7bc73cbSJiri Pirko 
13652581961SJiri Pirko static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev,
13752581961SJiri Pirko 					struct device_attribute *attr,
13852581961SJiri Pirko 					char *buf)
13952581961SJiri Pirko {
14052581961SJiri Pirko 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
14152581961SJiri Pirko 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
14252581961SJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
14352581961SJiri Pirko 	char mfsm_pl[MLXSW_REG_MFSM_LEN];
14452581961SJiri Pirko 	int err;
14552581961SJiri Pirko 
14652581961SJiri Pirko 	mlxsw_reg_mfsm_pack(mfsm_pl, mlwsw_hwmon_attr->type_index);
14752581961SJiri Pirko 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsm), mfsm_pl);
14852581961SJiri Pirko 	if (err) {
14952581961SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n");
15052581961SJiri Pirko 		return err;
15152581961SJiri Pirko 	}
15252581961SJiri Pirko 	return sprintf(buf, "%u\n", mlxsw_reg_mfsm_rpm_get(mfsm_pl));
15352581961SJiri Pirko }
15452581961SJiri Pirko 
15552581961SJiri Pirko static ssize_t mlxsw_hwmon_pwm_show(struct device *dev,
15652581961SJiri Pirko 				    struct device_attribute *attr,
15752581961SJiri Pirko 				    char *buf)
15852581961SJiri Pirko {
15952581961SJiri Pirko 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
16052581961SJiri Pirko 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
16152581961SJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
16252581961SJiri Pirko 	char mfsc_pl[MLXSW_REG_MFSC_LEN];
16352581961SJiri Pirko 	int err;
16452581961SJiri Pirko 
16552581961SJiri Pirko 	mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, 0);
16652581961SJiri Pirko 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl);
16752581961SJiri Pirko 	if (err) {
16852581961SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query PWM\n");
16952581961SJiri Pirko 		return err;
17052581961SJiri Pirko 	}
17152581961SJiri Pirko 	return sprintf(buf, "%u\n",
17252581961SJiri Pirko 		       mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl));
17352581961SJiri Pirko }
17452581961SJiri Pirko 
17552581961SJiri Pirko static ssize_t mlxsw_hwmon_pwm_store(struct device *dev,
17652581961SJiri Pirko 				     struct device_attribute *attr,
17752581961SJiri Pirko 				     const char *buf, size_t len)
17852581961SJiri Pirko {
17952581961SJiri Pirko 	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
18052581961SJiri Pirko 			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
18152581961SJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
18252581961SJiri Pirko 	char mfsc_pl[MLXSW_REG_MFSC_LEN];
18352581961SJiri Pirko 	unsigned long val;
18452581961SJiri Pirko 	int err;
18552581961SJiri Pirko 
18652581961SJiri Pirko 	err = kstrtoul(buf, 10, &val);
18752581961SJiri Pirko 	if (err)
18852581961SJiri Pirko 		return err;
18952581961SJiri Pirko 	if (val > 255)
19052581961SJiri Pirko 		return -EINVAL;
19152581961SJiri Pirko 
19252581961SJiri Pirko 	mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, val);
19352581961SJiri Pirko 	err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl);
19452581961SJiri Pirko 	if (err) {
19552581961SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to write PWM\n");
19652581961SJiri Pirko 		return err;
19752581961SJiri Pirko 	}
198515123e2SDan Carpenter 	return len;
19952581961SJiri Pirko }
20052581961SJiri Pirko 
20189309da3SJiri Pirko enum mlxsw_hwmon_attr_type {
20289309da3SJiri Pirko 	MLXSW_HWMON_ATTR_TYPE_TEMP,
20389309da3SJiri Pirko 	MLXSW_HWMON_ATTR_TYPE_TEMP_MAX,
204e7bc73cbSJiri Pirko 	MLXSW_HWMON_ATTR_TYPE_TEMP_RST,
20552581961SJiri Pirko 	MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
20652581961SJiri Pirko 	MLXSW_HWMON_ATTR_TYPE_PWM,
20789309da3SJiri Pirko };
20889309da3SJiri Pirko 
20989309da3SJiri Pirko static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
21089309da3SJiri Pirko 				 enum mlxsw_hwmon_attr_type attr_type,
21189309da3SJiri Pirko 				 unsigned int type_index, unsigned int num) {
21289309da3SJiri Pirko 	struct mlxsw_hwmon_attr *mlxsw_hwmon_attr;
21389309da3SJiri Pirko 	unsigned int attr_index;
21489309da3SJiri Pirko 
21589309da3SJiri Pirko 	attr_index = mlxsw_hwmon->attrs_count;
21689309da3SJiri Pirko 	mlxsw_hwmon_attr = &mlxsw_hwmon->hwmon_attrs[attr_index];
21789309da3SJiri Pirko 
21889309da3SJiri Pirko 	switch (attr_type) {
21989309da3SJiri Pirko 	case MLXSW_HWMON_ATTR_TYPE_TEMP:
22089309da3SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_show;
22189309da3SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO;
22289309da3SJiri Pirko 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
22389309da3SJiri Pirko 			 "temp%u_input", num + 1);
22489309da3SJiri Pirko 		break;
22589309da3SJiri Pirko 	case MLXSW_HWMON_ATTR_TYPE_TEMP_MAX:
22689309da3SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_max_show;
22789309da3SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO;
22889309da3SJiri Pirko 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
22989309da3SJiri Pirko 			 "temp%u_highest", num + 1);
23089309da3SJiri Pirko 		break;
231e7bc73cbSJiri Pirko 	case MLXSW_HWMON_ATTR_TYPE_TEMP_RST:
232e7bc73cbSJiri Pirko 		mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_temp_rst_store;
233e7bc73cbSJiri Pirko 		mlxsw_hwmon_attr->dev_attr.attr.mode = S_IWUSR;
234e7bc73cbSJiri Pirko 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
235e7bc73cbSJiri Pirko 			 "temp%u_reset_history", num + 1);
236e7bc73cbSJiri Pirko 		break;
23752581961SJiri Pirko 	case MLXSW_HWMON_ATTR_TYPE_FAN_RPM:
23852581961SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_rpm_show;
23952581961SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO;
24052581961SJiri Pirko 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
24152581961SJiri Pirko 			 "fan%u_input", num + 1);
24252581961SJiri Pirko 		break;
24352581961SJiri Pirko 	case MLXSW_HWMON_ATTR_TYPE_PWM:
24452581961SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_pwm_show;
24552581961SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_pwm_store;
24652581961SJiri Pirko 		mlxsw_hwmon_attr->dev_attr.attr.mode = S_IWUSR | S_IRUGO;
24752581961SJiri Pirko 		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
24852581961SJiri Pirko 			 "pwm%u", num + 1);
24952581961SJiri Pirko 		break;
25089309da3SJiri Pirko 	default:
2516b20da4dSJiri Pirko 		WARN_ON(1);
25289309da3SJiri Pirko 	}
25389309da3SJiri Pirko 
25489309da3SJiri Pirko 	mlxsw_hwmon_attr->type_index = type_index;
25589309da3SJiri Pirko 	mlxsw_hwmon_attr->hwmon = mlxsw_hwmon;
25689309da3SJiri Pirko 	mlxsw_hwmon_attr->dev_attr.attr.name = mlxsw_hwmon_attr->name;
25789309da3SJiri Pirko 	sysfs_attr_init(&mlxsw_hwmon_attr->dev_attr.attr);
25889309da3SJiri Pirko 
25989309da3SJiri Pirko 	mlxsw_hwmon->attrs[attr_index] = &mlxsw_hwmon_attr->dev_attr.attr;
26089309da3SJiri Pirko 	mlxsw_hwmon->attrs_count++;
26189309da3SJiri Pirko }
26289309da3SJiri Pirko 
26389309da3SJiri Pirko static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon)
26489309da3SJiri Pirko {
2655b090740SElad Raz 	char mtcap_pl[MLXSW_REG_MTCAP_LEN] = {0};
26689309da3SJiri Pirko 	char mtmp_pl[MLXSW_REG_MTMP_LEN];
26789309da3SJiri Pirko 	u8 sensor_count;
26889309da3SJiri Pirko 	int i;
26989309da3SJiri Pirko 	int err;
27089309da3SJiri Pirko 
27189309da3SJiri Pirko 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtcap), mtcap_pl);
27289309da3SJiri Pirko 	if (err) {
27389309da3SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get number of temp sensors\n");
27489309da3SJiri Pirko 		return err;
27589309da3SJiri Pirko 	}
27689309da3SJiri Pirko 	sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl);
27789309da3SJiri Pirko 	for (i = 0; i < sensor_count; i++) {
278b626f2cbSJiri Pirko 		mlxsw_reg_mtmp_pack(mtmp_pl, i, true, true);
27989309da3SJiri Pirko 		err = mlxsw_reg_write(mlxsw_hwmon->core,
28089309da3SJiri Pirko 				      MLXSW_REG(mtmp), mtmp_pl);
28189309da3SJiri Pirko 		if (err) {
28289309da3SJiri Pirko 			dev_err(mlxsw_hwmon->bus_info->dev, "Failed to setup temp sensor number %d\n",
28389309da3SJiri Pirko 				i);
28489309da3SJiri Pirko 			return err;
28589309da3SJiri Pirko 		}
28689309da3SJiri Pirko 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
28789309da3SJiri Pirko 				     MLXSW_HWMON_ATTR_TYPE_TEMP, i, i);
28889309da3SJiri Pirko 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
28989309da3SJiri Pirko 				     MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, i, i);
290e7bc73cbSJiri Pirko 		mlxsw_hwmon_attr_add(mlxsw_hwmon,
291e7bc73cbSJiri Pirko 				     MLXSW_HWMON_ATTR_TYPE_TEMP_RST, i, i);
29289309da3SJiri Pirko 	}
29389309da3SJiri Pirko 	return 0;
29489309da3SJiri Pirko }
29589309da3SJiri Pirko 
29652581961SJiri Pirko static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon)
29752581961SJiri Pirko {
2985b090740SElad Raz 	char mfcr_pl[MLXSW_REG_MFCR_LEN] = {0};
29952581961SJiri Pirko 	enum mlxsw_reg_mfcr_pwm_frequency freq;
30052581961SJiri Pirko 	unsigned int type_index;
30152581961SJiri Pirko 	unsigned int num;
30252581961SJiri Pirko 	u16 tacho_active;
30352581961SJiri Pirko 	u8 pwm_active;
30452581961SJiri Pirko 	int err;
30552581961SJiri Pirko 
30652581961SJiri Pirko 	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfcr), mfcr_pl);
30752581961SJiri Pirko 	if (err) {
30852581961SJiri Pirko 		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get to probe PWMs and Tachometers\n");
30952581961SJiri Pirko 		return err;
31052581961SJiri Pirko 	}
31152581961SJiri Pirko 	mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active);
31252581961SJiri Pirko 	num = 0;
31352581961SJiri Pirko 	for (type_index = 0; type_index < MLXSW_MFCR_TACHOS_MAX; type_index++) {
31452581961SJiri Pirko 		if (tacho_active & BIT(type_index))
31552581961SJiri Pirko 			mlxsw_hwmon_attr_add(mlxsw_hwmon,
31652581961SJiri Pirko 					     MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
31752581961SJiri Pirko 					     type_index, num++);
31852581961SJiri Pirko 	}
31952581961SJiri Pirko 	num = 0;
32052581961SJiri Pirko 	for (type_index = 0; type_index < MLXSW_MFCR_PWMS_MAX; type_index++) {
32152581961SJiri Pirko 		if (pwm_active & BIT(type_index))
32252581961SJiri Pirko 			mlxsw_hwmon_attr_add(mlxsw_hwmon,
32352581961SJiri Pirko 					     MLXSW_HWMON_ATTR_TYPE_PWM,
32452581961SJiri Pirko 					     type_index, num++);
32552581961SJiri Pirko 	}
32652581961SJiri Pirko 	return 0;
32752581961SJiri Pirko }
32852581961SJiri Pirko 
32989309da3SJiri Pirko int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
33089309da3SJiri Pirko 		     const struct mlxsw_bus_info *mlxsw_bus_info,
33189309da3SJiri Pirko 		     struct mlxsw_hwmon **p_hwmon)
33289309da3SJiri Pirko {
33389309da3SJiri Pirko 	struct mlxsw_hwmon *mlxsw_hwmon;
33489309da3SJiri Pirko 	struct device *hwmon_dev;
33589309da3SJiri Pirko 	int err;
33689309da3SJiri Pirko 
337f4cee3afSJiri Pirko 	mlxsw_hwmon = devm_kzalloc(mlxsw_bus_info->dev, sizeof(*mlxsw_hwmon),
338f4cee3afSJiri Pirko 				   GFP_KERNEL);
33989309da3SJiri Pirko 	if (!mlxsw_hwmon)
34089309da3SJiri Pirko 		return -ENOMEM;
34189309da3SJiri Pirko 	mlxsw_hwmon->core = mlxsw_core;
34289309da3SJiri Pirko 	mlxsw_hwmon->bus_info = mlxsw_bus_info;
34389309da3SJiri Pirko 
34489309da3SJiri Pirko 	err = mlxsw_hwmon_temp_init(mlxsw_hwmon);
34589309da3SJiri Pirko 	if (err)
34689309da3SJiri Pirko 		goto err_temp_init;
34789309da3SJiri Pirko 
34852581961SJiri Pirko 	err = mlxsw_hwmon_fans_init(mlxsw_hwmon);
34952581961SJiri Pirko 	if (err)
35052581961SJiri Pirko 		goto err_fans_init;
35152581961SJiri Pirko 
35289309da3SJiri Pirko 	mlxsw_hwmon->groups[0] = &mlxsw_hwmon->group;
35389309da3SJiri Pirko 	mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs;
35489309da3SJiri Pirko 
35589309da3SJiri Pirko 	hwmon_dev = devm_hwmon_device_register_with_groups(mlxsw_bus_info->dev,
35689309da3SJiri Pirko 							   "mlxsw",
35789309da3SJiri Pirko 							   mlxsw_hwmon,
35889309da3SJiri Pirko 							   mlxsw_hwmon->groups);
35989309da3SJiri Pirko 	if (IS_ERR(hwmon_dev)) {
36089309da3SJiri Pirko 		err = PTR_ERR(hwmon_dev);
36189309da3SJiri Pirko 		goto err_hwmon_register;
36289309da3SJiri Pirko 	}
36389309da3SJiri Pirko 
36489309da3SJiri Pirko 	mlxsw_hwmon->hwmon_dev = hwmon_dev;
36589309da3SJiri Pirko 	*p_hwmon = mlxsw_hwmon;
36689309da3SJiri Pirko 	return 0;
36789309da3SJiri Pirko 
36889309da3SJiri Pirko err_hwmon_register:
36952581961SJiri Pirko err_fans_init:
37089309da3SJiri Pirko err_temp_init:
37189309da3SJiri Pirko 	return err;
37289309da3SJiri Pirko }
373