blob: 0d7f0c4d06bb5ef0e0e705a1a6c1d9a9026c83d3 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 smsc47m1.c - Part of lm_sensors, Linux kernel modules
3 for hardware monitoring
4
Jean Delvare60917802006-10-08 22:00:44 +02005 Supports the SMSC LPC47B27x, LPC47M10x, LPC47M112, LPC47M13x,
Jean Delvare8eccbb62007-05-08 17:21:59 +02006 LPC47M14x, LPC47M15x, LPC47M192, LPC47M292 and LPC47M997
7 Super-I/O chips.
Linus Torvalds1da177e2005-04-16 15:20:36 -07008
9 Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
Jean Delvare8eccbb62007-05-08 17:21:59 +020010 Copyright (C) 2004-2007 Jean Delvare <khali@linux-fr.org>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 Ported to Linux 2.6 by Gabriele Gorla <gorlik@yahoo.com>
12 and Jean Delvare
13
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27*/
28
29#include <linux/module.h>
30#include <linux/slab.h>
31#include <linux/ioport.h>
32#include <linux/jiffies.h>
Jean Delvare51f2cca2007-05-08 17:22:00 +020033#include <linux/platform_device.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040034#include <linux/hwmon.h>
Jean Delvaree84cfbc2007-05-08 17:22:00 +020035#include <linux/hwmon-sysfs.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040036#include <linux/err.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/init.h>
Ingo Molnar9a61bf62006-01-18 23:19:26 +010038#include <linux/mutex.h>
Jean Delvarece8c6ce12006-09-24 21:25:12 +020039#include <linux/sysfs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <asm/io.h>
41
Jean Delvare67b671b2007-12-06 23:13:42 +010042static unsigned short force_id;
43module_param(force_id, ushort, 0);
44MODULE_PARM_DESC(force_id, "Override the detected device ID");
45
Jean Delvare51f2cca2007-05-08 17:22:00 +020046static struct platform_device *pdev;
47
48#define DRVNAME "smsc47m1"
Jean Delvare8eccbb62007-05-08 17:21:59 +020049enum chips { smsc47m1, smsc47m2 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
51/* Super-I/0 registers and commands */
52
53#define REG 0x2e /* The register to read/write */
54#define VAL 0x2f /* The value to read/write */
55
56static inline void
57superio_outb(int reg, int val)
58{
59 outb(reg, REG);
60 outb(val, VAL);
61}
62
63static inline int
64superio_inb(int reg)
65{
66 outb(reg, REG);
67 return inb(VAL);
68}
69
70/* logical device for fans is 0x0A */
71#define superio_select() superio_outb(0x07, 0x0A)
72
73static inline void
74superio_enter(void)
75{
76 outb(0x55, REG);
77}
78
79static inline void
80superio_exit(void)
81{
82 outb(0xAA, REG);
83}
84
85#define SUPERIO_REG_ACT 0x30
86#define SUPERIO_REG_BASE 0x60
87#define SUPERIO_REG_DEVID 0x20
88
89/* Logical device registers */
90
91#define SMSC_EXTENT 0x80
92
93/* nr is 0 or 1 in the macros below */
94#define SMSC47M1_REG_ALARM 0x04
95#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr))
96#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr))
Linus Torvalds1da177e2005-04-16 15:20:36 -070097#define SMSC47M1_REG_FANDIV 0x58
Jean Delvare8eccbb62007-05-08 17:21:59 +020098
99static const u8 SMSC47M1_REG_FAN[3] = { 0x59, 0x5a, 0x6b };
100static const u8 SMSC47M1_REG_FAN_PRELOAD[3] = { 0x5b, 0x5c, 0x6c };
101static const u8 SMSC47M1_REG_PWM[3] = { 0x56, 0x57, 0x69 };
102
103#define SMSC47M2_REG_ALARM6 0x09
104#define SMSC47M2_REG_TPIN1 0x38
105#define SMSC47M2_REG_TPIN2 0x37
106#define SMSC47M2_REG_TPIN3 0x2d
107#define SMSC47M2_REG_PPIN3 0x2c
108#define SMSC47M2_REG_FANDIV3 0x6a
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109
110#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \
111 983040/((192-(reg))*(div)))
112#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \
113 983040/(((reg)-(preload))*(div)))
114#define DIV_FROM_REG(reg) (1 << (reg))
115#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1)
116#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01)
117#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E)
118
119struct smsc47m1_data {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200120 unsigned short addr;
121 const char *name;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200122 enum chips type;
Tony Jones1beeffe2007-08-20 13:46:20 -0700123 struct device *hwmon_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100125 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 unsigned long last_updated; /* In jiffies */
127
Jean Delvare8eccbb62007-05-08 17:21:59 +0200128 u8 fan[3]; /* Register value */
129 u8 fan_preload[3]; /* Register value */
130 u8 fan_div[3]; /* Register encoding, shifted right */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 u8 alarms; /* Register encoding */
Jean Delvare8eccbb62007-05-08 17:21:59 +0200132 u8 pwm[3]; /* Register value (bit 0 is disable) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133};
134
Jean Delvare51f2cca2007-05-08 17:22:00 +0200135struct smsc47m1_sio_data {
136 enum chips type;
137};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
Jean Delvare51f2cca2007-05-08 17:22:00 +0200139
140static int smsc47m1_probe(struct platform_device *pdev);
Jean Delvared0546122007-07-22 12:09:48 +0200141static int __devexit smsc47m1_remove(struct platform_device *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
143 int init);
144
Jean Delvare51f2cca2007-05-08 17:22:00 +0200145static inline int smsc47m1_read_value(struct smsc47m1_data *data, u8 reg)
Jean Delvare94e183f2007-05-08 17:21:59 +0200146{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200147 return inb_p(data->addr + reg);
Jean Delvare94e183f2007-05-08 17:21:59 +0200148}
149
Jean Delvare51f2cca2007-05-08 17:22:00 +0200150static inline void smsc47m1_write_value(struct smsc47m1_data *data, u8 reg,
Jean Delvare94e183f2007-05-08 17:21:59 +0200151 u8 value)
152{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200153 outb_p(value, data->addr + reg);
Jean Delvare94e183f2007-05-08 17:21:59 +0200154}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155
Jean Delvare51f2cca2007-05-08 17:22:00 +0200156static struct platform_driver smsc47m1_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100157 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200158 .owner = THIS_MODULE,
Jean Delvare51f2cca2007-05-08 17:22:00 +0200159 .name = DRVNAME,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100160 },
Jean Delvare51f2cca2007-05-08 17:22:00 +0200161 .probe = smsc47m1_probe,
162 .remove = __devexit_p(smsc47m1_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163};
164
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200165static ssize_t get_fan(struct device *dev, struct device_attribute
166 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200168 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200170 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 /* This chip (stupidly) stops monitoring fan speed if PWM is
172 enabled and duty cycle is 0%. This is fine if the monitoring
173 and control concern the same fan, but troublesome if they are
174 not (which could as well happen). */
175 int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
176 FAN_FROM_REG(data->fan[nr],
177 DIV_FROM_REG(data->fan_div[nr]),
178 data->fan_preload[nr]);
179 return sprintf(buf, "%d\n", rpm);
180}
181
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200182static ssize_t get_fan_min(struct device *dev, struct device_attribute
183 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200185 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200187 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 int rpm = MIN_FROM_REG(data->fan_preload[nr],
189 DIV_FROM_REG(data->fan_div[nr]));
190 return sprintf(buf, "%d\n", rpm);
191}
192
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200193static ssize_t get_fan_div(struct device *dev, struct device_attribute
194 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200196 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200198 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199}
200
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200201static ssize_t get_pwm(struct device *dev, struct device_attribute
202 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200204 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200206 return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207}
208
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200209static ssize_t get_pwm_en(struct device *dev, struct device_attribute
210 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200212 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200214 return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215}
216
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200217static ssize_t get_alarms(struct device *dev, struct device_attribute
218 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219{
220 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
221 return sprintf(buf, "%d\n", data->alarms);
222}
223
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200224static ssize_t set_fan_min(struct device *dev, struct device_attribute
225 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200227 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200228 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200229 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 long rpmdiv, val = simple_strtol(buf, NULL, 10);
231
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100232 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
234
235 if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100236 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 return -EINVAL;
238 }
239
240 data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200241 smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100243 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
245 return count;
246}
247
248/* Note: we save and restore the fan minimum here, because its value is
249 determined in part by the fan clock divider. This follows the principle
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200250 of least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 because the divider changed. */
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200252static ssize_t set_fan_div(struct device *dev, struct device_attribute
253 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200255 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200256 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200257 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 long new_div = simple_strtol(buf, NULL, 10), tmp;
259 u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
260
261 if (new_div == old_div) /* No change */
262 return count;
263
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100264 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 switch (new_div) {
266 case 1: data->fan_div[nr] = 0; break;
267 case 2: data->fan_div[nr] = 1; break;
268 case 4: data->fan_div[nr] = 2; break;
269 case 8: data->fan_div[nr] = 3; break;
270 default:
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100271 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 return -EINVAL;
273 }
274
Jean Delvare8eccbb62007-05-08 17:21:59 +0200275 switch (nr) {
276 case 0:
277 case 1:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200278 tmp = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200279 & ~(0x03 << (4 + 2 * nr));
280 tmp |= data->fan_div[nr] << (4 + 2 * nr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200281 smsc47m1_write_value(data, SMSC47M1_REG_FANDIV, tmp);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200282 break;
283 case 2:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200284 tmp = smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) & 0xCF;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200285 tmp |= data->fan_div[2] << 4;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200286 smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200287 break;
288 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
290 /* Preserve fan min */
291 tmp = 192 - (old_div * (192 - data->fan_preload[nr])
292 + new_div / 2) / new_div;
293 data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200294 smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100296 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297
298 return count;
299}
300
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200301static ssize_t set_pwm(struct device *dev, struct device_attribute
302 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200304 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200305 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200306 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 long val = simple_strtol(buf, NULL, 10);
308
309 if (val < 0 || val > 255)
310 return -EINVAL;
311
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100312 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 data->pwm[nr] &= 0x81; /* Preserve additional bits */
314 data->pwm[nr] |= PWM_TO_REG(val);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200315 smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100317 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
319 return count;
320}
321
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200322static ssize_t set_pwm_en(struct device *dev, struct device_attribute
323 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200325 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200326 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200327 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 long val = simple_strtol(buf, NULL, 10);
329
330 if (val != 0 && val != 1)
331 return -EINVAL;
332
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100333 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 data->pwm[nr] &= 0xFE; /* preserve the other bits */
335 data->pwm[nr] |= !val;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200336 smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100338 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
340 return count;
341}
342
343#define fan_present(offset) \
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200344static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan, \
345 NULL, offset - 1); \
346static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
347 get_fan_min, set_fan_min, offset - 1); \
348static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
349 get_fan_div, set_fan_div, offset - 1); \
350static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
351 get_pwm, set_pwm, offset - 1); \
352static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
353 get_pwm_en, set_pwm_en, offset - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354
355fan_present(1);
356fan_present(2);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200357fan_present(3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358
359static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
360
Jean Delvare51f2cca2007-05-08 17:22:00 +0200361static ssize_t show_name(struct device *dev, struct device_attribute
362 *devattr, char *buf)
363{
364 struct smsc47m1_data *data = dev_get_drvdata(dev);
365
366 return sprintf(buf, "%s\n", data->name);
367}
368static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
369
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200370/* Almost all sysfs files may or may not be created depending on the chip
371 setup so we create them individually. It is still convenient to define a
372 group to remove them all at once. */
373static struct attribute *smsc47m1_attributes[] = {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200374 &sensor_dev_attr_fan1_input.dev_attr.attr,
375 &sensor_dev_attr_fan1_min.dev_attr.attr,
376 &sensor_dev_attr_fan1_div.dev_attr.attr,
377 &sensor_dev_attr_fan2_input.dev_attr.attr,
378 &sensor_dev_attr_fan2_min.dev_attr.attr,
379 &sensor_dev_attr_fan2_div.dev_attr.attr,
380 &sensor_dev_attr_fan3_input.dev_attr.attr,
381 &sensor_dev_attr_fan3_min.dev_attr.attr,
382 &sensor_dev_attr_fan3_div.dev_attr.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200383
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200384 &sensor_dev_attr_pwm1.dev_attr.attr,
385 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
386 &sensor_dev_attr_pwm2.dev_attr.attr,
387 &sensor_dev_attr_pwm2_enable.dev_attr.attr,
388 &sensor_dev_attr_pwm3.dev_attr.attr,
389 &sensor_dev_attr_pwm3_enable.dev_attr.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200390
391 &dev_attr_alarms.attr,
Jean Delvare51f2cca2007-05-08 17:22:00 +0200392 &dev_attr_name.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200393 NULL
394};
395
396static const struct attribute_group smsc47m1_group = {
397 .attrs = smsc47m1_attributes,
398};
399
Jean Delvare51f2cca2007-05-08 17:22:00 +0200400static int __init smsc47m1_find(unsigned short *addr,
401 struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402{
403 u8 val;
404
405 superio_enter();
Jean Delvare67b671b2007-12-06 23:13:42 +0100406 val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
408 /*
Jean Delvare60917802006-10-08 22:00:44 +0200409 * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
410 * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
Jean Delvareec5ce552005-04-26 22:09:43 +0200412 * can do much more besides (device id 0x60).
Jean Delvareb890a072005-10-26 22:21:24 +0200413 * The LPC47M997 is undocumented, but seems to be compatible with
414 * the LPC47M192, and has the same device id.
Jean Delvare8eccbb62007-05-08 17:21:59 +0200415 * The LPC47M292 (device id 0x6B) is somewhat compatible, but it
416 * supports a 3rd fan, and the pin configuration registers are
417 * unfortunately different.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200419 switch (val) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200420 case 0x51:
Jean Delvare620100c2007-05-08 17:22:00 +0200421 pr_info(DRVNAME ": Found SMSC LPC47B27x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200422 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200423 break;
424 case 0x59:
Jean Delvare620100c2007-05-08 17:22:00 +0200425 pr_info(DRVNAME ": Found SMSC LPC47M10x/LPC47M112/LPC47M13x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200426 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200427 break;
428 case 0x5F:
Jean Delvare620100c2007-05-08 17:22:00 +0200429 pr_info(DRVNAME ": Found SMSC LPC47M14x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200430 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200431 break;
432 case 0x60:
Jean Delvare620100c2007-05-08 17:22:00 +0200433 pr_info(DRVNAME ": Found SMSC LPC47M15x/LPC47M192/LPC47M997\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200434 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200435 break;
436 case 0x6B:
Jean Delvare620100c2007-05-08 17:22:00 +0200437 pr_info(DRVNAME ": Found SMSC LPC47M292\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200438 sio_data->type = smsc47m2;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200439 break;
440 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 superio_exit();
442 return -ENODEV;
443 }
444
445 superio_select();
Jean Delvare2d8672c2005-07-19 23:56:35 +0200446 *addr = (superio_inb(SUPERIO_REG_BASE) << 8)
447 | superio_inb(SUPERIO_REG_BASE + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 val = superio_inb(SUPERIO_REG_ACT);
Jean Delvare2d8672c2005-07-19 23:56:35 +0200449 if (*addr == 0 || (val & 0x01) == 0) {
Jean Delvare620100c2007-05-08 17:22:00 +0200450 pr_info(DRVNAME ": Device is disabled, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 superio_exit();
452 return -ENODEV;
453 }
454
455 superio_exit();
456 return 0;
457}
458
Jean Delvare51f2cca2007-05-08 17:22:00 +0200459static int __devinit smsc47m1_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200461 struct device *dev = &pdev->dev;
462 struct smsc47m1_sio_data *sio_data = dev->platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 struct smsc47m1_data *data;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200464 struct resource *res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 int err = 0;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200466 int fan1, fan2, fan3, pwm1, pwm2, pwm3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467
Jean Delvare51f2cca2007-05-08 17:22:00 +0200468 static const char *names[] = {
469 "smsc47m1",
470 "smsc47m2",
471 };
472
473 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
474 if (!request_region(res->start, SMSC_EXTENT, DRVNAME)) {
475 dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
476 (unsigned long)res->start,
477 (unsigned long)res->end);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 return -EBUSY;
479 }
480
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200481 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 err = -ENOMEM;
483 goto error_release;
484 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
Jean Delvare51f2cca2007-05-08 17:22:00 +0200486 data->addr = res->start;
487 data->type = sio_data->type;
488 data->name = names[sio_data->type];
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100489 mutex_init(&data->update_lock);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200490 platform_set_drvdata(pdev, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491
492 /* If no function is properly configured, there's no point in
493 actually registering the chip. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200494 pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 == 0x04;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200496 pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 == 0x04;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200498 if (data->type == smsc47m2) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200499 fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200500 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200501 fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200502 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200503 fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200504 & 0x0d) == 0x0d;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200505 pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200506 & 0x0d) == 0x08;
507 } else {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200508 fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200509 & 0x05) == 0x05;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200510 fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200511 & 0x05) == 0x05;
512 fan3 = 0;
513 pwm3 = 0;
514 }
515 if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200516 dev_warn(dev, "Device not configured, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 err = -ENODEV;
518 goto error_free;
519 }
520
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 /* Some values (fan min, clock dividers, pwm registers) may be
522 needed before any update is triggered, so we better read them
523 at least once here. We don't usually do it that way, but in
524 this particular case, manually reading 5 registers out of 8
525 doesn't make much sense and we're better using the existing
526 function. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200527 smsc47m1_update_device(dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400529 /* Register sysfs hooks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 if (fan1) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200531 if ((err = device_create_file(dev,
532 &sensor_dev_attr_fan1_input.dev_attr))
533 || (err = device_create_file(dev,
534 &sensor_dev_attr_fan1_min.dev_attr))
535 || (err = device_create_file(dev,
536 &sensor_dev_attr_fan1_div.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200537 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200539 dev_dbg(dev, "Fan 1 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
541 if (fan2) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200542 if ((err = device_create_file(dev,
543 &sensor_dev_attr_fan2_input.dev_attr))
544 || (err = device_create_file(dev,
545 &sensor_dev_attr_fan2_min.dev_attr))
546 || (err = device_create_file(dev,
547 &sensor_dev_attr_fan2_div.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200548 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200550 dev_dbg(dev, "Fan 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551
Jean Delvare8eccbb62007-05-08 17:21:59 +0200552 if (fan3) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200553 if ((err = device_create_file(dev,
554 &sensor_dev_attr_fan3_input.dev_attr))
555 || (err = device_create_file(dev,
556 &sensor_dev_attr_fan3_min.dev_attr))
557 || (err = device_create_file(dev,
558 &sensor_dev_attr_fan3_div.dev_attr)))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200559 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200560 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200561 dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200562
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 if (pwm1) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200564 if ((err = device_create_file(dev,
565 &sensor_dev_attr_pwm1.dev_attr))
566 || (err = device_create_file(dev,
567 &sensor_dev_attr_pwm1_enable.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200568 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200570 dev_dbg(dev, "PWM 1 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200571
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 if (pwm2) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200573 if ((err = device_create_file(dev,
574 &sensor_dev_attr_pwm2.dev_attr))
575 || (err = device_create_file(dev,
576 &sensor_dev_attr_pwm2_enable.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200577 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200579 dev_dbg(dev, "PWM 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
Jean Delvare8eccbb62007-05-08 17:21:59 +0200581 if (pwm3) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200582 if ((err = device_create_file(dev,
583 &sensor_dev_attr_pwm3.dev_attr))
584 || (err = device_create_file(dev,
585 &sensor_dev_attr_pwm3_enable.dev_attr)))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200586 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200587 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200588 dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200589
Jean Delvare51f2cca2007-05-08 17:22:00 +0200590 if ((err = device_create_file(dev, &dev_attr_alarms)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200591 goto error_remove_files;
Jean Delvare68a50b52007-08-12 13:58:50 +0200592 if ((err = device_create_file(dev, &dev_attr_name)))
593 goto error_remove_files;
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200594
Tony Jones1beeffe2007-08-20 13:46:20 -0700595 data->hwmon_dev = hwmon_device_register(dev);
596 if (IS_ERR(data->hwmon_dev)) {
597 err = PTR_ERR(data->hwmon_dev);
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200598 goto error_remove_files;
599 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
601 return 0;
602
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200603error_remove_files:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200604 sysfs_remove_group(&dev->kobj, &smsc47m1_group);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605error_free:
Jean Delvare04a62172007-06-12 13:57:19 +0200606 platform_set_drvdata(pdev, NULL);
Alexey Dobriyan1f57ff82005-08-26 01:49:14 +0400607 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608error_release:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200609 release_region(res->start, SMSC_EXTENT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 return err;
611}
612
Jean Delvare51f2cca2007-05-08 17:22:00 +0200613static int __devexit smsc47m1_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200615 struct smsc47m1_data *data = platform_get_drvdata(pdev);
616 struct resource *res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617
Tony Jones1beeffe2007-08-20 13:46:20 -0700618 hwmon_device_unregister(data->hwmon_dev);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200619 sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400620
Jean Delvare51f2cca2007-05-08 17:22:00 +0200621 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
622 release_region(res->start, SMSC_EXTENT);
Jean Delvare04a62172007-06-12 13:57:19 +0200623 platform_set_drvdata(pdev, NULL);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400624 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
626 return 0;
627}
628
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
630 int init)
631{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200632 struct smsc47m1_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100634 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
636 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200637 int i, fan_nr;
638 fan_nr = data->type == smsc47m2 ? 3 : 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
Jean Delvare8eccbb62007-05-08 17:21:59 +0200640 for (i = 0; i < fan_nr; i++) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200641 data->fan[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200642 SMSC47M1_REG_FAN[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200643 data->fan_preload[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200644 SMSC47M1_REG_FAN_PRELOAD[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200645 data->pwm[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200646 SMSC47M1_REG_PWM[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 }
648
Jean Delvare51f2cca2007-05-08 17:22:00 +0200649 i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 data->fan_div[0] = (i >> 4) & 0x03;
651 data->fan_div[1] = i >> 6;
652
Jean Delvare51f2cca2007-05-08 17:22:00 +0200653 data->alarms = smsc47m1_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 SMSC47M1_REG_ALARM) >> 6;
655 /* Clear alarms if needed */
656 if (data->alarms)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200657 smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658
Jean Delvare8eccbb62007-05-08 17:21:59 +0200659 if (fan_nr >= 3) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200660 data->fan_div[2] = (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200661 SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200662 data->alarms |= (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200663 SMSC47M2_REG_ALARM6) & 0x40) >> 4;
664 /* Clear alarm if needed */
665 if (data->alarms & 0x04)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200666 smsc47m1_write_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200667 SMSC47M2_REG_ALARM6,
668 0x40);
669 }
670
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 data->last_updated = jiffies;
672 }
673
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100674 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 return data;
676}
677
Jean Delvare51f2cca2007-05-08 17:22:00 +0200678static int __init smsc47m1_device_add(unsigned short address,
679 const struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200681 struct resource res = {
682 .start = address,
683 .end = address + SMSC_EXTENT - 1,
684 .name = DRVNAME,
685 .flags = IORESOURCE_IO,
686 };
687 int err;
688
689 pdev = platform_device_alloc(DRVNAME, address);
690 if (!pdev) {
691 err = -ENOMEM;
692 printk(KERN_ERR DRVNAME ": Device allocation failed\n");
693 goto exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 }
695
Jean Delvare51f2cca2007-05-08 17:22:00 +0200696 err = platform_device_add_resources(pdev, &res, 1);
697 if (err) {
698 printk(KERN_ERR DRVNAME ": Device resource addition failed "
699 "(%d)\n", err);
700 goto exit_device_put;
701 }
702
Jean Delvare2df6d812007-06-09 10:11:16 -0400703 err = platform_device_add_data(pdev, sio_data,
704 sizeof(struct smsc47m1_sio_data));
705 if (err) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200706 printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
707 goto exit_device_put;
708 }
Jean Delvare51f2cca2007-05-08 17:22:00 +0200709
710 err = platform_device_add(pdev);
711 if (err) {
712 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
713 err);
714 goto exit_device_put;
715 }
716
717 return 0;
718
719exit_device_put:
720 platform_device_put(pdev);
721exit:
722 return err;
723}
724
725static int __init sm_smsc47m1_init(void)
726{
727 int err;
728 unsigned short address;
729 struct smsc47m1_sio_data sio_data;
730
731 if (smsc47m1_find(&address, &sio_data))
732 return -ENODEV;
733
734 err = platform_driver_register(&smsc47m1_driver);
735 if (err)
736 goto exit;
737
738 /* Sets global pdev as a side effect */
739 err = smsc47m1_device_add(address, &sio_data);
740 if (err)
741 goto exit_driver;
742
743 return 0;
744
745exit_driver:
746 platform_driver_unregister(&smsc47m1_driver);
747exit:
748 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749}
750
751static void __exit sm_smsc47m1_exit(void)
752{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200753 platform_device_unregister(pdev);
754 platform_driver_unregister(&smsc47m1_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755}
756
757MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
758MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
759MODULE_LICENSE("GPL");
760
761module_init(sm_smsc47m1_init);
762module_exit(sm_smsc47m1_exit);