xref: /linux/drivers/hwmon/mlxreg-fan.c (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
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		24
16 #define MLXREG_FAN_MAX_PWM		4
17 #define MLXREG_FAN_PWM_NOT_CONNECTED	0xff
18 #define MLXREG_FAN_MAX_STATE		10
19 #define MLXREG_FAN_MIN_DUTY		51	/* 20% */
20 #define MLXREG_FAN_MAX_DUTY		255	/* 100% */
21 #define MLXREG_FAN_SPEED_MIN_LEVEL		2	/* 20 percent */
22 #define MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF	44
23 #define MLXREG_FAN_TACHO_DIV_MIN		283
24 #define MLXREG_FAN_TACHO_DIV_DEF		(MLXREG_FAN_TACHO_DIV_MIN * 4)
25 #define MLXREG_FAN_TACHO_DIV_SCALE_MAX	64
26 /*
27  * FAN datasheet defines the formula for RPM calculations as RPM = 15/t-high.
28  * The logic in a programmable device measures the time t-high by sampling the
29  * tachometer every t-sample (with the default value 11.32 uS) and increment
30  * a counter (N) as long as the pulse has not change:
31  * RPM = 15 / (t-sample * (K + Regval)), where:
32  * Regval: is the value read from the programmable device register;
33  *  - 0xff - represents tachometer fault;
34  *  - 0xfe - represents tachometer minimum value , which is 4444 RPM;
35  *  - 0x00 - represents tachometer maximum value , which is 300000 RPM;
36  * K: is 44 and it represents the minimum allowed samples per pulse;
37  * N: is equal K + Regval;
38  * In order to calculate RPM from the register value the following formula is
39  * used: RPM = 15 / ((Regval + K) * 11.32) * 10^(-6)), which in  the
40  * default case is modified to:
41  * RPM = 15000000 * 100 / ((Regval + 44) * 1132);
42  * - for Regval 0x00, RPM will be 15000000 * 100 / (44 * 1132) = 30115;
43  * - for Regval 0xfe, RPM will be 15000000 * 100 / ((254 + 44) * 1132) = 4446;
44  * In common case the formula is modified to:
45  * RPM = 15000000 * 100 / ((Regval + samples) * divider).
46  */
47 #define MLXREG_FAN_GET_RPM(rval, d, s)	(DIV_ROUND_CLOSEST(15000000 * 100, \
48 					 ((rval) + (s)) * (d)))
49 #define MLXREG_FAN_GET_FAULT(val, mask) ((val) == (mask))
50 #define MLXREG_FAN_PWM_DUTY2STATE(duty)	(DIV_ROUND_CLOSEST((duty) *	\
51 					 MLXREG_FAN_MAX_STATE,		\
52 					 MLXREG_FAN_MAX_DUTY))
53 #define MLXREG_FAN_PWM_STATE2DUTY(stat)	(DIV_ROUND_CLOSEST((stat) *	\
54 					 MLXREG_FAN_MAX_DUTY,		\
55 					 MLXREG_FAN_MAX_STATE))
56 
57 struct mlxreg_fan;
58 
59 /*
60  * struct mlxreg_fan_tacho - tachometer data (internal use):
61  *
62  * @connected: indicates if tachometer is connected;
63  * @reg: register offset;
64  * @mask: fault mask;
65  * @prsnt: present register offset;
66  * @shift: tacho presence bit shift;
67  */
68 struct mlxreg_fan_tacho {
69 	bool connected;
70 	u32 reg;
71 	u32 mask;
72 	u32 prsnt;
73 	u32 shift;
74 };
75 
76 /*
77  * struct mlxreg_fan_pwm - PWM data (internal use):
78  *
79  * @fan: private data;
80  * @connected: indicates if PWM is connected;
81  * @reg: register offset;
82  * @cooling: cooling device levels;
83  * @last_hwmon_state: last cooling state set by hwmon subsystem;
84  * @last_thermal_state: last cooling state set by thermal subsystem;
85  * @cdev: cooling device;
86  */
87 struct mlxreg_fan_pwm {
88 	struct mlxreg_fan *fan;
89 	bool connected;
90 	u32 reg;
91 	unsigned long last_hwmon_state;
92 	unsigned long last_thermal_state;
93 	struct thermal_cooling_device *cdev;
94 };
95 
96 /*
97  * struct mlxreg_fan - private data (internal use):
98  *
99  * @dev: basic device;
100  * @regmap: register map of parent device;
101  * @tacho: tachometer data;
102  * @pwm: PWM data;
103  * @tachos_per_drwr - number of tachometers per drawer;
104  * @samples: minimum allowed samples per pulse;
105  * @divider: divider value for tachometer RPM calculation;
106  */
107 struct mlxreg_fan {
108 	struct device *dev;
109 	void *regmap;
110 	struct mlxreg_core_platform_data *pdata;
111 	struct mlxreg_fan_tacho tacho[MLXREG_FAN_MAX_TACHO];
112 	struct mlxreg_fan_pwm pwm[MLXREG_FAN_MAX_PWM];
113 	int tachos_per_drwr;
114 	int samples;
115 	int divider;
116 };
117 
118 static int _mlxreg_fan_set_cur_state(struct thermal_cooling_device *cdev,
119 				     unsigned long state, bool thermal);
120 
121 static int
122 mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
123 		int channel, long *val)
124 {
125 	struct mlxreg_fan *fan = dev_get_drvdata(dev);
126 	struct mlxreg_fan_tacho *tacho;
127 	struct mlxreg_fan_pwm *pwm;
128 	u32 regval;
129 	int err;
130 
131 	switch (type) {
132 	case hwmon_fan:
133 		tacho = &fan->tacho[channel];
134 		switch (attr) {
135 		case hwmon_fan_input:
136 			/*
137 			 * Check FAN presence: FAN related bit in presence register is one,
138 			 * if FAN is physically connected, zero - otherwise.
139 			 */
140 			if (tacho->prsnt && fan->tachos_per_drwr) {
141 				err = regmap_read(fan->regmap, tacho->prsnt, &regval);
142 				if (err)
143 					return err;
144 
145 				/*
146 				 * Map channel to presence bit - drawer can be equipped with
147 				 * one or few FANs, while presence is indicated per drawer.
148 				 * Shift channel value if necessary to align with register value.
149 				 */
150 				if (BIT(rol32(channel, tacho->shift) / fan->tachos_per_drwr) &
151 					regval) {
152 					/* FAN is not connected - return zero for FAN speed. */
153 					*val = 0;
154 					return 0;
155 				}
156 			}
157 
158 			err = regmap_read(fan->regmap, tacho->reg, &regval);
159 			if (err)
160 				return err;
161 
162 			if (MLXREG_FAN_GET_FAULT(regval, tacho->mask)) {
163 				/* FAN is broken - return zero for FAN speed. */
164 				*val = 0;
165 				return 0;
166 			}
167 
168 			*val = MLXREG_FAN_GET_RPM(regval, fan->divider,
169 						  fan->samples);
170 			break;
171 
172 		case hwmon_fan_fault:
173 			err = regmap_read(fan->regmap, tacho->reg, &regval);
174 			if (err)
175 				return err;
176 
177 			*val = MLXREG_FAN_GET_FAULT(regval, tacho->mask);
178 			break;
179 
180 		default:
181 			return -EOPNOTSUPP;
182 		}
183 		break;
184 
185 	case hwmon_pwm:
186 		pwm = &fan->pwm[channel];
187 		switch (attr) {
188 		case hwmon_pwm_input:
189 			err = regmap_read(fan->regmap, pwm->reg, &regval);
190 			if (err)
191 				return err;
192 
193 			*val = regval;
194 			break;
195 
196 		default:
197 			return -EOPNOTSUPP;
198 		}
199 		break;
200 
201 	default:
202 		return -EOPNOTSUPP;
203 	}
204 
205 	return 0;
206 }
207 
208 static int
209 mlxreg_fan_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
210 		 int channel, long val)
211 {
212 	struct mlxreg_fan *fan = dev_get_drvdata(dev);
213 	struct mlxreg_fan_pwm *pwm;
214 
215 	switch (type) {
216 	case hwmon_pwm:
217 		switch (attr) {
218 		case hwmon_pwm_input:
219 			if (val < MLXREG_FAN_MIN_DUTY ||
220 			    val > MLXREG_FAN_MAX_DUTY)
221 				return -EINVAL;
222 			pwm = &fan->pwm[channel];
223 			/* If thermal is configured - handle PWM limit setting. */
224 			if (IS_REACHABLE(CONFIG_THERMAL)) {
225 				pwm->last_hwmon_state = MLXREG_FAN_PWM_DUTY2STATE(val);
226 				/*
227 				 * Update PWM only in case requested state is not less than the
228 				 * last thermal state.
229 				 */
230 				if (pwm->last_hwmon_state >= pwm->last_thermal_state)
231 					return _mlxreg_fan_set_cur_state(pwm->cdev,
232 									 pwm->last_hwmon_state,
233 									 false);
234 				return 0;
235 			}
236 			return regmap_write(fan->regmap, pwm->reg, val);
237 		default:
238 			return -EOPNOTSUPP;
239 		}
240 		break;
241 
242 	default:
243 		return -EOPNOTSUPP;
244 	}
245 
246 	return -EOPNOTSUPP;
247 }
248 
249 static umode_t
250 mlxreg_fan_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
251 		      int channel)
252 {
253 	switch (type) {
254 	case hwmon_fan:
255 		if (!(((struct mlxreg_fan *)data)->tacho[channel].connected))
256 			return 0;
257 
258 		switch (attr) {
259 		case hwmon_fan_input:
260 		case hwmon_fan_fault:
261 			return 0444;
262 		default:
263 			break;
264 		}
265 		break;
266 
267 	case hwmon_pwm:
268 		if (!(((struct mlxreg_fan *)data)->pwm[channel].connected))
269 			return 0;
270 
271 		switch (attr) {
272 		case hwmon_pwm_input:
273 			return 0644;
274 		default:
275 			break;
276 		}
277 		break;
278 
279 	default:
280 		break;
281 	}
282 
283 	return 0;
284 }
285 
286 static char *mlxreg_fan_name[] = {
287 	"mlxreg_fan",
288 	"mlxreg_fan1",
289 	"mlxreg_fan2",
290 	"mlxreg_fan3",
291 };
292 
293 static const struct hwmon_channel_info * const mlxreg_fan_hwmon_info[] = {
294 	HWMON_CHANNEL_INFO(fan,
295 			   HWMON_F_INPUT | HWMON_F_FAULT,
296 			   HWMON_F_INPUT | HWMON_F_FAULT,
297 			   HWMON_F_INPUT | HWMON_F_FAULT,
298 			   HWMON_F_INPUT | HWMON_F_FAULT,
299 			   HWMON_F_INPUT | HWMON_F_FAULT,
300 			   HWMON_F_INPUT | HWMON_F_FAULT,
301 			   HWMON_F_INPUT | HWMON_F_FAULT,
302 			   HWMON_F_INPUT | HWMON_F_FAULT,
303 			   HWMON_F_INPUT | HWMON_F_FAULT,
304 			   HWMON_F_INPUT | HWMON_F_FAULT,
305 			   HWMON_F_INPUT | HWMON_F_FAULT,
306 			   HWMON_F_INPUT | HWMON_F_FAULT,
307 			   HWMON_F_INPUT | HWMON_F_FAULT,
308 			   HWMON_F_INPUT | HWMON_F_FAULT,
309 			   HWMON_F_INPUT | HWMON_F_FAULT,
310 			   HWMON_F_INPUT | HWMON_F_FAULT,
311 			   HWMON_F_INPUT | HWMON_F_FAULT,
312 			   HWMON_F_INPUT | HWMON_F_FAULT,
313 			   HWMON_F_INPUT | HWMON_F_FAULT,
314 			   HWMON_F_INPUT | HWMON_F_FAULT,
315 			   HWMON_F_INPUT | HWMON_F_FAULT,
316 			   HWMON_F_INPUT | HWMON_F_FAULT,
317 			   HWMON_F_INPUT | HWMON_F_FAULT,
318 			   HWMON_F_INPUT | HWMON_F_FAULT),
319 	HWMON_CHANNEL_INFO(pwm,
320 			   HWMON_PWM_INPUT,
321 			   HWMON_PWM_INPUT,
322 			   HWMON_PWM_INPUT,
323 			   HWMON_PWM_INPUT),
324 	NULL
325 };
326 
327 static const struct hwmon_ops mlxreg_fan_hwmon_hwmon_ops = {
328 	.is_visible = mlxreg_fan_is_visible,
329 	.read = mlxreg_fan_read,
330 	.write = mlxreg_fan_write,
331 };
332 
333 static const struct hwmon_chip_info mlxreg_fan_hwmon_chip_info = {
334 	.ops = &mlxreg_fan_hwmon_hwmon_ops,
335 	.info = mlxreg_fan_hwmon_info,
336 };
337 
338 static int mlxreg_fan_get_max_state(struct thermal_cooling_device *cdev,
339 				    unsigned long *state)
340 {
341 	*state = MLXREG_FAN_MAX_STATE;
342 	return 0;
343 }
344 
345 static int mlxreg_fan_get_cur_state(struct thermal_cooling_device *cdev,
346 				    unsigned long *state)
347 
348 {
349 	struct mlxreg_fan_pwm *pwm = cdev->devdata;
350 	struct mlxreg_fan *fan = pwm->fan;
351 	u32 regval;
352 	int err;
353 
354 	err = regmap_read(fan->regmap, pwm->reg, &regval);
355 	if (err) {
356 		dev_err(fan->dev, "Failed to query PWM duty\n");
357 		return err;
358 	}
359 
360 	*state = MLXREG_FAN_PWM_DUTY2STATE(regval);
361 
362 	return 0;
363 }
364 
365 static int _mlxreg_fan_set_cur_state(struct thermal_cooling_device *cdev,
366 				     unsigned long state, bool thermal)
367 {
368 	struct mlxreg_fan_pwm *pwm = cdev->devdata;
369 	struct mlxreg_fan *fan = pwm->fan;
370 	int err;
371 
372 	if (state > MLXREG_FAN_MAX_STATE)
373 		return -EINVAL;
374 
375 	/* Save thermal state. */
376 	if (thermal)
377 		pwm->last_thermal_state = state;
378 
379 	state = max_t(unsigned long, state, pwm->last_hwmon_state);
380 	err = regmap_write(fan->regmap, pwm->reg,
381 			   MLXREG_FAN_PWM_STATE2DUTY(state));
382 	if (err) {
383 		dev_err(fan->dev, "Failed to write PWM duty\n");
384 		return err;
385 	}
386 	return 0;
387 }
388 
389 static int mlxreg_fan_set_cur_state(struct thermal_cooling_device *cdev,
390 				    unsigned long state)
391 
392 {
393 	return _mlxreg_fan_set_cur_state(cdev, state, true);
394 }
395 
396 static const struct thermal_cooling_device_ops mlxreg_fan_cooling_ops = {
397 	.get_max_state	= mlxreg_fan_get_max_state,
398 	.get_cur_state	= mlxreg_fan_get_cur_state,
399 	.set_cur_state	= mlxreg_fan_set_cur_state,
400 };
401 
402 static int mlxreg_fan_connect_verify(struct mlxreg_fan *fan,
403 				     struct mlxreg_core_data *data)
404 {
405 	u32 regval;
406 	int err;
407 
408 	err = regmap_read(fan->regmap, data->capability, &regval);
409 	if (err) {
410 		dev_err(fan->dev, "Failed to query capability register 0x%08x\n",
411 			data->capability);
412 		return err;
413 	}
414 
415 	return data->slot ? (data->slot <= regval ? 1 : 0) : !!(regval & data->bit);
416 }
417 
418 static int mlxreg_pwm_connect_verify(struct mlxreg_fan *fan,
419 				     struct mlxreg_core_data *data)
420 {
421 	u32 regval;
422 	int err;
423 
424 	err = regmap_read(fan->regmap, data->reg, &regval);
425 	if (err) {
426 		dev_err(fan->dev, "Failed to query pwm register 0x%08x\n",
427 			data->reg);
428 		return err;
429 	}
430 
431 	return regval != MLXREG_FAN_PWM_NOT_CONNECTED;
432 }
433 
434 static int mlxreg_fan_speed_divider_get(struct mlxreg_fan *fan,
435 					struct mlxreg_core_data *data)
436 {
437 	u32 regval;
438 	int err;
439 
440 	err = regmap_read(fan->regmap, data->capability, &regval);
441 	if (err) {
442 		dev_err(fan->dev, "Failed to query capability register 0x%08x\n",
443 			data->capability);
444 		return err;
445 	}
446 
447 	/*
448 	 * Set divider value according to the capability register, in case it
449 	 * contains valid value. Otherwise use default value. The purpose of
450 	 * this validation is to protect against the old hardware, in which
451 	 * this register can return zero.
452 	 */
453 	if (regval > 0 && regval <= MLXREG_FAN_TACHO_DIV_SCALE_MAX)
454 		fan->divider = regval * MLXREG_FAN_TACHO_DIV_MIN;
455 
456 	return 0;
457 }
458 
459 static int mlxreg_fan_config(struct mlxreg_fan *fan,
460 			     struct mlxreg_core_platform_data *pdata)
461 {
462 	int tacho_num = 0, tacho_avail = 0, pwm_num = 0, i;
463 	struct mlxreg_core_data *data = pdata->data;
464 	bool configured = false;
465 	int err;
466 
467 	fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF;
468 	fan->divider = MLXREG_FAN_TACHO_DIV_DEF;
469 	for (i = 0; i < pdata->counter; i++, data++) {
470 		if (strnstr(data->label, "tacho", sizeof(data->label))) {
471 			if (tacho_num == MLXREG_FAN_MAX_TACHO) {
472 				dev_err(fan->dev, "too many tacho entries: %s\n",
473 					data->label);
474 				return -EINVAL;
475 			}
476 
477 			if (data->capability) {
478 				err = mlxreg_fan_connect_verify(fan, data);
479 				if (err < 0)
480 					return err;
481 				else if (!err) {
482 					tacho_num++;
483 					continue;
484 				}
485 			}
486 
487 			fan->tacho[tacho_num].reg = data->reg;
488 			fan->tacho[tacho_num].mask = data->mask;
489 			fan->tacho[tacho_num].prsnt = data->reg_prsnt;
490 			fan->tacho[tacho_num++].connected = true;
491 			tacho_avail++;
492 		} else if (strnstr(data->label, "pwm", sizeof(data->label))) {
493 			if (pwm_num == MLXREG_FAN_MAX_TACHO) {
494 				dev_err(fan->dev, "too many pwm entries: %s\n",
495 					data->label);
496 				return -EINVAL;
497 			}
498 
499 			/* Validate if more then one PWM is connected. */
500 			if (pwm_num) {
501 				err = mlxreg_pwm_connect_verify(fan, data);
502 				if (err < 0)
503 					return err;
504 				else if (!err)
505 					continue;
506 			}
507 
508 			fan->pwm[pwm_num].reg = data->reg;
509 			fan->pwm[pwm_num].connected = true;
510 			pwm_num++;
511 		} else if (strnstr(data->label, "conf", sizeof(data->label))) {
512 			if (configured) {
513 				dev_err(fan->dev, "duplicate conf entry: %s\n",
514 					data->label);
515 				return -EINVAL;
516 			}
517 			/* Validate that conf parameters are not zeros. */
518 			if (!data->mask && !data->bit && !data->capability) {
519 				dev_err(fan->dev, "invalid conf entry params: %s\n",
520 					data->label);
521 				return -EINVAL;
522 			}
523 			if (data->capability) {
524 				err = mlxreg_fan_speed_divider_get(fan, data);
525 				if (err)
526 					return err;
527 			} else {
528 				if (data->mask)
529 					fan->samples = data->mask;
530 				if (data->bit)
531 					fan->divider = data->bit;
532 			}
533 			configured = true;
534 		} else {
535 			dev_err(fan->dev, "invalid label: %s\n", data->label);
536 			return -EINVAL;
537 		}
538 	}
539 
540 	if (pdata->capability) {
541 		int drwr_avail;
542 		u32 regval;
543 
544 		/* Obtain the number of FAN drawers, supported by system. */
545 		err = regmap_read(fan->regmap, pdata->capability, &regval);
546 		if (err) {
547 			dev_err(fan->dev, "Failed to query capability register 0x%08x\n",
548 				pdata->capability);
549 			return err;
550 		}
551 
552 		/*
553 		 * The number of drawers could be specified in registers by counters for newer
554 		 * systems, or by bitmasks for older systems. In case the data is provided by
555 		 * counter, it is indicated through 'version' field.
556 		 */
557 		if (pdata->version)
558 			drwr_avail = regval;
559 		else
560 			drwr_avail = hweight32(regval);
561 		if (!tacho_avail || !drwr_avail || tacho_avail < drwr_avail) {
562 			dev_err(fan->dev, "Configuration is invalid: drawers num %d tachos num %d\n",
563 				drwr_avail, tacho_avail);
564 			return -EINVAL;
565 		}
566 
567 		/* Set the number of tachometers per one drawer. */
568 		fan->tachos_per_drwr = tacho_avail / drwr_avail;
569 	}
570 
571 	return 0;
572 }
573 
574 static int mlxreg_fan_cooling_config(struct device *dev, struct mlxreg_fan *fan)
575 {
576 	int i;
577 
578 	for (i = 0; i < MLXREG_FAN_MAX_PWM; i++) {
579 		struct mlxreg_fan_pwm *pwm = &fan->pwm[i];
580 
581 		if (!pwm->connected)
582 			continue;
583 		pwm->fan = fan;
584 		/* Set minimal PWM speed. */
585 		pwm->last_hwmon_state = MLXREG_FAN_PWM_DUTY2STATE(MLXREG_FAN_MIN_DUTY);
586 		pwm->cdev = devm_thermal_of_cooling_device_register(dev, NULL, mlxreg_fan_name[i],
587 								    pwm, &mlxreg_fan_cooling_ops);
588 		if (IS_ERR(pwm->cdev)) {
589 			dev_err(dev, "Failed to register cooling device\n");
590 			return PTR_ERR(pwm->cdev);
591 		}
592 	}
593 
594 	return 0;
595 }
596 
597 static int mlxreg_fan_probe(struct platform_device *pdev)
598 {
599 	struct mlxreg_core_platform_data *pdata;
600 	struct device *dev = &pdev->dev;
601 	struct mlxreg_fan *fan;
602 	struct device *hwm;
603 	int err;
604 
605 	pdata = dev_get_platdata(dev);
606 	if (!pdata) {
607 		dev_err(dev, "Failed to get platform data.\n");
608 		return -EINVAL;
609 	}
610 
611 	fan = devm_kzalloc(dev, sizeof(*fan), GFP_KERNEL);
612 	if (!fan)
613 		return -ENOMEM;
614 
615 	fan->dev = dev;
616 	fan->regmap = pdata->regmap;
617 
618 	err = mlxreg_fan_config(fan, pdata);
619 	if (err)
620 		return err;
621 
622 	hwm = devm_hwmon_device_register_with_info(dev, "mlxreg_fan",
623 						   fan,
624 						   &mlxreg_fan_hwmon_chip_info,
625 						   NULL);
626 	if (IS_ERR(hwm)) {
627 		dev_err(dev, "Failed to register hwmon device\n");
628 		return PTR_ERR(hwm);
629 	}
630 
631 	if (IS_REACHABLE(CONFIG_THERMAL))
632 		err = mlxreg_fan_cooling_config(dev, fan);
633 
634 	return err;
635 }
636 
637 static struct platform_driver mlxreg_fan_driver = {
638 	.driver = {
639 	    .name = "mlxreg-fan",
640 	},
641 	.probe = mlxreg_fan_probe,
642 };
643 
644 module_platform_driver(mlxreg_fan_driver);
645 
646 MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
647 MODULE_DESCRIPTION("Mellanox FAN driver");
648 MODULE_LICENSE("GPL");
649 MODULE_ALIAS("platform:mlxreg-fan");
650