blob: 47132fd26b1bf9a4f1948864fe95a34ecb5afdf2 [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 Delvare0db97142005-09-06 21:21:54 +02005 Supports the SMSC LPC47B27x, LPC47M10x, LPC47M13x, LPC47M14x,
Jean Delvareb890a072005-10-26 22:21:24 +02006 LPC47M15x, LPC47M192 and LPC47M997 Super-I/O chips.
Linus Torvalds1da177e2005-04-16 15:20:36 -07007
8 Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
9 Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
10 Ported to Linux 2.6 by Gabriele Gorla <gorlik@yahoo.com>
11 and Jean Delvare
12
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26*/
27
28#include <linux/module.h>
29#include <linux/slab.h>
30#include <linux/ioport.h>
31#include <linux/jiffies.h>
32#include <linux/i2c.h>
Jean Delvarefde09502005-07-19 23:51:07 +020033#include <linux/i2c-isa.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040034#include <linux/hwmon.h>
35#include <linux/err.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <linux/init.h>
Ingo Molnar9a61bf62006-01-18 23:19:26 +010037#include <linux/mutex.h>
Jean Delvarece8c6ce12006-09-24 21:25:12 +020038#include <linux/sysfs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <asm/io.h>
40
Linus Torvalds1da177e2005-04-16 15:20:36 -070041/* Address is autodetected, there is no default value */
Jean Delvare2d8672c2005-07-19 23:56:35 +020042static unsigned short address;
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44/* Super-I/0 registers and commands */
45
46#define REG 0x2e /* The register to read/write */
47#define VAL 0x2f /* The value to read/write */
48
49static inline void
50superio_outb(int reg, int val)
51{
52 outb(reg, REG);
53 outb(val, VAL);
54}
55
56static inline int
57superio_inb(int reg)
58{
59 outb(reg, REG);
60 return inb(VAL);
61}
62
63/* logical device for fans is 0x0A */
64#define superio_select() superio_outb(0x07, 0x0A)
65
66static inline void
67superio_enter(void)
68{
69 outb(0x55, REG);
70}
71
72static inline void
73superio_exit(void)
74{
75 outb(0xAA, REG);
76}
77
78#define SUPERIO_REG_ACT 0x30
79#define SUPERIO_REG_BASE 0x60
80#define SUPERIO_REG_DEVID 0x20
81
82/* Logical device registers */
83
84#define SMSC_EXTENT 0x80
85
86/* nr is 0 or 1 in the macros below */
87#define SMSC47M1_REG_ALARM 0x04
88#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr))
89#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr))
90#define SMSC47M1_REG_PWM(nr) (0x56 + (nr))
91#define SMSC47M1_REG_FANDIV 0x58
92#define SMSC47M1_REG_FAN(nr) (0x59 + (nr))
93#define SMSC47M1_REG_FAN_PRELOAD(nr) (0x5B + (nr))
94
95#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \
96 983040/((192-(reg))*(div)))
97#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \
98 983040/(((reg)-(preload))*(div)))
99#define DIV_FROM_REG(reg) (1 << (reg))
100#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1)
101#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01)
102#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E)
103
104struct smsc47m1_data {
105 struct i2c_client client;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400106 struct class_device *class_dev;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100107 struct mutex lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100109 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 unsigned long last_updated; /* In jiffies */
111
112 u8 fan[2]; /* Register value */
113 u8 fan_preload[2]; /* Register value */
114 u8 fan_div[2]; /* Register encoding, shifted right */
115 u8 alarms; /* Register encoding */
116 u8 pwm[2]; /* Register value (bit 7 is enable) */
117};
118
119
Jean Delvare2d8672c2005-07-19 23:56:35 +0200120static int smsc47m1_detect(struct i2c_adapter *adapter);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121static int smsc47m1_detach_client(struct i2c_client *client);
122
123static int smsc47m1_read_value(struct i2c_client *client, u8 reg);
124static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value);
125
126static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
127 int init);
128
129
130static struct i2c_driver smsc47m1_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100131 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200132 .owner = THIS_MODULE,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100133 .name = "smsc47m1",
134 },
Jean Delvare2d8672c2005-07-19 23:56:35 +0200135 .attach_adapter = smsc47m1_detect,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 .detach_client = smsc47m1_detach_client,
137};
138
139/* nr is 0 or 1 in the callback functions below */
140
141static ssize_t get_fan(struct device *dev, char *buf, int nr)
142{
143 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
144 /* This chip (stupidly) stops monitoring fan speed if PWM is
145 enabled and duty cycle is 0%. This is fine if the monitoring
146 and control concern the same fan, but troublesome if they are
147 not (which could as well happen). */
148 int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
149 FAN_FROM_REG(data->fan[nr],
150 DIV_FROM_REG(data->fan_div[nr]),
151 data->fan_preload[nr]);
152 return sprintf(buf, "%d\n", rpm);
153}
154
155static ssize_t get_fan_min(struct device *dev, char *buf, int nr)
156{
157 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
158 int rpm = MIN_FROM_REG(data->fan_preload[nr],
159 DIV_FROM_REG(data->fan_div[nr]));
160 return sprintf(buf, "%d\n", rpm);
161}
162
163static ssize_t get_fan_div(struct device *dev, char *buf, int nr)
164{
165 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
166 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
167}
168
169static ssize_t get_pwm(struct device *dev, char *buf, int nr)
170{
171 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
172 return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));
173}
174
175static ssize_t get_pwm_en(struct device *dev, char *buf, int nr)
176{
177 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
178 return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[nr]));
179}
180
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400181static ssize_t get_alarms(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182{
183 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
184 return sprintf(buf, "%d\n", data->alarms);
185}
186
187static ssize_t set_fan_min(struct device *dev, const char *buf,
188 size_t count, int nr)
189{
190 struct i2c_client *client = to_i2c_client(dev);
191 struct smsc47m1_data *data = i2c_get_clientdata(client);
192 long rpmdiv, val = simple_strtol(buf, NULL, 10);
193
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100194 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
196
197 if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100198 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 return -EINVAL;
200 }
201
202 data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
203 smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr),
204 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100205 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206
207 return count;
208}
209
210/* Note: we save and restore the fan minimum here, because its value is
211 determined in part by the fan clock divider. This follows the principle
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200212 of least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 because the divider changed. */
214static ssize_t set_fan_div(struct device *dev, const char *buf,
215 size_t count, int nr)
216{
217 struct i2c_client *client = to_i2c_client(dev);
218 struct smsc47m1_data *data = i2c_get_clientdata(client);
219
220 long new_div = simple_strtol(buf, NULL, 10), tmp;
221 u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
222
223 if (new_div == old_div) /* No change */
224 return count;
225
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100226 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 switch (new_div) {
228 case 1: data->fan_div[nr] = 0; break;
229 case 2: data->fan_div[nr] = 1; break;
230 case 4: data->fan_div[nr] = 2; break;
231 case 8: data->fan_div[nr] = 3; break;
232 default:
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100233 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 return -EINVAL;
235 }
236
237 tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV) & 0x0F;
238 tmp |= (data->fan_div[0] << 4) | (data->fan_div[1] << 6);
239 smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp);
240
241 /* Preserve fan min */
242 tmp = 192 - (old_div * (192 - data->fan_preload[nr])
243 + new_div / 2) / new_div;
244 data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
245 smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr),
246 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100247 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
249 return count;
250}
251
252static ssize_t set_pwm(struct device *dev, const char *buf,
253 size_t count, int nr)
254{
255 struct i2c_client *client = to_i2c_client(dev);
256 struct smsc47m1_data *data = i2c_get_clientdata(client);
257
258 long val = simple_strtol(buf, NULL, 10);
259
260 if (val < 0 || val > 255)
261 return -EINVAL;
262
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100263 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 data->pwm[nr] &= 0x81; /* Preserve additional bits */
265 data->pwm[nr] |= PWM_TO_REG(val);
266 smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr),
267 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100268 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269
270 return count;
271}
272
273static ssize_t set_pwm_en(struct device *dev, const char *buf,
274 size_t count, int nr)
275{
276 struct i2c_client *client = to_i2c_client(dev);
277 struct smsc47m1_data *data = i2c_get_clientdata(client);
278
279 long val = simple_strtol(buf, NULL, 10);
280
281 if (val != 0 && val != 1)
282 return -EINVAL;
283
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100284 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 data->pwm[nr] &= 0xFE; /* preserve the other bits */
286 data->pwm[nr] |= !val;
287 smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr),
288 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100289 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
291 return count;
292}
293
294#define fan_present(offset) \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400295static ssize_t get_fan##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{ \
297 return get_fan(dev, buf, offset - 1); \
298} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400299static ssize_t get_fan##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300{ \
301 return get_fan_min(dev, buf, offset - 1); \
302} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400303static ssize_t set_fan##offset##_min (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 const char *buf, size_t count) \
305{ \
306 return set_fan_min(dev, buf, count, offset - 1); \
307} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400308static ssize_t get_fan##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309{ \
310 return get_fan_div(dev, buf, offset - 1); \
311} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400312static ssize_t set_fan##offset##_div (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 const char *buf, size_t count) \
314{ \
315 return set_fan_div(dev, buf, count, offset - 1); \
316} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400317static ssize_t get_pwm##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318{ \
319 return get_pwm(dev, buf, offset - 1); \
320} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400321static ssize_t set_pwm##offset (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 const char *buf, size_t count) \
323{ \
324 return set_pwm(dev, buf, count, offset - 1); \
325} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400326static ssize_t get_pwm##offset##_en (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327{ \
328 return get_pwm_en(dev, buf, offset - 1); \
329} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400330static ssize_t set_pwm##offset##_en (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 const char *buf, size_t count) \
332{ \
333 return set_pwm_en(dev, buf, count, offset - 1); \
334} \
335static DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan##offset, \
336 NULL); \
337static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
338 get_fan##offset##_min, set_fan##offset##_min); \
339static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
340 get_fan##offset##_div, set_fan##offset##_div); \
341static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
342 get_pwm##offset, set_pwm##offset); \
343static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
344 get_pwm##offset##_en, set_pwm##offset##_en);
345
346fan_present(1);
347fan_present(2);
348
349static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
350
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200351/* Almost all sysfs files may or may not be created depending on the chip
352 setup so we create them individually. It is still convenient to define a
353 group to remove them all at once. */
354static struct attribute *smsc47m1_attributes[] = {
355 &dev_attr_fan1_input.attr,
356 &dev_attr_fan1_min.attr,
357 &dev_attr_fan1_div.attr,
358 &dev_attr_fan2_input.attr,
359 &dev_attr_fan2_min.attr,
360 &dev_attr_fan2_div.attr,
361
362 &dev_attr_pwm1.attr,
363 &dev_attr_pwm1_enable.attr,
364 &dev_attr_pwm2.attr,
365 &dev_attr_pwm2_enable.attr,
366
367 &dev_attr_alarms.attr,
368 NULL
369};
370
371static const struct attribute_group smsc47m1_group = {
372 .attrs = smsc47m1_attributes,
373};
374
Jean Delvaree6cfb3a2005-07-27 21:32:02 +0200375static int __init smsc47m1_find(unsigned short *addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376{
377 u8 val;
378
379 superio_enter();
380 val = superio_inb(SUPERIO_REG_DEVID);
381
382 /*
383 * SMSC LPC47M10x/LPC47M13x (device id 0x59), LPC47M14x (device id
384 * 0x5F) and LPC47B27x (device id 0x51) have fan control.
385 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
Jean Delvareec5ce552005-04-26 22:09:43 +0200386 * can do much more besides (device id 0x60).
Jean Delvareb890a072005-10-26 22:21:24 +0200387 * The LPC47M997 is undocumented, but seems to be compatible with
388 * the LPC47M192, and has the same device id.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 */
390 if (val == 0x51)
Jean Delvareec5ce552005-04-26 22:09:43 +0200391 printk(KERN_INFO "smsc47m1: Found SMSC LPC47B27x\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 else if (val == 0x59)
Jean Delvareec5ce552005-04-26 22:09:43 +0200393 printk(KERN_INFO "smsc47m1: Found SMSC LPC47M10x/LPC47M13x\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 else if (val == 0x5F)
Jean Delvareec5ce552005-04-26 22:09:43 +0200395 printk(KERN_INFO "smsc47m1: Found SMSC LPC47M14x\n");
396 else if (val == 0x60)
Jean Delvareb890a072005-10-26 22:21:24 +0200397 printk(KERN_INFO "smsc47m1: Found SMSC "
398 "LPC47M15x/LPC47M192/LPC47M997\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 else {
400 superio_exit();
401 return -ENODEV;
402 }
403
404 superio_select();
Jean Delvare2d8672c2005-07-19 23:56:35 +0200405 *addr = (superio_inb(SUPERIO_REG_BASE) << 8)
406 | superio_inb(SUPERIO_REG_BASE + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 val = superio_inb(SUPERIO_REG_ACT);
Jean Delvare2d8672c2005-07-19 23:56:35 +0200408 if (*addr == 0 || (val & 0x01) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 printk(KERN_INFO "smsc47m1: Device is disabled, will not use\n");
410 superio_exit();
411 return -ENODEV;
412 }
413
414 superio_exit();
415 return 0;
416}
417
Jean Delvare2d8672c2005-07-19 23:56:35 +0200418static int smsc47m1_detect(struct i2c_adapter *adapter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419{
420 struct i2c_client *new_client;
421 struct smsc47m1_data *data;
422 int err = 0;
423 int fan1, fan2, pwm1, pwm2;
424
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100425 if (!request_region(address, SMSC_EXTENT, smsc47m1_driver.driver.name)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 dev_err(&adapter->dev, "Region 0x%x already in use!\n", address);
427 return -EBUSY;
428 }
429
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200430 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 err = -ENOMEM;
432 goto error_release;
433 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
435 new_client = &data->client;
436 i2c_set_clientdata(new_client, data);
437 new_client->addr = address;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100438 mutex_init(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 new_client->adapter = adapter;
440 new_client->driver = &smsc47m1_driver;
441 new_client->flags = 0;
442
443 strlcpy(new_client->name, "smsc47m1", I2C_NAME_SIZE);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100444 mutex_init(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445
446 /* If no function is properly configured, there's no point in
447 actually registering the chip. */
448 fan1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0)) & 0x05)
449 == 0x05;
450 fan2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1)) & 0x05)
451 == 0x05;
452 pwm1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(0)) & 0x05)
453 == 0x04;
454 pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05)
455 == 0x04;
456 if (!(fan1 || fan2 || pwm1 || pwm2)) {
Jean Delvare0dd76992006-08-28 14:22:34 +0200457 dev_warn(&adapter->dev, "Device at 0x%x is not configured, "
458 "will not use\n", new_client->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 err = -ENODEV;
460 goto error_free;
461 }
462
463 if ((err = i2c_attach_client(new_client)))
464 goto error_free;
465
466 /* Some values (fan min, clock dividers, pwm registers) may be
467 needed before any update is triggered, so we better read them
468 at least once here. We don't usually do it that way, but in
469 this particular case, manually reading 5 registers out of 8
470 doesn't make much sense and we're better using the existing
471 function. */
472 smsc47m1_update_device(&new_client->dev, 1);
473
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400474 /* Register sysfs hooks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 if (fan1) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200476 if ((err = device_create_file(&new_client->dev,
477 &dev_attr_fan1_input))
478 || (err = device_create_file(&new_client->dev,
479 &dev_attr_fan1_min))
480 || (err = device_create_file(&new_client->dev,
481 &dev_attr_fan1_div)))
482 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 } else
484 dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, "
485 "skipping\n");
486
487 if (fan2) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200488 if ((err = device_create_file(&new_client->dev,
489 &dev_attr_fan2_input))
490 || (err = device_create_file(&new_client->dev,
491 &dev_attr_fan2_min))
492 || (err = device_create_file(&new_client->dev,
493 &dev_attr_fan2_div)))
494 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 } else
496 dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, "
497 "skipping\n");
498
499 if (pwm1) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200500 if ((err = device_create_file(&new_client->dev,
501 &dev_attr_pwm1))
502 || (err = device_create_file(&new_client->dev,
503 &dev_attr_pwm1_enable)))
504 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 } else
506 dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, "
507 "skipping\n");
508 if (pwm2) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200509 if ((err = device_create_file(&new_client->dev,
510 &dev_attr_pwm2))
511 || (err = device_create_file(&new_client->dev,
512 &dev_attr_pwm2_enable)))
513 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 } else
515 dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, "
516 "skipping\n");
517
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200518 if ((err = device_create_file(&new_client->dev, &dev_attr_alarms)))
519 goto error_remove_files;
520
521 data->class_dev = hwmon_device_register(&new_client->dev);
522 if (IS_ERR(data->class_dev)) {
523 err = PTR_ERR(data->class_dev);
524 goto error_remove_files;
525 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526
527 return 0;
528
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200529error_remove_files:
530 sysfs_remove_group(&new_client->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400531 i2c_detach_client(new_client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532error_free:
Alexey Dobriyan1f57ff82005-08-26 01:49:14 +0400533 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534error_release:
535 release_region(address, SMSC_EXTENT);
536 return err;
537}
538
539static int smsc47m1_detach_client(struct i2c_client *client)
540{
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400541 struct smsc47m1_data *data = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 int err;
543
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400544 hwmon_device_unregister(data->class_dev);
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200545 sysfs_remove_group(&client->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400546
Jean Delvare7bef5592005-07-27 22:14:49 +0200547 if ((err = i2c_detach_client(client)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
550 release_region(client->addr, SMSC_EXTENT);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400551 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
553 return 0;
554}
555
556static int smsc47m1_read_value(struct i2c_client *client, u8 reg)
557{
558 int res;
559
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100560 mutex_lock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 res = inb_p(client->addr + reg);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100562 mutex_unlock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 return res;
564}
565
566static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value)
567{
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100568 mutex_lock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 outb_p(value, client->addr + reg);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100570 mutex_unlock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571}
572
573static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
574 int init)
575{
576 struct i2c_client *client = to_i2c_client(dev);
577 struct smsc47m1_data *data = i2c_get_clientdata(client);
578
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100579 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
581 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
582 int i;
583
584 for (i = 0; i < 2; i++) {
585 data->fan[i] = smsc47m1_read_value(client,
586 SMSC47M1_REG_FAN(i));
587 data->fan_preload[i] = smsc47m1_read_value(client,
588 SMSC47M1_REG_FAN_PRELOAD(i));
589 data->pwm[i] = smsc47m1_read_value(client,
590 SMSC47M1_REG_PWM(i));
591 }
592
593 i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV);
594 data->fan_div[0] = (i >> 4) & 0x03;
595 data->fan_div[1] = i >> 6;
596
597 data->alarms = smsc47m1_read_value(client,
598 SMSC47M1_REG_ALARM) >> 6;
599 /* Clear alarms if needed */
600 if (data->alarms)
601 smsc47m1_write_value(client, SMSC47M1_REG_ALARM, 0xC0);
602
603 data->last_updated = jiffies;
604 }
605
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100606 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 return data;
608}
609
610static int __init sm_smsc47m1_init(void)
611{
Jean Delvare2d8672c2005-07-19 23:56:35 +0200612 if (smsc47m1_find(&address)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 return -ENODEV;
614 }
615
Jean Delvarefde09502005-07-19 23:51:07 +0200616 return i2c_isa_add_driver(&smsc47m1_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617}
618
619static void __exit sm_smsc47m1_exit(void)
620{
Jean Delvarefde09502005-07-19 23:51:07 +0200621 i2c_isa_del_driver(&smsc47m1_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622}
623
624MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
625MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
626MODULE_LICENSE("GPL");
627
628module_init(sm_smsc47m1_init);
629module_exit(sm_smsc47m1_exit);