Blame view

drivers/hwmon/smsc47m1.c 24.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /*
      smsc47m1.c - Part of lm_sensors, Linux kernel modules
                   for hardware monitoring
6091780eb   Jean Delvare   smsc47m1: List th...
4
      Supports the SMSC LPC47B27x, LPC47M10x, LPC47M112, LPC47M13x,
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
5
6
      LPC47M14x, LPC47M15x, LPC47M192, LPC47M292 and LPC47M997
      Super-I/O chips.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
  
      Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
9
      Copyright (C) 2004-2007 Jean Delvare <khali@linux-fr.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
      Ported to Linux 2.6 by Gabriele Gorla <gorlik@yahoo.com>
                          and Jean Delvare
  
      This program is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.
  
      This program is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
  
      You should have received a copy of the GNU General Public License
      along with this program; if not, write to the Free Software
      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
512504e9f   Joe Perches   hwmon: (smsc47m1)...
27
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
31
  #include <linux/module.h>
  #include <linux/slab.h>
  #include <linux/ioport.h>
  #include <linux/jiffies.h>
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
32
  #include <linux/platform_device.h>
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
33
  #include <linux/hwmon.h>
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
34
  #include <linux/hwmon-sysfs.h>
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
35
  #include <linux/err.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
  #include <linux/init.h>
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
37
  #include <linux/mutex.h>
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
38
  #include <linux/sysfs.h>
b9acb64a3   Jean Delvare   hwmon: Check for ...
39
  #include <linux/acpi.h>
6055fae8a   H Hartley Sweeten   hwmon: Include <l...
40
  #include <linux/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41

67b671bce   Jean Delvare   hwmon: Let the us...
42
43
44
  static unsigned short force_id;
  module_param(force_id, ushort, 0);
  MODULE_PARM_DESC(force_id, "Override the detected device ID");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
45
46
47
  static struct platform_device *pdev;
  
  #define DRVNAME "smsc47m1"
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
48
  enum chips { smsc47m1, smsc47m2 };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
  
  /* Super-I/0 registers and commands */
  
  #define	REG	0x2e	/* The register to read/write */
  #define	VAL	0x2f	/* The value to read/write */
  
  static inline void
  superio_outb(int reg, int val)
  {
  	outb(reg, REG);
  	outb(val, VAL);
  }
  
  static inline int
  superio_inb(int reg)
  {
  	outb(reg, REG);
  	return inb(VAL);
  }
  
  /* logical device for fans is 0x0A */
  #define superio_select() superio_outb(0x07, 0x0A)
  
  static inline void
  superio_enter(void)
  {
  	outb(0x55, REG);
  }
  
  static inline void
  superio_exit(void)
  {
  	outb(0xAA, REG);
  }
  
  #define SUPERIO_REG_ACT		0x30
  #define SUPERIO_REG_BASE	0x60
  #define SUPERIO_REG_DEVID	0x20
1b54ab450   Jean Delvare   hwmon: (smsc47m1)...
87
  #define SUPERIO_REG_DEVREV	0x21
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
90
91
92
93
94
95
96
  
  /* Logical device registers */
  
  #define SMSC_EXTENT		0x80
  
  /* nr is 0 or 1 in the macros below */
  #define SMSC47M1_REG_ALARM		0x04
  #define SMSC47M1_REG_TPIN(nr)		(0x34 - (nr))
  #define SMSC47M1_REG_PPIN(nr)		(0x36 - (nr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
  #define SMSC47M1_REG_FANDIV		0x58
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
98
99
100
101
102
103
104
105
106
107
108
  
  static const u8 SMSC47M1_REG_FAN[3]		= { 0x59, 0x5a, 0x6b };
  static const u8 SMSC47M1_REG_FAN_PRELOAD[3]	= { 0x5b, 0x5c, 0x6c };
  static const u8 SMSC47M1_REG_PWM[3]		= { 0x56, 0x57, 0x69 };
  
  #define SMSC47M2_REG_ALARM6		0x09
  #define SMSC47M2_REG_TPIN1		0x38
  #define SMSC47M2_REG_TPIN2		0x37
  #define SMSC47M2_REG_TPIN3		0x2d
  #define SMSC47M2_REG_PPIN3		0x2c
  #define SMSC47M2_REG_FANDIV3		0x6a
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
113
114
115
116
117
118
119
  
  #define MIN_FROM_REG(reg,div)		((reg)>=192 ? 0 : \
  					 983040/((192-(reg))*(div)))
  #define FAN_FROM_REG(reg,div,preload)	((reg)<=(preload) || (reg)==255 ? 0 : \
  					 983040/(((reg)-(preload))*(div)))
  #define DIV_FROM_REG(reg)		(1 << (reg))
  #define PWM_FROM_REG(reg)		(((reg) & 0x7E) << 1)
  #define PWM_EN_FROM_REG(reg)		((~(reg)) & 0x01)
  #define PWM_TO_REG(reg)			(((reg) >> 1) & 0x7E)
  
  struct smsc47m1_data {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
120
121
  	unsigned short addr;
  	const char *name;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
122
  	enum chips type;
1beeffe43   Tony Jones   hwmon: Convert fr...
123
  	struct device *hwmon_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124

9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
125
  	struct mutex update_lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
  	unsigned long last_updated;	/* In jiffies */
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
127
128
129
  	u8 fan[3];		/* Register value */
  	u8 fan_preload[3];	/* Register value */
  	u8 fan_div[3];		/* Register encoding, shifted right */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
  	u8 alarms;		/* Register encoding */
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
131
  	u8 pwm[3];		/* Register value (bit 0 is disable) */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  };
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
133
134
  struct smsc47m1_sio_data {
  	enum chips type;
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
135
  	u8 activate;		/* Remember initial device state */
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
136
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137

51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
138

3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
139
  static int __exit smsc47m1_remove(struct platform_device *pdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
  static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
  		int init);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
142
  static inline int smsc47m1_read_value(struct smsc47m1_data *data, u8 reg)
94e183fd0   Jean Delvare   hwmon/smsc47m1: G...
143
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
144
  	return inb_p(data->addr + reg);
94e183fd0   Jean Delvare   hwmon/smsc47m1: G...
145
  }
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
146
  static inline void smsc47m1_write_value(struct smsc47m1_data *data, u8 reg,
94e183fd0   Jean Delvare   hwmon/smsc47m1: G...
147
148
  		u8 value)
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
149
  	outb_p(value, data->addr + reg);
94e183fd0   Jean Delvare   hwmon/smsc47m1: G...
150
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151

51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
152
  static struct platform_driver smsc47m1_driver = {
cdaf79349   Laurent Riffard   [PATCH] i2c: Drop...
153
  	.driver = {
872188420   Jean Delvare   i2c-isa: Restore ...
154
  		.owner	= THIS_MODULE,
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
155
  		.name	= DRVNAME,
cdaf79349   Laurent Riffard   [PATCH] i2c: Drop...
156
  	},
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
157
  	.remove		= __exit_p(smsc47m1_remove),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
  };
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
159
160
  static ssize_t get_fan(struct device *dev, struct device_attribute
  		       *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
162
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
164
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
167
168
169
170
171
172
173
174
175
  	/* This chip (stupidly) stops monitoring fan speed if PWM is
  	   enabled and duty cycle is 0%. This is fine if the monitoring
  	   and control concern the same fan, but troublesome if they are
  	   not (which could as well happen). */
  	int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
  		  FAN_FROM_REG(data->fan[nr],
  			       DIV_FROM_REG(data->fan_div[nr]),
  			       data->fan_preload[nr]);
  	return sprintf(buf, "%d
  ", rpm);
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
176
177
  static ssize_t get_fan_min(struct device *dev, struct device_attribute
  			   *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
179
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
181
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
184
185
186
  	int rpm = MIN_FROM_REG(data->fan_preload[nr],
  			       DIV_FROM_REG(data->fan_div[nr]));
  	return sprintf(buf, "%d
  ", rpm);
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
187
188
  static ssize_t get_fan_div(struct device *dev, struct device_attribute
  			   *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
190
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
192
193
  	return sprintf(buf, "%d
  ", DIV_FROM_REG(data->fan_div[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  }
1f08af7ea   Jean Delvare   hwmon: (smsc47m1)...
195
196
197
198
199
200
201
202
  static ssize_t get_fan_alarm(struct device *dev, struct device_attribute
  			     *devattr, char *buf)
  {
  	int bitnr = to_sensor_dev_attr(devattr)->index;
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
  	return sprintf(buf, "%u
  ", (data->alarms >> bitnr) & 1);
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
203
204
  static ssize_t get_pwm(struct device *dev, struct device_attribute
  		       *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
206
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
208
209
  	return sprintf(buf, "%d
  ", PWM_FROM_REG(data->pwm[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
211
212
  static ssize_t get_pwm_en(struct device *dev, struct device_attribute
  			  *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
214
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
216
217
  	return sprintf(buf, "%d
  ", PWM_EN_FROM_REG(data->pwm[attr->index]));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
219
220
  static ssize_t get_alarms(struct device *dev, struct device_attribute
  			  *devattr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
223
224
225
  {
  	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
  	return sprintf(buf, "%d
  ", data->alarms);
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
226
227
  static ssize_t set_fan_min(struct device *dev, struct device_attribute
  			   *devattr, const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
229
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
230
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
231
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
  	long rpmdiv, val = simple_strtol(buf, NULL, 10);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
233
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
236
  	rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
  
  	if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
237
  		mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
240
241
  		return -EINVAL;
  	}
  
  	data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
242
  	smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
  			     data->fan_preload[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
244
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
248
249
250
  
  	return count;
  }
  
  /* Note: we save and restore the fan minimum here, because its value is
     determined in part by the fan clock divider.  This follows the principle
d6e05edc5   Andreas Mohr   spelling fixes
251
     of least surprise; the user doesn't expect the fan minimum to change just
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
     because the divider changed. */
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
253
254
  static ssize_t set_fan_div(struct device *dev, struct device_attribute
  			   *devattr, const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
256
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
257
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
258
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
262
263
  	long new_div = simple_strtol(buf, NULL, 10), tmp;
  	u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
  
  	if (new_div == old_div) /* No change */
  		return count;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
264
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
269
270
  	switch (new_div) {
  	case 1: data->fan_div[nr] = 0; break;
  	case 2: data->fan_div[nr] = 1; break;
  	case 4: data->fan_div[nr] = 2; break;
  	case 8: data->fan_div[nr] = 3; break;
  	default:
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
271
  		mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
  		return -EINVAL;
  	}
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
274
275
276
  	switch (nr) {
  	case 0:
  	case 1:
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
277
  		tmp = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
278
279
  		      & ~(0x03 << (4 + 2 * nr));
  		tmp |= data->fan_div[nr] << (4 + 2 * nr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
280
  		smsc47m1_write_value(data, SMSC47M1_REG_FANDIV, tmp);
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
281
282
  		break;
  	case 2:
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
283
  		tmp = smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) & 0xCF;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
284
  		tmp |= data->fan_div[2] << 4;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
285
  		smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp);
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
286
287
  		break;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
289
290
291
292
  
  	/* Preserve fan min */
  	tmp = 192 - (old_div * (192 - data->fan_preload[nr])
  		     + new_div / 2) / new_div;
  	data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
293
  	smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
  			     data->fan_preload[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
295
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
298
  
  	return count;
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
299
300
  static ssize_t set_pwm(struct device *dev, struct device_attribute
  		       *devattr, const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
302
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
303
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
304
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
306
307
308
  	long val = simple_strtol(buf, NULL, 10);
  
  	if (val < 0 || val > 255)
  		return -EINVAL;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
309
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
311
  	data->pwm[nr] &= 0x81; /* Preserve additional bits */
  	data->pwm[nr] |= PWM_TO_REG(val);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
312
  	smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
  			     data->pwm[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
314
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
317
  
  	return count;
  }
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
318
319
  static ssize_t set_pwm_en(struct device *dev, struct device_attribute
  			  *devattr, const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
  {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
321
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
322
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
323
  	int nr = attr->index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
325
326
327
  	long val = simple_strtol(buf, NULL, 10);
  	
  	if (val != 0 && val != 1)
  		return -EINVAL;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
328
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
  	data->pwm[nr] &= 0xFE; /* preserve the other bits */
  	data->pwm[nr] |= !val;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
331
  	smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
  			     data->pwm[nr]);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
333
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
336
337
338
  
  	return count;
  }
  
  #define fan_present(offset)						\
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
339
340
341
342
343
344
  static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan,	\
  		NULL, offset - 1);					\
  static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR,		\
  		get_fan_min, set_fan_min, offset - 1);			\
  static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR,		\
  		get_fan_div, set_fan_div, offset - 1);			\
1f08af7ea   Jean Delvare   hwmon: (smsc47m1)...
345
346
  static SENSOR_DEVICE_ATTR(fan##offset##_alarm, S_IRUGO, get_fan_alarm,	\
  		NULL, offset - 1);					\
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
347
348
349
350
  static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR,		\
  		get_pwm, set_pwm, offset - 1);				\
  static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR,	\
  		get_pwm_en, set_pwm_en, offset - 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
353
  
  fan_present(1);
  fan_present(2);
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
354
  fan_present(3);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
356
  
  static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
357
358
359
360
361
362
363
364
365
  static ssize_t show_name(struct device *dev, struct device_attribute
  			 *devattr, char *buf)
  {
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
  
  	return sprintf(buf, "%s
  ", data->name);
  }
  static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
366
367
368
369
  /* Almost all sysfs files may or may not be created depending on the chip
     setup so we create them individually. It is still convenient to define a
     group to remove them all at once. */
  static struct attribute *smsc47m1_attributes[] = {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
370
371
372
  	&sensor_dev_attr_fan1_input.dev_attr.attr,
  	&sensor_dev_attr_fan1_min.dev_attr.attr,
  	&sensor_dev_attr_fan1_div.dev_attr.attr,
1f08af7ea   Jean Delvare   hwmon: (smsc47m1)...
373
  	&sensor_dev_attr_fan1_alarm.dev_attr.attr,
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
374
375
376
  	&sensor_dev_attr_fan2_input.dev_attr.attr,
  	&sensor_dev_attr_fan2_min.dev_attr.attr,
  	&sensor_dev_attr_fan2_div.dev_attr.attr,
1f08af7ea   Jean Delvare   hwmon: (smsc47m1)...
377
  	&sensor_dev_attr_fan2_alarm.dev_attr.attr,
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
378
379
380
  	&sensor_dev_attr_fan3_input.dev_attr.attr,
  	&sensor_dev_attr_fan3_min.dev_attr.attr,
  	&sensor_dev_attr_fan3_div.dev_attr.attr,
1f08af7ea   Jean Delvare   hwmon: (smsc47m1)...
381
  	&sensor_dev_attr_fan3_alarm.dev_attr.attr,
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
382
383
384
385
386
387
388
  
  	&sensor_dev_attr_pwm1.dev_attr.attr,
  	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
  	&sensor_dev_attr_pwm2.dev_attr.attr,
  	&sensor_dev_attr_pwm2_enable.dev_attr.attr,
  	&sensor_dev_attr_pwm3.dev_attr.attr,
  	&sensor_dev_attr_pwm3_enable.dev_attr.attr,
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
389
390
  
  	&dev_attr_alarms.attr,
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
391
  	&dev_attr_name.attr,
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
392
393
394
395
396
397
  	NULL
  };
  
  static const struct attribute_group smsc47m1_group = {
  	.attrs = smsc47m1_attributes,
  };
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
398
399
  static int __init smsc47m1_find(unsigned short *addr,
  				struct smsc47m1_sio_data *sio_data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
402
403
  {
  	u8 val;
  
  	superio_enter();
67b671bce   Jean Delvare   hwmon: Let the us...
404
  	val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
  
  	/*
6091780eb   Jean Delvare   smsc47m1: List th...
407
408
  	 * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
  	 * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
  	 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
ec5ce552d   Jean Delvare   [PATCH] I2C: Add ...
410
  	 * can do much more besides (device id 0x60).
b890a07f7   Jean Delvare   [PATCH] hwmon: sm...
411
412
  	 * The LPC47M997 is undocumented, but seems to be compatible with
  	 * the LPC47M192, and has the same device id.
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
413
414
415
  	 * The LPC47M292 (device id 0x6B) is somewhat compatible, but it
  	 * supports a 3rd fan, and the pin configuration registers are
  	 * unfortunately different.
1b54ab450   Jean Delvare   hwmon: (smsc47m1)...
416
417
418
  	 * The LPC47M233 has the same device id (0x6B) but is not compatible.
  	 * We check the high bit of the device revision register to
  	 * differentiate them.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
  	 */
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
420
  	switch (val) {
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
421
  	case 0x51:
512504e9f   Joe Perches   hwmon: (smsc47m1)...
422
423
  		pr_info("Found SMSC LPC47B27x
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
424
  		sio_data->type = smsc47m1;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
425
426
  		break;
  	case 0x59:
512504e9f   Joe Perches   hwmon: (smsc47m1)...
427
428
  		pr_info("Found SMSC LPC47M10x/LPC47M112/LPC47M13x
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
429
  		sio_data->type = smsc47m1;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
430
431
  		break;
  	case 0x5F:
512504e9f   Joe Perches   hwmon: (smsc47m1)...
432
433
  		pr_info("Found SMSC LPC47M14x
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
434
  		sio_data->type = smsc47m1;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
435
436
  		break;
  	case 0x60:
512504e9f   Joe Perches   hwmon: (smsc47m1)...
437
438
  		pr_info("Found SMSC LPC47M15x/LPC47M192/LPC47M997
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
439
  		sio_data->type = smsc47m1;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
440
441
  		break;
  	case 0x6B:
1b54ab450   Jean Delvare   hwmon: (smsc47m1)...
442
  		if (superio_inb(SUPERIO_REG_DEVREV) & 0x80) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
443
444
  			pr_debug("Found SMSC LPC47M233, unsupported
  ");
1b54ab450   Jean Delvare   hwmon: (smsc47m1)...
445
446
447
  			superio_exit();
  			return -ENODEV;
  		}
512504e9f   Joe Perches   hwmon: (smsc47m1)...
448
449
  		pr_info("Found SMSC LPC47M292
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
450
  		sio_data->type = smsc47m2;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
451
452
  		break;
  	default:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
454
455
456
457
  		superio_exit();
  		return -ENODEV;
  	}
  
  	superio_select();
2d8672c5a   Jean Delvare   [PATCH] I2C: Sepa...
458
459
  	*addr = (superio_inb(SUPERIO_REG_BASE) << 8)
  	      |  superio_inb(SUPERIO_REG_BASE + 1);
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
460
  	if (*addr == 0) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
461
462
  		pr_info("Device address not set, will not use
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
465
  		superio_exit();
  		return -ENODEV;
  	}
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
466
467
468
469
  	/* Enable only if address is set (needed at least on the
  	 * Compaq Presario S4000NX) */
  	sio_data->activate = superio_inb(SUPERIO_REG_ACT);
  	if ((sio_data->activate & 0x01) == 0) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
470
471
  		pr_info("Enabling device
  ");
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
472
473
  		superio_outb(SUPERIO_REG_ACT, sio_data->activate | 0x01);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
475
476
  	superio_exit();
  	return 0;
  }
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
477
  /* Restore device to its initial state */
a00d643a2   Jeff Mahoney   hwmon: (smsc47m1)...
478
  static void smsc47m1_restore(const struct smsc47m1_sio_data *sio_data)
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
479
480
481
482
  {
  	if ((sio_data->activate & 0x01) == 0) {
  		superio_enter();
  		superio_select();
512504e9f   Joe Perches   hwmon: (smsc47m1)...
483
484
  		pr_info("Disabling device
  ");
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
485
486
487
488
489
  		superio_outb(SUPERIO_REG_ACT, sio_data->activate);
  
  		superio_exit();
  	}
  }
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
  #define CHECK		1
  #define REQUEST		2
  #define RELEASE		3
  
  /*
   * This function can be used to:
   *  - test for resource conflicts with ACPI
   *  - request the resources
   *  - release the resources
   * We only allocate the I/O ports we really need, to minimize the risk of
   * conflicts with ACPI or with other drivers.
   */
  static int smsc47m1_handle_resources(unsigned short address, enum chips type,
  				     int action, struct device *dev)
  {
  	static const u8 ports_m1[] = {
  		/* register, region length */
  		0x04, 1,
  		0x33, 4,
  		0x56, 7,
  	};
  
  	static const u8 ports_m2[] = {
  		/* register, region length */
  		0x04, 1,
  		0x09, 1,
  		0x2c, 2,
  		0x35, 4,
  		0x56, 7,
  		0x69, 4,
  	};
  
  	int i, ports_size, err;
  	const u8 *ports;
  
  	switch (type) {
  	case smsc47m1:
  	default:
  		ports = ports_m1;
  		ports_size = ARRAY_SIZE(ports_m1);
  		break;
  	case smsc47m2:
  		ports = ports_m2;
  		ports_size = ARRAY_SIZE(ports_m2);
  		break;
  	}
  
  	for (i = 0; i + 1 < ports_size; i += 2) {
  		unsigned short start = address + ports[i];
  		unsigned short len = ports[i + 1];
  
  		switch (action) {
  		case CHECK:
  			/* Only check for conflicts */
  			err = acpi_check_region(start, len, DRVNAME);
  			if (err)
  				return err;
  			break;
  		case REQUEST:
  			/* Request the resources */
  			if (!request_region(start, len, DRVNAME)) {
  				dev_err(dev, "Region 0x%hx-0x%hx already in "
  					"use!
  ", start, start + len);
  
  				/* Undo all requests */
  				for (i -= 2; i >= 0; i -= 2)
  					release_region(address + ports[i],
  						       ports[i + 1]);
  				return -EBUSY;
  			}
  			break;
  		case RELEASE:
  			/* Release the resources */
  			release_region(start, len);
  			break;
  		}
  	}
  
  	return 0;
  }
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
571
  static int __init smsc47m1_probe(struct platform_device *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
573
574
  	struct device *dev = &pdev->dev;
  	struct smsc47m1_sio_data *sio_data = dev->platform_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
  	struct smsc47m1_data *data;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
576
  	struct resource *res;
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
577
  	int err;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
578
  	int fan1, fan2, fan3, pwm1, pwm2, pwm3;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579

51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
580
581
582
583
584
585
  	static const char *names[] = {
  		"smsc47m1",
  		"smsc47m2",
  	};
  
  	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
586
587
588
589
  	err = smsc47m1_handle_resources(res->start, sio_data->type,
  					REQUEST, dev);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590

ba9c2e8d1   Deepak Saxena   [PATCH] hwmon: kz...
591
  	if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
593
594
  		err = -ENOMEM;
  		goto error_release;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595

51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
596
597
598
  	data->addr = res->start;
  	data->type = sio_data->type;
  	data->name = names[sio_data->type];
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
599
  	mutex_init(&data->update_lock);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
600
  	platform_set_drvdata(pdev, data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
601
602
603
  
  	/* If no function is properly configured, there's no point in
  	   actually registering the chip. */
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
604
  	pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
605
  	       == 0x04;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
606
  	pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
  	       == 0x04;
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
608
  	if (data->type == smsc47m2) {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
609
  		fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
610
  			& 0x0d) == 0x09;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
611
  		fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
612
  			& 0x0d) == 0x09;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
613
  		fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
614
  			& 0x0d) == 0x0d;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
615
  		pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3)
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
616
617
  			& 0x0d) == 0x08;
  	} else {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
618
  		fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0))
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
619
  			& 0x05) == 0x05;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
620
  		fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1))
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
621
622
623
624
625
  			& 0x05) == 0x05;
  		fan3 = 0;
  		pwm3 = 0;
  	}
  	if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
626
627
  		dev_warn(dev, "Device not configured, will not use
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
629
630
  		err = -ENODEV;
  		goto error_free;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
632
633
634
635
636
  	/* Some values (fan min, clock dividers, pwm registers) may be
  	   needed before any update is triggered, so we better read them
  	   at least once here. We don't usually do it that way, but in
  	   this particular case, manually reading 5 registers out of 8
  	   doesn't make much sense and we're better using the existing
  	   function. */
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
637
  	smsc47m1_update_device(dev, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638

943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
639
  	/* Register sysfs hooks */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
  	if (fan1) {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
641
642
643
644
645
  		if ((err = device_create_file(dev,
  				&sensor_dev_attr_fan1_input.dev_attr))
  		 || (err = device_create_file(dev,
  				&sensor_dev_attr_fan1_min.dev_attr))
  		 || (err = device_create_file(dev,
1f08af7ea   Jean Delvare   hwmon: (smsc47m1)...
646
647
648
  				&sensor_dev_attr_fan1_div.dev_attr))
  		 || (err = device_create_file(dev,
  				&sensor_dev_attr_fan1_alarm.dev_attr)))
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
649
  			goto error_remove_files;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650
  	} else
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
651
652
  		dev_dbg(dev, "Fan 1 not enabled by hardware, skipping
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
653
654
  
  	if (fan2) {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
655
656
657
658
659
  		if ((err = device_create_file(dev,
  				&sensor_dev_attr_fan2_input.dev_attr))
  		 || (err = device_create_file(dev,
  				&sensor_dev_attr_fan2_min.dev_attr))
  		 || (err = device_create_file(dev,
1f08af7ea   Jean Delvare   hwmon: (smsc47m1)...
660
661
662
  				&sensor_dev_attr_fan2_div.dev_attr))
  		 || (err = device_create_file(dev,
  				&sensor_dev_attr_fan2_alarm.dev_attr)))
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
663
  			goto error_remove_files;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664
  	} else
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
665
666
  		dev_dbg(dev, "Fan 2 not enabled by hardware, skipping
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667

8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
668
  	if (fan3) {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
669
670
671
672
673
  		if ((err = device_create_file(dev,
  				&sensor_dev_attr_fan3_input.dev_attr))
  		 || (err = device_create_file(dev,
  				&sensor_dev_attr_fan3_min.dev_attr))
  		 || (err = device_create_file(dev,
1f08af7ea   Jean Delvare   hwmon: (smsc47m1)...
674
675
676
  				&sensor_dev_attr_fan3_div.dev_attr))
  		 || (err = device_create_file(dev,
  				&sensor_dev_attr_fan3_alarm.dev_attr)))
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
677
  			goto error_remove_files;
8477d0268   Jean Delvare   hwmon: (smsc47m1)...
678
  	} else if (data->type == smsc47m2)
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
679
680
  		dev_dbg(dev, "Fan 3 not enabled by hardware, skipping
  ");
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
681

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
682
  	if (pwm1) {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
683
684
685
686
  		if ((err = device_create_file(dev,
  				&sensor_dev_attr_pwm1.dev_attr))
  		 || (err = device_create_file(dev,
  				&sensor_dev_attr_pwm1_enable.dev_attr)))
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
687
  			goto error_remove_files;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
  	} else
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
689
690
  		dev_dbg(dev, "PWM 1 not enabled by hardware, skipping
  ");
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
691

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692
  	if (pwm2) {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
693
694
695
696
  		if ((err = device_create_file(dev,
  				&sensor_dev_attr_pwm2.dev_attr))
  		 || (err = device_create_file(dev,
  				&sensor_dev_attr_pwm2_enable.dev_attr)))
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
697
  			goto error_remove_files;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698
  	} else
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
699
700
  		dev_dbg(dev, "PWM 2 not enabled by hardware, skipping
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701

8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
702
  	if (pwm3) {
e84cfbcbe   Jean Delvare   hwmon/smsc47m1: U...
703
704
705
706
  		if ((err = device_create_file(dev,
  				&sensor_dev_attr_pwm3.dev_attr))
  		 || (err = device_create_file(dev,
  				&sensor_dev_attr_pwm3_enable.dev_attr)))
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
707
  			goto error_remove_files;
8477d0268   Jean Delvare   hwmon: (smsc47m1)...
708
  	} else if (data->type == smsc47m2)
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
709
710
  		dev_dbg(dev, "PWM 3 not enabled by hardware, skipping
  ");
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
711

51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
712
  	if ((err = device_create_file(dev, &dev_attr_alarms)))
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
713
  		goto error_remove_files;
68a50b567   Jean Delvare   hwmon: (smsc47m1)...
714
715
  	if ((err = device_create_file(dev, &dev_attr_name)))
  		goto error_remove_files;
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
716

1beeffe43   Tony Jones   hwmon: Convert fr...
717
718
719
  	data->hwmon_dev = hwmon_device_register(dev);
  	if (IS_ERR(data->hwmon_dev)) {
  		err = PTR_ERR(data->hwmon_dev);
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
720
721
  		goto error_remove_files;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722
723
  
  	return 0;
ce8c6ce1e   Jean Delvare   hwmon: Fix unchec...
724
  error_remove_files:
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
725
  	sysfs_remove_group(&dev->kobj, &smsc47m1_group);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
726
  error_free:
04a6217df   Jean Delvare   hwmon: Fix a pote...
727
  	platform_set_drvdata(pdev, NULL);
1f57ff89f   Alexey Dobriyan   [PATCH] drivers/h...
728
  	kfree(data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
729
  error_release:
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
730
  	smsc47m1_handle_resources(res->start, sio_data->type, RELEASE, dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
731
732
  	return err;
  }
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
733
  static int __exit smsc47m1_remove(struct platform_device *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
734
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
735
736
  	struct smsc47m1_data *data = platform_get_drvdata(pdev);
  	struct resource *res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737

1beeffe43   Tony Jones   hwmon: Convert fr...
738
  	hwmon_device_unregister(data->hwmon_dev);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
739
  	sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740

51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
741
  	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
742
  	smsc47m1_handle_resources(res->start, data->type, RELEASE, &pdev->dev);
04a6217df   Jean Delvare   hwmon: Fix a pote...
743
  	platform_set_drvdata(pdev, NULL);
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
744
  	kfree(data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
745
746
747
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
748
749
750
  static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
  		int init)
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
751
  	struct smsc47m1_data *data = dev_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752

9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
753
  	mutex_lock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
754
755
  
  	if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
756
757
  		int i, fan_nr;
  		fan_nr = data->type == smsc47m2 ? 3 : 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758

8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
759
  		for (i = 0; i < fan_nr; i++) {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
760
  			data->fan[i] = smsc47m1_read_value(data,
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
761
  				       SMSC47M1_REG_FAN[i]);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
762
  			data->fan_preload[i] = smsc47m1_read_value(data,
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
763
  					       SMSC47M1_REG_FAN_PRELOAD[i]);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
764
  			data->pwm[i] = smsc47m1_read_value(data,
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
765
  				       SMSC47M1_REG_PWM[i]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
766
  		}
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
767
  		i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
768
769
  		data->fan_div[0] = (i >> 4) & 0x03;
  		data->fan_div[1] = i >> 6;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
770
  		data->alarms = smsc47m1_read_value(data,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
771
772
773
  			       SMSC47M1_REG_ALARM) >> 6;
  		/* Clear alarms if needed */
  		if (data->alarms)
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
774
  			smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
775

8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
776
  		if (fan_nr >= 3) {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
777
  			data->fan_div[2] = (smsc47m1_read_value(data,
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
778
  					    SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
779
  			data->alarms |= (smsc47m1_read_value(data,
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
780
781
782
  					 SMSC47M2_REG_ALARM6) & 0x40) >> 4;
  			/* Clear alarm if needed */
  			if (data->alarms & 0x04)
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
783
  				smsc47m1_write_value(data,
8eccbb6fb   Jean Delvare   hwmon/smsc47m1: A...
784
785
786
  						     SMSC47M2_REG_ALARM6,
  						     0x40);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
787
788
  		data->last_updated = jiffies;
  	}
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
789
  	mutex_unlock(&data->update_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
790
791
  	return data;
  }
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
792
793
794
795
796
797
798
799
800
801
  static int __init smsc47m1_device_add(unsigned short address,
  				      const struct smsc47m1_sio_data *sio_data)
  {
  	struct resource res = {
  		.start	= address,
  		.end	= address + SMSC_EXTENT - 1,
  		.name	= DRVNAME,
  		.flags	= IORESOURCE_IO,
  	};
  	int err;
a0e92d70f   Jean Delvare   hwmon: (smsc47m1)...
802
  	err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL);
b9acb64a3   Jean Delvare   hwmon: Check for ...
803
804
  	if (err)
  		goto exit;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
805
806
807
  	pdev = platform_device_alloc(DRVNAME, address);
  	if (!pdev) {
  		err = -ENOMEM;
512504e9f   Joe Perches   hwmon: (smsc47m1)...
808
809
  		pr_err("Device allocation failed
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
810
811
812
813
814
  		goto exit;
  	}
  
  	err = platform_device_add_resources(pdev, &res, 1);
  	if (err) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
815
816
  		pr_err("Device resource addition failed (%d)
  ", err);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
817
818
  		goto exit_device_put;
  	}
2df6d8115   Jean Delvare   hwmon: Use platfo...
819
820
821
  	err = platform_device_add_data(pdev, sio_data,
  				       sizeof(struct smsc47m1_sio_data));
  	if (err) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
822
823
  		pr_err("Platform data allocation failed
  ");
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
824
825
  		goto exit_device_put;
  	}
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
826
827
828
  
  	err = platform_device_add(pdev);
  	if (err) {
512504e9f   Joe Perches   hwmon: (smsc47m1)...
829
830
  		pr_err("Device addition failed (%d)
  ", err);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
831
832
833
834
835
836
837
838
839
840
  		goto exit_device_put;
  	}
  
  	return 0;
  
  exit_device_put:
  	platform_device_put(pdev);
  exit:
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
842
  static int __init sm_smsc47m1_init(void)
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
843
844
845
846
847
  	int err;
  	unsigned short address;
  	struct smsc47m1_sio_data sio_data;
  
  	if (smsc47m1_find(&address, &sio_data))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
849

3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
850
851
  	/* Sets global pdev as a side effect */
  	err = smsc47m1_device_add(address, &sio_data);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
852
853
  	if (err)
  		goto exit;
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
854
  	err = platform_driver_probe(&smsc47m1_driver, smsc47m1_probe);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
855
  	if (err)
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
856
  		goto exit_device;
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
857
858
  
  	return 0;
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
859
860
  exit_device:
  	platform_device_unregister(pdev);
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
861
  	smsc47m1_restore(&sio_data);
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
862
863
  exit:
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864
865
866
867
  }
  
  static void __exit sm_smsc47m1_exit(void)
  {
51f2cca1f   Jean Delvare   hwmon/smsc47m1: C...
868
  	platform_driver_unregister(&smsc47m1_driver);
fa0bff022   Jean Delvare   hwmon: (smsc47m1)...
869
  	smsc47m1_restore(pdev->dev.platform_data);
3ecf44b31   Jean Delvare   hwmon: (smsc47m1)...
870
  	platform_device_unregister(pdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
871
872
873
874
875
876
877
878
  }
  
  MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
  MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
  MODULE_LICENSE("GPL");
  
  module_init(sm_smsc47m1_init);
  module_exit(sm_smsc47m1_exit);