blob: 6c81b843d8314b9ef6ee9a4df0003d7fbaa93ad4 [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include <asm/io.h>
39
Linus Torvalds1da177e2005-04-16 15:20:36 -070040/* Address is autodetected, there is no default value */
Jean Delvare2d8672c2005-07-19 23:56:35 +020041static unsigned short address;
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
43/* Super-I/0 registers and commands */
44
45#define REG 0x2e /* The register to read/write */
46#define VAL 0x2f /* The value to read/write */
47
48static inline void
49superio_outb(int reg, int val)
50{
51 outb(reg, REG);
52 outb(val, VAL);
53}
54
55static inline int
56superio_inb(int reg)
57{
58 outb(reg, REG);
59 return inb(VAL);
60}
61
62/* logical device for fans is 0x0A */
63#define superio_select() superio_outb(0x07, 0x0A)
64
65static inline void
66superio_enter(void)
67{
68 outb(0x55, REG);
69}
70
71static inline void
72superio_exit(void)
73{
74 outb(0xAA, REG);
75}
76
77#define SUPERIO_REG_ACT 0x30
78#define SUPERIO_REG_BASE 0x60
79#define SUPERIO_REG_DEVID 0x20
80
81/* Logical device registers */
82
83#define SMSC_EXTENT 0x80
84
85/* nr is 0 or 1 in the macros below */
86#define SMSC47M1_REG_ALARM 0x04
87#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr))
88#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr))
89#define SMSC47M1_REG_PWM(nr) (0x56 + (nr))
90#define SMSC47M1_REG_FANDIV 0x58
91#define SMSC47M1_REG_FAN(nr) (0x59 + (nr))
92#define SMSC47M1_REG_FAN_PRELOAD(nr) (0x5B + (nr))
93
94#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \
95 983040/((192-(reg))*(div)))
96#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \
97 983040/(((reg)-(preload))*(div)))
98#define DIV_FROM_REG(reg) (1 << (reg))
99#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1)
100#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01)
101#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E)
102
103struct smsc47m1_data {
104 struct i2c_client client;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400105 struct class_device *class_dev;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100106 struct mutex lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100108 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 unsigned long last_updated; /* In jiffies */
110
111 u8 fan[2]; /* Register value */
112 u8 fan_preload[2]; /* Register value */
113 u8 fan_div[2]; /* Register encoding, shifted right */
114 u8 alarms; /* Register encoding */
115 u8 pwm[2]; /* Register value (bit 7 is enable) */
116};
117
118
Jean Delvare2d8672c2005-07-19 23:56:35 +0200119static int smsc47m1_detect(struct i2c_adapter *adapter);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120static int smsc47m1_detach_client(struct i2c_client *client);
121
122static int smsc47m1_read_value(struct i2c_client *client, u8 reg);
123static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value);
124
125static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
126 int init);
127
128
129static struct i2c_driver smsc47m1_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100130 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200131 .owner = THIS_MODULE,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100132 .name = "smsc47m1",
133 },
Jean Delvare2d8672c2005-07-19 23:56:35 +0200134 .attach_adapter = smsc47m1_detect,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 .detach_client = smsc47m1_detach_client,
136};
137
138/* nr is 0 or 1 in the callback functions below */
139
140static ssize_t get_fan(struct device *dev, char *buf, int nr)
141{
142 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
143 /* This chip (stupidly) stops monitoring fan speed if PWM is
144 enabled and duty cycle is 0%. This is fine if the monitoring
145 and control concern the same fan, but troublesome if they are
146 not (which could as well happen). */
147 int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
148 FAN_FROM_REG(data->fan[nr],
149 DIV_FROM_REG(data->fan_div[nr]),
150 data->fan_preload[nr]);
151 return sprintf(buf, "%d\n", rpm);
152}
153
154static ssize_t get_fan_min(struct device *dev, char *buf, int nr)
155{
156 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
157 int rpm = MIN_FROM_REG(data->fan_preload[nr],
158 DIV_FROM_REG(data->fan_div[nr]));
159 return sprintf(buf, "%d\n", rpm);
160}
161
162static ssize_t get_fan_div(struct device *dev, char *buf, int nr)
163{
164 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
165 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
166}
167
168static ssize_t get_pwm(struct device *dev, char *buf, int nr)
169{
170 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
171 return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));
172}
173
174static ssize_t get_pwm_en(struct device *dev, char *buf, int nr)
175{
176 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
177 return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[nr]));
178}
179
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400180static ssize_t get_alarms(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181{
182 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
183 return sprintf(buf, "%d\n", data->alarms);
184}
185
186static ssize_t set_fan_min(struct device *dev, const char *buf,
187 size_t count, int nr)
188{
189 struct i2c_client *client = to_i2c_client(dev);
190 struct smsc47m1_data *data = i2c_get_clientdata(client);
191 long rpmdiv, val = simple_strtol(buf, NULL, 10);
192
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100193 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
195
196 if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100197 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 return -EINVAL;
199 }
200
201 data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
202 smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr),
203 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100204 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205
206 return count;
207}
208
209/* Note: we save and restore the fan minimum here, because its value is
210 determined in part by the fan clock divider. This follows the principle
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200211 of least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 because the divider changed. */
213static ssize_t set_fan_div(struct device *dev, const char *buf,
214 size_t count, int nr)
215{
216 struct i2c_client *client = to_i2c_client(dev);
217 struct smsc47m1_data *data = i2c_get_clientdata(client);
218
219 long new_div = simple_strtol(buf, NULL, 10), tmp;
220 u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
221
222 if (new_div == old_div) /* No change */
223 return count;
224
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100225 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 switch (new_div) {
227 case 1: data->fan_div[nr] = 0; break;
228 case 2: data->fan_div[nr] = 1; break;
229 case 4: data->fan_div[nr] = 2; break;
230 case 8: data->fan_div[nr] = 3; break;
231 default:
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100232 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 return -EINVAL;
234 }
235
236 tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV) & 0x0F;
237 tmp |= (data->fan_div[0] << 4) | (data->fan_div[1] << 6);
238 smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp);
239
240 /* Preserve fan min */
241 tmp = 192 - (old_div * (192 - data->fan_preload[nr])
242 + new_div / 2) / new_div;
243 data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
244 smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr),
245 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100246 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
248 return count;
249}
250
251static ssize_t set_pwm(struct device *dev, const char *buf,
252 size_t count, int nr)
253{
254 struct i2c_client *client = to_i2c_client(dev);
255 struct smsc47m1_data *data = i2c_get_clientdata(client);
256
257 long val = simple_strtol(buf, NULL, 10);
258
259 if (val < 0 || val > 255)
260 return -EINVAL;
261
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100262 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 data->pwm[nr] &= 0x81; /* Preserve additional bits */
264 data->pwm[nr] |= PWM_TO_REG(val);
265 smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr),
266 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100267 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
269 return count;
270}
271
272static ssize_t set_pwm_en(struct device *dev, const char *buf,
273 size_t count, int nr)
274{
275 struct i2c_client *client = to_i2c_client(dev);
276 struct smsc47m1_data *data = i2c_get_clientdata(client);
277
278 long val = simple_strtol(buf, NULL, 10);
279
280 if (val != 0 && val != 1)
281 return -EINVAL;
282
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100283 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 data->pwm[nr] &= 0xFE; /* preserve the other bits */
285 data->pwm[nr] |= !val;
286 smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr),
287 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100288 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
290 return count;
291}
292
293#define fan_present(offset) \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400294static ssize_t get_fan##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295{ \
296 return get_fan(dev, buf, offset - 1); \
297} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400298static ssize_t get_fan##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299{ \
300 return get_fan_min(dev, buf, offset - 1); \
301} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400302static ssize_t set_fan##offset##_min (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 const char *buf, size_t count) \
304{ \
305 return set_fan_min(dev, buf, count, offset - 1); \
306} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400307static ssize_t get_fan##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308{ \
309 return get_fan_div(dev, buf, offset - 1); \
310} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400311static ssize_t set_fan##offset##_div (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 const char *buf, size_t count) \
313{ \
314 return set_fan_div(dev, buf, count, offset - 1); \
315} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400316static ssize_t get_pwm##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317{ \
318 return get_pwm(dev, buf, offset - 1); \
319} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400320static ssize_t set_pwm##offset (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 const char *buf, size_t count) \
322{ \
323 return set_pwm(dev, buf, count, offset - 1); \
324} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400325static ssize_t get_pwm##offset##_en (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326{ \
327 return get_pwm_en(dev, buf, offset - 1); \
328} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400329static ssize_t set_pwm##offset##_en (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 const char *buf, size_t count) \
331{ \
332 return set_pwm_en(dev, buf, count, offset - 1); \
333} \
334static DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan##offset, \
335 NULL); \
336static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
337 get_fan##offset##_min, set_fan##offset##_min); \
338static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
339 get_fan##offset##_div, set_fan##offset##_div); \
340static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
341 get_pwm##offset, set_pwm##offset); \
342static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
343 get_pwm##offset##_en, set_pwm##offset##_en);
344
345fan_present(1);
346fan_present(2);
347
348static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
349
Jean Delvaree6cfb3a2005-07-27 21:32:02 +0200350static int __init smsc47m1_find(unsigned short *addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351{
352 u8 val;
353
354 superio_enter();
355 val = superio_inb(SUPERIO_REG_DEVID);
356
357 /*
358 * SMSC LPC47M10x/LPC47M13x (device id 0x59), LPC47M14x (device id
359 * 0x5F) and LPC47B27x (device id 0x51) have fan control.
360 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
Jean Delvareec5ce552005-04-26 22:09:43 +0200361 * can do much more besides (device id 0x60).
Jean Delvareb890a072005-10-26 22:21:24 +0200362 * The LPC47M997 is undocumented, but seems to be compatible with
363 * the LPC47M192, and has the same device id.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 */
365 if (val == 0x51)
Jean Delvareec5ce552005-04-26 22:09:43 +0200366 printk(KERN_INFO "smsc47m1: Found SMSC LPC47B27x\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 else if (val == 0x59)
Jean Delvareec5ce552005-04-26 22:09:43 +0200368 printk(KERN_INFO "smsc47m1: Found SMSC LPC47M10x/LPC47M13x\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 else if (val == 0x5F)
Jean Delvareec5ce552005-04-26 22:09:43 +0200370 printk(KERN_INFO "smsc47m1: Found SMSC LPC47M14x\n");
371 else if (val == 0x60)
Jean Delvareb890a072005-10-26 22:21:24 +0200372 printk(KERN_INFO "smsc47m1: Found SMSC "
373 "LPC47M15x/LPC47M192/LPC47M997\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 else {
375 superio_exit();
376 return -ENODEV;
377 }
378
379 superio_select();
Jean Delvare2d8672c2005-07-19 23:56:35 +0200380 *addr = (superio_inb(SUPERIO_REG_BASE) << 8)
381 | superio_inb(SUPERIO_REG_BASE + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 val = superio_inb(SUPERIO_REG_ACT);
Jean Delvare2d8672c2005-07-19 23:56:35 +0200383 if (*addr == 0 || (val & 0x01) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 printk(KERN_INFO "smsc47m1: Device is disabled, will not use\n");
385 superio_exit();
386 return -ENODEV;
387 }
388
389 superio_exit();
390 return 0;
391}
392
Jean Delvare2d8672c2005-07-19 23:56:35 +0200393static int smsc47m1_detect(struct i2c_adapter *adapter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394{
395 struct i2c_client *new_client;
396 struct smsc47m1_data *data;
397 int err = 0;
398 int fan1, fan2, pwm1, pwm2;
399
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100400 if (!request_region(address, SMSC_EXTENT, smsc47m1_driver.driver.name)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 dev_err(&adapter->dev, "Region 0x%x already in use!\n", address);
402 return -EBUSY;
403 }
404
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200405 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 err = -ENOMEM;
407 goto error_release;
408 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409
410 new_client = &data->client;
411 i2c_set_clientdata(new_client, data);
412 new_client->addr = address;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100413 mutex_init(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 new_client->adapter = adapter;
415 new_client->driver = &smsc47m1_driver;
416 new_client->flags = 0;
417
418 strlcpy(new_client->name, "smsc47m1", I2C_NAME_SIZE);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100419 mutex_init(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420
421 /* If no function is properly configured, there's no point in
422 actually registering the chip. */
423 fan1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0)) & 0x05)
424 == 0x05;
425 fan2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1)) & 0x05)
426 == 0x05;
427 pwm1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(0)) & 0x05)
428 == 0x04;
429 pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05)
430 == 0x04;
431 if (!(fan1 || fan2 || pwm1 || pwm2)) {
432 dev_warn(&new_client->dev, "Device is not configured, will not use\n");
433 err = -ENODEV;
434 goto error_free;
435 }
436
437 if ((err = i2c_attach_client(new_client)))
438 goto error_free;
439
440 /* Some values (fan min, clock dividers, pwm registers) may be
441 needed before any update is triggered, so we better read them
442 at least once here. We don't usually do it that way, but in
443 this particular case, manually reading 5 registers out of 8
444 doesn't make much sense and we're better using the existing
445 function. */
446 smsc47m1_update_device(&new_client->dev, 1);
447
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400448 /* Register sysfs hooks */
449 data->class_dev = hwmon_device_register(&new_client->dev);
450 if (IS_ERR(data->class_dev)) {
451 err = PTR_ERR(data->class_dev);
452 goto error_detach;
453 }
454
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 if (fan1) {
456 device_create_file(&new_client->dev, &dev_attr_fan1_input);
457 device_create_file(&new_client->dev, &dev_attr_fan1_min);
458 device_create_file(&new_client->dev, &dev_attr_fan1_div);
459 } else
460 dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, "
461 "skipping\n");
462
463 if (fan2) {
464 device_create_file(&new_client->dev, &dev_attr_fan2_input);
465 device_create_file(&new_client->dev, &dev_attr_fan2_min);
466 device_create_file(&new_client->dev, &dev_attr_fan2_div);
467 } else
468 dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, "
469 "skipping\n");
470
471 if (pwm1) {
472 device_create_file(&new_client->dev, &dev_attr_pwm1);
473 device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
474 } else
475 dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, "
476 "skipping\n");
477 if (pwm2) {
478 device_create_file(&new_client->dev, &dev_attr_pwm2);
479 device_create_file(&new_client->dev, &dev_attr_pwm2_enable);
480 } else
481 dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, "
482 "skipping\n");
483
484 device_create_file(&new_client->dev, &dev_attr_alarms);
485
486 return 0;
487
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400488error_detach:
489 i2c_detach_client(new_client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490error_free:
Alexey Dobriyan1f57ff82005-08-26 01:49:14 +0400491 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492error_release:
493 release_region(address, SMSC_EXTENT);
494 return err;
495}
496
497static int smsc47m1_detach_client(struct i2c_client *client)
498{
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400499 struct smsc47m1_data *data = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 int err;
501
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400502 hwmon_device_unregister(data->class_dev);
503
Jean Delvare7bef5592005-07-27 22:14:49 +0200504 if ((err = i2c_detach_client(client)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
507 release_region(client->addr, SMSC_EXTENT);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400508 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
510 return 0;
511}
512
513static int smsc47m1_read_value(struct i2c_client *client, u8 reg)
514{
515 int res;
516
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100517 mutex_lock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 res = inb_p(client->addr + reg);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100519 mutex_unlock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 return res;
521}
522
523static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value)
524{
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100525 mutex_lock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 outb_p(value, client->addr + reg);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100527 mutex_unlock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528}
529
530static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
531 int init)
532{
533 struct i2c_client *client = to_i2c_client(dev);
534 struct smsc47m1_data *data = i2c_get_clientdata(client);
535
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100536 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
538 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
539 int i;
540
541 for (i = 0; i < 2; i++) {
542 data->fan[i] = smsc47m1_read_value(client,
543 SMSC47M1_REG_FAN(i));
544 data->fan_preload[i] = smsc47m1_read_value(client,
545 SMSC47M1_REG_FAN_PRELOAD(i));
546 data->pwm[i] = smsc47m1_read_value(client,
547 SMSC47M1_REG_PWM(i));
548 }
549
550 i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV);
551 data->fan_div[0] = (i >> 4) & 0x03;
552 data->fan_div[1] = i >> 6;
553
554 data->alarms = smsc47m1_read_value(client,
555 SMSC47M1_REG_ALARM) >> 6;
556 /* Clear alarms if needed */
557 if (data->alarms)
558 smsc47m1_write_value(client, SMSC47M1_REG_ALARM, 0xC0);
559
560 data->last_updated = jiffies;
561 }
562
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100563 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 return data;
565}
566
567static int __init sm_smsc47m1_init(void)
568{
Jean Delvare2d8672c2005-07-19 23:56:35 +0200569 if (smsc47m1_find(&address)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 return -ENODEV;
571 }
572
Jean Delvarefde09502005-07-19 23:51:07 +0200573 return i2c_isa_add_driver(&smsc47m1_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574}
575
576static void __exit sm_smsc47m1_exit(void)
577{
Jean Delvarefde09502005-07-19 23:51:07 +0200578 i2c_isa_del_driver(&smsc47m1_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579}
580
581MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
582MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
583MODULE_LICENSE("GPL");
584
585module_init(sm_smsc47m1_init);
586module_exit(sm_smsc47m1_exit);