xref: /linux/drivers/hwmon/mlxreg-fan.c (revision dadca53d)
165afb4c8SVadim Pasternak // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
265afb4c8SVadim Pasternak //
365afb4c8SVadim Pasternak // Copyright (c) 2018 Mellanox Technologies. All rights reserved.
465afb4c8SVadim Pasternak // Copyright (c) 2018 Vadim Pasternak <vadimp@mellanox.com>
565afb4c8SVadim Pasternak 
665afb4c8SVadim Pasternak #include <linux/bitops.h>
765afb4c8SVadim Pasternak #include <linux/device.h>
865afb4c8SVadim Pasternak #include <linux/hwmon.h>
965afb4c8SVadim Pasternak #include <linux/module.h>
1065afb4c8SVadim Pasternak #include <linux/platform_data/mlxreg.h>
1165afb4c8SVadim Pasternak #include <linux/platform_device.h>
1265afb4c8SVadim Pasternak #include <linux/regmap.h>
1365afb4c8SVadim Pasternak #include <linux/thermal.h>
1465afb4c8SVadim Pasternak 
15*dadca53dSVadim Pasternak #define MLXREG_FAN_MAX_TACHO		24
16150f1e0cSVadim Pasternak #define MLXREG_FAN_MAX_PWM		4
17150f1e0cSVadim Pasternak #define MLXREG_FAN_PWM_NOT_CONNECTED	0xff
1865afb4c8SVadim Pasternak #define MLXREG_FAN_MAX_STATE		10
1965afb4c8SVadim Pasternak #define MLXREG_FAN_MIN_DUTY		51	/* 20% */
2065afb4c8SVadim Pasternak #define MLXREG_FAN_MAX_DUTY		255	/* 100% */
2165afb4c8SVadim Pasternak #define MLXREG_FAN_SPEED_MIN_LEVEL		2	/* 20 percent */
2265afb4c8SVadim Pasternak #define MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF	44
23b429ebc8SVadim Pasternak #define MLXREG_FAN_TACHO_DIV_MIN		283
24b429ebc8SVadim Pasternak #define MLXREG_FAN_TACHO_DIV_DEF		(MLXREG_FAN_TACHO_DIV_MIN * 4)
25b429ebc8SVadim Pasternak #define MLXREG_FAN_TACHO_DIV_SCALE_MAX	64
2665afb4c8SVadim Pasternak /*
2765afb4c8SVadim Pasternak  * FAN datasheet defines the formula for RPM calculations as RPM = 15/t-high.
2865afb4c8SVadim Pasternak  * The logic in a programmable device measures the time t-high by sampling the
2965afb4c8SVadim Pasternak  * tachometer every t-sample (with the default value 11.32 uS) and increment
3065afb4c8SVadim Pasternak  * a counter (N) as long as the pulse has not change:
3165afb4c8SVadim Pasternak  * RPM = 15 / (t-sample * (K + Regval)), where:
3265afb4c8SVadim Pasternak  * Regval: is the value read from the programmable device register;
3365afb4c8SVadim Pasternak  *  - 0xff - represents tachometer fault;
3465afb4c8SVadim Pasternak  *  - 0xfe - represents tachometer minimum value , which is 4444 RPM;
3565afb4c8SVadim Pasternak  *  - 0x00 - represents tachometer maximum value , which is 300000 RPM;
3665afb4c8SVadim Pasternak  * K: is 44 and it represents the minimum allowed samples per pulse;
3765afb4c8SVadim Pasternak  * N: is equal K + Regval;
3865afb4c8SVadim Pasternak  * In order to calculate RPM from the register value the following formula is
3965afb4c8SVadim Pasternak  * used: RPM = 15 / ((Regval + K) * 11.32) * 10^(-6)), which in  the
4065afb4c8SVadim Pasternak  * default case is modified to:
4165afb4c8SVadim Pasternak  * RPM = 15000000 * 100 / ((Regval + 44) * 1132);
4265afb4c8SVadim Pasternak  * - for Regval 0x00, RPM will be 15000000 * 100 / (44 * 1132) = 30115;
4365afb4c8SVadim Pasternak  * - for Regval 0xfe, RPM will be 15000000 * 100 / ((254 + 44) * 1132) = 4446;
4465afb4c8SVadim Pasternak  * In common case the formula is modified to:
4565afb4c8SVadim Pasternak  * RPM = 15000000 * 100 / ((Regval + samples) * divider).
4665afb4c8SVadim Pasternak  */
4765afb4c8SVadim Pasternak #define MLXREG_FAN_GET_RPM(rval, d, s)	(DIV_ROUND_CLOSEST(15000000 * 100, \
4865afb4c8SVadim Pasternak 					 ((rval) + (s)) * (d)))
493f9ffa5cSVadim Pasternak #define MLXREG_FAN_GET_FAULT(val, mask) ((val) == (mask))
5065afb4c8SVadim Pasternak #define MLXREG_FAN_PWM_DUTY2STATE(duty)	(DIV_ROUND_CLOSEST((duty) *	\
5165afb4c8SVadim Pasternak 					 MLXREG_FAN_MAX_STATE,		\
5265afb4c8SVadim Pasternak 					 MLXREG_FAN_MAX_DUTY))
5365afb4c8SVadim Pasternak #define MLXREG_FAN_PWM_STATE2DUTY(stat)	(DIV_ROUND_CLOSEST((stat) *	\
5465afb4c8SVadim Pasternak 					 MLXREG_FAN_MAX_DUTY,		\
5565afb4c8SVadim Pasternak 					 MLXREG_FAN_MAX_STATE))
5665afb4c8SVadim Pasternak 
57d7efb2ebSVadim Pasternak struct mlxreg_fan;
58d7efb2ebSVadim Pasternak 
5965afb4c8SVadim Pasternak /*
6065afb4c8SVadim Pasternak  * struct mlxreg_fan_tacho - tachometer data (internal use):
6165afb4c8SVadim Pasternak  *
6265afb4c8SVadim Pasternak  * @connected: indicates if tachometer is connected;
6365afb4c8SVadim Pasternak  * @reg: register offset;
6465afb4c8SVadim Pasternak  * @mask: fault mask;
65f7bf7eb2SVadim Pasternak  * @prsnt: present register offset;
6665afb4c8SVadim Pasternak  */
6765afb4c8SVadim Pasternak struct mlxreg_fan_tacho {
6865afb4c8SVadim Pasternak 	bool connected;
6965afb4c8SVadim Pasternak 	u32 reg;
7065afb4c8SVadim Pasternak 	u32 mask;
71f7bf7eb2SVadim Pasternak 	u32 prsnt;
7265afb4c8SVadim Pasternak };
7365afb4c8SVadim Pasternak 
7465afb4c8SVadim Pasternak /*
7565afb4c8SVadim Pasternak  * struct mlxreg_fan_pwm - PWM data (internal use):
7665afb4c8SVadim Pasternak  *
77d7efb2ebSVadim Pasternak  * @fan: private data;
7865afb4c8SVadim Pasternak  * @connected: indicates if PWM is connected;
7965afb4c8SVadim Pasternak  * @reg: register offset;
80d7efb2ebSVadim Pasternak  * @cooling: cooling device levels;
81da74944dSVadim Pasternak  * @last_hwmon_state: last cooling state set by hwmon subsystem;
82da74944dSVadim Pasternak  * @last_thermal_state: last cooling state set by thermal subsystem;
83d7efb2ebSVadim Pasternak  * @cdev: cooling device;
8465afb4c8SVadim Pasternak  */
8565afb4c8SVadim Pasternak struct mlxreg_fan_pwm {
86d7efb2ebSVadim Pasternak 	struct mlxreg_fan *fan;
8765afb4c8SVadim Pasternak 	bool connected;
8865afb4c8SVadim Pasternak 	u32 reg;
89da74944dSVadim Pasternak 	unsigned long last_hwmon_state;
90da74944dSVadim Pasternak 	unsigned long last_thermal_state;
91d7efb2ebSVadim Pasternak 	struct thermal_cooling_device *cdev;
9265afb4c8SVadim Pasternak };
9365afb4c8SVadim Pasternak 
9465afb4c8SVadim Pasternak /*
9565afb4c8SVadim Pasternak  * struct mlxreg_fan - private data (internal use):
9665afb4c8SVadim Pasternak  *
9765afb4c8SVadim Pasternak  * @dev: basic device;
9865afb4c8SVadim Pasternak  * @regmap: register map of parent device;
9965afb4c8SVadim Pasternak  * @tacho: tachometer data;
10065afb4c8SVadim Pasternak  * @pwm: PWM data;
101f7bf7eb2SVadim Pasternak  * @tachos_per_drwr - number of tachometers per drawer;
10265afb4c8SVadim Pasternak  * @samples: minimum allowed samples per pulse;
10365afb4c8SVadim Pasternak  * @divider: divider value for tachometer RPM calculation;
10465afb4c8SVadim Pasternak  */
10565afb4c8SVadim Pasternak struct mlxreg_fan {
10665afb4c8SVadim Pasternak 	struct device *dev;
10765afb4c8SVadim Pasternak 	void *regmap;
10865afb4c8SVadim Pasternak 	struct mlxreg_core_platform_data *pdata;
10965afb4c8SVadim Pasternak 	struct mlxreg_fan_tacho tacho[MLXREG_FAN_MAX_TACHO];
110150f1e0cSVadim Pasternak 	struct mlxreg_fan_pwm pwm[MLXREG_FAN_MAX_PWM];
111f7bf7eb2SVadim Pasternak 	int tachos_per_drwr;
11265afb4c8SVadim Pasternak 	int samples;
11365afb4c8SVadim Pasternak 	int divider;
11465afb4c8SVadim Pasternak };
11565afb4c8SVadim Pasternak 
116da74944dSVadim Pasternak static int mlxreg_fan_set_cur_state(struct thermal_cooling_device *cdev,
117da74944dSVadim Pasternak 				    unsigned long state);
118da74944dSVadim Pasternak 
11965afb4c8SVadim Pasternak static int
mlxreg_fan_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)12065afb4c8SVadim Pasternak mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
12165afb4c8SVadim Pasternak 		int channel, long *val)
12265afb4c8SVadim Pasternak {
12365afb4c8SVadim Pasternak 	struct mlxreg_fan *fan = dev_get_drvdata(dev);
12465afb4c8SVadim Pasternak 	struct mlxreg_fan_tacho *tacho;
125150f1e0cSVadim Pasternak 	struct mlxreg_fan_pwm *pwm;
12665afb4c8SVadim Pasternak 	u32 regval;
12765afb4c8SVadim Pasternak 	int err;
12865afb4c8SVadim Pasternak 
12965afb4c8SVadim Pasternak 	switch (type) {
13065afb4c8SVadim Pasternak 	case hwmon_fan:
13165afb4c8SVadim Pasternak 		tacho = &fan->tacho[channel];
13265afb4c8SVadim Pasternak 		switch (attr) {
13365afb4c8SVadim Pasternak 		case hwmon_fan_input:
134f7bf7eb2SVadim Pasternak 			/*
135f7bf7eb2SVadim Pasternak 			 * Check FAN presence: FAN related bit in presence register is one,
136f7bf7eb2SVadim Pasternak 			 * if FAN is physically connected, zero - otherwise.
137f7bf7eb2SVadim Pasternak 			 */
138f7bf7eb2SVadim Pasternak 			if (tacho->prsnt && fan->tachos_per_drwr) {
139f7bf7eb2SVadim Pasternak 				err = regmap_read(fan->regmap, tacho->prsnt, &regval);
140f7bf7eb2SVadim Pasternak 				if (err)
141f7bf7eb2SVadim Pasternak 					return err;
142f7bf7eb2SVadim Pasternak 
143f7bf7eb2SVadim Pasternak 				/*
144f7bf7eb2SVadim Pasternak 				 * Map channel to presence bit - drawer can be equipped with
145f7bf7eb2SVadim Pasternak 				 * one or few FANs, while presence is indicated per drawer.
146f7bf7eb2SVadim Pasternak 				 */
147f7bf7eb2SVadim Pasternak 				if (BIT(channel / fan->tachos_per_drwr) & regval) {
148f7bf7eb2SVadim Pasternak 					/* FAN is not connected - return zero for FAN speed. */
149f7bf7eb2SVadim Pasternak 					*val = 0;
150f7bf7eb2SVadim Pasternak 					return 0;
151f7bf7eb2SVadim Pasternak 				}
152f7bf7eb2SVadim Pasternak 			}
153f7bf7eb2SVadim Pasternak 
15465afb4c8SVadim Pasternak 			err = regmap_read(fan->regmap, tacho->reg, &regval);
15565afb4c8SVadim Pasternak 			if (err)
15665afb4c8SVadim Pasternak 				return err;
15765afb4c8SVadim Pasternak 
158a1ffd3c4SVadim Pasternak 			if (MLXREG_FAN_GET_FAULT(regval, tacho->mask)) {
159a1ffd3c4SVadim Pasternak 				/* FAN is broken - return zero for FAN speed. */
160a1ffd3c4SVadim Pasternak 				*val = 0;
161a1ffd3c4SVadim Pasternak 				return 0;
162a1ffd3c4SVadim Pasternak 			}
163a1ffd3c4SVadim Pasternak 
16465afb4c8SVadim Pasternak 			*val = MLXREG_FAN_GET_RPM(regval, fan->divider,
16565afb4c8SVadim Pasternak 						  fan->samples);
16665afb4c8SVadim Pasternak 			break;
16765afb4c8SVadim Pasternak 
16865afb4c8SVadim Pasternak 		case hwmon_fan_fault:
16965afb4c8SVadim Pasternak 			err = regmap_read(fan->regmap, tacho->reg, &regval);
17065afb4c8SVadim Pasternak 			if (err)
17165afb4c8SVadim Pasternak 				return err;
17265afb4c8SVadim Pasternak 
17365afb4c8SVadim Pasternak 			*val = MLXREG_FAN_GET_FAULT(regval, tacho->mask);
17465afb4c8SVadim Pasternak 			break;
17565afb4c8SVadim Pasternak 
17665afb4c8SVadim Pasternak 		default:
17765afb4c8SVadim Pasternak 			return -EOPNOTSUPP;
17865afb4c8SVadim Pasternak 		}
17965afb4c8SVadim Pasternak 		break;
18065afb4c8SVadim Pasternak 
18165afb4c8SVadim Pasternak 	case hwmon_pwm:
182150f1e0cSVadim Pasternak 		pwm = &fan->pwm[channel];
18365afb4c8SVadim Pasternak 		switch (attr) {
18465afb4c8SVadim Pasternak 		case hwmon_pwm_input:
185150f1e0cSVadim Pasternak 			err = regmap_read(fan->regmap, pwm->reg, &regval);
18665afb4c8SVadim Pasternak 			if (err)
18765afb4c8SVadim Pasternak 				return err;
18865afb4c8SVadim Pasternak 
18965afb4c8SVadim Pasternak 			*val = regval;
19065afb4c8SVadim Pasternak 			break;
19165afb4c8SVadim Pasternak 
19265afb4c8SVadim Pasternak 		default:
19365afb4c8SVadim Pasternak 			return -EOPNOTSUPP;
19465afb4c8SVadim Pasternak 		}
19565afb4c8SVadim Pasternak 		break;
19665afb4c8SVadim Pasternak 
19765afb4c8SVadim Pasternak 	default:
19865afb4c8SVadim Pasternak 		return -EOPNOTSUPP;
19965afb4c8SVadim Pasternak 	}
20065afb4c8SVadim Pasternak 
20165afb4c8SVadim Pasternak 	return 0;
20265afb4c8SVadim Pasternak }
20365afb4c8SVadim Pasternak 
20465afb4c8SVadim Pasternak static int
mlxreg_fan_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)20565afb4c8SVadim Pasternak mlxreg_fan_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
20665afb4c8SVadim Pasternak 		 int channel, long val)
20765afb4c8SVadim Pasternak {
20865afb4c8SVadim Pasternak 	struct mlxreg_fan *fan = dev_get_drvdata(dev);
209150f1e0cSVadim Pasternak 	struct mlxreg_fan_pwm *pwm;
21065afb4c8SVadim Pasternak 
21165afb4c8SVadim Pasternak 	switch (type) {
21265afb4c8SVadim Pasternak 	case hwmon_pwm:
21365afb4c8SVadim Pasternak 		switch (attr) {
21465afb4c8SVadim Pasternak 		case hwmon_pwm_input:
21565afb4c8SVadim Pasternak 			if (val < MLXREG_FAN_MIN_DUTY ||
21665afb4c8SVadim Pasternak 			    val > MLXREG_FAN_MAX_DUTY)
21765afb4c8SVadim Pasternak 				return -EINVAL;
218150f1e0cSVadim Pasternak 			pwm = &fan->pwm[channel];
219da74944dSVadim Pasternak 			/* If thermal is configured - handle PWM limit setting. */
220da74944dSVadim Pasternak 			if (IS_REACHABLE(CONFIG_THERMAL)) {
221da74944dSVadim Pasternak 				pwm->last_hwmon_state = MLXREG_FAN_PWM_DUTY2STATE(val);
222da74944dSVadim Pasternak 				/*
223da74944dSVadim Pasternak 				 * Update PWM only in case requested state is not less than the
224da74944dSVadim Pasternak 				 * last thermal state.
225da74944dSVadim Pasternak 				 */
226da74944dSVadim Pasternak 				if (pwm->last_hwmon_state >= pwm->last_thermal_state)
227da74944dSVadim Pasternak 					return mlxreg_fan_set_cur_state(pwm->cdev,
228da74944dSVadim Pasternak 									pwm->last_hwmon_state);
229da74944dSVadim Pasternak 				return 0;
230da74944dSVadim Pasternak 			}
231150f1e0cSVadim Pasternak 			return regmap_write(fan->regmap, pwm->reg, val);
23265afb4c8SVadim Pasternak 		default:
23365afb4c8SVadim Pasternak 			return -EOPNOTSUPP;
23465afb4c8SVadim Pasternak 		}
23565afb4c8SVadim Pasternak 		break;
23665afb4c8SVadim Pasternak 
23765afb4c8SVadim Pasternak 	default:
23865afb4c8SVadim Pasternak 		return -EOPNOTSUPP;
23965afb4c8SVadim Pasternak 	}
24065afb4c8SVadim Pasternak 
24165afb4c8SVadim Pasternak 	return -EOPNOTSUPP;
24265afb4c8SVadim Pasternak }
24365afb4c8SVadim Pasternak 
24465afb4c8SVadim Pasternak static umode_t
mlxreg_fan_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)24565afb4c8SVadim Pasternak mlxreg_fan_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
24665afb4c8SVadim Pasternak 		      int channel)
24765afb4c8SVadim Pasternak {
24865afb4c8SVadim Pasternak 	switch (type) {
24965afb4c8SVadim Pasternak 	case hwmon_fan:
25065afb4c8SVadim Pasternak 		if (!(((struct mlxreg_fan *)data)->tacho[channel].connected))
25165afb4c8SVadim Pasternak 			return 0;
25265afb4c8SVadim Pasternak 
25365afb4c8SVadim Pasternak 		switch (attr) {
25465afb4c8SVadim Pasternak 		case hwmon_fan_input:
25565afb4c8SVadim Pasternak 		case hwmon_fan_fault:
25665afb4c8SVadim Pasternak 			return 0444;
25765afb4c8SVadim Pasternak 		default:
25865afb4c8SVadim Pasternak 			break;
25965afb4c8SVadim Pasternak 		}
26065afb4c8SVadim Pasternak 		break;
26165afb4c8SVadim Pasternak 
26265afb4c8SVadim Pasternak 	case hwmon_pwm:
263150f1e0cSVadim Pasternak 		if (!(((struct mlxreg_fan *)data)->pwm[channel].connected))
26465afb4c8SVadim Pasternak 			return 0;
26565afb4c8SVadim Pasternak 
26665afb4c8SVadim Pasternak 		switch (attr) {
26765afb4c8SVadim Pasternak 		case hwmon_pwm_input:
26865afb4c8SVadim Pasternak 			return 0644;
26965afb4c8SVadim Pasternak 		default:
27065afb4c8SVadim Pasternak 			break;
27165afb4c8SVadim Pasternak 		}
27265afb4c8SVadim Pasternak 		break;
27365afb4c8SVadim Pasternak 
27465afb4c8SVadim Pasternak 	default:
27565afb4c8SVadim Pasternak 		break;
27665afb4c8SVadim Pasternak 	}
27765afb4c8SVadim Pasternak 
27865afb4c8SVadim Pasternak 	return 0;
27965afb4c8SVadim Pasternak }
28065afb4c8SVadim Pasternak 
281b2be2422SVadim Pasternak static char *mlxreg_fan_name[] = {
282b2be2422SVadim Pasternak 	"mlxreg_fan",
283b2be2422SVadim Pasternak 	"mlxreg_fan1",
284b2be2422SVadim Pasternak 	"mlxreg_fan2",
285b2be2422SVadim Pasternak 	"mlxreg_fan3",
286b2be2422SVadim Pasternak };
287b2be2422SVadim Pasternak 
288bb54a54bSKrzysztof Kozlowski static const struct hwmon_channel_info * const mlxreg_fan_hwmon_info[] = {
289725dcf08SGuenter Roeck 	HWMON_CHANNEL_INFO(fan,
290725dcf08SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_FAULT,
291725dcf08SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_FAULT,
292725dcf08SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_FAULT,
293725dcf08SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_FAULT,
294725dcf08SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_FAULT,
295725dcf08SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_FAULT,
296725dcf08SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_FAULT,
297725dcf08SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_FAULT,
298725dcf08SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_FAULT,
299725dcf08SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_FAULT,
300725dcf08SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_FAULT,
301bc8de07eSVadim Pasternak 			   HWMON_F_INPUT | HWMON_F_FAULT,
302bc8de07eSVadim Pasternak 			   HWMON_F_INPUT | HWMON_F_FAULT,
303*dadca53dSVadim Pasternak 			   HWMON_F_INPUT | HWMON_F_FAULT,
304*dadca53dSVadim Pasternak 			   HWMON_F_INPUT | HWMON_F_FAULT,
305*dadca53dSVadim Pasternak 			   HWMON_F_INPUT | HWMON_F_FAULT,
306*dadca53dSVadim Pasternak 			   HWMON_F_INPUT | HWMON_F_FAULT,
307*dadca53dSVadim Pasternak 			   HWMON_F_INPUT | HWMON_F_FAULT,
308*dadca53dSVadim Pasternak 			   HWMON_F_INPUT | HWMON_F_FAULT,
309*dadca53dSVadim Pasternak 			   HWMON_F_INPUT | HWMON_F_FAULT,
310*dadca53dSVadim Pasternak 			   HWMON_F_INPUT | HWMON_F_FAULT,
311*dadca53dSVadim Pasternak 			   HWMON_F_INPUT | HWMON_F_FAULT,
312*dadca53dSVadim Pasternak 			   HWMON_F_INPUT | HWMON_F_FAULT,
313725dcf08SGuenter Roeck 			   HWMON_F_INPUT | HWMON_F_FAULT),
314725dcf08SGuenter Roeck 	HWMON_CHANNEL_INFO(pwm,
315150f1e0cSVadim Pasternak 			   HWMON_PWM_INPUT,
316150f1e0cSVadim Pasternak 			   HWMON_PWM_INPUT,
317150f1e0cSVadim Pasternak 			   HWMON_PWM_INPUT,
318725dcf08SGuenter Roeck 			   HWMON_PWM_INPUT),
31965afb4c8SVadim Pasternak 	NULL
32065afb4c8SVadim Pasternak };
32165afb4c8SVadim Pasternak 
32265afb4c8SVadim Pasternak static const struct hwmon_ops mlxreg_fan_hwmon_hwmon_ops = {
32365afb4c8SVadim Pasternak 	.is_visible = mlxreg_fan_is_visible,
32465afb4c8SVadim Pasternak 	.read = mlxreg_fan_read,
32565afb4c8SVadim Pasternak 	.write = mlxreg_fan_write,
32665afb4c8SVadim Pasternak };
32765afb4c8SVadim Pasternak 
32865afb4c8SVadim Pasternak static const struct hwmon_chip_info mlxreg_fan_hwmon_chip_info = {
32965afb4c8SVadim Pasternak 	.ops = &mlxreg_fan_hwmon_hwmon_ops,
33065afb4c8SVadim Pasternak 	.info = mlxreg_fan_hwmon_info,
33165afb4c8SVadim Pasternak };
33265afb4c8SVadim Pasternak 
mlxreg_fan_get_max_state(struct thermal_cooling_device * cdev,unsigned long * state)33365afb4c8SVadim Pasternak static int mlxreg_fan_get_max_state(struct thermal_cooling_device *cdev,
33465afb4c8SVadim Pasternak 				    unsigned long *state)
33565afb4c8SVadim Pasternak {
33665afb4c8SVadim Pasternak 	*state = MLXREG_FAN_MAX_STATE;
33765afb4c8SVadim Pasternak 	return 0;
33865afb4c8SVadim Pasternak }
33965afb4c8SVadim Pasternak 
mlxreg_fan_get_cur_state(struct thermal_cooling_device * cdev,unsigned long * state)34065afb4c8SVadim Pasternak static int mlxreg_fan_get_cur_state(struct thermal_cooling_device *cdev,
34165afb4c8SVadim Pasternak 				    unsigned long *state)
34265afb4c8SVadim Pasternak 
34365afb4c8SVadim Pasternak {
344d7efb2ebSVadim Pasternak 	struct mlxreg_fan_pwm *pwm = cdev->devdata;
345d7efb2ebSVadim Pasternak 	struct mlxreg_fan *fan = pwm->fan;
34665afb4c8SVadim Pasternak 	u32 regval;
34765afb4c8SVadim Pasternak 	int err;
34865afb4c8SVadim Pasternak 
349d7efb2ebSVadim Pasternak 	err = regmap_read(fan->regmap, pwm->reg, &regval);
35065afb4c8SVadim Pasternak 	if (err) {
35165afb4c8SVadim Pasternak 		dev_err(fan->dev, "Failed to query PWM duty\n");
35265afb4c8SVadim Pasternak 		return err;
35365afb4c8SVadim Pasternak 	}
35465afb4c8SVadim Pasternak 
35565afb4c8SVadim Pasternak 	*state = MLXREG_FAN_PWM_DUTY2STATE(regval);
35665afb4c8SVadim Pasternak 
35765afb4c8SVadim Pasternak 	return 0;
35865afb4c8SVadim Pasternak }
35965afb4c8SVadim Pasternak 
mlxreg_fan_set_cur_state(struct thermal_cooling_device * cdev,unsigned long state)36065afb4c8SVadim Pasternak static int mlxreg_fan_set_cur_state(struct thermal_cooling_device *cdev,
36165afb4c8SVadim Pasternak 				    unsigned long state)
36265afb4c8SVadim Pasternak 
36365afb4c8SVadim Pasternak {
364d7efb2ebSVadim Pasternak 	struct mlxreg_fan_pwm *pwm = cdev->devdata;
365d7efb2ebSVadim Pasternak 	struct mlxreg_fan *fan = pwm->fan;
36665afb4c8SVadim Pasternak 	int err;
36765afb4c8SVadim Pasternak 
36865afb4c8SVadim Pasternak 	if (state > MLXREG_FAN_MAX_STATE)
36965afb4c8SVadim Pasternak 		return -EINVAL;
37065afb4c8SVadim Pasternak 
371da74944dSVadim Pasternak 	/* Save thermal state. */
372da74944dSVadim Pasternak 	pwm->last_thermal_state = state;
373da74944dSVadim Pasternak 
374da74944dSVadim Pasternak 	state = max_t(unsigned long, state, pwm->last_hwmon_state);
375d7efb2ebSVadim Pasternak 	err = regmap_write(fan->regmap, pwm->reg,
37665afb4c8SVadim Pasternak 			   MLXREG_FAN_PWM_STATE2DUTY(state));
37765afb4c8SVadim Pasternak 	if (err) {
37865afb4c8SVadim Pasternak 		dev_err(fan->dev, "Failed to write PWM duty\n");
37965afb4c8SVadim Pasternak 		return err;
38065afb4c8SVadim Pasternak 	}
381da74944dSVadim Pasternak 	return 0;
38265afb4c8SVadim Pasternak }
38365afb4c8SVadim Pasternak 
38465afb4c8SVadim Pasternak static const struct thermal_cooling_device_ops mlxreg_fan_cooling_ops = {
38565afb4c8SVadim Pasternak 	.get_max_state	= mlxreg_fan_get_max_state,
38665afb4c8SVadim Pasternak 	.get_cur_state	= mlxreg_fan_get_cur_state,
38765afb4c8SVadim Pasternak 	.set_cur_state	= mlxreg_fan_set_cur_state,
38865afb4c8SVadim Pasternak };
38965afb4c8SVadim Pasternak 
mlxreg_fan_connect_verify(struct mlxreg_fan * fan,struct mlxreg_core_data * data)390b429ebc8SVadim Pasternak static int mlxreg_fan_connect_verify(struct mlxreg_fan *fan,
391b429ebc8SVadim Pasternak 				     struct mlxreg_core_data *data)
392b429ebc8SVadim Pasternak {
393b429ebc8SVadim Pasternak 	u32 regval;
394b429ebc8SVadim Pasternak 	int err;
395b429ebc8SVadim Pasternak 
396b429ebc8SVadim Pasternak 	err = regmap_read(fan->regmap, data->capability, &regval);
397b429ebc8SVadim Pasternak 	if (err) {
398b429ebc8SVadim Pasternak 		dev_err(fan->dev, "Failed to query capability register 0x%08x\n",
399b429ebc8SVadim Pasternak 			data->capability);
400b429ebc8SVadim Pasternak 		return err;
401b429ebc8SVadim Pasternak 	}
402b429ebc8SVadim Pasternak 
403b429ebc8SVadim Pasternak 	return !!(regval & data->bit);
404b429ebc8SVadim Pasternak }
405b429ebc8SVadim Pasternak 
mlxreg_pwm_connect_verify(struct mlxreg_fan * fan,struct mlxreg_core_data * data)406150f1e0cSVadim Pasternak static int mlxreg_pwm_connect_verify(struct mlxreg_fan *fan,
407150f1e0cSVadim Pasternak 				     struct mlxreg_core_data *data)
408150f1e0cSVadim Pasternak {
409150f1e0cSVadim Pasternak 	u32 regval;
410150f1e0cSVadim Pasternak 	int err;
411150f1e0cSVadim Pasternak 
412150f1e0cSVadim Pasternak 	err = regmap_read(fan->regmap, data->reg, &regval);
413150f1e0cSVadim Pasternak 	if (err) {
414150f1e0cSVadim Pasternak 		dev_err(fan->dev, "Failed to query pwm register 0x%08x\n",
415150f1e0cSVadim Pasternak 			data->reg);
416150f1e0cSVadim Pasternak 		return err;
417150f1e0cSVadim Pasternak 	}
418150f1e0cSVadim Pasternak 
419150f1e0cSVadim Pasternak 	return regval != MLXREG_FAN_PWM_NOT_CONNECTED;
420150f1e0cSVadim Pasternak }
421150f1e0cSVadim Pasternak 
mlxreg_fan_speed_divider_get(struct mlxreg_fan * fan,struct mlxreg_core_data * data)422b429ebc8SVadim Pasternak static int mlxreg_fan_speed_divider_get(struct mlxreg_fan *fan,
423b429ebc8SVadim Pasternak 					struct mlxreg_core_data *data)
424b429ebc8SVadim Pasternak {
425b429ebc8SVadim Pasternak 	u32 regval;
426b429ebc8SVadim Pasternak 	int err;
427b429ebc8SVadim Pasternak 
428b429ebc8SVadim Pasternak 	err = regmap_read(fan->regmap, data->capability, &regval);
429b429ebc8SVadim Pasternak 	if (err) {
430b429ebc8SVadim Pasternak 		dev_err(fan->dev, "Failed to query capability register 0x%08x\n",
431b429ebc8SVadim Pasternak 			data->capability);
432b429ebc8SVadim Pasternak 		return err;
433b429ebc8SVadim Pasternak 	}
434b429ebc8SVadim Pasternak 
435b429ebc8SVadim Pasternak 	/*
436b429ebc8SVadim Pasternak 	 * Set divider value according to the capability register, in case it
437b429ebc8SVadim Pasternak 	 * contains valid value. Otherwise use default value. The purpose of
438b429ebc8SVadim Pasternak 	 * this validation is to protect against the old hardware, in which
439b429ebc8SVadim Pasternak 	 * this register can return zero.
440b429ebc8SVadim Pasternak 	 */
441b429ebc8SVadim Pasternak 	if (regval > 0 && regval <= MLXREG_FAN_TACHO_DIV_SCALE_MAX)
442b429ebc8SVadim Pasternak 		fan->divider = regval * MLXREG_FAN_TACHO_DIV_MIN;
443b429ebc8SVadim Pasternak 
444b429ebc8SVadim Pasternak 	return 0;
445b429ebc8SVadim Pasternak }
446b429ebc8SVadim Pasternak 
mlxreg_fan_config(struct mlxreg_fan * fan,struct mlxreg_core_platform_data * pdata)44765afb4c8SVadim Pasternak static int mlxreg_fan_config(struct mlxreg_fan *fan,
44865afb4c8SVadim Pasternak 			     struct mlxreg_core_platform_data *pdata)
44965afb4c8SVadim Pasternak {
450150f1e0cSVadim Pasternak 	int tacho_num = 0, tacho_avail = 0, pwm_num = 0, i;
45165afb4c8SVadim Pasternak 	struct mlxreg_core_data *data = pdata->data;
45265afb4c8SVadim Pasternak 	bool configured = false;
453b429ebc8SVadim Pasternak 	int err;
45465afb4c8SVadim Pasternak 
45565afb4c8SVadim Pasternak 	fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF;
456b429ebc8SVadim Pasternak 	fan->divider = MLXREG_FAN_TACHO_DIV_DEF;
45765afb4c8SVadim Pasternak 	for (i = 0; i < pdata->counter; i++, data++) {
45865afb4c8SVadim Pasternak 		if (strnstr(data->label, "tacho", sizeof(data->label))) {
45965afb4c8SVadim Pasternak 			if (tacho_num == MLXREG_FAN_MAX_TACHO) {
46065afb4c8SVadim Pasternak 				dev_err(fan->dev, "too many tacho entries: %s\n",
46165afb4c8SVadim Pasternak 					data->label);
46265afb4c8SVadim Pasternak 				return -EINVAL;
46365afb4c8SVadim Pasternak 			}
464b429ebc8SVadim Pasternak 
465b429ebc8SVadim Pasternak 			if (data->capability) {
466b429ebc8SVadim Pasternak 				err = mlxreg_fan_connect_verify(fan, data);
467b429ebc8SVadim Pasternak 				if (err < 0)
468b429ebc8SVadim Pasternak 					return err;
469b429ebc8SVadim Pasternak 				else if (!err) {
470b429ebc8SVadim Pasternak 					tacho_num++;
471b429ebc8SVadim Pasternak 					continue;
472b429ebc8SVadim Pasternak 				}
473b429ebc8SVadim Pasternak 			}
474b429ebc8SVadim Pasternak 
47565afb4c8SVadim Pasternak 			fan->tacho[tacho_num].reg = data->reg;
47665afb4c8SVadim Pasternak 			fan->tacho[tacho_num].mask = data->mask;
477f7bf7eb2SVadim Pasternak 			fan->tacho[tacho_num].prsnt = data->reg_prsnt;
47865afb4c8SVadim Pasternak 			fan->tacho[tacho_num++].connected = true;
479f7bf7eb2SVadim Pasternak 			tacho_avail++;
48065afb4c8SVadim Pasternak 		} else if (strnstr(data->label, "pwm", sizeof(data->label))) {
481150f1e0cSVadim Pasternak 			if (pwm_num == MLXREG_FAN_MAX_TACHO) {
482150f1e0cSVadim Pasternak 				dev_err(fan->dev, "too many pwm entries: %s\n",
48365afb4c8SVadim Pasternak 					data->label);
48465afb4c8SVadim Pasternak 				return -EINVAL;
48565afb4c8SVadim Pasternak 			}
486150f1e0cSVadim Pasternak 
487b1c24237SVadim Pasternak 			/* Validate if more then one PWM is connected. */
488b1c24237SVadim Pasternak 			if (pwm_num) {
489150f1e0cSVadim Pasternak 				err = mlxreg_pwm_connect_verify(fan, data);
490b1c24237SVadim Pasternak 				if (err < 0)
491150f1e0cSVadim Pasternak 					return err;
492b1c24237SVadim Pasternak 				else if (!err)
493b1c24237SVadim Pasternak 					continue;
494b1c24237SVadim Pasternak 			}
495150f1e0cSVadim Pasternak 
496150f1e0cSVadim Pasternak 			fan->pwm[pwm_num].reg = data->reg;
497150f1e0cSVadim Pasternak 			fan->pwm[pwm_num].connected = true;
498150f1e0cSVadim Pasternak 			pwm_num++;
49965afb4c8SVadim Pasternak 		} else if (strnstr(data->label, "conf", sizeof(data->label))) {
50065afb4c8SVadim Pasternak 			if (configured) {
50165afb4c8SVadim Pasternak 				dev_err(fan->dev, "duplicate conf entry: %s\n",
50265afb4c8SVadim Pasternak 					data->label);
50365afb4c8SVadim Pasternak 				return -EINVAL;
50465afb4c8SVadim Pasternak 			}
50565afb4c8SVadim Pasternak 			/* Validate that conf parameters are not zeros. */
506b429ebc8SVadim Pasternak 			if (!data->mask && !data->bit && !data->capability) {
50765afb4c8SVadim Pasternak 				dev_err(fan->dev, "invalid conf entry params: %s\n",
50865afb4c8SVadim Pasternak 					data->label);
50965afb4c8SVadim Pasternak 				return -EINVAL;
51065afb4c8SVadim Pasternak 			}
511b429ebc8SVadim Pasternak 			if (data->capability) {
512b429ebc8SVadim Pasternak 				err = mlxreg_fan_speed_divider_get(fan, data);
513b429ebc8SVadim Pasternak 				if (err)
514b429ebc8SVadim Pasternak 					return err;
515b429ebc8SVadim Pasternak 			} else {
516b429ebc8SVadim Pasternak 				if (data->mask)
51765afb4c8SVadim Pasternak 					fan->samples = data->mask;
518b429ebc8SVadim Pasternak 				if (data->bit)
51965afb4c8SVadim Pasternak 					fan->divider = data->bit;
520b429ebc8SVadim Pasternak 			}
52165afb4c8SVadim Pasternak 			configured = true;
52265afb4c8SVadim Pasternak 		} else {
52365afb4c8SVadim Pasternak 			dev_err(fan->dev, "invalid label: %s\n", data->label);
52465afb4c8SVadim Pasternak 			return -EINVAL;
52565afb4c8SVadim Pasternak 		}
52665afb4c8SVadim Pasternak 	}
52765afb4c8SVadim Pasternak 
528f7bf7eb2SVadim Pasternak 	if (pdata->capability) {
529f7bf7eb2SVadim Pasternak 		int drwr_avail;
530f7bf7eb2SVadim Pasternak 		u32 regval;
531f7bf7eb2SVadim Pasternak 
532f7bf7eb2SVadim Pasternak 		/* Obtain the number of FAN drawers, supported by system. */
533f7bf7eb2SVadim Pasternak 		err = regmap_read(fan->regmap, pdata->capability, &regval);
534f7bf7eb2SVadim Pasternak 		if (err) {
535f7bf7eb2SVadim Pasternak 			dev_err(fan->dev, "Failed to query capability register 0x%08x\n",
536f7bf7eb2SVadim Pasternak 				pdata->capability);
537f7bf7eb2SVadim Pasternak 			return err;
538f7bf7eb2SVadim Pasternak 		}
539f7bf7eb2SVadim Pasternak 
540f7bf7eb2SVadim Pasternak 		drwr_avail = hweight32(regval);
541f7bf7eb2SVadim Pasternak 		if (!tacho_avail || !drwr_avail || tacho_avail < drwr_avail) {
542f7bf7eb2SVadim Pasternak 			dev_err(fan->dev, "Configuration is invalid: drawers num %d tachos num %d\n",
543f7bf7eb2SVadim Pasternak 				drwr_avail, tacho_avail);
544f7bf7eb2SVadim Pasternak 			return -EINVAL;
545f7bf7eb2SVadim Pasternak 		}
546f7bf7eb2SVadim Pasternak 
547f7bf7eb2SVadim Pasternak 		/* Set the number of tachometers per one drawer. */
548f7bf7eb2SVadim Pasternak 		fan->tachos_per_drwr = tacho_avail / drwr_avail;
549f7bf7eb2SVadim Pasternak 	}
550f7bf7eb2SVadim Pasternak 
551d7efb2ebSVadim Pasternak 	return 0;
552d7efb2ebSVadim Pasternak }
553d7efb2ebSVadim Pasternak 
mlxreg_fan_cooling_config(struct device * dev,struct mlxreg_fan * fan)554d7efb2ebSVadim Pasternak static int mlxreg_fan_cooling_config(struct device *dev, struct mlxreg_fan *fan)
555d7efb2ebSVadim Pasternak {
556da74944dSVadim Pasternak 	int i;
557d7efb2ebSVadim Pasternak 
558000cc5bcSColin Ian King 	for (i = 0; i < MLXREG_FAN_MAX_PWM; i++) {
559d7efb2ebSVadim Pasternak 		struct mlxreg_fan_pwm *pwm = &fan->pwm[i];
560d7efb2ebSVadim Pasternak 
561d7efb2ebSVadim Pasternak 		if (!pwm->connected)
562d7efb2ebSVadim Pasternak 			continue;
563d7efb2ebSVadim Pasternak 		pwm->fan = fan;
564b2be2422SVadim Pasternak 		pwm->cdev = devm_thermal_of_cooling_device_register(dev, NULL, mlxreg_fan_name[i],
565b2be2422SVadim Pasternak 								    pwm, &mlxreg_fan_cooling_ops);
566d7efb2ebSVadim Pasternak 		if (IS_ERR(pwm->cdev)) {
567d7efb2ebSVadim Pasternak 			dev_err(dev, "Failed to register cooling device\n");
568d7efb2ebSVadim Pasternak 			return PTR_ERR(pwm->cdev);
569d7efb2ebSVadim Pasternak 		}
570d7efb2ebSVadim Pasternak 
571da74944dSVadim Pasternak 		/* Set minimal PWM speed. */
572da74944dSVadim Pasternak 		pwm->last_hwmon_state = MLXREG_FAN_PWM_DUTY2STATE(MLXREG_FAN_MIN_DUTY);
573d7efb2ebSVadim Pasternak 	}
57465afb4c8SVadim Pasternak 
57565afb4c8SVadim Pasternak 	return 0;
57665afb4c8SVadim Pasternak }
57765afb4c8SVadim Pasternak 
mlxreg_fan_probe(struct platform_device * pdev)57865afb4c8SVadim Pasternak static int mlxreg_fan_probe(struct platform_device *pdev)
57965afb4c8SVadim Pasternak {
58065afb4c8SVadim Pasternak 	struct mlxreg_core_platform_data *pdata;
5819ebe010eSGuenter Roeck 	struct device *dev = &pdev->dev;
58265afb4c8SVadim Pasternak 	struct mlxreg_fan *fan;
58365afb4c8SVadim Pasternak 	struct device *hwm;
58465afb4c8SVadim Pasternak 	int err;
58565afb4c8SVadim Pasternak 
5869ebe010eSGuenter Roeck 	pdata = dev_get_platdata(dev);
58765afb4c8SVadim Pasternak 	if (!pdata) {
5889ebe010eSGuenter Roeck 		dev_err(dev, "Failed to get platform data.\n");
58965afb4c8SVadim Pasternak 		return -EINVAL;
59065afb4c8SVadim Pasternak 	}
59165afb4c8SVadim Pasternak 
5929ebe010eSGuenter Roeck 	fan = devm_kzalloc(dev, sizeof(*fan), GFP_KERNEL);
59365afb4c8SVadim Pasternak 	if (!fan)
59465afb4c8SVadim Pasternak 		return -ENOMEM;
59565afb4c8SVadim Pasternak 
5969ebe010eSGuenter Roeck 	fan->dev = dev;
59765afb4c8SVadim Pasternak 	fan->regmap = pdata->regmap;
59865afb4c8SVadim Pasternak 
59965afb4c8SVadim Pasternak 	err = mlxreg_fan_config(fan, pdata);
60065afb4c8SVadim Pasternak 	if (err)
60165afb4c8SVadim Pasternak 		return err;
60265afb4c8SVadim Pasternak 
6039ebe010eSGuenter Roeck 	hwm = devm_hwmon_device_register_with_info(dev, "mlxreg_fan",
60465afb4c8SVadim Pasternak 						   fan,
60565afb4c8SVadim Pasternak 						   &mlxreg_fan_hwmon_chip_info,
60665afb4c8SVadim Pasternak 						   NULL);
60765afb4c8SVadim Pasternak 	if (IS_ERR(hwm)) {
6089ebe010eSGuenter Roeck 		dev_err(dev, "Failed to register hwmon device\n");
60965afb4c8SVadim Pasternak 		return PTR_ERR(hwm);
61065afb4c8SVadim Pasternak 	}
61165afb4c8SVadim Pasternak 
612d7efb2ebSVadim Pasternak 	if (IS_REACHABLE(CONFIG_THERMAL))
613d7efb2ebSVadim Pasternak 		err = mlxreg_fan_cooling_config(dev, fan);
61465afb4c8SVadim Pasternak 
615d7efb2ebSVadim Pasternak 	return err;
61665afb4c8SVadim Pasternak }
61765afb4c8SVadim Pasternak 
61865afb4c8SVadim Pasternak static struct platform_driver mlxreg_fan_driver = {
61965afb4c8SVadim Pasternak 	.driver = {
62065afb4c8SVadim Pasternak 	    .name = "mlxreg-fan",
62165afb4c8SVadim Pasternak 	},
62265afb4c8SVadim Pasternak 	.probe = mlxreg_fan_probe,
62365afb4c8SVadim Pasternak };
62465afb4c8SVadim Pasternak 
62565afb4c8SVadim Pasternak module_platform_driver(mlxreg_fan_driver);
62665afb4c8SVadim Pasternak 
62765afb4c8SVadim Pasternak MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
62865afb4c8SVadim Pasternak MODULE_DESCRIPTION("Mellanox FAN driver");
62965afb4c8SVadim Pasternak MODULE_LICENSE("GPL");
63065afb4c8SVadim Pasternak MODULE_ALIAS("platform:mlxreg-fan");
631