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