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, ®val);
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, ®val);
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, ®val);
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, ®val);
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, ®val);
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, ®val);
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, ®val);
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, ®val);
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, ®val);
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