blob: 897117a7213f0624a04e55deaaf46de38aaab88e [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
5 Supports the SMSC LPC47B27x, LPC47M10x, LPC47M13x and LPC47M14x
6 Super-I/O chips.
7
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>
33#include <linux/i2c-sensor.h>
34#include <linux/init.h>
35#include <asm/io.h>
36
37static unsigned short normal_i2c[] = { I2C_CLIENT_END };
38/* Address is autodetected, there is no default value */
39static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
40static struct i2c_force_data forces[] = {{NULL}};
41
42enum chips { any_chip, smsc47m1 };
43static struct i2c_address_data addr_data = {
44 .normal_i2c = normal_i2c,
45 .normal_isa = normal_isa,
46 .forces = forces,
47};
48
49/* Super-I/0 registers and commands */
50
51#define REG 0x2e /* The register to read/write */
52#define VAL 0x2f /* The value to read/write */
53
54static inline void
55superio_outb(int reg, int val)
56{
57 outb(reg, REG);
58 outb(val, VAL);
59}
60
61static inline int
62superio_inb(int reg)
63{
64 outb(reg, REG);
65 return inb(VAL);
66}
67
68/* logical device for fans is 0x0A */
69#define superio_select() superio_outb(0x07, 0x0A)
70
71static inline void
72superio_enter(void)
73{
74 outb(0x55, REG);
75}
76
77static inline void
78superio_exit(void)
79{
80 outb(0xAA, REG);
81}
82
83#define SUPERIO_REG_ACT 0x30
84#define SUPERIO_REG_BASE 0x60
85#define SUPERIO_REG_DEVID 0x20
86
87/* Logical device registers */
88
89#define SMSC_EXTENT 0x80
90
91/* nr is 0 or 1 in the macros below */
92#define SMSC47M1_REG_ALARM 0x04
93#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr))
94#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr))
95#define SMSC47M1_REG_PWM(nr) (0x56 + (nr))
96#define SMSC47M1_REG_FANDIV 0x58
97#define SMSC47M1_REG_FAN(nr) (0x59 + (nr))
98#define SMSC47M1_REG_FAN_PRELOAD(nr) (0x5B + (nr))
99
100#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \
101 983040/((192-(reg))*(div)))
102#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \
103 983040/(((reg)-(preload))*(div)))
104#define DIV_FROM_REG(reg) (1 << (reg))
105#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1)
106#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01)
107#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E)
108
109struct smsc47m1_data {
110 struct i2c_client client;
111 struct semaphore lock;
112
113 struct semaphore update_lock;
114 unsigned long last_updated; /* In jiffies */
115
116 u8 fan[2]; /* Register value */
117 u8 fan_preload[2]; /* Register value */
118 u8 fan_div[2]; /* Register encoding, shifted right */
119 u8 alarms; /* Register encoding */
120 u8 pwm[2]; /* Register value (bit 7 is enable) */
121};
122
123
124static int smsc47m1_attach_adapter(struct i2c_adapter *adapter);
125static int smsc47m1_find(int *address);
126static int smsc47m1_detect(struct i2c_adapter *adapter, int address, int kind);
127static int smsc47m1_detach_client(struct i2c_client *client);
128
129static int smsc47m1_read_value(struct i2c_client *client, u8 reg);
130static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value);
131
132static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
133 int init);
134
135
136static struct i2c_driver smsc47m1_driver = {
137 .owner = THIS_MODULE,
138 .name = "smsc47m1",
139 .id = I2C_DRIVERID_SMSC47M1,
140 .flags = I2C_DF_NOTIFY,
141 .attach_adapter = smsc47m1_attach_adapter,
142 .detach_client = smsc47m1_detach_client,
143};
144
145/* nr is 0 or 1 in the callback functions below */
146
147static ssize_t get_fan(struct device *dev, char *buf, int nr)
148{
149 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
150 /* This chip (stupidly) stops monitoring fan speed if PWM is
151 enabled and duty cycle is 0%. This is fine if the monitoring
152 and control concern the same fan, but troublesome if they are
153 not (which could as well happen). */
154 int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
155 FAN_FROM_REG(data->fan[nr],
156 DIV_FROM_REG(data->fan_div[nr]),
157 data->fan_preload[nr]);
158 return sprintf(buf, "%d\n", rpm);
159}
160
161static ssize_t get_fan_min(struct device *dev, char *buf, int nr)
162{
163 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
164 int rpm = MIN_FROM_REG(data->fan_preload[nr],
165 DIV_FROM_REG(data->fan_div[nr]));
166 return sprintf(buf, "%d\n", rpm);
167}
168
169static ssize_t get_fan_div(struct device *dev, char *buf, int nr)
170{
171 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
172 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
173}
174
175static ssize_t get_pwm(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_FROM_REG(data->pwm[nr]));
179}
180
181static ssize_t get_pwm_en(struct device *dev, char *buf, int nr)
182{
183 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
184 return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[nr]));
185}
186
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400187static ssize_t get_alarms(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188{
189 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
190 return sprintf(buf, "%d\n", data->alarms);
191}
192
193static ssize_t set_fan_min(struct device *dev, const char *buf,
194 size_t count, int nr)
195{
196 struct i2c_client *client = to_i2c_client(dev);
197 struct smsc47m1_data *data = i2c_get_clientdata(client);
198 long rpmdiv, val = simple_strtol(buf, NULL, 10);
199
200 down(&data->update_lock);
201 rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
202
203 if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
204 up(&data->update_lock);
205 return -EINVAL;
206 }
207
208 data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
209 smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr),
210 data->fan_preload[nr]);
211 up(&data->update_lock);
212
213 return count;
214}
215
216/* Note: we save and restore the fan minimum here, because its value is
217 determined in part by the fan clock divider. This follows the principle
218 of least suprise; the user doesn't expect the fan minimum to change just
219 because the divider changed. */
220static ssize_t set_fan_div(struct device *dev, const char *buf,
221 size_t count, int nr)
222{
223 struct i2c_client *client = to_i2c_client(dev);
224 struct smsc47m1_data *data = i2c_get_clientdata(client);
225
226 long new_div = simple_strtol(buf, NULL, 10), tmp;
227 u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
228
229 if (new_div == old_div) /* No change */
230 return count;
231
232 down(&data->update_lock);
233 switch (new_div) {
234 case 1: data->fan_div[nr] = 0; break;
235 case 2: data->fan_div[nr] = 1; break;
236 case 4: data->fan_div[nr] = 2; break;
237 case 8: data->fan_div[nr] = 3; break;
238 default:
239 up(&data->update_lock);
240 return -EINVAL;
241 }
242
243 tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV) & 0x0F;
244 tmp |= (data->fan_div[0] << 4) | (data->fan_div[1] << 6);
245 smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp);
246
247 /* Preserve fan min */
248 tmp = 192 - (old_div * (192 - data->fan_preload[nr])
249 + new_div / 2) / new_div;
250 data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
251 smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr),
252 data->fan_preload[nr]);
253 up(&data->update_lock);
254
255 return count;
256}
257
258static ssize_t set_pwm(struct device *dev, const char *buf,
259 size_t count, int nr)
260{
261 struct i2c_client *client = to_i2c_client(dev);
262 struct smsc47m1_data *data = i2c_get_clientdata(client);
263
264 long val = simple_strtol(buf, NULL, 10);
265
266 if (val < 0 || val > 255)
267 return -EINVAL;
268
269 down(&data->update_lock);
270 data->pwm[nr] &= 0x81; /* Preserve additional bits */
271 data->pwm[nr] |= PWM_TO_REG(val);
272 smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr),
273 data->pwm[nr]);
274 up(&data->update_lock);
275
276 return count;
277}
278
279static ssize_t set_pwm_en(struct device *dev, const char *buf,
280 size_t count, int nr)
281{
282 struct i2c_client *client = to_i2c_client(dev);
283 struct smsc47m1_data *data = i2c_get_clientdata(client);
284
285 long val = simple_strtol(buf, NULL, 10);
286
287 if (val != 0 && val != 1)
288 return -EINVAL;
289
290 down(&data->update_lock);
291 data->pwm[nr] &= 0xFE; /* preserve the other bits */
292 data->pwm[nr] |= !val;
293 smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr),
294 data->pwm[nr]);
295 up(&data->update_lock);
296
297 return count;
298}
299
300#define fan_present(offset) \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400301static ssize_t get_fan##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302{ \
303 return get_fan(dev, buf, offset - 1); \
304} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400305static ssize_t get_fan##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306{ \
307 return get_fan_min(dev, buf, offset - 1); \
308} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400309static ssize_t set_fan##offset##_min (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 const char *buf, size_t count) \
311{ \
312 return set_fan_min(dev, buf, count, offset - 1); \
313} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400314static ssize_t get_fan##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315{ \
316 return get_fan_div(dev, buf, offset - 1); \
317} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400318static ssize_t set_fan##offset##_div (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 const char *buf, size_t count) \
320{ \
321 return set_fan_div(dev, buf, count, offset - 1); \
322} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400323static ssize_t get_pwm##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324{ \
325 return get_pwm(dev, buf, offset - 1); \
326} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400327static ssize_t set_pwm##offset (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 const char *buf, size_t count) \
329{ \
330 return set_pwm(dev, buf, count, offset - 1); \
331} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400332static ssize_t get_pwm##offset##_en (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{ \
334 return get_pwm_en(dev, buf, offset - 1); \
335} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400336static ssize_t set_pwm##offset##_en (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 const char *buf, size_t count) \
338{ \
339 return set_pwm_en(dev, buf, count, offset - 1); \
340} \
341static DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan##offset, \
342 NULL); \
343static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
344 get_fan##offset##_min, set_fan##offset##_min); \
345static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
346 get_fan##offset##_div, set_fan##offset##_div); \
347static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
348 get_pwm##offset, set_pwm##offset); \
349static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
350 get_pwm##offset##_en, set_pwm##offset##_en);
351
352fan_present(1);
353fan_present(2);
354
355static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
356
357static int smsc47m1_attach_adapter(struct i2c_adapter *adapter)
358{
359 if (!(adapter->class & I2C_CLASS_HWMON))
360 return 0;
361 return i2c_detect(adapter, &addr_data, smsc47m1_detect);
362}
363
364static int smsc47m1_find(int *address)
365{
366 u8 val;
367
368 superio_enter();
369 val = superio_inb(SUPERIO_REG_DEVID);
370
371 /*
372 * SMSC LPC47M10x/LPC47M13x (device id 0x59), LPC47M14x (device id
373 * 0x5F) and LPC47B27x (device id 0x51) have fan control.
374 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
Jean Delvareec5ce552005-04-26 22:09:43 +0200375 * can do much more besides (device id 0x60).
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 */
377 if (val == 0x51)
Jean Delvareec5ce552005-04-26 22:09:43 +0200378 printk(KERN_INFO "smsc47m1: Found SMSC LPC47B27x\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 else if (val == 0x59)
Jean Delvareec5ce552005-04-26 22:09:43 +0200380 printk(KERN_INFO "smsc47m1: Found SMSC LPC47M10x/LPC47M13x\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 else if (val == 0x5F)
Jean Delvareec5ce552005-04-26 22:09:43 +0200382 printk(KERN_INFO "smsc47m1: Found SMSC LPC47M14x\n");
383 else if (val == 0x60)
384 printk(KERN_INFO "smsc47m1: Found SMSC LPC47M15x/LPC47M192\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 else {
386 superio_exit();
387 return -ENODEV;
388 }
389
390 superio_select();
391 *address = (superio_inb(SUPERIO_REG_BASE) << 8)
392 | superio_inb(SUPERIO_REG_BASE + 1);
393 val = superio_inb(SUPERIO_REG_ACT);
394 if (*address == 0 || (val & 0x01) == 0) {
395 printk(KERN_INFO "smsc47m1: Device is disabled, will not use\n");
396 superio_exit();
397 return -ENODEV;
398 }
399
400 superio_exit();
401 return 0;
402}
403
404static int smsc47m1_detect(struct i2c_adapter *adapter, int address, int kind)
405{
406 struct i2c_client *new_client;
407 struct smsc47m1_data *data;
408 int err = 0;
409 int fan1, fan2, pwm1, pwm2;
410
411 if (!i2c_is_isa_adapter(adapter)) {
412 return 0;
413 }
414
415 if (!request_region(address, SMSC_EXTENT, smsc47m1_driver.name)) {
416 dev_err(&adapter->dev, "Region 0x%x already in use!\n", address);
417 return -EBUSY;
418 }
419
420 if (!(data = kmalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
421 err = -ENOMEM;
422 goto error_release;
423 }
424 memset(data, 0x00, sizeof(struct smsc47m1_data));
425
426 new_client = &data->client;
427 i2c_set_clientdata(new_client, data);
428 new_client->addr = address;
429 init_MUTEX(&data->lock);
430 new_client->adapter = adapter;
431 new_client->driver = &smsc47m1_driver;
432 new_client->flags = 0;
433
434 strlcpy(new_client->name, "smsc47m1", I2C_NAME_SIZE);
435 init_MUTEX(&data->update_lock);
436
437 /* If no function is properly configured, there's no point in
438 actually registering the chip. */
439 fan1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0)) & 0x05)
440 == 0x05;
441 fan2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1)) & 0x05)
442 == 0x05;
443 pwm1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(0)) & 0x05)
444 == 0x04;
445 pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05)
446 == 0x04;
447 if (!(fan1 || fan2 || pwm1 || pwm2)) {
448 dev_warn(&new_client->dev, "Device is not configured, will not use\n");
449 err = -ENODEV;
450 goto error_free;
451 }
452
453 if ((err = i2c_attach_client(new_client)))
454 goto error_free;
455
456 /* Some values (fan min, clock dividers, pwm registers) may be
457 needed before any update is triggered, so we better read them
458 at least once here. We don't usually do it that way, but in
459 this particular case, manually reading 5 registers out of 8
460 doesn't make much sense and we're better using the existing
461 function. */
462 smsc47m1_update_device(&new_client->dev, 1);
463
464 if (fan1) {
465 device_create_file(&new_client->dev, &dev_attr_fan1_input);
466 device_create_file(&new_client->dev, &dev_attr_fan1_min);
467 device_create_file(&new_client->dev, &dev_attr_fan1_div);
468 } else
469 dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, "
470 "skipping\n");
471
472 if (fan2) {
473 device_create_file(&new_client->dev, &dev_attr_fan2_input);
474 device_create_file(&new_client->dev, &dev_attr_fan2_min);
475 device_create_file(&new_client->dev, &dev_attr_fan2_div);
476 } else
477 dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, "
478 "skipping\n");
479
480 if (pwm1) {
481 device_create_file(&new_client->dev, &dev_attr_pwm1);
482 device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
483 } else
484 dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, "
485 "skipping\n");
486 if (pwm2) {
487 device_create_file(&new_client->dev, &dev_attr_pwm2);
488 device_create_file(&new_client->dev, &dev_attr_pwm2_enable);
489 } else
490 dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, "
491 "skipping\n");
492
493 device_create_file(&new_client->dev, &dev_attr_alarms);
494
495 return 0;
496
497error_free:
498 kfree(new_client);
499error_release:
500 release_region(address, SMSC_EXTENT);
501 return err;
502}
503
504static int smsc47m1_detach_client(struct i2c_client *client)
505{
506 int err;
507
508 if ((err = i2c_detach_client(client))) {
509 dev_err(&client->dev, "Client deregistration failed, "
510 "client not detached.\n");
511 return err;
512 }
513
514 release_region(client->addr, SMSC_EXTENT);
515 kfree(i2c_get_clientdata(client));
516
517 return 0;
518}
519
520static int smsc47m1_read_value(struct i2c_client *client, u8 reg)
521{
522 int res;
523
524 down(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
525 res = inb_p(client->addr + reg);
526 up(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
527 return res;
528}
529
530static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value)
531{
532 down(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
533 outb_p(value, client->addr + reg);
534 up(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
535}
536
537static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
538 int init)
539{
540 struct i2c_client *client = to_i2c_client(dev);
541 struct smsc47m1_data *data = i2c_get_clientdata(client);
542
543 down(&data->update_lock);
544
545 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
546 int i;
547
548 for (i = 0; i < 2; i++) {
549 data->fan[i] = smsc47m1_read_value(client,
550 SMSC47M1_REG_FAN(i));
551 data->fan_preload[i] = smsc47m1_read_value(client,
552 SMSC47M1_REG_FAN_PRELOAD(i));
553 data->pwm[i] = smsc47m1_read_value(client,
554 SMSC47M1_REG_PWM(i));
555 }
556
557 i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV);
558 data->fan_div[0] = (i >> 4) & 0x03;
559 data->fan_div[1] = i >> 6;
560
561 data->alarms = smsc47m1_read_value(client,
562 SMSC47M1_REG_ALARM) >> 6;
563 /* Clear alarms if needed */
564 if (data->alarms)
565 smsc47m1_write_value(client, SMSC47M1_REG_ALARM, 0xC0);
566
567 data->last_updated = jiffies;
568 }
569
570 up(&data->update_lock);
571 return data;
572}
573
574static int __init sm_smsc47m1_init(void)
575{
576 if (smsc47m1_find(normal_isa)) {
577 return -ENODEV;
578 }
579
580 return i2c_add_driver(&smsc47m1_driver);
581}
582
583static void __exit sm_smsc47m1_exit(void)
584{
585 i2c_del_driver(&smsc47m1_driver);
586}
587
588MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
589MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
590MODULE_LICENSE("GPL");
591
592module_init(sm_smsc47m1_init);
593module_exit(sm_smsc47m1_exit);