blob: 3a8a0f7a7736dd48468ca3dfdd3cb839d046083e [file] [log] [blame]
Jean Delvareba224e22006-12-12 18:18:29 +01001/*
2 * pc87427.c - hardware monitoring driver for the
3 * National Semiconductor PC87427 Super-I/O chip
4 * Copyright (C) 2006 Jean Delvare <khali@linux-fr.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * Supports the following chips:
16 *
17 * Chip #vin #fan #pwm #temp devid
18 * PC87427 - 8 - - 0xF2
19 *
20 * This driver assumes that no more than one chip is present.
21 * Only fan inputs are supported so far, although the chip can do much more.
22 */
23
24#include <linux/module.h>
25#include <linux/init.h>
26#include <linux/slab.h>
27#include <linux/jiffies.h>
28#include <linux/platform_device.h>
29#include <linux/hwmon.h>
30#include <linux/hwmon-sysfs.h>
31#include <linux/err.h>
32#include <linux/mutex.h>
33#include <linux/sysfs.h>
Jean Delvarece7ee4e2007-05-08 17:21:59 +020034#include <linux/ioport.h>
Jean Delvareb9acb642009-01-07 16:37:35 +010035#include <linux/acpi.h>
Jean Delvareba224e22006-12-12 18:18:29 +010036#include <asm/io.h>
37
Jean Delvare67b671b2007-12-06 23:13:42 +010038static unsigned short force_id;
39module_param(force_id, ushort, 0);
40MODULE_PARM_DESC(force_id, "Override the detected device ID");
41
Jean Delvareba224e22006-12-12 18:18:29 +010042static struct platform_device *pdev;
43
44#define DRVNAME "pc87427"
45
46/* The lock mutex protects both the I/O accesses (needed because the
47 device is using banked registers) and the register cache (needed to keep
48 the data in the registers and the cache in sync at any time). */
49struct pc87427_data {
Tony Jones1beeffe2007-08-20 13:46:20 -070050 struct device *hwmon_dev;
Jean Delvareba224e22006-12-12 18:18:29 +010051 struct mutex lock;
52 int address[2];
53 const char *name;
54
55 unsigned long last_updated; /* in jiffies */
56 u8 fan_enabled; /* bit vector */
57 u16 fan[8]; /* register values */
58 u16 fan_min[8]; /* register values */
59 u8 fan_status[8]; /* register values */
60};
61
62/*
63 * Super-I/O registers and operations
64 */
65
66#define SIOREG_LDSEL 0x07 /* Logical device select */
67#define SIOREG_DEVID 0x20 /* Device ID */
68#define SIOREG_ACT 0x30 /* Device activation */
69#define SIOREG_MAP 0x50 /* I/O or memory mapping */
70#define SIOREG_IOBASE 0x60 /* I/O base address */
71
72static const u8 logdev[2] = { 0x09, 0x14 };
73static const char *logdev_str[2] = { DRVNAME " FMC", DRVNAME " HMC" };
74#define LD_FAN 0
75#define LD_IN 1
76#define LD_TEMP 1
77
78static inline void superio_outb(int sioaddr, int reg, int val)
79{
80 outb(reg, sioaddr);
81 outb(val, sioaddr + 1);
82}
83
84static inline int superio_inb(int sioaddr, int reg)
85{
86 outb(reg, sioaddr);
87 return inb(sioaddr + 1);
88}
89
90static inline void superio_exit(int sioaddr)
91{
92 outb(0x02, sioaddr);
93 outb(0x02, sioaddr + 1);
94}
95
96/*
97 * Logical devices
98 */
99
100#define REGION_LENGTH 32
101#define PC87427_REG_BANK 0x0f
102#define BANK_FM(nr) (nr)
103#define BANK_FT(nr) (0x08 + (nr))
104#define BANK_FC(nr) (0x10 + (nr) * 2)
105
106/*
107 * I/O access functions
108 */
109
110/* ldi is the logical device index */
111static inline int pc87427_read8(struct pc87427_data *data, u8 ldi, u8 reg)
112{
113 return inb(data->address[ldi] + reg);
114}
115
116/* Must be called with data->lock held, except during init */
117static inline int pc87427_read8_bank(struct pc87427_data *data, u8 ldi,
118 u8 bank, u8 reg)
119{
120 outb(bank, data->address[ldi] + PC87427_REG_BANK);
121 return inb(data->address[ldi] + reg);
122}
123
124/* Must be called with data->lock held, except during init */
125static inline void pc87427_write8_bank(struct pc87427_data *data, u8 ldi,
126 u8 bank, u8 reg, u8 value)
127{
128 outb(bank, data->address[ldi] + PC87427_REG_BANK);
129 outb(value, data->address[ldi] + reg);
130}
131
132/*
133 * Fan registers and conversions
134 */
135
136/* fan data registers are 16-bit wide */
137#define PC87427_REG_FAN 0x12
138#define PC87427_REG_FAN_MIN 0x14
139#define PC87427_REG_FAN_STATUS 0x10
140
141#define FAN_STATUS_STALL (1 << 3)
142#define FAN_STATUS_LOSPD (1 << 1)
143#define FAN_STATUS_MONEN (1 << 0)
144
145/* Dedicated function to read all registers related to a given fan input.
146 This saves us quite a few locks and bank selections.
147 Must be called with data->lock held.
148 nr is from 0 to 7 */
149static void pc87427_readall_fan(struct pc87427_data *data, u8 nr)
150{
151 int iobase = data->address[LD_FAN];
152
153 outb(BANK_FM(nr), iobase + PC87427_REG_BANK);
154 data->fan[nr] = inw(iobase + PC87427_REG_FAN);
155 data->fan_min[nr] = inw(iobase + PC87427_REG_FAN_MIN);
156 data->fan_status[nr] = inb(iobase + PC87427_REG_FAN_STATUS);
157 /* Clear fan alarm bits */
158 outb(data->fan_status[nr], iobase + PC87427_REG_FAN_STATUS);
159}
160
161/* The 2 LSB of fan speed registers are used for something different.
162 The actual 2 LSB of the measurements are not available. */
163static inline unsigned long fan_from_reg(u16 reg)
164{
165 reg &= 0xfffc;
166 if (reg == 0x0000 || reg == 0xfffc)
167 return 0;
168 return 5400000UL / reg;
169}
170
171/* The 2 LSB of the fan speed limit registers are not significant. */
172static inline u16 fan_to_reg(unsigned long val)
173{
174 if (val < 83UL)
175 return 0xffff;
176 if (val >= 1350000UL)
177 return 0x0004;
178 return ((1350000UL + val / 2) / val) << 2;
179}
180
181/*
182 * Data interface
183 */
184
185static struct pc87427_data *pc87427_update_device(struct device *dev)
186{
187 struct pc87427_data *data = dev_get_drvdata(dev);
188 int i;
189
190 mutex_lock(&data->lock);
191 if (!time_after(jiffies, data->last_updated + HZ)
192 && data->last_updated)
193 goto done;
194
195 /* Fans */
196 for (i = 0; i < 8; i++) {
197 if (!(data->fan_enabled & (1 << i)))
198 continue;
199 pc87427_readall_fan(data, i);
200 }
201 data->last_updated = jiffies;
202
203done:
204 mutex_unlock(&data->lock);
205 return data;
206}
207
208static ssize_t show_fan_input(struct device *dev, struct device_attribute
209 *devattr, char *buf)
210{
211 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
212 struct pc87427_data *data = pc87427_update_device(dev);
213 int nr = attr->index;
214
215 return sprintf(buf, "%lu\n", fan_from_reg(data->fan[nr]));
216}
217
218static ssize_t show_fan_min(struct device *dev, struct device_attribute
219 *devattr, char *buf)
220{
221 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
222 struct pc87427_data *data = pc87427_update_device(dev);
223 int nr = attr->index;
224
225 return sprintf(buf, "%lu\n", fan_from_reg(data->fan_min[nr]));
226}
227
228static ssize_t show_fan_alarm(struct device *dev, struct device_attribute
229 *devattr, char *buf)
230{
231 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
232 struct pc87427_data *data = pc87427_update_device(dev);
233 int nr = attr->index;
234
235 return sprintf(buf, "%d\n", !!(data->fan_status[nr]
236 & FAN_STATUS_LOSPD));
237}
238
239static ssize_t show_fan_fault(struct device *dev, struct device_attribute
240 *devattr, char *buf)
241{
242 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
243 struct pc87427_data *data = pc87427_update_device(dev);
244 int nr = attr->index;
245
246 return sprintf(buf, "%d\n", !!(data->fan_status[nr]
247 & FAN_STATUS_STALL));
248}
249
250static ssize_t set_fan_min(struct device *dev, struct device_attribute
251 *devattr, const char *buf, size_t count)
252{
253 struct pc87427_data *data = dev_get_drvdata(dev);
254 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
255 int nr = attr->index;
256 unsigned long val = simple_strtoul(buf, NULL, 10);
257 int iobase = data->address[LD_FAN];
258
259 mutex_lock(&data->lock);
260 outb(BANK_FM(nr), iobase + PC87427_REG_BANK);
261 /* The low speed limit registers are read-only while monitoring
262 is enabled, so we have to disable monitoring, then change the
263 limit, and finally enable monitoring again. */
264 outb(0, iobase + PC87427_REG_FAN_STATUS);
265 data->fan_min[nr] = fan_to_reg(val);
266 outw(data->fan_min[nr], iobase + PC87427_REG_FAN_MIN);
267 outb(FAN_STATUS_MONEN, iobase + PC87427_REG_FAN_STATUS);
268 mutex_unlock(&data->lock);
269
270 return count;
271}
272
273static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input, NULL, 0);
274static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input, NULL, 1);
275static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan_input, NULL, 2);
276static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan_input, NULL, 3);
277static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan_input, NULL, 4);
278static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan_input, NULL, 5);
279static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan_input, NULL, 6);
280static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan_input, NULL, 7);
281
282static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO,
283 show_fan_min, set_fan_min, 0);
284static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO,
285 show_fan_min, set_fan_min, 1);
286static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO,
287 show_fan_min, set_fan_min, 2);
288static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO,
289 show_fan_min, set_fan_min, 3);
290static SENSOR_DEVICE_ATTR(fan5_min, S_IWUSR | S_IRUGO,
291 show_fan_min, set_fan_min, 4);
292static SENSOR_DEVICE_ATTR(fan6_min, S_IWUSR | S_IRUGO,
293 show_fan_min, set_fan_min, 5);
294static SENSOR_DEVICE_ATTR(fan7_min, S_IWUSR | S_IRUGO,
295 show_fan_min, set_fan_min, 6);
296static SENSOR_DEVICE_ATTR(fan8_min, S_IWUSR | S_IRUGO,
297 show_fan_min, set_fan_min, 7);
298
299static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0);
300static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1);
301static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 2);
302static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3);
303static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_fan_alarm, NULL, 4);
304static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_fan_alarm, NULL, 5);
305static SENSOR_DEVICE_ATTR(fan7_alarm, S_IRUGO, show_fan_alarm, NULL, 6);
306static SENSOR_DEVICE_ATTR(fan8_alarm, S_IRUGO, show_fan_alarm, NULL, 7);
307
308static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, NULL, 0);
309static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_fan_fault, NULL, 1);
310static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, show_fan_fault, NULL, 2);
311static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, show_fan_fault, NULL, 3);
312static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, show_fan_fault, NULL, 4);
313static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, show_fan_fault, NULL, 5);
314static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, show_fan_fault, NULL, 6);
315static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, show_fan_fault, NULL, 7);
316
317static struct attribute *pc87427_attributes_fan[8][5] = {
318 {
319 &sensor_dev_attr_fan1_input.dev_attr.attr,
320 &sensor_dev_attr_fan1_min.dev_attr.attr,
321 &sensor_dev_attr_fan1_alarm.dev_attr.attr,
322 &sensor_dev_attr_fan1_fault.dev_attr.attr,
323 NULL
324 }, {
325 &sensor_dev_attr_fan2_input.dev_attr.attr,
326 &sensor_dev_attr_fan2_min.dev_attr.attr,
327 &sensor_dev_attr_fan2_alarm.dev_attr.attr,
328 &sensor_dev_attr_fan2_fault.dev_attr.attr,
329 NULL
330 }, {
331 &sensor_dev_attr_fan3_input.dev_attr.attr,
332 &sensor_dev_attr_fan3_min.dev_attr.attr,
333 &sensor_dev_attr_fan3_alarm.dev_attr.attr,
334 &sensor_dev_attr_fan3_fault.dev_attr.attr,
335 NULL
336 }, {
337 &sensor_dev_attr_fan4_input.dev_attr.attr,
338 &sensor_dev_attr_fan4_min.dev_attr.attr,
339 &sensor_dev_attr_fan4_alarm.dev_attr.attr,
340 &sensor_dev_attr_fan4_fault.dev_attr.attr,
341 NULL
342 }, {
343 &sensor_dev_attr_fan5_input.dev_attr.attr,
344 &sensor_dev_attr_fan5_min.dev_attr.attr,
345 &sensor_dev_attr_fan5_alarm.dev_attr.attr,
346 &sensor_dev_attr_fan5_fault.dev_attr.attr,
347 NULL
348 }, {
349 &sensor_dev_attr_fan6_input.dev_attr.attr,
350 &sensor_dev_attr_fan6_min.dev_attr.attr,
351 &sensor_dev_attr_fan6_alarm.dev_attr.attr,
352 &sensor_dev_attr_fan6_fault.dev_attr.attr,
353 NULL
354 }, {
355 &sensor_dev_attr_fan7_input.dev_attr.attr,
356 &sensor_dev_attr_fan7_min.dev_attr.attr,
357 &sensor_dev_attr_fan7_alarm.dev_attr.attr,
358 &sensor_dev_attr_fan7_fault.dev_attr.attr,
359 NULL
360 }, {
361 &sensor_dev_attr_fan8_input.dev_attr.attr,
362 &sensor_dev_attr_fan8_min.dev_attr.attr,
363 &sensor_dev_attr_fan8_alarm.dev_attr.attr,
364 &sensor_dev_attr_fan8_fault.dev_attr.attr,
365 NULL
366 }
367};
368
369static const struct attribute_group pc87427_group_fan[8] = {
370 { .attrs = pc87427_attributes_fan[0] },
371 { .attrs = pc87427_attributes_fan[1] },
372 { .attrs = pc87427_attributes_fan[2] },
373 { .attrs = pc87427_attributes_fan[3] },
374 { .attrs = pc87427_attributes_fan[4] },
375 { .attrs = pc87427_attributes_fan[5] },
376 { .attrs = pc87427_attributes_fan[6] },
377 { .attrs = pc87427_attributes_fan[7] },
378};
379
380static ssize_t show_name(struct device *dev, struct device_attribute
381 *devattr, char *buf)
382{
383 struct pc87427_data *data = dev_get_drvdata(dev);
384
385 return sprintf(buf, "%s\n", data->name);
386}
387static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
388
389
390/*
391 * Device detection, attach and detach
392 */
393
394static void __devinit pc87427_init_device(struct device *dev)
395{
396 struct pc87427_data *data = dev_get_drvdata(dev);
397 int i;
398 u8 reg;
399
400 /* The FMC module should be ready */
401 reg = pc87427_read8(data, LD_FAN, PC87427_REG_BANK);
402 if (!(reg & 0x80))
403 dev_warn(dev, "FMC module not ready!\n");
404
405 /* Check which fans are enabled */
406 for (i = 0; i < 8; i++) {
407 reg = pc87427_read8_bank(data, LD_FAN, BANK_FM(i),
408 PC87427_REG_FAN_STATUS);
409 if (reg & FAN_STATUS_MONEN)
410 data->fan_enabled |= (1 << i);
411 }
412
413 if (!data->fan_enabled) {
414 dev_dbg(dev, "Enabling all fan inputs\n");
415 for (i = 0; i < 8; i++)
416 pc87427_write8_bank(data, LD_FAN, BANK_FM(i),
417 PC87427_REG_FAN_STATUS,
418 FAN_STATUS_MONEN);
419 data->fan_enabled = 0xff;
420 }
421}
422
423static int __devinit pc87427_probe(struct platform_device *pdev)
424{
425 struct pc87427_data *data;
426 struct resource *res;
427 int i, err;
428
429 if (!(data = kzalloc(sizeof(struct pc87427_data), GFP_KERNEL))) {
430 err = -ENOMEM;
431 printk(KERN_ERR DRVNAME ": Out of memory\n");
432 goto exit;
433 }
434
435 /* This will need to be revisited when we add support for
436 temperature and voltage monitoring. */
437 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Jean Delvarece7ee4e2007-05-08 17:21:59 +0200438 if (!request_region(res->start, res->end - res->start + 1, DRVNAME)) {
439 err = -EBUSY;
440 dev_err(&pdev->dev, "Failed to request region 0x%lx-0x%lx\n",
441 (unsigned long)res->start, (unsigned long)res->end);
442 goto exit_kfree;
443 }
Jean Delvareba224e22006-12-12 18:18:29 +0100444 data->address[0] = res->start;
445
446 mutex_init(&data->lock);
447 data->name = "pc87427";
448 platform_set_drvdata(pdev, data);
449 pc87427_init_device(&pdev->dev);
450
451 /* Register sysfs hooks */
452 if ((err = device_create_file(&pdev->dev, &dev_attr_name)))
Jean Delvarece7ee4e2007-05-08 17:21:59 +0200453 goto exit_release_region;
Jean Delvareba224e22006-12-12 18:18:29 +0100454 for (i = 0; i < 8; i++) {
455 if (!(data->fan_enabled & (1 << i)))
456 continue;
457 if ((err = sysfs_create_group(&pdev->dev.kobj,
458 &pc87427_group_fan[i])))
459 goto exit_remove_files;
460 }
461
Tony Jones1beeffe2007-08-20 13:46:20 -0700462 data->hwmon_dev = hwmon_device_register(&pdev->dev);
463 if (IS_ERR(data->hwmon_dev)) {
464 err = PTR_ERR(data->hwmon_dev);
Jean Delvareba224e22006-12-12 18:18:29 +0100465 dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
466 goto exit_remove_files;
467 }
468
469 return 0;
470
471exit_remove_files:
472 for (i = 0; i < 8; i++) {
473 if (!(data->fan_enabled & (1 << i)))
474 continue;
475 sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]);
476 }
Jean Delvarece7ee4e2007-05-08 17:21:59 +0200477exit_release_region:
478 release_region(res->start, res->end - res->start + 1);
Jean Delvareba224e22006-12-12 18:18:29 +0100479exit_kfree:
480 platform_set_drvdata(pdev, NULL);
481 kfree(data);
482exit:
483 return err;
484}
485
486static int __devexit pc87427_remove(struct platform_device *pdev)
487{
488 struct pc87427_data *data = platform_get_drvdata(pdev);
Jean Delvarece7ee4e2007-05-08 17:21:59 +0200489 struct resource *res;
Jean Delvareba224e22006-12-12 18:18:29 +0100490 int i;
491
Tony Jones1beeffe2007-08-20 13:46:20 -0700492 hwmon_device_unregister(data->hwmon_dev);
Jean Delvareba224e22006-12-12 18:18:29 +0100493 device_remove_file(&pdev->dev, &dev_attr_name);
494 for (i = 0; i < 8; i++) {
495 if (!(data->fan_enabled & (1 << i)))
496 continue;
497 sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]);
498 }
Jean Delvare04a62172007-06-12 13:57:19 +0200499 platform_set_drvdata(pdev, NULL);
Jean Delvareba224e22006-12-12 18:18:29 +0100500 kfree(data);
501
Jean Delvarece7ee4e2007-05-08 17:21:59 +0200502 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
503 release_region(res->start, res->end - res->start + 1);
504
Jean Delvareba224e22006-12-12 18:18:29 +0100505 return 0;
506}
507
508
509static struct platform_driver pc87427_driver = {
510 .driver = {
511 .owner = THIS_MODULE,
512 .name = DRVNAME,
513 },
514 .probe = pc87427_probe,
515 .remove = __devexit_p(pc87427_remove),
516};
517
518static int __init pc87427_device_add(unsigned short address)
519{
520 struct resource res = {
521 .start = address,
522 .end = address + REGION_LENGTH - 1,
523 .name = logdev_str[0],
524 .flags = IORESOURCE_IO,
525 };
526 int err;
527
Jean Delvareb9acb642009-01-07 16:37:35 +0100528 err = acpi_check_resource_conflict(&res);
529 if (err)
530 goto exit;
531
Jean Delvareba224e22006-12-12 18:18:29 +0100532 pdev = platform_device_alloc(DRVNAME, address);
533 if (!pdev) {
534 err = -ENOMEM;
535 printk(KERN_ERR DRVNAME ": Device allocation failed\n");
536 goto exit;
537 }
538
539 err = platform_device_add_resources(pdev, &res, 1);
540 if (err) {
541 printk(KERN_ERR DRVNAME ": Device resource addition failed "
542 "(%d)\n", err);
543 goto exit_device_put;
544 }
545
546 err = platform_device_add(pdev);
547 if (err) {
548 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
549 err);
550 goto exit_device_put;
551 }
552
553 return 0;
554
555exit_device_put:
556 platform_device_put(pdev);
557exit:
558 return err;
559}
560
561static int __init pc87427_find(int sioaddr, unsigned short *address)
562{
563 u16 val;
564 int i, err = 0;
565
566 /* Identify device */
Jean Delvare67b671b2007-12-06 23:13:42 +0100567 val = force_id ? force_id : superio_inb(sioaddr, SIOREG_DEVID);
Jean Delvareba224e22006-12-12 18:18:29 +0100568 if (val != 0xf2) { /* PC87427 */
569 err = -ENODEV;
570 goto exit;
571 }
572
573 for (i = 0; i < 2; i++) {
574 address[i] = 0;
575 /* Select logical device */
576 superio_outb(sioaddr, SIOREG_LDSEL, logdev[i]);
577
578 val = superio_inb(sioaddr, SIOREG_ACT);
579 if (!(val & 0x01)) {
580 printk(KERN_INFO DRVNAME ": Logical device 0x%02x "
581 "not activated\n", logdev[i]);
582 continue;
583 }
584
585 val = superio_inb(sioaddr, SIOREG_MAP);
586 if (val & 0x01) {
587 printk(KERN_WARNING DRVNAME ": Logical device 0x%02x "
588 "is memory-mapped, can't use\n", logdev[i]);
589 continue;
590 }
591
592 val = (superio_inb(sioaddr, SIOREG_IOBASE) << 8)
593 | superio_inb(sioaddr, SIOREG_IOBASE + 1);
594 if (!val) {
595 printk(KERN_INFO DRVNAME ": I/O base address not set "
596 "for logical device 0x%02x\n", logdev[i]);
597 continue;
598 }
599 address[i] = val;
600 }
601
602exit:
603 superio_exit(sioaddr);
604 return err;
605}
606
607static int __init pc87427_init(void)
608{
609 int err;
610 unsigned short address[2];
611
612 if (pc87427_find(0x2e, address)
613 && pc87427_find(0x4e, address))
614 return -ENODEV;
615
616 /* For now the driver only handles fans so we only care about the
617 first address. */
618 if (!address[0])
619 return -ENODEV;
620
621 err = platform_driver_register(&pc87427_driver);
622 if (err)
623 goto exit;
624
625 /* Sets global pdev as a side effect */
626 err = pc87427_device_add(address[0]);
627 if (err)
628 goto exit_driver;
629
630 return 0;
631
632exit_driver:
633 platform_driver_unregister(&pc87427_driver);
634exit:
635 return err;
636}
637
638static void __exit pc87427_exit(void)
639{
640 platform_device_unregister(pdev);
641 platform_driver_unregister(&pc87427_driver);
642}
643
644MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
645MODULE_DESCRIPTION("PC87427 hardware monitoring driver");
646MODULE_LICENSE("GPL");
647
648module_init(pc87427_init);
649module_exit(pc87427_exit);