blob: 16762cca87e54a04e42a88424a415ba86d08bda6 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 smsc47m1.c - Part of lm_sensors, Linux kernel modules
3 for hardware monitoring
4
Jean Delvare60917802006-10-08 22:00:44 +02005 Supports the SMSC LPC47B27x, LPC47M10x, LPC47M112, LPC47M13x,
Jean Delvare8eccbb62007-05-08 17:21:59 +02006 LPC47M14x, LPC47M15x, LPC47M192, LPC47M292 and LPC47M997
7 Super-I/O chips.
Linus Torvalds1da177e2005-04-16 15:20:36 -07008
9 Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
Jean Delvare8eccbb62007-05-08 17:21:59 +020010 Copyright (C) 2004-2007 Jean Delvare <khali@linux-fr.org>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 Ported to Linux 2.6 by Gabriele Gorla <gorlik@yahoo.com>
12 and Jean Delvare
13
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27*/
28
29#include <linux/module.h>
30#include <linux/slab.h>
31#include <linux/ioport.h>
32#include <linux/jiffies.h>
33#include <linux/i2c.h>
Jean Delvarefde09502005-07-19 23:51:07 +020034#include <linux/i2c-isa.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040035#include <linux/hwmon.h>
36#include <linux/err.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/init.h>
Ingo Molnar9a61bf62006-01-18 23:19:26 +010038#include <linux/mutex.h>
Jean Delvarece8c6ce12006-09-24 21:25:12 +020039#include <linux/sysfs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <asm/io.h>
41
Linus Torvalds1da177e2005-04-16 15:20:36 -070042/* Address is autodetected, there is no default value */
Jean Delvare2d8672c2005-07-19 23:56:35 +020043static unsigned short address;
Jean Delvare8eccbb62007-05-08 17:21:59 +020044static u8 devid;
45enum chips { smsc47m1, smsc47m2 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
47/* Super-I/0 registers and commands */
48
49#define REG 0x2e /* The register to read/write */
50#define VAL 0x2f /* The value to read/write */
51
52static inline void
53superio_outb(int reg, int val)
54{
55 outb(reg, REG);
56 outb(val, VAL);
57}
58
59static inline int
60superio_inb(int reg)
61{
62 outb(reg, REG);
63 return inb(VAL);
64}
65
66/* logical device for fans is 0x0A */
67#define superio_select() superio_outb(0x07, 0x0A)
68
69static inline void
70superio_enter(void)
71{
72 outb(0x55, REG);
73}
74
75static inline void
76superio_exit(void)
77{
78 outb(0xAA, REG);
79}
80
81#define SUPERIO_REG_ACT 0x30
82#define SUPERIO_REG_BASE 0x60
83#define SUPERIO_REG_DEVID 0x20
84
85/* Logical device registers */
86
87#define SMSC_EXTENT 0x80
88
89/* nr is 0 or 1 in the macros below */
90#define SMSC47M1_REG_ALARM 0x04
91#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr))
92#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr))
Linus Torvalds1da177e2005-04-16 15:20:36 -070093#define SMSC47M1_REG_FANDIV 0x58
Jean Delvare8eccbb62007-05-08 17:21:59 +020094
95static const u8 SMSC47M1_REG_FAN[3] = { 0x59, 0x5a, 0x6b };
96static const u8 SMSC47M1_REG_FAN_PRELOAD[3] = { 0x5b, 0x5c, 0x6c };
97static const u8 SMSC47M1_REG_PWM[3] = { 0x56, 0x57, 0x69 };
98
99#define SMSC47M2_REG_ALARM6 0x09
100#define SMSC47M2_REG_TPIN1 0x38
101#define SMSC47M2_REG_TPIN2 0x37
102#define SMSC47M2_REG_TPIN3 0x2d
103#define SMSC47M2_REG_PPIN3 0x2c
104#define SMSC47M2_REG_FANDIV3 0x6a
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105
106#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \
107 983040/((192-(reg))*(div)))
108#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \
109 983040/(((reg)-(preload))*(div)))
110#define DIV_FROM_REG(reg) (1 << (reg))
111#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1)
112#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01)
113#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E)
114
115struct smsc47m1_data {
116 struct i2c_client client;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200117 enum chips type;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400118 struct class_device *class_dev;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100119 struct mutex lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100121 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 unsigned long last_updated; /* In jiffies */
123
Jean Delvare8eccbb62007-05-08 17:21:59 +0200124 u8 fan[3]; /* Register value */
125 u8 fan_preload[3]; /* Register value */
126 u8 fan_div[3]; /* Register encoding, shifted right */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 u8 alarms; /* Register encoding */
Jean Delvare8eccbb62007-05-08 17:21:59 +0200128 u8 pwm[3]; /* Register value (bit 0 is disable) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129};
130
131
Jean Delvare2d8672c2005-07-19 23:56:35 +0200132static int smsc47m1_detect(struct i2c_adapter *adapter);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133static int smsc47m1_detach_client(struct i2c_client *client);
134
135static int smsc47m1_read_value(struct i2c_client *client, u8 reg);
136static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value);
137
138static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
139 int init);
140
141
142static struct i2c_driver smsc47m1_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100143 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200144 .owner = THIS_MODULE,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100145 .name = "smsc47m1",
146 },
Jean Delvare2d8672c2005-07-19 23:56:35 +0200147 .attach_adapter = smsc47m1_detect,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 .detach_client = smsc47m1_detach_client,
149};
150
151/* nr is 0 or 1 in the callback functions below */
152
153static ssize_t get_fan(struct device *dev, char *buf, int nr)
154{
155 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
156 /* This chip (stupidly) stops monitoring fan speed if PWM is
157 enabled and duty cycle is 0%. This is fine if the monitoring
158 and control concern the same fan, but troublesome if they are
159 not (which could as well happen). */
160 int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
161 FAN_FROM_REG(data->fan[nr],
162 DIV_FROM_REG(data->fan_div[nr]),
163 data->fan_preload[nr]);
164 return sprintf(buf, "%d\n", rpm);
165}
166
167static ssize_t get_fan_min(struct device *dev, char *buf, int nr)
168{
169 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
170 int rpm = MIN_FROM_REG(data->fan_preload[nr],
171 DIV_FROM_REG(data->fan_div[nr]));
172 return sprintf(buf, "%d\n", rpm);
173}
174
175static ssize_t get_fan_div(struct device *dev, char *buf, int nr)
176{
177 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
178 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
179}
180
181static ssize_t get_pwm(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_FROM_REG(data->pwm[nr]));
185}
186
187static ssize_t get_pwm_en(struct device *dev, char *buf, int nr)
188{
189 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
190 return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[nr]));
191}
192
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400193static ssize_t get_alarms(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194{
195 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
196 return sprintf(buf, "%d\n", data->alarms);
197}
198
199static ssize_t set_fan_min(struct device *dev, const char *buf,
200 size_t count, int nr)
201{
202 struct i2c_client *client = to_i2c_client(dev);
203 struct smsc47m1_data *data = i2c_get_clientdata(client);
204 long rpmdiv, val = simple_strtol(buf, NULL, 10);
205
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100206 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
208
209 if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100210 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 return -EINVAL;
212 }
213
214 data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200215 smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100217 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218
219 return count;
220}
221
222/* Note: we save and restore the fan minimum here, because its value is
223 determined in part by the fan clock divider. This follows the principle
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200224 of least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 because the divider changed. */
226static ssize_t set_fan_div(struct device *dev, const char *buf,
227 size_t count, int nr)
228{
229 struct i2c_client *client = to_i2c_client(dev);
230 struct smsc47m1_data *data = i2c_get_clientdata(client);
231
232 long new_div = simple_strtol(buf, NULL, 10), tmp;
233 u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
234
235 if (new_div == old_div) /* No change */
236 return count;
237
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100238 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 switch (new_div) {
240 case 1: data->fan_div[nr] = 0; break;
241 case 2: data->fan_div[nr] = 1; break;
242 case 4: data->fan_div[nr] = 2; break;
243 case 8: data->fan_div[nr] = 3; break;
244 default:
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100245 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 return -EINVAL;
247 }
248
Jean Delvare8eccbb62007-05-08 17:21:59 +0200249 switch (nr) {
250 case 0:
251 case 1:
252 tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV)
253 & ~(0x03 << (4 + 2 * nr));
254 tmp |= data->fan_div[nr] << (4 + 2 * nr);
255 smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp);
256 break;
257 case 2:
258 tmp = smsc47m1_read_value(client, SMSC47M2_REG_FANDIV3) & 0xCF;
259 tmp |= data->fan_div[2] << 4;
260 smsc47m1_write_value(client, SMSC47M2_REG_FANDIV3, tmp);
261 break;
262 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
264 /* Preserve fan min */
265 tmp = 192 - (old_div * (192 - data->fan_preload[nr])
266 + new_div / 2) / new_div;
267 data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200268 smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100270 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271
272 return count;
273}
274
275static ssize_t set_pwm(struct device *dev, const char *buf,
276 size_t count, int nr)
277{
278 struct i2c_client *client = to_i2c_client(dev);
279 struct smsc47m1_data *data = i2c_get_clientdata(client);
280
281 long val = simple_strtol(buf, NULL, 10);
282
283 if (val < 0 || val > 255)
284 return -EINVAL;
285
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100286 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 data->pwm[nr] &= 0x81; /* Preserve additional bits */
288 data->pwm[nr] |= PWM_TO_REG(val);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200289 smsc47m1_write_value(client, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100291 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
293 return count;
294}
295
296static ssize_t set_pwm_en(struct device *dev, const char *buf,
297 size_t count, int nr)
298{
299 struct i2c_client *client = to_i2c_client(dev);
300 struct smsc47m1_data *data = i2c_get_clientdata(client);
301
302 long val = simple_strtol(buf, NULL, 10);
303
304 if (val != 0 && val != 1)
305 return -EINVAL;
306
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100307 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 data->pwm[nr] &= 0xFE; /* preserve the other bits */
309 data->pwm[nr] |= !val;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200310 smsc47m1_write_value(client, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100312 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313
314 return count;
315}
316
317#define fan_present(offset) \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400318static ssize_t get_fan##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319{ \
320 return get_fan(dev, buf, offset - 1); \
321} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400322static ssize_t get_fan##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323{ \
324 return get_fan_min(dev, buf, offset - 1); \
325} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400326static ssize_t set_fan##offset##_min (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 const char *buf, size_t count) \
328{ \
329 return set_fan_min(dev, buf, count, offset - 1); \
330} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400331static ssize_t get_fan##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332{ \
333 return get_fan_div(dev, buf, offset - 1); \
334} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400335static ssize_t set_fan##offset##_div (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 const char *buf, size_t count) \
337{ \
338 return set_fan_div(dev, buf, count, offset - 1); \
339} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400340static ssize_t get_pwm##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341{ \
342 return get_pwm(dev, buf, offset - 1); \
343} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400344static ssize_t set_pwm##offset (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 const char *buf, size_t count) \
346{ \
347 return set_pwm(dev, buf, count, offset - 1); \
348} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400349static ssize_t get_pwm##offset##_en (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350{ \
351 return get_pwm_en(dev, buf, offset - 1); \
352} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400353static ssize_t set_pwm##offset##_en (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 const char *buf, size_t count) \
355{ \
356 return set_pwm_en(dev, buf, count, offset - 1); \
357} \
358static DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan##offset, \
359 NULL); \
360static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
361 get_fan##offset##_min, set_fan##offset##_min); \
362static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
363 get_fan##offset##_div, set_fan##offset##_div); \
364static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
365 get_pwm##offset, set_pwm##offset); \
366static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
367 get_pwm##offset##_en, set_pwm##offset##_en);
368
369fan_present(1);
370fan_present(2);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200371fan_present(3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372
373static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
374
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200375/* Almost all sysfs files may or may not be created depending on the chip
376 setup so we create them individually. It is still convenient to define a
377 group to remove them all at once. */
378static struct attribute *smsc47m1_attributes[] = {
379 &dev_attr_fan1_input.attr,
380 &dev_attr_fan1_min.attr,
381 &dev_attr_fan1_div.attr,
382 &dev_attr_fan2_input.attr,
383 &dev_attr_fan2_min.attr,
384 &dev_attr_fan2_div.attr,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200385 &dev_attr_fan3_input.attr,
386 &dev_attr_fan3_min.attr,
387 &dev_attr_fan3_div.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200388
389 &dev_attr_pwm1.attr,
390 &dev_attr_pwm1_enable.attr,
391 &dev_attr_pwm2.attr,
392 &dev_attr_pwm2_enable.attr,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200393 &dev_attr_pwm3.attr,
394 &dev_attr_pwm3_enable.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200395
396 &dev_attr_alarms.attr,
397 NULL
398};
399
400static const struct attribute_group smsc47m1_group = {
401 .attrs = smsc47m1_attributes,
402};
403
Jean Delvaree6cfb3a2005-07-27 21:32:02 +0200404static int __init smsc47m1_find(unsigned short *addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405{
406 u8 val;
407
408 superio_enter();
Jean Delvare8eccbb62007-05-08 17:21:59 +0200409 devid = superio_inb(SUPERIO_REG_DEVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
411 /*
Jean Delvare60917802006-10-08 22:00:44 +0200412 * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
413 * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
Jean Delvareec5ce552005-04-26 22:09:43 +0200415 * can do much more besides (device id 0x60).
Jean Delvareb890a072005-10-26 22:21:24 +0200416 * The LPC47M997 is undocumented, but seems to be compatible with
417 * the LPC47M192, and has the same device id.
Jean Delvare8eccbb62007-05-08 17:21:59 +0200418 * The LPC47M292 (device id 0x6B) is somewhat compatible, but it
419 * supports a 3rd fan, and the pin configuration registers are
420 * unfortunately different.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 */
Jean Delvare8eccbb62007-05-08 17:21:59 +0200422 switch (devid) {
423 case 0x51:
Jean Delvareec5ce552005-04-26 22:09:43 +0200424 printk(KERN_INFO "smsc47m1: Found SMSC LPC47B27x\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200425 break;
426 case 0x59:
Jean Delvare60917802006-10-08 22:00:44 +0200427 printk(KERN_INFO "smsc47m1: Found SMSC "
428 "LPC47M10x/LPC47M112/LPC47M13x\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200429 break;
430 case 0x5F:
Jean Delvareec5ce552005-04-26 22:09:43 +0200431 printk(KERN_INFO "smsc47m1: Found SMSC LPC47M14x\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200432 break;
433 case 0x60:
Jean Delvareb890a072005-10-26 22:21:24 +0200434 printk(KERN_INFO "smsc47m1: Found SMSC "
435 "LPC47M15x/LPC47M192/LPC47M997\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200436 break;
437 case 0x6B:
438 printk(KERN_INFO "smsc47m1: Found SMSC LPC47M292\n");
439 break;
440 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 superio_exit();
442 return -ENODEV;
443 }
444
445 superio_select();
Jean Delvare2d8672c2005-07-19 23:56:35 +0200446 *addr = (superio_inb(SUPERIO_REG_BASE) << 8)
447 | superio_inb(SUPERIO_REG_BASE + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 val = superio_inb(SUPERIO_REG_ACT);
Jean Delvare2d8672c2005-07-19 23:56:35 +0200449 if (*addr == 0 || (val & 0x01) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 printk(KERN_INFO "smsc47m1: Device is disabled, will not use\n");
451 superio_exit();
452 return -ENODEV;
453 }
454
455 superio_exit();
456 return 0;
457}
458
Jean Delvare2d8672c2005-07-19 23:56:35 +0200459static int smsc47m1_detect(struct i2c_adapter *adapter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460{
461 struct i2c_client *new_client;
462 struct smsc47m1_data *data;
463 int err = 0;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200464 int fan1, fan2, fan3, pwm1, pwm2, pwm3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100466 if (!request_region(address, SMSC_EXTENT, smsc47m1_driver.driver.name)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 dev_err(&adapter->dev, "Region 0x%x already in use!\n", address);
468 return -EBUSY;
469 }
470
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200471 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 err = -ENOMEM;
473 goto error_release;
474 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475
Jean Delvare8eccbb62007-05-08 17:21:59 +0200476 data->type = devid == 0x6B ? smsc47m2 : smsc47m1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 new_client = &data->client;
478 i2c_set_clientdata(new_client, data);
479 new_client->addr = address;
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100480 mutex_init(&data->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 new_client->adapter = adapter;
482 new_client->driver = &smsc47m1_driver;
483 new_client->flags = 0;
484
Jean Delvare8eccbb62007-05-08 17:21:59 +0200485 strlcpy(new_client->name,
486 data->type == smsc47m2 ? "smsc47m2" : "smsc47m1",
487 I2C_NAME_SIZE);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100488 mutex_init(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489
490 /* If no function is properly configured, there's no point in
491 actually registering the chip. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 pwm1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(0)) & 0x05)
493 == 0x04;
494 pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05)
495 == 0x04;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200496 if (data->type == smsc47m2) {
497 fan1 = (smsc47m1_read_value(new_client, SMSC47M2_REG_TPIN1)
498 & 0x0d) == 0x09;
499 fan2 = (smsc47m1_read_value(new_client, SMSC47M2_REG_TPIN2)
500 & 0x0d) == 0x09;
501 fan3 = (smsc47m1_read_value(new_client, SMSC47M2_REG_TPIN3)
502 & 0x0d) == 0x0d;
503 pwm3 = (smsc47m1_read_value(new_client, SMSC47M2_REG_PPIN3)
504 & 0x0d) == 0x08;
505 } else {
506 fan1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0))
507 & 0x05) == 0x05;
508 fan2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1))
509 & 0x05) == 0x05;
510 fan3 = 0;
511 pwm3 = 0;
512 }
513 if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
Jean Delvare0dd76992006-08-28 14:22:34 +0200514 dev_warn(&adapter->dev, "Device at 0x%x is not configured, "
515 "will not use\n", new_client->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 err = -ENODEV;
517 goto error_free;
518 }
519
520 if ((err = i2c_attach_client(new_client)))
521 goto error_free;
522
523 /* Some values (fan min, clock dividers, pwm registers) may be
524 needed before any update is triggered, so we better read them
525 at least once here. We don't usually do it that way, but in
526 this particular case, manually reading 5 registers out of 8
527 doesn't make much sense and we're better using the existing
528 function. */
529 smsc47m1_update_device(&new_client->dev, 1);
530
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400531 /* Register sysfs hooks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 if (fan1) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200533 if ((err = device_create_file(&new_client->dev,
534 &dev_attr_fan1_input))
535 || (err = device_create_file(&new_client->dev,
536 &dev_attr_fan1_min))
537 || (err = device_create_file(&new_client->dev,
538 &dev_attr_fan1_div)))
539 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 } else
541 dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, "
542 "skipping\n");
543
544 if (fan2) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200545 if ((err = device_create_file(&new_client->dev,
546 &dev_attr_fan2_input))
547 || (err = device_create_file(&new_client->dev,
548 &dev_attr_fan2_min))
549 || (err = device_create_file(&new_client->dev,
550 &dev_attr_fan2_div)))
551 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 } else
553 dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, "
554 "skipping\n");
555
Jean Delvare8eccbb62007-05-08 17:21:59 +0200556 if (fan3) {
557 if ((err = device_create_file(&new_client->dev,
558 &dev_attr_fan3_input))
559 || (err = device_create_file(&new_client->dev,
560 &dev_attr_fan3_min))
561 || (err = device_create_file(&new_client->dev,
562 &dev_attr_fan3_div)))
563 goto error_remove_files;
564 } else
565 dev_dbg(&new_client->dev, "Fan 3 not enabled by hardware, "
566 "skipping\n");
567
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 if (pwm1) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200569 if ((err = device_create_file(&new_client->dev,
570 &dev_attr_pwm1))
571 || (err = device_create_file(&new_client->dev,
572 &dev_attr_pwm1_enable)))
573 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 } else
575 dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, "
576 "skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 if (pwm2) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200579 if ((err = device_create_file(&new_client->dev,
580 &dev_attr_pwm2))
581 || (err = device_create_file(&new_client->dev,
582 &dev_attr_pwm2_enable)))
583 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 } else
585 dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, "
586 "skipping\n");
587
Jean Delvare8eccbb62007-05-08 17:21:59 +0200588 if (pwm3) {
589 if ((err = device_create_file(&new_client->dev,
590 &dev_attr_pwm3))
591 || (err = device_create_file(&new_client->dev,
592 &dev_attr_pwm3_enable)))
593 goto error_remove_files;
594 } else
595 dev_dbg(&new_client->dev, "PWM 3 not enabled by hardware, "
596 "skipping\n");
597
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200598 if ((err = device_create_file(&new_client->dev, &dev_attr_alarms)))
599 goto error_remove_files;
600
601 data->class_dev = hwmon_device_register(&new_client->dev);
602 if (IS_ERR(data->class_dev)) {
603 err = PTR_ERR(data->class_dev);
604 goto error_remove_files;
605 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
607 return 0;
608
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200609error_remove_files:
610 sysfs_remove_group(&new_client->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400611 i2c_detach_client(new_client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612error_free:
Alexey Dobriyan1f57ff82005-08-26 01:49:14 +0400613 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614error_release:
615 release_region(address, SMSC_EXTENT);
616 return err;
617}
618
619static int smsc47m1_detach_client(struct i2c_client *client)
620{
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400621 struct smsc47m1_data *data = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 int err;
623
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400624 hwmon_device_unregister(data->class_dev);
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200625 sysfs_remove_group(&client->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400626
Jean Delvare7bef5592005-07-27 22:14:49 +0200627 if ((err = i2c_detach_client(client)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
630 release_region(client->addr, SMSC_EXTENT);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400631 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
633 return 0;
634}
635
636static int smsc47m1_read_value(struct i2c_client *client, u8 reg)
637{
638 int res;
639
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100640 mutex_lock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 res = inb_p(client->addr + reg);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100642 mutex_unlock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 return res;
644}
645
646static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value)
647{
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100648 mutex_lock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 outb_p(value, client->addr + reg);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100650 mutex_unlock(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651}
652
653static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
654 int init)
655{
656 struct i2c_client *client = to_i2c_client(dev);
657 struct smsc47m1_data *data = i2c_get_clientdata(client);
658
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100659 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660
661 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200662 int i, fan_nr;
663 fan_nr = data->type == smsc47m2 ? 3 : 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664
Jean Delvare8eccbb62007-05-08 17:21:59 +0200665 for (i = 0; i < fan_nr; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 data->fan[i] = smsc47m1_read_value(client,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200667 SMSC47M1_REG_FAN[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 data->fan_preload[i] = smsc47m1_read_value(client,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200669 SMSC47M1_REG_FAN_PRELOAD[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 data->pwm[i] = smsc47m1_read_value(client,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200671 SMSC47M1_REG_PWM[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 }
673
674 i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV);
675 data->fan_div[0] = (i >> 4) & 0x03;
676 data->fan_div[1] = i >> 6;
677
678 data->alarms = smsc47m1_read_value(client,
679 SMSC47M1_REG_ALARM) >> 6;
680 /* Clear alarms if needed */
681 if (data->alarms)
682 smsc47m1_write_value(client, SMSC47M1_REG_ALARM, 0xC0);
683
Jean Delvare8eccbb62007-05-08 17:21:59 +0200684 if (fan_nr >= 3) {
685 data->fan_div[2] = (smsc47m1_read_value(client,
686 SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
687 data->alarms |= (smsc47m1_read_value(client,
688 SMSC47M2_REG_ALARM6) & 0x40) >> 4;
689 /* Clear alarm if needed */
690 if (data->alarms & 0x04)
691 smsc47m1_write_value(client,
692 SMSC47M2_REG_ALARM6,
693 0x40);
694 }
695
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 data->last_updated = jiffies;
697 }
698
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100699 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 return data;
701}
702
703static int __init sm_smsc47m1_init(void)
704{
Jean Delvare2d8672c2005-07-19 23:56:35 +0200705 if (smsc47m1_find(&address)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 return -ENODEV;
707 }
708
Jean Delvarefde09502005-07-19 23:51:07 +0200709 return i2c_isa_add_driver(&smsc47m1_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710}
711
712static void __exit sm_smsc47m1_exit(void)
713{
Jean Delvarefde09502005-07-19 23:51:07 +0200714 i2c_isa_del_driver(&smsc47m1_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715}
716
717MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
718MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
719MODULE_LICENSE("GPL");
720
721module_init(sm_smsc47m1_init);
722module_exit(sm_smsc47m1_exit);