blob: a4b1b8b828c5b87d74901af870a9d315fc2e98d5 [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
Jean Delvare4e7d99e2010-08-14 21:08:56 +02004 * Copyright (C) 2006, 2008 Jean Delvare <khali@linux-fr.org>
Jean Delvareba224e22006-12-12 18:18:29 +01005 *
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>
H Hartley Sweeten6055fae2009-09-15 17:18:13 +020036#include <linux/io.h>
Jean Delvareba224e22006-12-12 18:18:29 +010037
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
Jean Delvare4e7d99e2010-08-14 21:08:56 +020062struct pc87427_sio_data {
63 u8 has_fanin;
64};
65
Jean Delvareba224e22006-12-12 18:18:29 +010066/*
67 * Super-I/O registers and operations
68 */
69
70#define SIOREG_LDSEL 0x07 /* Logical device select */
71#define SIOREG_DEVID 0x20 /* Device ID */
Jean Delvare4e7d99e2010-08-14 21:08:56 +020072#define SIOREG_CF2 0x22 /* Configuration 2 */
73#define SIOREG_CF3 0x23 /* Configuration 3 */
74#define SIOREG_CF4 0x24 /* Configuration 4 */
75#define SIOREG_CFB 0x2B /* Configuration B */
76#define SIOREG_CFD 0x2D /* Configuration D */
Jean Delvareba224e22006-12-12 18:18:29 +010077#define SIOREG_ACT 0x30 /* Device activation */
78#define SIOREG_MAP 0x50 /* I/O or memory mapping */
79#define SIOREG_IOBASE 0x60 /* I/O base address */
80
81static const u8 logdev[2] = { 0x09, 0x14 };
82static const char *logdev_str[2] = { DRVNAME " FMC", DRVNAME " HMC" };
83#define LD_FAN 0
84#define LD_IN 1
85#define LD_TEMP 1
86
87static inline void superio_outb(int sioaddr, int reg, int val)
88{
89 outb(reg, sioaddr);
90 outb(val, sioaddr + 1);
91}
92
93static inline int superio_inb(int sioaddr, int reg)
94{
95 outb(reg, sioaddr);
96 return inb(sioaddr + 1);
97}
98
99static inline void superio_exit(int sioaddr)
100{
101 outb(0x02, sioaddr);
102 outb(0x02, sioaddr + 1);
103}
104
105/*
106 * Logical devices
107 */
108
109#define REGION_LENGTH 32
110#define PC87427_REG_BANK 0x0f
111#define BANK_FM(nr) (nr)
112#define BANK_FT(nr) (0x08 + (nr))
113#define BANK_FC(nr) (0x10 + (nr) * 2)
114
115/*
116 * I/O access functions
117 */
118
119/* ldi is the logical device index */
120static inline int pc87427_read8(struct pc87427_data *data, u8 ldi, u8 reg)
121{
122 return inb(data->address[ldi] + reg);
123}
124
125/* Must be called with data->lock held, except during init */
126static inline int pc87427_read8_bank(struct pc87427_data *data, u8 ldi,
127 u8 bank, u8 reg)
128{
129 outb(bank, data->address[ldi] + PC87427_REG_BANK);
130 return inb(data->address[ldi] + reg);
131}
132
133/* Must be called with data->lock held, except during init */
134static inline void pc87427_write8_bank(struct pc87427_data *data, u8 ldi,
135 u8 bank, u8 reg, u8 value)
136{
137 outb(bank, data->address[ldi] + PC87427_REG_BANK);
138 outb(value, data->address[ldi] + reg);
139}
140
141/*
142 * Fan registers and conversions
143 */
144
145/* fan data registers are 16-bit wide */
146#define PC87427_REG_FAN 0x12
147#define PC87427_REG_FAN_MIN 0x14
148#define PC87427_REG_FAN_STATUS 0x10
149
150#define FAN_STATUS_STALL (1 << 3)
151#define FAN_STATUS_LOSPD (1 << 1)
152#define FAN_STATUS_MONEN (1 << 0)
153
154/* Dedicated function to read all registers related to a given fan input.
155 This saves us quite a few locks and bank selections.
156 Must be called with data->lock held.
157 nr is from 0 to 7 */
158static void pc87427_readall_fan(struct pc87427_data *data, u8 nr)
159{
160 int iobase = data->address[LD_FAN];
161
162 outb(BANK_FM(nr), iobase + PC87427_REG_BANK);
163 data->fan[nr] = inw(iobase + PC87427_REG_FAN);
164 data->fan_min[nr] = inw(iobase + PC87427_REG_FAN_MIN);
165 data->fan_status[nr] = inb(iobase + PC87427_REG_FAN_STATUS);
166 /* Clear fan alarm bits */
167 outb(data->fan_status[nr], iobase + PC87427_REG_FAN_STATUS);
168}
169
170/* The 2 LSB of fan speed registers are used for something different.
171 The actual 2 LSB of the measurements are not available. */
172static inline unsigned long fan_from_reg(u16 reg)
173{
174 reg &= 0xfffc;
175 if (reg == 0x0000 || reg == 0xfffc)
176 return 0;
177 return 5400000UL / reg;
178}
179
180/* The 2 LSB of the fan speed limit registers are not significant. */
181static inline u16 fan_to_reg(unsigned long val)
182{
183 if (val < 83UL)
184 return 0xffff;
185 if (val >= 1350000UL)
186 return 0x0004;
187 return ((1350000UL + val / 2) / val) << 2;
188}
189
190/*
191 * Data interface
192 */
193
194static struct pc87427_data *pc87427_update_device(struct device *dev)
195{
196 struct pc87427_data *data = dev_get_drvdata(dev);
197 int i;
198
199 mutex_lock(&data->lock);
200 if (!time_after(jiffies, data->last_updated + HZ)
201 && data->last_updated)
202 goto done;
203
204 /* Fans */
205 for (i = 0; i < 8; i++) {
206 if (!(data->fan_enabled & (1 << i)))
207 continue;
208 pc87427_readall_fan(data, i);
209 }
210 data->last_updated = jiffies;
211
212done:
213 mutex_unlock(&data->lock);
214 return data;
215}
216
217static ssize_t show_fan_input(struct device *dev, struct device_attribute
218 *devattr, char *buf)
219{
220 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
221 struct pc87427_data *data = pc87427_update_device(dev);
222 int nr = attr->index;
223
224 return sprintf(buf, "%lu\n", fan_from_reg(data->fan[nr]));
225}
226
227static ssize_t show_fan_min(struct device *dev, struct device_attribute
228 *devattr, char *buf)
229{
230 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
231 struct pc87427_data *data = pc87427_update_device(dev);
232 int nr = attr->index;
233
234 return sprintf(buf, "%lu\n", fan_from_reg(data->fan_min[nr]));
235}
236
237static ssize_t show_fan_alarm(struct device *dev, struct device_attribute
238 *devattr, char *buf)
239{
240 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
241 struct pc87427_data *data = pc87427_update_device(dev);
242 int nr = attr->index;
243
244 return sprintf(buf, "%d\n", !!(data->fan_status[nr]
245 & FAN_STATUS_LOSPD));
246}
247
248static ssize_t show_fan_fault(struct device *dev, struct device_attribute
249 *devattr, char *buf)
250{
251 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
252 struct pc87427_data *data = pc87427_update_device(dev);
253 int nr = attr->index;
254
255 return sprintf(buf, "%d\n", !!(data->fan_status[nr]
256 & FAN_STATUS_STALL));
257}
258
259static ssize_t set_fan_min(struct device *dev, struct device_attribute
260 *devattr, const char *buf, size_t count)
261{
262 struct pc87427_data *data = dev_get_drvdata(dev);
263 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
264 int nr = attr->index;
265 unsigned long val = simple_strtoul(buf, NULL, 10);
266 int iobase = data->address[LD_FAN];
267
268 mutex_lock(&data->lock);
269 outb(BANK_FM(nr), iobase + PC87427_REG_BANK);
270 /* The low speed limit registers are read-only while monitoring
271 is enabled, so we have to disable monitoring, then change the
272 limit, and finally enable monitoring again. */
273 outb(0, iobase + PC87427_REG_FAN_STATUS);
274 data->fan_min[nr] = fan_to_reg(val);
275 outw(data->fan_min[nr], iobase + PC87427_REG_FAN_MIN);
276 outb(FAN_STATUS_MONEN, iobase + PC87427_REG_FAN_STATUS);
277 mutex_unlock(&data->lock);
278
279 return count;
280}
281
282static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input, NULL, 0);
283static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input, NULL, 1);
284static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan_input, NULL, 2);
285static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan_input, NULL, 3);
286static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan_input, NULL, 4);
287static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan_input, NULL, 5);
288static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan_input, NULL, 6);
289static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan_input, NULL, 7);
290
291static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO,
292 show_fan_min, set_fan_min, 0);
293static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO,
294 show_fan_min, set_fan_min, 1);
295static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO,
296 show_fan_min, set_fan_min, 2);
297static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO,
298 show_fan_min, set_fan_min, 3);
299static SENSOR_DEVICE_ATTR(fan5_min, S_IWUSR | S_IRUGO,
300 show_fan_min, set_fan_min, 4);
301static SENSOR_DEVICE_ATTR(fan6_min, S_IWUSR | S_IRUGO,
302 show_fan_min, set_fan_min, 5);
303static SENSOR_DEVICE_ATTR(fan7_min, S_IWUSR | S_IRUGO,
304 show_fan_min, set_fan_min, 6);
305static SENSOR_DEVICE_ATTR(fan8_min, S_IWUSR | S_IRUGO,
306 show_fan_min, set_fan_min, 7);
307
308static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0);
309static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1);
310static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 2);
311static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3);
312static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_fan_alarm, NULL, 4);
313static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_fan_alarm, NULL, 5);
314static SENSOR_DEVICE_ATTR(fan7_alarm, S_IRUGO, show_fan_alarm, NULL, 6);
315static SENSOR_DEVICE_ATTR(fan8_alarm, S_IRUGO, show_fan_alarm, NULL, 7);
316
317static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, NULL, 0);
318static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_fan_fault, NULL, 1);
319static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, show_fan_fault, NULL, 2);
320static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, show_fan_fault, NULL, 3);
321static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, show_fan_fault, NULL, 4);
322static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, show_fan_fault, NULL, 5);
323static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, show_fan_fault, NULL, 6);
324static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, show_fan_fault, NULL, 7);
325
326static struct attribute *pc87427_attributes_fan[8][5] = {
327 {
328 &sensor_dev_attr_fan1_input.dev_attr.attr,
329 &sensor_dev_attr_fan1_min.dev_attr.attr,
330 &sensor_dev_attr_fan1_alarm.dev_attr.attr,
331 &sensor_dev_attr_fan1_fault.dev_attr.attr,
332 NULL
333 }, {
334 &sensor_dev_attr_fan2_input.dev_attr.attr,
335 &sensor_dev_attr_fan2_min.dev_attr.attr,
336 &sensor_dev_attr_fan2_alarm.dev_attr.attr,
337 &sensor_dev_attr_fan2_fault.dev_attr.attr,
338 NULL
339 }, {
340 &sensor_dev_attr_fan3_input.dev_attr.attr,
341 &sensor_dev_attr_fan3_min.dev_attr.attr,
342 &sensor_dev_attr_fan3_alarm.dev_attr.attr,
343 &sensor_dev_attr_fan3_fault.dev_attr.attr,
344 NULL
345 }, {
346 &sensor_dev_attr_fan4_input.dev_attr.attr,
347 &sensor_dev_attr_fan4_min.dev_attr.attr,
348 &sensor_dev_attr_fan4_alarm.dev_attr.attr,
349 &sensor_dev_attr_fan4_fault.dev_attr.attr,
350 NULL
351 }, {
352 &sensor_dev_attr_fan5_input.dev_attr.attr,
353 &sensor_dev_attr_fan5_min.dev_attr.attr,
354 &sensor_dev_attr_fan5_alarm.dev_attr.attr,
355 &sensor_dev_attr_fan5_fault.dev_attr.attr,
356 NULL
357 }, {
358 &sensor_dev_attr_fan6_input.dev_attr.attr,
359 &sensor_dev_attr_fan6_min.dev_attr.attr,
360 &sensor_dev_attr_fan6_alarm.dev_attr.attr,
361 &sensor_dev_attr_fan6_fault.dev_attr.attr,
362 NULL
363 }, {
364 &sensor_dev_attr_fan7_input.dev_attr.attr,
365 &sensor_dev_attr_fan7_min.dev_attr.attr,
366 &sensor_dev_attr_fan7_alarm.dev_attr.attr,
367 &sensor_dev_attr_fan7_fault.dev_attr.attr,
368 NULL
369 }, {
370 &sensor_dev_attr_fan8_input.dev_attr.attr,
371 &sensor_dev_attr_fan8_min.dev_attr.attr,
372 &sensor_dev_attr_fan8_alarm.dev_attr.attr,
373 &sensor_dev_attr_fan8_fault.dev_attr.attr,
374 NULL
375 }
376};
377
378static const struct attribute_group pc87427_group_fan[8] = {
379 { .attrs = pc87427_attributes_fan[0] },
380 { .attrs = pc87427_attributes_fan[1] },
381 { .attrs = pc87427_attributes_fan[2] },
382 { .attrs = pc87427_attributes_fan[3] },
383 { .attrs = pc87427_attributes_fan[4] },
384 { .attrs = pc87427_attributes_fan[5] },
385 { .attrs = pc87427_attributes_fan[6] },
386 { .attrs = pc87427_attributes_fan[7] },
387};
388
389static ssize_t show_name(struct device *dev, struct device_attribute
390 *devattr, char *buf)
391{
392 struct pc87427_data *data = dev_get_drvdata(dev);
393
394 return sprintf(buf, "%s\n", data->name);
395}
396static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
397
398
399/*
400 * Device detection, attach and detach
401 */
402
403static void __devinit pc87427_init_device(struct device *dev)
404{
Jean Delvare4e7d99e2010-08-14 21:08:56 +0200405 struct pc87427_sio_data *sio_data = dev->platform_data;
Jean Delvareba224e22006-12-12 18:18:29 +0100406 struct pc87427_data *data = dev_get_drvdata(dev);
407 int i;
408 u8 reg;
409
410 /* The FMC module should be ready */
411 reg = pc87427_read8(data, LD_FAN, PC87427_REG_BANK);
412 if (!(reg & 0x80))
413 dev_warn(dev, "FMC module not ready!\n");
414
415 /* Check which fans are enabled */
416 for (i = 0; i < 8; i++) {
Jean Delvare4e7d99e2010-08-14 21:08:56 +0200417 if (!(sio_data->has_fanin & (1 << i))) /* Not wired */
418 continue;
Jean Delvareba224e22006-12-12 18:18:29 +0100419 reg = pc87427_read8_bank(data, LD_FAN, BANK_FM(i),
420 PC87427_REG_FAN_STATUS);
421 if (reg & FAN_STATUS_MONEN)
422 data->fan_enabled |= (1 << i);
423 }
424
425 if (!data->fan_enabled) {
Jean Delvare4e7d99e2010-08-14 21:08:56 +0200426 dev_dbg(dev, "Enabling monitoring of all fans\n");
427 for (i = 0; i < 8; i++) {
428 if (!(sio_data->has_fanin & (1 << i))) /* Not wired */
429 continue;
Jean Delvareba224e22006-12-12 18:18:29 +0100430 pc87427_write8_bank(data, LD_FAN, BANK_FM(i),
431 PC87427_REG_FAN_STATUS,
432 FAN_STATUS_MONEN);
Jean Delvare4e7d99e2010-08-14 21:08:56 +0200433 }
434 data->fan_enabled = sio_data->has_fanin;
Jean Delvareba224e22006-12-12 18:18:29 +0100435 }
436}
437
438static int __devinit pc87427_probe(struct platform_device *pdev)
439{
440 struct pc87427_data *data;
441 struct resource *res;
442 int i, err;
443
444 if (!(data = kzalloc(sizeof(struct pc87427_data), GFP_KERNEL))) {
445 err = -ENOMEM;
446 printk(KERN_ERR DRVNAME ": Out of memory\n");
447 goto exit;
448 }
449
450 /* This will need to be revisited when we add support for
451 temperature and voltage monitoring. */
452 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Julia Lawall86855b02009-09-15 17:18:13 +0200453 if (!request_region(res->start, resource_size(res), DRVNAME)) {
Jean Delvarece7ee4e2007-05-08 17:21:59 +0200454 err = -EBUSY;
455 dev_err(&pdev->dev, "Failed to request region 0x%lx-0x%lx\n",
456 (unsigned long)res->start, (unsigned long)res->end);
457 goto exit_kfree;
458 }
Jean Delvareba224e22006-12-12 18:18:29 +0100459 data->address[0] = res->start;
460
461 mutex_init(&data->lock);
462 data->name = "pc87427";
463 platform_set_drvdata(pdev, data);
464 pc87427_init_device(&pdev->dev);
465
466 /* Register sysfs hooks */
467 if ((err = device_create_file(&pdev->dev, &dev_attr_name)))
Jean Delvarece7ee4e2007-05-08 17:21:59 +0200468 goto exit_release_region;
Jean Delvareba224e22006-12-12 18:18:29 +0100469 for (i = 0; i < 8; i++) {
470 if (!(data->fan_enabled & (1 << i)))
471 continue;
472 if ((err = sysfs_create_group(&pdev->dev.kobj,
473 &pc87427_group_fan[i])))
474 goto exit_remove_files;
475 }
476
Tony Jones1beeffe2007-08-20 13:46:20 -0700477 data->hwmon_dev = hwmon_device_register(&pdev->dev);
478 if (IS_ERR(data->hwmon_dev)) {
479 err = PTR_ERR(data->hwmon_dev);
Jean Delvareba224e22006-12-12 18:18:29 +0100480 dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
481 goto exit_remove_files;
482 }
483
484 return 0;
485
486exit_remove_files:
487 for (i = 0; i < 8; i++) {
488 if (!(data->fan_enabled & (1 << i)))
489 continue;
490 sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]);
491 }
Jean Delvarece7ee4e2007-05-08 17:21:59 +0200492exit_release_region:
Julia Lawall86855b02009-09-15 17:18:13 +0200493 release_region(res->start, resource_size(res));
Jean Delvareba224e22006-12-12 18:18:29 +0100494exit_kfree:
495 platform_set_drvdata(pdev, NULL);
496 kfree(data);
497exit:
498 return err;
499}
500
501static int __devexit pc87427_remove(struct platform_device *pdev)
502{
503 struct pc87427_data *data = platform_get_drvdata(pdev);
Jean Delvarece7ee4e2007-05-08 17:21:59 +0200504 struct resource *res;
Jean Delvareba224e22006-12-12 18:18:29 +0100505 int i;
506
Tony Jones1beeffe2007-08-20 13:46:20 -0700507 hwmon_device_unregister(data->hwmon_dev);
Jean Delvareba224e22006-12-12 18:18:29 +0100508 device_remove_file(&pdev->dev, &dev_attr_name);
509 for (i = 0; i < 8; i++) {
510 if (!(data->fan_enabled & (1 << i)))
511 continue;
512 sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]);
513 }
Jean Delvare04a62172007-06-12 13:57:19 +0200514 platform_set_drvdata(pdev, NULL);
Jean Delvareba224e22006-12-12 18:18:29 +0100515 kfree(data);
516
Jean Delvarece7ee4e2007-05-08 17:21:59 +0200517 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Julia Lawall86855b02009-09-15 17:18:13 +0200518 release_region(res->start, resource_size(res));
Jean Delvarece7ee4e2007-05-08 17:21:59 +0200519
Jean Delvareba224e22006-12-12 18:18:29 +0100520 return 0;
521}
522
523
524static struct platform_driver pc87427_driver = {
525 .driver = {
526 .owner = THIS_MODULE,
527 .name = DRVNAME,
528 },
529 .probe = pc87427_probe,
530 .remove = __devexit_p(pc87427_remove),
531};
532
Jean Delvare4e7d99e2010-08-14 21:08:56 +0200533static int __init pc87427_device_add(unsigned short address,
534 const struct pc87427_sio_data *sio_data)
Jean Delvareba224e22006-12-12 18:18:29 +0100535{
536 struct resource res = {
537 .start = address,
538 .end = address + REGION_LENGTH - 1,
539 .name = logdev_str[0],
540 .flags = IORESOURCE_IO,
541 };
542 int err;
543
Jean Delvareb9acb642009-01-07 16:37:35 +0100544 err = acpi_check_resource_conflict(&res);
545 if (err)
546 goto exit;
547
Jean Delvareba224e22006-12-12 18:18:29 +0100548 pdev = platform_device_alloc(DRVNAME, address);
549 if (!pdev) {
550 err = -ENOMEM;
551 printk(KERN_ERR DRVNAME ": Device allocation failed\n");
552 goto exit;
553 }
554
555 err = platform_device_add_resources(pdev, &res, 1);
556 if (err) {
557 printk(KERN_ERR DRVNAME ": Device resource addition failed "
558 "(%d)\n", err);
559 goto exit_device_put;
560 }
561
Jean Delvare4e7d99e2010-08-14 21:08:56 +0200562 err = platform_device_add_data(pdev, sio_data,
563 sizeof(struct pc87427_sio_data));
564 if (err) {
565 printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
566 goto exit_device_put;
567 }
568
Jean Delvareba224e22006-12-12 18:18:29 +0100569 err = platform_device_add(pdev);
570 if (err) {
571 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
572 err);
573 goto exit_device_put;
574 }
575
576 return 0;
577
578exit_device_put:
579 platform_device_put(pdev);
580exit:
581 return err;
582}
583
Jean Delvare4e7d99e2010-08-14 21:08:56 +0200584static int __init pc87427_find(int sioaddr, unsigned short *address,
585 struct pc87427_sio_data *sio_data)
Jean Delvareba224e22006-12-12 18:18:29 +0100586{
587 u16 val;
Jean Delvare4e7d99e2010-08-14 21:08:56 +0200588 u8 cfg, cfg_b;
Jean Delvareba224e22006-12-12 18:18:29 +0100589 int i, err = 0;
590
591 /* Identify device */
Jean Delvare67b671b2007-12-06 23:13:42 +0100592 val = force_id ? force_id : superio_inb(sioaddr, SIOREG_DEVID);
Jean Delvareba224e22006-12-12 18:18:29 +0100593 if (val != 0xf2) { /* PC87427 */
594 err = -ENODEV;
595 goto exit;
596 }
597
598 for (i = 0; i < 2; i++) {
599 address[i] = 0;
600 /* Select logical device */
601 superio_outb(sioaddr, SIOREG_LDSEL, logdev[i]);
602
603 val = superio_inb(sioaddr, SIOREG_ACT);
604 if (!(val & 0x01)) {
605 printk(KERN_INFO DRVNAME ": Logical device 0x%02x "
606 "not activated\n", logdev[i]);
607 continue;
608 }
609
610 val = superio_inb(sioaddr, SIOREG_MAP);
611 if (val & 0x01) {
612 printk(KERN_WARNING DRVNAME ": Logical device 0x%02x "
613 "is memory-mapped, can't use\n", logdev[i]);
614 continue;
615 }
616
617 val = (superio_inb(sioaddr, SIOREG_IOBASE) << 8)
618 | superio_inb(sioaddr, SIOREG_IOBASE + 1);
619 if (!val) {
620 printk(KERN_INFO DRVNAME ": I/O base address not set "
621 "for logical device 0x%02x\n", logdev[i]);
622 continue;
623 }
624 address[i] = val;
625 }
626
Jean Delvare4e7d99e2010-08-14 21:08:56 +0200627 /* Check which fan inputs are wired */
628 sio_data->has_fanin = (1 << 2) | (1 << 3); /* FANIN2, FANIN3 */
629
630 cfg = superio_inb(sioaddr, SIOREG_CF2);
631 if (!(cfg & (1 << 3)))
632 sio_data->has_fanin |= (1 << 0); /* FANIN0 */
633 if (!(cfg & (1 << 2)))
634 sio_data->has_fanin |= (1 << 4); /* FANIN4 */
635
636 cfg = superio_inb(sioaddr, SIOREG_CFD);
637 if (!(cfg & (1 << 0)))
638 sio_data->has_fanin |= (1 << 1); /* FANIN1 */
639
640 cfg = superio_inb(sioaddr, SIOREG_CF4);
641 if (!(cfg & (1 << 0)))
642 sio_data->has_fanin |= (1 << 7); /* FANIN7 */
643 cfg_b = superio_inb(sioaddr, SIOREG_CFB);
644 if (!(cfg & (1 << 1)) && (cfg_b & (1 << 3)))
645 sio_data->has_fanin |= (1 << 5); /* FANIN5 */
646 cfg = superio_inb(sioaddr, SIOREG_CF3);
647 if ((cfg & (1 << 3)) && !(cfg_b & (1 << 5)))
648 sio_data->has_fanin |= (1 << 6); /* FANIN6 */
649
Jean Delvareba224e22006-12-12 18:18:29 +0100650exit:
651 superio_exit(sioaddr);
652 return err;
653}
654
655static int __init pc87427_init(void)
656{
657 int err;
658 unsigned short address[2];
Jean Delvare4e7d99e2010-08-14 21:08:56 +0200659 struct pc87427_sio_data sio_data;
Jean Delvareba224e22006-12-12 18:18:29 +0100660
Jean Delvare4e7d99e2010-08-14 21:08:56 +0200661 if (pc87427_find(0x2e, address, &sio_data)
662 && pc87427_find(0x4e, address, &sio_data))
Jean Delvareba224e22006-12-12 18:18:29 +0100663 return -ENODEV;
664
665 /* For now the driver only handles fans so we only care about the
666 first address. */
667 if (!address[0])
668 return -ENODEV;
669
670 err = platform_driver_register(&pc87427_driver);
671 if (err)
672 goto exit;
673
674 /* Sets global pdev as a side effect */
Jean Delvare4e7d99e2010-08-14 21:08:56 +0200675 err = pc87427_device_add(address[0], &sio_data);
Jean Delvareba224e22006-12-12 18:18:29 +0100676 if (err)
677 goto exit_driver;
678
679 return 0;
680
681exit_driver:
682 platform_driver_unregister(&pc87427_driver);
683exit:
684 return err;
685}
686
687static void __exit pc87427_exit(void)
688{
689 platform_device_unregister(pdev);
690 platform_driver_unregister(&pc87427_driver);
691}
692
693MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
694MODULE_DESCRIPTION("PC87427 hardware monitoring driver");
695MODULE_LICENSE("GPL");
696
697module_init(pc87427_init);
698module_exit(pc87427_exit);