blob: beb881c4b2e8ce1f2c191f6ce2343655a0e57200 [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,
6 LPC47M14x, 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 /*
Jean Delvare60917802006-10-08 22:00:44 +0200383 * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
384 * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 * 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 Delvare60917802006-10-08 22:00:44 +0200393 printk(KERN_INFO "smsc47m1: Found SMSC "
394 "LPC47M10x/LPC47M112/LPC47M13x\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 else if (val == 0x5F)
Jean Delvareec5ce552005-04-26 22:09:43 +0200396 printk(KERN_INFO "smsc47m1: Found SMSC LPC47M14x\n");
397 else if (val == 0x60)
Jean Delvareb890a072005-10-26 22:21:24 +0200398 printk(KERN_INFO "smsc47m1: Found SMSC "
399 "LPC47M15x/LPC47M192/LPC47M997\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 else {
401 superio_exit();
402 return -ENODEV;
403 }
404
405 superio_select();
Jean Delvare2d8672c2005-07-19 23:56:35 +0200406 *addr = (superio_inb(SUPERIO_REG_BASE) << 8)
407 | superio_inb(SUPERIO_REG_BASE + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 val = superio_inb(SUPERIO_REG_ACT);
Jean Delvare2d8672c2005-07-19 23:56:35 +0200409 if (*addr == 0 || (val & 0x01) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 printk(KERN_INFO "smsc47m1: Device is disabled, will not use\n");
411 superio_exit();
412 return -ENODEV;
413 }
414
415 superio_exit();
416 return 0;
417}
418
Jean Delvare2d8672c2005-07-19 23:56:35 +0200419static int smsc47m1_detect(struct i2c_adapter *adapter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420{
421 struct i2c_client *new_client;
422 struct smsc47m1_data *data;
423 int err = 0;
424 int fan1, fan2, pwm1, pwm2;
425
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100426 if (!request_region(address, SMSC_EXTENT, smsc47m1_driver.driver.name)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 dev_err(&adapter->dev, "Region 0x%x already in use!\n", address);
428 return -EBUSY;
429 }
430
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200431 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 err = -ENOMEM;
433 goto error_release;
434 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435
436 new_client = &data->client;
437 i2c_set_clientdata(new_client, data);
438 new_client->addr = address;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100439 mutex_init(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 new_client->adapter = adapter;
441 new_client->driver = &smsc47m1_driver;
442 new_client->flags = 0;
443
444 strlcpy(new_client->name, "smsc47m1", I2C_NAME_SIZE);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100445 mutex_init(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446
447 /* If no function is properly configured, there's no point in
448 actually registering the chip. */
449 fan1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0)) & 0x05)
450 == 0x05;
451 fan2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1)) & 0x05)
452 == 0x05;
453 pwm1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(0)) & 0x05)
454 == 0x04;
455 pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05)
456 == 0x04;
457 if (!(fan1 || fan2 || pwm1 || pwm2)) {
Jean Delvare0dd76992006-08-28 14:22:34 +0200458 dev_warn(&adapter->dev, "Device at 0x%x is not configured, "
459 "will not use\n", new_client->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 err = -ENODEV;
461 goto error_free;
462 }
463
464 if ((err = i2c_attach_client(new_client)))
465 goto error_free;
466
467 /* Some values (fan min, clock dividers, pwm registers) may be
468 needed before any update is triggered, so we better read them
469 at least once here. We don't usually do it that way, but in
470 this particular case, manually reading 5 registers out of 8
471 doesn't make much sense and we're better using the existing
472 function. */
473 smsc47m1_update_device(&new_client->dev, 1);
474
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400475 /* Register sysfs hooks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 if (fan1) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200477 if ((err = device_create_file(&new_client->dev,
478 &dev_attr_fan1_input))
479 || (err = device_create_file(&new_client->dev,
480 &dev_attr_fan1_min))
481 || (err = device_create_file(&new_client->dev,
482 &dev_attr_fan1_div)))
483 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 } else
485 dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, "
486 "skipping\n");
487
488 if (fan2) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200489 if ((err = device_create_file(&new_client->dev,
490 &dev_attr_fan2_input))
491 || (err = device_create_file(&new_client->dev,
492 &dev_attr_fan2_min))
493 || (err = device_create_file(&new_client->dev,
494 &dev_attr_fan2_div)))
495 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 } else
497 dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, "
498 "skipping\n");
499
500 if (pwm1) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200501 if ((err = device_create_file(&new_client->dev,
502 &dev_attr_pwm1))
503 || (err = device_create_file(&new_client->dev,
504 &dev_attr_pwm1_enable)))
505 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 } else
507 dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, "
508 "skipping\n");
509 if (pwm2) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200510 if ((err = device_create_file(&new_client->dev,
511 &dev_attr_pwm2))
512 || (err = device_create_file(&new_client->dev,
513 &dev_attr_pwm2_enable)))
514 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 } else
516 dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, "
517 "skipping\n");
518
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200519 if ((err = device_create_file(&new_client->dev, &dev_attr_alarms)))
520 goto error_remove_files;
521
522 data->class_dev = hwmon_device_register(&new_client->dev);
523 if (IS_ERR(data->class_dev)) {
524 err = PTR_ERR(data->class_dev);
525 goto error_remove_files;
526 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
528 return 0;
529
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200530error_remove_files:
531 sysfs_remove_group(&new_client->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400532 i2c_detach_client(new_client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533error_free:
Alexey Dobriyan1f57ff82005-08-26 01:49:14 +0400534 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535error_release:
536 release_region(address, SMSC_EXTENT);
537 return err;
538}
539
540static int smsc47m1_detach_client(struct i2c_client *client)
541{
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400542 struct smsc47m1_data *data = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 int err;
544
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400545 hwmon_device_unregister(data->class_dev);
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200546 sysfs_remove_group(&client->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400547
Jean Delvare7bef5592005-07-27 22:14:49 +0200548 if ((err = i2c_detach_client(client)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550
551 release_region(client->addr, SMSC_EXTENT);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400552 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
554 return 0;
555}
556
557static int smsc47m1_read_value(struct i2c_client *client, u8 reg)
558{
559 int res;
560
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100561 mutex_lock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 res = inb_p(client->addr + reg);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100563 mutex_unlock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 return res;
565}
566
567static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value)
568{
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100569 mutex_lock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 outb_p(value, client->addr + reg);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100571 mutex_unlock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572}
573
574static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
575 int init)
576{
577 struct i2c_client *client = to_i2c_client(dev);
578 struct smsc47m1_data *data = i2c_get_clientdata(client);
579
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100580 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
582 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
583 int i;
584
585 for (i = 0; i < 2; i++) {
586 data->fan[i] = smsc47m1_read_value(client,
587 SMSC47M1_REG_FAN(i));
588 data->fan_preload[i] = smsc47m1_read_value(client,
589 SMSC47M1_REG_FAN_PRELOAD(i));
590 data->pwm[i] = smsc47m1_read_value(client,
591 SMSC47M1_REG_PWM(i));
592 }
593
594 i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV);
595 data->fan_div[0] = (i >> 4) & 0x03;
596 data->fan_div[1] = i >> 6;
597
598 data->alarms = smsc47m1_read_value(client,
599 SMSC47M1_REG_ALARM) >> 6;
600 /* Clear alarms if needed */
601 if (data->alarms)
602 smsc47m1_write_value(client, SMSC47M1_REG_ALARM, 0xC0);
603
604 data->last_updated = jiffies;
605 }
606
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100607 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 return data;
609}
610
611static int __init sm_smsc47m1_init(void)
612{
Jean Delvare2d8672c2005-07-19 23:56:35 +0200613 if (smsc47m1_find(&address)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 return -ENODEV;
615 }
616
Jean Delvarefde09502005-07-19 23:51:07 +0200617 return i2c_isa_add_driver(&smsc47m1_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618}
619
620static void __exit sm_smsc47m1_exit(void)
621{
Jean Delvarefde09502005-07-19 23:51:07 +0200622 i2c_isa_del_driver(&smsc47m1_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623}
624
625MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
626MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
627MODULE_LICENSE("GPL");
628
629module_init(sm_smsc47m1_init);
630module_exit(sm_smsc47m1_exit);