aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/mlxreg-fan.c
diff options
context:
space:
mode:
authorVadim Pasternak <vadimp@mellanox.com>2018-07-03 03:00:09 -0400
committerGuenter Roeck <linux@roeck-us.net>2018-07-08 23:08:13 -0400
commit65afb4c8e7e4e7e74b28efa1df62da503ca3e7a6 (patch)
tree442b2826d1a000f24e82c2e74451d1d10c11c4c1 /drivers/hwmon/mlxreg-fan.c
parentffb32432457c6a6deffb64982b54d8f7b72ef618 (diff)
hwmon: (mlxreg-fan) Add support for Mellanox FAN driver
Driver obtains PWM and tachometers registers location according to the system configuration and creates FAN/PWM hwmon objects and a cooling device. PWM and tachometers are controlled through the on-board programmable device, which exports its register map. This device could be attached to any bus type, for which register mapping is supported. Single instance is created with one PWM control, up to 12 tachometers and one cooling device. It could be as many instances as programmable device supports. Currently driver will be activated from the Mellanox platform driver: drivers/platform/x86/mlx-platform.c. For the future ARM based systems it could be activated from the ARM platform module. Signed-off-by: Vadim Pasternak <vadimp@mellanox.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers/hwmon/mlxreg-fan.c')
-rw-r--r--drivers/hwmon/mlxreg-fan.c489
1 files changed, 489 insertions, 0 deletions
diff --git a/drivers/hwmon/mlxreg-fan.c b/drivers/hwmon/mlxreg-fan.c
new file mode 100644
index 000000000000..de46577c7d5a
--- /dev/null
+++ b/drivers/hwmon/mlxreg-fan.c
@@ -0,0 +1,489 @@
1// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2//
3// Copyright (c) 2018 Mellanox Technologies. All rights reserved.
4// Copyright (c) 2018 Vadim Pasternak <vadimp@mellanox.com>
5
6#include <linux/bitops.h>
7#include <linux/device.h>
8#include <linux/hwmon.h>
9#include <linux/module.h>
10#include <linux/platform_data/mlxreg.h>
11#include <linux/platform_device.h>
12#include <linux/regmap.h>
13#include <linux/thermal.h>
14
15#define MLXREG_FAN_MAX_TACHO 12
16#define MLXREG_FAN_MAX_STATE 10
17#define MLXREG_FAN_MIN_DUTY 51 /* 20% */
18#define MLXREG_FAN_MAX_DUTY 255 /* 100% */
19/*
20 * Minimum and maximum FAN allowed speed in percent: from 20% to 100%. Values
21 * MLXREG_FAN_MAX_STATE + x, where x is between 2 and 10 are used for
22 * setting FAN speed dynamic minimum. For example, if value is set to 14 (40%)
23 * cooling levels vector will be set to 4, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10 to
24 * introduce PWM speed in percent: 40, 40, 40, 40, 40, 50, 60. 70, 80, 90, 100.
25 */
26#define MLXREG_FAN_SPEED_MIN (MLXREG_FAN_MAX_STATE + 2)
27#define MLXREG_FAN_SPEED_MAX (MLXREG_FAN_MAX_STATE * 2)
28#define MLXREG_FAN_SPEED_MIN_LEVEL 2 /* 20 percent */
29#define MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF 44
30#define MLXREG_FAN_TACHO_DIVIDER_DEF 1132
31/*
32 * FAN datasheet defines the formula for RPM calculations as RPM = 15/t-high.
33 * The logic in a programmable device measures the time t-high by sampling the
34 * tachometer every t-sample (with the default value 11.32 uS) and increment
35 * a counter (N) as long as the pulse has not change:
36 * RPM = 15 / (t-sample * (K + Regval)), where:
37 * Regval: is the value read from the programmable device register;
38 * - 0xff - represents tachometer fault;
39 * - 0xfe - represents tachometer minimum value , which is 4444 RPM;
40 * - 0x00 - represents tachometer maximum value , which is 300000 RPM;
41 * K: is 44 and it represents the minimum allowed samples per pulse;
42 * N: is equal K + Regval;
43 * In order to calculate RPM from the register value the following formula is
44 * used: RPM = 15 / ((Regval + K) * 11.32) * 10^(-6)), which in the
45 * default case is modified to:
46 * RPM = 15000000 * 100 / ((Regval + 44) * 1132);
47 * - for Regval 0x00, RPM will be 15000000 * 100 / (44 * 1132) = 30115;
48 * - for Regval 0xfe, RPM will be 15000000 * 100 / ((254 + 44) * 1132) = 4446;
49 * In common case the formula is modified to:
50 * RPM = 15000000 * 100 / ((Regval + samples) * divider).
51 */
52#define MLXREG_FAN_GET_RPM(rval, d, s) (DIV_ROUND_CLOSEST(15000000 * 100, \
53 ((rval) + (s)) * (d)))
54#define MLXREG_FAN_GET_FAULT(val, mask) (!!((val) ^ (mask)))
55#define MLXREG_FAN_PWM_DUTY2STATE(duty) (DIV_ROUND_CLOSEST((duty) * \
56 MLXREG_FAN_MAX_STATE, \
57 MLXREG_FAN_MAX_DUTY))
58#define MLXREG_FAN_PWM_STATE2DUTY(stat) (DIV_ROUND_CLOSEST((stat) * \
59 MLXREG_FAN_MAX_DUTY, \
60 MLXREG_FAN_MAX_STATE))
61
62/*
63 * struct mlxreg_fan_tacho - tachometer data (internal use):
64 *
65 * @connected: indicates if tachometer is connected;
66 * @reg: register offset;
67 * @mask: fault mask;
68 */
69struct mlxreg_fan_tacho {
70 bool connected;
71 u32 reg;
72 u32 mask;
73};
74
75/*
76 * struct mlxreg_fan_pwm - PWM data (internal use):
77 *
78 * @connected: indicates if PWM is connected;
79 * @reg: register offset;
80 */
81struct mlxreg_fan_pwm {
82 bool connected;
83 u32 reg;
84};
85
86/*
87 * struct mlxreg_fan - private data (internal use):
88 *
89 * @dev: basic device;
90 * @regmap: register map of parent device;
91 * @tacho: tachometer data;
92 * @pwm: PWM data;
93 * @samples: minimum allowed samples per pulse;
94 * @divider: divider value for tachometer RPM calculation;
95 * @cooling: cooling device levels;
96 * @cdev: cooling device;
97 */
98struct mlxreg_fan {
99 struct device *dev;
100 void *regmap;
101 struct mlxreg_core_platform_data *pdata;
102 struct mlxreg_fan_tacho tacho[MLXREG_FAN_MAX_TACHO];
103 struct mlxreg_fan_pwm pwm;
104 int samples;
105 int divider;
106 u8 cooling_levels[MLXREG_FAN_MAX_STATE + 1];
107 struct thermal_cooling_device *cdev;
108};
109
110static int
111mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
112 int channel, long *val)
113{
114 struct mlxreg_fan *fan = dev_get_drvdata(dev);
115 struct mlxreg_fan_tacho *tacho;
116 u32 regval;
117 int err;
118
119 switch (type) {
120 case hwmon_fan:
121 tacho = &fan->tacho[channel];
122 switch (attr) {
123 case hwmon_fan_input:
124 err = regmap_read(fan->regmap, tacho->reg, &regval);
125 if (err)
126 return err;
127
128 *val = MLXREG_FAN_GET_RPM(regval, fan->divider,
129 fan->samples);
130 break;
131
132 case hwmon_fan_fault:
133 err = regmap_read(fan->regmap, tacho->reg, &regval);
134 if (err)
135 return err;
136
137 *val = MLXREG_FAN_GET_FAULT(regval, tacho->mask);
138 break;
139
140 default:
141 return -EOPNOTSUPP;
142 }
143 break;
144
145 case hwmon_pwm:
146 switch (attr) {
147 case hwmon_pwm_input:
148 err = regmap_read(fan->regmap, fan->pwm.reg, &regval);
149 if (err)
150 return err;
151
152 *val = regval;
153 break;
154
155 default:
156 return -EOPNOTSUPP;
157 }
158 break;
159
160 default:
161 return -EOPNOTSUPP;
162 }
163
164 return 0;
165}
166
167static int
168mlxreg_fan_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
169 int channel, long val)
170{
171 struct mlxreg_fan *fan = dev_get_drvdata(dev);
172
173 switch (type) {
174 case hwmon_pwm:
175 switch (attr) {
176 case hwmon_pwm_input:
177 if (val < MLXREG_FAN_MIN_DUTY ||
178 val > MLXREG_FAN_MAX_DUTY)
179 return -EINVAL;
180 return regmap_write(fan->regmap, fan->pwm.reg, val);
181 default:
182 return -EOPNOTSUPP;
183 }
184 break;
185
186 default:
187 return -EOPNOTSUPP;
188 }
189
190 return -EOPNOTSUPP;
191}
192
193static umode_t
194mlxreg_fan_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
195 int channel)
196{
197 switch (type) {
198 case hwmon_fan:
199 if (!(((struct mlxreg_fan *)data)->tacho[channel].connected))
200 return 0;
201
202 switch (attr) {
203 case hwmon_fan_input:
204 case hwmon_fan_fault:
205 return 0444;
206 default:
207 break;
208 }
209 break;
210
211 case hwmon_pwm:
212 if (!(((struct mlxreg_fan *)data)->pwm.connected))
213 return 0;
214
215 switch (attr) {
216 case hwmon_pwm_input:
217 return 0644;
218 default:
219 break;
220 }
221 break;
222
223 default:
224 break;
225 }
226
227 return 0;
228}
229
230static const u32 mlxreg_fan_hwmon_fan_config[] = {
231 HWMON_F_INPUT | HWMON_F_FAULT,
232 HWMON_F_INPUT | HWMON_F_FAULT,
233 HWMON_F_INPUT | HWMON_F_FAULT,
234 HWMON_F_INPUT | HWMON_F_FAULT,
235 HWMON_F_INPUT | HWMON_F_FAULT,
236 HWMON_F_INPUT | HWMON_F_FAULT,
237 HWMON_F_INPUT | HWMON_F_FAULT,
238 HWMON_F_INPUT | HWMON_F_FAULT,
239 HWMON_F_INPUT | HWMON_F_FAULT,
240 HWMON_F_INPUT | HWMON_F_FAULT,
241 HWMON_F_INPUT | HWMON_F_FAULT,
242 HWMON_F_INPUT | HWMON_F_FAULT,
243 0
244};
245
246static const struct hwmon_channel_info mlxreg_fan_hwmon_fan = {
247 .type = hwmon_fan,
248 .config = mlxreg_fan_hwmon_fan_config,
249};
250
251static const u32 mlxreg_fan_hwmon_pwm_config[] = {
252 HWMON_PWM_INPUT,
253 0
254};
255
256static const struct hwmon_channel_info mlxreg_fan_hwmon_pwm = {
257 .type = hwmon_pwm,
258 .config = mlxreg_fan_hwmon_pwm_config,
259};
260
261static const struct hwmon_channel_info *mlxreg_fan_hwmon_info[] = {
262 &mlxreg_fan_hwmon_fan,
263 &mlxreg_fan_hwmon_pwm,
264 NULL
265};
266
267static const struct hwmon_ops mlxreg_fan_hwmon_hwmon_ops = {
268 .is_visible = mlxreg_fan_is_visible,
269 .read = mlxreg_fan_read,
270 .write = mlxreg_fan_write,
271};
272
273static const struct hwmon_chip_info mlxreg_fan_hwmon_chip_info = {
274 .ops = &mlxreg_fan_hwmon_hwmon_ops,
275 .info = mlxreg_fan_hwmon_info,
276};
277
278static int mlxreg_fan_get_max_state(struct thermal_cooling_device *cdev,
279 unsigned long *state)
280{
281 *state = MLXREG_FAN_MAX_STATE;
282 return 0;
283}
284
285static int mlxreg_fan_get_cur_state(struct thermal_cooling_device *cdev,
286 unsigned long *state)
287
288{
289 struct mlxreg_fan *fan = cdev->devdata;
290 u32 regval;
291 int err;
292
293 err = regmap_read(fan->regmap, fan->pwm.reg, &regval);
294 if (err) {
295 dev_err(fan->dev, "Failed to query PWM duty\n");
296 return err;
297 }
298
299 *state = MLXREG_FAN_PWM_DUTY2STATE(regval);
300
301 return 0;
302}
303
304static int mlxreg_fan_set_cur_state(struct thermal_cooling_device *cdev,
305 unsigned long state)
306
307{
308 struct mlxreg_fan *fan = cdev->devdata;
309 unsigned long cur_state;
310 u32 regval;
311 int i;
312 int err;
313
314 /*
315 * Verify if this request is for changing allowed FAN dynamical
316 * minimum. If it is - update cooling levels accordingly and update
317 * state, if current state is below the newly requested minimum state.
318 * For example, if current state is 5, and minimal state is to be
319 * changed from 4 to 6, fan->cooling_levels[0 to 5] will be changed all
320 * from 4 to 6. And state 5 (fan->cooling_levels[4]) should be
321 * overwritten.
322 */
323 if (state >= MLXREG_FAN_SPEED_MIN && state <= MLXREG_FAN_SPEED_MAX) {
324 state -= MLXREG_FAN_MAX_STATE;
325 for (i = 0; i < state; i++)
326 fan->cooling_levels[i] = state;
327 for (i = state; i <= MLXREG_FAN_MAX_STATE; i++)
328 fan->cooling_levels[i] = i;
329
330 err = regmap_read(fan->regmap, fan->pwm.reg, &regval);
331 if (err) {
332 dev_err(fan->dev, "Failed to query PWM duty\n");
333 return err;
334 }
335
336 cur_state = MLXREG_FAN_PWM_DUTY2STATE(regval);
337 if (state < cur_state)
338 return 0;
339
340 state = cur_state;
341 }
342
343 if (state > MLXREG_FAN_MAX_STATE)
344 return -EINVAL;
345
346 /* Normalize the state to the valid speed range. */
347 state = fan->cooling_levels[state];
348 err = regmap_write(fan->regmap, fan->pwm.reg,
349 MLXREG_FAN_PWM_STATE2DUTY(state));
350 if (err) {
351 dev_err(fan->dev, "Failed to write PWM duty\n");
352 return err;
353 }
354 return 0;
355}
356
357static const struct thermal_cooling_device_ops mlxreg_fan_cooling_ops = {
358 .get_max_state = mlxreg_fan_get_max_state,
359 .get_cur_state = mlxreg_fan_get_cur_state,
360 .set_cur_state = mlxreg_fan_set_cur_state,
361};
362
363static int mlxreg_fan_config(struct mlxreg_fan *fan,
364 struct mlxreg_core_platform_data *pdata)
365{
366 struct mlxreg_core_data *data = pdata->data;
367 bool configured = false;
368 int tacho_num = 0, i;
369
370 fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF;
371 fan->divider = MLXREG_FAN_TACHO_DIVIDER_DEF;
372 for (i = 0; i < pdata->counter; i++, data++) {
373 if (strnstr(data->label, "tacho", sizeof(data->label))) {
374 if (tacho_num == MLXREG_FAN_MAX_TACHO) {
375 dev_err(fan->dev, "too many tacho entries: %s\n",
376 data->label);
377 return -EINVAL;
378 }
379 fan->tacho[tacho_num].reg = data->reg;
380 fan->tacho[tacho_num].mask = data->mask;
381 fan->tacho[tacho_num++].connected = true;
382 } else if (strnstr(data->label, "pwm", sizeof(data->label))) {
383 if (fan->pwm.connected) {
384 dev_err(fan->dev, "duplicate pwm entry: %s\n",
385 data->label);
386 return -EINVAL;
387 }
388 fan->pwm.reg = data->reg;
389 fan->pwm.connected = true;
390 } else if (strnstr(data->label, "conf", sizeof(data->label))) {
391 if (configured) {
392 dev_err(fan->dev, "duplicate conf entry: %s\n",
393 data->label);
394 return -EINVAL;
395 }
396 /* Validate that conf parameters are not zeros. */
397 if (!data->mask || !data->bit) {
398 dev_err(fan->dev, "invalid conf entry params: %s\n",
399 data->label);
400 return -EINVAL;
401 }
402 fan->samples = data->mask;
403 fan->divider = data->bit;
404 configured = true;
405 } else {
406 dev_err(fan->dev, "invalid label: %s\n", data->label);
407 return -EINVAL;
408 }
409 }
410
411 /* Init cooling levels per PWM state. */
412 for (i = 0; i < MLXREG_FAN_SPEED_MIN_LEVEL; i++)
413 fan->cooling_levels[i] = MLXREG_FAN_SPEED_MIN_LEVEL;
414 for (i = MLXREG_FAN_SPEED_MIN_LEVEL; i <= MLXREG_FAN_MAX_STATE; i++)
415 fan->cooling_levels[i] = i;
416
417 return 0;
418}
419
420static int mlxreg_fan_probe(struct platform_device *pdev)
421{
422 struct mlxreg_core_platform_data *pdata;
423 struct mlxreg_fan *fan;
424 struct device *hwm;
425 int err;
426
427 pdata = dev_get_platdata(&pdev->dev);
428 if (!pdata) {
429 dev_err(&pdev->dev, "Failed to get platform data.\n");
430 return -EINVAL;
431 }
432
433 fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
434 if (!fan)
435 return -ENOMEM;
436
437 fan->dev = &pdev->dev;
438 fan->regmap = pdata->regmap;
439 platform_set_drvdata(pdev, fan);
440
441 err = mlxreg_fan_config(fan, pdata);
442 if (err)
443 return err;
444
445 hwm = devm_hwmon_device_register_with_info(&pdev->dev, "mlxreg_fan",
446 fan,
447 &mlxreg_fan_hwmon_chip_info,
448 NULL);
449 if (IS_ERR(hwm)) {
450 dev_err(&pdev->dev, "Failed to register hwmon device\n");
451 return PTR_ERR(hwm);
452 }
453
454 if (IS_REACHABLE(CONFIG_THERMAL)) {
455 fan->cdev = thermal_cooling_device_register("mlxreg_fan", fan,
456 &mlxreg_fan_cooling_ops);
457 if (IS_ERR(fan->cdev)) {
458 dev_err(&pdev->dev, "Failed to register cooling device\n");
459 return PTR_ERR(fan->cdev);
460 }
461 }
462
463 return 0;
464}
465
466static int mlxreg_fan_remove(struct platform_device *pdev)
467{
468 struct mlxreg_fan *fan = platform_get_drvdata(pdev);
469
470 if (IS_REACHABLE(CONFIG_THERMAL))
471 thermal_cooling_device_unregister(fan->cdev);
472
473 return 0;
474}
475
476static struct platform_driver mlxreg_fan_driver = {
477 .driver = {
478 .name = "mlxreg-fan",
479 },
480 .probe = mlxreg_fan_probe,
481 .remove = mlxreg_fan_remove,
482};
483
484module_platform_driver(mlxreg_fan_driver);
485
486MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
487MODULE_DESCRIPTION("Mellanox FAN driver");
488MODULE_LICENSE("GPL");
489MODULE_ALIAS("platform:mlxreg-fan");