aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/sch5636.c
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2011-07-25 15:46:09 -0400
committerJean Delvare <khali@endymion.delvare>2011-07-25 15:46:09 -0400
commit0772a640793986d66fd3f89c3cc677bba5d5f94f (patch)
tree2dae2f59bc55011f477aba2a3042ec146c69f97a /drivers/hwmon/sch5636.c
parent28ff2f7a742daba86ccd7021be7b27a4673b2797 (diff)
hwmon: New driver sch5636
This patch adds a new driver for SMSC SCH5636 Super I/O chips. The chips include an embedded microcontroller for hardware monitoring solutions, allowing motherboard manufacturers to create their own custom hwmon solution based upon the SCH5636. Currently the sch5636 driver only supports the Fujitsu Theseus SCH5636 based hwmon solution. The sch5636 driver runs a sanity check on loading to ensure it is dealing with a Fujitsu Theseus and not with another custom SCH5636 based hwmon solution. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/hwmon/sch5636.c')
-rw-r--r--drivers/hwmon/sch5636.c539
1 files changed, 539 insertions, 0 deletions
diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c
new file mode 100644
index 000000000000..244407aa79fc
--- /dev/null
+++ b/drivers/hwmon/sch5636.c
@@ -0,0 +1,539 @@
1/***************************************************************************
2 * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
18 ***************************************************************************/
19
20#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
21
22#include <linux/module.h>
23#include <linux/init.h>
24#include <linux/slab.h>
25#include <linux/jiffies.h>
26#include <linux/platform_device.h>
27#include <linux/hwmon.h>
28#include <linux/hwmon-sysfs.h>
29#include <linux/err.h>
30#include <linux/mutex.h>
31#include "sch56xx-common.h"
32
33#define DRVNAME "sch5636"
34#define DEVNAME "theseus" /* We only support one model for now */
35
36#define SCH5636_REG_FUJITSU_ID 0x780
37#define SCH5636_REG_FUJITSU_REV 0x783
38
39#define SCH5636_NO_INS 5
40#define SCH5636_NO_TEMPS 16
41#define SCH5636_NO_FANS 8
42
43static const u16 SCH5636_REG_IN_VAL[SCH5636_NO_INS] = {
44 0x22, 0x23, 0x24, 0x25, 0x189 };
45static const u16 SCH5636_REG_IN_FACTORS[SCH5636_NO_INS] = {
46 4400, 1500, 4000, 4400, 16000 };
47static const char * const SCH5636_IN_LABELS[SCH5636_NO_INS] = {
48 "3.3V", "VREF", "VBAT", "3.3AUX", "12V" };
49
50static const u16 SCH5636_REG_TEMP_VAL[SCH5636_NO_TEMPS] = {
51 0x2B, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x180, 0x181,
52 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C };
53#define SCH5636_REG_TEMP_CTRL(i) (0x790 + (i))
54#define SCH5636_TEMP_WORKING 0x01
55#define SCH5636_TEMP_ALARM 0x02
56#define SCH5636_TEMP_DEACTIVATED 0x80
57
58static const u16 SCH5636_REG_FAN_VAL[SCH5636_NO_FANS] = {
59 0x2C, 0x2E, 0x30, 0x32, 0x62, 0x64, 0x66, 0x68 };
60#define SCH5636_REG_FAN_CTRL(i) (0x880 + (i))
61/* FAULT in datasheet, but acts as an alarm */
62#define SCH5636_FAN_ALARM 0x04
63#define SCH5636_FAN_NOT_PRESENT 0x08
64#define SCH5636_FAN_DEACTIVATED 0x80
65
66
67struct sch5636_data {
68 unsigned short addr;
69 struct device *hwmon_dev;
70
71 struct mutex update_lock;
72 char valid; /* !=0 if following fields are valid */
73 unsigned long last_updated; /* In jiffies */
74 u8 in[SCH5636_NO_INS];
75 u8 temp_val[SCH5636_NO_TEMPS];
76 u8 temp_ctrl[SCH5636_NO_TEMPS];
77 u16 fan_val[SCH5636_NO_FANS];
78 u8 fan_ctrl[SCH5636_NO_FANS];
79};
80
81static struct sch5636_data *sch5636_update_device(struct device *dev)
82{
83 struct sch5636_data *data = dev_get_drvdata(dev);
84 struct sch5636_data *ret = data;
85 int i, val;
86
87 mutex_lock(&data->update_lock);
88
89 /* Cache the values for 1 second */
90 if (data->valid && !time_after(jiffies, data->last_updated + HZ))
91 goto abort;
92
93 for (i = 0; i < SCH5636_NO_INS; i++) {
94 val = sch56xx_read_virtual_reg(data->addr,
95 SCH5636_REG_IN_VAL[i]);
96 if (unlikely(val < 0)) {
97 ret = ERR_PTR(val);
98 goto abort;
99 }
100 data->in[i] = val;
101 }
102
103 for (i = 0; i < SCH5636_NO_TEMPS; i++) {
104 if (data->temp_ctrl[i] & SCH5636_TEMP_DEACTIVATED)
105 continue;
106
107 val = sch56xx_read_virtual_reg(data->addr,
108 SCH5636_REG_TEMP_VAL[i]);
109 if (unlikely(val < 0)) {
110 ret = ERR_PTR(val);
111 goto abort;
112 }
113 data->temp_val[i] = val;
114
115 val = sch56xx_read_virtual_reg(data->addr,
116 SCH5636_REG_TEMP_CTRL(i));
117 if (unlikely(val < 0)) {
118 ret = ERR_PTR(val);
119 goto abort;
120 }
121 data->temp_ctrl[i] = val;
122 /* Alarms need to be explicitly write-cleared */
123 if (val & SCH5636_TEMP_ALARM) {
124 sch56xx_write_virtual_reg(data->addr,
125 SCH5636_REG_TEMP_CTRL(i), val);
126 }
127 }
128
129 for (i = 0; i < SCH5636_NO_FANS; i++) {
130 if (data->fan_ctrl[i] & SCH5636_FAN_DEACTIVATED)
131 continue;
132
133 val = sch56xx_read_virtual_reg16(data->addr,
134 SCH5636_REG_FAN_VAL[i]);
135 if (unlikely(val < 0)) {
136 ret = ERR_PTR(val);
137 goto abort;
138 }
139 data->fan_val[i] = val;
140
141 val = sch56xx_read_virtual_reg(data->addr,
142 SCH5636_REG_FAN_CTRL(i));
143 if (unlikely(val < 0)) {
144 ret = ERR_PTR(val);
145 goto abort;
146 }
147 data->fan_ctrl[i] = val;
148 /* Alarms need to be explicitly write-cleared */
149 if (val & SCH5636_FAN_ALARM) {
150 sch56xx_write_virtual_reg(data->addr,
151 SCH5636_REG_FAN_CTRL(i), val);
152 }
153 }
154
155 data->last_updated = jiffies;
156 data->valid = 1;
157abort:
158 mutex_unlock(&data->update_lock);
159 return ret;
160}
161
162static int reg_to_rpm(u16 reg)
163{
164 if (reg == 0)
165 return -EIO;
166 if (reg == 0xffff)
167 return 0;
168
169 return 5400540 / reg;
170}
171
172static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
173 char *buf)
174{
175 return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME);
176}
177
178static ssize_t show_in_value(struct device *dev, struct device_attribute
179 *devattr, char *buf)
180{
181 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
182 struct sch5636_data *data = sch5636_update_device(dev);
183 int val;
184
185 if (IS_ERR(data))
186 return PTR_ERR(data);
187
188 val = DIV_ROUND_CLOSEST(
189 data->in[attr->index] * SCH5636_REG_IN_FACTORS[attr->index],
190 255);
191 return snprintf(buf, PAGE_SIZE, "%d\n", val);
192}
193
194static ssize_t show_in_label(struct device *dev, struct device_attribute
195 *devattr, char *buf)
196{
197 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
198
199 return snprintf(buf, PAGE_SIZE, "%s\n",
200 SCH5636_IN_LABELS[attr->index]);
201}
202
203static ssize_t show_temp_value(struct device *dev, struct device_attribute
204 *devattr, char *buf)
205{
206 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
207 struct sch5636_data *data = sch5636_update_device(dev);
208 int val;
209
210 if (IS_ERR(data))
211 return PTR_ERR(data);
212
213 val = (data->temp_val[attr->index] - 64) * 1000;
214 return snprintf(buf, PAGE_SIZE, "%d\n", val);
215}
216
217static ssize_t show_temp_fault(struct device *dev, struct device_attribute
218 *devattr, char *buf)
219{
220 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
221 struct sch5636_data *data = sch5636_update_device(dev);
222 int val;
223
224 if (IS_ERR(data))
225 return PTR_ERR(data);
226
227 val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_WORKING) ? 0 : 1;
228 return snprintf(buf, PAGE_SIZE, "%d\n", val);
229}
230
231static ssize_t show_temp_alarm(struct device *dev, struct device_attribute
232 *devattr, char *buf)
233{
234 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
235 struct sch5636_data *data = sch5636_update_device(dev);
236 int val;
237
238 if (IS_ERR(data))
239 return PTR_ERR(data);
240
241 val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_ALARM) ? 1 : 0;
242 return snprintf(buf, PAGE_SIZE, "%d\n", val);
243}
244
245static ssize_t show_fan_value(struct device *dev, struct device_attribute
246 *devattr, char *buf)
247{
248 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
249 struct sch5636_data *data = sch5636_update_device(dev);
250 int val;
251
252 if (IS_ERR(data))
253 return PTR_ERR(data);
254
255 val = reg_to_rpm(data->fan_val[attr->index]);
256 if (val < 0)
257 return val;
258
259 return snprintf(buf, PAGE_SIZE, "%d\n", val);
260}
261
262static ssize_t show_fan_fault(struct device *dev, struct device_attribute
263 *devattr, char *buf)
264{
265 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
266 struct sch5636_data *data = sch5636_update_device(dev);
267 int val;
268
269 if (IS_ERR(data))
270 return PTR_ERR(data);
271
272 val = (data->fan_ctrl[attr->index] & SCH5636_FAN_NOT_PRESENT) ? 1 : 0;
273 return snprintf(buf, PAGE_SIZE, "%d\n", val);
274}
275
276static ssize_t show_fan_alarm(struct device *dev, struct device_attribute
277 *devattr, char *buf)
278{
279 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
280 struct sch5636_data *data = sch5636_update_device(dev);
281 int val;
282
283 if (IS_ERR(data))
284 return PTR_ERR(data);
285
286 val = (data->fan_ctrl[attr->index] & SCH5636_FAN_ALARM) ? 1 : 0;
287 return snprintf(buf, PAGE_SIZE, "%d\n", val);
288}
289
290static struct sensor_device_attribute sch5636_attr[] = {
291 SENSOR_ATTR(name, 0444, show_name, NULL, 0),
292 SENSOR_ATTR(in0_input, 0444, show_in_value, NULL, 0),
293 SENSOR_ATTR(in0_label, 0444, show_in_label, NULL, 0),
294 SENSOR_ATTR(in1_input, 0444, show_in_value, NULL, 1),
295 SENSOR_ATTR(in1_label, 0444, show_in_label, NULL, 1),
296 SENSOR_ATTR(in2_input, 0444, show_in_value, NULL, 2),
297 SENSOR_ATTR(in2_label, 0444, show_in_label, NULL, 2),
298 SENSOR_ATTR(in3_input, 0444, show_in_value, NULL, 3),
299 SENSOR_ATTR(in3_label, 0444, show_in_label, NULL, 3),
300 SENSOR_ATTR(in4_input, 0444, show_in_value, NULL, 4),
301 SENSOR_ATTR(in4_label, 0444, show_in_label, NULL, 4),
302};
303
304static struct sensor_device_attribute sch5636_temp_attr[] = {
305 SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0),
306 SENSOR_ATTR(temp1_fault, 0444, show_temp_fault, NULL, 0),
307 SENSOR_ATTR(temp1_alarm, 0444, show_temp_alarm, NULL, 0),
308 SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1),
309 SENSOR_ATTR(temp2_fault, 0444, show_temp_fault, NULL, 1),
310 SENSOR_ATTR(temp2_alarm, 0444, show_temp_alarm, NULL, 1),
311 SENSOR_ATTR(temp3_input, 0444, show_temp_value, NULL, 2),
312 SENSOR_ATTR(temp3_fault, 0444, show_temp_fault, NULL, 2),
313 SENSOR_ATTR(temp3_alarm, 0444, show_temp_alarm, NULL, 2),
314 SENSOR_ATTR(temp4_input, 0444, show_temp_value, NULL, 3),
315 SENSOR_ATTR(temp4_fault, 0444, show_temp_fault, NULL, 3),
316 SENSOR_ATTR(temp4_alarm, 0444, show_temp_alarm, NULL, 3),
317 SENSOR_ATTR(temp5_input, 0444, show_temp_value, NULL, 4),
318 SENSOR_ATTR(temp5_fault, 0444, show_temp_fault, NULL, 4),
319 SENSOR_ATTR(temp5_alarm, 0444, show_temp_alarm, NULL, 4),
320 SENSOR_ATTR(temp6_input, 0444, show_temp_value, NULL, 5),
321 SENSOR_ATTR(temp6_fault, 0444, show_temp_fault, NULL, 5),
322 SENSOR_ATTR(temp6_alarm, 0444, show_temp_alarm, NULL, 5),
323 SENSOR_ATTR(temp7_input, 0444, show_temp_value, NULL, 6),
324 SENSOR_ATTR(temp7_fault, 0444, show_temp_fault, NULL, 6),
325 SENSOR_ATTR(temp7_alarm, 0444, show_temp_alarm, NULL, 6),
326 SENSOR_ATTR(temp8_input, 0444, show_temp_value, NULL, 7),
327 SENSOR_ATTR(temp8_fault, 0444, show_temp_fault, NULL, 7),
328 SENSOR_ATTR(temp8_alarm, 0444, show_temp_alarm, NULL, 7),
329 SENSOR_ATTR(temp9_input, 0444, show_temp_value, NULL, 8),
330 SENSOR_ATTR(temp9_fault, 0444, show_temp_fault, NULL, 8),
331 SENSOR_ATTR(temp9_alarm, 0444, show_temp_alarm, NULL, 8),
332 SENSOR_ATTR(temp10_input, 0444, show_temp_value, NULL, 9),
333 SENSOR_ATTR(temp10_fault, 0444, show_temp_fault, NULL, 9),
334 SENSOR_ATTR(temp10_alarm, 0444, show_temp_alarm, NULL, 9),
335 SENSOR_ATTR(temp11_input, 0444, show_temp_value, NULL, 10),
336 SENSOR_ATTR(temp11_fault, 0444, show_temp_fault, NULL, 10),
337 SENSOR_ATTR(temp11_alarm, 0444, show_temp_alarm, NULL, 10),
338 SENSOR_ATTR(temp12_input, 0444, show_temp_value, NULL, 11),
339 SENSOR_ATTR(temp12_fault, 0444, show_temp_fault, NULL, 11),
340 SENSOR_ATTR(temp12_alarm, 0444, show_temp_alarm, NULL, 11),
341 SENSOR_ATTR(temp13_input, 0444, show_temp_value, NULL, 12),
342 SENSOR_ATTR(temp13_fault, 0444, show_temp_fault, NULL, 12),
343 SENSOR_ATTR(temp13_alarm, 0444, show_temp_alarm, NULL, 12),
344 SENSOR_ATTR(temp14_input, 0444, show_temp_value, NULL, 13),
345 SENSOR_ATTR(temp14_fault, 0444, show_temp_fault, NULL, 13),
346 SENSOR_ATTR(temp14_alarm, 0444, show_temp_alarm, NULL, 13),
347 SENSOR_ATTR(temp15_input, 0444, show_temp_value, NULL, 14),
348 SENSOR_ATTR(temp15_fault, 0444, show_temp_fault, NULL, 14),
349 SENSOR_ATTR(temp15_alarm, 0444, show_temp_alarm, NULL, 14),
350 SENSOR_ATTR(temp16_input, 0444, show_temp_value, NULL, 15),
351 SENSOR_ATTR(temp16_fault, 0444, show_temp_fault, NULL, 15),
352 SENSOR_ATTR(temp16_alarm, 0444, show_temp_alarm, NULL, 15),
353};
354
355static struct sensor_device_attribute sch5636_fan_attr[] = {
356 SENSOR_ATTR(fan1_input, 0444, show_fan_value, NULL, 0),
357 SENSOR_ATTR(fan1_fault, 0444, show_fan_fault, NULL, 0),
358 SENSOR_ATTR(fan1_alarm, 0444, show_fan_alarm, NULL, 0),
359 SENSOR_ATTR(fan2_input, 0444, show_fan_value, NULL, 1),
360 SENSOR_ATTR(fan2_fault, 0444, show_fan_fault, NULL, 1),
361 SENSOR_ATTR(fan2_alarm, 0444, show_fan_alarm, NULL, 1),
362 SENSOR_ATTR(fan3_input, 0444, show_fan_value, NULL, 2),
363 SENSOR_ATTR(fan3_fault, 0444, show_fan_fault, NULL, 2),
364 SENSOR_ATTR(fan3_alarm, 0444, show_fan_alarm, NULL, 2),
365 SENSOR_ATTR(fan4_input, 0444, show_fan_value, NULL, 3),
366 SENSOR_ATTR(fan4_fault, 0444, show_fan_fault, NULL, 3),
367 SENSOR_ATTR(fan4_alarm, 0444, show_fan_alarm, NULL, 3),
368 SENSOR_ATTR(fan5_input, 0444, show_fan_value, NULL, 4),
369 SENSOR_ATTR(fan5_fault, 0444, show_fan_fault, NULL, 4),
370 SENSOR_ATTR(fan5_alarm, 0444, show_fan_alarm, NULL, 4),
371 SENSOR_ATTR(fan6_input, 0444, show_fan_value, NULL, 5),
372 SENSOR_ATTR(fan6_fault, 0444, show_fan_fault, NULL, 5),
373 SENSOR_ATTR(fan6_alarm, 0444, show_fan_alarm, NULL, 5),
374 SENSOR_ATTR(fan7_input, 0444, show_fan_value, NULL, 6),
375 SENSOR_ATTR(fan7_fault, 0444, show_fan_fault, NULL, 6),
376 SENSOR_ATTR(fan7_alarm, 0444, show_fan_alarm, NULL, 6),
377 SENSOR_ATTR(fan8_input, 0444, show_fan_value, NULL, 7),
378 SENSOR_ATTR(fan8_fault, 0444, show_fan_fault, NULL, 7),
379 SENSOR_ATTR(fan8_alarm, 0444, show_fan_alarm, NULL, 7),
380};
381
382static int sch5636_remove(struct platform_device *pdev)
383{
384 struct sch5636_data *data = platform_get_drvdata(pdev);
385 int i;
386
387 if (data->hwmon_dev)
388 hwmon_device_unregister(data->hwmon_dev);
389
390 for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++)
391 device_remove_file(&pdev->dev, &sch5636_attr[i].dev_attr);
392
393 for (i = 0; i < SCH5636_NO_TEMPS * 3; i++)
394 device_remove_file(&pdev->dev,
395 &sch5636_temp_attr[i].dev_attr);
396
397 for (i = 0; i < SCH5636_NO_FANS * 3; i++)
398 device_remove_file(&pdev->dev,
399 &sch5636_fan_attr[i].dev_attr);
400
401 platform_set_drvdata(pdev, NULL);
402 kfree(data);
403
404 return 0;
405}
406
407static int __devinit sch5636_probe(struct platform_device *pdev)
408{
409 struct sch5636_data *data;
410 int i, err, val, revision[2];
411 char id[4];
412
413 data = kzalloc(sizeof(struct sch5636_data), GFP_KERNEL);
414 if (!data)
415 return -ENOMEM;
416
417 data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
418 mutex_init(&data->update_lock);
419 platform_set_drvdata(pdev, data);
420
421 for (i = 0; i < 3; i++) {
422 val = sch56xx_read_virtual_reg(data->addr,
423 SCH5636_REG_FUJITSU_ID + i);
424 if (val < 0) {
425 pr_err("Could not read Fujitsu id byte at %#x\n",
426 SCH5636_REG_FUJITSU_ID + i);
427 err = val;
428 goto error;
429 }
430 id[i] = val;
431 }
432 id[i] = '\0';
433
434 if (strcmp(id, "THS")) {
435 pr_err("Unknown Fujitsu id: %02x%02x%02x\n",
436 id[0], id[1], id[2]);
437 err = -ENODEV;
438 goto error;
439 }
440
441 for (i = 0; i < 2; i++) {
442 val = sch56xx_read_virtual_reg(data->addr,
443 SCH5636_REG_FUJITSU_REV + i);
444 if (val < 0) {
445 err = val;
446 goto error;
447 }
448 revision[i] = val;
449 }
450 pr_info("Found %s chip at %#hx, revison: %d.%02d\n", DEVNAME,
451 data->addr, revision[0], revision[1]);
452
453 /* Read all temp + fan ctrl registers to determine which are active */
454 for (i = 0; i < SCH5636_NO_TEMPS; i++) {
455 val = sch56xx_read_virtual_reg(data->addr,
456 SCH5636_REG_TEMP_CTRL(i));
457 if (unlikely(val < 0)) {
458 err = val;
459 goto error;
460 }
461 data->temp_ctrl[i] = val;
462 }
463
464 for (i = 0; i < SCH5636_NO_FANS; i++) {
465 val = sch56xx_read_virtual_reg(data->addr,
466 SCH5636_REG_FAN_CTRL(i));
467 if (unlikely(val < 0)) {
468 err = val;
469 goto error;
470 }
471 data->fan_ctrl[i] = val;
472 }
473
474 for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++) {
475 err = device_create_file(&pdev->dev,
476 &sch5636_attr[i].dev_attr);
477 if (err)
478 goto error;
479 }
480
481 for (i = 0; i < (SCH5636_NO_TEMPS * 3); i++) {
482 if (data->temp_ctrl[i/3] & SCH5636_TEMP_DEACTIVATED)
483 continue;
484
485 err = device_create_file(&pdev->dev,
486 &sch5636_temp_attr[i].dev_attr);
487 if (err)
488 goto error;
489 }
490
491 for (i = 0; i < (SCH5636_NO_FANS * 3); i++) {
492 if (data->fan_ctrl[i/3] & SCH5636_FAN_DEACTIVATED)
493 continue;
494
495 err = device_create_file(&pdev->dev,
496 &sch5636_fan_attr[i].dev_attr);
497 if (err)
498 goto error;
499 }
500
501 data->hwmon_dev = hwmon_device_register(&pdev->dev);
502 if (IS_ERR(data->hwmon_dev)) {
503 err = PTR_ERR(data->hwmon_dev);
504 data->hwmon_dev = NULL;
505 goto error;
506 }
507
508 return 0;
509
510error:
511 sch5636_remove(pdev);
512 return err;
513}
514
515static struct platform_driver sch5636_driver = {
516 .driver = {
517 .owner = THIS_MODULE,
518 .name = DRVNAME,
519 },
520 .probe = sch5636_probe,
521 .remove = sch5636_remove,
522};
523
524static int __init sch5636_init(void)
525{
526 return platform_driver_register(&sch5636_driver);
527}
528
529static void __exit sch5636_exit(void)
530{
531 platform_driver_unregister(&sch5636_driver);
532}
533
534MODULE_DESCRIPTION("SMSC SCH5636 Hardware Monitoring Driver");
535MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
536MODULE_LICENSE("GPL");
537
538module_init(sch5636_init);
539module_exit(sch5636_exit);