blob: f219d7c6a98270ab074487e53326f8c47a5632b1 [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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100120 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 unsigned long last_updated; /* In jiffies */
122
Jean Delvare8eccbb62007-05-08 17:21:59 +0200123 u8 fan[3]; /* Register value */
124 u8 fan_preload[3]; /* Register value */
125 u8 fan_div[3]; /* Register encoding, shifted right */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 u8 alarms; /* Register encoding */
Jean Delvare8eccbb62007-05-08 17:21:59 +0200127 u8 pwm[3]; /* Register value (bit 0 is disable) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128};
129
130
Jean Delvare2d8672c2005-07-19 23:56:35 +0200131static int smsc47m1_detect(struct i2c_adapter *adapter);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132static int smsc47m1_detach_client(struct i2c_client *client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
134 int init);
135
Jean Delvare94e183f2007-05-08 17:21:59 +0200136static inline int smsc47m1_read_value(struct i2c_client *client, u8 reg)
137{
138 return inb_p(client->addr + reg);
139}
140
141static inline void smsc47m1_write_value(struct i2c_client *client, u8 reg,
142 u8 value)
143{
144 outb_p(value, client->addr + reg);
145}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
147static struct i2c_driver smsc47m1_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100148 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200149 .owner = THIS_MODULE,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100150 .name = "smsc47m1",
151 },
Jean Delvare2d8672c2005-07-19 23:56:35 +0200152 .attach_adapter = smsc47m1_detect,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 .detach_client = smsc47m1_detach_client,
154};
155
156/* nr is 0 or 1 in the callback functions below */
157
158static ssize_t get_fan(struct device *dev, char *buf, int nr)
159{
160 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
161 /* This chip (stupidly) stops monitoring fan speed if PWM is
162 enabled and duty cycle is 0%. This is fine if the monitoring
163 and control concern the same fan, but troublesome if they are
164 not (which could as well happen). */
165 int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
166 FAN_FROM_REG(data->fan[nr],
167 DIV_FROM_REG(data->fan_div[nr]),
168 data->fan_preload[nr]);
169 return sprintf(buf, "%d\n", rpm);
170}
171
172static ssize_t get_fan_min(struct device *dev, char *buf, int nr)
173{
174 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
175 int rpm = MIN_FROM_REG(data->fan_preload[nr],
176 DIV_FROM_REG(data->fan_div[nr]));
177 return sprintf(buf, "%d\n", rpm);
178}
179
180static ssize_t get_fan_div(struct device *dev, char *buf, int nr)
181{
182 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
183 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
184}
185
186static ssize_t get_pwm(struct device *dev, char *buf, int nr)
187{
188 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
189 return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));
190}
191
192static ssize_t get_pwm_en(struct device *dev, char *buf, int nr)
193{
194 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
195 return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[nr]));
196}
197
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400198static ssize_t get_alarms(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199{
200 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
201 return sprintf(buf, "%d\n", data->alarms);
202}
203
204static ssize_t set_fan_min(struct device *dev, const char *buf,
205 size_t count, int nr)
206{
207 struct i2c_client *client = to_i2c_client(dev);
208 struct smsc47m1_data *data = i2c_get_clientdata(client);
209 long rpmdiv, val = simple_strtol(buf, NULL, 10);
210
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100211 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
213
214 if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100215 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 return -EINVAL;
217 }
218
219 data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200220 smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100222 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223
224 return count;
225}
226
227/* Note: we save and restore the fan minimum here, because its value is
228 determined in part by the fan clock divider. This follows the principle
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200229 of least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 because the divider changed. */
231static ssize_t set_fan_div(struct device *dev, const char *buf,
232 size_t count, int nr)
233{
234 struct i2c_client *client = to_i2c_client(dev);
235 struct smsc47m1_data *data = i2c_get_clientdata(client);
236
237 long new_div = simple_strtol(buf, NULL, 10), tmp;
238 u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
239
240 if (new_div == old_div) /* No change */
241 return count;
242
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100243 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 switch (new_div) {
245 case 1: data->fan_div[nr] = 0; break;
246 case 2: data->fan_div[nr] = 1; break;
247 case 4: data->fan_div[nr] = 2; break;
248 case 8: data->fan_div[nr] = 3; break;
249 default:
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100250 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 return -EINVAL;
252 }
253
Jean Delvare8eccbb62007-05-08 17:21:59 +0200254 switch (nr) {
255 case 0:
256 case 1:
257 tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV)
258 & ~(0x03 << (4 + 2 * nr));
259 tmp |= data->fan_div[nr] << (4 + 2 * nr);
260 smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp);
261 break;
262 case 2:
263 tmp = smsc47m1_read_value(client, SMSC47M2_REG_FANDIV3) & 0xCF;
264 tmp |= data->fan_div[2] << 4;
265 smsc47m1_write_value(client, SMSC47M2_REG_FANDIV3, tmp);
266 break;
267 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
269 /* Preserve fan min */
270 tmp = 192 - (old_div * (192 - data->fan_preload[nr])
271 + new_div / 2) / new_div;
272 data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200273 smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100275 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
277 return count;
278}
279
280static ssize_t set_pwm(struct device *dev, const char *buf,
281 size_t count, int nr)
282{
283 struct i2c_client *client = to_i2c_client(dev);
284 struct smsc47m1_data *data = i2c_get_clientdata(client);
285
286 long val = simple_strtol(buf, NULL, 10);
287
288 if (val < 0 || val > 255)
289 return -EINVAL;
290
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100291 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 data->pwm[nr] &= 0x81; /* Preserve additional bits */
293 data->pwm[nr] |= PWM_TO_REG(val);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200294 smsc47m1_write_value(client, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100296 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297
298 return count;
299}
300
301static ssize_t set_pwm_en(struct device *dev, const char *buf,
302 size_t count, int nr)
303{
304 struct i2c_client *client = to_i2c_client(dev);
305 struct smsc47m1_data *data = i2c_get_clientdata(client);
306
307 long val = simple_strtol(buf, NULL, 10);
308
309 if (val != 0 && val != 1)
310 return -EINVAL;
311
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100312 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 data->pwm[nr] &= 0xFE; /* preserve the other bits */
314 data->pwm[nr] |= !val;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200315 smsc47m1_write_value(client, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100317 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
319 return count;
320}
321
322#define fan_present(offset) \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400323static ssize_t get_fan##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324{ \
325 return get_fan(dev, buf, offset - 1); \
326} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400327static ssize_t get_fan##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328{ \
329 return get_fan_min(dev, buf, offset - 1); \
330} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400331static ssize_t set_fan##offset##_min (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 const char *buf, size_t count) \
333{ \
334 return set_fan_min(dev, buf, count, offset - 1); \
335} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400336static ssize_t get_fan##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337{ \
338 return get_fan_div(dev, buf, offset - 1); \
339} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400340static ssize_t set_fan##offset##_div (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 const char *buf, size_t count) \
342{ \
343 return set_fan_div(dev, buf, count, offset - 1); \
344} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400345static ssize_t get_pwm##offset (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346{ \
347 return get_pwm(dev, buf, offset - 1); \
348} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400349static ssize_t set_pwm##offset (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 const char *buf, size_t count) \
351{ \
352 return set_pwm(dev, buf, count, offset - 1); \
353} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400354static ssize_t get_pwm##offset##_en (struct device *dev, struct device_attribute *attr, char *buf) \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{ \
356 return get_pwm_en(dev, buf, offset - 1); \
357} \
Yani Ioannoua5099cf2005-05-17 06:42:25 -0400358static ssize_t set_pwm##offset##_en (struct device *dev, struct device_attribute *attr, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 const char *buf, size_t count) \
360{ \
361 return set_pwm_en(dev, buf, count, offset - 1); \
362} \
363static DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan##offset, \
364 NULL); \
365static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
366 get_fan##offset##_min, set_fan##offset##_min); \
367static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
368 get_fan##offset##_div, set_fan##offset##_div); \
369static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
370 get_pwm##offset, set_pwm##offset); \
371static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
372 get_pwm##offset##_en, set_pwm##offset##_en);
373
374fan_present(1);
375fan_present(2);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200376fan_present(3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
378static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
379
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200380/* Almost all sysfs files may or may not be created depending on the chip
381 setup so we create them individually. It is still convenient to define a
382 group to remove them all at once. */
383static struct attribute *smsc47m1_attributes[] = {
384 &dev_attr_fan1_input.attr,
385 &dev_attr_fan1_min.attr,
386 &dev_attr_fan1_div.attr,
387 &dev_attr_fan2_input.attr,
388 &dev_attr_fan2_min.attr,
389 &dev_attr_fan2_div.attr,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200390 &dev_attr_fan3_input.attr,
391 &dev_attr_fan3_min.attr,
392 &dev_attr_fan3_div.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200393
394 &dev_attr_pwm1.attr,
395 &dev_attr_pwm1_enable.attr,
396 &dev_attr_pwm2.attr,
397 &dev_attr_pwm2_enable.attr,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200398 &dev_attr_pwm3.attr,
399 &dev_attr_pwm3_enable.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200400
401 &dev_attr_alarms.attr,
402 NULL
403};
404
405static const struct attribute_group smsc47m1_group = {
406 .attrs = smsc47m1_attributes,
407};
408
Jean Delvaree6cfb3a2005-07-27 21:32:02 +0200409static int __init smsc47m1_find(unsigned short *addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410{
411 u8 val;
412
413 superio_enter();
Jean Delvare8eccbb62007-05-08 17:21:59 +0200414 devid = superio_inb(SUPERIO_REG_DEVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415
416 /*
Jean Delvare60917802006-10-08 22:00:44 +0200417 * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
418 * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
Jean Delvareec5ce552005-04-26 22:09:43 +0200420 * can do much more besides (device id 0x60).
Jean Delvareb890a072005-10-26 22:21:24 +0200421 * The LPC47M997 is undocumented, but seems to be compatible with
422 * the LPC47M192, and has the same device id.
Jean Delvare8eccbb62007-05-08 17:21:59 +0200423 * The LPC47M292 (device id 0x6B) is somewhat compatible, but it
424 * supports a 3rd fan, and the pin configuration registers are
425 * unfortunately different.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 */
Jean Delvare8eccbb62007-05-08 17:21:59 +0200427 switch (devid) {
428 case 0x51:
Jean Delvareec5ce552005-04-26 22:09:43 +0200429 printk(KERN_INFO "smsc47m1: Found SMSC LPC47B27x\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200430 break;
431 case 0x59:
Jean Delvare60917802006-10-08 22:00:44 +0200432 printk(KERN_INFO "smsc47m1: Found SMSC "
433 "LPC47M10x/LPC47M112/LPC47M13x\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200434 break;
435 case 0x5F:
Jean Delvareec5ce552005-04-26 22:09:43 +0200436 printk(KERN_INFO "smsc47m1: Found SMSC LPC47M14x\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200437 break;
438 case 0x60:
Jean Delvareb890a072005-10-26 22:21:24 +0200439 printk(KERN_INFO "smsc47m1: Found SMSC "
440 "LPC47M15x/LPC47M192/LPC47M997\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200441 break;
442 case 0x6B:
443 printk(KERN_INFO "smsc47m1: Found SMSC LPC47M292\n");
444 break;
445 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 superio_exit();
447 return -ENODEV;
448 }
449
450 superio_select();
Jean Delvare2d8672c2005-07-19 23:56:35 +0200451 *addr = (superio_inb(SUPERIO_REG_BASE) << 8)
452 | superio_inb(SUPERIO_REG_BASE + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 val = superio_inb(SUPERIO_REG_ACT);
Jean Delvare2d8672c2005-07-19 23:56:35 +0200454 if (*addr == 0 || (val & 0x01) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 printk(KERN_INFO "smsc47m1: Device is disabled, will not use\n");
456 superio_exit();
457 return -ENODEV;
458 }
459
460 superio_exit();
461 return 0;
462}
463
Jean Delvare2d8672c2005-07-19 23:56:35 +0200464static int smsc47m1_detect(struct i2c_adapter *adapter)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465{
466 struct i2c_client *new_client;
467 struct smsc47m1_data *data;
468 int err = 0;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200469 int fan1, fan2, fan3, pwm1, pwm2, pwm3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100471 if (!request_region(address, SMSC_EXTENT, smsc47m1_driver.driver.name)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 dev_err(&adapter->dev, "Region 0x%x already in use!\n", address);
473 return -EBUSY;
474 }
475
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200476 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 err = -ENOMEM;
478 goto error_release;
479 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
Jean Delvare8eccbb62007-05-08 17:21:59 +0200481 data->type = devid == 0x6B ? smsc47m2 : smsc47m1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 new_client = &data->client;
483 i2c_set_clientdata(new_client, data);
484 new_client->addr = address;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 new_client->adapter = adapter;
486 new_client->driver = &smsc47m1_driver;
487 new_client->flags = 0;
488
Jean Delvare8eccbb62007-05-08 17:21:59 +0200489 strlcpy(new_client->name,
490 data->type == smsc47m2 ? "smsc47m2" : "smsc47m1",
491 I2C_NAME_SIZE);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100492 mutex_init(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
494 /* If no function is properly configured, there's no point in
495 actually registering the chip. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 pwm1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(0)) & 0x05)
497 == 0x04;
498 pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05)
499 == 0x04;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200500 if (data->type == smsc47m2) {
501 fan1 = (smsc47m1_read_value(new_client, SMSC47M2_REG_TPIN1)
502 & 0x0d) == 0x09;
503 fan2 = (smsc47m1_read_value(new_client, SMSC47M2_REG_TPIN2)
504 & 0x0d) == 0x09;
505 fan3 = (smsc47m1_read_value(new_client, SMSC47M2_REG_TPIN3)
506 & 0x0d) == 0x0d;
507 pwm3 = (smsc47m1_read_value(new_client, SMSC47M2_REG_PPIN3)
508 & 0x0d) == 0x08;
509 } else {
510 fan1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0))
511 & 0x05) == 0x05;
512 fan2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1))
513 & 0x05) == 0x05;
514 fan3 = 0;
515 pwm3 = 0;
516 }
517 if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
Jean Delvare0dd76992006-08-28 14:22:34 +0200518 dev_warn(&adapter->dev, "Device at 0x%x is not configured, "
519 "will not use\n", new_client->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 err = -ENODEV;
521 goto error_free;
522 }
523
524 if ((err = i2c_attach_client(new_client)))
525 goto error_free;
526
527 /* Some values (fan min, clock dividers, pwm registers) may be
528 needed before any update is triggered, so we better read them
529 at least once here. We don't usually do it that way, but in
530 this particular case, manually reading 5 registers out of 8
531 doesn't make much sense and we're better using the existing
532 function. */
533 smsc47m1_update_device(&new_client->dev, 1);
534
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400535 /* Register sysfs hooks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 if (fan1) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200537 if ((err = device_create_file(&new_client->dev,
538 &dev_attr_fan1_input))
539 || (err = device_create_file(&new_client->dev,
540 &dev_attr_fan1_min))
541 || (err = device_create_file(&new_client->dev,
542 &dev_attr_fan1_div)))
543 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 } else
545 dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, "
546 "skipping\n");
547
548 if (fan2) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200549 if ((err = device_create_file(&new_client->dev,
550 &dev_attr_fan2_input))
551 || (err = device_create_file(&new_client->dev,
552 &dev_attr_fan2_min))
553 || (err = device_create_file(&new_client->dev,
554 &dev_attr_fan2_div)))
555 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 } else
557 dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, "
558 "skipping\n");
559
Jean Delvare8eccbb62007-05-08 17:21:59 +0200560 if (fan3) {
561 if ((err = device_create_file(&new_client->dev,
562 &dev_attr_fan3_input))
563 || (err = device_create_file(&new_client->dev,
564 &dev_attr_fan3_min))
565 || (err = device_create_file(&new_client->dev,
566 &dev_attr_fan3_div)))
567 goto error_remove_files;
568 } else
569 dev_dbg(&new_client->dev, "Fan 3 not enabled by hardware, "
570 "skipping\n");
571
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 if (pwm1) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200573 if ((err = device_create_file(&new_client->dev,
574 &dev_attr_pwm1))
575 || (err = device_create_file(&new_client->dev,
576 &dev_attr_pwm1_enable)))
577 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 } else
579 dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, "
580 "skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200581
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 if (pwm2) {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200583 if ((err = device_create_file(&new_client->dev,
584 &dev_attr_pwm2))
585 || (err = device_create_file(&new_client->dev,
586 &dev_attr_pwm2_enable)))
587 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 } else
589 dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, "
590 "skipping\n");
591
Jean Delvare8eccbb62007-05-08 17:21:59 +0200592 if (pwm3) {
593 if ((err = device_create_file(&new_client->dev,
594 &dev_attr_pwm3))
595 || (err = device_create_file(&new_client->dev,
596 &dev_attr_pwm3_enable)))
597 goto error_remove_files;
598 } else
599 dev_dbg(&new_client->dev, "PWM 3 not enabled by hardware, "
600 "skipping\n");
601
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200602 if ((err = device_create_file(&new_client->dev, &dev_attr_alarms)))
603 goto error_remove_files;
604
605 data->class_dev = hwmon_device_register(&new_client->dev);
606 if (IS_ERR(data->class_dev)) {
607 err = PTR_ERR(data->class_dev);
608 goto error_remove_files;
609 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
611 return 0;
612
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200613error_remove_files:
614 sysfs_remove_group(&new_client->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400615 i2c_detach_client(new_client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616error_free:
Alexey Dobriyan1f57ff82005-08-26 01:49:14 +0400617 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618error_release:
619 release_region(address, SMSC_EXTENT);
620 return err;
621}
622
623static int smsc47m1_detach_client(struct i2c_client *client)
624{
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400625 struct smsc47m1_data *data = i2c_get_clientdata(client);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 int err;
627
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400628 hwmon_device_unregister(data->class_dev);
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200629 sysfs_remove_group(&client->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400630
Jean Delvare7bef5592005-07-27 22:14:49 +0200631 if ((err = i2c_detach_client(client)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
634 release_region(client->addr, SMSC_EXTENT);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400635 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
637 return 0;
638}
639
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
641 int init)
642{
643 struct i2c_client *client = to_i2c_client(dev);
644 struct smsc47m1_data *data = i2c_get_clientdata(client);
645
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100646 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647
648 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200649 int i, fan_nr;
650 fan_nr = data->type == smsc47m2 ? 3 : 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
Jean Delvare8eccbb62007-05-08 17:21:59 +0200652 for (i = 0; i < fan_nr; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 data->fan[i] = smsc47m1_read_value(client,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200654 SMSC47M1_REG_FAN[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 data->fan_preload[i] = smsc47m1_read_value(client,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200656 SMSC47M1_REG_FAN_PRELOAD[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 data->pwm[i] = smsc47m1_read_value(client,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200658 SMSC47M1_REG_PWM[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 }
660
661 i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV);
662 data->fan_div[0] = (i >> 4) & 0x03;
663 data->fan_div[1] = i >> 6;
664
665 data->alarms = smsc47m1_read_value(client,
666 SMSC47M1_REG_ALARM) >> 6;
667 /* Clear alarms if needed */
668 if (data->alarms)
669 smsc47m1_write_value(client, SMSC47M1_REG_ALARM, 0xC0);
670
Jean Delvare8eccbb62007-05-08 17:21:59 +0200671 if (fan_nr >= 3) {
672 data->fan_div[2] = (smsc47m1_read_value(client,
673 SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
674 data->alarms |= (smsc47m1_read_value(client,
675 SMSC47M2_REG_ALARM6) & 0x40) >> 4;
676 /* Clear alarm if needed */
677 if (data->alarms & 0x04)
678 smsc47m1_write_value(client,
679 SMSC47M2_REG_ALARM6,
680 0x40);
681 }
682
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 data->last_updated = jiffies;
684 }
685
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100686 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 return data;
688}
689
690static int __init sm_smsc47m1_init(void)
691{
Jean Delvare2d8672c2005-07-19 23:56:35 +0200692 if (smsc47m1_find(&address)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 return -ENODEV;
694 }
695
Jean Delvarefde09502005-07-19 23:51:07 +0200696 return i2c_isa_add_driver(&smsc47m1_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697}
698
699static void __exit sm_smsc47m1_exit(void)
700{
Jean Delvarefde09502005-07-19 23:51:07 +0200701 i2c_isa_del_driver(&smsc47m1_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702}
703
704MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
705MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
706MODULE_LICENSE("GPL");
707
708module_init(sm_smsc47m1_init);
709module_exit(sm_smsc47m1_exit);